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 
10 #include <grouptokenconverter.hxx>
11 #include <document.hxx>
12 #include <formulacell.hxx>
13 #include <tokenarray.hxx>
14 #include <refdata.hxx>
15 
16 #include <formula/token.hxx>
17 #include <formula/vectortoken.hxx>
18 
19 using 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);
157  mrGroupTokens.AddToken(aTok);
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 (aRef.Ref1.Tab() != aRef.Ref2.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  formula::DoubleVectorRefToken aTok(aArrays, nArrayLength, nRefRowSize, bAbsFirst, bAbsLast);
261  mrGroupTokens.AddToken(aTok);
262  rScope.addRefMessage(mrPos, aAbs.aStart, nRequestedLength, aArrays);
263 
264  if (nArrayLength && !aArrays.empty() && !mxFormulaGroupContext)
265  {
266  //tdf#98880 if the DoubleVectorRefToken relies on the
267  //underlying storage provided by the Document
268  //FormulaGroupContext, take a reference to it here to
269  //ensure that backing storage exists for our lifetime
271  }
272  }
273  break;
274  case svIndex:
275  {
276  if (p->GetOpCode() != ocName)
277  {
278  // May be DB-range or TableRef
280  break;
281  }
282 
283  // Named range.
284  ScRangeName* pNames = mrDoc.GetRangeName();
285  if (!pNames)
286  // This should never fail.
287  return false;
288 
289  ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
290  if (!pRange)
291  // No named range exists by that index.
292  return false;
293 
294  ScTokenArray* pNamedTokens = pRange->GetCode();
295  if (!pNamedTokens)
296  // This named range is empty.
297  return false;
298 
300 
301  if (!convert(*pNamedTokens, rScope))
302  return false;
303 
305  }
306  break;
307  default:
309  }
310  }
311 
312  return true;
313 }
314 
315 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void AssertNoInterpretNeeded(const ScAddress &rPos, SCROW nLength)
Definition: document.cxx:1806
ScGroupTokenConverter(ScTokenArray &rGroupTokens, ScDocument &rDoc, const ScFormulaCell &rCell, const ScAddress &rPos)
ScAddress aStart
Definition: address.hxx:500
ocName
SCROW Row() const
Definition: address.hxx:262
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
SC_DLLPUBLIC ScRangeName * GetRangeName(SCTAB nTab) const
Definition: documen3.cxx:168
ocOpen
std::shared_ptr< sc::FormulaGroupContext > & GetFormulaGroupContext()
Definition: document.cxx:3526
std::shared_ptr< sc::FormulaGroupContext > mxFormulaGroupContext
SC_DLLPUBLIC ScRange toAbs(ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
ScAddress aEnd
Definition: address.hxx:501
bool isSelfReferenceRelative(const ScAddress &rRefPos, SCROW nRelRow)
void CreateStringFromTokenArray(OUString &rFormula)
ScAddress & mrPos
bool convert(const ScTokenArray &rCode, sc::FormulaLogger::GroupScope &rScope)
bool IsDeleted() const
Definition: refdata.cxx:125
const ScFormulaCellGroupRef & GetCellGroup() const
SCTAB Tab() const
Definition: address.hxx:271
formula::VectorRefArray FetchVectorRefArray(const ScAddress &rPos, SCROW nLength)
Definition: document.cxx:1796
void SetCol(SCCOL nColP)
Definition: address.hxx:279
FormulaToken * AddToken(const FormulaToken &)
int i
sal_Int16 SCCOL
Definition: types.hxx:22
ScSingleRefData Ref1
Definition: refdata.hxx:125
svIndex
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
ScSingleRefData Ref2
Definition: refdata.hxx:126
bool IsRowRel() const
Definition: refdata.hxx:68
SC_DLLPUBLIC ScRangeData * findByIndex(sal_uInt16 i) const
Definition: rangenam.cxx:694
SCTAB Tab() const
Definition: refdata.cxx:254
svSingleRef
SCCOL Col() const
Definition: address.hxx:267
ScAddress toAbs(ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
svDoubleRef
virtual formula::FormulaToken * AddOpCode(OpCode eCode) override
Definition: token.cxx:2195
sal_Int32 SCROW
Definition: types.hxx:18
bool isSelfReferenceAbsolute(const ScAddress &rRefPos)
SCROW Row() const
Definition: refdata.cxx:240
void * p
bool IsDeleted() const
Definition: refdata.cxx:567
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
formula::FormulaTokenRef ResolveStaticReference(const ScAddress &rPos)
Definition: document.cxx:1777
ScTokenArray * GetCode()
Definition: rangenam.hxx:120
SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
ocClose
void addRefMessage(const ScAddress &, const ScAddress &, size_t, const formula::VectorRefArray &)
const ScFormulaCell & mrCell
sal_Int16 SCTAB
Definition: types.hxx:23