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 #include <boost/scoped_ptr.hpp>
32 using namespace formula
;
36 void ScRefTokenHelper::compileRangeRepresentation(
37 vector
<ScTokenRef
>& rRefTokens
, const OUString
& rRangeStr
, ScDocument
* pDoc
,
38 const sal_Unicode cSep
, FormulaGrammar::Grammar eGrammar
, bool bOnly3DRef
)
40 const sal_Unicode cQuote
= '\'';
42 // #i107275# ignore parentheses
43 OUString aRangeStr
= rRangeStr
;
44 while( (aRangeStr
.getLength() >= 2) && (aRangeStr
[ 0 ] == '(') && (aRangeStr
[ aRangeStr
.getLength() - 1 ] == ')') )
45 aRangeStr
= aRangeStr
.copy( 1, aRangeStr
.getLength() - 2 );
47 bool bFailure
= false;
48 sal_Int32 nOffset
= 0;
49 while (nOffset
>= 0 && !bFailure
)
52 ScRangeStringConverter::GetTokenByOffset(aToken
, aRangeStr
, nOffset
, cSep
, cQuote
);
56 ScCompiler
aCompiler(pDoc
, ScAddress(0,0,0));
57 aCompiler
.SetGrammar(eGrammar
);
58 boost::scoped_ptr
<ScTokenArray
> pArray(aCompiler
.CompileString(aToken
));
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 sal_uInt16 nLen
= pArray
->GetLen();
64 continue; // Should a missing range really be allowed?
72 const FormulaToken
* p
= pArray
->Next();
83 const ScSingleRefData
& rRef
= *p
->GetSingleRef();
86 else if (bOnly3DRef
&& !rRef
.IsFlag3D())
92 const ScComplexRefData
& rRef
= *p
->GetDoubleRef();
95 else if (bOnly3DRef
&& !rRef
.Ref1
.IsFlag3D())
99 case svExternalSingleRef
:
101 if (!p
->GetSingleRef()->ValidExternal())
105 case svExternalDoubleRef
:
107 if (!p
->GetDoubleRef()->ValidExternal())
112 if (p
->GetString().isEmpty())
120 rRefTokens
.push_back(ScTokenRef(p
->Clone()));
127 bool ScRefTokenHelper::getRangeFromToken(
128 ScRange
& rRange
, const ScTokenRef
& pToken
, const ScAddress
& rPos
, bool bExternal
)
130 StackVar eType
= pToken
->GetType();
131 switch (pToken
->GetType())
134 case svExternalSingleRef
:
136 if ((eType
== svExternalSingleRef
&& !bExternal
) ||
137 (eType
== svSingleRef
&& bExternal
))
140 const ScSingleRefData
& rRefData
= *pToken
->GetSingleRef();
141 rRange
.aStart
= rRefData
.toAbs(rPos
);
142 rRange
.aEnd
= rRange
.aStart
;
146 case svExternalDoubleRef
:
148 if ((eType
== svExternalDoubleRef
&& !bExternal
) ||
149 (eType
== svDoubleRef
&& bExternal
))
152 const ScComplexRefData
& rRefData
= *pToken
->GetDoubleRef();
153 rRange
= rRefData
.toAbs(rPos
);
162 void ScRefTokenHelper::getRangeListFromTokens(
163 ScRangeList
& rRangeList
, const vector
<ScTokenRef
>& rTokens
, const ScAddress
& rPos
)
165 vector
<ScTokenRef
>::const_iterator itr
= rTokens
.begin(), itrEnd
= rTokens
.end();
166 for (; itr
!= itrEnd
; ++itr
)
169 getRangeFromToken(aRange
, *itr
, rPos
);
170 rRangeList
.Append(aRange
);
174 void ScRefTokenHelper::getTokenFromRange(ScTokenRef
& pToken
, const ScRange
& rRange
)
176 ScComplexRefData aData
;
177 aData
.InitRange(rRange
);
178 aData
.Ref1
.SetFlag3D(true);
180 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
182 aData
.Ref2
.SetFlag3D(rRange
.aStart
.Tab() != rRange
.aEnd
.Tab());
184 pToken
.reset(new ScDoubleRefToken(aData
));
187 void ScRefTokenHelper::getTokensFromRangeList(vector
<ScTokenRef
>& pTokens
, const ScRangeList
& rRanges
)
189 vector
<ScTokenRef
> aTokens
;
190 size_t nCount
= rRanges
.size();
191 aTokens
.reserve(nCount
);
192 for (size_t i
= 0; i
< nCount
; ++i
)
194 const ScRange
* pRange
= rRanges
[i
];
200 ScRefTokenHelper::getTokenFromRange(pToken
,* pRange
);
201 aTokens
.push_back(pToken
);
203 pTokens
.swap(aTokens
);
206 bool ScRefTokenHelper::isRef(const ScTokenRef
& pToken
)
208 switch (pToken
->GetType())
212 case svExternalSingleRef
:
213 case svExternalDoubleRef
:
221 bool ScRefTokenHelper::isExternalRef(const ScTokenRef
& pToken
)
223 switch (pToken
->GetType())
225 case svExternalSingleRef
:
226 case svExternalDoubleRef
:
234 bool ScRefTokenHelper::intersects(
235 const vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
240 bool bExternal
= isExternalRef(pToken
);
241 sal_uInt16 nFileId
= bExternal
? pToken
->GetIndex() : 0;
244 getRangeFromToken(aRange
, pToken
, rPos
, bExternal
);
246 vector
<ScTokenRef
>::const_iterator itr
= rTokens
.begin(), itrEnd
= rTokens
.end();
247 for (; itr
!= itrEnd
; ++itr
)
249 const ScTokenRef
& p
= *itr
;
253 if (bExternal
!= isExternalRef(p
))
257 getRangeFromToken(aRange2
, p
, rPos
, bExternal
);
259 if (bExternal
&& nFileId
!= p
->GetIndex())
260 // different external file
263 if (aRange
.Intersects(aRange2
))
271 class JoinRefTokenRanges
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.
278 * @param rTokens existing list of reference tokens
279 * @param rToken new token
281 void operator() (vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
283 join(rTokens
, pToken
, rPos
);
289 * Check two 1-dimensional ranges to see if they overlap each other.
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
299 static bool overlaps(T nMin1
, T nMax1
, T nMin2
, T nMax2
, T
& rNewMin
, T
& rNewMax
)
301 bool bDisjoint1
= (nMin1
> nMax2
) && (nMin1
- nMax2
> 1);
302 bool bDisjoint2
= (nMin2
> nMax1
) && (nMin2
- nMax1
> 1);
303 if (bDisjoint1
|| bDisjoint2
)
304 // These two ranges cannot be joined. Move on.
307 T nMin
= nMin1
< nMin2
? nMin1
: nMin2
;
308 T nMax
= nMax1
> nMax2
? nMax1
: nMax2
;
316 void join(vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
318 // Normalize the token to a double reference.
319 ScComplexRefData aData
;
320 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData
, pToken
))
323 // Get the information of the new token.
324 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
325 sal_uInt16 nFileId
= bExternal
? pToken
->GetIndex() : 0;
326 OUString aTabName
= bExternal
? pToken
->GetString().getString() : OUString();
328 bool bJoined
= false;
329 vector
<ScTokenRef
>::iterator itr
= rTokens
.begin(), itrEnd
= rTokens
.end();
330 for (; itr
!= itrEnd
; ++itr
)
332 ScTokenRef
& pOldToken
= *itr
;
334 if (!ScRefTokenHelper::isRef(pOldToken
))
335 // A non-ref token should not have been added here in the first
339 if (bExternal
!= ScRefTokenHelper::isExternalRef(pOldToken
))
340 // External and internal refs don't mix.
345 if (nFileId
!= pOldToken
->GetIndex())
346 // Different external files.
349 if (aTabName
!= pOldToken
->GetString().getString())
350 // Different table names.
354 ScComplexRefData aOldData
;
355 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData
, pOldToken
))
358 ScRange aOld
= aOldData
.toAbs(rPos
), aNew
= aData
.toAbs(rPos
);
360 if (aNew
.aStart
.Tab() != aOld
.aStart
.Tab() || aNew
.aEnd
.Tab() != aOld
.aEnd
.Tab())
361 // Sheet ranges differ.
365 // This new range is part of an existing range. Skip it.
368 bool bSameRows
= (aNew
.aStart
.Row() == aOld
.aStart
.Row()) && (aNew
.aEnd
.Row() == aOld
.aEnd
.Row());
369 bool bSameCols
= (aNew
.aStart
.Col() == aOld
.aStart
.Col()) && (aNew
.aEnd
.Col() == aOld
.aEnd
.Col());
370 ScComplexRefData aNewData
= aOldData
;
371 bool bJoinRanges
= false;
374 SCCOL nNewMin
, nNewMax
;
375 bJoinRanges
= overlaps(
376 aNew
.aStart
.Col(), aNew
.aEnd
.Col(), aOld
.aStart
.Col(), aOld
.aEnd
.Col(),
381 aNew
.aStart
.SetCol(nNewMin
);
382 aNew
.aEnd
.SetCol(nNewMax
);
383 aNewData
.SetRange(aNew
, rPos
);
388 SCROW nNewMin
, nNewMax
;
389 bJoinRanges
= overlaps(
390 aNew
.aStart
.Row(), aNew
.aEnd
.Row(), aOld
.aStart
.Row(), aOld
.aEnd
.Row(),
395 aNew
.aStart
.SetRow(nNewMin
);
396 aNew
.aEnd
.SetRow(nNewMax
);
397 aNewData
.SetRange(aNew
, rPos
);
404 pOldToken
.reset(new ScExternalDoubleRefToken(nFileId
, aTabName
, aNewData
));
406 pOldToken
.reset(new ScDoubleRefToken(aNewData
));
415 if (rTokens
.size() == 1)
416 // There is only one left. No need to do more joining.
419 // Pop the last token from the list, and keep joining recursively.
420 ScTokenRef p
= rTokens
.back();
422 join(rTokens
, p
, rPos
);
425 rTokens
.push_back(pToken
);
431 void ScRefTokenHelper::join(vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
433 JoinRefTokenRanges join
;
434 join(rTokens
, pToken
, rPos
);
437 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData
& rData
, const ScTokenRef
& pToken
)
439 switch (pToken
->GetType())
442 case svExternalSingleRef
:
444 const ScSingleRefData
& r
= *pToken
->GetSingleRef();
446 rData
.Ref1
.SetFlag3D(true);
448 rData
.Ref2
.SetFlag3D(false); // Don't display sheet name on second reference.
452 case svExternalDoubleRef
:
453 rData
= *pToken
->GetDoubleRef();
456 // Not a reference token. Bail out.
462 ScTokenRef
ScRefTokenHelper::createRefToken(const ScAddress
& rAddr
)
464 ScSingleRefData aRefData
;
465 aRefData
.InitAddress(rAddr
);
466 ScTokenRef
pRef(new ScSingleRefToken(aRefData
));
470 ScTokenRef
ScRefTokenHelper::createRefToken(const ScRange
& rRange
)
472 ScComplexRefData aRefData
;
473 aRefData
.InitRange(rRange
);
474 ScTokenRef
pRef(new ScDoubleRefToken(aRefData
));
478 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */