LibreOffice Module sc (master) 1
grouptokenconverter.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 */
9
11#include <document.hxx>
12#include <formulacell.hxx>
13#include <tokenarray.hxx>
14#include <refdata.hxx>
15
16#include <formula/token.hxx>
18
19using namespace formula;
20
22{
23 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
24 return false;
25
26 SCROW nLen = mrCell.GetCellGroup()->mnLength;
27 SCROW nEndRow = mrPos.Row() + nLen - 1;
28
29 if (nRelRow < 0)
30 {
31 SCROW nTest = nEndRow;
32 nTest += nRelRow;
33 if (nTest >= mrPos.Row())
34 return true;
35 }
36 else if (nRelRow > 0)
37 {
38 SCROW nTest = mrPos.Row(); // top row.
39 nTest += nRelRow;
40 if (nTest <= nEndRow)
41 return true;
42 }
43
44 return false;
45}
46
48{
49 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
50 return false;
51
52 SCROW nLen = mrCell.GetCellGroup()->mnLength;
53 SCROW nEndRow = mrPos.Row() + nLen - 1;
54
55 if (rRefPos.Row() < mrPos.Row())
56 return false;
57
58 if (rRefPos.Row() > nEndRow)
59 return false;
60
61 return true;
62}
63
65{
66 SCROW nLastRow = nRow + nRowLen - 1; // current last row.
67 nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
68 if (nLastRow < (nRow + nRowLen - 1))
69 {
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 nRowLen = nLastRow - nRow + 1;
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 (nRowLen < 0)
80 nRowLen = 0;
81 }
82 else if (nLastRow == 0)
83 // Column is empty.
84 nRowLen = 1;
85
86 return nRowLen;
87}
88
90 ScTokenArray& rGroupTokens, ScDocument& rDoc, const ScFormulaCell& rCell, const ScAddress& rPos) :
91 mrGroupTokens(rGroupTokens),
92 mrDoc(rDoc),
93 mrCell(rCell),
94 mrPos(rPos)
95{
96}
97
99{
100#if 0
101 { // debug to start with:
103 OUStringBuffer aAsString;
104 aComp.CreateStringFromTokenArray(aAsString);
105 }
106#endif
107
108 const SCROW nLen = mrCell.GetCellGroup()->mnLength;
110 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
111 {
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.
116
117 switch (p->GetType())
118 {
119 case svSingleRef:
120 {
121 ScSingleRefData aRef = *p->GetSingleRef();
122 if( aRef.IsDeleted())
123 return false;
124 ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
125 if (aRef.IsRowRel())
126 {
127 if (isSelfReferenceRelative(aRefPos, aRef.Row()))
128 return false;
129
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
134 // length.
135
137 if (nTrimLen)
138 {
139#ifdef DBG_UTIL
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.
148 ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nTrimLen + aRefPos.Row());
149#endif
150 aArray = mrDoc.FetchVectorRefArray(aRefPos, nTrimLen);
151 }
152
153 if (!aArray.isValid())
154 return false;
155
156 formula::SingleVectorRefToken aTok(aArray, nTrimLen);
158 rScope.addRefMessage(mrPos, aRefPos, nLen, aArray);
159
160 if (nTrimLen && !mxFormulaGroupContext)
161 {
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
167 }
168 }
169 else
170 {
171 // Absolute row reference.
172 if (isSelfReferenceAbsolute(aRefPos))
173 return false;
174
176 if (!pNewToken)
177 return false;
178
179 mrGroupTokens.AddToken(*pNewToken);
180 rScope.addRefMessage(mrPos, aRefPos, *pNewToken);
181 }
182 }
183 break;
184 case svDoubleRef:
185 {
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().
191
192 ScComplexRefData aRef = *p->GetDoubleRef();
193 if( aRef.IsDeleted())
194 return false;
195 ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
196
197 // Multiple sheets not handled by vector/matrix.
198 if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
199 return false;
200
201 // Check for self reference.
202 if (aRef.Ref1.IsRowRel())
203 {
204 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
205 return false;
206 }
207 else if (isSelfReferenceAbsolute(aAbs.aStart))
208 return false;
209
210 if (aRef.Ref2.IsRowRel())
211 {
212 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
213 return false;
214 }
215 else if (isSelfReferenceAbsolute(aAbs.aEnd))
216 return false;
217
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;
227 if (!bAbsLast)
228 {
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;
235 }
236
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);
240
241 for (SCCOL i = aAbs.aStart.Col(); i <= aAbs.aEnd.Col(); ++i)
242 {
243 aRefPos.SetCol(i);
245 if (nArrayLength)
246 {
247#ifdef DBG_UTIL
249 ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nArrayLength + aRefPos.Row());
250#endif
251 aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
252 }
253
254 if (!aArray.isValid())
255 return false;
256
257 aArrays.push_back(aArray);
258 }
259
260 std::vector<formula::VectorRefArray> aArraysTmp = aArrays;
261 formula::DoubleVectorRefToken aTok( std::move(aArraysTmp), nArrayLength, nRefRowSize, bAbsFirst, bAbsLast );
263 rScope.addRefMessage(mrPos, aAbs.aStart, nRequestedLength, aArrays);
264
265 if (nArrayLength && !aArrays.empty() && !mxFormulaGroupContext)
266 {
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
272 }
273 }
274 break;
275 case svIndex:
276 {
277 if (p->GetOpCode() != ocName)
278 {
279 // May be DB-range or TableRef
281 break;
282 }
283
284 // Named range.
285 ScRangeName* pNames = mrDoc.GetRangeName();
286 if (!pNames)
287 // This should never fail.
288 return false;
289
290 ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
291 if (!pRange)
292 // No named range exists by that index.
293 return false;
294
295 ScTokenArray* pNamedTokens = pRange->GetCode();
296 if (!pNamedTokens)
297 // This named range is empty.
298 return false;
299
301
302 if (!convert(*pNamedTokens, rScope))
303 return false;
304
306 }
307 break;
308 default:
310 }
311 }
312
313 return true;
314}
315
316/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SCTAB Tab() const
Definition: address.hxx:283
void SetCol(SCCOL nColP)
Definition: address.hxx:291
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
formula::VectorRefArray FetchVectorRefArray(const ScAddress &rPos, SCROW nLength)
Definition: document.cxx:1763
std::shared_ptr< sc::FormulaGroupContext > & GetFormulaGroupContext()
Definition: document.cxx:3533
SC_DLLPUBLIC SCROW GetLastDataRow(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow) const
Return the last non-empty row position in given columns that's no greater than the initial last row p...
Definition: document.cxx:1063
SC_DLLPUBLIC ScRangeName * GetRangeName(SCTAB nTab) const
Definition: documen3.cxx:171
formula::FormulaTokenRef ResolveStaticReference(const ScAddress &rPos)
Definition: document.cxx:1745
void AssertNoInterpretNeeded(const ScAddress &rPos, SCROW nLength)
Definition: document.cxx:1772
const ScFormulaCellGroupRef & GetCellGroup() const
SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
bool convert(const ScTokenArray &rCode, sc::FormulaLogger::GroupScope &rScope)
bool isSelfReferenceAbsolute(const ScAddress &rRefPos)
ScGroupTokenConverter(ScTokenArray &rGroupTokens, ScDocument &rDoc, const ScFormulaCell &rCell, const ScAddress &rPos)
bool isSelfReferenceRelative(const ScAddress &rRefPos, SCROW nRelRow)
const ScFormulaCell & mrCell
std::shared_ptr< sc::FormulaGroupContext > mxFormulaGroupContext
ScTokenArray * GetCode()
Definition: rangenam.hxx:119
SC_DLLPUBLIC ScRangeData * findByIndex(sal_uInt16 i) const
Definition: rangenam.cxx:716
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
virtual formula::FormulaToken * AddOpCode(OpCode eCode) override
Definition: token.cxx:2265
void CreateStringFromTokenArray(OUString &rFormula)
FormulaToken * AddToken(const FormulaToken &)
void addRefMessage(const ScAddress &, const ScAddress &, size_t, const formula::VectorRefArray &)
void * p
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
svIndex
svDoubleRef
svSingleRef
int i
ocClose
ocOpen
ocName
ScAddress & mrPos
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
ScSingleRefData Ref2
Definition: refdata.hxx:125
bool IsDeleted() const
Definition: refdata.cxx:580
ScSingleRefData Ref1
Definition: refdata.hxx:124
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
bool IsDeleted() const
Definition: refdata.cxx:125
SCROW Row() const
Definition: refdata.cxx:240
bool IsRowRel() const
Definition: refdata.hxx:67
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17