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