LibreOffice Module sc (master)  1
formulacell.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_feature_opencl.h>
21 
22 #include <sal/config.h>
23 #include <sal/log.hxx>
24 #include <osl/diagnose.h>
25 
26 #include <cassert>
27 #include <cstdlib>
28 
29 #include <formulacell.hxx>
30 #include <grouptokenconverter.hxx>
31 
32 #include <compiler.hxx>
33 #include <document.hxx>
34 #include <cellvalue.hxx>
35 #include <interpre.hxx>
36 #include <macromgr.hxx>
37 #include <refupdat.hxx>
38 #include <recursionhelper.hxx>
39 #include <docoptio.hxx>
40 #include <rangenam.hxx>
41 #include <rangelst.hxx>
42 #include <dbdata.hxx>
43 #include <progress.hxx>
44 #include <scmatrix.hxx>
45 #include <rechead.hxx>
46 #include <scitems.hxx>
47 #include <validat.hxx>
48 #include <editutil.hxx>
49 #include <chgtrack.hxx>
50 #include <tokenarray.hxx>
51 
53 #include <editeng/editobj.hxx>
54 #include <tools/cpuid.hxx>
55 #include <formula/errorcodes.hxx>
56 #include <svl/intitem.hxx>
57 #include <svl/numformat.hxx>
58 #include <formulagroup.hxx>
59 #include <listenercontext.hxx>
60 #include <types.hxx>
61 #include <scopetools.hxx>
62 #include <refupdatecontext.hxx>
63 #include <tokenstringcontext.hxx>
64 #include <refhint.hxx>
65 #include <listenerquery.hxx>
66 #include <listenerqueryids.hxx>
67 #include <grouparealistener.hxx>
68 #include <formulalogger.hxx>
69 #include <com/sun/star/sheet/FormulaLanguage.hpp>
70 
71 #if HAVE_FEATURE_OPENCL
72 #include <opencl/openclwrapper.hxx>
73 #endif
74 
75 #include <memory>
76 #include <map>
77 
78 using namespace formula;
79 
80 #define DEBUG_CALCULATION 0
81 #if DEBUG_CALCULATION
82 static bool bDebugCalculationActive = false; // Set to true for global active init,
83 static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like
84 
85 struct DebugCalculationEntry
86 {
87  ScAddress maPos;
88  OUString maResult;
89  const ScDocument& mrDoc;
90  sal_uInt32 mnGroup;
91  sal_uInt16 mnRecursion;
92 
93  DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) :
94  maPos(rPos),
95  mrDoc(rDoc),
96  mnGroup(nGroup),
97  mnRecursion(rDoc.GetRecursionHelper().GetRecursionCount())
98  {
99  }
100 };
101 
108 static struct DebugCalculation
109 {
110  std::vector< DebugCalculationEntry > mvPos;
111  std::vector< DebugCalculationEntry > mvResults;
112  ScAddress maTrigger;
113  sal_uInt32 mnGroup;
114  bool mbActive;
115  bool mbSwitchOff;
116  bool mbPrint;
117  bool mbPrintResults;
118 
119  DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
120  mbPrint(true), mbPrintResults(false) {}
121 
123  void print() const
124  {
125  for (auto const& it : mvPos)
126  {
127  OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc) +
128  " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
129  fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
130  }
131  fprintf( stderr, "%s", "END\n");
132  }
133 
135  void printResults() const
136  {
137  for (auto const& it : mvResults)
138  {
139  OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc));
140  aStr += " (" + it.maResult + ")";
141  fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
142  }
143  fprintf( stderr, "%s", "END\n");
144  }
145 
146  void storeResult( const svl::SharedString& rStr )
147  {
148  if (mbActive && !mvPos.empty())
149  mvPos.back().maResult = "\"" + rStr.getString() + "\"";
150  }
151 
152  void storeResult( const double& fVal )
153  {
154  if (mbActive && !mvPos.empty())
155  mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true);
156  }
157 
158  void storeResultError( FormulaError nErr )
159  {
160  if (mbActive && !mvPos.empty())
161  mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
162  }
163 
164  void enterGroup()
165  {
166  ++mnGroup;
167  }
168 
169  void leaveGroup()
170  {
171  --mnGroup;
172  }
173 
174 } aDC;
175 
176 struct DebugCalculationStacker
177 {
178  DebugCalculationStacker( const ScAddress& rPos, ScDocument& rDoc )
179  {
180  if (!aDC.mbActive && rPos == aDC.maTrigger)
181  aDC.mbActive = aDC.mbSwitchOff = true;
182  if (aDC.mbActive)
183  {
184  aDC.mvPos.push_back( DebugCalculationEntry( rPos, rDoc, aDC.mnGroup));
185  aDC.mbPrint = true;
186  }
187  }
188 
189  ~DebugCalculationStacker()
190  {
191  if (aDC.mbActive)
192  {
193  if (!aDC.mvPos.empty())
194  {
195  if (aDC.mbPrint)
196  {
197  aDC.print();
198  aDC.mbPrint = false;
199  }
200  if (aDC.mbPrintResults)
201  {
202  // Store results until final result is available, reversing order.
203  aDC.mvResults.push_back( aDC.mvPos.back());
204  }
205  aDC.mvPos.pop_back();
206  if (aDC.mbPrintResults && aDC.mvPos.empty())
207  {
208  aDC.printResults();
209  std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
210  }
211  if (aDC.mbSwitchOff && aDC.mvPos.empty())
212  aDC.mbActive = false;
213  }
214  }
215  }
216 };
217 #endif
218 
219 namespace {
220 
221 // More or less arbitrary, of course all recursions must fit into available
222 // stack space (which is what on all systems we don't know yet?). Choosing a
223 // lower value may be better than trying a much higher value that also isn't
224 // sufficient but temporarily leads to high memory consumption. On the other
225 // hand, if the value fits all recursions, execution is quicker as no resumes
226 // are necessary. Could be made a configurable option.
227 // Allow for a year's calendar (366).
228 const sal_uInt16 MAXRECURSION = 400;
229 
230 typedef SCCOLROW(*DimensionSelector)(const ScDocument&, const ScAddress&, const ScSingleRefData&);
231 
232 SCCOLROW lcl_GetCol(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
233 {
234  return rData.toAbs(rDoc, rPos).Col();
235 }
236 
237 SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
238 {
239  return rData.toAbs(rDoc, rPos).Row();
240 }
241 
242 SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
243 {
244  return rData.toAbs(rDoc, rPos).Tab();
245 }
246 
249 bool
250 lcl_checkRangeDimension(
251  const ScDocument& rDoc,
252  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
253  const DimensionSelector aWhich)
254 {
255  return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) &&
256  aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2);
257 }
258 
259 bool
260 lcl_checkRangeDimensions(
261  const ScDocument& rDoc,
262  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
263  bool& bCol, bool& bRow, bool& bTab)
264 {
265  const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol));
266  const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow));
267  const bool bSameTabs(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetTab));
268 
269  // Test if exactly two dimensions are equal
270  if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
271  {
272  bCol = !bSameCols;
273  bRow = !bSameRows;
274  bTab = !bSameTabs;
275  return true;
276  }
277  return false;
278 }
279 
283 bool
284 lcl_checkRangeDimensions(
285  const ScDocument& rDoc, const ScAddress& rPos,
286  const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
287  const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
288  bool& bCol, bool& bRow, bool& bTab)
289 {
290  std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
291  ++aCur;
292  const SingleDoubleRefProvider aRef(**rBegin);
293  bool bOk(false);
294  {
295  const SingleDoubleRefProvider aRefCur(**aCur);
296  bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bCol, bRow, bTab);
297  }
298  while (bOk && aCur != rEnd)
299  {
300  const SingleDoubleRefProvider aRefCur(**aCur);
301  bool bColTmp(false);
302  bool bRowTmp(false);
303  bool bTabTmp(false);
304  bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
305  bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
306  ++aCur;
307  }
308 
309  return bOk && aCur == rEnd;
310 }
311 
312 class LessByReference
313 {
314  const ScDocument& mrDoc;
315  ScAddress maPos;
316  DimensionSelector maFunc;
317 public:
318  LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
319  mrDoc(rDoc), maPos(rPos), maFunc(rFunc) {}
320 
321  bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
322  {
323  const SingleDoubleRefProvider aRef1(*pRef1);
324  const SingleDoubleRefProvider aRef2(*pRef2);
325  return maFunc(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1);
326  }
327 };
328 
334 class AdjacentByReference
335 {
336  const ScDocument& mrDoc;
337  ScAddress maPos;
338  DimensionSelector maFunc;
339 public:
340  AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) :
341  mrDoc(rDoc), maPos(rPos), maFunc(aFunc) {}
342 
343  bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
344  {
345  const SingleDoubleRefProvider aRef1(*p1);
346  const SingleDoubleRefProvider aRef2(*p2);
347  return maFunc(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1;
348  }
349 };
350 
351 bool
352 lcl_checkIfAdjacent(
353  const ScDocument& rDoc,
354  const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
355 {
356  auto aBegin(rReferences.cbegin());
357  auto aEnd(rReferences.cend());
358  auto aBegin1(aBegin);
359  ++aBegin1;
360  --aEnd;
361  return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rDoc, rPos, aWhich));
362 }
363 
364 void
365 lcl_fillRangeFromRefList(
366  const ScDocument& rDoc,
367  const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
368 {
369  const ScSingleRefData aStart(
370  SingleDoubleRefProvider(*rReferences.front()).Ref1);
371  rRange.aStart = aStart.toAbs(rDoc, aPos);
372  const ScSingleRefData aEnd(
373  SingleDoubleRefProvider(*rReferences.back()).Ref2);
374  rRange.aEnd = aEnd.toAbs(rDoc, aPos);
375 }
376 
377 bool
378 lcl_refListFormsOneRange(
379  const ScDocument& rDoc,
380  const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
381  ScRange& rRange)
382 {
383  if (rReferences.size() == 1)
384  {
385  lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
386  return true;
387  }
388 
389  bool bCell(false);
390  bool bRow(false);
391  bool bTab(false);
392  if (lcl_checkRangeDimensions(rDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
393  {
394  DimensionSelector aWhich;
395  if (bCell)
396  {
397  aWhich = lcl_GetCol;
398  }
399  else if (bRow)
400  {
401  aWhich = lcl_GetRow;
402  }
403  else if (bTab)
404  {
405  aWhich = lcl_GetTab;
406  }
407  else
408  {
409  OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
410  aWhich = lcl_GetRow; // initialize to avoid warning
411  }
412 
413  // Sort the references by start of range
414  std::sort(rReferences.begin(), rReferences.end(), LessByReference(rDoc, rPos, aWhich));
415  if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich))
416  {
417  lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
418  return true;
419  }
420  }
421  return false;
422 }
423 
424 bool lcl_isReference(const FormulaToken& rToken)
425 {
426  return
427  rToken.GetType() == svSingleRef ||
428  rToken.GetType() == svDoubleRef;
429 }
430 
431 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc,
432  const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
433 {
434  ScRangeData* pRangeData = nullptr;
435  SCTAB nSheet = pToken->GetSheet();
436  sal_uInt16 nIndex = pToken->GetIndex();
437  if (!rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
438  return; // nothing to do
439 
440  if (!pRangeData)
441  {
442  // If this happened we have a real problem.
443  pToken->SetIndex(0);
444  assert(!"inserting the range name should not fail");
445  return;
446  }
447 
448  pToken->SetIndex(nIndex);
449  pToken->SetSheet(nSheet);
450 }
451 
452 void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc)
453 {
454  ScDBCollection* pOldDBCollection = rOldDoc.GetDBCollection();
455  if (!pOldDBCollection)
456  return;//strange error case, don't do anything
457  ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
458  ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
459  if (!pDBData)
460  return; //invalid index
461  OUString aDBName = pDBData->GetUpperName();
462 
463  //search in new document
464  ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
465  if (!pNewDBCollection)
466  {
467  rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(rNewDoc)));
468  pNewDBCollection = rNewDoc.GetDBCollection();
469  }
470  ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
471  ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
472  if (!pNewDBData)
473  {
474  pNewDBData = new ScDBData(*pDBData);
475  bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
476  assert(ins); (void)ins;
477  }
478  pToken->SetIndex(pNewDBData->GetIndex());
479 }
480 
481 }
482 
484 {
485  if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
486  return maRange.aStart.Tab() < r.maRange.aStart.Tab();
487  if (maRange.aStart.Col() != r.maRange.aStart.Col())
488  return maRange.aStart.Col() < r.maRange.aStart.Col();
489  if (maRange.aStart.Row() != r.maRange.aStart.Row())
490  return maRange.aStart.Row() < r.maRange.aStart.Row();
491  if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
492  return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
493  if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
494  return maRange.aEnd.Col() < r.maRange.aEnd.Col();
495  if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
496  return maRange.aEnd.Row() < r.maRange.aEnd.Row();
497  if (mbStartFixed != r.mbStartFixed)
498  return r.mbStartFixed;
499  if (mbEndFixed != r.mbEndFixed)
500  return r.mbEndFixed;
501 
502  return false;
503 }
504 
506  mnRefCount(0),
507  mpTopCell(nullptr),
508  mnLength(0),
509  mnWeight(0),
510  mnFormatType(SvNumFormatType::NUMBER),
511  mbInvariant(false),
512  mbSubTotal(false),
513  mbPartOfCycle(false),
514  meCalcState(sc::GroupCalcEnabled)
515 {
516 }
517 
519 {
520 }
521 
523 {
524  mpCode = rCode.CloneValue();
525  mbInvariant = mpCode->IsInvariant();
526  mpCode->GenHash();
527 }
528 
530  ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
531 {
532  if (!mpCode)
533  return;
534 
535  if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
536  {
537  bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
538  ScCompiler aComp(rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
539  mbSubTotal = aComp.CompileTokenArray();
540  mnFormatType = aComp.GetNumFormatType();
541  }
542  else
543  {
544  mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
545  }
546 }
547 
549  ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
550 {
551  AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
552 
553  AreaListenersType::iterator it = m_AreaListeners.lower_bound(aKey);
554  if (it == m_AreaListeners.end() || m_AreaListeners.key_comp()(aKey, it->first))
555  {
556  // Insert a new one.
557  it = m_AreaListeners.insert(
558  it, std::make_pair(aKey, std::make_unique<sc::FormulaGroupAreaListener>(
559  rRange, (*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed)));
560  }
561 
562  return it->second.get();
563 }
564 
566 {
567  for (const auto& rEntry : m_AreaListeners)
568  {
569  sc::FormulaGroupAreaListener *const pListener = rEntry.second.get();
570  ScRange aListenRange = pListener->getListeningRange();
571  // This "always listen" special range is never grouped.
572  bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
573  rDoc.EndListeningArea(aListenRange, bGroupListening, pListener);
574  }
575 
576  m_AreaListeners.clear();
577 }
578 
579 ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos ) :
580  bDirty(false),
581  bTableOpDirty(false),
582  bChanged(false),
583  bRunning(false),
584  bCompile(false),
585  bSubTotal(false),
586  bIsIterCell(false),
587  bInChangeTrack(false),
588  bNeedListening(false),
589  mbNeedsNumberFormat(false),
590  mbAllowNumberFormatChange(false),
591  mbPostponedDirty(false),
592  mbIsExtRef(false),
593  mbSeenInPath(false),
594  mbFreeFlying(false),
595  cMatrixFlag(ScMatrixMode::NONE),
596  nSeenInIteration(0),
597  nFormatType(SvNumFormatType::NUMBER),
598  eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
599  pCode(new ScTokenArray(rDoc)),
600  rDocument(rDoc),
601  pPrevious(nullptr),
602  pNext(nullptr),
603  pPreviousTrack(nullptr),
604  pNextTrack(nullptr),
605  aPos(rPos)
606 {
607 }
608 
609 ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos,
610  const OUString& rFormula,
611  const FormulaGrammar::Grammar eGrammar,
612  ScMatrixMode cMatInd ) :
613  bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
614  bTableOpDirty( false ),
615  bChanged( false ),
616  bRunning( false ),
617  bCompile( false ),
618  bSubTotal( false ),
619  bIsIterCell( false ),
620  bInChangeTrack( false ),
621  bNeedListening( false ),
622  mbNeedsNumberFormat( false ),
623  mbAllowNumberFormatChange(false),
624  mbPostponedDirty(false),
625  mbIsExtRef(false),
626  mbSeenInPath(false),
627  mbFreeFlying(false),
628  cMatrixFlag ( cMatInd ),
629  nSeenInIteration(0),
630  nFormatType ( SvNumFormatType::NUMBER ),
631  eTempGrammar( eGrammar),
632  pCode( nullptr ),
633  rDocument( rDoc ),
634  pPrevious(nullptr),
635  pNext(nullptr),
636  pPreviousTrack(nullptr),
637  pNextTrack(nullptr),
638  aPos(rPos)
639 {
640  Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
641  if (!pCode)
642  // We need to have a non-NULL token array instance at all times.
643  pCode = new ScTokenArray(rDoc);
644 }
645 
647  ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray,
648  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
649  bDirty( true ),
650  bTableOpDirty( false ),
651  bChanged( false ),
652  bRunning( false ),
653  bCompile( false ),
654  bSubTotal( false ),
655  bIsIterCell( false ),
656  bInChangeTrack( false ),
657  bNeedListening( false ),
658  mbNeedsNumberFormat( false ),
659  mbAllowNumberFormatChange(false),
660  mbPostponedDirty(false),
661  mbIsExtRef(false),
662  mbSeenInPath(false),
663  mbFreeFlying(false),
664  cMatrixFlag ( cMatInd ),
665  nSeenInIteration(0),
666  nFormatType ( SvNumFormatType::NUMBER ),
667  eTempGrammar( eGrammar),
668  pCode(pArray.release()),
669  rDocument( rDoc ),
670  pPrevious(nullptr),
671  pNext(nullptr),
672  pPreviousTrack(nullptr),
673  pNextTrack(nullptr),
674  aPos(rPos)
675 {
676  assert(pCode); // Never pass a NULL pointer here.
677 
678  pCode->Finalize(); // Reduce memory usage if needed.
679 
680  // Generate RPN token array.
681  if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
682  {
684  bSubTotal = aComp.CompileTokenArray();
685  nFormatType = aComp.GetNumFormatType();
686  }
687  else
688  {
690  bSubTotal = true;
691  }
692 
693  if (bSubTotal)
695 
696  pCode->GenHash();
697 }
698 
700  ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray,
701  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
702  bDirty( true ),
703  bTableOpDirty( false ),
704  bChanged( false ),
705  bRunning( false ),
706  bCompile( false ),
707  bSubTotal( false ),
708  bIsIterCell( false ),
709  bInChangeTrack( false ),
710  bNeedListening( false ),
711  mbNeedsNumberFormat( false ),
712  mbAllowNumberFormatChange(false),
713  mbPostponedDirty(false),
714  mbIsExtRef(false),
715  mbSeenInPath(false),
716  mbFreeFlying(false),
717  cMatrixFlag ( cMatInd ),
718  nSeenInIteration(0),
719  nFormatType ( SvNumFormatType::NUMBER ),
720  eTempGrammar( eGrammar),
721  pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
722  rDocument( rDoc ),
723  pPrevious(nullptr),
724  pNext(nullptr),
725  pPreviousTrack(nullptr),
726  pNextTrack(nullptr),
727  aPos(rPos)
728 {
729  // RPN array generation
730  if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
731  {
733  bSubTotal = aComp.CompileTokenArray();
734  nFormatType = aComp.GetNumFormatType();
735  }
736  else
737  {
739  bSubTotal = true;
740  }
741 
742  if (bSubTotal)
744 
745  pCode->GenHash();
746 }
747 
749  ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
750  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
751  mxGroup(xGroup),
752  bDirty(true),
753  bTableOpDirty( false ),
754  bChanged( false ),
755  bRunning( false ),
756  bCompile( false ),
757  bSubTotal(xGroup->mbSubTotal),
758  bIsIterCell( false ),
759  bInChangeTrack( false ),
760  bNeedListening( false ),
761  mbNeedsNumberFormat( false ),
762  mbAllowNumberFormatChange(false),
763  mbPostponedDirty(false),
764  mbIsExtRef(false),
765  mbSeenInPath(false),
766  mbFreeFlying(false),
767  cMatrixFlag ( cInd ),
768  nSeenInIteration(0),
769  nFormatType(xGroup->mnFormatType),
770  eTempGrammar( eGrammar),
771  pCode(xGroup->mpCode ? &*xGroup->mpCode : new ScTokenArray(rDoc)),
772  rDocument( rDoc ),
773  pPrevious(nullptr),
774  pNext(nullptr),
775  pPreviousTrack(nullptr),
776  pNextTrack(nullptr),
777  aPos(rPos)
778 {
779  if (bSubTotal)
781 }
782 
783 ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
784  bDirty( rCell.bDirty ),
785  bTableOpDirty( false ),
786  bChanged( rCell.bChanged ),
787  bRunning( false ),
788  bCompile( rCell.bCompile ),
789  bSubTotal( rCell.bSubTotal ),
790  bIsIterCell( false ),
791  bInChangeTrack( false ),
792  bNeedListening( false ),
793  mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
794  mbAllowNumberFormatChange(false),
795  mbPostponedDirty(false),
796  mbIsExtRef(false),
797  mbSeenInPath(false),
798  mbFreeFlying(false),
799  cMatrixFlag ( rCell.cMatrixFlag ),
800  nSeenInIteration(0),
801  nFormatType( rCell.nFormatType ),
802  aResult( rCell.aResult ),
803  eTempGrammar( rCell.eTempGrammar),
804  rDocument( rDoc ),
805  pPrevious(nullptr),
806  pNext(nullptr),
807  pPreviousTrack(nullptr),
808  pNextTrack(nullptr),
809  aPos(rPos)
810 {
811  pCode = rCell.pCode->Clone().release();
812 
813  // set back any errors and recompile
814  // not in the Clipboard - it must keep the received error flag
815  // Special Length=0: as bad cells are generated, then they are also retained
816  if ( pCode->GetCodeError() != FormulaError::NONE && !rDocument.IsClipboard() && pCode->GetLen() )
817  {
818  pCode->SetCodeError( FormulaError::NONE );
819  bCompile = true;
820  }
821  // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
822  bool bCompileLater = false;
823  bool bClipMode = rCell.rDocument.IsClipboard();
824 
825  //update ScNameTokens
826  if (!rDocument.IsClipOrUndo() || rDoc.IsUndo())
827  {
828  if (!rDocument.IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
829  {
830  bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
831  formula::FormulaToken* pToken = nullptr;
833  while((pToken = aIter.GetNextName())!= nullptr)
834  {
835  OpCode eOpCode = pToken->GetOpCode();
836  if (eOpCode == ocName)
837  adjustRangeName(pToken, rDoc, rCell.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
838  else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
839  adjustDBRange(pToken, rDoc, rCell.rDocument);
840  }
841  }
842 
843  bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool();
844  if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
845  {
846  pCode->ReadjustAbsolute3DReferences(rCell.rDocument, rDoc, rCell.aPos);
847  }
848 
849  pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs );
850  }
851 
852  if (!rDocument.IsClipOrUndo())
853  {
857  }
858 
859  if( !bCompile )
860  { // Name references with references and ColRowNames
862  for (;;)
863  {
865  if (!t || bCompile)
866  break;
867  if ( t->IsExternalRef() )
868  {
869  // External name, cell, and area references.
870  bCompile = true;
871  }
872  else if ( t->GetType() == svIndex )
873  {
874  const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
875  if( pRangeData )
876  {
877  if( pRangeData->HasReferences() )
878  bCompile = true;
879  }
880  else
881  bCompile = true; // invalid reference!
882  }
883  else if ( t->GetOpCode() == ocColRowName )
884  {
885  bCompile = true; // new lookup needed
886  bCompileLater = bClipMode;
887  }
888  }
889  }
890  if( bCompile )
891  {
892  if ( !bCompileLater && bClipMode )
893  {
894  // Merging ranges needs the actual positions after UpdateReference.
895  // ColRowNames and TableRefs need new lookup after positions are
896  // adjusted.
897  bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
899  }
900  if ( !bCompileLater )
901  {
902  // bNoListening, not at all if in Clipboard/Undo,
903  // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
904  CompileTokenArray( true );
905  }
906  }
907 
908  if( nCloneFlags & ScCloneFlags::StartListening )
909  StartListeningTo( rDoc );
910 
911  if (bSubTotal)
913 }
914 
916 {
920  if (pCode->HasOpCode(ocMacro))
922 
925 
926  if (!mxGroup || !mxGroup->mpCode)
927  // Formula token is not shared.
928  delete pCode;
929 }
930 
932 {
933  return new ScFormulaCell(*this, rDocument, aPos);
934 }
935 
936 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
937 {
938  return new ScFormulaCell(*this, rDocument, rPos, ScCloneFlags::Default);
939 }
940 
942 {
943  return pCode->GetHash();
944 }
945 
946 OUString ScFormulaCell::GetFormula( const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
947 {
948  if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
949  {
951  }
952  OUStringBuffer buffer;
954  {
955  // Reference to another cell that contains a matrix formula.
958  if( p )
959  {
960  /* FIXME: original GetFormula() code obtained
961  * pCell only if (!IsInChangeTrack()),
962  * GetEnglishFormula() omitted that test.
963  * Can we live without in all cases? */
964  ScFormulaCell* pCell = nullptr;
965  ScSingleRefData& rRef = *p->GetSingleRef();
966  ScAddress aAbs = rRef.toAbs(rDocument, aPos);
967  if (rDocument.ValidAddress(aAbs))
968  pCell = rDocument.GetFormulaCell(aAbs);
969 
970  if (pCell)
971  {
972  return pCell->GetFormula( eGrammar, pContext );
973  }
974  else
975  {
976  ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
977  aComp.CreateStringFromTokenArray( buffer );
978  }
979  }
980  else
981  {
982  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
983  }
984  }
985  else
986  {
987  ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
988  aComp.CreateStringFromTokenArray( buffer );
989  }
990 
991  buffer.insert( 0, '=');
993  {
994  buffer.insert( 0, '{');
995  buffer.append( '}');
996  }
997  return buffer.makeStringAndClear();
998 }
999 
1001 {
1002  OUStringBuffer aBuf;
1003  if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
1004  {
1005  ScTokenArray aCode(rCxt.getDoc());
1007  ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
1008  aComp.CreateStringFromTokenArray(aBuf);
1009  return aBuf.makeStringAndClear();
1010  }
1011  else if( cMatrixFlag == ScMatrixMode::Reference )
1012  {
1013  // Reference to another cell that contains a matrix formula.
1016  if( p )
1017  {
1018  /* FIXME: original GetFormula() code obtained
1019  * pCell only if (!IsInChangeTrack()),
1020  * GetEnglishFormula() omitted that test.
1021  * Can we live without in all cases? */
1022  ScFormulaCell* pCell = nullptr;
1023  ScSingleRefData& rRef = *p->GetSingleRef();
1024  ScAddress aAbs = rRef.toAbs(rDocument, aPos);
1025  if (rDocument.ValidAddress(aAbs))
1026  pCell = rDocument.GetFormulaCell(aAbs);
1027 
1028  if (pCell)
1029  {
1030  return pCell->GetFormula(rCxt);
1031  }
1032  else
1033  {
1034  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1035  aComp.CreateStringFromTokenArray(aBuf);
1036  }
1037  }
1038  else
1039  {
1040  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1041  }
1042  }
1043  else
1044  {
1045  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1046  aComp.CreateStringFromTokenArray(aBuf);
1047  }
1048 
1049  aBuf.insert( 0, '=');
1051  {
1052  aBuf.insert( 0, '{');
1053  aBuf.append( '}');
1054  }
1055 
1056  return aBuf.makeStringAndClear();
1057 }
1058 
1060 {
1061  MaybeInterpret();
1062 
1063  if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
1064  {
1065  const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
1066  if (pMat)
1067  {
1068  pMat->GetDimensions( rCols, rRows );
1069  if (pCode->IsHyperLink())
1070  {
1071  // Row 2 element is the URL that is not to be displayed and the
1072  // result dimension not to be extended.
1073  assert(rRows == 2);
1074  rRows = 1;
1075  }
1076  return;
1077  }
1078  }
1079  rCols = 0;
1080  rRows = 0;
1081 }
1082 
1085 
1087 {
1088  mbPostponedDirty = bVar;
1089 }
1090 
1092 {
1094 }
1095 
1096 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1097  const FormulaGrammar::Grammar eGrammar )
1098 {
1099  if ( rDocument.IsClipOrUndo() )
1100  return;
1101  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1102  if ( bWasInFormulaTree )
1104  // pCode may not deleted for queries, but must be empty
1105  if ( pCode )
1106  pCode->Clear();
1107  ScTokenArray* pCodeOld = pCode;
1108  ScCompiler aComp( rDocument, aPos, eGrammar);
1109  pCode = aComp.CompileString( rFormula ).release();
1110  assert(!mxGroup);
1111  delete pCodeOld;
1112  if( pCode->GetCodeError() == FormulaError::NONE )
1113  {
1114  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1115  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1116  if ( rFormula[0] == '=' )
1117  pCode->AddBad( rFormula.copy(1) );
1118  else
1119  pCode->AddBad( rFormula );
1120  }
1121  bCompile = true;
1122  CompileTokenArray( bNoListening );
1123  }
1124  else
1125  bChanged = true;
1126 
1127  if ( bWasInFormulaTree )
1128  rDocument.PutInFormulaTree( this );
1129 }
1130 
1132  sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1133 {
1134  if ( rDocument.IsClipOrUndo() )
1135  return;
1136  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1137  if ( bWasInFormulaTree )
1139  // pCode may not deleted for queries, but must be empty
1140  if ( pCode )
1141  pCode->Clear();
1142  ScTokenArray* pCodeOld = pCode;
1143  ScCompiler aComp(rCxt, aPos);
1144  pCode = aComp.CompileString( rFormula ).release();
1145  assert(!mxGroup);
1146  delete pCodeOld;
1147  if( pCode->GetCodeError() == FormulaError::NONE )
1148  {
1149  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1150  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1151  if ( rFormula[0] == '=' )
1152  pCode->AddBad( rFormula.copy(1) );
1153  else
1154  pCode->AddBad( rFormula );
1155  }
1156  bCompile = true;
1157  CompileTokenArray(rCxt, bNoListening);
1158  }
1159  else
1160  bChanged = true;
1161 
1162  if ( bWasInFormulaTree )
1163  rDocument.PutInFormulaTree( this );
1164 }
1165 
1166 void ScFormulaCell::CompileTokenArray( bool bNoListening )
1167 {
1168  // Not already compiled?
1169  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1170  {
1171  Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1172  }
1173  else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1174  {
1175  // RPN length may get changed
1176  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1177  if ( bWasInFormulaTree )
1179 
1180  // Loading from within filter? No listening yet!
1182  bNoListening = true;
1183 
1184  if( !bNoListening && pCode->GetCodeLen() )
1187  bSubTotal = aComp.CompileTokenArray();
1188  if( pCode->GetCodeError() == FormulaError::NONE )
1189  {
1190  nFormatType = aComp.GetNumFormatType();
1191  bChanged = true;
1192  aResult.SetToken( nullptr);
1193  bCompile = false;
1194  if ( !bNoListening )
1196  }
1197  if ( bWasInFormulaTree )
1198  rDocument.PutInFormulaTree( this );
1199 
1200  if (bSubTotal)
1201  rDocument.AddSubTotalCell(this);
1202  }
1203 }
1204 
1206 {
1207  // Not already compiled?
1208  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1209  {
1210  rCxt.setGrammar(eTempGrammar);
1211  Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1212  }
1213  else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1214  {
1215  // RPN length may get changed
1216  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1217  if ( bWasInFormulaTree )
1219 
1220  // Loading from within filter? No listening yet!
1222  bNoListening = true;
1223 
1224  if( !bNoListening && pCode->GetCodeLen() )
1226  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1227  bSubTotal = aComp.CompileTokenArray();
1228  if( pCode->GetCodeError() == FormulaError::NONE )
1229  {
1230  nFormatType = aComp.GetNumFormatType();
1231  bChanged = true;
1232  aResult.SetToken( nullptr);
1233  bCompile = false;
1234  if ( !bNoListening )
1236  }
1237  if ( bWasInFormulaTree )
1238  rDocument.PutInFormulaTree( this );
1239 
1240  if (bSubTotal)
1241  rDocument.AddSubTotalCell(this);
1242  }
1243 }
1244 
1246 {
1248  { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1249  // just establish listeners
1251  return ;
1252  }
1253 
1254  // Error constant formula cell stays as is.
1255  if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
1256  return;
1257 
1258  // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1259  // was in to update its count.
1260  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this);
1261  if (bWasInFormulaTree)
1263  rCxt.setGrammar(eTempGrammar);
1264  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1265  OUString aFormula, aFormulaNmsp;
1266  aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1267  rDocument.DecXMLImportedFormulaCount( aFormula.getLength() );
1269  // pCode may not deleted for queries, but must be empty
1270  pCode->Clear();
1271 
1272  bool bDoCompile = true;
1273 
1274  if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1275  {
1276  ScAddress aPreviousCell( aPos );
1277  aPreviousCell.IncRow( -1 );
1278  ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell );
1279  if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
1280  {
1281  // Build formula string using the tokens from the previous cell,
1282  // but use the current cell position.
1283  ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1284  OUStringBuffer aShouldBeBuf;
1285  aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1286 
1287  // The initial '=' is optional in ODFF.
1288  const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
1289  OUString aShouldBe = aShouldBeBuf.makeStringAndClear();
1290  if (aFormula.getLength() == aShouldBe.getLength() + nLeadingEqual &&
1291  aFormula.match( aShouldBe, nLeadingEqual))
1292  {
1293  // Put them in the same formula group.
1294  ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1295  if (!xGroup) // Last cell is not grouped yet. Start a new group.
1296  xGroup = pPreviousCell->CreateCellGroup(1, false);
1297  ++xGroup->mnLength;
1298  SetCellGroup( xGroup );
1299 
1300  // Do setup here based on previous cell.
1301 
1302  nFormatType = pPreviousCell->nFormatType;
1303  bSubTotal = pPreviousCell->bSubTotal;
1304  bChanged = true;
1305  bCompile = false;
1306 
1307  if (bSubTotal)
1308  rDocument.AddSubTotalCell(this);
1309 
1310  bDoCompile = false;
1311  pCode = pPreviousCell->pCode;
1312  if (pPreviousCell->mbIsExtRef)
1313  rDocument.GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
1314  }
1315  }
1316  }
1317 
1318  if (bDoCompile)
1319  {
1320  ScTokenArray* pCodeOld = pCode;
1321  pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
1322  assert(!mxGroup);
1323  delete pCodeOld;
1324 
1325  if( pCode->GetCodeError() == FormulaError::NONE )
1326  {
1327  if ( !pCode->GetLen() )
1328  {
1329  if ( !aFormula.isEmpty() && aFormula[0] == '=' )
1330  pCode->AddBad( aFormula.copy( 1 ) );
1331  else
1332  pCode->AddBad( aFormula );
1333  }
1334  bSubTotal = aComp.CompileTokenArray();
1335  if( pCode->GetCodeError() == FormulaError::NONE )
1336  {
1337  nFormatType = aComp.GetNumFormatType();
1338  bChanged = true;
1339  bCompile = false;
1340  }
1341 
1342  if (bSubTotal)
1343  rDocument.AddSubTotalCell(this);
1344  }
1345  else
1346  bChanged = true;
1347  }
1348 
1349  // After loading, it must be known if ocDde/ocWebservice is in any formula
1350  // (for external links warning, CompileXML is called at the end of loading XML file)
1352 
1353  //volatile cells must be added here for import
1355  {
1356  // During load, only those cells that are marked explicitly dirty get
1357  // recalculated. So we need to set it dirty here.
1358  SetDirtyVar();
1360  // Do not call TrackFormulas() here, not all listeners may have been
1361  // established, postponed until ScDocument::CompileXML() finishes.
1362  }
1363  else if (bWasInFormulaTree)
1365 }
1366 
1367 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1368 {
1369  bool bNewCompiled = false;
1370  // If a Calc 1.0-doc is read, we have a result, but no token array
1371  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1372  {
1373  rCxt.setGrammar(eTempGrammar);
1374  Compile(rCxt, aResult.GetHybridFormula(), true);
1375  aResult.SetToken( nullptr);
1376  bDirty = true;
1377  bNewCompiled = true;
1378  }
1379  // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1380  if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1381  {
1382  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1383  bSubTotal = aComp.CompileTokenArray();
1384  nFormatType = aComp.GetNumFormatType();
1385  bDirty = true;
1386  bCompile = false;
1387  bNewCompiled = true;
1388 
1389  if (bSubTotal)
1390  rDocument.AddSubTotalCell(this);
1391  }
1392 
1393  // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1394  // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1395  // We iron this out here for all systems, such that we also have an Err503 here.
1396  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
1397  {
1398  OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1399  aResult.SetResultError( FormulaError::IllegalFPOperation );
1400  bDirty = true;
1401  }
1402 
1403  // DoubleRefs for binary operators were always a Matrix before version v5.0.
1404  // Now this is only the case when in an array formula, otherwise it's an implicit intersection
1407  {
1409  SetMatColsRows( 1, 1);
1410  }
1411 
1412  // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1413  // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1414  if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
1415  {
1416  if (bStartListening)
1418 
1419  if( !pCode->IsRecalcModeNormal() )
1420  bDirty = true;
1421  }
1422  if ( pCode->IsRecalcModeAlways() )
1423  { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1424  // for each F9
1425  bDirty = true;
1426  }
1427  // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1428 }
1429 
1431 {
1432  return pCode && rDocument.MarkUsedExternalReferences(*pCode, aPos);
1433 }
1434 
1435 namespace {
1436 class RecursionCounter
1437 {
1438  ScRecursionHelper& rRec;
1439  bool bStackedInIteration;
1440 #if defined DBG_UTIL && !defined NDEBUG
1441  const ScFormulaCell* cell;
1442 #endif
1443 public:
1444  RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
1445  : rRec(r)
1446 #if defined DBG_UTIL && !defined NDEBUG
1447  , cell(p)
1448 #endif
1449  {
1450  bStackedInIteration = rRec.IsDoingIteration();
1451  if (bStackedInIteration)
1452  rRec.GetRecursionInIterationStack().push( p);
1453  rRec.IncRecursionCount();
1454  }
1455  ~RecursionCounter()
1456  {
1457  rRec.DecRecursionCount();
1458  if (bStackedInIteration)
1459  {
1460 #if defined DBG_UTIL && !defined NDEBUG
1461  assert(rRec.GetRecursionInIterationStack().top() == cell);
1462 #endif
1463  rRec.GetRecursionInIterationStack().pop();
1464  }
1465  }
1466 };
1467 
1468 // Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
1469 // Remove the group again at the end, since there are some places throughout the code
1470 // that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
1471 // reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
1472 // a group immediately would remove the info), for this reason affected cells are stored in the recursion
1473 // helper.
1474 struct TemporaryCellGroupMaker
1475 {
1476  TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
1477  : mCell( cell )
1478  , mEnabled( enable )
1479  {
1480  if( mEnabled && mCell->GetCellGroup() == nullptr )
1481  {
1482  mCell->CreateCellGroup( 1, false );
1483  mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell );
1484  }
1485  }
1486  ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
1487  {
1488  if( mEnabled )
1489  mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells();
1490  }
1491  ScFormulaCell* mCell;
1492  const bool mEnabled;
1493 };
1494 
1495 } // namespace
1496 
1497 bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
1498 {
1499  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
1500  bool bGroupInterpreted = false;
1501 
1502  // The result would possibly depend on a cell without a valid value, bail out
1503  // the entire dependency computation.
1504  if (rRecursionHelper.IsAbortingDependencyComputation())
1505  return false;
1506 
1507  if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
1508  return bGroupInterpreted;
1509 
1511  TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );
1512 
1513  ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
1514 
1515  if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
1516  rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
1517  {
1518  // This call arose from a dependency calculation and we just found a cycle.
1519  // This will mark all elements in the cycle as parts-of-cycle.
1520  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
1521  // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
1522  // If there is a genuine circular reference, it will be marked so when all groups
1523  // in the cycle get out of dependency evaluation mode.
1524  // But returning without calculation a new value means other cells depending
1525  // on this one would use a possibly invalid value, so ensure the dependency
1526  // computation is aborted without resetting the dirty flag of any cell.
1527  rRecursionHelper.AbortDependencyComputation();
1528  return bGroupInterpreted;
1529  }
1530 
1531 #if DEBUG_CALCULATION
1532  static bool bDebugCalculationInit = true;
1533  if (bDebugCalculationInit)
1534  {
1535  aDC.maTrigger = aDebugCalculationTriggerAddress;
1536  aDC.mbPrintResults = true;
1537  bDebugCalculationInit = false;
1538  }
1539  DebugCalculationStacker aDebugEntry(aPos, rDocument);
1540 #endif
1541 
1542  if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
1543  return bGroupInterpreted; // no double/triple processing
1544 
1545  //FIXME:
1546  // If the call originates from a Reschedule in DdeLink update, leave dirty
1547  // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1548  if ( rDocument.IsInDdeLinkUpdate() )
1549  return bGroupInterpreted;
1550 
1551  if (bRunning)
1552  {
1553  if (!rDocument.GetDocOptions().IsIter())
1554  {
1555  aResult.SetResultError( FormulaError::CircularReference );
1556  return bGroupInterpreted;
1557  }
1558 
1559  if (aResult.GetResultError() == FormulaError::CircularReference)
1560  aResult.SetResultError( FormulaError::NONE );
1561 
1562  // Start or add to iteration list.
1563  if (!rRecursionHelper.IsDoingIteration() ||
1564  !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
1565  rRecursionHelper.SetInIterationReturn( true);
1566 
1567  return bGroupInterpreted;
1568  }
1569  // no multiple interprets for GetErrCode, IsValue, GetValue and
1570  // different entry point recursions. Would also lead to premature
1571  // convergence in iterations.
1572  if (rRecursionHelper.GetIteration() && nSeenInIteration ==
1573  rRecursionHelper.GetIteration())
1574  return bGroupInterpreted;
1575 
1576  bool bOldRunning = bRunning;
1577  if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1578  {
1579  bRunning = true;
1580  rRecursionHelper.SetInRecursionReturn( true);
1581  }
1582  else
1583  {
1585 
1586 #if DEBUG_CALCULATION
1587  aDC.enterGroup();
1588 #endif
1589  bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
1590  bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
1591  bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
1592 
1593 #if DEBUG_CALCULATION
1594  aDC.leaveGroup();
1595 #endif
1596  if (!bGroupInterpreted)
1597  {
1598  // This call resulted from a dependency calculation for a multigroup-threading attempt,
1599  // but found dependency among the groups.
1600  if (!rRecursionHelper.AreGroupsIndependent())
1601  {
1603  return bGroupInterpreted;
1604  }
1605  // Dependency calc inside InterpretFormulaGroup() failed due to
1606  // detection of a cycle and there are parent FG's in the cycle.
1607  // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
1608  if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
1609  {
1611  return bGroupInterpreted;
1612  }
1613 
1614  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
1616  InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1617  }
1618 
1620  }
1621 
1622  // While leaving a recursion or iteration stack, insert its cells to the
1623  // recursion list in reverse order.
1624  if (rRecursionHelper.IsInReturn())
1625  {
1626  bool bFreeFlyingInserted = false;
1627  if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion())
1628  {
1629  rRecursionHelper.Insert( this, bOldRunning, aResult);
1630  bFreeFlyingInserted = mbFreeFlying;
1631  }
1632  bool bIterationFromRecursion = false;
1633  bool bResumeIteration = false;
1634  do
1635  {
1636  if ((rRecursionHelper.IsInIterationReturn() &&
1637  rRecursionHelper.GetRecursionCount() == 0 &&
1638  !rRecursionHelper.IsDoingIteration()) ||
1639  bIterationFromRecursion || bResumeIteration)
1640  {
1641  bool & rDone = rRecursionHelper.GetConvergingReference();
1642  rDone = false;
1643  if (!bIterationFromRecursion && bResumeIteration)
1644  {
1645  bResumeIteration = false;
1646  // Resuming iteration expands the range.
1647  ScFormulaRecursionList::const_iterator aOldStart(
1648  rRecursionHelper.GetLastIterationStart());
1649  rRecursionHelper.ResumeIteration();
1650  // Mark new cells being in iteration.
1651  for (ScFormulaRecursionList::const_iterator aIter(
1652  rRecursionHelper.GetIterationStart()); aIter !=
1653  aOldStart; ++aIter)
1654  {
1655  ScFormulaCell* pIterCell = (*aIter).pCell;
1656  pIterCell->bIsIterCell = true;
1657  }
1658  // Mark older cells dirty again, in case they converted
1659  // without accounting for all remaining cells in the circle
1660  // that weren't touched so far, e.g. conditional. Restore
1661  // backupped result.
1662  sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1663  for (ScFormulaRecursionList::const_iterator aIter(
1664  aOldStart); aIter !=
1665  rRecursionHelper.GetIterationEnd(); ++aIter)
1666  {
1667  ScFormulaCell* pIterCell = (*aIter).pCell;
1668  if (pIterCell->nSeenInIteration == nIteration)
1669  {
1670  if (!pIterCell->bDirty || aIter == aOldStart)
1671  {
1672  pIterCell->aResult = (*aIter).aPreviousResult;
1673  }
1674  --pIterCell->nSeenInIteration;
1675  }
1676  pIterCell->bDirty = true;
1677  }
1678  }
1679  else
1680  {
1681  bResumeIteration = false;
1682  // Close circle once. If 'this' is self-referencing only
1683  // (e.g. counter or self-adder) then it is already
1684  // implicitly closed.
1685  /* TODO: does this even make sense anymore? The last cell
1686  * added above with rRecursionHelper.Insert() should always
1687  * be 'this', shouldn't it? */
1688  if (rRecursionHelper.GetList().size() > 1)
1689  {
1690  ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
1691  if (pLastCell != this)
1692  {
1695  pLastCell->InterpretTail(
1696  *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1698  }
1699  }
1700  // Start at 1, init things.
1701  rRecursionHelper.StartIteration();
1702  // Mark all cells being in iteration. Reset results to
1703  // original values, formula cells have been interpreted
1704  // already, discard that step.
1705  for (ScFormulaRecursionList::const_iterator aIter(
1706  rRecursionHelper.GetIterationStart()); aIter !=
1707  rRecursionHelper.GetIterationEnd(); ++aIter)
1708  {
1709  ScFormulaCell* pIterCell = (*aIter).pCell;
1710  pIterCell->aResult = (*aIter).aPreviousResult;
1711  pIterCell->bIsIterCell = true;
1712  }
1713  }
1714  bIterationFromRecursion = false;
1715  sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
1716  for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1717  rRecursionHelper.IncIteration())
1718  {
1719  rDone = false;
1720  bool bFirst = true;
1721  for ( ScFormulaRecursionList::iterator aIter(
1722  rRecursionHelper.GetIterationStart()); aIter !=
1723  rRecursionHelper.GetIterationEnd() &&
1724  !rRecursionHelper.IsInReturn(); ++aIter)
1725  {
1726  ScFormulaCell* pIterCell = (*aIter).pCell;
1727  if (pIterCell->IsDirtyOrInTableOpDirty() &&
1728  rRecursionHelper.GetIteration() !=
1729  pIterCell->GetSeenInIteration())
1730  {
1731  (*aIter).aPreviousResult = pIterCell->aResult;
1734  pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
1736  }
1737  if (bFirst)
1738  {
1739  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1740  bFirst = false;
1741  }
1742  else if (rDone)
1743  {
1744  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1745  }
1746  }
1747  if (rRecursionHelper.IsInReturn())
1748  {
1749  bResumeIteration = true;
1750  break; // for
1751  // Don't increment iteration.
1752  }
1753  }
1754  if (!bResumeIteration)
1755  {
1756  if (rDone)
1757  {
1758  for (ScFormulaRecursionList::const_iterator aIter(
1759  rRecursionHelper.GetIterationStart());
1760  aIter != rRecursionHelper.GetIterationEnd();
1761  ++aIter)
1762  {
1763  ScFormulaCell* pIterCell = (*aIter).pCell;
1764  pIterCell->bIsIterCell = false;
1765  pIterCell->nSeenInIteration = 0;
1766  pIterCell->bRunning = (*aIter).bOldRunning;
1767  }
1768  }
1769  else
1770  {
1771  for (ScFormulaRecursionList::const_iterator aIter(
1772  rRecursionHelper.GetIterationStart());
1773  aIter != rRecursionHelper.GetIterationEnd();
1774  ++aIter)
1775  {
1776  ScFormulaCell* pIterCell = (*aIter).pCell;
1777  pIterCell->bIsIterCell = false;
1778  pIterCell->nSeenInIteration = 0;
1779  pIterCell->bRunning = (*aIter).bOldRunning;
1780  pIterCell->ResetDirty();
1781  // The difference to Excel is that Excel does not
1782  // produce an error for non-convergence thus a
1783  // delta of 0.001 still works to execute the
1784  // maximum number of iterations and display the
1785  // results no matter if the result anywhere reached
1786  // near delta, but also never indicates whether the
1787  // result actually makes sense in case of
1788  // non-counter context. Calc does check the delta
1789  // in every case. If we wanted to support what
1790  // Excel does then add another option "indicate
1791  // non-convergence error" (default on) and execute
1792  // the following block only if set.
1793 #if 1
1794  // If one cell didn't converge, all cells of this
1795  // circular dependency don't, no matter whether
1796  // single cells did.
1797  pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1798  pIterCell->bChanged = true;
1799 #endif
1800  }
1801  }
1802  // End this iteration and remove entries.
1803  rRecursionHelper.EndIteration();
1804  bResumeIteration = rRecursionHelper.IsDoingIteration();
1805  }
1806  }
1807  if (rRecursionHelper.IsInRecursionReturn() &&
1808  rRecursionHelper.GetRecursionCount() == 0 &&
1809  !rRecursionHelper.IsDoingRecursion())
1810  {
1811  bIterationFromRecursion = false;
1812  // Iterate over cells known so far, start with the last cell
1813  // encountered, inserting new cells if another recursion limit
1814  // is reached. Repeat until solved.
1815  rRecursionHelper.SetDoingRecursion( true);
1816  do
1817  {
1818  rRecursionHelper.SetInRecursionReturn( false);
1819  for (ScFormulaRecursionList::const_iterator aIter(
1820  rRecursionHelper.GetIterationStart());
1821  !rRecursionHelper.IsInReturn() && aIter !=
1822  rRecursionHelper.GetIterationEnd(); ++aIter)
1823  {
1824  ScFormulaCell* pCell = (*aIter).pCell;
1825  if (pCell->IsDirtyOrInTableOpDirty())
1826  {
1829  pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1831  if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1832  pCell->bRunning = (*aIter).bOldRunning;
1833  }
1834  }
1835  } while (rRecursionHelper.IsInRecursionReturn());
1836  rRecursionHelper.SetDoingRecursion( false);
1837  if (rRecursionHelper.IsInIterationReturn())
1838  {
1839  if (!bResumeIteration)
1840  bIterationFromRecursion = true;
1841  }
1842  else if (bResumeIteration ||
1843  rRecursionHelper.IsDoingIteration())
1844  rRecursionHelper.GetList().erase(
1845  rRecursionHelper.GetIterationStart(),
1846  rRecursionHelper.GetLastIterationStart());
1847  else
1848  rRecursionHelper.Clear();
1849  }
1850  } while (bIterationFromRecursion || bResumeIteration);
1851 
1852  if (bFreeFlyingInserted)
1853  {
1854  // Remove this from recursion list, it may get deleted.
1855  // It additionally also should mean that the recursion/iteration
1856  // ends here as it must had been triggered by this free-flying
1857  // out-of-sheets cell
1858  /* TODO: replace by a simple rRecursionHelper.EndIteration() call
1859  * if the assertions hold. */
1860  const bool bOnlyThis = (rRecursionHelper.GetList().size() == 1);
1861  assert(bOnlyThis);
1862  rRecursionHelper.GetList().remove_if([this](const ScFormulaRecursionEntry& r){return r.pCell == this;});
1863  if (bOnlyThis)
1864  {
1865  assert(rRecursionHelper.GetList().empty());
1866  if (rRecursionHelper.GetList().empty())
1867  rRecursionHelper.EndIteration();
1868  }
1869  }
1870  }
1871 
1872 #if DEBUG_CALCULATION
1874  if (nErr != FormulaError::NONE)
1875  aDC.storeResultError( nErr);
1876  else if (aResult.IsValue())
1877  aDC.storeResult( aResult.GetDouble());
1878  else
1879  aDC.storeResult( aResult.GetString());
1880 #endif
1881 
1882  return bGroupInterpreted;
1883 }
1884 
1886 {
1887  RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this);
1888  // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1889  if(bIsIterCell)
1891  if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1892  {
1893  // #i11719# no RPN and no error and no token code but result string present
1894  // => interpretation of this cell during name-compilation and unknown names
1895  // => can't exchange underlying code array in CompileTokenArray() /
1896  // Compile() because interpreter's token iterator would crash or pCode
1897  // would be deleted twice if this cell was interpreted during
1898  // compilation.
1899  // This should only be a temporary condition and, since we set an
1900  // error, if ran into it again we'd bump into the dirty-clearing
1901  // condition further down.
1902  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1903  {
1904  pCode->SetCodeError( FormulaError::NoCode );
1905  // This is worth an assertion; if encountered in daily work
1906  // documents we might need another solution. Or just confirm correctness.
1907  return;
1908  }
1910  }
1911 
1912  if( pCode->GetCodeLen() )
1913  {
1914  std::unique_ptr<ScInterpreter> pScopedInterpreter;
1915  ScInterpreter* pInterpreter;
1916  if (rContext.pInterpreter)
1917  {
1918  pInterpreter = rContext.pInterpreter;
1919  pInterpreter->Init(this, aPos, *pCode);
1920  }
1921  else
1922  {
1923  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode ));
1924  pInterpreter = pScopedInterpreter.get();
1925  }
1926 
1927  FormulaError nOldErrCode = aResult.GetResultError();
1928  if ( nSeenInIteration == 0 )
1929  { // Only the first time
1930  // With bChanged=false, if a newly compiled cell has a result of
1931  // 0.0, no change is detected and the cell will not be repainted.
1932  // bChanged = false;
1933  aResult.SetResultError( FormulaError::NONE );
1934  }
1935 
1936  switch ( aResult.GetResultError() )
1937  {
1938  case FormulaError::CircularReference : // will be determined again if so
1939  aResult.SetResultError( FormulaError::NONE );
1940  break;
1941  default: break;
1942  }
1943 
1944  bool bOldRunning = bRunning;
1945  bRunning = true;
1946  pInterpreter->Interpret();
1948  {
1949  if (nSeenInIteration > 0)
1950  --nSeenInIteration; // retry when iteration is resumed
1951 
1952  if ( aResult.GetType() == formula::svUnknown )
1953  aResult.SetToken( pInterpreter->GetResultToken().get() );
1954 
1955  return;
1956  }
1957  bRunning = bOldRunning;
1958 
1959  // The result may be invalid or depend on another invalid result, just abort
1960  // without updating the cell value. Since the dirty flag will not be reset,
1961  // the proper value will be computed later.
1963  return;
1964 
1965  // #i102616# For single-sheet saving consider only content changes, not format type,
1966  // because format type isn't set on loading (might be changed later)
1967  bool bContentChanged = false;
1968 
1969  // Do not create a HyperLink() cell if the formula results in an error.
1970  if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1971  pCode->SetHyperLink(false);
1972 
1973  if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1974  {
1975  bChanged = true;
1976 
1977  if (pInterpreter->GetError() == FormulaError::RetryCircular)
1978  {
1979  // Array formula matrix calculation corner case. Keep dirty
1980  // state, do not remove from formula tree or anything else, but
1981  // store FormulaError::CircularReference in case this cell does not get
1982  // recalculated.
1983  aResult.SetResultError( FormulaError::CircularReference);
1984  return;
1985  }
1986 
1987  ResetDirty();
1988  }
1989 
1990  if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1991  {
1992  bool bIsValue = aResult.IsValue(); // the previous type
1993  // Did it converge?
1994  if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
1995  pInterpreter->GetNumResult() - aResult.GetDouble()) <=
1997  (!bIsValue && pInterpreter->GetResultType() == svString &&
1998  pInterpreter->GetStringResult() == aResult.GetString()))
1999  {
2000  // A convergence in the first iteration doesn't necessarily
2001  // mean that it's done, it may be as not all related cells
2002  // of a circle changed their values yet. If the set really
2003  // converges it will do so also during the next iteration. This
2004  // fixes situations like of #i44115#. If this wasn't wanted an
2005  // initial "uncalculated" value would be needed for all cells
2006  // of a circular dependency => graph needed before calculation.
2007  if (nSeenInIteration > 1 ||
2009  {
2010  ResetDirty();
2011  }
2012  }
2013  }
2014 
2015  // New error code?
2016  if( pInterpreter->GetError() != nOldErrCode )
2017  {
2018  bChanged = true;
2019  // bContentChanged only has to be set if the file content would be changed
2020  if ( aResult.GetCellResultType() != svUnknown )
2021  bContentChanged = true;
2022  }
2023 
2024  ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
2025 
2026  // For IF() and other jumps or changed formatted source data the result
2027  // format may change for different runs, e.g. =IF(B1,B1) with first
2028  // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
2029  // displayed as TRUE. Do not force a general format though if
2030  // mbNeedsNumberFormat is set (because there was a general format..).
2031  // Note that nFormatType may be out of sync here if a format was
2032  // applied or cleared after the last run, but obtaining the current
2033  // format always just to check would be expensive. There may be
2034  // cases where the format should be changed but is not. If that turns
2035  // out to be a real problem then obtain the current format type after
2036  // the initial check when needed.
2037  bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
2039 
2040  // We have some requirements additionally to IsCompatible().
2041  // * Do not apply a NumberFormat::LOGICAL if the result value is not
2042  // 1.0 or 0.0
2043  // * Do not override an already set numeric number format if the result
2044  // is of type NumberFormat::LOGICAL, it could be user applied.
2045  // On the other hand, for an empty jump path instead of FALSE an
2046  // unexpected for example 0% could be displayed. YMMV.
2047  // * Never override a non-standard number format that indicates user
2048  // applied.
2049  // * NumberFormat::TEXT does not force a change.
2050  if (bForceNumberFormat)
2051  {
2052  sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
2053  const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
2054  if (nRetType == SvNumFormatType::LOGICAL)
2055  {
2056  double fVal = aNewResult.GetDouble();
2057  if (fVal != 1.0 && fVal != 0.0)
2058  bForceNumberFormat = false;
2059  else
2060  {
2061  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2062  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2063  switch (nFormatType)
2064  {
2065  case SvNumFormatType::PERCENT:
2066  case SvNumFormatType::CURRENCY:
2067  case SvNumFormatType::SCIENTIFIC:
2068  case SvNumFormatType::FRACTION:
2069  bForceNumberFormat = false;
2070  break;
2071  case SvNumFormatType::NUMBER:
2072  if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2073  bForceNumberFormat = false;
2074  break;
2075  default: break;
2076  }
2077  }
2078  }
2079  else if (nRetType == SvNumFormatType::TEXT)
2080  {
2081  bForceNumberFormat = false;
2082  }
2083  if (bForceNumberFormat)
2084  {
2085  if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2086  {
2087  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2088  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2089  }
2090  if (nOldFormatIndex !=
2091  ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2092  bForceNumberFormat = false;
2093  }
2094  }
2095 
2096  if( mbNeedsNumberFormat || bForceNumberFormat )
2097  {
2098  bool bSetFormat = true;
2099  const SvNumFormatType nOldFormatType = nFormatType;
2100  nFormatType = pInterpreter->GetRetFormatType();
2101  sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2102 
2103  if (nFormatType == SvNumFormatType::TEXT)
2104  {
2105  // Don't set text format as hard format.
2106  bSetFormat = false;
2107  }
2108  else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2109  {
2110  // In a matrix range do not set an (inherited) logical format
2111  // as hard format if the value does not represent a strict TRUE
2112  // or FALSE value. But do set for a top left error value so
2113  // following matrix cells can inherit for non-error values.
2114  // This solves a problem with IF() expressions in array context
2115  // where incidentally the top left element results in logical
2116  // type but some others don't. It still doesn't solve the
2117  // reverse case though, where top left is not logical type but
2118  // some other elements should be. We'd need to transport type
2119  // or format information on arrays.
2120  StackVar eNewCellResultType = aNewResult.GetCellResultType();
2121  if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2122  {
2123  if (eNewCellResultType != svDouble)
2124  {
2125  bSetFormat = false;
2126  nFormatType = nOldFormatType; // that? or number?
2127  }
2128  else
2129  {
2130  double fVal = aNewResult.GetDouble();
2131  if (fVal != 1.0 && fVal != 0.0)
2132  {
2133  bSetFormat = false;
2134  nFormatType = SvNumFormatType::NUMBER;
2135  }
2136  }
2137  }
2138  }
2139 
2140  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
2141  nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
2142  nFormatIndex, nFormatType);
2143 
2144  // Do not replace a General format (which was the reason why
2145  // mbNeedsNumberFormat was set) with a General format.
2146  // 1. setting a format has quite some overhead in the
2147  // ScPatternAttr/ScAttrArray handling, even if identical.
2148  // 2. the General formats may be of different locales.
2149  // XXX if mbNeedsNumberFormat was set even if the current format
2150  // was not General then we'd have to obtain the current format here
2151  // and check at least the types.
2152  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)))
2153  {
2154  // set number format explicitly
2156  rDocument.SetNumberFormat( aPos, nFormatIndex );
2157  else
2158  {
2159  // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
2160  // to the main thread. Since thread calculations operate on formula groups,
2161  // it's enough to store just the row.
2162  DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex };
2163  rContext.maDelayedSetNumberFormat.push_back( data );
2164  }
2165  bChanged = true;
2166  }
2167 
2168  // Currently (2019-05-10) nothing else can cope with a duration
2169  // format type, change to time as it was before.
2170  if (nFormatType == SvNumFormatType::DURATION)
2171  nFormatType = SvNumFormatType::TIME;
2172 
2173  mbNeedsNumberFormat = false;
2174  }
2175 
2176  // In case of changes just obtain the result, no temporary and
2177  // comparison needed anymore.
2178  if (bChanged)
2179  {
2180  // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
2181  // Also handle special cases of initial results after loading.
2182  if ( !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2183  {
2185  StackVar eNew = aNewResult.GetCellResultType();
2186  if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
2187  {
2188  // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
2189  // -> no change
2190  }
2191  else
2192  {
2193  if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
2194  eOld = svString; // ScHybridCellToken has a valid GetString method
2195 
2196  // #i106045# use approxEqual to compare with stored value
2197  bContentChanged = (eOld != eNew ||
2198  (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
2199  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2200  }
2201  }
2202 
2203  aResult.SetToken( pInterpreter->GetResultToken().get() );
2204  }
2205  else
2206  {
2208  StackVar eNew = aNewResult.GetCellResultType();
2209  bChanged = (eOld != eNew ||
2210  (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
2211  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2212 
2213  // #i102616# handle special cases of initial results after loading
2214  // (only if the sheet is still marked unchanged)
2215  if ( bChanged && !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2216  {
2217  if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
2218  ((eOld == svHybridCell) &&
2219  eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
2220  (eOld == svDouble && eNew == svDouble &&
2221  rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
2222  {
2223  // no change, see above
2224  }
2225  else
2226  bContentChanged = true;
2227  }
2228 
2229  aResult.Assign( aNewResult);
2230  }
2231 
2232  // Precision as shown?
2233  if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
2235  && nFormatType != SvNumFormatType::DATE
2236  && nFormatType != SvNumFormatType::TIME
2237  && nFormatType != SvNumFormatType::DATETIME )
2238  {
2239  sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos );
2241  aResult.GetDouble(), nFormat, &rContext));
2242  }
2243  if (eTailParam == SCITP_NORMAL)
2244  {
2245  ResetDirty();
2246  }
2247  if( aResult.GetMatrix() )
2248  {
2249  // If the formula wasn't entered as a matrix formula, live on with
2250  // the upper left corner and let reference counting delete the matrix.
2253  }
2254  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
2255  {
2256  // Coded double error may occur via filter import.
2258  aResult.SetResultError( nErr);
2259  bChanged = bContentChanged = true;
2260  }
2261 
2262  if (bContentChanged && rDocument.IsStreamValid(aPos.Tab()))
2263  {
2264  // pass bIgnoreLock=true, because even if called from pending row height update,
2265  // a changed result must still reset the stream flag
2266  rDocument.SetStreamValid(aPos.Tab(), false, true);
2267  }
2270 
2271  // FORCED cells also immediately tested for validity (start macro possibly)
2272 
2273  if ( pCode->IsRecalcModeForced() )
2274  {
2275  sal_uLong nValidation = rDocument.GetAttr(
2276  aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2277  if ( nValidation )
2278  {
2279  const ScValidationData* pData = rDocument.GetValidationEntry( nValidation );
2280  ScRefCellValue aTmpCell(this);
2281  if ( pData && !pData->IsDataValid(aTmpCell, aPos))
2282  pData->DoCalcError( this );
2283  }
2284  }
2285 
2286  // Reschedule slows the whole thing down considerably, thus only execute on percent change
2288  {
2290  if (pProgress && pProgress->Enabled())
2291  {
2292  pProgress->SetStateCountDownOnPercent(
2294  }
2295 
2296  switch (pInterpreter->GetVolatileType())
2297  {
2299  // Volatile via built-in volatile functions. No actions needed.
2300  break;
2302  // The formula contains a volatile macro.
2306  break;
2308  if (pCode->IsRecalcModeAlways())
2309  {
2310  // The formula was previously volatile, but no more.
2313  }
2314  else
2315  {
2316  // non-volatile formula. End listening to the area in case
2317  // it's listening due to macro module change.
2319  }
2321  break;
2322  default:
2323  ;
2324  }
2325  }
2326  }
2327  else
2328  {
2329  // Cells with compiler errors should not be marked dirty forever
2330  OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
2331  ResetDirty();
2332  }
2333 }
2334 
2336 {
2337  if( !pCode->GetCodeLen() )
2338  return;
2339 
2340  if ( !pCode->IsRecalcModeAlways() )
2342 
2343  std::unique_ptr<ScInterpreter> pScopedInterpreter;
2344  if (pInterpreter)
2345  pInterpreter->Init(this, aPos, *pCode);
2346  else
2347  {
2348  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
2349  pInterpreter = pScopedInterpreter.get();
2350  }
2351 
2352  switch (pInterpreter->GetVolatileType())
2353  {
2355  // The formula contains a volatile macro.
2359  break;
2361  if (pCode->IsRecalcModeAlways())
2362  {
2363  // The formula was previously volatile, but no more.
2366  }
2367  else
2368  {
2369  // non-volatile formula. End listening to the area in case
2370  // it's listening due to macro module change.
2372  }
2374  break;
2375  default:
2376  ;
2377  }
2378 }
2379 
2380 void ScFormulaCell::SetCompile( bool bVal )
2381 {
2382  bCompile = bVal;
2383 }
2384 
2386 {
2388  if (pMat)
2389  pMat->SetMatColsRows( nCols, nRows );
2390  else if (nCols || nRows)
2391  {
2392  aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2393  // Setting the new token actually forces an empty result at this top
2394  // left cell, so have that recalculated.
2395  SetDirty();
2396  }
2397 }
2398 
2399 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2400 {
2402  if (pMat)
2403  pMat->GetMatColsRows( nCols, nRows);
2404  else
2405  {
2406  nCols = 0;
2407  nRows = 0;
2408  }
2409 }
2410 
2412 {
2413  bInChangeTrack = bVal;
2414 }
2415 
2416 void ScFormulaCell::Notify( const SfxHint& rHint )
2417 {
2418  if (rDocument.IsInDtorClear())
2419  return;
2420 
2421  const SfxHintId nHint = rHint.GetId();
2422  if (nHint == SfxHintId::ScReference)
2423  {
2424  const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2425 
2426  switch (rRefHint.getType())
2427  {
2429  {
2430  const sc::RefColReorderHint& rRefColReorder =
2431  static_cast<const sc::RefColReorderHint&>(rRefHint);
2432  if (!IsShared() || IsSharedTop())
2434  aPos, rRefColReorder.getTab(),
2435  rRefColReorder.getStartRow(),
2436  rRefColReorder.getEndRow(),
2437  rRefColReorder.getColMap());
2438  }
2439  break;
2441  {
2442  const sc::RefRowReorderHint& rRefRowReorder =
2443  static_cast<const sc::RefRowReorderHint&>(rRefHint);
2444  if (!IsShared() || IsSharedTop())
2446  aPos, rRefRowReorder.getTab(),
2447  rRefRowReorder.getStartColumn(),
2448  rRefRowReorder.getEndColumn(),
2449  rRefRowReorder.getRowMap());
2450  }
2451  break;
2453  {
2455  }
2456  break;
2458  {
2460  }
2461  break;
2462  default:
2463  ;
2464  }
2465 
2466  return;
2467  }
2468 
2470  return;
2471 
2472  if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
2473  return;
2474 
2475  bool bForceTrack = false;
2476  if ( nHint == SfxHintId::ScTableOpDirty )
2477  {
2478  bForceTrack = !bTableOpDirty;
2479  if ( !bTableOpDirty )
2480  {
2482  bTableOpDirty = true;
2483  }
2484  }
2485  else
2486  {
2487  bForceTrack = !bDirty;
2488  SetDirtyVar();
2489  }
2490  // Don't remove from FormulaTree to put in FormulaTrack to
2491  // put in FormulaTree again and again, only if necessary.
2492  // Any other means except ScRecalcMode::ALWAYS by which a cell could
2493  // be in FormulaTree if it would notify other cells through
2494  // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2495  // Yes. The new TableOpDirty made it necessary to have a
2496  // forced mode where formulas may still be in FormulaTree from
2497  // TableOpDirty but have to notify dependents for normal dirty.
2498  if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
2499  || pCode->IsRecalcModeAlways())
2500  && !rDocument.IsInFormulaTrack( this ) )
2502 }
2503 
2505 {
2506  switch (rQuery.getId())
2507  {
2509  {
2510  sc::RefQueryFormulaGroup& rRefQuery =
2511  static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2512  if (IsShared())
2513  rRefQuery.add(aPos);
2514  }
2515  break;
2516  default:
2517  ;
2518  }
2519 }
2520 
2521 void ScFormulaCell::SetDirty( bool bDirtyFlag )
2522 {
2523  if (IsInChangeTrack())
2524  return;
2525 
2527  {
2528  SetDirtyVar();
2529  rDocument.SetStreamValid(aPos.Tab(), false);
2530  return;
2531  }
2532 
2533  // Avoid multiple formula tracking in Load() and in CompileAll()
2534  // after CopyScenario() and CopyBlockFromClip().
2535  // If unconditional formula tracking is needed, set bDirty=false
2536  // before calling SetDirty(), for example in CompileTokenArray().
2537  if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
2538  {
2539  if( bDirtyFlag )
2540  SetDirtyVar();
2542 
2543  // While loading a document listeners have not been established yet.
2544  // Tracking would remove this cell from the FormulaTrack and add it to
2545  // the FormulaTree, once in there it would be assumed that its
2546  // dependents already had been tracked and it would be skipped on a
2547  // subsequent notify. Postpone tracking until all listeners are set.
2548  if (!rDocument.IsImportingXML())
2550  }
2551 
2552  rDocument.SetStreamValid(aPos.Tab(), false);
2553 }
2554 
2556 {
2557  bDirty = true;
2558  mbPostponedDirty = false;
2559  if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2560  {
2561  mxGroup->meCalcState = sc::GroupCalcEnabled;
2562  mxGroup->mbPartOfCycle = false;
2563  }
2564 
2565  // mark the sheet of this cell to be calculated
2566  //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
2567 }
2568 
2570 {
2571  bDirty = true;
2573  rDocument.PutInFormulaTree( this );
2574 }
2575 
2577 {
2578  bTableOpDirty = false;
2579 }
2580 
2582 {
2583  if ( IsInChangeTrack() )
2584  return;
2585 
2587  bTableOpDirty = true;
2588  else
2589  {
2590  if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
2591  {
2592  if ( !bTableOpDirty )
2593  {
2595  bTableOpDirty = true;
2596  }
2598  rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
2599  }
2600  }
2601 }
2602 
2604 {
2605  aResult.SetDouble(n);
2606 }
2607 
2609 {
2610  aResult.SetToken(pToken);
2611 }
2612 
2614 {
2615  return aResult.GetString();
2616 }
2617 
2619 {
2621 }
2622 
2624 {
2625  aResult.SetMatrix(nCols, nRows, pMat, pUL);
2626 }
2627 
2629 {
2630  /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2631  * used whether it is solely for transport of a simple result error and get
2632  * rid of that abuse. */
2633  pCode->SetCodeError( n );
2634  // Hard set errors are transported as result type value per convention,
2635  // e.g. via clipboard. ScFormulaResult::IsValue() and
2636  // ScFormulaResult::GetDouble() handle that.
2637  aResult.SetResultError( n );
2638 }
2639 
2641 {
2642  aResult.SetResultError( n );
2643 }
2644 
2646 {
2647  if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2648  SetDirtyVar();
2649  if ( nBits & ScRecalcMode::ONLOAD_ONCE )
2650  { // OnLoadOnce is used only to set Dirty after filter import.
2651  nBits = (nBits & ~ScRecalcMode::EMask) | ScRecalcMode::NORMAL;
2652  }
2653  pCode->AddRecalcMode( nBits );
2654 }
2655 
2657 {
2659 }
2660 
2662 {
2664 }
2665 
2667 {
2669 }
2670 
2671 void ScFormulaCell::SetHybridFormula( const OUString& r,
2672  const formula::FormulaGrammar::Grammar eGrammar )
2673 {
2674  aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2675 }
2676 
2678 {
2679  return aResult.GetHybridFormula();
2680 }
2681 
2682 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2683 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2684 {
2685  OUString aCellString;
2686 
2687  const Color* pColor;
2688 
2689  // Cell Text uses the Cell format while the URL uses
2690  // the default format for the type.
2691  const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
2692  SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
2693 
2694  const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2695 
2696  if ( IsValue() )
2697  {
2698  double fValue = GetValue();
2699  pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2700  }
2701  else
2702  {
2703  aCellString = GetString().getString();
2704  pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2705  }
2707  if (xMat)
2708  {
2709  // determine if the matrix result is a string or value.
2710  if (!xMat->IsValue(0, 1))
2711  rURL = xMat->GetString(0, 1).getString();
2712  else
2713  pFormatter->GetOutputString(
2714  xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2715  }
2716 
2717  if(rURL.isEmpty())
2718  {
2719  if(IsValue())
2720  pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2721  else
2722  pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2723  }
2724 }
2725 
2727 {
2728  if (!IsValue())
2729  return aResult.IsMultiline();
2730  return false;
2731 }
2732 
2734 {
2735  return pCode && pCode->IsHyperLink();
2736 }
2737 
2738 std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2739 {
2740  OUString aCellText;
2741  OUString aURL;
2742  GetURLResult( aURL, aCellText );
2743 
2744  return ScEditUtil::CreateURLObjectFromURL( rDocument, aURL, aCellText );
2745 }
2746 
2748 {
2749  MaybeInterpret();
2751 }
2752 
2754 {
2755  MaybeInterpret();
2757 }
2758 
2760 {
2761  MaybeInterpret();
2762  return aResult.IsValue();
2763 }
2764 
2766 {
2767  MaybeInterpret();
2768  if (pCode->GetCodeError() != FormulaError::NONE)
2769  return false;
2770 
2771  return aResult.IsValueNoError();
2772 }
2773 
2775 {
2776  if (NeedsInterpret())
2777  // false if the cell is dirty & needs to be interpreted.
2778  return false;
2779 
2780  if (pCode->GetCodeError() != FormulaError::NONE)
2781  return false;
2782 
2783  return aResult.IsValueNoError();
2784 }
2785 
2787 {
2788  MaybeInterpret();
2789  return GetRawValue();
2790 }
2791 
2793 {
2794  MaybeInterpret();
2795  return GetRawString();
2796 }
2797 
2799 {
2800  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2801  aResult.GetResultError() == FormulaError::NONE)
2802  return aResult.GetDouble();
2803  return 0.0;
2804 }
2805 
2807 {
2808  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2809  aResult.GetResultError() == FormulaError::NONE)
2810  return aResult.GetString();
2811 
2813 }
2814 
2816 {
2817  if ( rDocument.GetAutoCalc() )
2818  {
2820  // Was stored !bDirty but an accompanying matrix cell was bDirty?
2822  Interpret();
2823  }
2824  return aResult.GetMatrix().get();
2825 }
2826 
2827 bool ScFormulaCell::GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const
2828 {
2829  switch ( cMatrixFlag )
2830  {
2831  case ScMatrixMode::Formula :
2832  rPos = aPos;
2833  return true;
2835  {
2838  if( t )
2839  {
2840  ScSingleRefData& rRef = *t->GetSingleRef();
2841  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
2842  if (rDoc.ValidAddress(aAbs))
2843  {
2844  rPos = aAbs;
2845  return true;
2846  }
2847  }
2848  }
2849  break;
2850  default: break;
2851  }
2852  return false;
2853 }
2854 
2855 sc::MatrixEdge ScFormulaCell::GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const
2856 {
2857  switch ( cMatrixFlag )
2858  {
2859  case ScMatrixMode::Formula :
2861  {
2862  static thread_local SCCOL nC;
2863  static thread_local SCROW nR;
2864  ScAddress aOrg;
2865  if ( !GetMatrixOrigin( rDoc, aOrg ) )
2866  return sc::MatrixEdge::Nothing;
2867  if ( aOrg != rOrgPos )
2868  { // First time or a different matrix than last time.
2869  rOrgPos = aOrg;
2870  const ScFormulaCell* pFCell;
2872  pFCell = rDocument.GetFormulaCell(aOrg);
2873  else
2874  pFCell = this; // this ScMatrixMode::Formula
2875  // There's only one this, don't compare pFCell==this.
2876  if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2877  {
2878  pFCell->GetMatColsRows( nC, nR );
2879  if ( nC == 0 || nR == 0 )
2880  {
2881  // No ScMatrixFormulaCellToken available yet, calculate new.
2882  nC = 1;
2883  nR = 1;
2884  ScAddress aTmpOrg;
2885  ScFormulaCell* pCell;
2886  ScAddress aAdr( aOrg );
2887  aAdr.IncCol();
2888  bool bCont = true;
2889  do
2890  {
2891  pCell = rDocument.GetFormulaCell(aAdr);
2892  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2893  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2894  {
2895  nC++;
2896  aAdr.IncCol();
2897  }
2898  else
2899  bCont = false;
2900  } while ( bCont );
2901  aAdr = aOrg;
2902  aAdr.IncRow();
2903  bCont = true;
2904  do
2905  {
2906  pCell = rDocument.GetFormulaCell(aAdr);
2907  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2908  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2909  {
2910  nR++;
2911  aAdr.IncRow();
2912  }
2913  else
2914  bCont = false;
2915  } while ( bCont );
2916 
2917  const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2918  }
2919  }
2920  else
2921  {
2922 #if OSL_DEBUG_LEVEL > 0
2923  SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2925  << ", MatOrg: "
2927 #endif
2928  return sc::MatrixEdge::Nothing;
2929  }
2930  }
2931  // here we are, healthy and clean, somewhere in between
2932  SCCOL dC = aPos.Col() - aOrg.Col();
2933  SCROW dR = aPos.Row() - aOrg.Row();
2935  if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2936  {
2937  if ( dC == 0 )
2938  nEdges |= sc::MatrixEdge::Left;
2939  if ( dC+1 == nC )
2940  nEdges |= sc::MatrixEdge::Right;
2941  if ( dR == 0 )
2942  nEdges |= sc::MatrixEdge::Top;
2943  if ( dR+1 == nR )
2944  nEdges |= sc::MatrixEdge::Bottom;
2945  if ( nEdges == sc::MatrixEdge::Nothing )
2946  nEdges = sc::MatrixEdge::Inside;
2947  }
2948  else
2949  {
2950  SAL_WARN( "sc", "broken Matrix, Pos: "
2952  << ", MatOrg: "
2954  << ", MatCols: " << static_cast<sal_Int32>( nC )
2955  << ", MatRows: " << static_cast<sal_Int32>( nR )
2956  << ", DiffCols: " << static_cast<sal_Int32>( dC )
2957  << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2958  }
2959  return nEdges;
2960  }
2961  default:
2962  return sc::MatrixEdge::Nothing;
2963  }
2964 }
2965 
2967 {
2968  MaybeInterpret();
2969 
2970  /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2971  * and not also abused for signaling other error conditions we could bail
2972  * out even before attempting to interpret broken code. */
2973  FormulaError nErr = pCode->GetCodeError();
2974  if (nErr != FormulaError::NONE)
2975  return nErr;
2976  return aResult.GetResultError();
2977 }
2978 
2980 {
2981  FormulaError nErr = pCode->GetCodeError();
2982  if (nErr != FormulaError::NONE)
2983  return nErr;
2984  return aResult.GetResultError();
2985 }
2986 
2988 {
2989  MaybeInterpret();
2990 
2991  rErr = pCode->GetCodeError();
2992  if (rErr != FormulaError::NONE)
2993  return true;
2994 
2995  return aResult.GetErrorOrDouble(rErr, rVal);
2996 }
2997 
2999 {
3000  MaybeInterpret();
3001 
3002  FormulaError nErr = pCode->GetCodeError();
3003  if (nErr != FormulaError::NONE)
3004  return sc::FormulaResultValue(nErr);
3005 
3006  return aResult.GetResult();
3007 }
3008 
3010 {
3011  FormulaError nErr = pCode->GetCodeError();
3012  if (nErr != FormulaError::NONE)
3013  return sc::FormulaResultValue(nErr);
3014 
3015  return aResult.GetResult();
3016 }
3017 
3019 {
3022  if( p && !aIter.GetNextReferenceRPN() ) // only one!
3023  {
3024  SingleDoubleRefProvider aProv( *p );
3025  r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
3026  r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
3027  return true;
3028  }
3029  else
3030  return false;
3031 }
3032 
3033 bool
3035 {
3036  /* If there appears just one reference in the formula, it's the same
3037  as HasOneReference(). If there are more of them, they can denote
3038  one range if they are (sole) arguments of one function.
3039  Union of these references must form one range and their
3040  intersection must be empty set.
3041  */
3042 
3043  // Detect the simple case of exactly one reference in advance without all
3044  // overhead.
3045  // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
3046  // work again, where the function does not have only references.
3047  if (HasOneReference( rRange))
3048  return true;
3049 
3050  // Get first reference, if any
3052  formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
3053  if (pFirstReference)
3054  {
3055  // Collect all consecutive references, starting by the one
3056  // already found
3057  std::vector<formula::FormulaToken*> aReferences { pFirstReference };
3058  FormulaToken* pToken(aIter.NextRPN());
3059  FormulaToken* pFunction(nullptr);
3060  while (pToken)
3061  {
3062  if (lcl_isReference(*pToken))
3063  {
3064  aReferences.push_back(pToken);
3065  pToken = aIter.NextRPN();
3066  }
3067  else
3068  {
3069  if (pToken->IsFunction())
3070  {
3071  pFunction = pToken;
3072  }
3073  break;
3074  }
3075  }
3076  if (pFunction && !aIter.GetNextReferenceRPN()
3077  && (pFunction->GetParamCount() == aReferences.size()))
3078  {
3079  return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
3080  }
3081  }
3082  return false;
3083 }
3084 
3086 {
3087  RelNameRef eRelNameRef = RelNameRef::NONE;
3090  while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3091  {
3092  switch (t->GetType())
3093  {
3094  case formula::svSingleRef:
3095  if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3096  eRelNameRef = RelNameRef::SINGLE;
3097  break;
3098  case formula::svDoubleRef:
3099  if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3100  // May originate from individual cell names, in which case
3101  // it needs recompilation.
3102  return RelNameRef::DOUBLE;
3103  /* TODO: have an extra flag at ScComplexRefData if range was
3104  * extended? or too cumbersome? might narrow recompilation to
3105  * only needed cases.
3106  * */
3107  break;
3108  default:
3109  ; // nothing
3110  }
3111  }
3112  return eRelNameRef;
3113 }
3114 
3116 {
3117  if (rCxt.meMode != URM_INSDEL)
3118  // Just in case...
3119  return false;
3120 
3121  if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3122  // No movement.
3123  return false;
3124 
3125  if (!rCxt.maRange.Contains(aPos))
3126  return false;
3127 
3128  // This formula cell itself is being shifted during cell range
3129  // insertion or deletion. Update its position.
3130  ScAddress aErrorPos( ScAddress::UNINITIALIZED );
3131  if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos))
3132  {
3133  assert(!"can't move ScFormulaCell");
3134  }
3135 
3136  return true;
3137 }
3138 
3139 namespace {
3140 
3144 bool checkCompileColRowName(
3145  const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3146  const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3147 {
3148  switch (rCxt.meMode)
3149  {
3150  case URM_INSDEL:
3151  {
3152  if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3153  return false;
3154 
3157  ScRangePairList* pColList = rDoc.GetColNameRanges();
3158  ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3159  while ((t = aIter.GetNextColRowName()) != nullptr)
3160  {
3161  ScSingleRefData& rRef = *t->GetSingleRef();
3162  if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3163  { // ColName
3164  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3165  ScRangePair* pR = pColList->Find( aAdr );
3166  if ( pR )
3167  { // defined
3168  if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3169  return true;
3170  }
3171  else
3172  { // on the fly
3173  if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3174  return true;
3175  }
3176  }
3177  if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3178  { // RowName
3179  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3180  ScRangePair* pR = pRowList->Find( aAdr );
3181  if ( pR )
3182  { // defined
3183  if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3184  return true;
3185  }
3186  else
3187  { // on the fly
3188  if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3189  return true;
3190  }
3191  }
3192  }
3193  }
3194  break;
3195  case URM_MOVE:
3196  { // Recompile for Move/D&D when ColRowName was moved or this Cell
3197  // points to one and was moved.
3198  bool bMoved = (aPos != aOldPos);
3199  if (bMoved)
3200  return true;
3201 
3203  const formula::FormulaToken* t = aIter.GetNextColRowName();
3204  for (; t; t = aIter.GetNextColRowName())
3205  {
3206  const ScSingleRefData& rRef = *t->GetSingleRef();
3207  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
3208  if (rDoc.ValidAddress(aAbs))
3209  {
3210  if (rCxt.maRange.Contains(aAbs))
3211  return true;
3212  }
3213  }
3214  }
3215  break;
3216  case URM_COPY:
3217  return bValChanged;
3218  default:
3219  ;
3220  }
3221 
3222  return false;
3223 }
3224 
3225 void setOldCodeToUndo(
3226  ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3227 {
3228  // Copy the cell to aUndoPos, which is its current position in the document,
3229  // so this works when UpdateReference is called before moving the cells
3230  // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3231  // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3232 
3233  // If there is already a formula cell in the undo document, don't overwrite it,
3234  // the first (oldest) is the important cell.
3235  if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3236  return;
3237 
3238  ScFormulaCell* pFCell =
3239  new ScFormulaCell(
3240  rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
3241 
3242  pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3243  rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
3244 }
3245 
3246 }
3247 
3249  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3250 {
3251  if (rCxt.meMode != URM_INSDEL)
3252  // Just in case...
3253  return false;
3254 
3255  bool bCellStateChanged = false;
3256  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3257  if ( pUndoCellPos )
3258  aUndoPos = *pUndoCellPos;
3259  ScAddress aOldPos( aPos );
3260  bCellStateChanged = UpdatePosOnShift(rCxt);
3261 
3262  // Check presence of any references or column row names.
3263  bool bHasRefs = pCode->HasReferences();
3264  bool bHasColRowNames = false;
3265  if (!bHasRefs)
3266  {
3267  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3268  bHasRefs = bHasColRowNames;
3269  }
3270  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3271 
3272  if (!bHasRefs && !bOnRefMove)
3273  // This formula cell contains no references, nor needs recalculating
3274  // on reference update. Bail out.
3275  return bCellStateChanged;
3276 
3277  std::unique_ptr<ScTokenArray> pOldCode;
3278  if (pUndoDoc)
3279  pOldCode = pCode->Clone();
3280 
3281  bool bValChanged = false;
3282  bool bRefModified = false;
3283  bool bRecompile = bCompile;
3284 
3285  if (bHasRefs)
3286  {
3287  // Update cell or range references.
3288  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3289  bRefModified = aRes.mbReferenceModified;
3290  bValChanged = aRes.mbValueChanged;
3291  if (aRes.mbNameModified)
3292  bRecompile = true;
3293  }
3294 
3295  if (bValChanged || bRefModified)
3296  bCellStateChanged = true;
3297 
3298  if (bOnRefMove)
3299  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3300  bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3301 
3302  bool bNewListening = false;
3303  bool bInDeleteUndo = false;
3304 
3305  if (bHasRefs)
3306  {
3307  // Upon Insert ColRowNames have to be recompiled in case the
3308  // insertion occurs right in front of the range.
3309  if (bHasColRowNames && !bRecompile)
3310  bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3311 
3312  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3313  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3314 
3315  // RelNameRefs are always moved
3316  bool bHasRelName = false;
3317  if (!bRecompile)
3318  {
3319  RelNameRef eRelNameRef = HasRelNameReference();
3320  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3321  bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3322  }
3323  // Reference changed and new listening needed?
3324  // Except in Insert/Delete without specialities.
3325  bNewListening = (bRefModified || bRecompile
3326  || (bValChanged && bInDeleteUndo) || bHasRelName);
3327 
3328  if ( bNewListening )
3329  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3330  }
3331 
3332  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3333  bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3334 
3335  if (pUndoDoc && (bValChanged || bOnRefMove))
3336  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3337 
3338  bCompile |= bRecompile;
3339  if (bCompile)
3340  {
3341  CompileTokenArray( bNewListening ); // no Listening
3342  bNeedDirty = true;
3343  }
3344 
3345  if ( !bInDeleteUndo )
3346  { // In ChangeTrack Delete-Reject listeners are established in
3347  // InsertCol/InsertRow
3348  if ( bNewListening )
3349  {
3350  // Inserts/Deletes re-establish listeners after all
3351  // UpdateReference calls.
3352  // All replaced shared formula listeners have to be
3353  // established after an Insert or Delete. Do nothing here.
3354  SetNeedsListening( true);
3355  }
3356  }
3357 
3358  if (bNeedDirty)
3359  { // Cut off references, invalid or similar?
3360  // Postpone SetDirty() until all listeners have been re-established in
3361  // Inserts/Deletes.
3362  mbPostponedDirty = true;
3363  }
3364 
3365  return bCellStateChanged;
3366 }
3367 
3369  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3370 {
3371  if (rCxt.meMode != URM_MOVE)
3372  return false;
3373 
3374  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3375  if ( pUndoCellPos )
3376  aUndoPos = *pUndoCellPos;
3377  ScAddress aOldPos( aPos );
3378 
3379  bool bCellInMoveTarget = rCxt.maRange.Contains(aPos);
3380 
3381  if ( bCellInMoveTarget )
3382  {
3383  // The cell is being moved or copied to a new position. I guess the
3384  // position has been updated prior to this call? Determine
3385  // its original position before the move which will be used to adjust
3386  // relative references later.
3387  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3388  }
3389 
3390  // Check presence of any references or column row names.
3391  bool bHasRefs = pCode->HasReferences();
3392  bool bHasColRowNames = false;
3393  if (!bHasRefs)
3394  {
3395  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3396  bHasRefs = bHasColRowNames;
3397  }
3398  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3399 
3400  if (!bHasRefs && !bOnRefMove)
3401  // This formula cell contains no references, nor needs recalculating
3402  // on reference update. Bail out.
3403  return false;
3404 
3405  bool bCellStateChanged = false;
3406  std::unique_ptr<ScTokenArray> pOldCode;
3407  if (pUndoDoc)
3408  pOldCode = pCode->Clone();
3409 
3410  bool bValChanged = false;
3411  bool bRefModified = false;
3412 
3413  if (bHasRefs)
3414  {
3415  // Update cell or range references.
3416  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3417  bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3418  bValChanged = aRes.mbValueChanged;
3419  if (aRes.mbNameModified)
3420  // Re-compile to get the RPN token regenerated to reflect updated names.
3421  bCompile = true;
3422  }
3423 
3424  if (bValChanged || bRefModified)
3425  bCellStateChanged = true;
3426 
3427  if (bOnRefMove)
3428  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3429  bOnRefMove = (bValChanged || (aPos != aOldPos));
3430 
3431  bool bColRowNameCompile = false;
3432  bool bHasRelName = false;
3433  bool bNewListening = false;
3434  bool bInDeleteUndo = false;
3435 
3436  if (bHasRefs)
3437  {
3438  // Upon Insert ColRowNames have to be recompiled in case the
3439  // insertion occurs right in front of the range.
3440  if (bHasColRowNames)
3441  bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3442 
3443  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3444  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3445 
3446  // RelNameRefs are always moved
3447  RelNameRef eRelNameRef = HasRelNameReference();
3448  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3449  bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3450  // Reference changed and new listening needed?
3451  // Except in Insert/Delete without specialties.
3452  bNewListening = (bRefModified || bColRowNameCompile
3453  || bValChanged || bHasRelName)
3454  // #i36299# Don't duplicate action during cut&paste / drag&drop
3455  // on a cell in the range moved, start/end listeners is done
3456  // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3457  && !(rDocument.IsInsertingFromOtherDoc() && rCxt.maRange.Contains(aPos));
3458 
3459  if ( bNewListening )
3460  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3461  }
3462 
3463  bool bNeedDirty = false;
3464  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3465  if ( bRefModified || bColRowNameCompile ||
3466  (bValChanged && bHasRelName ) || bOnRefMove)
3467  bNeedDirty = true;
3468 
3469  if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3470  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3471 
3472  bValChanged = false;
3473 
3474  bCompile = (bCompile || bValChanged || bColRowNameCompile);
3475  if ( bCompile )
3476  {
3477  CompileTokenArray( bNewListening ); // no Listening
3478  bNeedDirty = true;
3479  }
3480 
3481  if ( !bInDeleteUndo )
3482  { // In ChangeTrack Delete-Reject listeners are established in
3483  // InsertCol/InsertRow
3484  if ( bNewListening )
3485  {
3487  }
3488  }
3489 
3490  if (bNeedDirty)
3491  { // Cut off references, invalid or similar?
3492  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3493  SetDirty();
3494  }
3495 
3496  return bCellStateChanged;
3497 }
3498 
3500  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3501 {
3502  if (rCxt.meMode != URM_COPY)
3503  return false;
3504 
3505  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3506  if ( pUndoCellPos )
3507  aUndoPos = *pUndoCellPos;
3508  ScAddress aOldPos( aPos );
3509 
3510  if (rCxt.maRange.Contains(aPos))
3511  {
3512  // The cell is being moved or copied to a new position. I guess the
3513  // position has been updated prior to this call? Determine
3514  // its original position before the move which will be used to adjust
3515  // relative references later.
3516  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3517  }
3518 
3519  // Check presence of any references or column row names.
3520  bool bHasRefs = pCode->HasReferences();
3521  bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3522  bHasRefs = bHasRefs || bHasColRowNames;
3523  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3524 
3525  if (!bHasRefs && !bOnRefMove)
3526  // This formula cell contains no references, nor needs recalculating
3527  // on reference update. Bail out.
3528  return false;
3529 
3530  std::unique_ptr<ScTokenArray> pOldCode;
3531  if (pUndoDoc)
3532  pOldCode = pCode->Clone();
3533 
3534  if (bOnRefMove)
3535  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3536  bOnRefMove = (aPos != aOldPos);
3537 
3538  bool bNeedDirty = bOnRefMove;
3539 
3540  if (pUndoDoc && bOnRefMove)
3541  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3542 
3543  if (bCompile)
3544  {
3545  CompileTokenArray(); // no Listening
3546  bNeedDirty = true;
3547  }
3548 
3549  if (bNeedDirty)
3550  { // Cut off references, invalid or similar?
3551  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3552  SetDirty();
3553  }
3554 
3555  return false;
3556 }
3557 
3559  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3560 {
3561  if (rDocument.IsClipOrUndo())
3562  return false;
3563 
3564  if (mxGroup && mxGroup->mpTopCell != this)
3565  {
3566  // This is not a top cell of a formula group. Don't update references.
3567 
3568  switch (rCxt.meMode)
3569  {
3570  case URM_INSDEL:
3571  return UpdatePosOnShift(rCxt);
3572  default:
3573  ;
3574  }
3575  return false;
3576  }
3577 
3578  switch (rCxt.meMode)
3579  {
3580  case URM_INSDEL:
3581  return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3582  case URM_MOVE:
3583  return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3584  case URM_COPY:
3585  return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3586  default:
3587  ;
3588  }
3589 
3590  return false;
3591 }
3592 
3594 {
3595  // Adjust tokens only when it's not grouped or grouped top cell.
3596  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3597  bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3599  {
3600  if (bPosChanged)
3601  aPos.IncTab(rCxt.mnSheets);
3602 
3603  return;
3604  }
3605 
3607  ScAddress aOldPos = aPos;
3608  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3609  if (bPosChanged)
3610  aPos.IncTab(rCxt.mnSheets);
3611 
3612  if (!bAdjustCode)
3613  return;
3614 
3616  if (aRes.mbNameModified)
3617  // Re-compile after new sheet(s) have been inserted.
3618  bCompile = true;
3619 
3620  // no StartListeningTo because the new sheets have not been inserted yet.
3621 }
3622 
3624 {
3625  // Adjust tokens only when it's not grouped or grouped top cell.
3626  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3627  bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3629  {
3630  if (bPosChanged)
3631  aPos.IncTab(-1*rCxt.mnSheets);
3632  return;
3633  }
3634 
3636  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3637  ScAddress aOldPos = aPos;
3638  if (bPosChanged)
3639  aPos.IncTab(-1*rCxt.mnSheets);
3640 
3641  if (!bAdjustCode)
3642  return;
3643 
3644  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3645  if (aRes.mbNameModified)
3646  // Re-compile after sheet(s) have been deleted.
3647  bCompile = true;
3648 }
3649 
3651 {
3652  // Adjust tokens only when it's not grouped or grouped top cell.
3653  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3654 
3656  {
3657  aPos.SetTab(nTabNo);
3658  return;
3659  }
3660 
3662  ScAddress aOldPos = aPos;
3663  // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3664  aPos.SetTab(nTabNo);
3665 
3666  // no StartListeningTo because pTab[nTab] not yet correct!
3667 
3668  if (!bAdjustCode)
3669  return;
3670 
3671  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3672  if (aRes.mbNameModified)
3673  // Re-compile after sheet(s) have been deleted.
3674  bCompile = true;
3675 }
3676 
3678 {
3679  if (rDocument.IsClipOrUndo())
3680  return;
3681 
3682  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3683  if (!bAdjustCode)
3684  return;
3685 
3688  while (p)
3689  {
3690  ScSingleRefData& rRef1 = *p->GetSingleRef();
3691  if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3692  rRef1.IncTab(1);
3693  if (p->GetType() == formula::svDoubleRef)
3694  {
3695  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3696  if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3697  rRef2.IncTab(1);
3698  }
3699  p = aIter.GetNextReferenceRPN();
3700  }
3701 }
3702 
3704 {
3705  if (rDocument.IsClipOrUndo())
3706  return false;
3707 
3708  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3709  if (!bAdjustCode)
3710  return false;
3711 
3712  bool bRet = false;
3715  while (p)
3716  {
3717  ScSingleRefData& rRef1 = *p->GetSingleRef();
3718  if (!rRef1.IsTabRel())
3719  {
3720  if (nTable != rRef1.Tab())
3721  bRet = true;
3722  else if (nTable != aPos.Tab())
3723  rRef1.SetAbsTab(aPos.Tab());
3724  }
3725  if (p->GetType() == formula::svDoubleRef)
3726  {
3727  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3728  if (!rRef2.IsTabRel())
3729  {
3730  if(nTable != rRef2.Tab())
3731  bRet = true;
3732  else if (nTable != aPos.Tab())
3733  rRef2.SetAbsTab(aPos.Tab());
3734  }
3735  }
3736  p = aIter.GetNextReferenceRPN();
3737  }
3738  return bRet;
3739 }
3740 
3741 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3742 {
3743  if ( bForceIfNameInUse && !bCompile )
3745  if ( bCompile )
3746  pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3748 }
3749 
3751 {
3752  // References to or over filtered rows are not adjusted
3753  // analog to the normal (non-transposed) case
3754  SCCOLROW nTemp = rRef.Col();
3755  rRef.SetRelCol(rRef.Row());
3756  rRef.SetRelRow(nTemp);
3757 }
3758 
3759 // Reference transposition is only called in Clipboard Document
3761 {
3762  bool bFound = false;
3765  while ( ( t = aIter.GetNextReference() ) != nullptr )
3766  {
3767  ScSingleRefData& rRef1 = *t->GetSingleRef();
3768  if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3769  {
3770  bool bDouble = (t->GetType() == formula::svDoubleRef);
3771  ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3772  if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3773  {
3774  lcl_TransposeReference(rRef1);
3775 
3776  if ( bDouble )
3777  lcl_TransposeReference(rRef2);
3778 
3779  bFound = true;
3780  }
3781  }
3782  }
3783 
3784  if (bFound)
3785  bCompile = true;
3786 }
3787 
3788 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3789  ScDocument* pUndoDoc )
3790 {
3792 
3793  ScAddress aOldPos = aPos;
3794  bool bPosChanged = false; // Whether this cell has been moved
3795 
3796  // Dest range is transposed
3797  ScRange aDestRange( rDest, ScAddress(
3798  static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3799  static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3800  rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3801 
3802  // cell within range
3803  if ( aDestRange.Contains( aOldPos ) )
3804  {
3805  // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
3806  // Count back Positions
3807  SCCOL nRelPosX = aOldPos.Col();
3808  SCROW nRelPosY = aOldPos.Row();
3809  SCTAB nRelPosZ = aOldPos.Tab();
3810  ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
3811  aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3812  bPosChanged = true;
3813  }
3814 
3815  std::unique_ptr<ScTokenArray> pOld;
3816  if (pUndoDoc)
3817  pOld = pCode->Clone();
3818  bool bRefChanged = false;
3819 
3822  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3823  {
3824  if( t->GetOpCode() == ocName )
3825  {
3827  if (pName && pName->IsModified())
3828  bRefChanged = true;
3829  }
3830  else if( t->GetType() != svIndex )
3831  {
3832  SingleDoubleRefModifier aMod(*t);
3833  ScComplexRefData& rRef = aMod.Ref();
3834  ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
3835  bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3836  if (bMod)
3837  {
3838  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3839  bRefChanged = true;
3840 
3841  // Absolute sheet reference => set 3D flag.
3842  // More than one sheet referenced => has to have both 3D flags.
3843  // If end part has 3D flag => start part must have it too.
3844  // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
3845  rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
3846  rRef.Ref1.SetFlag3D(
3847  (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
3848  || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
3849  }
3850  }
3851  }
3852 
3853  if (bRefChanged)
3854  {
3855  if (pUndoDoc)
3856  {
3857  // Similar to setOldCodeToUndo(), but it cannot be used due to the check
3858  // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
3859  ScFormulaCell* pFCell = new ScFormulaCell(
3860  *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
3861 
3862  pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3863  pUndoDoc->SetFormulaCell(aPos, pFCell);
3864  }
3865 
3866  bCompile = true;
3867  CompileTokenArray(); // also call StartListeningTo
3868  SetDirty();
3869  }
3870  else
3871  StartListeningTo( rDocument ); // Listener as previous
3872 }
3873 
3874 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3875 {
3877 
3878  bool bRefChanged = false;
3879 
3882 
3883  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3884  {
3885  if( t->GetOpCode() == ocName )
3886  {
3888  if (pName && pName->IsModified())
3889  bRefChanged = true;
3890  }
3891  else if( t->GetType() != svIndex )
3892  {
3893  SingleDoubleRefModifier aMod(*t);
3894  ScComplexRefData& rRef = aMod.Ref();
3895  ScRange aAbs = rRef.toAbs(rDocument, aPos);
3896  bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3897  if (bMod)
3898  {
3899  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
3900  bRefChanged = true;
3901  }
3902  }
3903  }
3904 
3905  if (bRefChanged)
3906  {
3907  bCompile = true;
3908  CompileTokenArray(); // Also call StartListeningTo
3909  SetDirty();
3910  }
3911  else
3912  StartListeningTo( rDocument ); // Listener as previous
3913 }
3914 
3915 // See also ScDocument::FindRangeNamesReferencingSheet()
3916 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
3917  int nRecursion)
3918 {
3919  FormulaTokenArrayPlainIterator aIter(*pCode);
3920  for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3921  {
3922  if (p->GetOpCode() == ocName)
3923  {
3924  sal_uInt16 nTokenIndex = p->GetIndex();
3925  SCTAB nTab = p->GetSheet();
3926  rIndexes.setUpdatedName( nTab, nTokenIndex);
3927 
3928  if (nRecursion < 126) // whatever... 42*3
3929  {
3930  ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3931  if (pSubName)
3932  lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
3933  }
3934  }
3935  }
3936 }
3937 
3939 {
3940  lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
3941 }
3942 
3944 {
3945  bChanged = b;
3946 }
3947 
3948 void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3949 {
3950  assert(!mxGroup); // Don't call this if it's shared.
3951  delete pCode;
3952  pCode = pNew.release(); // takes ownership.
3953 }
3954 
3955 void ScFormulaCell::SetRunning( bool bVal )
3956 {
3957  bRunning = bVal;
3958 }
3959 
3961 {
3963  for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3964  {
3965  OpCode eOp = p->GetOpCode();
3966  if ( eOp == ocDBArea || eOp == ocTableRef )
3967  {
3968  bCompile = true;
3969  CompileTokenArray(rCxt);
3970  SetDirty();
3971  break;
3972  }
3973  }
3974 }
3975 
3977 {
3979  for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3980  {
3981  if ( p->GetOpCode() == ocColRowName )
3982  {
3983  bCompile = true;
3984  CompileTokenArray(rCxt);
3985  SetDirty();
3986  break;
3987  }
3988  }
3989 }
3990 
3995 
3997 {
3998  if (mxGroup)
3999  {
4000  // You can't create a new group if the cell is already a part of a group.
4001  // Is this a sign of some inconsistent or incorrect data structures? Or normal?
4002  SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
4003  return ScFormulaCellGroupRef();
4004  }
4005 
4006  mxGroup.reset(new ScFormulaCellGroup);
4007  mxGroup->mpTopCell = this;
4008  mxGroup->mbInvariant = bInvariant;
4009  mxGroup->mnLength = nLen;
4010  mxGroup->mpCode = std::move(*pCode); // Move this to the shared location.
4011  delete pCode;
4012  pCode = &*mxGroup->mpCode;
4013  return mxGroup;
4014 }
4015 
4017 {
4018  if (!xRef)
4019  {
4020  // Make this cell a non-grouped cell.
4021  if (mxGroup)
4022  pCode = mxGroup->mpCode->Clone().release();
4023 
4024  mxGroup = xRef;
4025  return;
4026  }
4027 
4028  // Group object has shared token array.
4029  if (!mxGroup)
4030  // Currently not shared. Delete the existing token array first.
4031  delete pCode;
4032 
4033  mxGroup = xRef;
4034  pCode = &*mxGroup->mpCode;
4035  mxGroup->mnWeight = 0; // invalidate
4036 }
4037 
4039 {
4040  // no Matrix formulae yet.
4041  if ( GetMatrixFlag() != ScMatrixMode::NONE )
4042  return NotEqual;
4043 
4044  // are these formulas at all similar ?
4045  if ( GetHash() != rOther.GetHash() )
4046  return NotEqual;
4047 
4048  if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4049  return NotEqual;
4050 
4051  FormulaToken **pThis = pCode->GetCode();
4052  sal_uInt16 nThisLen = pCode->GetCodeLen();
4053  FormulaToken **pOther = rOther.pCode->GetCode();
4054  sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4055 
4056  if ( !pThis || !pOther )
4057  {
4058  // Error: no compiled code for cells !"
4059  return NotEqual;
4060  }
4061 
4062  if ( nThisLen != nOtherLen )
4063  return NotEqual;
4064 
4065  // No tokens can be an error cell so check error code, otherwise we could
4066  // end up with a series of equal error values instead of individual error
4067  // values. Also if for any reason different errors are set even if all
4068  // tokens are equal, the cells are not equal.
4069  if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4070  return NotEqual;
4071 
4072  bool bInvariant = true;
4073 
4074  // check we are basically the same function
4075  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4076  {
4077  formula::FormulaToken *pThisTok = pThis[i];
4078  formula::FormulaToken *pOtherTok = pOther[i];
4079 
4080  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4081  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4082  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4083  {
4084  // Incompatible type, op-code or param counts.
4085  return NotEqual;
4086  }
4087 
4088  switch (pThisTok->GetType())
4089  {
4090  case formula::svMatrix:
4093  // Ignoring matrix and external references for now.
4094  return NotEqual;
4095 
4096  case formula::svSingleRef:
4097  {
4098  // Single cell reference.
4099  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4100  if (rRef != *pOtherTok->GetSingleRef())
4101  return NotEqual;
4102 
4103  if (rRef.IsRowRel())
4104  bInvariant = false;
4105  }
4106  break;
4107  case formula::svDoubleRef:
4108  {
4109  // Range reference.
4110  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4111  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4112  if (rRef1 != *pOtherTok->GetSingleRef())
4113  return NotEqual;
4114 
4115  if (rRef2 != *pOtherTok->GetSingleRef2())
4116  return NotEqual;
4117 
4118  if (rRef1.IsRowRel())
4119  bInvariant = false;
4120 
4121  if (rRef2.IsRowRel())
4122  bInvariant = false;
4123  }
4124  break;
4125  case formula::svDouble:
4126  {
4127  if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4128  return NotEqual;
4129  }
4130  break;
4131  case formula::svString:
4132  {
4133  if(pThisTok->GetString() != pOtherTok->GetString())
4134  return NotEqual;
4135  }
4136  break;
4137  case formula::svIndex:
4138  {
4139  if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4140  return NotEqual;
4141  }
4142  break;
4143  case formula::svByte:
4144  {
4145  if(pThisTok->GetByte() != pOtherTok->GetByte())
4146  return NotEqual;
4147  }
4148  break;
4149  case formula::svExternal:
4150  {
4151  if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4152  return NotEqual;
4153 
4154  if (pThisTok->GetByte() != pOtherTok->GetByte())
4155  return NotEqual;
4156  }
4157  break;
4158  case formula::svError:
4159  {
4160  if (pThisTok->GetError() != pOtherTok->GetError())
4161  return NotEqual;
4162  }
4163  break;
4164  default:
4165  ;
4166  }
4167  }
4168 
4169  // If still the same, check lexical names as different names may result in
4170  // identical RPN code.
4171 
4172  pThis = pCode->GetArray();
4173  nThisLen = pCode->GetLen();
4174  pOther = rOther.pCode->GetArray();
4175  nOtherLen = rOther.pCode->GetLen();
4176 
4177  if ( !pThis || !pOther )
4178  {
4179  // Error: no code for cells !"
4180  return NotEqual;
4181  }
4182 
4183  if ( nThisLen != nOtherLen )
4184  return NotEqual;
4185 
4186  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4187  {
4188  formula::FormulaToken *pThisTok = pThis[i];
4189  formula::FormulaToken *pOtherTok = pOther[i];
4190 
4191  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4192  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4193  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4194  {
4195  // Incompatible type, op-code or param counts.
4196  return NotEqual;
4197  }
4198 
4199  switch (pThisTok->GetType())
4200  {
4201  // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4202  // resulting in identical RPN references that could lead to creating
4203  // a formula group from formulas that should not be merged into a group,
4204  // so check also the formula itself.
4205  case formula::svSingleRef:
4206  {
4207  // Single cell reference.
4208  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4209  if (rRef != *pOtherTok->GetSingleRef())
4210  return NotEqual;
4211 
4212  if (rRef.IsRowRel())
4213  bInvariant = false;
4214  }
4215  break;
4216  case formula::svDoubleRef:
4217  {
4218  // Range reference.
4219  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4220  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4221  if (rRef1 != *pOtherTok->GetSingleRef())
4222  return NotEqual;
4223 
4224  if (rRef2 != *pOtherTok->GetSingleRef2())
4225  return NotEqual;
4226 
4227  if (rRef1.IsRowRel())
4228  bInvariant = false;
4229 
4230  if (rRef2.IsRowRel())
4231  bInvariant = false;
4232  }
4233  break;
4234  // All index tokens are names. Different categories already had
4235  // different OpCode values.
4236  case formula::svIndex:
4237  {
4238  if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4239  return NotEqual;
4240  switch (pThisTok->GetOpCode())
4241  {
4242  case ocTableRef:
4243  // nothing, sheet value assumed as -1, silence
4244  // ScTableRefToken::GetSheet() SAL_WARN about
4245  // unhandled
4246  ;
4247  break;
4248  default: // ocName, ocDBArea
4249  if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4250  return NotEqual;
4251  }
4252  }
4253  break;
4254  default:
4255  ;
4256  }
4257  }
4258 
4259  return bInvariant ? EqualInvariant : EqualRelativeRef;
4260 }
4261 
4262 namespace {
4263 
4264 // Split N into optimally equal-sized pieces, each not larger than K.
4265 // Return value P is number of pieces. A returns the number of pieces
4266 // one larger than N/P, 0..P-1.
4267 
4268 int splitup(int N, int K, int& A)
4269 {
4270  assert(N > 0);
4271  assert(K > 0);
4272 
4273  A = 0;
4274 
4275  if (N <= K)
4276  return 1;
4277 
4278  const int ideal_num_parts = N / K;
4279  if (ideal_num_parts * K == N)
4280  return ideal_num_parts;
4281 
4282  const int num_parts = ideal_num_parts + 1;
4283  const int nominal_part_size = N / num_parts;
4284 
4285  A = N - num_parts * nominal_part_size;
4286 
4287  return num_parts;
4288 }
4289 
4290 struct ScDependantsCalculator
4291 {
4292  ScDocument& mrDoc;
4293  const ScTokenArray& mrCode;
4294  const ScFormulaCellGroupRef& mxGroup;
4295  const SCROW mnLen;
4296  const ScAddress& mrPos;
4297  const bool mFromFirstRow;
4298  const SCROW mnStartOffset;
4299  const SCROW mnEndOffset;
4300  const SCROW mnSpanLen;
4301 
4302  ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4303  const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4304  mrDoc(rDoc),
4305  mrCode(rCode),
4306  mxGroup(rCell.GetCellGroup()),
4307  mnLen(mxGroup->mnLength),
4308  mrPos(rPos),
4309  // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4310  // only from further rows. This data fetching could also lead to Interpret() calls, so
4311  // in OpenCL mode the formula in practice depends on those cells too.
4312  mFromFirstRow(fromFirstRow),
4313  mnStartOffset(nStartOffset),
4314  mnEndOffset(nEndOffset),
4315  mnSpanLen(nEndOffset - nStartOffset + 1)
4316  {
4317  }
4318 
4319  // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4320  // (note already modified a bit, mFromFirstRow)
4321 
4322  // I think what this function does is to check whether the relative row reference nRelRow points
4323  // to a row that is inside the range of rows covered by the formula group.
4324 
4325  bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4326  {
4327  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4328  return false;
4329 
4330  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4331 
4332  if (nRelRow <= 0)
4333  {
4334  SCROW nTest = nEndRow;
4335  nTest += nRelRow;
4336  if (nTest >= mrPos.Row())
4337  return true;
4338  }
4339  else
4340  {
4341  SCROW nTest = mrPos.Row(); // top row.
4342  nTest += nRelRow;
4343  if (nTest <= nEndRow)
4344  return true;
4345  // If pointing below the formula, it's always included if going from first row.
4346  if (mFromFirstRow)
4347  return true;
4348  }
4349 
4350  return false;
4351  }
4352 
4353  // FIXME: another copy-paste
4354 
4355  // And this correspondingly checks whether an absolute row is inside the range of rows covered
4356  // by the formula group.
4357 
4358  bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4359  {
4360  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4361  return false;
4362 
4363  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4364 
4365  if (rRefPos.Row() < mrPos.Row())
4366  return false;
4367 
4368  // If pointing below the formula, it's always included if going from first row.
4369  if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4370  return false;
4371 
4372  return true;
4373  }
4374 
4375  // Checks if the doubleref engulfs all of formula group cells
4376  // Note : does not check if there is a partial overlap, that can be done by calling
4377  // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4378  bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4379  {
4380  if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4381  || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4382  {
4383  return false;
4384  }
4385 
4386  SCROW nStartRow = mrPos.Row();
4387  SCROW nEndRow = nStartRow + mnLen - 1;
4388  SCROW nRefStartRow = rAbs.aStart.Row();
4389  SCROW nRefEndRow = rAbs.aEnd.Row();
4390 
4391  if (bIsRef1RowRel && bIsRef2RowRel &&
4392  ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4393  ((nRefStartRow + mnLen - 1) <= nStartRow &&
4394  (nRefEndRow + mnLen - 1) >= nEndRow)))
4395  return true;
4396 
4397  if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4398  (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4399  return true;
4400 
4401  if (!bIsRef2RowRel &&
4402  nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4403  return true;
4404 
4405  // If going from first row, the referenced range must be entirely above the formula,
4406  // otherwise the formula would be included.
4407  if (mFromFirstRow && nRefEndRow >= nStartRow)
4408  return true;
4409 
4410  return false;
4411  }
4412 
4413  // FIXME: another copy-paste
4414  SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4415  {
4416  SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4417  nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4418  if (nLastRow < (nRow + nRowLen - 1))
4419  {
4420  // This can end up negative! Was that the original intent, or
4421  // is it accidental? Was it not like that originally but the
4422  // surrounding conditions changed?
4423  nRowLen = nLastRow - nRow + 1;
4424  // Anyway, let's assume it doesn't make sense to return a
4425  // negative or zero value here.
4426  if (nRowLen <= 0)
4427  nRowLen = 1;
4428  }
4429  else if (nLastRow == 0)
4430  // Column is empty.
4431  nRowLen = 1;
4432 
4433  return nRowLen;
4434  }
4435 
4436  bool DoIt()
4437  {
4438  // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4439 
4440  ScRangeList aRangeList;
4441 
4442  // Self references should be checked by considering the entire formula-group not just the provided span.
4443  bool bHasSelfReferences = false;
4444  bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4445 
4446  FormulaToken** pRPNArray = mrCode.GetCode();
4447  sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4448  for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4449  {
4450  auto p = pRPNArray[nTokenIdx];
4451  if (!bInDocShellRecalc)
4452  {
4453  // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4454  // of the result of the condition expression.
4455  // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4456  // in the document. So lets disable threading and stop dependency evaluation if
4457  // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4458  // for formulae with IF/IFS/SWITCH
4459  OpCode nOpCode = p->GetOpCode();
4460  if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4461  return false;
4462  }
4463 
4464  switch (p->GetType())
4465  {
4466  case svSingleRef:
4467  {
4468  ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4469  if( aRef.IsDeleted())
4470  return false;
4471  ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
4472 
4473  if (!mrDoc.TableExists(aRefPos.Tab()))
4474  return false; // or true?
4475 
4476  if (aRef.IsRowRel())
4477  {
4478  if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4479  {
4480  bHasSelfReferences = true;
4481  continue;
4482  }
4483 
4484  // Trim data array length to actual data range.
4485  SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4486 
4487  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4488  aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4489  }
4490  else
4491  {
4492  if (isSelfReferenceAbsolute(aRefPos))
4493  {
4494  bHasSelfReferences = true;
4495  continue;
4496  }
4497 
4498  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4499  }
4500  }
4501  break;
4502  case svDoubleRef:
4503  {
4504  ScComplexRefData aRef = *p->GetDoubleRef();
4505  if( aRef.IsDeleted())
4506  return false;
4507  ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
4508 
4509  // Multiple sheet
4510  if (aRef.Ref1.Tab() != aRef.Ref2.Tab())
4511  return false;
4512 
4513  bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4514  // Check for self reference.
4515  if (bIsRef1RowRel)
4516  {
4517  if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4518  {
4519  bHasSelfReferences = true;
4520  continue;
4521  }
4522  }
4523  else if (isSelfReferenceAbsolute(aAbs.aStart))
4524  {
4525  bHasSelfReferences = true;
4526  continue;
4527  }
4528 
4529  bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4530  if (bIsRef2RowRel)
4531  {
4532  if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4533  {
4534  bHasSelfReferences = true;
4535  continue;
4536  }
4537  }
4538  else if (isSelfReferenceAbsolute(aAbs.aEnd))
4539  {
4540  bHasSelfReferences = true;
4541  continue;
4542  }
4543 
4544  if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4545  {
4546  bHasSelfReferences = true;
4547  continue;
4548  }
4549 
4550  // The first row that will be referenced through the doubleref.
4551  SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4552  // The last row that will be referenced through the doubleref.
4553  SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4554  // Number of rows to be evaluated from nFirstRefRow.
4555  SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4556  assert(nArrayLength > 0);
4557 
4558  // Trim trailing empty rows.
4559  nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4560 
4561  aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4562  aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4563  }
4564  break;
4565  default:
4566  break;
4567  }
4568  }
4569 
4570  // Compute dependencies irrespective of the presence of any self references.
4571  // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4572  // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4573  for (size_t i = 0; i < aRangeList.size(); ++i)
4574  {
4575  const ScRange & rRange = aRangeList[i];
4576  assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4577  for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4578  {
4579  SCROW nStartRow = rRange.aStart.Row();
4580  SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4581  if( mFromFirstRow )
4582  { // include also all previous rows
4583  nLength += nStartRow;
4584  nStartRow = 0;
4585  }
4586  if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4587  nLength, mxGroup))
4588  return false;
4589  }
4590  }
4591 
4592  if (bHasSelfReferences)
4593  mxGroup->mbPartOfCycle = true;
4594 
4595  return !bHasSelfReferences;
4596  }
4597 };
4598 
4599 } // anonymous namespace
4600 
4601 bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset)
4602 {
4603  if (!mxGroup || !pCode)
4604  return false;
4605 
4606  auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
4607  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4608 
4609  if (mxGroup->mbPartOfCycle)
4610  {
4611  aScope.addMessage("This formula-group is part of a cycle");
4612  return false;
4613  }
4614 
4615  if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4616  {
4617  static constexpr OUStringLiteral MESSAGE = u"group calc disabled";
4618  aScope.addMessage(MESSAGE);
4619  return false;
4620  }
4621 
4622  // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests
4624  if (forceType == ForceCalculationCore
4625  || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4626  && forceType != ForceCalculationOpenCL
4627  && forceType != ForceCalculationThreads))
4628  {
4629  mxGroup->meCalcState = sc::GroupCalcDisabled;
4630  aScope.addGroupSizeThresholdMessage(*this);
4631  return false;
4632  }
4633 
4634  if (cMatrixFlag != ScMatrixMode::NONE)
4635  {
4636  mxGroup->meCalcState = sc::GroupCalcDisabled;
4637  aScope.addMessage("matrix skipped");
4638  return false;
4639  }
4640 
4641  if( forceType != ForceCalculationNone )
4642  {
4643  // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4644  // without it actually being in the document at the specified position.
4645  // That would confuse opencl/threading code, as they refer to the cell group
4646  // also using the position. This is normally not triggered (single cells
4647  // are normally not in a cell group), but if forced, check for this explicitly.
4648  if( rDocument.GetFormulaCell( aPos ) != this )
4649  {
4650  mxGroup->meCalcState = sc::GroupCalcDisabled;
4651  aScope.addMessage("cell not in document");
4652  return false;
4653  }
4654  }
4655 
4656  // Get rid of -1's in offsets (defaults) or any invalid offsets.
4657  SCROW nMaxOffset = mxGroup->mnLength - 1;
4658  nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4659  nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4660 
4661  if (nEndOffset < nStartOffset)
4662  {
4663  nStartOffset = 0;
4664  nEndOffset = nMaxOffset;
4665  }
4666 
4667  if (nEndOffset == nStartOffset)
4668  return false; // Do not use threads for a single row.
4669 
4670  // Guard against endless recursion of Interpret() calls, for this to work
4671  // ScFormulaCell::InterpretFormulaGroup() must never be called through
4672  // anything else than ScFormulaCell::Interpret(), same as
4673  // ScFormulaCell::InterpretTail()
4674  RecursionCounter aRecursionCounter( rRecursionHelper, this);
4675 
4676  bool bDependencyComputed = false;
4677  bool bDependencyCheckFailed = false;
4678 
4679  // Preference order: First try OpenCL, then threading.
4680  // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4681  if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4682  return true;
4683 
4684  if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4685  return true;
4686 
4687  return false;
4688 }
4689 
4691  SCROW nStartOffset, SCROW nEndOffset,
4692  bool bCalcDependencyOnly)
4693 {
4694  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4695  // iterate over code in the formula ...
4696  // ensure all input is pre-calculated -
4697  // to avoid writing during the calculation
4698  if (bCalcDependencyOnly)
4699  {
4700  // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4701  // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4702  // (We can only reach here from a multi-group dependency evaluation attempt).
4703  // (These two have to be in pairs always for any given formula-group)
4704  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4705  return aCalculator.DoIt();
4706  }
4707 
4708  bool bOKToParallelize = false;
4709  {
4710  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4711  if (mxGroup->mbPartOfCycle)
4712  {
4713  mxGroup->meCalcState = sc::GroupCalcDisabled;
4714  rScope.addMessage("found circular formula-group dependencies");
4715  return false;
4716  }
4717 
4718  ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4719  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4720  bOKToParallelize = aCalculator.DoIt();
4721 
4722  }
4723 
4724  if (rRecursionHelper.IsInRecursionReturn())
4725  {
4726  mxGroup->meCalcState = sc::GroupCalcDisabled;
4727  rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4728  return false;
4729  }
4730 
4731  if (mxGroup->mbPartOfCycle)
4732  {
4733  mxGroup->meCalcState = sc::GroupCalcDisabled;
4734  rScope.addMessage("found circular formula-group dependencies");
4735  return false;
4736  }
4737 
4738  if (!rRecursionHelper.AreGroupsIndependent())
4739  {
4740  // This call resulted from a dependency calculation for a multigroup-threading attempt,
4741  // but found dependency among the groups.
4742  rScope.addMessage("multi-group-dependency failed");
4743  return false;
4744  }
4745 
4746  if (!bOKToParallelize)
4747  {
4748  mxGroup->meCalcState = sc::GroupCalcDisabled;
4749  rScope.addMessage("could not do new dependencies calculation thing");
4750  return false;
4751  }
4752 
4753  return true;
4754 }
4755 
4756 static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc,
4758  std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4759 {
4760  const SCROW nLen = xGroup->mnLength;
4761  const sal_Int32 nWt = xGroup->mnWeight;
4762  ScAddress aAddr(xGroup->mpTopCell->aPos);
4763 
4764  SCCOL nColRet = aAddr.Col();
4765 
4766  const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4767  if (bLeft)
4768  --nColRet;
4769  else
4770  ++nColRet;
4771 
4772  while (nColRet >= 0 && nColRet <= nMaxCol)
4773  {
4774  aAddr.SetCol(nColRet);
4775  const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4776  if (!pCell)
4777  break;
4778 
4779  if (!pCell->NeedsInterpret())
4780  break;
4781 
4782  const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4783  if (!xNGroup)
4784  break;
4785 
4786  if (!pCell->GetCode()->IsEnabledForThreading())
4787  break;
4788 
4789  if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4790  break;
4791 
4792  const SCROW nNLen = xNGroup->mnLength;
4793  const sal_Int32 nNWt = pCell->GetWeight();
4794  if (nNLen != nLen || nNWt != nWt)
4795  break;
4796 
4797  rFGSet.insert(xNGroup.get());
4798  rFGMap[nColRet] = xNGroup->mpTopCell;
4799 
4800  if (bLeft)
4801  --nColRet;
4802  else
4803  ++nColRet;
4804  }
4805 
4806  if (bLeft)
4807  ++nColRet;
4808  else
4809  --nColRet;
4810 
4811  return nColRet;
4812 }
4813 
4814 // To be called only from InterpretFormulaGroup().
4816  bool& bDependencyComputed,
4817  bool& bDependencyCheckFailed,
4818  SCROW nStartOffset,
4819  SCROW nEndOffset)
4820 {
4821  static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4822  if (!bDependencyCheckFailed && !bThreadingProhibited &&
4825  {
4826  if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4827  {
4828  bDependencyComputed = true;
4829  bDependencyCheckFailed = true;
4830  return false;
4831  }
4832 
4833  bDependencyComputed = true;
4834 
4835  // Then do the threaded calculation
4836 
4837  class Executor : public comphelper::ThreadTask
4838  {
4839  private:
4840  const unsigned mnThisThread;
4841  const unsigned mnThreadsTotal;
4842  ScDocument* mpDocument;
4844  const ScAddress& mrTopPos;
4845  SCCOL mnStartCol;
4846  SCCOL mnEndCol;
4847  SCROW mnStartOffset;
4848  SCROW mnEndOffset;
4849 
4850  public:
4851  Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4852  unsigned nThisThread,
4853  unsigned nThreadsTotal,
4854  ScDocument* pDocument2,
4855  ScInterpreterContext* pContext,
4856  const ScAddress& rTopPos,
4857  SCCOL nStartCol,
4858  SCCOL nEndCol,
4859  SCROW nStartOff,
4860  SCROW nEndOff) :
4861  comphelper::ThreadTask(rTag),
4862  mnThisThread(nThisThread),
4863  mnThreadsTotal(nThreadsTotal),
4864  mpDocument(pDocument2),
4865  mpContext(pContext),
4866  mrTopPos(rTopPos),
4867  mnStartCol(nStartCol),
4868  mnEndCol(nEndCol),
4869  mnStartOffset(nStartOff),
4870  mnEndOffset(nEndOff)
4871  {
4872  }
4873 
4874  virtual void doWork() override
4875  {
4876  ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4877  mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4878  mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4879  }
4880 
4881  };
4882 
4883  SvNumberFormatter* pNonThreadedFormatter = rDocument.GetNonThreadedContext().GetFormatTable();
4884 
4886  sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4887 
4888  SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4889 
4891  std::map<SCCOL, ScFormulaCell*> aFGMap;
4892  aFGSet.insert(mxGroup.get());
4893 
4894  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4895  SCCOL nColStart = aPos.Col();
4896  SCCOL nColEnd = nColStart;
4897  if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
4898  {
4899  nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
4900  nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
4901  }
4902 
4903  if (nColStart != nColEnd)
4904  {
4905  ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4906  for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4907  {
4908  if (nCurrCol == aPos.Col())
4909  continue;
4910 
4911  bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4912  if (!bFGOK || !aGuard.AreGroupsIndependent())
4913  {
4914  nColEnd = nColStart = aPos.Col();
4915  break;
4916  }
4917  }
4918  }
4919 
4920  std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
4921  {
4924 
4926 
4927  // Start nThreadCount new threads
4928  std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4929  ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
4930  ScInterpreterContext* context = nullptr;
4931 
4932  for (int i = 0; i < nThreadCount; ++i)
4933  {
4934  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4935  assert(!context->pInterpreter);
4936  aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
4937  context->pInterpreter = aInterpreters[i].get();
4939  rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos,
4940  nColStart, nColEnd, nStartOffset, nEndOffset));
4941  }
4942 
4943  SAL_INFO("sc.threaded", "Waiting for threads to finish work");
4944  // Do not join the threads here. They will get joined in ScDocument destructor
4945  // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
4946  rThreadPool.waitUntilDone(aTag, false);
4947 
4949 
4950  for (int i = 0; i < nThreadCount; ++i)
4951  {
4952  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4953  // This is intentionally done in this main thread in order to avoid locking.
4955  context->pInterpreter = nullptr;
4956  }
4957 
4958  SAL_INFO("sc.threaded", "Done");
4959  }
4960 
4961  ScAddress aStartPos(mxGroup->mpTopCell->aPos);
4962  SCROW nSpanLen = nEndOffset - nStartOffset + 1;
4963  aStartPos.SetRow(aStartPos.Row() + nStartOffset);
4964  // Reuse one of the previously allocated interpreter objects here.
4965  rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
4966  aStartPos.Tab(), aInterpreters[0].get());
4967 
4968  return true;
4969  }
4970 
4971  return false;
4972 }
4973 
4974 // To be called only from InterpretFormulaGroup().
4976  bool& bDependencyComputed,
4977  bool& bDependencyCheckFailed)
4978 {
4979  bool bCanVectorize = pCode->IsEnabledForOpenCL();
4980  switch (pCode->GetVectorState())
4981  {
4982  case FormulaVectorEnabled:
4984  break;
4985 
4986  // Not good.
4988  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4989  break;
4991  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4992  break;
4994  aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
4995  break;
4996  case FormulaVectorDisabled:
4997  case FormulaVectorUnknown:
4998  default:
4999  aScope.addMessage("group calc disabled due to vector state (unknown)");
5000  return false;
5001  }
5002 
5003  if (!bCanVectorize)
5004  return false;
5005 
5007  {
5008  aScope.addMessage("opencl not enabled");
5009  return false;
5010  }
5011 
5012  // TableOp does tricks with using a cell with different values, just bail out.
5014  return false;
5015 
5016  if (bDependencyCheckFailed)
5017  return false;
5018 
5019  if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
5020  {
5021  bDependencyComputed = true;
5022  bDependencyCheckFailed = true;
5023  return false;
5024  }
5025 
5026  bDependencyComputed = true;
5027 
5028  // TODO : Disable invariant formula group interpretation for now in order
5029  // to get implicit intersection to work.
5030  if (mxGroup->mbInvariant && false)
5032 
5033  int nMaxGroupLength = INT_MAX;
5034 
5035 #ifdef _WIN32
5036  // Heuristic: Certain old low-end OpenCL implementations don't
5037  // work for us with too large group lengths. 1000 was determined
5038  // empirically to be a good compromise.
5039  if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
5040  nMaxGroupLength = 1000;
5041 #endif
5042 
5043  if (std::getenv("SC_MAX_GROUP_LENGTH"))
5044  nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
5045 
5046  int nNumOnePlus;
5047  const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
5048 
5049  int nOffset = 0;
5050  int nCurChunkSize;
5051  ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
5052  for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
5053  {
5054  nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
5055 
5056  ScFormulaCellGroupRef xGroup;
5057 
5058  if (nNumParts == 1)
5059  xGroup = mxGroup;
5060  else
5061  {
5062  // Ugly hack
5063  xGroup = new ScFormulaCellGroup();
5064  xGroup->mpTopCell = mxGroup->mpTopCell;
5065  xGroup->mpTopCell->aPos = aOrigPos;
5066  xGroup->mpTopCell->aPos.IncRow(nOffset);
5067  xGroup->mbInvariant = mxGroup->mbInvariant;
5068  xGroup->mnLength = nCurChunkSize;
5069  xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
5070  }
5071 
5072  ScTokenArray aCode(rDocument);
5073  ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos);
5074  // TODO avoid this extra compilation
5075  ScCompiler aComp( rDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
5076  aComp.CompileTokenArray();
5077  if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
5078  {
5080  {
5081  SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
5082 #ifdef DBG_UTIL
5083  for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
5084  {
5085  SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
5086  << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
5087  << "(" << int(opcode) << ")");
5088  }
5089 #endif
5090  }
5091  else
5092  SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
5093 
5094  mxGroup->meCalcState = sc::GroupCalcDisabled;
5095 
5096  // Undo the hack above
5097