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 5032 : void ScRefTokenHelper::compileRangeRepresentation(
37 : vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
38 : const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
39 : {
40 5032 : const sal_Unicode cQuote = '\'';
41 :
42 : // #i107275# ignore parentheses
43 5032 : OUString aRangeStr = rRangeStr;
44 10064 : while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
45 0 : aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
46 :
47 5032 : bool bFailure = false;
48 5032 : sal_Int32 nOffset = 0;
49 15172 : while (nOffset >= 0 && !bFailure)
50 : {
51 10120 : OUString aToken;
52 10120 : ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote);
53 10120 : if (nOffset < 0)
54 5012 : break;
55 :
56 10216 : ScCompiler aCompiler(pDoc, ScAddress(0,0,0));
57 5108 : aCompiler.SetGrammar(eGrammar);
58 10216 : 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 5108 : sal_uInt16 nLen = pArray->GetLen();
63 5108 : if (!nLen)
64 0 : continue; // Should a missing range really be allowed?
65 5108 : if (nLen != 1)
66 : {
67 0 : bFailure = true;
68 0 : break;
69 : }
70 :
71 5108 : pArray->Reset();
72 5108 : const FormulaToken* p = pArray->Next();
73 5108 : if (!p)
74 : {
75 0 : bFailure = true;
76 0 : break;
77 : }
78 :
79 5108 : switch (p->GetType())
80 : {
81 : case svSingleRef:
82 : {
83 2026 : const ScSingleRefData& rRef = *p->GetSingleRef();
84 2026 : if (!rRef.Valid())
85 0 : bFailure = true;
86 2026 : else if (bOnly3DRef && !rRef.IsFlag3D())
87 0 : bFailure = true;
88 : }
89 2026 : break;
90 : case svDoubleRef:
91 : {
92 3030 : const ScComplexRefData& rRef = *p->GetDoubleRef();
93 3030 : if (!rRef.Valid())
94 0 : bFailure = true;
95 3030 : else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
96 0 : bFailure = true;
97 : }
98 3030 : 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 32 : if (p->GetString().isEmpty())
113 0 : bFailure = true;
114 32 : break;
115 : default:
116 20 : bFailure = true;
117 20 : break;
118 : }
119 5108 : if (!bFailure)
120 5088 : rRefTokens.push_back(ScTokenRef(p->Clone()));
121 :
122 5108 : }
123 5032 : if (bFailure)
124 20 : rRefTokens.clear();
125 5032 : }
126 :
127 30651 : bool ScRefTokenHelper::getRangeFromToken(
128 : ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
129 : {
130 30651 : StackVar eType = pToken->GetType();
131 30651 : switch (pToken->GetType())
132 : {
133 : case svSingleRef:
134 : case svExternalSingleRef:
135 : {
136 15848 : if ((eType == svExternalSingleRef && !bExternal) ||
137 15848 : (eType == svSingleRef && bExternal))
138 0 : return false;
139 :
140 15848 : const ScSingleRefData& rRefData = *pToken->GetSingleRef();
141 15848 : rRange.aStart = rRefData.toAbs(rPos);
142 15848 : rRange.aEnd = rRange.aStart;
143 15848 : return true;
144 : }
145 : case svDoubleRef:
146 : case svExternalDoubleRef:
147 : {
148 14781 : if ((eType == svExternalDoubleRef && !bExternal) ||
149 14781 : (eType == svDoubleRef && bExternal))
150 0 : return false;
151 :
152 14781 : const ScComplexRefData& rRefData = *pToken->GetDoubleRef();
153 14781 : rRange = rRefData.toAbs(rPos);
154 14781 : return true;
155 : }
156 : default:
157 : ; // do nothing
158 : }
159 22 : return false;
160 : }
161 :
162 13615 : void ScRefTokenHelper::getRangeListFromTokens(
163 : ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
164 : {
165 13615 : vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
166 27368 : for (; itr != itrEnd; ++itr)
167 : {
168 13753 : ScRange aRange;
169 13753 : getRangeFromToken(aRange, *itr, rPos);
170 13753 : rRangeList.Append(aRange);
171 : }
172 13615 : }
173 :
174 64 : void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
175 : {
176 : ScComplexRefData aData;
177 64 : aData.InitRange(rRange);
178 64 : 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 64 : aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
183 :
184 64 : pToken.reset(new ScDoubleRefToken(aData));
185 64 : }
186 :
187 40 : void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
188 : {
189 40 : vector<ScTokenRef> aTokens;
190 40 : size_t nCount = rRanges.size();
191 40 : aTokens.reserve(nCount);
192 104 : for (size_t i = 0; i < nCount; ++i)
193 : {
194 64 : const ScRange* pRange = rRanges[i];
195 64 : if (!pRange)
196 : // failed.
197 40 : return;
198 :
199 64 : ScTokenRef pToken;
200 64 : ScRefTokenHelper::getTokenFromRange(pToken,* pRange);
201 64 : aTokens.push_back(pToken);
202 64 : }
203 40 : pTokens.swap(aTokens);
204 : }
205 :
206 15098 : bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
207 : {
208 15098 : switch (pToken->GetType())
209 : {
210 : case svSingleRef:
211 : case svDoubleRef:
212 : case svExternalSingleRef:
213 : case svExternalDoubleRef:
214 15098 : return true;
215 : default:
216 : ;
217 : }
218 0 : return false;
219 : }
220 :
221 42320 : bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
222 : {
223 42320 : switch (pToken->GetType())
224 : {
225 : case svExternalSingleRef:
226 : case svExternalDoubleRef:
227 0 : return true;
228 : default:
229 : ;
230 : }
231 42320 : return false;
232 : }
233 :
234 8 : bool ScRefTokenHelper::intersects(
235 : const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
236 : {
237 8 : if (!isRef(pToken))
238 0 : return false;
239 :
240 8 : bool bExternal = isExternalRef(pToken);
241 8 : sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
242 :
243 8 : ScRange aRange;
244 8 : getRangeFromToken(aRange, pToken, rPos, bExternal);
245 :
246 8 : vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
247 12 : for (; itr != itrEnd; ++itr)
248 : {
249 8 : const ScTokenRef& p = *itr;
250 8 : if (!isRef(p))
251 0 : continue;
252 :
253 8 : if (bExternal != isExternalRef(p))
254 0 : continue;
255 :
256 8 : ScRange aRange2;
257 8 : getRangeFromToken(aRange2, p, rPos, bExternal);
258 :
259 8 : if (bExternal && nFileId != p->GetIndex())
260 : // different external file
261 0 : continue;
262 :
263 8 : if (aRange.Intersects(aRange2))
264 4 : return true;
265 : }
266 4 : 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 18370 : void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
282 : {
283 18370 : join(rTokens, pToken, rPos);
284 18370 : }
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 10934 : static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
300 : {
301 10934 : bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
302 10934 : bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
303 10934 : if (bDisjoint1 || bDisjoint2)
304 : // These two ranges cannot be joined. Move on.
305 0 : return false;
306 :
307 10934 : T nMin = nMin1 < nMin2 ? nMin1 : nMin2;
308 10934 : T nMax = nMax1 > nMax2 ? nMax1 : nMax2;
309 :
310 10934 : rNewMin = nMin;
311 10934 : rNewMax = nMax;
312 :
313 10934 : return true;
314 : }
315 :
316 19824 : void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
317 : {
318 : // Normalize the token to a double reference.
319 : ScComplexRefData aData;
320 19824 : if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
321 9584 : return;
322 :
323 : // Get the information of the new token.
324 19772 : bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
325 19772 : sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
326 19772 : OUString aTabName = bExternal ? pToken->GetString().getString() : OUString();
327 :
328 19772 : bool bJoined = false;
329 19772 : vector<ScTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end();
330 22634 : for (; itr != itrEnd; ++itr)
331 : {
332 13796 : ScTokenRef& pOldToken = *itr;
333 :
334 13796 : 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 13796 : if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
340 : // External and internal refs don't mix.
341 0 : continue;
342 :
343 13796 : 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 13796 : if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
356 0 : continue;
357 :
358 13796 : ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
359 :
360 13796 : if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
361 : // Sheet ranges differ.
362 0 : continue;
363 :
364 13796 : if (aOld.In(aNew))
365 : // This new range is part of an existing range. Skip it.
366 0 : return;
367 :
368 13796 : bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
369 13796 : bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
370 13796 : ScComplexRefData aNewData = aOldData;
371 13796 : bool bJoinRanges = false;
372 13796 : if (bSameRows)
373 : {
374 : SCCOL nNewMin, nNewMax;
375 : bJoinRanges = overlaps(
376 19688 : aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
377 19688 : nNewMin, nNewMax);
378 :
379 4922 : if (bJoinRanges)
380 : {
381 4922 : aNew.aStart.SetCol(nNewMin);
382 4922 : aNew.aEnd.SetCol(nNewMax);
383 4922 : aNewData.SetRange(aNew, rPos);
384 : }
385 : }
386 8874 : 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 6012 : nNewMin, nNewMax);
392 :
393 6012 : if (bJoinRanges)
394 : {
395 6012 : aNew.aStart.SetRow(nNewMin);
396 6012 : aNew.aEnd.SetRow(nNewMax);
397 6012 : aNewData.SetRange(aNew, rPos);
398 : }
399 : }
400 :
401 13796 : if (bJoinRanges)
402 : {
403 10934 : if (bExternal)
404 0 : pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
405 : else
406 10934 : pOldToken.reset(new ScDoubleRefToken(aNewData));
407 :
408 10934 : bJoined = true;
409 10934 : break;
410 : }
411 : }
412 :
413 19772 : if (bJoined)
414 : {
415 10934 : if (rTokens.size() == 1)
416 : // There is only one left. No need to do more joining.
417 9480 : return;
418 :
419 : // Pop the last token from the list, and keep joining recursively.
420 1454 : ScTokenRef p = rTokens.back();
421 1454 : rTokens.pop_back();
422 1454 : join(rTokens, p, rPos);
423 : }
424 : else
425 8838 : rTokens.push_back(pToken);
426 : }
427 : };
428 :
429 : }
430 :
431 18370 : void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
432 : {
433 : JoinRefTokenRanges join;
434 18370 : join(rTokens, pToken, rPos);
435 18370 : }
436 :
437 35590 : bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
438 : {
439 35590 : switch (pToken->GetType())
440 : {
441 : case svSingleRef:
442 : case svExternalSingleRef:
443 : {
444 20562 : const ScSingleRefData& r = *pToken->GetSingleRef();
445 20562 : rData.Ref1 = r;
446 20562 : rData.Ref1.SetFlag3D(true);
447 20562 : rData.Ref2 = r;
448 20562 : rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
449 : }
450 20562 : break;
451 : case svDoubleRef:
452 : case svExternalDoubleRef:
453 14974 : rData = *pToken->GetDoubleRef();
454 14974 : break;
455 : default:
456 : // Not a reference token. Bail out.
457 54 : return false;
458 : }
459 35536 : return true;
460 : }
461 :
462 4 : ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
463 : {
464 : ScSingleRefData aRefData;
465 4 : aRefData.InitAddress(rAddr);
466 4 : ScTokenRef pRef(new ScSingleRefToken(aRefData));
467 4 : return pRef;
468 : }
469 :
470 2 : ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
471 : {
472 : ScComplexRefData aRefData;
473 2 : aRefData.InitRange(rRange);
474 2 : ScTokenRef pRef(new ScDoubleRefToken(aRefData));
475 2 : return pRef;
476 228 : }
477 :
478 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|