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