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