1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <reftokenhelper.hxx>
21 #include <document.hxx>
22 #include <rangeutl.hxx>
23 #include <compiler.hxx>
24 #include <tokenarray.hxx>
25 
26 #include <rtl/ustring.hxx>
27 #include <formula/grammar.hxx>
28 #include <formula/token.hxx>
29 
30 #include <memory>
31 
32 using namespace formula;
33 
34 using ::std::vector;
35 
compileRangeRepresentation(vector<ScTokenRef> & rRefTokens,const OUString & rRangeStr,ScDocument * pDoc,const sal_Unicode cSep,FormulaGrammar::Grammar eGrammar,bool bOnly3DRef)36 void ScRefTokenHelper::compileRangeRepresentation(
37     vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
38     const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
39 {
40     // #i107275# ignore parentheses
41     OUString aRangeStr = rRangeStr;
42     while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
43         aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
44 
45     bool bFailure = false;
46     sal_Int32 nOffset = 0;
47     while (nOffset >= 0 && !bFailure)
48     {
49         OUString aToken;
50         ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep);
51         if (nOffset < 0)
52             break;
53 
54         ScCompiler aCompiler(pDoc, ScAddress(0,0,0), eGrammar);
55         std::unique_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
56 
57         // There MUST be exactly one reference per range token and nothing
58         // else, and it MUST be a valid reference, not some #REF!
59         sal_uInt16 nLen = pArray->GetLen();
60         if (!nLen)
61             continue;   // Should a missing range really be allowed?
62         if (nLen != 1)
63         {
64             bFailure = true;
65             break;
66         }
67 
68         const FormulaToken* p = pArray->FirstToken();
69         if (!p)
70         {
71             bFailure = true;
72             break;
73         }
74 
75         switch (p->GetType())
76         {
77             case svSingleRef:
78                 {
79                     const ScSingleRefData& rRef = *p->GetSingleRef();
80                     if (!rRef.Valid(pDoc))
81                         bFailure = true;
82                     else if (bOnly3DRef && !rRef.IsFlag3D())
83                         bFailure = true;
84                 }
85                 break;
86             case svDoubleRef:
87                 {
88                     const ScComplexRefData& rRef = *p->GetDoubleRef();
89                     if (!rRef.Valid(pDoc))
90                         bFailure = true;
91                     else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
92                         bFailure = true;
93                 }
94                 break;
95             case svExternalSingleRef:
96                 {
97                     if (!p->GetSingleRef()->ValidExternal(pDoc))
98                         bFailure = true;
99                 }
100                 break;
101             case svExternalDoubleRef:
102                 {
103                     if (!p->GetDoubleRef()->ValidExternal(pDoc))
104                         bFailure = true;
105                 }
106                 break;
107             case svString:
108                 if (p->GetString().isEmpty())
109                     bFailure = true;
110                 break;
111             default:
112                 bFailure = true;
113                 break;
114         }
115         if (!bFailure)
116             rRefTokens.emplace_back(p->Clone());
117 
118     }
119     if (bFailure)
120         rRefTokens.clear();
121 }
122 
getRangeFromToken(ScRange & rRange,const ScTokenRef & pToken,const ScAddress & rPos,bool bExternal)123 bool ScRefTokenHelper::getRangeFromToken(
124     ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
125 {
126     StackVar eType = pToken->GetType();
127     switch (pToken->GetType())
128     {
129         case svSingleRef:
130         case svExternalSingleRef:
131         {
132             if ((eType == svExternalSingleRef && !bExternal) ||
133                 (eType == svSingleRef && bExternal))
134                 return false;
135 
136             const ScSingleRefData& rRefData = *pToken->GetSingleRef();
137             rRange.aStart = rRefData.toAbs(rPos);
138             rRange.aEnd = rRange.aStart;
139             return true;
140         }
141         case svDoubleRef:
142         case svExternalDoubleRef:
143         {
144             if ((eType == svExternalDoubleRef && !bExternal) ||
145                 (eType == svDoubleRef && bExternal))
146                 return false;
147 
148             const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
149             rRange = rRefData.toAbs(rPos);
150             return true;
151         }
152         default:
153             ; // do nothing
154     }
155     return false;
156 }
157 
getRangeListFromTokens(ScRangeList & rRangeList,const vector<ScTokenRef> & rTokens,const ScAddress & rPos)158 void ScRefTokenHelper::getRangeListFromTokens(
159     ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
160 {
161     for (const auto& rToken : rTokens)
162     {
163         ScRange aRange;
164         getRangeFromToken(aRange, rToken, rPos);
165         rRangeList.push_back(aRange);
166     }
167 }
168 
getTokenFromRange(ScTokenRef & pToken,const ScRange & rRange)169 void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
170 {
171     ScComplexRefData aData;
172     aData.InitRange(rRange);
173     aData.Ref1.SetFlag3D(true);
174 
175     // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
176     // different sheets.
177     aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
178 
179     pToken.reset(new ScDoubleRefToken(aData));
180 }
181 
getTokensFromRangeList(vector<ScTokenRef> & pTokens,const ScRangeList & rRanges)182 void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
183 {
184     vector<ScTokenRef> aTokens;
185     size_t nCount = rRanges.size();
186     aTokens.reserve(nCount);
187     for (size_t i = 0; i < nCount; ++i)
188     {
189         const ScRange & rRange = rRanges[i];
190         ScTokenRef pToken;
191         ScRefTokenHelper::getTokenFromRange(pToken, rRange);
192         aTokens.push_back(pToken);
193     }
194     pTokens.swap(aTokens);
195 }
196 
isRef(const ScTokenRef & pToken)197 bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
198 {
199     switch (pToken->GetType())
200     {
201         case svSingleRef:
202         case svDoubleRef:
203         case svExternalSingleRef:
204         case svExternalDoubleRef:
205             return true;
206         default:
207             ;
208     }
209     return false;
210 }
211 
isExternalRef(const ScTokenRef & pToken)212 bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
213 {
214     switch (pToken->GetType())
215     {
216         case svExternalSingleRef:
217         case svExternalDoubleRef:
218             return true;
219         default:
220             ;
221     }
222     return false;
223 }
224 
intersects(const vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)225 bool ScRefTokenHelper::intersects(
226     const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
227 {
228     if (!isRef(pToken))
229         return false;
230 
231     bool bExternal = isExternalRef(pToken);
232     sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
233 
234     ScRange aRange;
235     getRangeFromToken(aRange, pToken, rPos, bExternal);
236 
237     for (const ScTokenRef& p : rTokens)
238     {
239         if (!isRef(p))
240             continue;
241 
242         if (bExternal != isExternalRef(p))
243             continue;
244 
245         ScRange aRange2;
246         getRangeFromToken(aRange2, p, rPos, bExternal);
247 
248         if (bExternal && nFileId != p->GetIndex())
249             // different external file
250             continue;
251 
252         if (aRange.Intersects(aRange2))
253             return true;
254     }
255     return false;
256 }
257 
258 namespace {
259 
260 class JoinRefTokenRanges
261 {
262 public:
263     /**
264      * Insert a new reference token into the existing list of reference tokens,
265      * but in that process, try to join as many adjacent ranges as possible.
266      *
267      * @param rTokens existing list of reference tokens
268      * @param rToken new token
269      */
operator ()(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)270     void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
271     {
272         join(rTokens, pToken, rPos);
273     }
274 
275 private:
276 
277     /**
278      * Check two 1-dimensional ranges to see if they overlap each other.
279      *
280      * @param nMin1 min value of range 1
281      * @param nMax1 max value of range 1
282      * @param nMin2 min value of range 2
283      * @param nMax2 max value of range 2
284      * @param rNewMin min value of new range in case they overlap
285      * @param rNewMax max value of new range in case they overlap
286      */
287     template<typename T>
overlaps(T nMin1,T nMax1,T nMin2,T nMax2,T & rNewMin,T & rNewMax)288     static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
289     {
290         bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
291         bool bDisjoint2  = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
292         if (bDisjoint1 || bDisjoint2)
293             // These two ranges cannot be joined.  Move on.
294             return false;
295 
296         T nMin = std::min(nMin1, nMin2);
297         T nMax = std::max(nMax1, nMax2);
298 
299         rNewMin = nMin;
300         rNewMax = nMax;
301 
302         return true;
303     }
304 
join(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)305     void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
306     {
307         // Normalize the token to a double reference.
308         ScComplexRefData aData;
309         if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
310             return;
311 
312         // Get the information of the new token.
313         bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
314         sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
315         svl::SharedString aTabName = bExternal ? pToken->GetString() : svl::SharedString::getEmptyString();
316 
317         bool bJoined = false;
318         for (ScTokenRef& pOldToken : rTokens)
319         {
320             if (!ScRefTokenHelper::isRef(pOldToken))
321                 // A non-ref token should not have been added here in the first
322                 // place!
323                 continue;
324 
325             if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
326                 // External and internal refs don't mix.
327                 continue;
328 
329             if (bExternal)
330             {
331                 if (nFileId != pOldToken->GetIndex())
332                     // Different external files.
333                     continue;
334 
335                 if (aTabName != pOldToken->GetString())
336                     // Different table names.
337                     continue;
338             }
339 
340             ScComplexRefData aOldData;
341             if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
342                 continue;
343 
344             ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
345 
346             if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
347                 // Sheet ranges differ.
348                 continue;
349 
350             if (aOld.In(aNew))
351                 // This new range is part of an existing range.  Skip it.
352                 return;
353 
354             bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
355             bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
356             ScComplexRefData aNewData = aOldData;
357             bool bJoinRanges = false;
358             if (bSameRows)
359             {
360                 SCCOL nNewMin, nNewMax;
361                 bJoinRanges = overlaps(
362                     aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
363                     nNewMin, nNewMax);
364 
365                 if (bJoinRanges)
366                 {
367                     aNew.aStart.SetCol(nNewMin);
368                     aNew.aEnd.SetCol(nNewMax);
369                     aNewData.SetRange(aNew, rPos);
370                 }
371             }
372             else if (bSameCols)
373             {
374                 SCROW nNewMin, nNewMax;
375                 bJoinRanges = overlaps(
376                     aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
377                     nNewMin, nNewMax);
378 
379                 if (bJoinRanges)
380                 {
381                     aNew.aStart.SetRow(nNewMin);
382                     aNew.aEnd.SetRow(nNewMax);
383                     aNewData.SetRange(aNew, rPos);
384                 }
385             }
386 
387             if (bJoinRanges)
388             {
389                 if (bExternal)
390                     pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
391                 else
392                     pOldToken.reset(new ScDoubleRefToken(aNewData));
393 
394                 bJoined = true;
395                 break;
396             }
397         }
398 
399         if (bJoined)
400         {
401             if (rTokens.size() == 1)
402                 // There is only one left.  No need to do more joining.
403                 return;
404 
405             // Pop the last token from the list, and keep joining recursively.
406             ScTokenRef p = rTokens.back();
407             rTokens.pop_back();
408             join(rTokens, p, rPos);
409         }
410         else
411             rTokens.push_back(pToken);
412     }
413 };
414 
415 }
416 
join(vector<ScTokenRef> & rTokens,const ScTokenRef & pToken,const ScAddress & rPos)417 void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
418 {
419     JoinRefTokenRanges join;
420     join(rTokens, pToken, rPos);
421 }
422 
getDoubleRefDataFromToken(ScComplexRefData & rData,const ScTokenRef & pToken)423 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
424 {
425     switch (pToken->GetType())
426     {
427         case svSingleRef:
428         case svExternalSingleRef:
429         {
430             const ScSingleRefData& r = *pToken->GetSingleRef();
431             rData.Ref1 = r;
432             rData.Ref1.SetFlag3D(true);
433             rData.Ref2 = r;
434             rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
435         }
436         break;
437         case svDoubleRef:
438         case svExternalDoubleRef:
439             rData = *pToken->GetDoubleRef();
440         break;
441         default:
442             // Not a reference token.  Bail out.
443             return false;
444     }
445     return true;
446 }
447 
createRefToken(const ScAddress & rAddr)448 ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
449 {
450     ScSingleRefData aRefData;
451     aRefData.InitAddress(rAddr);
452     ScTokenRef pRef(new ScSingleRefToken(aRefData));
453     return pRef;
454 }
455 
createRefToken(const ScRange & rRange)456 ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
457 {
458     ScComplexRefData aRefData;
459     aRefData.InitRange(rRange);
460     ScTokenRef pRef(new ScDoubleRefToken(aRefData));
461     return pRef;
462 }
463 
464 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
465