Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / grouptokenconverter.cxx
blob18b2807515ee2a2898d29045a08db587aebddbfc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
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())
24 return false;
26 SCROW nLen = mrCell.GetCellGroup()->mnLength;
27 SCROW nEndRow = mrPos.Row() + nLen - 1;
29 if (nRelRow < 0)
31 SCROW nTest = nEndRow;
32 nTest += nRelRow;
33 if (nTest >= mrPos.Row())
34 return true;
36 else if (nRelRow > 0)
38 SCROW nTest = mrPos.Row(); // top row.
39 nTest += nRelRow;
40 if (nTest <= nEndRow)
41 return true;
44 return false;
47 bool ScGroupTokenConverter::isSelfReferenceAbsolute(const ScAddress& rRefPos)
49 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
50 return false;
52 SCROW nLen = mrCell.GetCellGroup()->mnLength;
53 SCROW nEndRow = mrPos.Row() + nLen - 1;
55 if (rRefPos.Row() < mrPos.Row())
56 return false;
58 if (rRefPos.Row() > nEndRow)
59 return false;
61 return true;
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)
80 nRowLen = 0;
82 else if (nLastRow == 0)
83 // Column is empty.
84 nRowLen = 1;
86 return nRowLen;
89 ScGroupTokenConverter::ScGroupTokenConverter(
90 ScTokenArray& rGroupTokens, ScDocument& rDoc, const ScFormulaCell& rCell, const ScAddress& rPos) :
91 mrGroupTokens(rGroupTokens),
92 mrDoc(rDoc),
93 mrCell(rCell),
94 mrPos(rPos)
98 bool ScGroupTokenConverter::convert( const ScTokenArray& rCode, sc::FormulaLogger::GroupScope& rScope )
100 #if 0
101 { // debug to start with:
102 ScCompiler aComp( &mrDoc, mrPos, rCode, formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
103 OUStringBuffer aAsString;
104 aComp.CreateStringFromTokenArray(aAsString);
106 #endif
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())
119 case svSingleRef:
121 ScSingleRefData aRef = *p->GetSingleRef();
122 if( aRef.IsDeleted())
123 return false;
124 ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
125 if (aRef.IsRowRel())
127 if (isSelfReferenceRelative(aRefPos, aRef.Row()))
128 return false;
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.
136 formula::VectorRefArray aArray;
137 if (nTrimLen)
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.
147 mrDoc.AssertNoInterpretNeeded(
148 ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nTrimLen + aRefPos.Row());
149 #endif
150 aArray = mrDoc.FetchVectorRefArray(aRefPos, nTrimLen);
153 if (!aArray.isValid())
154 return false;
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();
169 else
171 // Absolute row reference.
172 if (isSelfReferenceAbsolute(aRefPos))
173 return false;
175 formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
176 if (!pNewToken)
177 return false;
179 mrGroupTokens.AddToken(*pNewToken);
180 rScope.addRefMessage(mrPos, aRefPos, *pNewToken);
183 break;
184 case svDoubleRef:
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())
194 return false;
195 ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
197 // Multiple sheets not handled by vector/matrix.
198 if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
199 return false;
201 // Check for self reference.
202 if (aRef.Ref1.IsRowRel())
204 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
205 return false;
207 else if (isSelfReferenceAbsolute(aAbs.aStart))
208 return false;
210 if (aRef.Ref2.IsRowRel())
212 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
213 return false;
215 else if (isSelfReferenceAbsolute(aAbs.aEnd))
216 return false;
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)
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)
243 aRefPos.SetCol(i);
244 formula::VectorRefArray aArray;
245 if (nArrayLength)
247 #ifdef DBG_UTIL
248 mrDoc.AssertNoInterpretNeeded(
249 ScAddress(aRefPos.Col(), 0, aRefPos.Tab()), nArrayLength + aRefPos.Row());
250 #endif
251 aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
254 if (!aArray.isValid())
255 return false;
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();
274 break;
275 case svIndex:
277 if (p->GetOpCode() != ocName)
279 // May be DB-range or TableRef
280 mrGroupTokens.AddToken(*p);
281 break;
284 // Named range.
285 ScRangeName* pNames = mrDoc.GetRangeName();
286 if (!pNames)
287 // This should never fail.
288 return false;
290 ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
291 if (!pRange)
292 // No named range exists by that index.
293 return false;
295 ScTokenArray* pNamedTokens = pRange->GetCode();
296 if (!pNamedTokens)
297 // This named range is empty.
298 return false;
300 mrGroupTokens.AddOpCode(ocOpen);
302 if (!convert(*pNamedTokens, rScope))
303 return false;
305 mrGroupTokens.AddOpCode(ocClose);
307 break;
308 default:
309 mrGroupTokens.AddToken(*p);
313 return true;
316 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */