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/.
10 #include <grouptokenconverter.hxx>
11 #include <document.hxx>
12 #include <formulacell.hxx>
13 #include <tokenarray.hxx>
14 #include <refdata.hxx>
16 #include <formula/token.hxx>
17 #include <formula/vectortoken.hxx>
19 using namespace formula
;
21 bool ScGroupTokenConverter::isSelfReferenceRelative(const ScAddress
& rRefPos
, SCROW nRelRow
)
23 if (rRefPos
.Col() != mrPos
.Col() || rRefPos
.Tab() != mrPos
.Tab())
26 SCROW nLen
= mrCell
.GetCellGroup()->mnLength
;
27 SCROW nEndRow
= mrPos
.Row() + nLen
- 1;
31 SCROW nTest
= nEndRow
;
33 if (nTest
>= mrPos
.Row())
38 SCROW nTest
= mrPos
.Row(); // top row.
47 bool ScGroupTokenConverter::isSelfReferenceAbsolute(const ScAddress
& rRefPos
)
49 if (rRefPos
.Col() != mrPos
.Col() || rRefPos
.Tab() != mrPos
.Tab())
52 SCROW nLen
= mrCell
.GetCellGroup()->mnLength
;
53 SCROW nEndRow
= mrPos
.Row() + nLen
- 1;
55 if (rRefPos
.Row() < mrPos
.Row())
58 if (rRefPos
.Row() > nEndRow
)
64 SCROW
ScGroupTokenConverter::trimLength(SCTAB nTab
, SCCOL nCol1
, SCCOL nCol2
, SCROW nRow
, SCROW nRowLen
)
66 SCROW nLastRow
= nRow
+ nRowLen
- 1; // current last row.
67 nLastRow
= mrDoc
.GetLastDataRow(nTab
, nCol1
, nCol2
, nLastRow
);
68 if (nLastRow
< (nRow
+ nRowLen
- 1))
70 // This can end up negative! Was that the original intent, or
71 // is it accidental? Was it not like that originally but the
72 // surrounding conditions changed?
73 const bool bFail
= o3tl::checked_sub(nLastRow
+ 1, nRow
, nRowLen
);
74 // Anyway, let's assume it doesn't make sense to return a
75 // negative value here. But should we then return 0 or 1? In
76 // the "Column is empty" case below, we return 1, why!? And,
77 // at the callsites there are tests for a zero value returned
78 // from this function (but not for a negative one).
79 if (bFail
|| nRowLen
< 0)
82 else if (nLastRow
== 0)
89 ScGroupTokenConverter::ScGroupTokenConverter(
90 ScTokenArray
& rGroupTokens
, ScDocument
& rDoc
, const ScFormulaCell
& rCell
, const ScAddress
& rPos
) :
91 mrGroupTokens(rGroupTokens
),
98 bool ScGroupTokenConverter::convert( const ScTokenArray
& rCode
, sc::FormulaLogger::GroupScope
& rScope
)
101 { // debug to start with:
102 ScCompiler
aComp( &mrDoc
, mrPos
, rCode
, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1
);
103 OUStringBuffer aAsString
;
104 aComp
.CreateStringFromTokenArray(aAsString
);
108 const SCROW nLen
= mrCell
.GetCellGroup()->mnLength
;
109 formula::FormulaTokenArrayPlainIterator
aIter(rCode
);
110 for (const formula::FormulaToken
* p
= aIter
.First(); p
; p
= aIter
.Next())
112 // A reference can be either absolute or relative. If it's absolute,
113 // convert it to a static value token. If relative, convert it to a
114 // vector reference token. Note: we only care about relative vs
115 // absolute reference state for row directions.
117 switch (p
->GetType())
121 ScSingleRefData aRef
= *p
->GetSingleRef();
122 if( aRef
.IsDeleted())
124 ScAddress aRefPos
= aRef
.toAbs(mrDoc
, mrPos
);
127 if (isSelfReferenceRelative(aRefPos
, aRef
.Row()))
130 // Trim data array length to actual data range.
131 SCROW nTrimLen
= trimLength(aRefPos
.Tab(), aRefPos
.Col(), aRefPos
.Col(), aRefPos
.Row(), nLen
);
132 // Fetch double array guarantees that the length of the
133 // returned array equals or greater than the requested
136 formula::VectorRefArray aArray
;
140 // All the necessary Interpret() calls for all the cells
141 // should have been already handled by ScDependantsCalculator
142 // calling HandleRefArrayForParallelism(), and that handling also checks
143 // for cycles etc. Recursively calling Interpret() from here (which shouldn't
144 // happen) could lead to unhandled problems.
145 // Also, because of caching FetchVectorRefArray() fetches values for all rows
146 // up to the maximum one, so check those too.
147 mrDoc
.AssertNoInterpretNeeded(
148 ScAddress(aRefPos
.Col(), 0, aRefPos
.Tab()), nTrimLen
+ aRefPos
.Row());
150 aArray
= mrDoc
.FetchVectorRefArray(aRefPos
, nTrimLen
);
153 if (!aArray
.isValid())
156 formula::SingleVectorRefToken
aTok(aArray
, nTrimLen
);
157 mrGroupTokens
.AddToken(aTok
);
158 rScope
.addRefMessage(mrPos
, aRefPos
, nLen
, aArray
);
160 if (nTrimLen
&& !mxFormulaGroupContext
)
162 //tdf#98880 if the SingleVectorRefToken relies on the
163 //underlying storage provided by the Document
164 //FormulaGroupContext, take a reference to it here to
165 //ensure that backing storage exists for our lifetime
166 mxFormulaGroupContext
= mrDoc
.GetFormulaGroupContext();
171 // Absolute row reference.
172 if (isSelfReferenceAbsolute(aRefPos
))
175 formula::FormulaTokenRef pNewToken
= mrDoc
.ResolveStaticReference(aRefPos
);
179 mrGroupTokens
.AddToken(*pNewToken
);
180 rScope
.addRefMessage(mrPos
, aRefPos
, *pNewToken
);
186 // This code may break in case of implicit intersection, leading to unnecessarily large
187 // matrix operations and possibly incorrect results (=C:C/D:D). That is handled by
188 // having ScCompiler check that there are no possible implicit intersections.
189 // Additionally some functions such as INDEX() and OFFSET() require a reference,
190 // that is handled by denylisting those opcodes in ScTokenArray::CheckToken().
192 ScComplexRefData aRef
= *p
->GetDoubleRef();
193 if( aRef
.IsDeleted())
195 ScRange aAbs
= aRef
.toAbs(mrDoc
, mrPos
);
197 // Multiple sheets not handled by vector/matrix.
198 if (aAbs
.aStart
.Tab() != aAbs
.aEnd
.Tab())
201 // Check for self reference.
202 if (aRef
.Ref1
.IsRowRel())
204 if (isSelfReferenceRelative(aAbs
.aStart
, aRef
.Ref1
.Row()))
207 else if (isSelfReferenceAbsolute(aAbs
.aStart
))
210 if (aRef
.Ref2
.IsRowRel())
212 if (isSelfReferenceRelative(aAbs
.aEnd
, aRef
.Ref2
.Row()))
215 else if (isSelfReferenceAbsolute(aAbs
.aEnd
))
218 // Row reference is relative.
219 bool bAbsFirst
= !aRef
.Ref1
.IsRowRel();
220 bool bAbsLast
= !aRef
.Ref2
.IsRowRel();
221 ScAddress aRefPos
= aAbs
.aStart
;
222 size_t nCols
= aAbs
.aEnd
.Col() - aAbs
.aStart
.Col() + 1;
223 std::vector
<formula::VectorRefArray
> aArrays
;
224 aArrays
.reserve(nCols
);
225 SCROW nRefRowSize
= aAbs
.aEnd
.Row() - aAbs
.aStart
.Row() + 1;
226 SCROW nArrayLength
= nRefRowSize
;
229 // range end position is relative. Extend the array length.
230 SCROW nLastRefRowOffset
= aAbs
.aEnd
.Row() - mrPos
.Row();
231 SCROW nLastRefRow
= mrPos
.Row() + nLen
- 1 + nLastRefRowOffset
;
232 SCROW nNewLength
= nLastRefRow
- aAbs
.aStart
.Row() + 1;
233 if (nNewLength
> nArrayLength
)
234 nArrayLength
= nNewLength
;
237 // Trim trailing empty rows.
238 SCROW nRequestedLength
= nArrayLength
; // keep the original length.
239 nArrayLength
= trimLength(aRefPos
.Tab(), aAbs
.aStart
.Col(), aAbs
.aEnd
.Col(), aRefPos
.Row(), nArrayLength
);
241 for (SCCOL i
= aAbs
.aStart
.Col(); i
<= aAbs
.aEnd
.Col(); ++i
)
244 formula::VectorRefArray aArray
;
248 mrDoc
.AssertNoInterpretNeeded(
249 ScAddress(aRefPos
.Col(), 0, aRefPos
.Tab()), nArrayLength
+ aRefPos
.Row());
251 aArray
= mrDoc
.FetchVectorRefArray(aRefPos
, nArrayLength
);
254 if (!aArray
.isValid())
257 aArrays
.push_back(aArray
);
260 std::vector
<formula::VectorRefArray
> aArraysTmp
= aArrays
;
261 formula::DoubleVectorRefToken
aTok( std::move(aArraysTmp
), nArrayLength
, nRefRowSize
, bAbsFirst
, bAbsLast
);
262 mrGroupTokens
.AddToken(aTok
);
263 rScope
.addRefMessage(mrPos
, aAbs
.aStart
, nRequestedLength
, aArrays
);
265 if (nArrayLength
&& !aArrays
.empty() && !mxFormulaGroupContext
)
267 //tdf#98880 if the DoubleVectorRefToken relies on the
268 //underlying storage provided by the Document
269 //FormulaGroupContext, take a reference to it here to
270 //ensure that backing storage exists for our lifetime
271 mxFormulaGroupContext
= mrDoc
.GetFormulaGroupContext();
277 if (p
->GetOpCode() != ocName
)
279 // May be DB-range or TableRef
280 mrGroupTokens
.AddToken(*p
);
285 ScRangeName
* pNames
= mrDoc
.GetRangeName();
287 // This should never fail.
290 ScRangeData
* pRange
= pNames
->findByIndex(p
->GetIndex());
292 // No named range exists by that index.
295 ScTokenArray
* pNamedTokens
= pRange
->GetCode();
297 // This named range is empty.
300 mrGroupTokens
.AddOpCode(ocOpen
);
302 if (!convert(*pNamedTokens
, rScope
))
305 mrGroupTokens
.AddOpCode(ocClose
);
309 mrGroupTokens
.AddToken(*p
);
316 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */