Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / reftokenhelper.cxx
bloba02f02be118c4b10d82d3fb73b3ff410ea020d67
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include "reftokenhelper.hxx"
21 #include "document.hxx"
22 #include "rangeutl.hxx"
23 #include "compiler.hxx"
24 #include "tokenarray.hxx"
26 #include "rtl/ustring.hxx"
27 #include "formula/grammar.hxx"
28 #include "formula/token.hxx"
30 using namespace formula;
32 using ::std::vector;
33 using ::std::auto_ptr;
35 void ScRefTokenHelper::compileRangeRepresentation(
36 vector<ScTokenRef>& rRefTokens, const OUString& rRangeStr, ScDocument* pDoc,
37 const sal_Unicode cSep, FormulaGrammar::Grammar eGrammar, bool bOnly3DRef)
39 const sal_Unicode cQuote = '\'';
41 // #i107275# ignore parentheses
42 OUString aRangeStr = rRangeStr;
43 while( (aRangeStr.getLength() >= 2) && (aRangeStr[ 0 ] == '(') && (aRangeStr[ aRangeStr.getLength() - 1 ] == ')') )
44 aRangeStr = aRangeStr.copy( 1, aRangeStr.getLength() - 2 );
46 bool bFailure = false;
47 sal_Int32 nOffset = 0;
48 while (nOffset >= 0 && !bFailure)
50 OUString aToken;
51 ScRangeStringConverter::GetTokenByOffset(aToken, aRangeStr, nOffset, cSep, cQuote);
52 if (nOffset < 0)
53 break;
55 ScCompiler aCompiler(pDoc, ScAddress(0,0,0));
56 aCompiler.SetGrammar(eGrammar);
57 auto_ptr<ScTokenArray> pArray(aCompiler.CompileString(aToken));
59 // There MUST be exactly one reference per range token and nothing
60 // else, and it MUST be a valid reference, not some #REF!
61 sal_uInt16 nLen = pArray->GetLen();
62 if (!nLen)
63 continue; // Should a missing range really be allowed?
64 if (nLen != 1)
66 bFailure = true;
67 break;
70 pArray->Reset();
71 const FormulaToken* p = pArray->Next();
72 if (!p)
74 bFailure = true;
75 break;
78 const ScToken* pT = static_cast<const ScToken*>(p);
79 switch (pT->GetType())
81 case svSingleRef:
83 const ScSingleRefData& rRef = pT->GetSingleRef();
84 if (!rRef.Valid())
85 bFailure = true;
86 else if (bOnly3DRef && !rRef.IsFlag3D())
87 bFailure = true;
89 break;
90 case svDoubleRef:
92 const ScComplexRefData& rRef = pT->GetDoubleRef();
93 if (!rRef.Valid())
94 bFailure = true;
95 else if (bOnly3DRef && !rRef.Ref1.IsFlag3D())
96 bFailure = true;
98 break;
99 case svExternalSingleRef:
100 if (!pT->GetSingleRef().ValidExternal())
101 bFailure = true;
102 break;
103 case svExternalDoubleRef:
104 if (!pT->GetDoubleRef().ValidExternal())
105 bFailure = true;
106 break;
107 case svString:
108 if (pT->GetString().isEmpty())
109 bFailure = true;
110 break;
111 default:
112 bFailure = true;
113 break;
115 if (!bFailure)
116 rRefTokens.push_back(
117 ScTokenRef(static_cast<ScToken*>(p->Clone())));
120 if (bFailure)
121 rRefTokens.clear();
124 bool ScRefTokenHelper::getRangeFromToken(
125 ScRange& rRange, const ScTokenRef& pToken, const ScAddress& rPos, bool bExternal)
127 StackVar eType = pToken->GetType();
128 switch (pToken->GetType())
130 case svSingleRef:
131 case svExternalSingleRef:
133 if ((eType == svExternalSingleRef && !bExternal) ||
134 (eType == svSingleRef && bExternal))
135 return false;
137 const ScSingleRefData& rRefData = pToken->GetSingleRef();
138 rRange.aStart = rRefData.toAbs(rPos);
139 rRange.aEnd = rRange.aStart;
140 return true;
142 case svDoubleRef:
143 case svExternalDoubleRef:
145 if ((eType == svExternalDoubleRef && !bExternal) ||
146 (eType == svDoubleRef && bExternal))
147 return false;
149 const ScComplexRefData& rRefData = pToken->GetDoubleRef();
150 rRange = rRefData.toAbs(rPos);
151 return true;
153 default:
154 ; // do nothing
156 return false;
159 void ScRefTokenHelper::getRangeListFromTokens(
160 ScRangeList& rRangeList, const vector<ScTokenRef>& rTokens, const ScAddress& rPos)
162 vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
163 for (; itr != itrEnd; ++itr)
165 ScRange aRange;
166 getRangeFromToken(aRange, *itr, rPos);
167 rRangeList.Append(aRange);
171 void ScRefTokenHelper::getTokenFromRange(ScTokenRef& pToken, const ScRange& rRange)
173 ScComplexRefData aData;
174 aData.InitRange(rRange);
175 aData.Ref1.SetFlag3D(true);
177 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
178 // different sheets.
179 aData.Ref2.SetFlag3D(rRange.aStart.Tab() != rRange.aEnd.Tab());
181 pToken.reset(new ScDoubleRefToken(aData));
184 void ScRefTokenHelper::getTokensFromRangeList(vector<ScTokenRef>& pTokens, const ScRangeList& rRanges)
186 vector<ScTokenRef> aTokens;
187 size_t nCount = rRanges.size();
188 aTokens.reserve(nCount);
189 for (size_t i = 0; i < nCount; ++i)
191 const ScRange* pRange = rRanges[i];
192 if (!pRange)
193 // failed.
194 return;
196 ScTokenRef pToken;
197 ScRefTokenHelper::getTokenFromRange(pToken,* pRange);
198 aTokens.push_back(pToken);
200 pTokens.swap(aTokens);
203 bool ScRefTokenHelper::isRef(const ScTokenRef& pToken)
205 switch (pToken->GetType())
207 case svSingleRef:
208 case svDoubleRef:
209 case svExternalSingleRef:
210 case svExternalDoubleRef:
211 return true;
212 default:
215 return false;
218 bool ScRefTokenHelper::isExternalRef(const ScTokenRef& pToken)
220 switch (pToken->GetType())
222 case svExternalSingleRef:
223 case svExternalDoubleRef:
224 return true;
225 default:
228 return false;
231 bool ScRefTokenHelper::intersects(
232 const vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
234 if (!isRef(pToken))
235 return false;
237 bool bExternal = isExternalRef(pToken);
238 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
240 ScRange aRange;
241 getRangeFromToken(aRange, pToken, rPos, bExternal);
243 vector<ScTokenRef>::const_iterator itr = rTokens.begin(), itrEnd = rTokens.end();
244 for (; itr != itrEnd; ++itr)
246 const ScTokenRef& p = *itr;
247 if (!isRef(p))
248 continue;
250 if (bExternal != isExternalRef(p))
251 continue;
253 ScRange aRange2;
254 getRangeFromToken(aRange2, p, rPos, bExternal);
256 if (bExternal && nFileId != p->GetIndex())
257 // different external file
258 continue;
260 if (aRange.Intersects(aRange2))
261 return true;
263 return false;
266 namespace {
268 class JoinRefTokenRanges
270 public:
272 * Insert a new reference token into the existing list of reference tokens,
273 * but in that process, try to join as many adjacent ranges as possible.
275 * @param rTokens existing list of reference tokens
276 * @param rToken new token
278 void operator() (vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
280 join(rTokens, pToken, rPos);
283 private:
286 * Check two 1-dimensional ranges to see if they overlap each other.
288 * @param nMin1 min value of range 1
289 * @param nMax1 max value of range 1
290 * @param nMin2 min value of range 2
291 * @param nMax2 max value of range 2
292 * @param rNewMin min value of new range in case they overlap
293 * @param rNewMax max value of new range in case they overlap
295 template<typename T>
296 static bool overlaps(T nMin1, T nMax1, T nMin2, T nMax2, T& rNewMin, T& rNewMax)
298 bool bDisjoint1 = (nMin1 > nMax2) && (nMin1 - nMax2 > 1);
299 bool bDisjoint2 = (nMin2 > nMax1) && (nMin2 - nMax1 > 1);
300 if (bDisjoint1 || bDisjoint2)
301 // These two ranges cannot be joined. Move on.
302 return false;
304 T nMin = nMin1 < nMin2 ? nMin1 : nMin2;
305 T nMax = nMax1 > nMax2 ? nMax1 : nMax2;
307 rNewMin = nMin;
308 rNewMax = nMax;
310 return true;
313 void join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
315 // Normalize the token to a double reference.
316 ScComplexRefData aData;
317 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData, pToken))
318 return;
320 // Get the information of the new token.
321 bool bExternal = ScRefTokenHelper::isExternalRef(pToken);
322 sal_uInt16 nFileId = bExternal ? pToken->GetIndex() : 0;
323 OUString aTabName = bExternal ? pToken->GetString().getString() : OUString();
325 bool bJoined = false;
326 vector<ScTokenRef>::iterator itr = rTokens.begin(), itrEnd = rTokens.end();
327 for (; itr != itrEnd; ++itr)
329 ScTokenRef& pOldToken = *itr;
331 if (!ScRefTokenHelper::isRef(pOldToken))
332 // A non-ref token should not have been added here in the first
333 // place!
334 continue;
336 if (bExternal != ScRefTokenHelper::isExternalRef(pOldToken))
337 // External and internal refs don't mix.
338 continue;
340 if (bExternal)
342 if (nFileId != pOldToken->GetIndex())
343 // Different external files.
344 continue;
346 if (aTabName != pOldToken->GetString().getString())
347 // Different table names.
348 continue;
351 ScComplexRefData aOldData;
352 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData, pOldToken))
353 continue;
355 ScRange aOld = aOldData.toAbs(rPos), aNew = aData.toAbs(rPos);
357 if (aNew.aStart.Tab() != aOld.aStart.Tab() || aNew.aEnd.Tab() != aOld.aEnd.Tab())
358 // Sheet ranges differ.
359 continue;
361 if (aOld.In(aNew))
362 // This new range is part of an existing range. Skip it.
363 return;
365 bool bSameRows = (aNew.aStart.Row() == aOld.aStart.Row()) && (aNew.aEnd.Row() == aOld.aEnd.Row());
366 bool bSameCols = (aNew.aStart.Col() == aOld.aStart.Col()) && (aNew.aEnd.Col() == aOld.aEnd.Col());
367 ScComplexRefData aNewData = aOldData;
368 bool bJoinRanges = false;
369 if (bSameRows)
371 SCCOL nNewMin, nNewMax;
372 bJoinRanges = overlaps(
373 aNew.aStart.Col(), aNew.aEnd.Col(), aOld.aStart.Col(), aOld.aEnd.Col(),
374 nNewMin, nNewMax);
376 if (bJoinRanges)
378 aNew.aStart.SetCol(nNewMin);
379 aNew.aEnd.SetCol(nNewMax);
380 aNewData.SetRange(aNew, rPos);
383 else if (bSameCols)
385 SCROW nNewMin, nNewMax;
386 bJoinRanges = overlaps(
387 aNew.aStart.Row(), aNew.aEnd.Row(), aOld.aStart.Row(), aOld.aEnd.Row(),
388 nNewMin, nNewMax);
390 if (bJoinRanges)
392 aNew.aStart.SetRow(nNewMin);
393 aNew.aEnd.SetRow(nNewMax);
394 aNewData.SetRange(aNew, rPos);
398 if (bJoinRanges)
400 if (bExternal)
401 pOldToken.reset(new ScExternalDoubleRefToken(nFileId, aTabName, aNewData));
402 else
403 pOldToken.reset(new ScDoubleRefToken(aNewData));
405 bJoined = true;
406 break;
410 if (bJoined)
412 if (rTokens.size() == 1)
413 // There is only one left. No need to do more joining.
414 return;
416 // Pop the last token from the list, and keep joining recursively.
417 ScTokenRef p = rTokens.back();
418 rTokens.pop_back();
419 join(rTokens, p, rPos);
421 else
422 rTokens.push_back(pToken);
428 void ScRefTokenHelper::join(vector<ScTokenRef>& rTokens, const ScTokenRef& pToken, const ScAddress& rPos)
430 JoinRefTokenRanges join;
431 join(rTokens, pToken, rPos);
434 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData& rData, const ScTokenRef& pToken)
436 switch (pToken->GetType())
438 case svSingleRef:
439 case svExternalSingleRef:
441 const ScSingleRefData& r = pToken->GetSingleRef();
442 rData.Ref1 = r;
443 rData.Ref1.SetFlag3D(true);
444 rData.Ref2 = r;
445 rData.Ref2.SetFlag3D(false); // Don't display sheet name on second reference.
447 break;
448 case svDoubleRef:
449 case svExternalDoubleRef:
450 rData = pToken->GetDoubleRef();
451 break;
452 default:
453 // Not a reference token. Bail out.
454 return false;
456 return true;
459 ScTokenRef ScRefTokenHelper::createRefToken(const ScAddress& rAddr)
461 ScSingleRefData aRefData;
462 aRefData.InitAddress(rAddr);
463 ScTokenRef pRef(new ScSingleRefToken(aRefData));
464 return pRef;
467 ScTokenRef ScRefTokenHelper::createRefToken(const ScRange& rRange)
469 ScComplexRefData aRefData;
470 aRefData.InitRange(rRange);
471 ScTokenRef pRef(new ScDoubleRefToken(aRefData));
472 return pRef;
475 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */