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  OSL_FAIL("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 void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
947  const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
948 {
949  if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
950  {
952  return;
953  }
955  {
956  // Reference to another cell that contains a matrix formula.
959  if( p )
960  {
961  /* FIXME: original GetFormula() code obtained
962  * pCell only if (!IsInChangeTrack()),
963  * GetEnglishFormula() omitted that test.
964  * Can we live without in all cases? */
965  ScFormulaCell* pCell = nullptr;
966  ScSingleRefData& rRef = *p->GetSingleRef();
967  ScAddress aAbs = rRef.toAbs(rDocument, aPos);
968  if (rDocument.ValidAddress(aAbs))
969  pCell = rDocument.GetFormulaCell(aAbs);
970 
971  if (pCell)
972  {
973  pCell->GetFormula( rBuffer, eGrammar, pContext );
974  return;
975  }
976  else
977  {
978  ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
979  aComp.CreateStringFromTokenArray( rBuffer );
980  }
981  }
982  else
983  {
984  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
985  }
986  }
987  else
988  {
989  ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext );
990  aComp.CreateStringFromTokenArray( rBuffer );
991  }
992 
993  rBuffer.insert( 0, '=');
995  {
996  rBuffer.insert( 0, '{');
997  rBuffer.append( '}');
998  }
999 }
1000 
1001 void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar,
1002  const ScInterpreterContext* pContext ) const
1003 {
1004  OUStringBuffer rBuffer( rFormula );
1005  GetFormula( rBuffer, eGrammar, pContext );
1006  rFormula = rBuffer.makeStringAndClear();
1007 }
1008 
1010 {
1011  OUStringBuffer aBuf;
1012  if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
1013  {
1014  ScTokenArray aCode(rCxt.getDoc());
1016  ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
1017  aComp.CreateStringFromTokenArray(aBuf);
1018  return aBuf.makeStringAndClear();
1019  }
1020  else if( cMatrixFlag == ScMatrixMode::Reference )
1021  {
1022  // Reference to another cell that contains a matrix formula.
1025  if( p )
1026  {
1027  /* FIXME: original GetFormula() code obtained
1028  * pCell only if (!IsInChangeTrack()),
1029  * GetEnglishFormula() omitted that test.
1030  * Can we live without in all cases? */
1031  ScFormulaCell* pCell = nullptr;
1032  ScSingleRefData& rRef = *p->GetSingleRef();
1033  ScAddress aAbs = rRef.toAbs(rDocument, aPos);
1034  if (rDocument.ValidAddress(aAbs))
1035  pCell = rDocument.GetFormulaCell(aAbs);
1036 
1037  if (pCell)
1038  {
1039  return pCell->GetFormula(rCxt);
1040  }
1041  else
1042  {
1043  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1044  aComp.CreateStringFromTokenArray(aBuf);
1045  }
1046  }
1047  else
1048  {
1049  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1050  }
1051  }
1052  else
1053  {
1054  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1055  aComp.CreateStringFromTokenArray(aBuf);
1056  }
1057 
1058  aBuf.insert( 0, '=');
1060  {
1061  aBuf.insert( 0, '{');
1062  aBuf.append( '}');
1063  }
1064 
1065  return aBuf.makeStringAndClear();
1066 }
1067 
1069 {
1070  MaybeInterpret();
1071 
1072  if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
1073  {
1074  const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
1075  if (pMat)
1076  {
1077  pMat->GetDimensions( rCols, rRows );
1078  if (pCode->IsHyperLink())
1079  {
1080  // Row 2 element is the URL that is not to be displayed and the
1081  // result dimension not to be extended.
1082  assert(rRows == 2);
1083  rRows = 1;
1084  }
1085  return;
1086  }
1087  }
1088  rCols = 0;
1089  rRows = 0;
1090 }
1091 
1094 
1096 {
1097  mbPostponedDirty = bVar;
1098 }
1099 
1101 {
1103 }
1104 
1105 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1106  const FormulaGrammar::Grammar eGrammar )
1107 {
1108  if ( rDocument.IsClipOrUndo() )
1109  return;
1110  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1111  if ( bWasInFormulaTree )
1113  // pCode may not deleted for queries, but must be empty
1114  if ( pCode )
1115  pCode->Clear();
1116  ScTokenArray* pCodeOld = pCode;
1117  ScCompiler aComp( rDocument, aPos, eGrammar);
1118  pCode = aComp.CompileString( rFormula ).release();
1119  assert(!mxGroup);
1120  delete pCodeOld;
1121  if( pCode->GetCodeError() == FormulaError::NONE )
1122  {
1123  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1124  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1125  if ( rFormula[0] == '=' )
1126  pCode->AddBad( rFormula.copy(1) );
1127  else
1128  pCode->AddBad( rFormula );
1129  }
1130  bCompile = true;
1131  CompileTokenArray( bNoListening );
1132  }
1133  else
1134  bChanged = true;
1135 
1136  if ( bWasInFormulaTree )
1137  rDocument.PutInFormulaTree( this );
1138 }
1139 
1141  sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1142 {
1143  if ( rDocument.IsClipOrUndo() )
1144  return;
1145  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1146  if ( bWasInFormulaTree )
1148  // pCode may not deleted for queries, but must be empty
1149  if ( pCode )
1150  pCode->Clear();
1151  ScTokenArray* pCodeOld = pCode;
1152  ScCompiler aComp(rCxt, aPos);
1153  pCode = aComp.CompileString( rFormula ).release();
1154  assert(!mxGroup);
1155  delete pCodeOld;
1156  if( pCode->GetCodeError() == FormulaError::NONE )
1157  {
1158  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1159  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1160  if ( rFormula[0] == '=' )
1161  pCode->AddBad( rFormula.copy(1) );
1162  else
1163  pCode->AddBad( rFormula );
1164  }
1165  bCompile = true;
1166  CompileTokenArray(rCxt, bNoListening);
1167  }
1168  else
1169  bChanged = true;
1170 
1171  if ( bWasInFormulaTree )
1172  rDocument.PutInFormulaTree( this );
1173 }
1174 
1175 void ScFormulaCell::CompileTokenArray( bool bNoListening )
1176 {
1177  // Not already compiled?
1178  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1179  {
1180  Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1181  }
1182  else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1183  {
1184  // RPN length may get changed
1185  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1186  if ( bWasInFormulaTree )
1188 
1189  // Loading from within filter? No listening yet!
1191  bNoListening = true;
1192 
1193  if( !bNoListening && pCode->GetCodeLen() )
1196  bSubTotal = aComp.CompileTokenArray();
1197  if( pCode->GetCodeError() == FormulaError::NONE )
1198  {
1199  nFormatType = aComp.GetNumFormatType();
1200  bChanged = true;
1201  aResult.SetToken( nullptr);
1202  bCompile = false;
1203  if ( !bNoListening )
1205  }
1206  if ( bWasInFormulaTree )
1207  rDocument.PutInFormulaTree( this );
1208 
1209  if (bSubTotal)
1210  rDocument.AddSubTotalCell(this);
1211  }
1212 }
1213 
1215 {
1216  // Not already compiled?
1217  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1218  {
1219  rCxt.setGrammar(eTempGrammar);
1220  Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1221  }
1222  else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1223  {
1224  // RPN length may get changed
1225  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this );
1226  if ( bWasInFormulaTree )
1228 
1229  // Loading from within filter? No listening yet!
1231  bNoListening = true;
1232 
1233  if( !bNoListening && pCode->GetCodeLen() )
1235  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1236  bSubTotal = aComp.CompileTokenArray();
1237  if( pCode->GetCodeError() == FormulaError::NONE )
1238  {
1239  nFormatType = aComp.GetNumFormatType();
1240  bChanged = true;
1241  aResult.SetToken( nullptr);
1242  bCompile = false;
1243  if ( !bNoListening )
1245  }
1246  if ( bWasInFormulaTree )
1247  rDocument.PutInFormulaTree( this );
1248 
1249  if (bSubTotal)
1250  rDocument.AddSubTotalCell(this);
1251  }
1252 }
1253 
1255 {
1257  { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1258  // just establish listeners
1260  return ;
1261  }
1262 
1263  // Error constant formula cell stays as is.
1264  if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
1265  return;
1266 
1267  // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1268  // was in to update its count.
1269  bool bWasInFormulaTree = rDocument.IsInFormulaTree( this);
1270  if (bWasInFormulaTree)
1272  rCxt.setGrammar(eTempGrammar);
1273  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1274  OUString aFormula, aFormulaNmsp;
1275  aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1276  rDocument.DecXMLImportedFormulaCount( aFormula.getLength() );
1278  // pCode may not deleted for queries, but must be empty
1279  pCode->Clear();
1280 
1281  bool bDoCompile = true;
1282 
1283  if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1284  {
1285  ScAddress aPreviousCell( aPos );
1286  aPreviousCell.IncRow( -1 );
1287  ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell );
1288  if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
1289  {
1290  // Build formula string using the tokens from the previous cell,
1291  // but use the current cell position.
1292  ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1293  OUStringBuffer aShouldBeBuf;
1294  aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1295 
1296  // The initial '=' is optional in ODFF.
1297  const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
1298  OUString aShouldBe = aShouldBeBuf.makeStringAndClear();
1299  if (aFormula.getLength() == aShouldBe.getLength() + nLeadingEqual &&
1300  aFormula.match( aShouldBe, nLeadingEqual))
1301  {
1302  // Put them in the same formula group.
1303  ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1304  if (!xGroup) // Last cell is not grouped yet. Start a new group.
1305  xGroup = pPreviousCell->CreateCellGroup(1, false);
1306  ++xGroup->mnLength;
1307  SetCellGroup( xGroup );
1308 
1309  // Do setup here based on previous cell.
1310 
1311  nFormatType = pPreviousCell->nFormatType;
1312  bSubTotal = pPreviousCell->bSubTotal;
1313  bChanged = true;
1314  bCompile = false;
1315 
1316  if (bSubTotal)
1317  rDocument.AddSubTotalCell(this);
1318 
1319  bDoCompile = false;
1320  pCode = pPreviousCell->pCode;
1321  if (pPreviousCell->mbIsExtRef)
1322  rDocument.GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
1323  }
1324  }
1325  }
1326 
1327  if (bDoCompile)
1328  {
1329  ScTokenArray* pCodeOld = pCode;
1330  pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
1331  assert(!mxGroup);
1332  delete pCodeOld;
1333 
1334  if( pCode->GetCodeError() == FormulaError::NONE )
1335  {
1336  if ( !pCode->GetLen() )
1337  {
1338  if ( !aFormula.isEmpty() && aFormula[0] == '=' )
1339  pCode->AddBad( aFormula.copy( 1 ) );
1340  else
1341  pCode->AddBad( aFormula );
1342  }
1343  bSubTotal = aComp.CompileTokenArray();
1344  if( pCode->GetCodeError() == FormulaError::NONE )
1345  {
1346  nFormatType = aComp.GetNumFormatType();
1347  bChanged = true;
1348  bCompile = false;
1349  }
1350 
1351  if (bSubTotal)
1352  rDocument.AddSubTotalCell(this);
1353  }
1354  else
1355  bChanged = true;
1356  }
1357 
1358  // After loading, it must be known if ocDde/ocWebservice is in any formula
1359  // (for external links warning, CompileXML is called at the end of loading XML file)
1361 
1362  //volatile cells must be added here for import
1364  {
1365  // During load, only those cells that are marked explicitly dirty get
1366  // recalculated. So we need to set it dirty here.
1367  SetDirtyVar();
1369  // Do not call TrackFormulas() here, not all listeners may have been
1370  // established, postponed until ScDocument::CompileXML() finishes.
1371  }
1372  else if (bWasInFormulaTree)
1374 }
1375 
1376 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1377 {
1378  bool bNewCompiled = false;
1379  // If a Calc 1.0-doc is read, we have a result, but no token array
1380  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1381  {
1382  rCxt.setGrammar(eTempGrammar);
1383  Compile(rCxt, aResult.GetHybridFormula(), true);
1384  aResult.SetToken( nullptr);
1385  bDirty = true;
1386  bNewCompiled = true;
1387  }
1388  // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1389  if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1390  {
1391  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1392  bSubTotal = aComp.CompileTokenArray();
1393  nFormatType = aComp.GetNumFormatType();
1394  bDirty = true;
1395  bCompile = false;
1396  bNewCompiled = true;
1397 
1398  if (bSubTotal)
1399  rDocument.AddSubTotalCell(this);
1400  }
1401 
1402  // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1403  // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1404  // We iron this out here for all systems, such that we also have an Err503 here.
1405  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
1406  {
1407  OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1408  aResult.SetResultError( FormulaError::IllegalFPOperation );
1409  bDirty = true;
1410  }
1411 
1412  // DoubleRefs for binary operators were always a Matrix before version v5.0.
1413  // Now this is only the case when in an array formula, otherwise it's an implicit intersection
1416  {
1418  SetMatColsRows( 1, 1);
1419  }
1420 
1421  // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1422  // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1423  if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
1424  {
1425  if (bStartListening)
1427 
1428  if( !pCode->IsRecalcModeNormal() )
1429  bDirty = true;
1430  }
1431  if ( pCode->IsRecalcModeAlways() )
1432  { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1433  // for each F9
1434  bDirty = true;
1435  }
1436  // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1437 }
1438 
1440 {
1441  return pCode && rDocument.MarkUsedExternalReferences(*pCode, aPos);
1442 }
1443 
1444 namespace {
1445 class RecursionCounter
1446 {
1447  ScRecursionHelper& rRec;
1448  bool bStackedInIteration;
1449 #if defined DBG_UTIL && !defined NDEBUG
1450  const ScFormulaCell* cell;
1451 #endif
1452 public:
1453  RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
1454  : rRec(r)
1455 #if defined DBG_UTIL && !defined NDEBUG
1456  , cell(p)
1457 #endif
1458  {
1459  bStackedInIteration = rRec.IsDoingIteration();
1460  if (bStackedInIteration)
1461  rRec.GetRecursionInIterationStack().push( p);
1462  rRec.IncRecursionCount();
1463  }
1464  ~RecursionCounter()
1465  {
1466  rRec.DecRecursionCount();
1467  if (bStackedInIteration)
1468  {
1469 #if defined DBG_UTIL && !defined NDEBUG
1470  assert(rRec.GetRecursionInIterationStack().top() == cell);
1471 #endif
1472  rRec.GetRecursionInIterationStack().pop();
1473  }
1474  }
1475 };
1476 
1477 // Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
1478 // Remove the group again at the end, since there are some places throughout the code
1479 // that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
1480 // reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
1481 // a group immediately would remove the info), for this reason affected cells are stored in the recursion
1482 // helper.
1483 struct TemporaryCellGroupMaker
1484 {
1485  TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
1486  : mCell( cell )
1487  , mEnabled( enable )
1488  {
1489  if( mEnabled && mCell->GetCellGroup() == nullptr )
1490  {
1491  mCell->CreateCellGroup( 1, false );
1492  mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell );
1493  }
1494  }
1495  ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
1496  {
1497  if( mEnabled )
1498  mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells();
1499  }
1500  ScFormulaCell* mCell;
1501  const bool mEnabled;
1502 };
1503 
1504 } // namespace
1505 
1506 bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
1507 {
1508  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
1509  bool bGroupInterpreted = false;
1510 
1511  // The result would possibly depend on a cell without a valid value, bail out
1512  // the entire dependency computation.
1513  if (rRecursionHelper.IsAbortingDependencyComputation())
1514  return false;
1515 
1516  if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
1517  return bGroupInterpreted;
1518 
1520  TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );
1521 
1522  ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
1523 
1524  if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
1525  rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
1526  {
1527  // This call arose from a dependency calculation and we just found a cycle.
1528  // This will mark all elements in the cycle as parts-of-cycle.
1529  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
1530  // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
1531  // If there is a genuine circular reference, it will be marked so when all groups
1532  // in the cycle get out of dependency evaluation mode.
1533  // But returning without calculation a new value means other cells depending
1534  // on this one would use a possibly invalid value, so ensure the dependency
1535  // computation is aborted without resetting the dirty flag of any cell.
1536  rRecursionHelper.AbortDependencyComputation();
1537  return bGroupInterpreted;
1538  }
1539 
1540 #if DEBUG_CALCULATION
1541  static bool bDebugCalculationInit = true;
1542  if (bDebugCalculationInit)
1543  {
1544  aDC.maTrigger = aDebugCalculationTriggerAddress;
1545  aDC.mbPrintResults = true;
1546  bDebugCalculationInit = false;
1547  }
1548  DebugCalculationStacker aDebugEntry(aPos, rDocument);
1549 #endif
1550 
1551  if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
1552  return bGroupInterpreted; // no double/triple processing
1553 
1554  //FIXME:
1555  // If the call originates from a Reschedule in DdeLink update, leave dirty
1556  // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1557  if ( rDocument.IsInDdeLinkUpdate() )
1558  return bGroupInterpreted;
1559 
1560  if (bRunning)
1561  {
1562  if (!rDocument.GetDocOptions().IsIter())
1563  {
1564  aResult.SetResultError( FormulaError::CircularReference );
1565  return bGroupInterpreted;
1566  }
1567 
1568  if (aResult.GetResultError() == FormulaError::CircularReference)
1569  aResult.SetResultError( FormulaError::NONE );
1570 
1571  // Start or add to iteration list.
1572  if (!rRecursionHelper.IsDoingIteration() ||
1573  !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
1574  rRecursionHelper.SetInIterationReturn( true);
1575 
1576  return bGroupInterpreted;
1577  }
1578  // no multiple interprets for GetErrCode, IsValue, GetValue and
1579  // different entry point recursions. Would also lead to premature
1580  // convergence in iterations.
1581  if (rRecursionHelper.GetIteration() && nSeenInIteration ==
1582  rRecursionHelper.GetIteration())
1583  return bGroupInterpreted;
1584 
1585  bool bOldRunning = bRunning;
1586  if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1587  {
1588  bRunning = true;
1589  rRecursionHelper.SetInRecursionReturn( true);
1590  }
1591  else
1592  {
1594 
1595 #if DEBUG_CALCULATION
1596  aDC.enterGroup();
1597 #endif
1598  bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
1599  bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
1600  bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
1601 
1602 #if DEBUG_CALCULATION
1603  aDC.leaveGroup();
1604 #endif
1605  if (!bGroupInterpreted)
1606  {
1607  // This call resulted from a dependency calculation for a multigroup-threading attempt,
1608  // but found dependency among the groups.
1609  if (!rRecursionHelper.AreGroupsIndependent())
1610  {
1612  return bGroupInterpreted;
1613  }
1614  // Dependency calc inside InterpretFormulaGroup() failed due to
1615  // detection of a cycle and there are parent FG's in the cycle.
1616  // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
1617  if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
1618  {
1620  return bGroupInterpreted;
1621  }
1622 
1623  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
1625  InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1626  }
1627 
1629  }
1630 
1631  // While leaving a recursion or iteration stack, insert its cells to the
1632  // recursion list in reverse order.
1633  if (rRecursionHelper.IsInReturn())
1634  {
1635  bool bFreeFlyingInserted = false;
1636  if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion())
1637  {
1638  rRecursionHelper.Insert( this, bOldRunning, aResult);
1639  bFreeFlyingInserted = mbFreeFlying;
1640  }
1641  bool bIterationFromRecursion = false;
1642  bool bResumeIteration = false;
1643  do
1644  {
1645  if ((rRecursionHelper.IsInIterationReturn() &&
1646  rRecursionHelper.GetRecursionCount() == 0 &&
1647  !rRecursionHelper.IsDoingIteration()) ||
1648  bIterationFromRecursion || bResumeIteration)
1649  {
1650  bool & rDone = rRecursionHelper.GetConvergingReference();
1651  rDone = false;
1652  if (!bIterationFromRecursion && bResumeIteration)
1653  {
1654  bResumeIteration = false;
1655  // Resuming iteration expands the range.
1656  ScFormulaRecursionList::const_iterator aOldStart(
1657  rRecursionHelper.GetLastIterationStart());
1658  rRecursionHelper.ResumeIteration();
1659  // Mark new cells being in iteration.
1660  for (ScFormulaRecursionList::const_iterator aIter(
1661  rRecursionHelper.GetIterationStart()); aIter !=
1662  aOldStart; ++aIter)
1663  {
1664  ScFormulaCell* pIterCell = (*aIter).pCell;
1665  pIterCell->bIsIterCell = true;
1666  }
1667  // Mark older cells dirty again, in case they converted
1668  // without accounting for all remaining cells in the circle
1669  // that weren't touched so far, e.g. conditional. Restore
1670  // backupped result.
1671  sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1672  for (ScFormulaRecursionList::const_iterator aIter(
1673  aOldStart); aIter !=
1674  rRecursionHelper.GetIterationEnd(); ++aIter)
1675  {
1676  ScFormulaCell* pIterCell = (*aIter).pCell;
1677  if (pIterCell->nSeenInIteration == nIteration)
1678  {
1679  if (!pIterCell->bDirty || aIter == aOldStart)
1680  {
1681  pIterCell->aResult = (*aIter).aPreviousResult;
1682  }
1683  --pIterCell->nSeenInIteration;
1684  }
1685  pIterCell->bDirty = true;
1686  }
1687  }
1688  else
1689  {
1690  bResumeIteration = false;
1691  // Close circle once. If 'this' is self-referencing only
1692  // (e.g. counter or self-adder) then it is already
1693  // implicitly closed.
1694  /* TODO: does this even make sense anymore? The last cell
1695  * added above with rRecursionHelper.Insert() should always
1696  * be 'this', shouldn't it? */
1697  if (rRecursionHelper.GetList().size() > 1)
1698  {
1699  ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
1700  if (pLastCell != this)
1701  {
1704  pLastCell->InterpretTail(
1705  *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1707  }
1708  }
1709  // Start at 1, init things.
1710  rRecursionHelper.StartIteration();
1711  // Mark all cells being in iteration. Reset results to
1712  // original values, formula cells have been interpreted
1713  // already, discard that step.
1714  for (ScFormulaRecursionList::const_iterator aIter(
1715  rRecursionHelper.GetIterationStart()); aIter !=
1716  rRecursionHelper.GetIterationEnd(); ++aIter)
1717  {
1718  ScFormulaCell* pIterCell = (*aIter).pCell;
1719  pIterCell->aResult = (*aIter).aPreviousResult;
1720  pIterCell->bIsIterCell = true;
1721  }
1722  }
1723  bIterationFromRecursion = false;
1724  sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
1725  for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1726  rRecursionHelper.IncIteration())
1727  {
1728  rDone = false;
1729  bool bFirst = true;
1730  for ( ScFormulaRecursionList::iterator aIter(
1731  rRecursionHelper.GetIterationStart()); aIter !=
1732  rRecursionHelper.GetIterationEnd() &&
1733  !rRecursionHelper.IsInReturn(); ++aIter)
1734  {
1735  ScFormulaCell* pIterCell = (*aIter).pCell;
1736  if (pIterCell->IsDirtyOrInTableOpDirty() &&
1737  rRecursionHelper.GetIteration() !=
1738  pIterCell->GetSeenInIteration())
1739  {
1740  (*aIter).aPreviousResult = pIterCell->aResult;
1743  pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
1745  }
1746  if (bFirst)
1747  {
1748  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1749  bFirst = false;
1750  }
1751  else if (rDone)
1752  {
1753  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1754  }
1755  }
1756  if (rRecursionHelper.IsInReturn())
1757  {
1758  bResumeIteration = true;
1759  break; // for
1760  // Don't increment iteration.
1761  }
1762  }
1763  if (!bResumeIteration)
1764  {
1765  if (rDone)
1766  {
1767  for (ScFormulaRecursionList::const_iterator aIter(
1768  rRecursionHelper.GetIterationStart());
1769  aIter != rRecursionHelper.GetIterationEnd();
1770  ++aIter)
1771  {
1772  ScFormulaCell* pIterCell = (*aIter).pCell;
1773  pIterCell->bIsIterCell = false;
1774  pIterCell->nSeenInIteration = 0;
1775  pIterCell->bRunning = (*aIter).bOldRunning;
1776  }
1777  }
1778  else
1779  {
1780  for (ScFormulaRecursionList::const_iterator aIter(
1781  rRecursionHelper.GetIterationStart());
1782  aIter != rRecursionHelper.GetIterationEnd();
1783  ++aIter)
1784  {
1785  ScFormulaCell* pIterCell = (*aIter).pCell;
1786  pIterCell->bIsIterCell = false;
1787  pIterCell->nSeenInIteration = 0;
1788  pIterCell->bRunning = (*aIter).bOldRunning;
1789  pIterCell->ResetDirty();
1790  // The difference to Excel is that Excel does not
1791  // produce an error for non-convergence thus a
1792  // delta of 0.001 still works to execute the
1793  // maximum number of iterations and display the
1794  // results no matter if the result anywhere reached
1795  // near delta, but also never indicates whether the
1796  // result actually makes sense in case of
1797  // non-counter context. Calc does check the delta
1798  // in every case. If we wanted to support what
1799  // Excel does then add another option "indicate
1800  // non-convergence error" (default on) and execute
1801  // the following block only if set.
1802 #if 1
1803  // If one cell didn't converge, all cells of this
1804  // circular dependency don't, no matter whether
1805  // single cells did.
1806  pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1807  pIterCell->bChanged = true;
1808 #endif
1809  }
1810  }
1811  // End this iteration and remove entries.
1812  rRecursionHelper.EndIteration();
1813  bResumeIteration = rRecursionHelper.IsDoingIteration();
1814  }
1815  }
1816  if (rRecursionHelper.IsInRecursionReturn() &&
1817  rRecursionHelper.GetRecursionCount() == 0 &&
1818  !rRecursionHelper.IsDoingRecursion())
1819  {
1820  bIterationFromRecursion = false;
1821  // Iterate over cells known so far, start with the last cell
1822  // encountered, inserting new cells if another recursion limit
1823  // is reached. Repeat until solved.
1824  rRecursionHelper.SetDoingRecursion( true);
1825  do
1826  {
1827  rRecursionHelper.SetInRecursionReturn( false);
1828  for (ScFormulaRecursionList::const_iterator aIter(
1829  rRecursionHelper.GetIterationStart());
1830  !rRecursionHelper.IsInReturn() && aIter !=
1831  rRecursionHelper.GetIterationEnd(); ++aIter)
1832  {
1833  ScFormulaCell* pCell = (*aIter).pCell;
1834  if (pCell->IsDirtyOrInTableOpDirty())
1835  {
1838  pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1840  if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1841  pCell->bRunning = (*aIter).bOldRunning;
1842  }
1843  }
1844  } while (rRecursionHelper.IsInRecursionReturn());
1845  rRecursionHelper.SetDoingRecursion( false);
1846  if (rRecursionHelper.IsInIterationReturn())
1847  {
1848  if (!bResumeIteration)
1849  bIterationFromRecursion = true;
1850  }
1851  else if (bResumeIteration ||
1852  rRecursionHelper.IsDoingIteration())
1853  rRecursionHelper.GetList().erase(
1854  rRecursionHelper.GetIterationStart(),
1855  rRecursionHelper.GetLastIterationStart());
1856  else
1857  rRecursionHelper.Clear();
1858  }
1859  } while (bIterationFromRecursion || bResumeIteration);
1860 
1861  if (bFreeFlyingInserted)
1862  {
1863  // Remove this from recursion list, it may get deleted.
1864  // It additionally also should mean that the recursion/iteration
1865  // ends here as it must had been triggered by this free-flying
1866  // out-of-sheets cell
1867  /* TODO: replace by a simple rRecursionHelper.EndIteration() call
1868  * if the assertions hold. */
1869  const bool bOnlyThis = (rRecursionHelper.GetList().size() == 1);
1870  assert(bOnlyThis);
1871  rRecursionHelper.GetList().remove_if([this](const ScFormulaRecursionEntry& r){return r.pCell == this;});
1872  if (bOnlyThis)
1873  {
1874  assert(rRecursionHelper.GetList().empty());
1875  if (rRecursionHelper.GetList().empty())
1876  rRecursionHelper.EndIteration();
1877  }
1878  }
1879  }
1880 
1881 #if DEBUG_CALCULATION
1883  if (nErr != FormulaError::NONE)
1884  aDC.storeResultError( nErr);
1885  else if (aResult.IsValue())
1886  aDC.storeResult( aResult.GetDouble());
1887  else
1888  aDC.storeResult( aResult.GetString());
1889 #endif
1890 
1891  return bGroupInterpreted;
1892 }
1893 
1895 {
1896  RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this);
1897  // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1898  if(bIsIterCell)
1900  if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1901  {
1902  // #i11719# no RPN and no error and no token code but result string present
1903  // => interpretation of this cell during name-compilation and unknown names
1904  // => can't exchange underlying code array in CompileTokenArray() /
1905  // Compile() because interpreter's token iterator would crash or pCode
1906  // would be deleted twice if this cell was interpreted during
1907  // compilation.
1908  // This should only be a temporary condition and, since we set an
1909  // error, if ran into it again we'd bump into the dirty-clearing
1910  // condition further down.
1911  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1912  {
1913  pCode->SetCodeError( FormulaError::NoCode );
1914  // This is worth an assertion; if encountered in daily work
1915  // documents we might need another solution. Or just confirm correctness.
1916  return;
1917  }
1919  }
1920 
1921  if( pCode->GetCodeLen() )
1922  {
1923  std::unique_ptr<ScInterpreter> pScopedInterpreter;
1924  ScInterpreter* pInterpreter;
1925  if (rContext.pInterpreter)
1926  {
1927  pInterpreter = rContext.pInterpreter;
1928  pInterpreter->Init(this, aPos, *pCode);
1929  }
1930  else
1931  {
1932  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode ));
1933  pInterpreter = pScopedInterpreter.get();
1934  }
1935 
1936  FormulaError nOldErrCode = aResult.GetResultError();
1937  if ( nSeenInIteration == 0 )
1938  { // Only the first time
1939  // With bChanged=false, if a newly compiled cell has a result of
1940  // 0.0, no change is detected and the cell will not be repainted.
1941  // bChanged = false;
1942  aResult.SetResultError( FormulaError::NONE );
1943  }
1944 
1945  switch ( aResult.GetResultError() )
1946  {
1947  case FormulaError::CircularReference : // will be determined again if so
1948  aResult.SetResultError( FormulaError::NONE );
1949  break;
1950  default: break;
1951  }
1952 
1953  bool bOldRunning = bRunning;
1954  bRunning = true;
1955  pInterpreter->Interpret();
1957  {
1958  if (nSeenInIteration > 0)
1959  --nSeenInIteration; // retry when iteration is resumed
1960 
1961  if ( aResult.GetType() == formula::svUnknown )
1962  aResult.SetToken( pInterpreter->GetResultToken().get() );
1963 
1964  return;
1965  }
1966  bRunning = bOldRunning;
1967 
1968  // The result may be invalid or depend on another invalid result, just abort
1969  // without updating the cell value. Since the dirty flag will not be reset,
1970  // the proper value will be computed later.
1972  return;
1973 
1974  // #i102616# For single-sheet saving consider only content changes, not format type,
1975  // because format type isn't set on loading (might be changed later)
1976  bool bContentChanged = false;
1977 
1978  // Do not create a HyperLink() cell if the formula results in an error.
1979  if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1980  pCode->SetHyperLink(false);
1981 
1982  if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1983  {
1984  bChanged = true;
1985 
1986  if (pInterpreter->GetError() == FormulaError::RetryCircular)
1987  {
1988  // Array formula matrix calculation corner case. Keep dirty
1989  // state, do not remove from formula tree or anything else, but
1990  // store FormulaError::CircularReference in case this cell does not get
1991  // recalculated.
1992  aResult.SetResultError( FormulaError::CircularReference);
1993  return;
1994  }
1995 
1996  ResetDirty();
1997  }
1998 
1999  if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
2000  {
2001  bool bIsValue = aResult.IsValue(); // the previous type
2002  // Did it converge?
2003  if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
2004  pInterpreter->GetNumResult() - aResult.GetDouble()) <=
2006  (!bIsValue && pInterpreter->GetResultType() == svString &&
2007  pInterpreter->GetStringResult() == aResult.GetString()))
2008  {
2009  // A convergence in the first iteration doesn't necessarily
2010  // mean that it's done, it may be as not all related cells
2011  // of a circle changed their values yet. If the set really
2012  // converges it will do so also during the next iteration. This
2013  // fixes situations like of #i44115#. If this wasn't wanted an
2014  // initial "uncalculated" value would be needed for all cells
2015  // of a circular dependency => graph needed before calculation.
2016  if (nSeenInIteration > 1 ||
2018  {
2019  ResetDirty();
2020  }
2021  }
2022  }
2023 
2024  // New error code?
2025  if( pInterpreter->GetError() != nOldErrCode )
2026  {
2027  bChanged = true;
2028  // bContentChanged only has to be set if the file content would be changed
2029  if ( aResult.GetCellResultType() != svUnknown )
2030  bContentChanged = true;
2031  }
2032 
2033  ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
2034 
2035  // For IF() and other jumps or changed formatted source data the result
2036  // format may change for different runs, e.g. =IF(B1,B1) with first
2037  // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
2038  // displayed as TRUE. Do not force a general format though if
2039  // mbNeedsNumberFormat is set (because there was a general format..).
2040  // Note that nFormatType may be out of sync here if a format was
2041  // applied or cleared after the last run, but obtaining the current
2042  // format always just to check would be expensive. There may be
2043  // cases where the format should be changed but is not. If that turns
2044  // out to be a real problem then obtain the current format type after
2045  // the initial check when needed.
2046  bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
2048 
2049  // We have some requirements additionally to IsCompatible().
2050  // * Do not apply a NumberFormat::LOGICAL if the result value is not
2051  // 1.0 or 0.0
2052  // * Do not override an already set numeric number format if the result
2053  // is of type NumberFormat::LOGICAL, it could be user applied.
2054  // On the other hand, for an empty jump path instead of FALSE an
2055  // unexpected for example 0% could be displayed. YMMV.
2056  // * Never override a non-standard number format that indicates user
2057  // applied.
2058  // * NumberFormat::TEXT does not force a change.
2059  if (bForceNumberFormat)
2060  {
2061  sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
2062  const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
2063  if (nRetType == SvNumFormatType::LOGICAL)
2064  {
2065  double fVal = aNewResult.GetDouble();
2066  if (fVal != 1.0 && fVal != 0.0)
2067  bForceNumberFormat = false;
2068  else
2069  {
2070  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2071  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2072  switch (nFormatType)
2073  {
2074  case SvNumFormatType::PERCENT:
2075  case SvNumFormatType::CURRENCY:
2076  case SvNumFormatType::SCIENTIFIC:
2077  case SvNumFormatType::FRACTION:
2078  bForceNumberFormat = false;
2079  break;
2080  case SvNumFormatType::NUMBER:
2081  if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2082  bForceNumberFormat = false;
2083  break;
2084  default: break;
2085  }
2086  }
2087  }
2088  else if (nRetType == SvNumFormatType::TEXT)
2089  {
2090  bForceNumberFormat = false;
2091  }
2092  if (bForceNumberFormat)
2093  {
2094  if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2095  {
2096  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2097  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2098  }
2099  if (nOldFormatIndex !=
2100  ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2101  bForceNumberFormat = false;
2102  }
2103  }
2104 
2105  if( mbNeedsNumberFormat || bForceNumberFormat )
2106  {
2107  bool bSetFormat = true;
2108  const SvNumFormatType nOldFormatType = nFormatType;
2109  nFormatType = pInterpreter->GetRetFormatType();
2110  sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2111 
2112  if (nFormatType == SvNumFormatType::TEXT)
2113  {
2114  // Don't set text format as hard format.
2115  bSetFormat = false;
2116  }
2117  else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2118  {
2119  // In a matrix range do not set an (inherited) logical format
2120  // as hard format if the value does not represent a strict TRUE
2121  // or FALSE value. But do set for a top left error value so
2122  // following matrix cells can inherit for non-error values.
2123  // This solves a problem with IF() expressions in array context
2124  // where incidentally the top left element results in logical
2125  // type but some others don't. It still doesn't solve the
2126  // reverse case though, where top left is not logical type but
2127  // some other elements should be. We'd need to transport type
2128  // or format information on arrays.
2129  StackVar eNewCellResultType = aNewResult.GetCellResultType();
2130  if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2131  {
2132  if (eNewCellResultType != svDouble)
2133  {
2134  bSetFormat = false;
2135  nFormatType = nOldFormatType; // that? or number?
2136  }
2137  else
2138  {
2139  double fVal = aNewResult.GetDouble();
2140  if (fVal != 1.0 && fVal != 0.0)
2141  {
2142  bSetFormat = false;
2143  nFormatType = SvNumFormatType::NUMBER;
2144  }
2145  }
2146  }
2147  }
2148 
2149  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
2150  nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
2151  nFormatIndex, nFormatType);
2152 
2153  // Do not replace a General format (which was the reason why
2154  // mbNeedsNumberFormat was set) with a General format.
2155  // 1. setting a format has quite some overhead in the
2156  // ScPatternAttr/ScAttrArray handling, even if identical.
2157  // 2. the General formats may be of different locales.
2158  // XXX if mbNeedsNumberFormat was set even if the current format
2159  // was not General then we'd have to obtain the current format here
2160  // and check at least the types.
2161  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)))
2162  {
2163  // set number format explicitly
2165  rDocument.SetNumberFormat( aPos, nFormatIndex );
2166  else
2167  {
2168  // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
2169  // to the main thread. Since thread calculations operate on formula groups,
2170  // it's enough to store just the row.
2171  DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex };
2172  rContext.maDelayedSetNumberFormat.push_back( data );
2173  }
2174  bChanged = true;
2175  }
2176 
2177  // Currently (2019-05-10) nothing else can cope with a duration
2178  // format type, change to time as it was before.
2179  if (nFormatType == SvNumFormatType::DURATION)
2180  nFormatType = SvNumFormatType::TIME;
2181 
2182  mbNeedsNumberFormat = false;
2183  }
2184 
2185  // In case of changes just obtain the result, no temporary and
2186  // comparison needed anymore.
2187  if (bChanged)
2188  {
2189  // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
2190  // Also handle special cases of initial results after loading.
2191  if ( !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2192  {
2194  StackVar eNew = aNewResult.GetCellResultType();
2195  if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
2196  {
2197  // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
2198  // -> no change
2199  }
2200  else
2201  {
2202  if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
2203  eOld = svString; // ScHybridCellToken has a valid GetString method
2204 
2205  // #i106045# use approxEqual to compare with stored value
2206  bContentChanged = (eOld != eNew ||
2207  (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
2208  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2209  }
2210  }
2211 
2212  aResult.SetToken( pInterpreter->GetResultToken().get() );
2213  }
2214  else
2215  {
2217  StackVar eNew = aNewResult.GetCellResultType();
2218  bChanged = (eOld != eNew ||
2219  (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
2220  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2221 
2222  // #i102616# handle special cases of initial results after loading
2223  // (only if the sheet is still marked unchanged)
2224  if ( bChanged && !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2225  {
2226  if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
2227  ((eOld == svHybridCell) &&
2228  eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
2229  (eOld == svDouble && eNew == svDouble &&
2230  rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
2231  {
2232  // no change, see above
2233  }
2234  else
2235  bContentChanged = true;
2236  }
2237 
2238  aResult.Assign( aNewResult);
2239  }
2240 
2241  // Precision as shown?
2242  if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
2244  && nFormatType != SvNumFormatType::DATE
2245  && nFormatType != SvNumFormatType::TIME
2246  && nFormatType != SvNumFormatType::DATETIME )
2247  {
2248  sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos );
2250  aResult.GetDouble(), nFormat, &rContext));
2251  }
2252  if (eTailParam == SCITP_NORMAL)
2253  {
2254  ResetDirty();
2255  }
2256  if( aResult.GetMatrix() )
2257  {
2258  // If the formula wasn't entered as a matrix formula, live on with
2259  // the upper left corner and let reference counting delete the matrix.
2262  }
2263  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
2264  {
2265  // Coded double error may occur via filter import.
2267  aResult.SetResultError( nErr);
2268  bChanged = bContentChanged = true;
2269  }
2270 
2271  if (bContentChanged && rDocument.IsStreamValid(aPos.Tab()))
2272  {
2273  // pass bIgnoreLock=true, because even if called from pending row height update,
2274  // a changed result must still reset the stream flag
2275  rDocument.SetStreamValid(aPos.Tab(), false, true);
2276  }
2279 
2280  // FORCED cells also immediately tested for validity (start macro possibly)
2281 
2282  if ( pCode->IsRecalcModeForced() )
2283  {
2284  sal_uLong nValidation = rDocument.GetAttr(
2285  aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2286  if ( nValidation )
2287  {
2288  const ScValidationData* pData = rDocument.GetValidationEntry( nValidation );
2289  ScRefCellValue aTmpCell(this);
2290  if ( pData && !pData->IsDataValid(aTmpCell, aPos))
2291  pData->DoCalcError( this );
2292  }
2293  }
2294 
2295  // Reschedule slows the whole thing down considerably, thus only execute on percent change
2297  {
2299  if (pProgress && pProgress->Enabled())
2300  {
2301  pProgress->SetStateCountDownOnPercent(
2303  }
2304 
2305  switch (pInterpreter->GetVolatileType())
2306  {
2308  // Volatile via built-in volatile functions. No actions needed.
2309  break;
2311  // The formula contains a volatile macro.
2315  break;
2317  if (pCode->IsRecalcModeAlways())
2318  {
2319  // The formula was previously volatile, but no more.
2322  }
2323  else
2324  {
2325  // non-volatile formula. End listening to the area in case
2326  // it's listening due to macro module change.
2328  }
2330  break;
2331  default:
2332  ;
2333  }
2334  }
2335  }
2336  else
2337  {
2338  // Cells with compiler errors should not be marked dirty forever
2339  OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
2340  ResetDirty();
2341  }
2342 }
2343 
2345 {
2346  if( !pCode->GetCodeLen() )
2347  return;
2348 
2349  if ( !pCode->IsRecalcModeAlways() )
2351 
2352  std::unique_ptr<ScInterpreter> pScopedInterpreter;
2353  if (pInterpreter)
2354  pInterpreter->Init(this, aPos, *pCode);
2355  else
2356  {
2357  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
2358  pInterpreter = pScopedInterpreter.get();
2359  }
2360 
2361  switch (pInterpreter->GetVolatileType())
2362  {
2364  // The formula contains a volatile macro.
2368  break;
2370  if (pCode->IsRecalcModeAlways())
2371  {
2372  // The formula was previously volatile, but no more.
2375  }
2376  else
2377  {
2378  // non-volatile formula. End listening to the area in case
2379  // it's listening due to macro module change.
2381  }
2383  break;
2384  default:
2385  ;
2386  }
2387 }
2388 
2389 void ScFormulaCell::SetCompile( bool bVal )
2390 {
2391  bCompile = bVal;
2392 }
2393 
2395 {
2397  if (pMat)
2398  pMat->SetMatColsRows( nCols, nRows );
2399  else if (nCols || nRows)
2400  {
2401  aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2402  // Setting the new token actually forces an empty result at this top
2403  // left cell, so have that recalculated.
2404  SetDirty();
2405  }
2406 }
2407 
2408 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2409 {
2411  if (pMat)
2412  pMat->GetMatColsRows( nCols, nRows);
2413  else
2414  {
2415  nCols = 0;
2416  nRows = 0;
2417  }
2418 }
2419 
2421 {
2422  bInChangeTrack = bVal;
2423 }
2424 
2425 void ScFormulaCell::Notify( const SfxHint& rHint )
2426 {
2427  if (rDocument.IsInDtorClear())
2428  return;
2429 
2430  const SfxHintId nHint = rHint.GetId();
2431  if (nHint == SfxHintId::ScReference)
2432  {
2433  const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2434 
2435  switch (rRefHint.getType())
2436  {
2438  {
2439  const sc::RefColReorderHint& rRefColReorder =
2440  static_cast<const sc::RefColReorderHint&>(rRefHint);
2441  if (!IsShared() || IsSharedTop())
2443  aPos, rRefColReorder.getTab(),
2444  rRefColReorder.getStartRow(),
2445  rRefColReorder.getEndRow(),
2446  rRefColReorder.getColMap());
2447  }
2448  break;
2450  {
2451  const sc::RefRowReorderHint& rRefRowReorder =
2452  static_cast<const sc::RefRowReorderHint&>(rRefHint);
2453  if (!IsShared() || IsSharedTop())
2455  aPos, rRefRowReorder.getTab(),
2456  rRefRowReorder.getStartColumn(),
2457  rRefRowReorder.getEndColumn(),
2458  rRefRowReorder.getRowMap());
2459  }
2460  break;
2462  {
2464  }
2465  break;
2467  {
2469  }
2470  break;
2471  default:
2472  ;
2473  }
2474 
2475  return;
2476  }
2477 
2479  return;
2480 
2481  if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
2482  return;
2483 
2484  bool bForceTrack = false;
2485  if ( nHint == SfxHintId::ScTableOpDirty )
2486  {
2487  bForceTrack = !bTableOpDirty;
2488  if ( !bTableOpDirty )
2489  {
2491  bTableOpDirty = true;
2492  }
2493  }
2494  else
2495  {
2496  bForceTrack = !bDirty;
2497  SetDirtyVar();
2498  }
2499  // Don't remove from FormulaTree to put in FormulaTrack to
2500  // put in FormulaTree again and again, only if necessary.
2501  // Any other means except ScRecalcMode::ALWAYS by which a cell could
2502  // be in FormulaTree if it would notify other cells through
2503  // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2504  // Yes. The new TableOpDirty made it necessary to have a
2505  // forced mode where formulas may still be in FormulaTree from
2506  // TableOpDirty but have to notify dependents for normal dirty.
2507  if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
2508  || pCode->IsRecalcModeAlways())
2509  && !rDocument.IsInFormulaTrack( this ) )
2511 }
2512 
2514 {
2515  switch (rQuery.getId())
2516  {
2518  {
2519  sc::RefQueryFormulaGroup& rRefQuery =
2520  static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2521  if (IsShared())
2522  rRefQuery.add(aPos);
2523  }
2524  break;
2525  default:
2526  ;
2527  }
2528 }
2529 
2530 void ScFormulaCell::SetDirty( bool bDirtyFlag )
2531 {
2532  if (IsInChangeTrack())
2533  return;
2534 
2536  {
2537  SetDirtyVar();
2538  rDocument.SetStreamValid(aPos.Tab(), false);
2539  return;
2540  }
2541 
2542  // Avoid multiple formula tracking in Load() and in CompileAll()
2543  // after CopyScenario() and CopyBlockFromClip().
2544  // If unconditional formula tracking is needed, set bDirty=false
2545  // before calling SetDirty(), for example in CompileTokenArray().
2546  if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
2547  {
2548  if( bDirtyFlag )
2549  SetDirtyVar();
2551 
2552  // While loading a document listeners have not been established yet.
2553  // Tracking would remove this cell from the FormulaTrack and add it to
2554  // the FormulaTree, once in there it would be assumed that its
2555  // dependents already had been tracked and it would be skipped on a
2556  // subsequent notify. Postpone tracking until all listeners are set.
2557  if (!rDocument.IsImportingXML())
2559  }
2560 
2561  rDocument.SetStreamValid(aPos.Tab(), false);
2562 }
2563 
2565 {
2566  bDirty = true;
2567  mbPostponedDirty = false;
2568  if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2569  {
2570  mxGroup->meCalcState = sc::GroupCalcEnabled;
2571  mxGroup->mbPartOfCycle = false;
2572  }
2573 
2574  // mark the sheet of this cell to be calculated
2575  //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
2576 }
2577 
2579 {
2580  bDirty = true;
2582  rDocument.PutInFormulaTree( this );
2583 }
2584 
2586 {
2587  bTableOpDirty = false;
2588 }
2589 
2591 {
2592  if ( IsInChangeTrack() )
2593  return;
2594 
2596  bTableOpDirty = true;
2597  else
2598  {
2599  if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
2600  {
2601  if ( !bTableOpDirty )
2602  {
2604  bTableOpDirty = true;
2605  }
2607  rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
2608  }
2609  }
2610 }
2611 
2613 {
2614  aResult.SetDouble(n);
2615 }
2616 
2618 {
2619  aResult.SetToken(pToken);
2620 }
2621 
2623 {
2624  return aResult.GetString();
2625 }
2626 
2628 {
2630 }
2631 
2633 {
2634  aResult.SetMatrix(nCols, nRows, pMat, pUL);
2635 }
2636 
2638 {
2639  /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2640  * used whether it is solely for transport of a simple result error and get
2641  * rid of that abuse. */
2642  pCode->SetCodeError( n );
2643  // Hard set errors are transported as result type value per convention,
2644  // e.g. via clipboard. ScFormulaResult::IsValue() and
2645  // ScFormulaResult::GetDouble() handle that.
2646  aResult.SetResultError( n );
2647 }
2648 
2650 {
2651  aResult.SetResultError( n );
2652 }
2653 
2655 {
2656  if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2657  SetDirtyVar();
2658  if ( nBits & ScRecalcMode::ONLOAD_ONCE )
2659  { // OnLoadOnce is used only to set Dirty after filter import.
2660  nBits = (nBits & ~ScRecalcMode::EMask) | ScRecalcMode::NORMAL;
2661  }
2662  pCode->AddRecalcMode( nBits );
2663 }
2664 
2666 {
2668 }
2669 
2671 {
2673 }
2674 
2676 {
2678 }
2679 
2680 void ScFormulaCell::SetHybridFormula( const OUString& r,
2681  const formula::FormulaGrammar::Grammar eGrammar )
2682 {
2683  aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2684 }
2685 
2686 const OUString& ScFormulaCell::GetHybridFormula() const
2687 {
2688  return aResult.GetHybridFormula();
2689 }
2690 
2691 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2692 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2693 {
2694  OUString aCellString;
2695 
2696  const Color* pColor;
2697 
2698  // Cell Text uses the Cell format while the URL uses
2699  // the default format for the type.
2700  const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
2701  SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
2702 
2703  const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2704 
2705  if ( IsValue() )
2706  {
2707  double fValue = GetValue();
2708  pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2709  }
2710  else
2711  {
2712  aCellString = GetString().getString();
2713  pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2714  }
2716  if (xMat)
2717  {
2718  // determine if the matrix result is a string or value.
2719  if (!xMat->IsValue(0, 1))
2720  rURL = xMat->GetString(0, 1).getString();
2721  else
2722  pFormatter->GetOutputString(
2723  xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2724  }
2725 
2726  if(rURL.isEmpty())
2727  {
2728  if(IsValue())
2729  pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2730  else
2731  pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2732  }
2733 }
2734 
2736 {
2737  if (!IsValue())
2738  return aResult.IsMultiline();
2739  return false;
2740 }
2741 
2743 {
2744  return pCode && pCode->IsHyperLink();
2745 }
2746 
2747 std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2748 {
2749  OUString aCellText;
2750  OUString aURL;
2751  GetURLResult( aURL, aCellText );
2752 
2753  return ScEditUtil::CreateURLObjectFromURL( rDocument, aURL, aCellText );
2754 }
2755 
2757 {
2758  MaybeInterpret();
2760 }
2761 
2763 {
2764  MaybeInterpret();
2766 }
2767 
2769 {
2770  MaybeInterpret();
2771  return aResult.IsValue();
2772 }
2773 
2775 {
2776  MaybeInterpret();
2777  if (pCode->GetCodeError() != FormulaError::NONE)
2778  return false;
2779 
2780  return aResult.IsValueNoError();
2781 }
2782 
2784 {
2785  if (NeedsInterpret())
2786  // false if the cell is dirty & needs to be interpreted.
2787  return false;
2788 
2789  if (pCode->GetCodeError() != FormulaError::NONE)
2790  return false;
2791 
2792  return aResult.IsValueNoError();
2793 }
2794 
2796 {
2797  MaybeInterpret();
2798  return GetRawValue();
2799 }
2800 
2802 {
2803  MaybeInterpret();
2804  return GetRawString();
2805 }
2806 
2808 {
2809  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2810  aResult.GetResultError() == FormulaError::NONE)
2811  return aResult.GetDouble();
2812  return 0.0;
2813 }
2814 
2816 {
2817  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2818  aResult.GetResultError() == FormulaError::NONE)
2819  return aResult.GetString();
2820 
2822 }
2823 
2825 {
2826  if ( rDocument.GetAutoCalc() )
2827  {
2829  // Was stored !bDirty but an accompanying matrix cell was bDirty?
2831  Interpret();
2832  }
2833  return aResult.GetMatrix().get();
2834 }
2835 
2836 bool ScFormulaCell::GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const
2837 {
2838  switch ( cMatrixFlag )
2839  {
2840  case ScMatrixMode::Formula :
2841  rPos = aPos;
2842  return true;
2844  {
2847  if( t )
2848  {
2849  ScSingleRefData& rRef = *t->GetSingleRef();
2850  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
2851  if (rDoc.ValidAddress(aAbs))
2852  {
2853  rPos = aAbs;
2854  return true;
2855  }
2856  }
2857  }
2858  break;
2859  default: break;
2860  }
2861  return false;
2862 }
2863 
2864 sc::MatrixEdge ScFormulaCell::GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const
2865 {
2866  switch ( cMatrixFlag )
2867  {
2868  case ScMatrixMode::Formula :
2870  {
2871  static thread_local SCCOL nC;
2872  static thread_local SCROW nR;
2873  ScAddress aOrg;
2874  if ( !GetMatrixOrigin( rDoc, aOrg ) )
2875  return sc::MatrixEdge::Nothing;
2876  if ( aOrg != rOrgPos )
2877  { // First time or a different matrix than last time.
2878  rOrgPos = aOrg;
2879  const ScFormulaCell* pFCell;
2881  pFCell = rDocument.GetFormulaCell(aOrg);
2882  else
2883  pFCell = this; // this ScMatrixMode::Formula
2884  // There's only one this, don't compare pFCell==this.
2885  if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2886  {
2887  pFCell->GetMatColsRows( nC, nR );
2888  if ( nC == 0 || nR == 0 )
2889  {
2890  // No ScMatrixFormulaCellToken available yet, calculate new.
2891  nC = 1;
2892  nR = 1;
2893  ScAddress aTmpOrg;
2894  ScFormulaCell* pCell;
2895  ScAddress aAdr( aOrg );
2896  aAdr.IncCol();
2897  bool bCont = true;
2898  do
2899  {
2900  pCell = rDocument.GetFormulaCell(aAdr);
2901  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2902  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2903  {
2904  nC++;
2905  aAdr.IncCol();
2906  }
2907  else
2908  bCont = false;
2909  } while ( bCont );
2910  aAdr = aOrg;
2911  aAdr.IncRow();
2912  bCont = true;
2913  do
2914  {
2915  pCell = rDocument.GetFormulaCell(aAdr);
2916  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2917  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2918  {
2919  nR++;
2920  aAdr.IncRow();
2921  }
2922  else
2923  bCont = false;
2924  } while ( bCont );
2925 
2926  const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2927  }
2928  }
2929  else
2930  {
2931 #if OSL_DEBUG_LEVEL > 0
2932  SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2934  << ", MatOrg: "
2936 #endif
2937  return sc::MatrixEdge::Nothing;
2938  }
2939  }
2940  // here we are, healthy and clean, somewhere in between
2941  SCCOL dC = aPos.Col() - aOrg.Col();
2942  SCROW dR = aPos.Row() - aOrg.Row();
2944  if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2945  {
2946  if ( dC == 0 )
2947  nEdges |= sc::MatrixEdge::Left;
2948  if ( dC+1 == nC )
2949  nEdges |= sc::MatrixEdge::Right;
2950  if ( dR == 0 )
2951  nEdges |= sc::MatrixEdge::Top;
2952  if ( dR+1 == nR )
2953  nEdges |= sc::MatrixEdge::Bottom;
2954  if ( nEdges == sc::MatrixEdge::Nothing )
2955  nEdges = sc::MatrixEdge::Inside;
2956  }
2957  else
2958  {
2959  SAL_WARN( "sc", "broken Matrix, Pos: "
2961  << ", MatOrg: "
2963  << ", MatCols: " << static_cast<sal_Int32>( nC )
2964  << ", MatRows: " << static_cast<sal_Int32>( nR )
2965  << ", DiffCols: " << static_cast<sal_Int32>( dC )
2966  << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2967  }
2968  return nEdges;
2969  }
2970  default:
2971  return sc::MatrixEdge::Nothing;
2972  }
2973 }
2974 
2976 {
2977  MaybeInterpret();
2978 
2979  /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2980  * and not also abused for signaling other error conditions we could bail
2981  * out even before attempting to interpret broken code. */
2982  FormulaError nErr = pCode->GetCodeError();
2983  if (nErr != FormulaError::NONE)
2984  return nErr;
2985  return aResult.GetResultError();
2986 }
2987 
2989 {
2990  FormulaError nErr = pCode->GetCodeError();
2991  if (nErr != FormulaError::NONE)
2992  return nErr;
2993  return aResult.GetResultError();
2994 }
2995 
2997 {
2998  MaybeInterpret();
2999 
3000  rErr = pCode->GetCodeError();
3001  if (rErr != FormulaError::NONE)
3002  return true;
3003 
3004  return aResult.GetErrorOrDouble(rErr, rVal);
3005 }
3006 
3008 {
3009  MaybeInterpret();
3010 
3011  FormulaError nErr = pCode->GetCodeError();
3012  if (nErr != FormulaError::NONE)
3013  return sc::FormulaResultValue(nErr);
3014 
3015  return aResult.GetResult();
3016 }
3017 
3019 {
3020  FormulaError nErr = pCode->GetCodeError();
3021  if (nErr != FormulaError::NONE)
3022  return sc::FormulaResultValue(nErr);
3023 
3024  return aResult.GetResult();
3025 }
3026 
3028 {
3031  if( p && !aIter.GetNextReferenceRPN() ) // only one!
3032  {
3033  SingleDoubleRefProvider aProv( *p );
3034  r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
3035  r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
3036  return true;
3037  }
3038  else
3039  return false;
3040 }
3041 
3042 bool
3044 {
3045  /* If there appears just one reference in the formula, it's the same
3046  as HasOneReference(). If there are more of them, they can denote
3047  one range if they are (sole) arguments of one function.
3048  Union of these references must form one range and their
3049  intersection must be empty set.
3050  */
3051 
3052  // Detect the simple case of exactly one reference in advance without all
3053  // overhead.
3054  // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
3055  // work again, where the function does not have only references.
3056  if (HasOneReference( rRange))
3057  return true;
3058 
3059  // Get first reference, if any
3061  formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
3062  if (pFirstReference)
3063  {
3064  // Collect all consecutive references, starting by the one
3065  // already found
3066  std::vector<formula::FormulaToken*> aReferences;
3067  aReferences.push_back(pFirstReference);
3068  FormulaToken* pToken(aIter.NextRPN());
3069  FormulaToken* pFunction(nullptr);
3070  while (pToken)
3071  {
3072  if (lcl_isReference(*pToken))
3073  {
3074  aReferences.push_back(pToken);
3075  pToken = aIter.NextRPN();
3076  }
3077  else
3078  {
3079  if (pToken->IsFunction())
3080  {
3081  pFunction = pToken;
3082  }
3083  break;
3084  }
3085  }
3086  if (pFunction && !aIter.GetNextReferenceRPN()
3087  && (pFunction->GetParamCount() == aReferences.size()))
3088  {
3089  return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
3090  }
3091  }
3092  return false;
3093 }
3094 
3096 {
3097  RelNameRef eRelNameRef = RelNameRef::NONE;
3100  while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3101  {
3102  switch (t->GetType())
3103  {
3104  case formula::svSingleRef:
3105  if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3106  eRelNameRef = RelNameRef::SINGLE;
3107  break;
3108  case formula::svDoubleRef:
3109  if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3110  // May originate from individual cell names, in which case
3111  // it needs recompilation.
3112  return RelNameRef::DOUBLE;
3113  /* TODO: have an extra flag at ScComplexRefData if range was
3114  * extended? or too cumbersome? might narrow recompilation to
3115  * only needed cases.
3116  * */
3117  break;
3118  default:
3119  ; // nothing
3120  }
3121  }
3122  return eRelNameRef;
3123 }
3124 
3126 {
3127  if (rCxt.meMode != URM_INSDEL)
3128  // Just in case...
3129  return false;
3130 
3131  if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3132  // No movement.
3133  return false;
3134 
3135  if (!rCxt.maRange.In(aPos))
3136  return false;
3137 
3138  // This formula cell itself is being shifted during cell range
3139  // insertion or deletion. Update its position.
3140  ScAddress aErrorPos( ScAddress::UNINITIALIZED );
3141  if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos))
3142  {
3143  assert(!"can't move ScFormulaCell");
3144  }
3145 
3146  return true;
3147 }
3148 
3149 namespace {
3150 
3154 bool checkCompileColRowName(
3155  const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3156  const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3157 {
3158  switch (rCxt.meMode)
3159  {
3160  case URM_INSDEL:
3161  {
3162  if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3163  return false;
3164 
3167  ScRangePairList* pColList = rDoc.GetColNameRanges();
3168  ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3169  while ((t = aIter.GetNextColRowName()) != nullptr)
3170  {
3171  ScSingleRefData& rRef = *t->GetSingleRef();
3172  if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3173  { // ColName
3174  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3175  ScRangePair* pR = pColList->Find( aAdr );
3176  if ( pR )
3177  { // defined
3178  if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3179  return true;
3180  }
3181  else
3182  { // on the fly
3183  if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3184  return true;
3185  }
3186  }
3187  if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3188  { // RowName
3189  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3190  ScRangePair* pR = pRowList->Find( aAdr );
3191  if ( pR )
3192  { // defined
3193  if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3194  return true;
3195  }
3196  else
3197  { // on the fly
3198  if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3199  return true;
3200  }
3201  }
3202  }
3203  }
3204  break;
3205  case URM_MOVE:
3206  { // Recompile for Move/D&D when ColRowName was moved or this Cell
3207  // points to one and was moved.
3208  bool bMoved = (aPos != aOldPos);
3209  if (bMoved)
3210  return true;
3211 
3213  const formula::FormulaToken* t = aIter.GetNextColRowName();
3214  for (; t; t = aIter.GetNextColRowName())
3215  {
3216  const ScSingleRefData& rRef = *t->GetSingleRef();
3217  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
3218  if (rDoc.ValidAddress(aAbs))
3219  {
3220  if (rCxt.maRange.In(aAbs))
3221  return true;
3222  }
3223  }
3224  }
3225  break;
3226  case URM_COPY:
3227  return bValChanged;
3228  default:
3229  ;
3230  }
3231 
3232  return false;
3233 }
3234 
3235 void setOldCodeToUndo(
3236  ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3237 {
3238  // Copy the cell to aUndoPos, which is its current position in the document,
3239  // so this works when UpdateReference is called before moving the cells
3240  // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3241  // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3242 
3243  // If there is already a formula cell in the undo document, don't overwrite it,
3244  // the first (oldest) is the important cell.
3245  if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3246  return;
3247 
3248  ScFormulaCell* pFCell =
3249  new ScFormulaCell(
3250  rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
3251 
3252  pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3253  rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
3254 }
3255 
3256 }
3257 
3259  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3260 {
3261  if (rCxt.meMode != URM_INSDEL)
3262  // Just in case...
3263  return false;
3264 
3265  bool bCellStateChanged = false;
3266  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3267  if ( pUndoCellPos )
3268  aUndoPos = *pUndoCellPos;
3269  ScAddress aOldPos( aPos );
3270  bCellStateChanged = UpdatePosOnShift(rCxt);
3271 
3272  // Check presence of any references or column row names.
3273  bool bHasRefs = pCode->HasReferences();
3274  bool bHasColRowNames = false;
3275  if (!bHasRefs)
3276  {
3277  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3278  bHasRefs = bHasColRowNames;
3279  }
3280  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3281 
3282  if (!bHasRefs && !bOnRefMove)
3283  // This formula cell contains no references, nor needs recalculating
3284  // on reference update. Bail out.
3285  return bCellStateChanged;
3286 
3287  std::unique_ptr<ScTokenArray> pOldCode;
3288  if (pUndoDoc)
3289  pOldCode = pCode->Clone();
3290 
3291  bool bValChanged = false;
3292  bool bRefModified = false;
3293  bool bRecompile = bCompile;
3294 
3295  if (bHasRefs)
3296  {
3297  // Update cell or range references.
3298  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3299  bRefModified = aRes.mbReferenceModified;
3300  bValChanged = aRes.mbValueChanged;
3301  if (aRes.mbNameModified)
3302  bRecompile = true;
3303  }
3304 
3305  if (bValChanged || bRefModified)
3306  bCellStateChanged = true;
3307 
3308  if (bOnRefMove)
3309  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3310  bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3311 
3312  bool bNewListening = false;
3313  bool bInDeleteUndo = false;
3314 
3315  if (bHasRefs)
3316  {
3317  // Upon Insert ColRowNames have to be recompiled in case the
3318  // insertion occurs right in front of the range.
3319  if (bHasColRowNames && !bRecompile)
3320  bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3321 
3322  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3323  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3324 
3325  // RelNameRefs are always moved
3326  bool bHasRelName = false;
3327  if (!bRecompile)
3328  {
3329  RelNameRef eRelNameRef = HasRelNameReference();
3330  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3331  bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3332  }
3333  // Reference changed and new listening needed?
3334  // Except in Insert/Delete without specialities.
3335  bNewListening = (bRefModified || bRecompile
3336  || (bValChanged && bInDeleteUndo) || bHasRelName);
3337 
3338  if ( bNewListening )
3339  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3340  }
3341 
3342  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3343  bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3344 
3345  if (pUndoDoc && (bValChanged || bOnRefMove))
3346  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3347 
3348  bCompile |= bRecompile;
3349  if (bCompile)
3350  {
3351  CompileTokenArray( bNewListening ); // no Listening
3352  bNeedDirty = true;
3353  }
3354 
3355  if ( !bInDeleteUndo )
3356  { // In ChangeTrack Delete-Reject listeners are established in
3357  // InsertCol/InsertRow
3358  if ( bNewListening )
3359  {
3360  // Inserts/Deletes re-establish listeners after all
3361  // UpdateReference calls.
3362  // All replaced shared formula listeners have to be
3363  // established after an Insert or Delete. Do nothing here.
3364  SetNeedsListening( true);
3365  }
3366  }
3367 
3368  if (bNeedDirty)
3369  { // Cut off references, invalid or similar?
3370  // Postpone SetDirty() until all listeners have been re-established in
3371  // Inserts/Deletes.
3372  mbPostponedDirty = true;
3373  }
3374 
3375  return bCellStateChanged;
3376 }
3377 
3379  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3380 {
3381  if (rCxt.meMode != URM_MOVE)
3382  return false;
3383 
3384  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3385  if ( pUndoCellPos )
3386  aUndoPos = *pUndoCellPos;
3387  ScAddress aOldPos( aPos );
3388 
3389  bool bCellInMoveTarget = rCxt.maRange.In(aPos);
3390 
3391  if ( bCellInMoveTarget )
3392  {
3393  // The cell is being moved or copied to a new position. I guess the
3394  // position has been updated prior to this call? Determine
3395  // its original position before the move which will be used to adjust
3396  // relative references later.
3397  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3398  }
3399 
3400  // Check presence of any references or column row names.
3401  bool bHasRefs = pCode->HasReferences();
3402  bool bHasColRowNames = false;
3403  if (!bHasRefs)
3404  {
3405  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3406  bHasRefs = bHasColRowNames;
3407  }
3408  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3409 
3410  if (!bHasRefs && !bOnRefMove)
3411  // This formula cell contains no references, nor needs recalculating
3412  // on reference update. Bail out.
3413  return false;
3414 
3415  bool bCellStateChanged = false;
3416  std::unique_ptr<ScTokenArray> pOldCode;
3417  if (pUndoDoc)
3418  pOldCode = pCode->Clone();
3419 
3420  bool bValChanged = false;
3421  bool bRefModified = false;
3422 
3423  if (bHasRefs)
3424  {
3425  // Update cell or range references.
3426  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3427  bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3428  bValChanged = aRes.mbValueChanged;
3429  if (aRes.mbNameModified)
3430  // Re-compile to get the RPN token regenerated to reflect updated names.
3431  bCompile = true;
3432  }
3433 
3434  if (bValChanged || bRefModified)
3435  bCellStateChanged = true;
3436 
3437  if (bOnRefMove)
3438  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3439  bOnRefMove = (bValChanged || (aPos != aOldPos));
3440 
3441  bool bColRowNameCompile = false;
3442  bool bHasRelName = false;
3443  bool bNewListening = false;
3444  bool bInDeleteUndo = false;
3445 
3446  if (bHasRefs)
3447  {
3448  // Upon Insert ColRowNames have to be recompiled in case the
3449  // insertion occurs right in front of the range.
3450  if (bHasColRowNames)
3451  bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3452 
3453  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3454  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3455 
3456  // RelNameRefs are always moved
3457  RelNameRef eRelNameRef = HasRelNameReference();
3458  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3459  bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3460  // Reference changed and new listening needed?
3461  // Except in Insert/Delete without specialties.
3462  bNewListening = (bRefModified || bColRowNameCompile
3463  || bValChanged || bHasRelName)
3464  // #i36299# Don't duplicate action during cut&paste / drag&drop
3465  // on a cell in the range moved, start/end listeners is done
3466  // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3467  && !(rDocument.IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
3468 
3469  if ( bNewListening )
3470  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3471  }
3472 
3473  bool bNeedDirty = false;
3474  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3475  if ( bRefModified || bColRowNameCompile ||
3476  (bValChanged && bHasRelName ) || bOnRefMove)
3477  bNeedDirty = true;
3478 
3479  if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3480  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3481 
3482  bValChanged = false;
3483 
3484  bCompile = (bCompile || bValChanged || bColRowNameCompile);
3485  if ( bCompile )
3486  {
3487  CompileTokenArray( bNewListening ); // no Listening
3488  bNeedDirty = true;
3489  }
3490 
3491  if ( !bInDeleteUndo )
3492  { // In ChangeTrack Delete-Reject listeners are established in
3493  // InsertCol/InsertRow
3494  if ( bNewListening )
3495  {
3497  }
3498  }
3499 
3500  if (bNeedDirty)
3501  { // Cut off references, invalid or similar?
3502  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3503  SetDirty();
3504  }
3505 
3506  return bCellStateChanged;
3507 }
3508 
3510  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3511 {
3512  if (rCxt.meMode != URM_COPY)
3513  return false;
3514 
3515  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3516  if ( pUndoCellPos )
3517  aUndoPos = *pUndoCellPos;
3518  ScAddress aOldPos( aPos );
3519 
3520  if (rCxt.maRange.In(aPos))
3521  {
3522  // The cell is being moved or copied to a new position. I guess the
3523  // position has been updated prior to this call? Determine
3524  // its original position before the move which will be used to adjust
3525  // relative references later.
3526  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3527  }
3528 
3529  // Check presence of any references or column row names.
3530  bool bHasRefs = pCode->HasReferences();
3531  bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3532  bHasRefs = bHasRefs || bHasColRowNames;
3533  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3534 
3535  if (!bHasRefs && !bOnRefMove)
3536  // This formula cell contains no references, nor needs recalculating
3537  // on reference update. Bail out.
3538  return false;
3539 
3540  std::unique_ptr<ScTokenArray> pOldCode;
3541  if (pUndoDoc)
3542  pOldCode = pCode->Clone();
3543 
3544  if (bOnRefMove)
3545  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3546  bOnRefMove = (aPos != aOldPos);
3547 
3548  bool bNeedDirty = bOnRefMove;
3549 
3550  if (pUndoDoc && bOnRefMove)
3551  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3552 
3553  if (bCompile)
3554  {
3555  CompileTokenArray(); // no Listening
3556  bNeedDirty = true;
3557  }
3558 
3559  if (bNeedDirty)
3560  { // Cut off references, invalid or similar?
3561  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3562  SetDirty();
3563  }
3564 
3565  return false;
3566 }
3567 
3569  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3570 {
3571  if (rDocument.IsClipOrUndo())
3572  return false;
3573 
3574  if (mxGroup && mxGroup->mpTopCell != this)
3575  {
3576  // This is not a top cell of a formula group. Don't update references.
3577 
3578  switch (rCxt.meMode)
3579  {
3580  case URM_INSDEL:
3581  return UpdatePosOnShift(rCxt);
3582  default:
3583  ;
3584  }
3585  return false;
3586  }
3587 
3588  switch (rCxt.meMode)
3589  {
3590  case URM_INSDEL:
3591  return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3592  case URM_MOVE:
3593  return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3594  case URM_COPY:
3595  return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3596  default:
3597  ;
3598  }
3599 
3600  return false;
3601 }
3602 
3604 {
3605  // Adjust tokens only when it's not grouped or grouped top cell.
3606  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3607  bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3609  {
3610  if (bPosChanged)
3611  aPos.IncTab(rCxt.mnSheets);
3612 
3613  return;
3614  }
3615 
3617  ScAddress aOldPos = aPos;
3618  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3619  if (bPosChanged)
3620  aPos.IncTab(rCxt.mnSheets);
3621 
3622  if (!bAdjustCode)
3623  return;
3624 
3626  if (aRes.mbNameModified)
3627  // Re-compile after new sheet(s) have been inserted.
3628  bCompile = true;
3629 
3630  // no StartListeningTo because the new sheets have not been inserted yet.
3631 }
3632 
3634 {
3635  // Adjust tokens only when it's not grouped or grouped top cell.
3636  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3637  bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3639  {
3640  if (bPosChanged)
3641  aPos.IncTab(-1*rCxt.mnSheets);
3642  return;
3643  }
3644 
3646  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3647  ScAddress aOldPos = aPos;
3648  if (bPosChanged)
3649  aPos.IncTab(-1*rCxt.mnSheets);
3650 
3651  if (!bAdjustCode)
3652  return;
3653 
3654  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3655  if (aRes.mbNameModified)
3656  // Re-compile after sheet(s) have been deleted.
3657  bCompile = true;
3658 }
3659 
3661 {
3662  // Adjust tokens only when it's not grouped or grouped top cell.
3663  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3664 
3666  {
3667  aPos.SetTab(nTabNo);
3668  return;
3669  }
3670 
3672  ScAddress aOldPos = aPos;
3673  // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3674  aPos.SetTab(nTabNo);
3675 
3676  // no StartListeningTo because pTab[nTab] not yet correct!
3677 
3678  if (!bAdjustCode)
3679  return;
3680 
3681  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3682  if (aRes.mbNameModified)
3683  // Re-compile after sheet(s) have been deleted.
3684  bCompile = true;
3685 }
3686 
3688 {
3689  if (rDocument.IsClipOrUndo())
3690  return;
3691 
3692  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3693  if (!bAdjustCode)
3694  return;
3695 
3698  while (p)
3699  {
3700  ScSingleRefData& rRef1 = *p->GetSingleRef();
3701  if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3702  rRef1.IncTab(1);
3703  if (p->GetType() == formula::svDoubleRef)
3704  {
3705  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3706  if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3707  rRef2.IncTab(1);
3708  }
3709  p = aIter.GetNextReferenceRPN();
3710  }
3711 }
3712 
3714 {
3715  if (rDocument.IsClipOrUndo())
3716  return false;
3717 
3718  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3719  if (!bAdjustCode)
3720  return false;
3721 
3722  bool bRet = false;
3725  while (p)
3726  {
3727  ScSingleRefData& rRef1 = *p->GetSingleRef();
3728  if (!rRef1.IsTabRel())
3729  {
3730  if (nTable != rRef1.Tab())
3731  bRet = true;
3732  else if (nTable != aPos.Tab())
3733  rRef1.SetAbsTab(aPos.Tab());
3734  }
3735  if (p->GetType() == formula::svDoubleRef)
3736  {
3737  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3738  if (!rRef2.IsTabRel())
3739  {
3740  if(nTable != rRef2.Tab())
3741  bRet = true;
3742  else if (nTable != aPos.Tab())
3743  rRef2.SetAbsTab(aPos.Tab());
3744  }
3745  }
3746  p = aIter.GetNextReferenceRPN();
3747  }
3748  return bRet;
3749 }
3750 
3751 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3752 {
3753  if ( bForceIfNameInUse && !bCompile )
3755  if ( bCompile )
3756  pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3758 }
3759 
3761 {
3762  // References to or over filtered rows are not adjusted
3763  // analog to the normal (non-transposed) case
3764  SCCOLROW nTemp = rRef.Col();
3765  rRef.SetRelCol(rRef.Row());
3766  rRef.SetRelRow(nTemp);
3767 }
3768 
3769 // Reference transposition is only called in Clipboard Document
3771 {
3772  bool bFound = false;
3775  while ( ( t = aIter.GetNextReference() ) != nullptr )
3776  {
3777  ScSingleRefData& rRef1 = *t->GetSingleRef();
3778  if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3779  {
3780  bool bDouble = (t->GetType() == formula::svDoubleRef);
3781  ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3782  if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3783  {
3784  lcl_TransposeReference(rRef1);
3785 
3786  if ( bDouble )
3787  lcl_TransposeReference(rRef2);
3788 
3789  bFound = true;
3790  }
3791  }
3792  }
3793 
3794  if (bFound)
3795  bCompile = true;
3796 }
3797 
3798 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3799  ScDocument* pUndoDoc )
3800 {
3802 
3803  ScAddress aOldPos = aPos;
3804  bool bPosChanged = false; // Whether this cell has been moved
3805 
3806  // Dest range is transposed
3807  ScRange aDestRange( rDest, ScAddress(
3808  static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3809  static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3810  rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3811 
3812  // cell within range
3813  if ( aDestRange.In( aOldPos ) )
3814  {
3815  // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
3816  // Count back Positions
3817  SCCOL nRelPosX = aOldPos.Col();
3818  SCROW nRelPosY = aOldPos.Row();
3819  SCTAB nRelPosZ = aOldPos.Tab();
3820  ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
3821  aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3822  bPosChanged = true;
3823  }
3824 
3825  std::unique_ptr<ScTokenArray> pOld;
3826  if (pUndoDoc)
3827  pOld = pCode->Clone();
3828  bool bRefChanged = false;
3829 
3832  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3833  {
3834  if( t->GetOpCode() == ocName )
3835  {
3837  if (pName && pName->IsModified())
3838  bRefChanged = true;
3839  }
3840  else if( t->GetType() != svIndex )
3841  {
3842  SingleDoubleRefModifier aMod(*t);
3843  ScComplexRefData& rRef = aMod.Ref();
3844  ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
3845  bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3846  if (bMod)
3847  {
3848  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3849  bRefChanged = true;
3850 
3851  // Absolute sheet reference => set 3D flag.
3852  // More than one sheet referenced => has to have both 3D flags.
3853  // If end part has 3D flag => start part must have it too.
3854  // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
3855  rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
3856  rRef.Ref1.SetFlag3D(
3857  (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
3858  || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
3859  }
3860  }
3861  }
3862 
3863  if (bRefChanged)
3864  {
3865  if (pUndoDoc)
3866  {
3867  // Similar to setOldCodeToUndo(), but it cannot be used due to the check
3868  // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
3869  ScFormulaCell* pFCell = new ScFormulaCell(
3870  *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
3871 
3872  pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3873  pUndoDoc->SetFormulaCell(aPos, pFCell);
3874  }
3875 
3876  bCompile = true;
3877  CompileTokenArray(); // also call StartListeningTo
3878  SetDirty();
3879  }
3880  else
3881  StartListeningTo( rDocument ); // Listener as previous
3882 }
3883 
3884 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3885 {
3887 
3888  bool bRefChanged = false;
3889 
3892 
3893  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3894  {
3895  if( t->GetOpCode() == ocName )
3896  {
3898  if (pName && pName->IsModified())
3899  bRefChanged = true;
3900  }
3901  else if( t->GetType() != svIndex )
3902  {
3903  SingleDoubleRefModifier aMod(*t);
3904  ScComplexRefData& rRef = aMod.Ref();
3905  ScRange aAbs = rRef.toAbs(rDocument, aPos);
3906  bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3907  if (bMod)
3908  {
3909  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
3910  bRefChanged = true;
3911  }
3912  }
3913  }
3914 
3915  if (bRefChanged)
3916  {
3917  bCompile = true;
3918  CompileTokenArray(); // Also call StartListeningTo
3919  SetDirty();
3920  }
3921  else
3922  StartListeningTo( rDocument ); // Listener as previous
3923 }
3924 
3925 // See also ScDocument::FindRangeNamesReferencingSheet()
3926 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
3927  int nRecursion)
3928 {
3929  FormulaTokenArrayPlainIterator aIter(*pCode);
3930  for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3931  {
3932  if (p->GetOpCode() == ocName)
3933  {
3934  sal_uInt16 nTokenIndex = p->GetIndex();
3935  SCTAB nTab = p->GetSheet();
3936  rIndexes.setUpdatedName( nTab, nTokenIndex);
3937 
3938  if (nRecursion < 126) // whatever... 42*3
3939  {
3940  ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3941  if (pSubName)
3942  lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
3943  }
3944  }
3945  }
3946 }
3947 
3949 {
3950  lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
3951 }
3952 
3954 {
3955  bChanged = b;
3956 }
3957 
3958 void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3959 {
3960  assert(!mxGroup); // Don't call this if it's shared.
3961  delete pCode;
3962  pCode = pNew.release(); // takes ownership.
3963 }
3964 
3965 void ScFormulaCell::SetRunning( bool bVal )
3966 {
3967  bRunning = bVal;
3968 }
3969 
3971 {
3973  for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3974  {
3975  OpCode eOp = p->GetOpCode();
3976  if ( eOp == ocDBArea || eOp == ocTableRef )
3977  {
3978  bCompile = true;
3979  CompileTokenArray(rCxt);
3980  SetDirty();
3981  break;
3982  }
3983  }
3984 }
3985 
3987 {
3989  for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3990  {
3991  if ( p->GetOpCode() == ocColRowName )
3992  {
3993  bCompile = true;
3994  CompileTokenArray(rCxt);
3995  SetDirty();
3996  break;
3997  }
3998  }
3999 }
4000 
4005 
4007 {
4008  if (mxGroup)
4009  {
4010  // You can't create a new group if the cell is already a part of a group.
4011  // Is this a sign of some inconsistent or incorrect data structures? Or normal?
4012  SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
4013  return ScFormulaCellGroupRef();
4014  }
4015 
4016  mxGroup.reset(new ScFormulaCellGroup);
4017  mxGroup->mpTopCell = this;
4018  mxGroup->mbInvariant = bInvariant;
4019  mxGroup->mnLength = nLen;
4020  mxGroup->mpCode = std::move(*pCode); // Move this to the shared location.
4021  delete pCode;
4022  pCode = &*mxGroup->mpCode;
4023  return mxGroup;
4024 }
4025 
4027 {
4028  if (!xRef)
4029  {
4030  // Make this cell a non-grouped cell.
4031  if (mxGroup)
4032  pCode = mxGroup->mpCode->Clone().release();
4033 
4034  mxGroup = xRef;
4035  return;
4036  }
4037 
4038  // Group object has shared token array.
4039  if (!mxGroup)
4040  // Currently not shared. Delete the existing token array first.
4041  delete pCode;
4042 
4043  mxGroup = xRef;
4044  pCode = &*mxGroup->mpCode;
4045  mxGroup->mnWeight = 0; // invalidate
4046 }
4047 
4049 {
4050  // no Matrix formulae yet.
4051  if ( GetMatrixFlag() != ScMatrixMode::NONE )
4052  return NotEqual;
4053 
4054  // are these formulas at all similar ?
4055  if ( GetHash() != rOther.GetHash() )
4056  return NotEqual;
4057 
4058  if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4059  return NotEqual;
4060 
4061  FormulaToken **pThis = pCode->GetCode();
4062  sal_uInt16 nThisLen = pCode->GetCodeLen();
4063  FormulaToken **pOther = rOther.pCode->GetCode();
4064  sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4065 
4066  if ( !pThis || !pOther )
4067  {
4068  // Error: no compiled code for cells !"
4069  return NotEqual;
4070  }
4071 
4072  if ( nThisLen != nOtherLen )
4073  return NotEqual;
4074 
4075  // No tokens can be an error cell so check error code, otherwise we could
4076  // end up with a series of equal error values instead of individual error
4077  // values. Also if for any reason different errors are set even if all
4078  // tokens are equal, the cells are not equal.
4079  if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4080  return NotEqual;
4081 
4082  bool bInvariant = true;
4083 
4084  // check we are basically the same function
4085  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4086  {
4087  formula::FormulaToken *pThisTok = pThis[i];
4088  formula::FormulaToken *pOtherTok = pOther[i];
4089 
4090  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4091  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4092  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4093  {
4094  // Incompatible type, op-code or param counts.
4095  return NotEqual;
4096  }
4097 
4098  switch (pThisTok->GetType())
4099  {
4100  case formula::svMatrix:
4103  // Ignoring matrix and external references for now.
4104  return NotEqual;
4105 
4106  case formula::svSingleRef:
4107  {
4108  // Single cell reference.
4109  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4110  if (rRef != *pOtherTok->GetSingleRef())
4111  return NotEqual;
4112 
4113  if (rRef.IsRowRel())
4114  bInvariant = false;
4115  }
4116  break;
4117  case formula::svDoubleRef:
4118  {
4119  // Range reference.
4120  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4121  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4122  if (rRef1 != *pOtherTok->GetSingleRef())
4123  return NotEqual;
4124 
4125  if (rRef2 != *pOtherTok->GetSingleRef2())
4126  return NotEqual;
4127 
4128  if (rRef1.IsRowRel())
4129  bInvariant = false;
4130 
4131  if (rRef2.IsRowRel())
4132  bInvariant = false;
4133  }
4134  break;
4135  case formula::svDouble:
4136  {
4137  if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4138  return NotEqual;
4139  }
4140  break;
4141  case formula::svString:
4142  {
4143  if(pThisTok->GetString() != pOtherTok->GetString())
4144  return NotEqual;
4145  }
4146  break;
4147  case formula::svIndex:
4148  {
4149  if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4150  return NotEqual;
4151  }
4152  break;
4153  case formula::svByte:
4154  {
4155  if(pThisTok->GetByte() != pOtherTok->GetByte())
4156  return NotEqual;
4157  }
4158  break;
4159  case formula::svExternal:
4160  {
4161  if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4162  return NotEqual;
4163 
4164  if (pThisTok->GetByte() != pOtherTok->GetByte())
4165  return NotEqual;
4166  }
4167  break;
4168  case formula::svError:
4169  {
4170  if (pThisTok->GetError() != pOtherTok->GetError())
4171  return NotEqual;
4172  }
4173  break;
4174  default:
4175  ;
4176  }
4177  }
4178 
4179  // If still the same, check lexical names as different names may result in
4180  // identical RPN code.
4181 
4182  pThis = pCode->GetArray();
4183  nThisLen = pCode->GetLen();
4184  pOther = rOther.pCode->GetArray();
4185  nOtherLen = rOther.pCode->GetLen();
4186 
4187  if ( !pThis || !pOther )
4188  {
4189  // Error: no code for cells !"
4190  return NotEqual;
4191  }
4192 
4193  if ( nThisLen != nOtherLen )
4194  return NotEqual;
4195 
4196  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4197  {
4198  formula::FormulaToken *pThisTok = pThis[i];
4199  formula::FormulaToken *pOtherTok = pOther[i];
4200 
4201  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4202  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4203  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4204  {
4205  // Incompatible type, op-code or param counts.
4206  return NotEqual;
4207  }
4208 
4209  switch (pThisTok->GetType())
4210  {
4211  // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4212  // resulting in identical RPN references that could lead to creating
4213  // a formula group from formulas that should not be merged into a group,
4214  // so check also the formula itself.
4215  case formula::svSingleRef:
4216  {
4217  // Single cell reference.
4218  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4219  if (rRef != *pOtherTok->GetSingleRef())
4220  return NotEqual;
4221 
4222  if (rRef.IsRowRel())
4223  bInvariant = false;
4224  }
4225  break;
4226  case formula::svDoubleRef:
4227  {
4228  // Range reference.
4229  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4230  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4231  if (rRef1 != *pOtherTok->GetSingleRef())
4232  return NotEqual;
4233 
4234  if (rRef2 != *pOtherTok->GetSingleRef2())
4235  return NotEqual;
4236 
4237  if (rRef1.IsRowRel())
4238  bInvariant = false;
4239 
4240  if (rRef2.IsRowRel())
4241  bInvariant = false;
4242  }
4243  break;
4244  // All index tokens are names. Different categories already had
4245  // different OpCode values.
4246  case formula::svIndex:
4247  {
4248  if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4249  return NotEqual;
4250  switch (pThisTok->GetOpCode())
4251  {
4252  case ocTableRef:
4253  // nothing, sheet value assumed as -1, silence
4254  // ScTableRefToken::GetSheet() SAL_WARN about
4255  // unhandled
4256  ;
4257  break;
4258  default: // ocName, ocDBArea
4259  if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4260  return NotEqual;
4261  }
4262  }
4263  break;
4264  default:
4265  ;
4266  }
4267  }
4268 
4269  return bInvariant ? EqualInvariant : EqualRelativeRef;
4270 }
4271 
4272 namespace {
4273 
4274 // Split N into optimally equal-sized pieces, each not larger than K.
4275 // Return value P is number of pieces. A returns the number of pieces
4276 // one larger than N/P, 0..P-1.
4277 
4278 int splitup(int N, int K, int& A)
4279 {
4280  assert(N > 0);
4281  assert(K > 0);
4282 
4283  A = 0;
4284 
4285  if (N <= K)
4286  return 1;
4287 
4288  const int ideal_num_parts = N / K;
4289  if (ideal_num_parts * K == N)
4290  return ideal_num_parts;
4291 
4292  const int num_parts = ideal_num_parts + 1;
4293  const int nominal_part_size = N / num_parts;
4294 
4295  A = N - num_parts * nominal_part_size;
4296 
4297  return num_parts;
4298 }
4299 
4300 struct ScDependantsCalculator
4301 {
4302  ScDocument& mrDoc;
4303  const ScTokenArray& mrCode;
4304  const ScFormulaCellGroupRef& mxGroup;
4305  const SCROW mnLen;
4306  const ScAddress& mrPos;
4307  const bool mFromFirstRow;
4308  const SCROW mnStartOffset;
4309  const SCROW mnEndOffset;
4310  const SCROW mnSpanLen;
4311 
4312  ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4313  const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4314  mrDoc(rDoc),
4315  mrCode(rCode),
4316  mxGroup(rCell.GetCellGroup()),
4317  mnLen(mxGroup->mnLength),
4318  mrPos(rPos),
4319  // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4320  // only from further rows. This data fetching could also lead to Interpret() calls, so
4321  // in OpenCL mode the formula in practice depends on those cells too.
4322  mFromFirstRow(fromFirstRow),
4323  mnStartOffset(nStartOffset),
4324  mnEndOffset(nEndOffset),
4325  mnSpanLen(nEndOffset - nStartOffset + 1)
4326  {
4327  }
4328 
4329  // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4330  // (note already modified a bit, mFromFirstRow)
4331 
4332  // I think what this function does is to check whether the relative row reference nRelRow points
4333  // to a row that is inside the range of rows covered by the formula group.
4334 
4335  bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4336  {
4337  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4338  return false;
4339 
4340  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4341 
4342  if (nRelRow <= 0)
4343  {
4344  SCROW nTest = nEndRow;
4345  nTest += nRelRow;
4346  if (nTest >= mrPos.Row())
4347  return true;
4348  }
4349  else
4350  {
4351  SCROW nTest = mrPos.Row(); // top row.
4352  nTest += nRelRow;
4353  if (nTest <= nEndRow)
4354  return true;
4355  // If pointing below the formula, it's always included if going from first row.
4356  if (mFromFirstRow)
4357  return true;
4358  }
4359 
4360  return false;
4361  }
4362 
4363  // FIXME: another copy-paste
4364 
4365  // And this correspondingly checks whether an absolute row is inside the range of rows covered
4366  // by the formula group.
4367 
4368  bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4369  {
4370  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4371  return false;
4372 
4373  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4374 
4375  if (rRefPos.Row() < mrPos.Row())
4376  return false;
4377 
4378  // If pointing below the formula, it's always included if going from first row.
4379  if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4380  return false;
4381 
4382  return true;
4383  }
4384 
4385  // Checks if the doubleref engulfs all of formula group cells
4386  // Note : does not check if there is a partial overlap, that can be done by calling
4387  // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4388  bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4389  {
4390  if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4391  || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4392  {
4393  return false;
4394  }
4395 
4396  SCROW nStartRow = mrPos.Row();
4397  SCROW nEndRow = nStartRow + mnLen - 1;
4398  SCROW nRefStartRow = rAbs.aStart.Row();
4399  SCROW nRefEndRow = rAbs.aEnd.Row();
4400 
4401  if (bIsRef1RowRel && bIsRef2RowRel &&
4402  ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4403  ((nRefStartRow + mnLen - 1) <= nStartRow &&
4404  (nRefEndRow + mnLen - 1) >= nEndRow)))
4405  return true;
4406 
4407  if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4408  (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4409  return true;
4410 
4411  if (!bIsRef2RowRel &&
4412  nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4413  return true;
4414 
4415  // If going from first row, the referenced range must be entirely above the formula,
4416  // otherwise the formula would be included.
4417  if (mFromFirstRow && nRefEndRow >= nStartRow)
4418  return true;
4419 
4420  return false;
4421  }
4422 
4423  // FIXME: another copy-paste
4424  SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4425  {
4426  SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4427  nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4428  if (nLastRow < (nRow + nRowLen - 1))
4429  {
4430  // This can end up negative! Was that the original intent, or
4431  // is it accidental? Was it not like that originally but the
4432  // surrounding conditions changed?
4433  nRowLen = nLastRow - nRow + 1;
4434  // Anyway, let's assume it doesn't make sense to return a
4435  // negative or zero value here.
4436  if (nRowLen <= 0)
4437  nRowLen = 1;
4438  }
4439  else if (nLastRow == 0)
4440  // Column is empty.
4441  nRowLen = 1;
4442 
4443  return nRowLen;
4444  }
4445 
4446  bool DoIt()
4447  {
4448  // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4449 
4450  ScRangeList aRangeList;
4451 
4452  // Self references should be checked by considering the entire formula-group not just the provided span.
4453  bool bHasSelfReferences = false;
4454  bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4455 
4456  FormulaToken** pRPNArray = mrCode.GetCode();
4457  sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4458  for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4459  {
4460  auto p = pRPNArray[nTokenIdx];
4461  if (!bInDocShellRecalc)
4462  {
4463  // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4464  // of the result of the condition expression.
4465  // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4466  // in the document. So lets disable threading and stop dependency evaluation if
4467  // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4468  // for formulae with IF/IFS/SWITCH
4469  OpCode nOpCode = p->GetOpCode();
4470  if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4471  return false;
4472  }
4473 
4474  switch (p->GetType())
4475  {
4476  case svSingleRef:
4477  {
4478  ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4479  if( aRef.IsDeleted())
4480  return false;
4481  ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
4482 
4483  if (!mrDoc.TableExists(aRefPos.Tab()))
4484  return false; // or true?
4485 
4486  if (aRef.IsRowRel())
4487  {
4488  if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4489  {
4490  bHasSelfReferences = true;
4491  continue;
4492  }
4493 
4494  // Trim data array length to actual data range.
4495  SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4496 
4497  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4498  aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4499  }
4500  else
4501  {
4502  if (isSelfReferenceAbsolute(aRefPos))
4503  {
4504  bHasSelfReferences = true;
4505  continue;
4506  }
4507 
4508  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4509  }
4510  }
4511  break;
4512  case svDoubleRef:
4513  {
4514  ScComplexRefData aRef = *p->GetDoubleRef();
4515  if( aRef.IsDeleted())
4516  return false;
4517  ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
4518 
4519  // Multiple sheet
4520  if (aRef.Ref1.Tab() != aRef.Ref2.Tab())
4521  return false;
4522 
4523  bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4524  // Check for self reference.
4525  if (bIsRef1RowRel)
4526  {
4527  if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4528  {
4529  bHasSelfReferences = true;
4530  continue;
4531  }
4532  }
4533  else if (isSelfReferenceAbsolute(aAbs.aStart))
4534  {
4535  bHasSelfReferences = true;
4536  continue;
4537  }
4538 
4539  bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4540  if (bIsRef2RowRel)
4541  {
4542  if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4543  {
4544  bHasSelfReferences = true;
4545  continue;
4546  }
4547  }
4548  else if (isSelfReferenceAbsolute(aAbs.aEnd))
4549  {
4550  bHasSelfReferences = true;
4551  continue;
4552  }
4553 
4554  if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4555  {
4556  bHasSelfReferences = true;
4557  continue;
4558  }
4559 
4560  // The first row that will be referenced through the doubleref.
4561  SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4562  // The last row that will be referenced through the doubleref.
4563  SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4564  // Number of rows to be evaluated from nFirstRefRow.
4565  SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4566  assert(nArrayLength > 0);
4567 
4568  // Trim trailing empty rows.
4569  nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4570 
4571  aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4572  aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4573  }
4574  break;
4575  default:
4576  break;
4577  }
4578  }
4579 
4580  // Compute dependencies irrespective of the presence of any self references.
4581  // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4582  // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4583  for (size_t i = 0; i < aRangeList.size(); ++i)
4584  {
4585  const ScRange & rRange = aRangeList[i];
4586  assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4587  for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4588  {
4589  SCROW nStartRow = rRange.aStart.Row();
4590  SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4591  if( mFromFirstRow )
4592  { // include also all previous rows
4593  nLength += nStartRow;
4594  nStartRow = 0;
4595  }
4596  if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4597  nLength, mxGroup))
4598  return false;
4599  }
4600  }
4601 
4602  if (bHasSelfReferences)
4603  mxGroup->mbPartOfCycle = true;
4604 
4605  return !bHasSelfReferences;
4606  }
4607 };
4608 
4609 } // anonymous namespace
4610 
4611 bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset)
4612 {
4613  if (!mxGroup || !pCode)
4614  return false;
4615 
4616  auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
4617  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4618 
4619  if (mxGroup->mbPartOfCycle)
4620  {
4621  aScope.addMessage("This formula-group is part of a cycle");
4622  return false;
4623  }
4624 
4625  if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4626  {
4627  aScope.addMessage("group calc disabled");
4628  return false;
4629  }
4630 
4631  // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests
4633  if (forceType == ForceCalculationCore
4634  || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4635  && forceType != ForceCalculationOpenCL
4636  && forceType != ForceCalculationThreads))
4637  {
4638  mxGroup->meCalcState = sc::GroupCalcDisabled;
4639  aScope.addGroupSizeThresholdMessage(*this);
4640  return false;
4641  }
4642 
4643  if (cMatrixFlag != ScMatrixMode::NONE)
4644  {
4645  mxGroup->meCalcState = sc::GroupCalcDisabled;
4646  aScope.addMessage("matrix skipped");
4647  return false;
4648  }
4649 
4650  if( forceType != ForceCalculationNone )
4651  {
4652  // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4653  // without it actually being in the document at the specified position.
4654  // That would confuse opencl/threading code, as they refer to the cell group
4655  // also using the position. This is normally not triggered (single cells
4656  // are normally not in a cell group), but if forced, check for this explicitly.
4657  if( rDocument.GetFormulaCell( aPos ) != this )
4658  {
4659  mxGroup->meCalcState = sc::GroupCalcDisabled;
4660  aScope.addMessage("cell not in document");
4661  return false;
4662  }
4663  }
4664 
4665  // Guard against endless recursion of Interpret() calls, for this to work
4666  // ScFormulaCell::InterpretFormulaGroup() must never be called through
4667  // anything else than ScFormulaCell::Interpret(), same as
4668  // ScFormulaCell::InterpretTail()
4669  RecursionCounter aRecursionCounter( rRecursionHelper, this);
4670 
4671  bool bDependencyComputed = false;
4672  bool bDependencyCheckFailed = false;
4673 
4674  // Get rid of -1's in offsets (defaults) or any invalid offsets.
4675  SCROW nMaxOffset = mxGroup->mnLength - 1;
4676  nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4677  nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4678 
4679  if (nEndOffset < nStartOffset)
4680  {
4681  nStartOffset = 0;
4682  nEndOffset = nMaxOffset;
4683  }
4684 
4685  // Preference order: First try OpenCL, then threading.
4686  // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4687  if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4688  return true;
4689 
4690  if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4691  return true;
4692 
4693  return false;
4694 }
4695 
4697  SCROW nStartOffset, SCROW nEndOffset,
4698  bool bCalcDependencyOnly)
4699 {
4700  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4701  // iterate over code in the formula ...
4702  // ensure all input is pre-calculated -
4703  // to avoid writing during the calculation
4704  if (bCalcDependencyOnly)
4705  {
4706  // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4707  // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4708  // (We can only reach here from a multi-group dependency evaluation attempt).
4709  // (These two have to be in pairs always for any given formula-group)
4710  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4711  return aCalculator.DoIt();
4712  }
4713 
4714  bool bOKToParallelize = false;
4715  {
4716  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4717  if (mxGroup->mbPartOfCycle)
4718  {
4719  mxGroup->meCalcState = sc::GroupCalcDisabled;
4720  rScope.addMessage("found circular formula-group dependencies");
4721  return false;
4722  }
4723 
4724  ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4725  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4726  bOKToParallelize = aCalculator.DoIt();
4727 
4728  }
4729 
4730  if (rRecursionHelper.IsInRecursionReturn())
4731  {
4732  mxGroup->meCalcState = sc::GroupCalcDisabled;
4733  rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4734  return false;
4735  }
4736 
4737  if (mxGroup->mbPartOfCycle)
4738  {
4739  mxGroup->meCalcState = sc::GroupCalcDisabled;
4740  rScope.addMessage("found circular formula-group dependencies");
4741  return false;
4742  }
4743 
4744  if (!rRecursionHelper.AreGroupsIndependent())
4745  {
4746  // This call resulted from a dependency calculation for a multigroup-threading attempt,
4747  // but found dependency among the groups.
4748  rScope.addMessage("multi-group-dependency failed");
4749  return false;
4750  }
4751 
4752  if (!bOKToParallelize)
4753  {
4754  mxGroup->meCalcState = sc::GroupCalcDisabled;
4755  rScope.addMessage("could not do new dependencies calculation thing");
4756  return false;
4757  }
4758 
4759  return true;
4760 }
4761 
4762 static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc,
4764  std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4765 {
4766  const SCROW nLen = xGroup->mnLength;
4767  const sal_Int32 nWt = xGroup->mnWeight;
4768  ScAddress aAddr(xGroup->mpTopCell->aPos);
4769 
4770  SCCOL nColRet = aAddr.Col();
4771 
4772  const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4773  if (bLeft)
4774  --nColRet;
4775  else
4776  ++nColRet;
4777 
4778  while (nColRet >= 0 && nColRet <= nMaxCol)
4779  {
4780  aAddr.SetCol(nColRet);
4781  const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4782  if (!pCell)
4783  break;
4784 
4785  if (!pCell->NeedsInterpret())
4786  break;
4787 
4788  const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4789  if (!xNGroup)
4790  break;
4791 
4792  if (!pCell->GetCode()->IsEnabledForThreading())
4793  break;
4794 
4795  if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4796  break;
4797 
4798  const SCROW nNLen = xNGroup->mnLength;
4799  const sal_Int32 nNWt = pCell->GetWeight();
4800  if (nNLen != nLen || nNWt != nWt)
4801  break;
4802 
4803  rFGSet.insert(xNGroup.get());
4804  rFGMap[nColRet] = xNGroup->mpTopCell;
4805 
4806  if (bLeft)
4807  --nColRet;
4808  else
4809  ++nColRet;
4810  }
4811 
4812  if (bLeft)
4813  ++nColRet;
4814  else
4815  --nColRet;
4816 
4817  return nColRet;
4818 }
4819 
4820 // To be called only from InterpretFormulaGroup().
4822  bool& bDependencyComputed,
4823  bool& bDependencyCheckFailed,
4824  SCROW nStartOffset,
4825  SCROW nEndOffset)
4826 {
4827  static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4828  if (!bDependencyCheckFailed && !bThreadingProhibited &&
4831  {
4832  if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4833  {
4834  bDependencyComputed = true;
4835  bDependencyCheckFailed = true;
4836  return false;
4837  }
4838 
4839  bDependencyComputed = true;
4840 
4841  const static bool bHyperThreadingActive = cpuid::hasHyperThreading();
4842 
4843  // Then do the threaded calculation
4844 
4845  class Executor : public comphelper::ThreadTask
4846  {
4847  private:
4848  const unsigned mnThisThread;
4849  const unsigned mnThreadsTotal;
4850  ScDocument* mpDocument;
4852  const ScAddress& mrTopPos;
4853  SCCOL mnStartCol;
4854  SCCOL mnEndCol;
4855  SCROW mnStartOffset;
4856  SCROW mnEndOffset;
4857 
4858  public:
4859  Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4860  unsigned nThisThread,
4861  unsigned nThreadsTotal,
4862  ScDocument* pDocument2,
4863  ScInterpreterContext* pContext,
4864  const ScAddress& rTopPos,
4865  SCCOL nStartCol,
4866  SCCOL nEndCol,
4867  SCROW nStartOff,
4868  SCROW nEndOff) :
4869  comphelper::ThreadTask(rTag),
4870  mnThisThread(nThisThread),
4871  mnThreadsTotal(nThreadsTotal),
4872  mpDocument(pDocument2),
4873  mpContext(pContext),
4874  mrTopPos(rTopPos),
4875  mnStartCol(nStartCol),
4876  mnEndCol(nEndCol),
4877  mnStartOffset(nStartOff),
4878  mnEndOffset(nEndOff)
4879  {
4880  }
4881 
4882  virtual void doWork() override
4883  {
4884  ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4885  mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4886  mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4887  }
4888 
4889  };
4890 
4891  SvNumberFormatter* pNonThreadedFormatter = rDocument.GetNonThreadedContext().GetFormatTable();
4892 
4894  sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4895 
4896  if ( bHyperThreadingActive && nThreadCount >= 2 )
4897  nThreadCount /= 2;
4898 
4899  SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4900 
4902  std::map<SCCOL, ScFormulaCell*> aFGMap;
4903  aFGSet.insert(mxGroup.get());
4904 
4905  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4906  SCCOL nColStart = aPos.Col();
4907  SCCOL nColEnd = nColStart;
4908  if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
4909  {
4910  nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
4911  nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
4912  }
4913 
4914  if (nColStart != nColEnd)
4915  {
4916  ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4917  for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4918  {
4919  if (nCurrCol == aPos.Col())
4920  continue;
4921 
4922  bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4923  if (!bFGOK || !aGuard.AreGroupsIndependent())
4924  {
4925  nColEnd = nColStart = aPos.Col();
4926  break;
4927  }
4928  }
4929  }
4930 
4931  std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
4932  {
4935 
4937 
4938  // Start nThreadCount new threads
4939  std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4940  ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
4941  ScInterpreterContext* context = nullptr;
4942 
4943  for (int i = 0; i < nThreadCount; ++i)
4944  {
4945  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4946  assert(!context->pInterpreter);
4947  aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
4948  context->pInterpreter = aInterpreters[i].get();
4950  rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos,
4951  nColStart, nColEnd, nStartOffset, nEndOffset));
4952  }
4953 
4954  SAL_INFO("sc.threaded", "Waiting for threads to finish work");
4955  // Do not join the threads here. They will get joined in ScDocument destructor
4956  // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
4957  rThreadPool.waitUntilDone(aTag, false);
4958 
4960 
4961  for (int i = 0; i < nThreadCount; ++i)
4962  {
4963  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4964  // This is intentionally done in this main thread in order to avoid locking.
4966  context->pInterpreter = nullptr;
4967  }
4968 
4969  SAL_INFO("sc.threaded", "Done");
4970  }
4971 
4972  ScAddress aStartPos(mxGroup->mpTopCell->aPos);
4973  SCROW nSpanLen = nEndOffset - nStartOffset + 1;
4974  aStartPos.SetRow(aStartPos.Row() + nStartOffset);
4975  // Reuse one of the previously allocated interpreter objects here.
4976  rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
4977  aStartPos.Tab(), aInterpreters[0].get());
4978 
4979  return true;
4980  }
4981 
4982  return false;
4983 }
4984 
4985 // To be called only from InterpretFormulaGroup().
4987  bool& bDependencyComputed,
4988  bool& bDependencyCheckFailed)
4989 {
4990  bool bCanVectorize = pCode->IsEnabledForOpenCL();
4991  switch (pCode->GetVectorState())
4992  {
4993  case FormulaVectorEnabled:
4995  break;
4996 
4997  // Not good.
4999  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
5000  break;
5002  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
5003  break;
5005  aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
5006  break;
5007  case FormulaVectorDisabled:
5008  case FormulaVectorUnknown:
5009  default:
5010  aScope.addMessage("group calc disabled due to vector state (unknown)");
5011  return false;
5012  }
5013 
5014  if (!bCanVectorize)
5015  return false;
5016 
5018  {
5019  aScope.addMessage("opencl not enabled");
5020  return false;
5021  }
5022 
5023  // TableOp does tricks with using a cell with different values, just bail out.
5025  return false;
5026 
5027  if (bDependencyCheckFailed)
5028  return false;
5029 
5030  if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
5031  {
5032  bDependencyComputed = true;
5033  bDependencyCheckFailed = true;
5034  return false;
5035  }
5036 
5037  bDependencyComputed = true;
5038 
5039  // TODO : Disable invariant formula group interpretation for now in order
5040  // to get implicit intersection to work.
5041  if (mxGroup->mbInvariant && false)
5043 
5044  int nMaxGroupLength = INT_MAX;
5045 
5046 #ifdef _WIN32
5047  // Heuristic: Certain old low-end OpenCL implementations don't
5048  // work for us with too large group lengths. 1000 was determined
5049  // empirically to be a good compromise.
5050  if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
5051  nMaxGroupLength = 1000;
5052 #endif
5053 
5054  if (std::getenv("SC_MAX_GROUP_LENGTH"))
5055  nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
5056 
5057  int nNumOnePlus;
5058  const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
5059 
5060  int nOffset = 0;
5061  int nCurChunkSize;
5062  ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
5063  for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
5064  {
5065  nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
5066 
5067  ScFormulaCellGroupRef xGroup;
5068 
5069  if (nNumParts == 1)
5070  xGroup = mxGroup;
5071  else
5072  {
5073  // Ugly hack
5074  xGroup = new ScFormulaCellGroup();
5075  xGroup->mpTopCell = mxGroup->mpTopCell;
5076  xGroup->mpTopCell->aPos = aOrigPos;
5077  xGroup->mpTopCell->aPos.IncRow(nOffset);
5078  xGroup->mbInvariant = mxGroup->mbInvariant;
5079  xGroup->mnLength = nCurChunkSize;
5080  xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
5081  }
5082 
5083  ScTokenArray aCode(rDocument);
5084  ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos);
5085  // TODO avoid this extra compilation
5086  ScCompiler aComp( rDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
5087  aComp.CompileTokenArray();
5088  if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
5089  {
5091  {
5092  SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
5093 #ifdef DBG_UTIL
5094  for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
5095  {
5096  SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
5097  <<