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