1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
;
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
)
51 ScRangeStringConverter::GetTokenByOffset(aToken
, aRangeStr
, nOffset
, cSep
, cQuote
);
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();
63 continue; // Should a missing range really be allowed?
71 const FormulaToken
* p
= pArray
->Next();
78 const ScToken
* pT
= static_cast<const ScToken
*>(p
);
79 switch (pT
->GetType())
83 const ScSingleRefData
& rRef
= pT
->GetSingleRef();
86 else if (bOnly3DRef
&& !rRef
.IsFlag3D())
92 const ScComplexRefData
& rRef
= pT
->GetDoubleRef();
95 else if (bOnly3DRef
&& !rRef
.Ref1
.IsFlag3D())
99 case svExternalSingleRef
:
100 if (!pT
->GetSingleRef().ValidExternal())
103 case svExternalDoubleRef
:
104 if (!pT
->GetDoubleRef().ValidExternal())
108 if (pT
->GetString().isEmpty())
116 rRefTokens
.push_back(
117 ScTokenRef(static_cast<ScToken
*>(p
->Clone())));
124 bool ScRefTokenHelper::getRangeFromToken(
125 ScRange
& rRange
, const ScTokenRef
& pToken
, const ScAddress
& rPos
, bool bExternal
)
127 StackVar eType
= pToken
->GetType();
128 switch (pToken
->GetType())
131 case svExternalSingleRef
:
133 if ((eType
== svExternalSingleRef
&& !bExternal
) ||
134 (eType
== svSingleRef
&& bExternal
))
137 const ScSingleRefData
& rRefData
= pToken
->GetSingleRef();
138 rRange
.aStart
= rRefData
.toAbs(rPos
);
139 rRange
.aEnd
= rRange
.aStart
;
143 case svExternalDoubleRef
:
145 if ((eType
== svExternalDoubleRef
&& !bExternal
) ||
146 (eType
== svDoubleRef
&& bExternal
))
149 const ScComplexRefData
& rRefData
= pToken
->GetDoubleRef();
150 rRange
= rRefData
.toAbs(rPos
);
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
)
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
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
];
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())
209 case svExternalSingleRef
:
210 case svExternalDoubleRef
:
218 bool ScRefTokenHelper::isExternalRef(const ScTokenRef
& pToken
)
220 switch (pToken
->GetType())
222 case svExternalSingleRef
:
223 case svExternalDoubleRef
:
231 bool ScRefTokenHelper::intersects(
232 const vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
237 bool bExternal
= isExternalRef(pToken
);
238 sal_uInt16 nFileId
= bExternal
? pToken
->GetIndex() : 0;
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
;
250 if (bExternal
!= isExternalRef(p
))
254 getRangeFromToken(aRange2
, p
, rPos
, bExternal
);
256 if (bExternal
&& nFileId
!= p
->GetIndex())
257 // different external file
260 if (aRange
.Intersects(aRange2
))
268 class JoinRefTokenRanges
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
);
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
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.
304 T nMin
= nMin1
< nMin2
? nMin1
: nMin2
;
305 T nMax
= nMax1
> nMax2
? nMax1
: nMax2
;
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
))
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
336 if (bExternal
!= ScRefTokenHelper::isExternalRef(pOldToken
))
337 // External and internal refs don't mix.
342 if (nFileId
!= pOldToken
->GetIndex())
343 // Different external files.
346 if (aTabName
!= pOldToken
->GetString().getString())
347 // Different table names.
351 ScComplexRefData aOldData
;
352 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData
, pOldToken
))
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.
362 // This new range is part of an existing range. Skip it.
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;
371 SCCOL nNewMin
, nNewMax
;
372 bJoinRanges
= overlaps(
373 aNew
.aStart
.Col(), aNew
.aEnd
.Col(), aOld
.aStart
.Col(), aOld
.aEnd
.Col(),
378 aNew
.aStart
.SetCol(nNewMin
);
379 aNew
.aEnd
.SetCol(nNewMax
);
380 aNewData
.SetRange(aNew
, rPos
);
385 SCROW nNewMin
, nNewMax
;
386 bJoinRanges
= overlaps(
387 aNew
.aStart
.Row(), aNew
.aEnd
.Row(), aOld
.aStart
.Row(), aOld
.aEnd
.Row(),
392 aNew
.aStart
.SetRow(nNewMin
);
393 aNew
.aEnd
.SetRow(nNewMax
);
394 aNewData
.SetRange(aNew
, rPos
);
401 pOldToken
.reset(new ScExternalDoubleRefToken(nFileId
, aTabName
, aNewData
));
403 pOldToken
.reset(new ScDoubleRefToken(aNewData
));
412 if (rTokens
.size() == 1)
413 // There is only one left. No need to do more joining.
416 // Pop the last token from the list, and keep joining recursively.
417 ScTokenRef p
= rTokens
.back();
419 join(rTokens
, p
, rPos
);
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())
439 case svExternalSingleRef
:
441 const ScSingleRefData
& r
= pToken
->GetSingleRef();
443 rData
.Ref1
.SetFlag3D(true);
445 rData
.Ref2
.SetFlag3D(false); // Don't display sheet name on second reference.
449 case svExternalDoubleRef
:
450 rData
= pToken
->GetDoubleRef();
453 // Not a reference token. Bail out.
459 ScTokenRef
ScRefTokenHelper::createRefToken(const ScAddress
& rAddr
)
461 ScSingleRefData aRefData
;
462 aRefData
.InitAddress(rAddr
);
463 ScTokenRef
pRef(new ScSingleRefToken(aRefData
));
467 ScTokenRef
ScRefTokenHelper::createRefToken(const ScRange
& rRange
)
469 ScComplexRefData aRefData
;
470 aRefData
.InitRange(rRange
);
471 ScTokenRef
pRef(new ScDoubleRefToken(aRefData
));
475 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */