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