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>
32 using namespace formula
;
36 void ScRefTokenHelper::compileRangeRepresentation(
37 vector
<ScTokenRef
>& rRefTokens
, const OUString
& rRangeStr
, ScDocument
& rDoc
,
38 const sal_Unicode cSep
, FormulaGrammar::Grammar eGrammar
, bool bOnly3DRef
)
40 // #i107275# ignore parentheses
41 OUString aRangeStr
= rRangeStr
;
42 while( (aRangeStr
.getLength() >= 2) && (aRangeStr
[ 0 ] == '(') && (aRangeStr
[ aRangeStr
.getLength() - 1 ] == ')') )
43 aRangeStr
= aRangeStr
.copy( 1, aRangeStr
.getLength() - 2 );
45 bool bFailure
= false;
46 sal_Int32 nOffset
= 0;
47 while (nOffset
>= 0 && !bFailure
)
50 ScRangeStringConverter::GetTokenByOffset(aToken
, aRangeStr
, nOffset
, cSep
);
54 ScCompiler
aCompiler(rDoc
, ScAddress(0,0,0), eGrammar
);
55 std::unique_ptr
<ScTokenArray
> pArray(aCompiler
.CompileString(aToken
));
57 // There MUST be exactly one reference per range token and nothing
58 // else, and it MUST be a valid reference, not some #REF!
59 sal_uInt16 nLen
= pArray
->GetLen();
61 continue; // Should a missing range really be allowed?
68 const FormulaToken
* p
= pArray
->FirstToken();
79 const ScSingleRefData
& rRef
= *p
->GetSingleRef();
80 if (!rRef
.Valid(rDoc
))
82 else if (bOnly3DRef
&& !rRef
.IsFlag3D())
88 const ScComplexRefData
& rRef
= *p
->GetDoubleRef();
89 if (!rRef
.Valid(rDoc
))
91 else if (bOnly3DRef
&& !rRef
.Ref1
.IsFlag3D())
95 case svExternalSingleRef
:
97 if (!p
->GetSingleRef()->ValidExternal(rDoc
))
101 case svExternalDoubleRef
:
103 if (!p
->GetDoubleRef()->ValidExternal(rDoc
))
108 if (p
->GetString().isEmpty())
113 if (p
->GetOpCode() == ocName
)
115 ScRangeData
* pNameRange
= rDoc
.FindRangeNameBySheetAndIndex(p
->GetSheet(), p
->GetIndex());
116 if (!pNameRange
->HasReferences())
126 rRefTokens
.emplace_back(p
->Clone());
133 bool ScRefTokenHelper::getRangeFromToken(
134 const ScDocument
* pDoc
,
135 ScRange
& rRange
, const ScTokenRef
& pToken
, const ScAddress
& rPos
, bool bExternal
)
137 StackVar eType
= pToken
->GetType();
138 switch (pToken
->GetType())
141 case svExternalSingleRef
:
143 if ((eType
== svExternalSingleRef
&& !bExternal
) ||
144 (eType
== svSingleRef
&& bExternal
))
147 const ScSingleRefData
& rRefData
= *pToken
->GetSingleRef();
148 rRange
.aStart
= rRefData
.toAbs(*pDoc
, rPos
);
149 rRange
.aEnd
= rRange
.aStart
;
153 case svExternalDoubleRef
:
155 if ((eType
== svExternalDoubleRef
&& !bExternal
) ||
156 (eType
== svDoubleRef
&& bExternal
))
159 const ScComplexRefData
& rRefData
= *pToken
->GetDoubleRef();
160 rRange
= rRefData
.toAbs(*pDoc
, rPos
);
165 if (pToken
->GetOpCode() == ocName
)
167 ScRangeData
* pNameRange
= pDoc
->FindRangeNameBySheetAndIndex(pToken
->GetSheet(), pToken
->GetIndex());
168 if (pNameRange
->IsReference(rRange
, rPos
))
179 void ScRefTokenHelper::getRangeListFromTokens(
180 const ScDocument
* pDoc
, ScRangeList
& rRangeList
, const vector
<ScTokenRef
>& rTokens
, const ScAddress
& rPos
)
182 for (const auto& rToken
: rTokens
)
185 getRangeFromToken(pDoc
, aRange
, rToken
, rPos
);
186 rRangeList
.push_back(aRange
);
190 void ScRefTokenHelper::getTokenFromRange(const ScDocument
* pDoc
, ScTokenRef
& pToken
, const ScRange
& rRange
)
192 ScComplexRefData aData
;
193 aData
.InitRange(rRange
);
194 aData
.Ref1
.SetFlag3D(true);
196 // Display sheet name on 2nd reference only when the 1st and 2nd refs are on
198 aData
.Ref2
.SetFlag3D(rRange
.aStart
.Tab() != rRange
.aEnd
.Tab());
200 pToken
.reset(new ScDoubleRefToken(pDoc
->GetSheetLimits(), aData
));
203 void ScRefTokenHelper::getTokensFromRangeList(const ScDocument
* pDoc
, vector
<ScTokenRef
>& pTokens
, const ScRangeList
& rRanges
)
205 vector
<ScTokenRef
> aTokens
;
206 size_t nCount
= rRanges
.size();
207 aTokens
.reserve(nCount
);
208 for (size_t i
= 0; i
< nCount
; ++i
)
210 const ScRange
& rRange
= rRanges
[i
];
212 ScRefTokenHelper::getTokenFromRange(pDoc
, pToken
, rRange
);
213 aTokens
.push_back(pToken
);
215 pTokens
.swap(aTokens
);
218 bool ScRefTokenHelper::isRef(const ScTokenRef
& pToken
)
220 switch (pToken
->GetType())
224 case svExternalSingleRef
:
225 case svExternalDoubleRef
:
233 bool ScRefTokenHelper::isExternalRef(const ScTokenRef
& pToken
)
235 switch (pToken
->GetType())
237 case svExternalSingleRef
:
238 case svExternalDoubleRef
:
246 bool ScRefTokenHelper::intersects(
247 const ScDocument
* pDoc
,
248 const vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
253 bool bExternal
= isExternalRef(pToken
);
254 sal_uInt16 nFileId
= bExternal
? pToken
->GetIndex() : 0;
257 getRangeFromToken(pDoc
, aRange
, pToken
, rPos
, bExternal
);
259 for (const ScTokenRef
& p
: rTokens
)
264 if (bExternal
!= isExternalRef(p
))
268 getRangeFromToken(pDoc
, aRange2
, p
, rPos
, bExternal
);
270 if (bExternal
&& nFileId
!= p
->GetIndex())
271 // different external file
274 if (aRange
.Intersects(aRange2
))
282 class JoinRefTokenRanges
286 * Insert a new reference token into the existing list of reference tokens,
287 * but in that process, try to join as many adjacent ranges as possible.
289 * @param rTokens existing list of reference tokens
290 * @param rToken new token
292 void operator() (const ScDocument
* pDoc
, vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
294 join(pDoc
, rTokens
, pToken
, rPos
);
300 * Check two 1-dimensional ranges to see if they overlap each other.
302 * @param nMin1 min value of range 1
303 * @param nMax1 max value of range 1
304 * @param nMin2 min value of range 2
305 * @param nMax2 max value of range 2
306 * @param rNewMin min value of new range in case they overlap
307 * @param rNewMax max value of new range in case they overlap
310 static bool overlaps(T nMin1
, T nMax1
, T nMin2
, T nMax2
, T
& rNewMin
, T
& rNewMax
)
312 bool bDisjoint1
= (nMin1
> nMax2
) && (nMin1
- nMax2
> 1);
313 bool bDisjoint2
= (nMin2
> nMax1
) && (nMin2
- nMax1
> 1);
314 if (bDisjoint1
|| bDisjoint2
)
315 // These two ranges cannot be joined. Move on.
318 T nMin
= std::min(nMin1
, nMin2
);
319 T nMax
= std::max(nMax1
, nMax2
);
327 void join(const ScDocument
* pDoc
, vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
329 // Normalize the token to a double reference.
330 ScComplexRefData aData
;
331 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aData
, pToken
))
334 // Get the information of the new token.
335 bool bExternal
= ScRefTokenHelper::isExternalRef(pToken
);
336 sal_uInt16 nFileId
= bExternal
? pToken
->GetIndex() : 0;
337 svl::SharedString aTabName
= bExternal
? pToken
->GetString() : svl::SharedString::getEmptyString();
339 bool bJoined
= false;
340 for (ScTokenRef
& pOldToken
: rTokens
)
342 if (!ScRefTokenHelper::isRef(pOldToken
))
343 // A non-ref token should not have been added here in the first
347 if (bExternal
!= ScRefTokenHelper::isExternalRef(pOldToken
))
348 // External and internal refs don't mix.
353 if (nFileId
!= pOldToken
->GetIndex())
354 // Different external files.
357 if (aTabName
!= pOldToken
->GetString())
358 // Different table names.
362 ScComplexRefData aOldData
;
363 if (!ScRefTokenHelper::getDoubleRefDataFromToken(aOldData
, pOldToken
))
366 ScRange aOld
= aOldData
.toAbs(*pDoc
, rPos
), aNew
= aData
.toAbs(*pDoc
, rPos
);
368 if (aNew
.aStart
.Tab() != aOld
.aStart
.Tab() || aNew
.aEnd
.Tab() != aOld
.aEnd
.Tab())
369 // Sheet ranges differ.
372 if (aOld
.Contains(aNew
))
373 // This new range is part of an existing range. Skip it.
376 bool bSameRows
= (aNew
.aStart
.Row() == aOld
.aStart
.Row()) && (aNew
.aEnd
.Row() == aOld
.aEnd
.Row());
377 bool bSameCols
= (aNew
.aStart
.Col() == aOld
.aStart
.Col()) && (aNew
.aEnd
.Col() == aOld
.aEnd
.Col());
378 ScComplexRefData aNewData
= aOldData
;
379 bool bJoinRanges
= false;
382 SCCOL nNewMin
, nNewMax
;
383 bJoinRanges
= overlaps(
384 aNew
.aStart
.Col(), aNew
.aEnd
.Col(), aOld
.aStart
.Col(), aOld
.aEnd
.Col(),
389 aNew
.aStart
.SetCol(nNewMin
);
390 aNew
.aEnd
.SetCol(nNewMax
);
391 aNewData
.SetRange(pDoc
->GetSheetLimits(), aNew
, rPos
);
396 SCROW nNewMin
, nNewMax
;
397 bJoinRanges
= overlaps(
398 aNew
.aStart
.Row(), aNew
.aEnd
.Row(), aOld
.aStart
.Row(), aOld
.aEnd
.Row(),
403 aNew
.aStart
.SetRow(nNewMin
);
404 aNew
.aEnd
.SetRow(nNewMax
);
405 aNewData
.SetRange(pDoc
->GetSheetLimits(), aNew
, rPos
);
412 pOldToken
.reset(new ScExternalDoubleRefToken(nFileId
, aTabName
, aNewData
));
414 pOldToken
.reset(new ScDoubleRefToken(pDoc
->GetSheetLimits(), aNewData
));
423 if (rTokens
.size() == 1)
424 // There is only one left. No need to do more joining.
427 // Pop the last token from the list, and keep joining recursively.
428 ScTokenRef p
= rTokens
.back();
430 join(pDoc
, rTokens
, p
, rPos
);
433 rTokens
.push_back(pToken
);
439 void ScRefTokenHelper::join(const ScDocument
* pDoc
, vector
<ScTokenRef
>& rTokens
, const ScTokenRef
& pToken
, const ScAddress
& rPos
)
441 JoinRefTokenRanges join
;
442 join(pDoc
, rTokens
, pToken
, rPos
);
445 bool ScRefTokenHelper::getDoubleRefDataFromToken(ScComplexRefData
& rData
, const ScTokenRef
& pToken
)
447 switch (pToken
->GetType())
450 case svExternalSingleRef
:
452 const ScSingleRefData
& r
= *pToken
->GetSingleRef();
454 rData
.Ref1
.SetFlag3D(true);
456 rData
.Ref2
.SetFlag3D(false); // Don't display sheet name on second reference.
460 case svExternalDoubleRef
:
461 rData
= *pToken
->GetDoubleRef();
464 // Not a reference token. Bail out.
470 ScTokenRef
ScRefTokenHelper::createRefToken(const ScDocument
& rDoc
, const ScAddress
& rAddr
)
472 ScSingleRefData aRefData
;
473 aRefData
.InitAddress(rAddr
);
474 ScTokenRef
pRef(new ScSingleRefToken(rDoc
.GetSheetLimits(), aRefData
));
478 ScTokenRef
ScRefTokenHelper::createRefToken(const ScDocument
& rDoc
, const ScRange
& rRange
)
480 ScComplexRefData aRefData
;
481 aRefData
.InitRange(rRange
);
482 ScTokenRef
pRef(new ScDoubleRefToken(rDoc
.GetSheetLimits(), aRefData
));
486 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */