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 <formulagroup.hxx>
58 #include <listenercontext.hxx>
59 #include <types.hxx>
60 #include <scopetools.hxx>
61 #include <refupdatecontext.hxx>
62 #include <tokenstringcontext.hxx>
63 #include <refhint.hxx>
64 #include <listenerquery.hxx>
65 #include <listenerqueryids.hxx>
66 #include <grouparealistener.hxx>
67 #include <formulalogger.hxx>
68 #include <com/sun/star/sheet/FormulaLanguage.hpp>
69 
70 #if HAVE_FEATURE_OPENCL
71 #include <opencl/openclwrapper.hxx>
72 #endif
73 
74 #include <memory>
75 #include <map>
76 #include <vector>
77 #include <unordered_set>
78 
79 using namespace formula;
80 
81 #define DEBUG_CALCULATION 0
82 #if DEBUG_CALCULATION
83 static bool bDebugCalculationActive = false; // Set to true for global active init,
84 static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like
85 
86 struct DebugCalculationEntry
87 {
88  ScAddress maPos;
89  OUString maResult;
90  const ScDocument* mpDoc;
91  sal_uInt32 mnGroup;
92  sal_uInt16 mnRecursion;
93 
94  DebugCalculationEntry( const ScAddress& rPos, ScDocument* pDoc, sal_uInt32 nGroup ) :
95  maPos(rPos),
96  mpDoc(pDoc),
97  mnGroup(nGroup),
98  mnRecursion(pDoc->GetRecursionHelper().GetRecursionCount())
99  {
100  }
101 };
102 
109 static struct DebugCalculation
110 {
111  std::vector< DebugCalculationEntry > mvPos;
112  std::vector< DebugCalculationEntry > mvResults;
113  ScAddress maTrigger;
114  sal_uInt32 mnGroup;
115  bool mbActive;
116  bool mbSwitchOff;
117  bool mbPrint;
118  bool mbPrintResults;
119 
120  DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false),
121  mbPrint(true), mbPrintResults(false) {}
122 
124  void print() const
125  {
126  for (auto const& it : mvPos)
127  {
128  OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, it.mpDoc) +
129  " [" + OUString::number( it.mnRecursion) + "," + OUString::number( it.mnGroup) + "]");
130  fprintf( stderr, "%s -> ", aStr.toUtf8().getStr());
131  }
132  fprintf( stderr, "%s", "END\n");
133  }
134 
136  void printResults() const
137  {
138  for (auto const& it : mvResults)
139  {
140  OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, it.mpDoc));
141  aStr += " (" + it.maResult + ")";
142  fprintf( stderr, "%s, ", aStr.toUtf8().getStr());
143  }
144  fprintf( stderr, "%s", "END\n");
145  }
146 
147  void storeResult( const svl::SharedString& rStr )
148  {
149  if (mbActive && !mvPos.empty())
150  mvPos.back().maResult = "\"" + rStr.getString() + "\"";
151  }
152 
153  void storeResult( const double& fVal )
154  {
155  if (mbActive && !mvPos.empty())
156  mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true);
157  }
158 
159  void storeResultError( FormulaError nErr )
160  {
161  if (mbActive && !mvPos.empty())
162  mvPos.back().maResult = "Err:" + OUString::number( int( nErr ));
163  }
164 
165  void enterGroup()
166  {
167  ++mnGroup;
168  }
169 
170  void leaveGroup()
171  {
172  --mnGroup;
173  }
174 
175 } aDC;
176 
177 struct DebugCalculationStacker
178 {
179  DebugCalculationStacker( const ScAddress& rPos, ScDocument* pDoc )
180  {
181  if (!aDC.mbActive && rPos == aDC.maTrigger)
182  aDC.mbActive = aDC.mbSwitchOff = true;
183  if (aDC.mbActive)
184  {
185  aDC.mvPos.push_back( DebugCalculationEntry( rPos, pDoc, aDC.mnGroup));
186  aDC.mbPrint = true;
187  }
188  }
189 
190  ~DebugCalculationStacker()
191  {
192  if (aDC.mbActive)
193  {
194  if (!aDC.mvPos.empty())
195  {
196  if (aDC.mbPrint)
197  {
198  aDC.print();
199  aDC.mbPrint = false;
200  }
201  if (aDC.mbPrintResults)
202  {
203  // Store results until final result is available, reversing order.
204  aDC.mvResults.push_back( aDC.mvPos.back());
205  }
206  aDC.mvPos.pop_back();
207  if (aDC.mbPrintResults && aDC.mvPos.empty())
208  {
209  aDC.printResults();
210  std::vector< DebugCalculationEntry >().swap( aDC.mvResults);
211  }
212  if (aDC.mbSwitchOff && aDC.mvPos.empty())
213  aDC.mbActive = false;
214  }
215  }
216  }
217 };
218 #endif
219 
220 namespace {
221 
222 // More or less arbitrary, of course all recursions must fit into available
223 // stack space (which is what on all systems we don't know yet?). Choosing a
224 // lower value may be better than trying a much higher value that also isn't
225 // sufficient but temporarily leads to high memory consumption. On the other
226 // hand, if the value fits all recursions, execution is quicker as no resumes
227 // are necessary. Could be made a configurable option.
228 // Allow for a year's calendar (366).
229 const sal_uInt16 MAXRECURSION = 400;
230 
231 typedef SCCOLROW(*DimensionSelector)(const ScDocument*, const ScAddress&, const ScSingleRefData&);
232 
233 SCCOLROW lcl_GetCol(const ScDocument* pDoc, const ScAddress& rPos, const ScSingleRefData& rData)
234 {
235  return rData.toAbs(pDoc, rPos).Col();
236 }
237 
238 SCCOLROW lcl_GetRow(const ScDocument* pDoc, const ScAddress& rPos, const ScSingleRefData& rData)
239 {
240  return rData.toAbs(pDoc, rPos).Row();
241 }
242 
243 SCCOLROW lcl_GetTab(const ScDocument* pDoc, const ScAddress& rPos, const ScSingleRefData& rData)
244 {
245  return rData.toAbs(pDoc, rPos).Tab();
246 }
247 
250 bool
251 lcl_checkRangeDimension(
252  const ScDocument* pDoc,
253  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
254  const DimensionSelector aWhich)
255 {
256  return aWhich(pDoc, rPos, rRef1.Ref1) == aWhich(pDoc, rPos, rRef2.Ref1) &&
257  aWhich(pDoc, rPos, rRef1.Ref2) == aWhich(pDoc, rPos, rRef2.Ref2);
258 }
259 
260 bool
261 lcl_checkRangeDimensions(
262  const ScDocument* pDoc,
263  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
264  bool& bCol, bool& bRow, bool& bTab)
265 {
266  const bool bSameCols(lcl_checkRangeDimension(pDoc, rPos, rRef1, rRef2, lcl_GetCol));
267  const bool bSameRows(lcl_checkRangeDimension(pDoc, rPos, rRef1, rRef2, lcl_GetRow));
268  const bool bSameTabs(lcl_checkRangeDimension(pDoc, rPos, rRef1, rRef2, lcl_GetTab));
269 
270  // Test if exactly two dimensions are equal
271  if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2)
272  {
273  bCol = !bSameCols;
274  bRow = !bSameRows;
275  bTab = !bSameTabs;
276  return true;
277  }
278  return false;
279 }
280 
284 bool
285 lcl_checkRangeDimensions(
286  const ScDocument* pDoc, const ScAddress& rPos,
287  const std::vector<formula::FormulaToken*>::const_iterator& rBegin,
288  const std::vector<formula::FormulaToken*>::const_iterator& rEnd,
289  bool& bCol, bool& bRow, bool& bTab)
290 {
291  std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin);
292  ++aCur;
293  const SingleDoubleRefProvider aRef(**rBegin);
294  bool bOk(false);
295  {
296  const SingleDoubleRefProvider aRefCur(**aCur);
297  bOk = lcl_checkRangeDimensions(pDoc, rPos, aRef, aRefCur, bCol, bRow, bTab);
298  }
299  while (bOk && aCur != rEnd)
300  {
301  const SingleDoubleRefProvider aRefCur(**aCur);
302  bool bColTmp(false);
303  bool bRowTmp(false);
304  bool bTabTmp(false);
305  bOk = lcl_checkRangeDimensions(pDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp);
306  bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp);
307  ++aCur;
308  }
309 
310  return bOk && aCur == rEnd;
311 }
312 
313 class LessByReference
314 {
315  const ScDocument* mpDoc;
316  ScAddress maPos;
317  DimensionSelector maFunc;
318 public:
319  LessByReference(const ScDocument* pDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
320  mpDoc(pDoc), maPos(rPos), maFunc(rFunc) {}
321 
322  bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2)
323  {
324  const SingleDoubleRefProvider aRef1(*pRef1);
325  const SingleDoubleRefProvider aRef2(*pRef2);
326  return maFunc(mpDoc, maPos, aRef1.Ref1) < maFunc(mpDoc, maPos, aRef2.Ref1);
327  }
328 };
329 
335 class AdjacentByReference
336 {
337  const ScDocument* mpDoc;
338  ScAddress maPos;
339  DimensionSelector maFunc;
340 public:
341  AdjacentByReference(const ScDocument* pDoc, const ScAddress& rPos, DimensionSelector aFunc) :
342  mpDoc(pDoc), maPos(rPos), maFunc(aFunc) {}
343 
344  bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2)
345  {
346  const SingleDoubleRefProvider aRef1(*p1);
347  const SingleDoubleRefProvider aRef2(*p2);
348  return maFunc(mpDoc, maPos, aRef2.Ref1) - maFunc(mpDoc, maPos, aRef1.Ref2) == 1;
349  }
350 };
351 
352 bool
353 lcl_checkIfAdjacent(
354  const ScDocument* pDoc,
355  const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich)
356 {
357  auto aBegin(rReferences.cbegin());
358  auto aEnd(rReferences.cend());
359  auto aBegin1(aBegin);
360  ++aBegin1;
361  --aEnd;
362  return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(pDoc, rPos, aWhich));
363 }
364 
365 void
366 lcl_fillRangeFromRefList(
367  const ScDocument* pDoc,
368  const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange)
369 {
370  const ScSingleRefData aStart(
371  SingleDoubleRefProvider(*rReferences.front()).Ref1);
372  rRange.aStart = aStart.toAbs(pDoc, aPos);
373  const ScSingleRefData aEnd(
374  SingleDoubleRefProvider(*rReferences.back()).Ref2);
375  rRange.aEnd = aEnd.toAbs(pDoc, aPos);
376 }
377 
378 bool
379 lcl_refListFormsOneRange(
380  const ScDocument* pDoc,
381  const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
382  ScRange& rRange)
383 {
384  if (rReferences.size() == 1)
385  {
386  lcl_fillRangeFromRefList(pDoc, rPos, rReferences, rRange);
387  return true;
388  }
389 
390  bool bCell(false);
391  bool bRow(false);
392  bool bTab(false);
393  if (lcl_checkRangeDimensions(pDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab))
394  {
395  DimensionSelector aWhich;
396  if (bCell)
397  {
398  aWhich = lcl_GetCol;
399  }
400  else if (bRow)
401  {
402  aWhich = lcl_GetRow;
403  }
404  else if (bTab)
405  {
406  aWhich = lcl_GetTab;
407  }
408  else
409  {
410  OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!");
411  aWhich = lcl_GetRow; // initialize to avoid warning
412  }
413 
414  // Sort the references by start of range
415  std::sort(rReferences.begin(), rReferences.end(), LessByReference(pDoc, rPos, aWhich));
416  if (lcl_checkIfAdjacent(pDoc, rPos, rReferences, aWhich))
417  {
418  lcl_fillRangeFromRefList(pDoc, rPos, rReferences, rRange);
419  return true;
420  }
421  }
422  return false;
423 }
424 
425 bool lcl_isReference(const FormulaToken& rToken)
426 {
427  return
428  rToken.GetType() == svSingleRef ||
429  rToken.GetType() == svDoubleRef;
430 }
431 
432 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc,
433  const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal)
434 {
435  ScRangeData* pRangeData = nullptr;
436  SCTAB nSheet = pToken->GetSheet();
437  sal_uInt16 nIndex = pToken->GetIndex();
438  if (!pOldDoc->CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true))
439  return; // nothing to do
440 
441  if (!pRangeData)
442  {
443  // If this happened we have a real problem.
444  pToken->SetIndex(0);
445  OSL_FAIL("inserting the range name should not fail");
446  return;
447  }
448 
449  pToken->SetIndex(nIndex);
450  pToken->SetSheet(nSheet);
451 }
452 
453 void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldDoc)
454 {
455  ScDBCollection* pOldDBCollection = pOldDoc->GetDBCollection();
456  if (!pOldDBCollection)
457  return;//strange error case, don't do anything
458  ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs();
459  ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex());
460  if (!pDBData)
461  return; //invalid index
462  OUString aDBName = pDBData->GetUpperName();
463 
464  //search in new document
465  ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection();
466  if (!pNewDBCollection)
467  {
468  rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(&rNewDoc)));
469  pNewDBCollection = rNewDoc.GetDBCollection();
470  }
471  ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs();
472  ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName);
473  if (!pNewDBData)
474  {
475  pNewDBData = new ScDBData(*pDBData);
476  bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData));
477  assert(ins); (void)ins;
478  }
479  pToken->SetIndex(pNewDBData->GetIndex());
480 }
481 
482 struct AreaListenerKey
483 {
485  bool mbStartFixed;
486  bool mbEndFixed;
487 
488  AreaListenerKey( const ScRange& rRange, bool bStartFixed, bool bEndFixed ) :
489  maRange(rRange), mbStartFixed(bStartFixed), mbEndFixed(bEndFixed) {}
490 
491  bool operator < ( const AreaListenerKey& r ) const
492  {
493  if (maRange.aStart.Tab() != r.maRange.aStart.Tab())
494  return maRange.aStart.Tab() < r.maRange.aStart.Tab();
495  if (maRange.aStart.Col() != r.maRange.aStart.Col())
496  return maRange.aStart.Col() < r.maRange.aStart.Col();
497  if (maRange.aStart.Row() != r.maRange.aStart.Row())
498  return maRange.aStart.Row() < r.maRange.aStart.Row();
499  if (maRange.aEnd.Tab() != r.maRange.aEnd.Tab())
500  return maRange.aEnd.Tab() < r.maRange.aEnd.Tab();
501  if (maRange.aEnd.Col() != r.maRange.aEnd.Col())
502  return maRange.aEnd.Col() < r.maRange.aEnd.Col();
503  if (maRange.aEnd.Row() != r.maRange.aEnd.Row())
504  return maRange.aEnd.Row() < r.maRange.aEnd.Row();
505  if (mbStartFixed != r.mbStartFixed)
506  return r.mbStartFixed;
507  if (mbEndFixed != r.mbEndFixed)
508  return r.mbEndFixed;
509 
510  return false;
511  }
512 };
513 
514 typedef std::map<AreaListenerKey, std::unique_ptr<sc::FormulaGroupAreaListener>> AreaListenersType;
515 
516 }
517 
519 {
520  AreaListenersType m_AreaListeners;
521 };
522 
524  mpImpl(new Impl),
525  mnRefCount(0),
526  mpTopCell(nullptr),
527  mnLength(0),
528  mnWeight(0),
529  mnFormatType(SvNumFormatType::NUMBER),
530  mbInvariant(false),
531  mbSubTotal(false),
532  mbPartOfCycle(false),
533  meCalcState(sc::GroupCalcEnabled)
534 {
535 }
536 
538 {
539 }
540 
542 {
543  mpCode = rCode.Clone();
544  mbInvariant = mpCode->IsInvariant();
545  mpCode->GenHash();
546 }
547 
548 void ScFormulaCellGroup::setCode( std::unique_ptr<ScTokenArray> pCode )
549 {
550  mpCode = std::move(pCode); // takes ownership of the token array.
551  mpCode->Finalize(); // Reduce memory usage if needed.
552  mbInvariant = mpCode->IsInvariant();
553  mpCode->GenHash();
554 }
555 
557  ScDocument& rDoc, const ScAddress& rPos, FormulaGrammar::Grammar eGram )
558 {
559  if (!mpCode)
560  return;
561 
562  if (mpCode->GetLen() && mpCode->GetCodeError() == FormulaError::NONE && !mpCode->GetCodeLen())
563  {
564  bool bMatrixFormula = mpTopCell->GetMatrixFlag() != ScMatrixMode::NONE;
565  ScCompiler aComp(&rDoc, rPos, *mpCode, eGram, true, bMatrixFormula);
566  mbSubTotal = aComp.CompileTokenArray();
567  mnFormatType = aComp.GetNumFormatType();
568  }
569  else
570  {
571  mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate );
572  }
573 }
574 
576  ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed )
577 {
578  AreaListenerKey aKey(rRange, bStartFixed, bEndFixed);
579 
580  AreaListenersType::iterator it = mpImpl->m_AreaListeners.lower_bound(aKey);
581  if (it == mpImpl->m_AreaListeners.end() || mpImpl->m_AreaListeners.key_comp()(aKey, it->first))
582  {
583  // Insert a new one.
584  it = mpImpl->m_AreaListeners.insert(
585  it, std::make_pair(aKey, std::make_unique<sc::FormulaGroupAreaListener>(
586  rRange, *(*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed)));
587  }
588 
589  return it->second.get();
590 }
591 
593 {
594  for (const auto& rEntry : mpImpl->m_AreaListeners)
595  {
596  sc::FormulaGroupAreaListener *const pListener = rEntry.second.get();
597  ScRange aListenRange = pListener->getListeningRange();
598  // This "always listen" special range is never grouped.
599  bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS);
600  rDoc.EndListeningArea(aListenRange, bGroupListening, pListener);
601  }
602 
603  mpImpl->m_AreaListeners.clear();
604 }
605 
606 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos ) :
607  bDirty(false),
608  bTableOpDirty(false),
609  bChanged(false),
610  bRunning(false),
611  bCompile(false),
612  bSubTotal(false),
613  bIsIterCell(false),
614  bInChangeTrack(false),
615  bNeedListening(false),
616  mbNeedsNumberFormat(false),
617  mbAllowNumberFormatChange(false),
618  mbPostponedDirty(false),
619  mbIsExtRef(false),
620  mbSeenInPath(false),
621  cMatrixFlag(ScMatrixMode::NONE),
622  nSeenInIteration(0),
623  nFormatType(SvNumFormatType::NUMBER),
624  eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT),
625  pCode(new ScTokenArray(pDoc)),
626  pDocument(pDoc),
627  pPrevious(nullptr),
628  pNext(nullptr),
629  pPreviousTrack(nullptr),
630  pNextTrack(nullptr),
631  aPos(rPos)
632 {
633 }
634 
635 ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos,
636  const OUString& rFormula,
637  const FormulaGrammar::Grammar eGrammar,
638  ScMatrixMode cMatInd ) :
639  bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0
640  bTableOpDirty( false ),
641  bChanged( false ),
642  bRunning( false ),
643  bCompile( false ),
644  bSubTotal( false ),
645  bIsIterCell( false ),
646  bInChangeTrack( false ),
647  bNeedListening( false ),
648  mbNeedsNumberFormat( false ),
649  mbAllowNumberFormatChange(false),
650  mbPostponedDirty(false),
651  mbIsExtRef(false),
652  mbSeenInPath(false),
653  cMatrixFlag ( cMatInd ),
654  nSeenInIteration(0),
655  nFormatType ( SvNumFormatType::NUMBER ),
656  eTempGrammar( eGrammar),
657  pCode( nullptr ),
658  pDocument( pDoc ),
659  pPrevious(nullptr),
660  pNext(nullptr),
661  pPreviousTrack(nullptr),
662  pNextTrack(nullptr),
663  aPos(rPos)
664 {
665  Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that
666  if (!pCode)
667  // We need to have a non-NULL token array instance at all times.
668  pCode = new ScTokenArray(pDoc);
669 }
670 
672  ScDocument* pDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray,
673  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
674  bDirty( true ),
675  bTableOpDirty( false ),
676  bChanged( false ),
677  bRunning( false ),
678  bCompile( false ),
679  bSubTotal( false ),
680  bIsIterCell( false ),
681  bInChangeTrack( false ),
682  bNeedListening( false ),
683  mbNeedsNumberFormat( false ),
684  mbAllowNumberFormatChange(false),
685  mbPostponedDirty(false),
686  mbIsExtRef(false),
687  mbSeenInPath(false),
688  cMatrixFlag ( cMatInd ),
689  nSeenInIteration(0),
690  nFormatType ( SvNumFormatType::NUMBER ),
691  eTempGrammar( eGrammar),
692  pCode(pArray.release()),
693  pDocument( pDoc ),
694  pPrevious(nullptr),
695  pNext(nullptr),
696  pPreviousTrack(nullptr),
697  pNextTrack(nullptr),
698  aPos(rPos)
699 {
700  assert(pCode); // Never pass a NULL pointer here.
701 
702  pCode->Finalize(); // Reduce memory usage if needed.
703 
704  // Generate RPN token array.
705  if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen())
706  {
708  bSubTotal = aComp.CompileTokenArray();
709  nFormatType = aComp.GetNumFormatType();
710  }
711  else
712  {
714  bSubTotal = true;
715  }
716 
717  if (bSubTotal)
718  pDocument->AddSubTotalCell(this);
719 
720  pCode->GenHash();
721 }
722 
724  ScDocument* pDoc, const ScAddress& rPos, const ScTokenArray& rArray,
725  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) :
726  bDirty( true ),
727  bTableOpDirty( false ),
728  bChanged( false ),
729  bRunning( false ),
730  bCompile( false ),
731  bSubTotal( false ),
732  bIsIterCell( false ),
733  bInChangeTrack( false ),
734  bNeedListening( false ),
735  mbNeedsNumberFormat( false ),
736  mbAllowNumberFormatChange(false),
737  mbPostponedDirty(false),
738  mbIsExtRef(false),
739  mbSeenInPath(false),
740  cMatrixFlag ( cMatInd ),
741  nSeenInIteration(0),
742  nFormatType ( SvNumFormatType::NUMBER ),
743  eTempGrammar( eGrammar),
744  pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array
745  pDocument( pDoc ),
746  pPrevious(nullptr),
747  pNext(nullptr),
748  pPreviousTrack(nullptr),
749  pNextTrack(nullptr),
750  aPos(rPos)
751 {
752  // RPN array generation
753  if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() )
754  {
756  bSubTotal = aComp.CompileTokenArray();
757  nFormatType = aComp.GetNumFormatType();
758  }
759  else
760  {
762  bSubTotal = true;
763  }
764 
765  if (bSubTotal)
766  pDocument->AddSubTotalCell(this);
767 
768  pCode->GenHash();
769 }
770 
772  ScDocument* pDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup,
773  const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) :
774  mxGroup(xGroup),
775  bDirty(true),
776  bTableOpDirty( false ),
777  bChanged( false ),
778  bRunning( false ),
779  bCompile( false ),
780  bSubTotal(xGroup->mbSubTotal),
781  bIsIterCell( false ),
782  bInChangeTrack( false ),
783  bNeedListening( false ),
784  mbNeedsNumberFormat( false ),
785  mbAllowNumberFormatChange(false),
786  mbPostponedDirty(false),
787  mbIsExtRef(false),
788  mbSeenInPath(false),
789  cMatrixFlag ( cInd ),
790  nSeenInIteration(0),
791  nFormatType(xGroup->mnFormatType),
792  eTempGrammar( eGrammar),
793  pCode(xGroup->mpCode ? xGroup->mpCode.get() : new ScTokenArray(pDoc)),
794  pDocument( pDoc ),
795  pPrevious(nullptr),
796  pNext(nullptr),
797  pPreviousTrack(nullptr),
798  pNextTrack(nullptr),
799  aPos(rPos)
800 {
801  if (bSubTotal)
802  pDocument->AddSubTotalCell(this);
803 }
804 
805 ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) :
806  SvtListener(),
807  bDirty( rCell.bDirty ),
808  bTableOpDirty( false ),
809  bChanged( rCell.bChanged ),
810  bRunning( false ),
811  bCompile( rCell.bCompile ),
812  bSubTotal( rCell.bSubTotal ),
813  bIsIterCell( false ),
814  bInChangeTrack( false ),
815  bNeedListening( false ),
816  mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ),
817  mbAllowNumberFormatChange(false),
818  mbPostponedDirty(false),
819  mbIsExtRef(false),
820  mbSeenInPath(false),
821  cMatrixFlag ( rCell.cMatrixFlag ),
822  nSeenInIteration(0),
823  nFormatType( rCell.nFormatType ),
824  aResult( rCell.aResult ),
825  eTempGrammar( rCell.eTempGrammar),
826  pDocument( &rDoc ),
827  pPrevious(nullptr),
828  pNext(nullptr),
829  pPreviousTrack(nullptr),
830  pNextTrack(nullptr),
831  aPos(rPos)
832 {
833  pCode = rCell.pCode->Clone().release();
834 
835  // set back any errors and recompile
836  // not in the Clipboard - it must keep the received error flag
837  // Special Length=0: as bad cells are generated, then they are also retained
838  if ( pCode->GetCodeError() != FormulaError::NONE && !pDocument->IsClipboard() && pCode->GetLen() )
839  {
840  pCode->SetCodeError( FormulaError::NONE );
841  bCompile = true;
842  }
843  // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference !
844  bool bCompileLater = false;
845  bool bClipMode = rCell.pDocument->IsClipboard();
846 
847  //update ScNameTokens
848  if (!pDocument->IsClipOrUndo() || rDoc.IsUndo())
849  {
850  if (!pDocument->IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab())
851  {
852  bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default);
853  formula::FormulaToken* pToken = nullptr;
855  while((pToken = aIter.GetNextName())!= nullptr)
856  {
857  OpCode eOpCode = pToken->GetOpCode();
858  if (eOpCode == ocName)
859  adjustRangeName(pToken, rDoc, rCell.pDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
860  else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
861  adjustDBRange(pToken, rDoc, rCell.pDocument);
862  }
863  }
864 
865  bool bCopyBetweenDocs = pDocument->GetPool() != rCell.pDocument->GetPool();
866  if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
867  {
868  pCode->ReadjustAbsolute3DReferences( rCell.pDocument, &rDoc, rCell.aPos);
869  }
870 
871  pCode->AdjustAbsoluteRefs( rCell.pDocument, rCell.aPos, aPos, bCopyBetweenDocs );
872  }
873 
874  if (!pDocument->IsClipOrUndo())
875  {
879  }
880 
881  if( !bCompile )
882  { // Name references with references and ColRowNames
884  for (;;)
885  {
887  if (!t || bCompile)
888  break;
889  if ( t->IsExternalRef() )
890  {
891  // External name, cell, and area references.
892  bCompile = true;
893  }
894  else if ( t->GetType() == svIndex )
895  {
896  const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
897  if( pRangeData )
898  {
899  if( pRangeData->HasReferences() )
900  bCompile = true;
901  }
902  else
903  bCompile = true; // invalid reference!
904  }
905  else if ( t->GetOpCode() == ocColRowName )
906  {
907  bCompile = true; // new lookup needed
908  bCompileLater = bClipMode;
909  }
910  }
911  }
912  if( bCompile )
913  {
914  if ( !bCompileLater && bClipMode )
915  {
916  // Merging ranges needs the actual positions after UpdateReference.
917  // ColRowNames and TableRefs need new lookup after positions are
918  // adjusted.
919  bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) ||
921  }
922  if ( !bCompileLater )
923  {
924  // bNoListening, not at all if in Clipboard/Undo,
925  // and not from Clipboard either, instead after Insert(Clone) and UpdateReference.
926  CompileTokenArray( true );
927  }
928  }
929 
930  if( nCloneFlags & ScCloneFlags::StartListening )
931  StartListeningTo( &rDoc );
932 
933  if (bSubTotal)
934  pDocument->AddSubTotalCell(this);
935 }
936 
938 {
942  if (pCode->HasOpCode(ocMacro))
944 
947 
948  if (!mxGroup || !mxGroup->mpCode)
949  // Formula token is not shared.
950  delete pCode;
951 }
952 
954 {
955  return new ScFormulaCell(*this, *pDocument, aPos);
956 }
957 
958 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
959 {
960  return new ScFormulaCell(*this, *pDocument, rPos, ScCloneFlags::Default);
961 }
962 
964 {
965  return pCode->GetHash();
966 }
967 
969 {
970  return pCode->GetVectorState();
971 }
972 
973 void ScFormulaCell::GetFormula( OUStringBuffer& rBuffer,
974  const FormulaGrammar::Grammar eGrammar, const ScInterpreterContext* pContext ) const
975 {
976  if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() )
977  {
979  return;
980  }
982  {
983  // Reference to another cell that contains a matrix formula.
986  if( p )
987  {
988  /* FIXME: original GetFormula() code obtained
989  * pCell only if (!IsInChangeTrack()),
990  * GetEnglishFormula() omitted that test.
991  * Can we live without in all cases? */
992  ScFormulaCell* pCell = nullptr;
993  ScSingleRefData& rRef = *p->GetSingleRef();
994  ScAddress aAbs = rRef.toAbs(pDocument, aPos);
995  if (pDocument->ValidAddress(aAbs))
996  pCell = pDocument->GetFormulaCell(aAbs);
997 
998  if (pCell)
999  {
1000  pCell->GetFormula( rBuffer, eGrammar, pContext );
1001  return;
1002  }
1003  else
1004  {
1005  ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
1006  aComp.CreateStringFromTokenArray( rBuffer );
1007  }
1008  }
1009  else
1010  {
1011  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1012  }
1013  }
1014  else
1015  {
1016  ScCompiler aComp( pDocument, aPos, *pCode, eGrammar, false, false, pContext );
1017  aComp.CreateStringFromTokenArray( rBuffer );
1018  }
1019 
1020  rBuffer.insert( 0, '=');
1022  {
1023  rBuffer.insert( 0, '{');
1024  rBuffer.append( '}');
1025  }
1026 }
1027 
1028 void ScFormulaCell::GetFormula( OUString& rFormula, const FormulaGrammar::Grammar eGrammar,
1029  const ScInterpreterContext* pContext ) const
1030 {
1031  OUStringBuffer rBuffer( rFormula );
1032  GetFormula( rBuffer, eGrammar, pContext );
1033  rFormula = rBuffer.makeStringAndClear();
1034 }
1035 
1037 {
1038  OUStringBuffer aBuf;
1039  if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen())
1040  {
1041  ScTokenArray aCode(rCxt.getDoc());
1043  ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext);
1044  aComp.CreateStringFromTokenArray(aBuf);
1045  return aBuf.makeStringAndClear();
1046  }
1047  else if( cMatrixFlag == ScMatrixMode::Reference )
1048  {
1049  // Reference to another cell that contains a matrix formula.
1052  if( p )
1053  {
1054  /* FIXME: original GetFormula() code obtained
1055  * pCell only if (!IsInChangeTrack()),
1056  * GetEnglishFormula() omitted that test.
1057  * Can we live without in all cases? */
1058  ScFormulaCell* pCell = nullptr;
1059  ScSingleRefData& rRef = *p->GetSingleRef();
1060  ScAddress aAbs = rRef.toAbs(pDocument, aPos);
1061  if (pDocument->ValidAddress(aAbs))
1062  pCell = pDocument->GetFormulaCell(aAbs);
1063 
1064  if (pCell)
1065  {
1066  return pCell->GetFormula(rCxt);
1067  }
1068  else
1069  {
1070  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1071  aComp.CreateStringFromTokenArray(aBuf);
1072  }
1073  }
1074  else
1075  {
1076  OSL_FAIL("ScFormulaCell::GetFormula: not a matrix");
1077  }
1078  }
1079  else
1080  {
1081  ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext);
1082  aComp.CreateStringFromTokenArray(aBuf);
1083  }
1084 
1085  aBuf.insert( 0, '=');
1087  {
1088  aBuf.insert( 0, '{');
1089  aBuf.append( '}');
1090  }
1091 
1092  return aBuf.makeStringAndClear();
1093 }
1094 
1096 {
1097  MaybeInterpret();
1098 
1099  if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell)
1100  {
1101  const ScMatrix* pMat = aResult.GetToken()->GetMatrix();
1102  if (pMat)
1103  {
1104  pMat->GetDimensions( rCols, rRows );
1105  return;
1106  }
1107  }
1108  rCols = 0;
1109  rRows = 0;
1110 }
1111 
1114 
1116 {
1117  mbPostponedDirty = bVar;
1118 }
1119 
1121 {
1123 }
1124 
1125 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening,
1126  const FormulaGrammar::Grammar eGrammar )
1127 {
1128  if ( pDocument->IsClipOrUndo() )
1129  return;
1130  bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1131  if ( bWasInFormulaTree )
1133  // pCode may not deleted for queries, but must be empty
1134  if ( pCode )
1135  pCode->Clear();
1136  ScTokenArray* pCodeOld = pCode;
1137  ScCompiler aComp( pDocument, aPos, eGrammar);
1138  pCode = aComp.CompileString( rFormula ).release();
1139  assert(!mxGroup);
1140  delete pCodeOld;
1141  if( pCode->GetCodeError() == FormulaError::NONE )
1142  {
1143  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1144  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1145  if ( rFormula[0] == '=' )
1146  pCode->AddBad( rFormula.copy(1) );
1147  else
1148  pCode->AddBad( rFormula );
1149  }
1150  bCompile = true;
1151  CompileTokenArray( bNoListening );
1152  }
1153  else
1154  bChanged = true;
1155 
1156  if ( bWasInFormulaTree )
1157  pDocument->PutInFormulaTree( this );
1158 }
1159 
1161  sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1162 {
1163  if ( pDocument->IsClipOrUndo() )
1164  return;
1165  bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1166  if ( bWasInFormulaTree )
1168  // pCode may not deleted for queries, but must be empty
1169  if ( pCode )
1170  pCode->Clear();
1171  ScTokenArray* pCodeOld = pCode;
1172  ScCompiler aComp(rCxt, aPos);
1173  pCode = aComp.CompileString( rFormula ).release();
1174  assert(!mxGroup);
1175  delete pCodeOld;
1176  if( pCode->GetCodeError() == FormulaError::NONE )
1177  {
1178  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() )
1179  { // not recursive CompileTokenArray/Compile/CompileTokenArray
1180  if ( rFormula[0] == '=' )
1181  pCode->AddBad( rFormula.copy(1) );
1182  else
1183  pCode->AddBad( rFormula );
1184  }
1185  bCompile = true;
1186  CompileTokenArray(rCxt, bNoListening);
1187  }
1188  else
1189  bChanged = true;
1190 
1191  if ( bWasInFormulaTree )
1192  pDocument->PutInFormulaTree( this );
1193 }
1194 
1195 void ScFormulaCell::CompileTokenArray( bool bNoListening )
1196 {
1197  // Not already compiled?
1198  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1199  {
1200  Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
1201  }
1202  else if( bCompile && !pDocument->IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1203  {
1204  // RPN length may get changed
1205  bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1206  if ( bWasInFormulaTree )
1208 
1209  // Loading from within filter? No listening yet!
1211  bNoListening = true;
1212 
1213  if( !bNoListening && pCode->GetCodeLen() )
1216  bSubTotal = aComp.CompileTokenArray();
1217  if( pCode->GetCodeError() == FormulaError::NONE )
1218  {
1219  nFormatType = aComp.GetNumFormatType();
1220  bChanged = true;
1221  aResult.SetToken( nullptr);
1222  bCompile = false;
1223  if ( !bNoListening )
1225  }
1226  if ( bWasInFormulaTree )
1227  pDocument->PutInFormulaTree( this );
1228 
1229  if (bSubTotal)
1230  pDocument->AddSubTotalCell(this);
1231  }
1232 }
1233 
1235 {
1236  // Not already compiled?
1237  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1238  {
1239  rCxt.setGrammar(eTempGrammar);
1240  Compile(rCxt, aResult.GetHybridFormula(), bNoListening);
1241  }
1242  else if( bCompile && !pDocument->IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1243  {
1244  // RPN length may get changed
1245  bool bWasInFormulaTree = pDocument->IsInFormulaTree( this );
1246  if ( bWasInFormulaTree )
1248 
1249  // Loading from within filter? No listening yet!
1251  bNoListening = true;
1252 
1253  if( !bNoListening && pCode->GetCodeLen() )
1255  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1256  bSubTotal = aComp.CompileTokenArray();
1257  if( pCode->GetCodeError() == FormulaError::NONE )
1258  {
1259  nFormatType = aComp.GetNumFormatType();
1260  bChanged = true;
1261  aResult.SetToken( nullptr);
1262  bCompile = false;
1263  if ( !bNoListening )
1265  }
1266  if ( bWasInFormulaTree )
1267  pDocument->PutInFormulaTree( this );
1268 
1269  if (bSubTotal)
1270  pDocument->AddSubTotalCell(this);
1271  }
1272 }
1273 
1275 {
1277  { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula
1278  // just establish listeners
1280  return ;
1281  }
1282 
1283  // Error constant formula cell stays as is.
1284  if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE)
1285  return;
1286 
1287  // Compilation changes RPN count, remove and reinsert to FormulaTree if it
1288  // was in to update its count.
1289  bool bWasInFormulaTree = pDocument->IsInFormulaTree( this);
1290  if (bWasInFormulaTree)
1292  rCxt.setGrammar(eTempGrammar);
1293  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1294  OUString aFormula, aFormulaNmsp;
1295  aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp );
1296  pDocument->DecXMLImportedFormulaCount( aFormula.getLength() );
1298  // pCode may not deleted for queries, but must be empty
1299  pCode->Clear();
1300 
1301  bool bDoCompile = true;
1302 
1303  if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization
1304  {
1305  ScAddress aPreviousCell( aPos );
1306  aPreviousCell.IncRow( -1 );
1307  ScFormulaCell *pPreviousCell = pDocument->GetFormulaCell( aPreviousCell );
1308  if (pPreviousCell && pPreviousCell->GetCode()->IsShareable())
1309  {
1310  // Build formula string using the tokens from the previous cell,
1311  // but use the current cell position.
1312  ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) );
1313  OUStringBuffer aShouldBeBuf;
1314  aBackComp.CreateStringFromTokenArray( aShouldBeBuf );
1315 
1316  // The initial '=' is optional in ODFF.
1317  const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0;
1318  OUString aShouldBe = aShouldBeBuf.makeStringAndClear();
1319  if (aFormula.getLength() == aShouldBe.getLength() + nLeadingEqual &&
1320  aFormula.match( aShouldBe, nLeadingEqual))
1321  {
1322  // Put them in the same formula group.
1323  ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup();
1324  if (!xGroup) // Last cell is not grouped yet. Start a new group.
1325  xGroup = pPreviousCell->CreateCellGroup(1, false);
1326  ++xGroup->mnLength;
1327  SetCellGroup( xGroup );
1328 
1329  // Do setup here based on previous cell.
1330 
1331  nFormatType = pPreviousCell->nFormatType;
1332  bSubTotal = pPreviousCell->bSubTotal;
1333  bChanged = true;
1334  bCompile = false;
1335 
1336  if (bSubTotal)
1337  pDocument->AddSubTotalCell(this);
1338 
1339  bDoCompile = false;
1340  pCode = pPreviousCell->pCode;
1341  if (pPreviousCell->mbIsExtRef)
1342  pDocument->GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this );
1343  }
1344  }
1345  }
1346 
1347  if (bDoCompile)
1348  {
1349  ScTokenArray* pCodeOld = pCode;
1350  pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release();
1351  assert(!mxGroup);
1352  delete pCodeOld;
1353 
1354  if( pCode->GetCodeError() == FormulaError::NONE )
1355  {
1356  if ( !pCode->GetLen() )
1357  {
1358  if ( !aFormula.isEmpty() && aFormula[0] == '=' )
1359  pCode->AddBad( aFormula.copy( 1 ) );
1360  else
1361  pCode->AddBad( aFormula );
1362  }
1363  bSubTotal = aComp.CompileTokenArray();
1364  if( pCode->GetCodeError() == FormulaError::NONE )
1365  {
1366  nFormatType = aComp.GetNumFormatType();
1367  bChanged = true;
1368  bCompile = false;
1369  }
1370 
1371  if (bSubTotal)
1372  pDocument->AddSubTotalCell(this);
1373  }
1374  else
1375  bChanged = true;
1376  }
1377 
1378  // After loading, it must be known if ocDde/ocWebservice is in any formula
1379  // (for external links warning, CompileXML is called at the end of loading XML file)
1381 
1382  //volatile cells must be added here for import
1384  {
1385  // During load, only those cells that are marked explicitly dirty get
1386  // recalculated. So we need to set it dirty here.
1387  SetDirtyVar();
1389  // Do not call TrackFormulas() here, not all listeners may have been
1390  // established, postponed until ScDocument::CompileXML() finishes.
1391  }
1392  else if (bWasInFormulaTree)
1393  pDocument->PutInFormulaTree(this);
1394 }
1395 
1396 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
1397 {
1398  bool bNewCompiled = false;
1399  // If a Calc 1.0-doc is read, we have a result, but no token array
1400  if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1401  {
1402  rCxt.setGrammar(eTempGrammar);
1403  Compile(rCxt, aResult.GetHybridFormula(), true);
1404  aResult.SetToken( nullptr);
1405  bDirty = true;
1406  bNewCompiled = true;
1407  }
1408  // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now.
1409  if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1410  {
1411  ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE);
1412  bSubTotal = aComp.CompileTokenArray();
1413  nFormatType = aComp.GetNumFormatType();
1414  bDirty = true;
1415  bCompile = false;
1416  bNewCompiled = true;
1417 
1418  if (bSubTotal)
1419  pDocument->AddSubTotalCell(this);
1420  }
1421 
1422  // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in
1423  // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #).
1424  // We iron this out here for all systems, such that we also have an Err503 here.
1425  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
1426  {
1427  OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?");
1428  aResult.SetResultError( FormulaError::IllegalFPOperation );
1429  bDirty = true;
1430  }
1431 
1432  // DoubleRefs for binary operators were always a Matrix before version v5.0.
1433  // Now this is only the case when in an array formula, otherwise it's an implicit intersection
1436  {
1438  SetMatColsRows( 1, 1);
1439  }
1440 
1441  // Do the cells need to be calculated? After Load cells can contain an error code, and then start
1442  // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL
1443  if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE )
1444  {
1445  if (bStartListening)
1447 
1448  if( !pCode->IsRecalcModeNormal() )
1449  bDirty = true;
1450  }
1451  if ( pCode->IsRecalcModeAlways() )
1452  { // random(), today(), now() always stay in the FormulaTree, so that they are calculated
1453  // for each F9
1454  bDirty = true;
1455  }
1456  // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad)
1457 }
1458 
1460 {
1461  return pCode && pDocument->MarkUsedExternalReferences(*pCode, aPos);
1462 }
1463 
1464 namespace {
1465 class RecursionCounter
1466 {
1467  ScRecursionHelper& rRec;
1468  bool bStackedInIteration;
1469 #if defined DBG_UTIL && !defined NDEBUG
1470  const ScFormulaCell* cell;
1471 #endif
1472 public:
1473  RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p )
1474  : rRec(r)
1475 #if defined DBG_UTIL && !defined NDEBUG
1476  , cell(p)
1477 #endif
1478  {
1479  bStackedInIteration = rRec.IsDoingIteration();
1480  if (bStackedInIteration)
1481  rRec.GetRecursionInIterationStack().push( p);
1482  rRec.IncRecursionCount();
1483  }
1484  ~RecursionCounter()
1485  {
1486  rRec.DecRecursionCount();
1487  if (bStackedInIteration)
1488  {
1489 #if defined DBG_UTIL && !defined NDEBUG
1490  assert(rRec.GetRecursionInIterationStack().top() == cell);
1491 #endif
1492  rRec.GetRecursionInIterationStack().pop();
1493  }
1494  }
1495 };
1496 
1497 // Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group".
1498 // Remove the group again at the end, since there are some places throughout the code
1499 // that do not handle well groups with just 1 cell. Remove the groups only when the recursion level
1500 // reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing
1501 // a group immediately would remove the info), for this reason affected cells are stored in the recursion
1502 // helper.
1503 struct TemporaryCellGroupMaker
1504 {
1505  TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable )
1506  : mCell( cell )
1507  , mEnabled( enable )
1508  {
1509  if( mEnabled && mCell->GetCellGroup() == nullptr )
1510  {
1511  mCell->CreateCellGroup( 1, false );
1512  mCell->GetDocument()->GetRecursionHelper().AddTemporaryGroupCell( mCell );
1513  }
1514  }
1515  ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE
1516  {
1517  if( mEnabled )
1518  mCell->GetDocument()->GetRecursionHelper().CleanTemporaryGroupCells();
1519  }
1520  ScFormulaCell* mCell;
1521  const bool mEnabled;
1522 };
1523 
1524 } // namespace
1525 
1526 bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset)
1527 {
1528  ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
1529  bool bGroupInterpreted = false;
1530 
1531  // The result would possibly depend on a cell without a valid value, bail out
1532  // the entire dependency computation.
1533  if (rRecursionHelper.IsAbortingDependencyComputation())
1534  return false;
1535 
1536  if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent())
1537  return bGroupInterpreted;
1538 
1540  TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore );
1541 
1542  ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this;
1543 
1544  if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() &&
1545  rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell))
1546  {
1547  // This call arose from a dependency calculation and we just found a cycle.
1548  // This will mark all elements in the cycle as parts-of-cycle.
1549  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell);
1550  // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet.
1551  // If there is a genuine circular reference, it will be marked so when all groups
1552  // in the cycle get out of dependency evaluation mode.
1553  // But returning without calculation a new value means other cells depending
1554  // on this one would use a possibly invalid value, so ensure the dependency
1555  // computation is aborted without resetting the dirty flag of any cell.
1556  rRecursionHelper.AbortDependencyComputation();
1557  return bGroupInterpreted;
1558  }
1559 
1560 #if DEBUG_CALCULATION
1561  static bool bDebugCalculationInit = true;
1562  if (bDebugCalculationInit)
1563  {
1564  aDC.maTrigger = aDebugCalculationTriggerAddress;
1565  aDC.mbPrintResults = true;
1566  bDebugCalculationInit = false;
1567  }
1568  DebugCalculationStacker aDebugEntry( aPos, pDocument);
1569 #endif
1570 
1571  if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn())
1572  return bGroupInterpreted; // no double/triple processing
1573 
1574  //FIXME:
1575  // If the call originates from a Reschedule in DdeLink update, leave dirty
1576  // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously!
1577  if ( pDocument->IsInDdeLinkUpdate() )
1578  return bGroupInterpreted;
1579 
1580  if (bRunning)
1581  {
1582  if (!pDocument->GetDocOptions().IsIter())
1583  {
1584  aResult.SetResultError( FormulaError::CircularReference );
1585  return bGroupInterpreted;
1586  }
1587 
1588  if (aResult.GetResultError() == FormulaError::CircularReference)
1589  aResult.SetResultError( FormulaError::NONE );
1590 
1591  // Start or add to iteration list.
1592  if (!rRecursionHelper.IsDoingIteration() ||
1593  !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell)
1594  rRecursionHelper.SetInIterationReturn( true);
1595 
1596  return bGroupInterpreted;
1597  }
1598  // no multiple interprets for GetErrCode, IsValue, GetValue and
1599  // different entry point recursions. Would also lead to premature
1600  // convergence in iterations.
1601  if (rRecursionHelper.GetIteration() && nSeenInIteration ==
1602  rRecursionHelper.GetIteration())
1603  return bGroupInterpreted;
1604 
1605  bool bOldRunning = bRunning;
1606  if (rRecursionHelper.GetRecursionCount() > MAXRECURSION)
1607  {
1608  bRunning = true;
1609  rRecursionHelper.SetInRecursionReturn( true);
1610  }
1611  else
1612  {
1614 
1615 #if DEBUG_CALCULATION
1616  aDC.enterGroup();
1617 #endif
1618  bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle;
1619  bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset);
1620  bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle;
1621 
1622 #if DEBUG_CALCULATION
1623  aDC.leaveGroup();
1624 #endif
1625  if (!bGroupInterpreted)
1626  {
1627  // This call resulted from a dependency calculation for a multigroup-threading attempt,
1628  // but found dependency among the groups.
1629  if (!rRecursionHelper.AreGroupsIndependent())
1630  {
1632  return bGroupInterpreted;
1633  }
1634  // Dependency calc inside InterpretFormulaGroup() failed due to
1635  // detection of a cycle and there are parent FG's in the cycle.
1636  // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG
1637  if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle())
1638  {
1640  return bGroupInterpreted;
1641  }
1642 
1643  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
1645  InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1646  }
1647 
1649  }
1650 
1651  // While leaving a recursion or iteration stack, insert its cells to the
1652  // recursion list in reverse order.
1653  if (rRecursionHelper.IsInReturn())
1654  {
1655  if (rRecursionHelper.GetRecursionCount() > 0 ||
1656  !rRecursionHelper.IsDoingRecursion())
1657  rRecursionHelper.Insert( this, bOldRunning, aResult);
1658  bool bIterationFromRecursion = false;
1659  bool bResumeIteration = false;
1660  do
1661  {
1662  if ((rRecursionHelper.IsInIterationReturn() &&
1663  rRecursionHelper.GetRecursionCount() == 0 &&
1664  !rRecursionHelper.IsDoingIteration()) ||
1665  bIterationFromRecursion || bResumeIteration)
1666  {
1667  bool & rDone = rRecursionHelper.GetConvergingReference();
1668  rDone = false;
1669  if (!bIterationFromRecursion && bResumeIteration)
1670  {
1671  bResumeIteration = false;
1672  // Resuming iteration expands the range.
1673  ScFormulaRecursionList::const_iterator aOldStart(
1674  rRecursionHelper.GetLastIterationStart());
1675  rRecursionHelper.ResumeIteration();
1676  // Mark new cells being in iteration.
1677  for (ScFormulaRecursionList::const_iterator aIter(
1678  rRecursionHelper.GetIterationStart()); aIter !=
1679  aOldStart; ++aIter)
1680  {
1681  ScFormulaCell* pIterCell = (*aIter).pCell;
1682  pIterCell->bIsIterCell = true;
1683  }
1684  // Mark older cells dirty again, in case they converted
1685  // without accounting for all remaining cells in the circle
1686  // that weren't touched so far, e.g. conditional. Restore
1687  // backupped result.
1688  sal_uInt16 nIteration = rRecursionHelper.GetIteration();
1689  for (ScFormulaRecursionList::const_iterator aIter(
1690  aOldStart); aIter !=
1691  rRecursionHelper.GetIterationEnd(); ++aIter)
1692  {
1693  ScFormulaCell* pIterCell = (*aIter).pCell;
1694  if (pIterCell->nSeenInIteration == nIteration)
1695  {
1696  if (!pIterCell->bDirty || aIter == aOldStart)
1697  {
1698  pIterCell->aResult = (*aIter).aPreviousResult;
1699  }
1700  --pIterCell->nSeenInIteration;
1701  }
1702  pIterCell->bDirty = true;
1703  }
1704  }
1705  else
1706  {
1707  bResumeIteration = false;
1708  // Close circle once. If 'this' is self-referencing only
1709  // (e.g. counter or self-adder) then it is already
1710  // implicitly closed.
1711  /* TODO: does this even make sense anymore? The last cell
1712  * added above with rRecursionHelper.Insert() should always
1713  * be 'this', shouldn't it? */
1714  ScFormulaCell* pLastCell = nullptr;
1715  if (rRecursionHelper.GetList().size() > 1)
1716  {
1717  pLastCell = rRecursionHelper.GetList().back().pCell;
1718  if (pLastCell != this)
1719  {
1722  pLastCell->InterpretTail(
1723  *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1725  }
1726  }
1727  // Start at 1, init things.
1728  rRecursionHelper.StartIteration();
1729  // Mark all cells being in iteration. Reset results to
1730  // original values, formula cells have been interpreted
1731  // already, discard that step.
1732  for (ScFormulaRecursionList::const_iterator aIter(
1733  rRecursionHelper.GetIterationStart()); aIter !=
1734  rRecursionHelper.GetIterationEnd(); ++aIter)
1735  {
1736  ScFormulaCell* pIterCell = (*aIter).pCell;
1737  pIterCell->aResult = (*aIter).aPreviousResult;
1738  pIterCell->bIsIterCell = true;
1739  }
1740  }
1741  bIterationFromRecursion = false;
1742  sal_uInt16 nIterMax = pDocument->GetDocOptions().GetIterCount();
1743  for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1744  rRecursionHelper.IncIteration())
1745  {
1746  rDone = false;
1747  bool bFirst = true;
1748  for ( ScFormulaRecursionList::iterator aIter(
1749  rRecursionHelper.GetIterationStart()); aIter !=
1750  rRecursionHelper.GetIterationEnd() &&
1751  !rRecursionHelper.IsInReturn(); ++aIter)
1752  {
1753  ScFormulaCell* pIterCell = (*aIter).pCell;
1754  if (pIterCell->IsDirtyOrInTableOpDirty() &&
1755  rRecursionHelper.GetIteration() !=
1756  pIterCell->GetSeenInIteration())
1757  {
1758  (*aIter).aPreviousResult = pIterCell->aResult;
1761  pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
1763  }
1764  if (bFirst)
1765  {
1766  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1767  bFirst = false;
1768  }
1769  else if (rDone)
1770  {
1771  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1772  }
1773  }
1774  if (rRecursionHelper.IsInReturn())
1775  {
1776  bResumeIteration = true;
1777  break; // for
1778  // Don't increment iteration.
1779  }
1780  }
1781  if (!bResumeIteration)
1782  {
1783  if (rDone)
1784  {
1785  for (ScFormulaRecursionList::const_iterator aIter(
1786  rRecursionHelper.GetIterationStart());
1787  aIter != rRecursionHelper.GetIterationEnd();
1788  ++aIter)
1789  {
1790  ScFormulaCell* pIterCell = (*aIter).pCell;
1791  pIterCell->bIsIterCell = false;
1792  pIterCell->nSeenInIteration = 0;
1793  pIterCell->bRunning = (*aIter).bOldRunning;
1794  }
1795  }
1796  else
1797  {
1798  for (ScFormulaRecursionList::const_iterator aIter(
1799  rRecursionHelper.GetIterationStart());
1800  aIter != rRecursionHelper.GetIterationEnd();
1801  ++aIter)
1802  {
1803  ScFormulaCell* pIterCell = (*aIter).pCell;
1804  pIterCell->bIsIterCell = false;
1805  pIterCell->nSeenInIteration = 0;
1806  pIterCell->bRunning = (*aIter).bOldRunning;
1807  pIterCell->ResetDirty();
1808  // The difference to Excel is that Excel does not
1809  // produce an error for non-convergence thus a
1810  // delta of 0.001 still works to execute the
1811  // maximum number of iterations and display the
1812  // results no matter if the result anywhere reached
1813  // near delta, but also never indicates whether the
1814  // result actually makes sense in case of
1815  // non-counter context. Calc does check the delta
1816  // in every case. If we wanted to support what
1817  // Excel does then add another option "indicate
1818  // non-convergence error" (default on) and execute
1819  // the following block only if set.
1820 #if 1
1821  // If one cell didn't converge, all cells of this
1822  // circular dependency don't, no matter whether
1823  // single cells did.
1824  pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1825  pIterCell->bChanged = true;
1826 #endif
1827  }
1828  }
1829  // End this iteration and remove entries.
1830  rRecursionHelper.EndIteration();
1831  bResumeIteration = rRecursionHelper.IsDoingIteration();
1832  }
1833  }
1834  if (rRecursionHelper.IsInRecursionReturn() &&
1835  rRecursionHelper.GetRecursionCount() == 0 &&
1836  !rRecursionHelper.IsDoingRecursion())
1837  {
1838  bIterationFromRecursion = false;
1839  // Iterate over cells known so far, start with the last cell
1840  // encountered, inserting new cells if another recursion limit
1841  // is reached. Repeat until solved.
1842  rRecursionHelper.SetDoingRecursion( true);
1843  do
1844  {
1845  rRecursionHelper.SetInRecursionReturn( false);
1846  for (ScFormulaRecursionList::const_iterator aIter(
1847  rRecursionHelper.GetIterationStart());
1848  !rRecursionHelper.IsInReturn() && aIter !=
1849  rRecursionHelper.GetIterationEnd(); ++aIter)
1850  {
1851  ScFormulaCell* pCell = (*aIter).pCell;
1852  if (pCell->IsDirtyOrInTableOpDirty())
1853  {
1856  pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1858  if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1859  pCell->bRunning = (*aIter).bOldRunning;
1860  }
1861  }
1862  } while (rRecursionHelper.IsInRecursionReturn());
1863  rRecursionHelper.SetDoingRecursion( false);
1864  if (rRecursionHelper.IsInIterationReturn())
1865  {
1866  if (!bResumeIteration)
1867  bIterationFromRecursion = true;
1868  }
1869  else if (bResumeIteration ||
1870  rRecursionHelper.IsDoingIteration())
1871  rRecursionHelper.GetList().erase(
1872  rRecursionHelper.GetIterationStart(),
1873  rRecursionHelper.GetLastIterationStart());
1874  else
1875  rRecursionHelper.Clear();
1876  }
1877  } while (bIterationFromRecursion || bResumeIteration);
1878  }
1879 
1880 #if DEBUG_CALCULATION
1882  if (nErr != FormulaError::NONE)
1883  aDC.storeResultError( nErr);
1884  else if (aResult.IsValue())
1885  aDC.storeResult( aResult.GetDouble());
1886  else
1887  aDC.storeResult( aResult.GetString());
1888 #endif
1889 
1890  return bGroupInterpreted;
1891 }
1892 
1894 {
1895  RecursionCounter aRecursionCounter( pDocument->GetRecursionHelper(), this);
1896  // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1897  if(bIsIterCell)
1899  if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1900  {
1901  // #i11719# no RPN and no error and no token code but result string present
1902  // => interpretation of this cell during name-compilation and unknown names
1903  // => can't exchange underlying code array in CompileTokenArray() /
1904  // Compile() because interpreter's token iterator would crash or pCode
1905  // would be deleted twice if this cell was interpreted during
1906  // compilation.
1907  // This should only be a temporary condition and, since we set an
1908  // error, if ran into it again we'd bump into the dirty-clearing
1909  // condition further down.
1910  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1911  {
1912  pCode->SetCodeError( FormulaError::NoCode );
1913  // This is worth an assertion; if encountered in daily work
1914  // documents we might need another solution. Or just confirm correctness.
1915  return;
1916  }
1918  }
1919 
1920  if( pCode->GetCodeLen() && pDocument )
1921  {
1922  std::unique_ptr<ScInterpreter> pScopedInterpreter;
1923  ScInterpreter* pInterpreter;
1924  if (rContext.pInterpreter)
1925  {
1926  pInterpreter = rContext.pInterpreter;
1927  pInterpreter->Init(this, aPos, *pCode);
1928  }
1929  else
1930  {
1931  pScopedInterpreter.reset(new ScInterpreter( this, pDocument, rContext, aPos, *pCode ));
1932  pInterpreter = pScopedInterpreter.get();
1933  }
1934 
1935  FormulaError nOldErrCode = aResult.GetResultError();
1936  if ( nSeenInIteration == 0 )
1937  { // Only the first time
1938  // With bChanged=false, if a newly compiled cell has a result of
1939  // 0.0, no change is detected and the cell will not be repainted.
1940  // bChanged = false;
1941  aResult.SetResultError( FormulaError::NONE );
1942  }
1943 
1944  switch ( aResult.GetResultError() )
1945  {
1946  case FormulaError::CircularReference : // will be determined again if so
1947  aResult.SetResultError( FormulaError::NONE );
1948  break;
1949  default: break;
1950  }
1951 
1952  bool bOldRunning = bRunning;
1953  bRunning = true;
1954  pInterpreter->Interpret();
1956  {
1957  if (nSeenInIteration > 0)
1958  --nSeenInIteration; // retry when iteration is resumed
1959 
1960  if ( aResult.GetType() == formula::svUnknown )
1961  aResult.SetToken( pInterpreter->GetResultToken().get() );
1962 
1963  return;
1964  }
1965  bRunning = bOldRunning;
1966 
1967  // The result may be invalid or depend on another invalid result, just abort
1968  // without updating the cell value. Since the dirty flag will not be reset,
1969  // the proper value will be computed later.
1971  return;
1972 
1973  // #i102616# For single-sheet saving consider only content changes, not format type,
1974  // because format type isn't set on loading (might be changed later)
1975  bool bContentChanged = false;
1976 
1977  // Do not create a HyperLink() cell if the formula results in an error.
1978  if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1979  pCode->SetHyperLink(false);
1980 
1981  if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1982  {
1983  bChanged = true;
1984 
1985  if (pInterpreter->GetError() == FormulaError::RetryCircular)
1986  {
1987  // Array formula matrix calculation corner case. Keep dirty
1988  // state, do not remove from formula tree or anything else, but
1989  // store FormulaError::CircularReference in case this cell does not get
1990  // recalculated.
1991  aResult.SetResultError( FormulaError::CircularReference);
1992  return;
1993  }
1994 
1995  ResetDirty();
1996  }
1997 
1998  if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1999  {
2000  bool bIsValue = aResult.IsValue(); // the previous type
2001  // Did it converge?
2002  if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
2003  pInterpreter->GetNumResult() - aResult.GetDouble()) <=
2005  (!bIsValue && pInterpreter->GetResultType() == svString &&
2006  pInterpreter->GetStringResult() == aResult.GetString()))
2007  {
2008  // A convergence in the first iteration doesn't necessarily
2009  // mean that it's done, it may be as not all related cells
2010  // of a circle changed their values yet. If the set really
2011  // converges it will do so also during the next iteration. This
2012  // fixes situations like of #i44115#. If this wasn't wanted an
2013  // initial "uncalculated" value would be needed for all cells
2014  // of a circular dependency => graph needed before calculation.
2015  if (nSeenInIteration > 1 ||
2017  {
2018  ResetDirty();
2019  }
2020  }
2021  }
2022 
2023  // New error code?
2024  if( pInterpreter->GetError() != nOldErrCode )
2025  {
2026  bChanged = true;
2027  // bContentChanged only has to be set if the file content would be changed
2028  if ( aResult.GetCellResultType() != svUnknown )
2029  bContentChanged = true;
2030  }
2031 
2032  ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
2033 
2034  // For IF() and other jumps or changed formatted source data the result
2035  // format may change for different runs, e.g. =IF(B1,B1) with first
2036  // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
2037  // displayed as TRUE. Do not force a general format though if
2038  // mbNeedsNumberFormat is set (because there was a general format..).
2039  // Note that nFormatType may be out of sync here if a format was
2040  // applied or cleared after the last run, but obtaining the current
2041  // format always just to check would be expensive. There may be
2042  // cases where the format should be changed but is not. If that turns
2043  // out to be a real problem then obtain the current format type after
2044  // the initial check when needed.
2045  bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
2047 
2048  // We have some requirements additionally to IsCompatible().
2049  // * Do not apply a NumberFormat::LOGICAL if the result value is not
2050  // 1.0 or 0.0
2051  // * Do not override an already set numeric number format if the result
2052  // is of type NumberFormat::LOGICAL, it could be user applied.
2053  // On the other hand, for an empty jump path instead of FALSE an
2054  // unexpected for example 0% could be displayed. YMMV.
2055  // * Never override a non-standard number format that indicates user
2056  // applied.
2057  // * NumberFormat::TEXT does not force a change.
2058  if (bForceNumberFormat)
2059  {
2060  sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
2061  const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
2062  if (nRetType == SvNumFormatType::LOGICAL)
2063  {
2064  double fVal = aNewResult.GetDouble();
2065  if (fVal != 1.0 && fVal != 0.0)
2066  bForceNumberFormat = false;
2067  else
2068  {
2069  nOldFormatIndex = pDocument->GetNumberFormat( rContext, aPos);
2070  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2071  switch (nFormatType)
2072  {
2073  case SvNumFormatType::PERCENT:
2074  case SvNumFormatType::CURRENCY:
2075  case SvNumFormatType::SCIENTIFIC:
2076  case SvNumFormatType::FRACTION:
2077  bForceNumberFormat = false;
2078  break;
2079  case SvNumFormatType::NUMBER:
2080  if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2081  bForceNumberFormat = false;
2082  break;
2083  default: break;
2084  }
2085  }
2086  }
2087  else if (nRetType == SvNumFormatType::TEXT)
2088  {
2089  bForceNumberFormat = false;
2090  }
2091  if (bForceNumberFormat)
2092  {
2093  if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2094  {
2095  nOldFormatIndex = pDocument->GetNumberFormat( rContext, aPos);
2096  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2097  }
2098  if (nOldFormatIndex !=
2099  ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2100  bForceNumberFormat = false;
2101  }
2102  }
2103 
2104  if( mbNeedsNumberFormat || bForceNumberFormat )
2105  {
2106  bool bSetFormat = true;
2107  const SvNumFormatType nOldFormatType = nFormatType;
2108  nFormatType = pInterpreter->GetRetFormatType();
2109  sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2110 
2111  if (nFormatType == SvNumFormatType::TEXT)
2112  {
2113  // Don't set text format as hard format.
2114  bSetFormat = false;
2115  }
2116  else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2117  {
2118  // In a matrix range do not set an (inherited) logical format
2119  // as hard format if the value does not represent a strict TRUE
2120  // or FALSE value. But do set for a top left error value so
2121  // following matrix cells can inherit for non-error values.
2122  // This solves a problem with IF() expressions in array context
2123  // where incidentally the top left element results in logical
2124  // type but some others don't. It still doesn't solve the
2125  // reverse case though, where top left is not logical type but
2126  // some other elements should be. We'd need to transport type
2127  // or format information on arrays.
2128  StackVar eNewCellResultType = aNewResult.GetCellResultType();
2129  if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2130  {
2131  double fVal;
2132  if (eNewCellResultType != svDouble)
2133  {
2134  bSetFormat = false;
2135  nFormatType = nOldFormatType; // that? or number?
2136  }
2137  else
2138  {
2139  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  pDocument->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 && pDocument->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 && pDocument->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 = pDocument->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 && pDocument->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  pDocument->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 = pDocument->GetAttr(
2285  aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2286  if ( nValidation )
2287  {
2288  const ScValidationData* pData = pDocument->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.
2313  pDocument->PutInFormulaTree(this);
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() && pDocument) )
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, pDocument, pDocument->GetNonThreadedContext(), aPos, *pCode ));
2358  pInterpreter = pScopedInterpreter.get();
2359  }
2360 
2361  switch (pInterpreter->GetVolatileType())
2362  {
2364  // The formula contains a volatile macro.
2366  pDocument->PutInFormulaTree(this);
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 (pDocument->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 || !pDocument->IsInFormulaTree( this )
2508  || pCode->IsRecalcModeAlways())
2509  && !pDocument->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  pDocument->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 || !pDocument->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 (!pDocument->IsImportingXML())
2559  }
2560 
2561  pDocument->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? pDocument->AddCalculateTable( aPos.Tab() );
2576 }
2577 
2579 {
2580  bDirty = true;
2582  pDocument->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 || !pDocument->IsInFormulaTree( this ) )
2600  {
2601  if ( !bTableOpDirty )
2602  {
2604  bTableOpDirty = true;
2605  }
2607  pDocument->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  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 = pDocument->GetNumberFormat( aPos );
2701  SvNumberFormatter* pFormatter = pDocument->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( *pDocument, 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 ( pDocument->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* pDoc, 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(pDoc, aPos);
2851  if (pDoc->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* pDoc, 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( pDoc, 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 = pDocument->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 = pDocument->GetFormulaCell(aAdr);
2901  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2902  pCell->GetMatrixOrigin(pDocument, 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 = pDocument->GetFormulaCell(aAdr);
2916  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2917  pCell->GetMatrixOrigin(pDocument, 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(pDocument, aPos);
3035  r.aEnd = aProv.Ref2.toAbs(pDocument, 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(pDocument, 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* pUndoDoc, 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 (pUndoDoc->GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3246  return;
3247 
3248  ScFormulaCell* pFCell =
3249  new ScFormulaCell(
3250  pUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(pUndoDoc), eTempGrammar, cMatrixFlag);
3251 
3252  pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3253  pUndoDoc->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, *pDocument, *pCode, aOldPos, aPos, bValChanged);
3321 
3322  ScChangeTrack* pChangeTrack = pDocument->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(pDocument, 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, *pDocument, *pCode, aOldPos, aPos, bValChanged);
3452 
3453  ScChangeTrack* pChangeTrack = pDocument->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  && !(pDocument->IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
3468 
3469  if ( bNewListening )
3470  EndListeningTo(pDocument, 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(*pDocument, 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(*pDocument, false);
3562  SetDirty();
3563  }
3564 
3565  return false;
3566 }
3567 
3569  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3570 {
3571  if (pDocument->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  break;
3583  default:
3584  ;
3585  }
3586  return false;
3587  }
3588 
3589  switch (rCxt.meMode)
3590  {
3591  case URM_INSDEL:
3592  return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3593  case URM_MOVE:
3594  return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3595  case URM_COPY:
3596  return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3597  default:
3598  ;
3599  }
3600 
3601  return false;
3602 }
3603 
3605 {
3606  // Adjust tokens only when it's not grouped or grouped top cell.
3607  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3608  bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3609  if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3610  {
3611  if (bPosChanged)
3612  aPos.IncTab(rCxt.mnSheets);
3613 
3614  return;
3615  }
3616 
3618  ScAddress aOldPos = aPos;
3619  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3620  if (bPosChanged)
3621  aPos.IncTab(rCxt.mnSheets);
3622 
3623  if (!bAdjustCode)
3624  return;
3625 
3627  if (aRes.mbNameModified)
3628  // Re-compile after new sheet(s) have been inserted.
3629  bCompile = true;
3630 
3631  // no StartListeningTo because the new sheets have not been inserted yet.
3632 }
3633 
3635 {
3636  // Adjust tokens only when it's not grouped or grouped top cell.
3637  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3638  bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3639  if (pDocument->IsClipOrUndo() || !pCode->HasReferences())
3640  {
3641  if (bPosChanged)
3642  aPos.IncTab(-1*rCxt.mnSheets);
3643  return;
3644  }
3645 
3647  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3648  ScAddress aOldPos = aPos;
3649  if (bPosChanged)
3650  aPos.IncTab(-1*rCxt.mnSheets);
3651 
3652  if (!bAdjustCode)
3653  return;
3654 
3655  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3656  if (aRes.mbNameModified)
3657  // Re-compile after sheet(s) have been deleted.
3658  bCompile = true;
3659 }
3660 
3662 {
3663  // Adjust tokens only when it's not grouped or grouped top cell.
3664  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3665 
3666  if (!pCode->HasReferences() || pDocument->IsClipOrUndo())
3667  {
3668  aPos.SetTab(nTabNo);
3669  return;
3670  }
3671 
3673  ScAddress aOldPos = aPos;
3674  // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3675  aPos.SetTab(nTabNo);
3676 
3677  // no StartListeningTo because pTab[nTab] not yet correct!
3678 
3679  if (!bAdjustCode)
3680  return;
3681 
3682  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3683  if (aRes.mbNameModified)
3684  // Re-compile after sheet(s) have been deleted.
3685  bCompile = true;
3686 }
3687 
3689 {
3690  if (pDocument->IsClipOrUndo())
3691  return;
3692 
3693  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3694  if (!bAdjustCode)
3695  return;
3696 
3699  while (p)
3700  {
3701  ScSingleRefData& rRef1 = *p->GetSingleRef();
3702  if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3703  rRef1.IncTab(1);
3704  if (p->GetType() == formula::svDoubleRef)
3705  {
3706  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3707  if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3708  rRef2.IncTab(1);
3709  }
3710  p = aIter.GetNextReferenceRPN();
3711  }
3712 }
3713 
3715 {
3716  if (pDocument->IsClipOrUndo())
3717  return false;
3718 
3719  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3720  if (!bAdjustCode)
3721  return false;
3722 
3723  bool bRet = false;
3726  while (p)
3727  {
3728  ScSingleRefData& rRef1 = *p->GetSingleRef();
3729  if (!rRef1.IsTabRel())
3730  {
3731  if (nTable != rRef1.Tab())
3732  bRet = true;
3733  else if (nTable != aPos.Tab())
3734  rRef1.SetAbsTab(aPos.Tab());
3735  }
3736  if (p->GetType() == formula::svDoubleRef)
3737  {
3738  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3739  if (!rRef2.IsTabRel())
3740  {
3741  if(nTable != rRef2.Tab())
3742  bRet = true;
3743  else if (nTable != aPos.Tab())
3744  rRef2.SetAbsTab(aPos.Tab());
3745  }
3746  }
3747  p = aIter.GetNextReferenceRPN();
3748  }
3749  return bRet;
3750 }
3751 
3752 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3753 {
3754  if ( bForceIfNameInUse && !bCompile )
3756  if ( bCompile )
3757  pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3759 }
3760 
3761 // Reference transposition is only called in Clipboard Document
3763 {
3764  bool bFound = false;
3767  while ( ( t = aIter.GetNextReference() ) != nullptr )
3768  {
3769  ScSingleRefData& rRef1 = *t->GetSingleRef();
3770  if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3771  {
3772  bool bDouble = (t->GetType() == formula::svDoubleRef);
3773  ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3774  if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3775  {
3776  SCCOLROW nTemp;
3777 
3778  nTemp = rRef1.Col();
3779  rRef1.SetRelCol(rRef1.Row());
3780  rRef1.SetRelRow(nTemp);
3781 
3782  if ( bDouble )
3783  {
3784  nTemp = rRef2.Col();
3785  rRef2.SetRelCol(rRef2.Row());
3786  rRef2.SetRelRow(nTemp);
3787  }
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  ScRange aDestRange( rDest, ScAddress(
3807  static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3808  static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3809  rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3810  if ( aDestRange.In( aOldPos ) )
3811  {
3812  // Count back Positions
3813  SCCOL nRelPosX = aOldPos.Col();
3814  SCROW nRelPosY = aOldPos.Row();
3815  SCTAB nRelPosZ = aOldPos.Tab();
3816  ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart );
3817  aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3818  bPosChanged = true;
3819  }
3820 
3821  std::unique_ptr<ScTokenArray> pOld;
3822  if (pUndoDoc)
3823  pOld = pCode->Clone();
3824  bool bRefChanged = false;
3825 
3828  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3829  {
3830  if( t->GetOpCode() == ocName )
3831  {
3833  if (pName && pName->IsModified())
3834  bRefChanged = true;
3835  }
3836  else if( t->GetType() != svIndex )
3837  {
3838  SingleDoubleRefModifier aMod(*t);
3839  ScComplexRefData& rRef = aMod.Ref();
3840  ScRange aAbs = rRef.toAbs(pDocument, aOldPos);
3841  bool bMod = (ScRefUpdate::UpdateTranspose(pDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3842  if (bMod)
3843  {
3844  rRef.SetRange(pDocument->GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3845  bRefChanged = true;
3846  }
3847  }
3848  }
3849 
3850  if (bRefChanged)
3851  {
3852  if (pUndoDoc)
3853  {
3854  ScFormulaCell* pFCell = new ScFormulaCell(
3855  pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(pUndoDoc), eTempGrammar, cMatrixFlag);
3856 
3857  pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3858  pUndoDoc->SetFormulaCell(aPos, pFCell);
3859  }
3860 
3861  bCompile = true;
3862  CompileTokenArray(); // also call StartListeningTo
3863  SetDirty();
3864  }
3865  else
3866  StartListeningTo( pDocument ); // Listener as previous
3867 }
3868 
3869 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3870 {
3872 
3873  bool bRefChanged = false;
3874 
3877 
3878  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3879  {
3880  if( t->GetOpCode() == ocName )
3881  {
3883  if (pName && pName->IsModified())
3884  bRefChanged = true;
3885  }
3886  else if( t->GetType() != svIndex )
3887  {
3888  SingleDoubleRefModifier aMod(*t);
3889  ScComplexRefData& rRef = aMod.Ref();
3890  ScRange aAbs = rRef.toAbs(pDocument, aPos);
3891  bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3892  if (bMod)
3893  {
3894  rRef.SetRange(pDocument->GetSheetLimits(), aAbs, aPos);
3895  bRefChanged = true;
3896  }
3897  }
3898  }
3899 
3900  if (bRefChanged)
3901  {
3902  bCompile = true;
3903  CompileTokenArray(); // Also call StartListeningTo
3904  SetDirty();
3905  }
3906  else
3907  StartListeningTo( pDocument ); // Listener as previous
3908 }
3909 
3910 // See also ScDocument::FindRangeNamesReferencingSheet()
3911 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument* pDoc,
3912  int nRecursion)
3913 {
3914  FormulaTokenArrayPlainIterator aIter(*pCode);
3915  for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3916  {
3917  if (p->GetOpCode() == ocName)
3918  {
3919  sal_uInt16 nTokenIndex = p->GetIndex();
3920  SCTAB nTab = p->GetSheet();
3921  rIndexes.setUpdatedName( nTab, nTokenIndex);
3922 
3923  if (nRecursion < 126) // whatever... 42*3
3924  {
3925  ScRangeData* pSubName = pDoc->FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3926  if (pSubName)
3927  lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pDoc, nRecursion+1);
3928  }
3929  }
3930  }
3931 }
3932 
3934 {
3935  lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument, 0);
3936 }
3937 
3939 {
3940  bChanged = b;
3941 }
3942 
3943 void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3944 {
3945  assert(!mxGroup); // Don't call this if it's shared.
3946  delete pCode;
3947  pCode = pNew.release(); // takes ownership.
3948 }
3949 
3950 void ScFormulaCell::SetRunning( bool bVal )
3951 {
3952  bRunning = bVal;
3953 }
3954 
3956 {
3958  for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3959  {
3960  OpCode eOp = p->GetOpCode();
3961  if ( eOp == ocDBArea || eOp == ocTableRef )
3962  {
3963  bCompile = true;
3964  CompileTokenArray(rCxt);
3965  SetDirty();
3966  break;
3967  }
3968  }
3969 }
3970 
3972 {
3974  for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3975  {
3976  if ( p->GetOpCode() == ocColRowName )
3977  {
3978  bCompile = true;
3979  CompileTokenArray(rCxt);
3980  SetDirty();
3981  break;
3982  }
3983  }
3984 }
3985 
3990 
3992 {
3993  if (mxGroup)
3994  {
3995  // You can't create a new group if the cell is already a part of a group.
3996  // Is this a sign of some inconsistent or incorrect data structures? Or normal?
3997  SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
3998  return ScFormulaCellGroupRef();
3999  }
4000 
4001  mxGroup.reset(new ScFormulaCellGroup);
4002  mxGroup->mpTopCell = this;
4003  mxGroup->mbInvariant = bInvariant;
4004  mxGroup->mnLength = nLen;
4005  mxGroup->mpCode.reset(pCode); // Move this to the shared location.
4006  return mxGroup;
4007 }
4008 
4010 {
4011  if (!xRef)
4012  {
4013  // Make this cell a non-grouped cell.
4014  if (mxGroup)
4015  pCode = mxGroup->mpCode->Clone().release();
4016 
4017  mxGroup = xRef;
4018  return;
4019  }
4020 
4021  // Group object has shared token array.
4022  if (!mxGroup)
4023  // Currently not shared. Delete the existing token array first.
4024  delete pCode;
4025 
4026  mxGroup = xRef;
4027  pCode = mxGroup->mpCode.get();
4028  mxGroup->mnWeight = 0; // invalidate
4029 }
4030 
4032 {
4033  // no Matrix formulae yet.
4034  if ( GetMatrixFlag() != ScMatrixMode::NONE )
4035  return NotEqual;
4036 
4037  // are these formulas at all similar ?
4038  if ( GetHash() != rOther.GetHash() )
4039  return NotEqual;
4040 
4041  if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4042  return NotEqual;
4043 
4044  FormulaToken **pThis = pCode->GetCode();
4045  sal_uInt16 nThisLen = pCode->GetCodeLen();
4046  FormulaToken **pOther = rOther.pCode->GetCode();
4047  sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4048 
4049  if ( !pThis || !pOther )
4050  {
4051  // Error: no compiled code for cells !"
4052  return NotEqual;
4053  }
4054 
4055  if ( nThisLen != nOtherLen )
4056  return NotEqual;
4057 
4058  // No tokens can be an error cell so check error code, otherwise we could
4059  // end up with a series of equal error values instead of individual error
4060  // values. Also if for any reason different errors are set even if all
4061  // tokens are equal, the cells are not equal.
4062  if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4063  return NotEqual;
4064 
4065  bool bInvariant = true;
4066 
4067  // check we are basically the same function
4068  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4069  {
4070  formula::FormulaToken *pThisTok = pThis[i];
4071  formula::FormulaToken *pOtherTok = pOther[i];
4072 
4073  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4074  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4075  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4076  {
4077  // Incompatible type, op-code or param counts.
4078  return NotEqual;
4079  }
4080 
4081  switch (pThisTok->GetType())
4082  {
4083  case formula::svMatrix:
4086  // Ignoring matrix and external references for now.
4087  return NotEqual;
4088 
4089  case formula::svSingleRef:
4090  {
4091  // Single cell reference.
4092  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4093  if (rRef != *pOtherTok->GetSingleRef())
4094  return NotEqual;
4095 
4096  if (rRef.IsRowRel())
4097  bInvariant = false;
4098  }
4099  break;
4100  case formula::svDoubleRef:
4101  {
4102  // Range reference.
4103  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4104  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4105  if (rRef1 != *pOtherTok->GetSingleRef())
4106  return NotEqual;
4107 
4108  if (rRef2 != *pOtherTok->GetSingleRef2())
4109  return NotEqual;
4110 
4111  if (rRef1.IsRowRel())
4112  bInvariant = false;
4113 
4114  if (rRef2.IsRowRel())
4115  bInvariant = false;
4116  }
4117  break;
4118  case formula::svDouble:
4119  {
4120  if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4121  return NotEqual;
4122  }
4123  break;
4124  case formula::svString:
4125  {
4126  if(pThisTok->GetString() != pOtherTok->GetString())
4127  return NotEqual;
4128  }
4129  break;
4130  case formula::svIndex:
4131  {
4132  if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4133  return NotEqual;
4134  }
4135  break;
4136  case formula::svByte:
4137  {
4138  if(pThisTok->GetByte() != pOtherTok->GetByte())
4139  return NotEqual;
4140  }
4141  break;
4142  case formula::svExternal:
4143  {
4144  if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4145  return NotEqual;
4146 
4147  if (pThisTok->GetByte() != pOtherTok->GetByte())
4148  return NotEqual;
4149  }
4150  break;
4151  case formula::svError:
4152  {
4153  if (pThisTok->GetError() != pOtherTok->GetError())
4154  return NotEqual;
4155  }
4156  break;
4157  default:
4158  ;
4159  }
4160  }
4161 
4162  // If still the same, check lexical names as different names may result in
4163  // identical RPN code.
4164 
4165  pThis = pCode->GetArray();
4166  nThisLen = pCode->GetLen();
4167  pOther = rOther.pCode->GetArray();
4168  nOtherLen = rOther.pCode->GetLen();
4169 
4170  if ( !pThis || !pOther )
4171  {
4172  // Error: no code for cells !"
4173  return NotEqual;
4174  }
4175 
4176  if ( nThisLen != nOtherLen )
4177  return NotEqual;
4178 
4179  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4180  {
4181  formula::FormulaToken *pThisTok = pThis[i];
4182  formula::FormulaToken *pOtherTok = pOther[i];
4183 
4184  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4185  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4186  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4187  {
4188  // Incompatible type, op-code or param counts.
4189  return NotEqual;
4190  }
4191 
4192  switch (pThisTok->GetType())
4193  {
4194  // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4195  // resulting in identical RPN references that could lead to creating
4196  // a formula group from formulas that should not be merged into a group,
4197  // so check also the formula itself.
4198  case formula::svSingleRef:
4199  {
4200  // Single cell reference.
4201  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4202  if (rRef != *pOtherTok->GetSingleRef())
4203  return NotEqual;
4204 
4205  if (rRef.IsRowRel())
4206  bInvariant = false;
4207  }
4208  break;
4209  case formula::svDoubleRef:
4210  {
4211  // Range reference.
4212  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4213  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4214  if (rRef1 != *pOtherTok->GetSingleRef())
4215  return NotEqual;
4216 
4217  if (rRef2 != *pOtherTok->GetSingleRef2())
4218  return NotEqual;
4219 
4220  if (rRef1.IsRowRel())
4221  bInvariant = false;
4222 
4223  if (rRef2.IsRowRel())
4224  bInvariant = false;
4225  }
4226  break;
4227  // All index tokens are names. Different categories already had
4228  // different OpCode values.
4229  case formula::svIndex:
4230  {
4231  if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4232  return NotEqual;
4233  switch (pThisTok->GetOpCode())
4234  {
4235  case ocTableRef:
4236  // nothing, sheet value assumed as -1, silence
4237  // ScTableRefToken::GetSheet() SAL_WARN about
4238  // unhandled
4239  ;
4240  break;
4241  default: // ocName, ocDBArea
4242  if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4243  return NotEqual;
4244  }
4245  }
4246  break;
4247  default:
4248  ;
4249  }
4250  }
4251 
4252  return bInvariant ? EqualInvariant : EqualRelativeRef;
4253 }
4254 
4255 namespace {
4256 
4257 // Split N into optimally equal-sized pieces, each not larger than K.
4258 // Return value P is number of pieces. A returns the number of pieces
4259 // one larger than N/P, 0..P-1.
4260 
4261 int splitup(int N, int K, int& A)
4262 {
4263  assert(N > 0);
4264  assert(K > 0);
4265 
4266  A = 0;
4267 
4268  if (N <= K)
4269  return 1;
4270 
4271  const int ideal_num_parts = N / K;
4272  if (ideal_num_parts * K == N)
4273  return ideal_num_parts;
4274 
4275  const int num_parts = ideal_num_parts + 1;
4276  const int nominal_part_size = N / num_parts;
4277 
4278  A = N - num_parts * nominal_part_size;
4279 
4280  return num_parts;
4281 }
4282 
4283 struct ScDependantsCalculator
4284 {
4285  ScDocument& mrDoc;
4286  const ScTokenArray& mrCode;
4287  const ScFormulaCellGroupRef& mxGroup;
4288  const SCROW mnLen;
4289  const ScAddress& mrPos;
4290  const bool mFromFirstRow;
4291  const SCROW mnStartOffset;
4292  const SCROW mnEndOffset;
4293  const SCROW mnSpanLen;
4294 
4295  ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4296  const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4297  mrDoc(rDoc),
4298  mrCode(rCode),
4299  mxGroup(rCell.GetCellGroup()),
4300  mnLen(mxGroup->mnLength),
4301  mrPos(rPos),
4302  // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4303  // only from further rows. This data fetching could also lead to Interpret() calls, so
4304  // in OpenCL mode the formula in practice depends on those cells too.
4305  mFromFirstRow(fromFirstRow),
4306  mnStartOffset(nStartOffset),
4307  mnEndOffset(nEndOffset),
4308  mnSpanLen(nEndOffset - nStartOffset + 1)
4309  {
4310  }
4311 
4312  // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4313  // (note already modified a bit, mFromFirstRow)
4314 
4315  // I think what this function does is to check whether the relative row reference nRelRow points
4316  // to a row that is inside the range of rows covered by the formula group.
4317 
4318  bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4319  {
4320  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4321  return false;
4322 
4323  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4324 
4325  if (nRelRow <= 0)
4326  {
4327  SCROW nTest = nEndRow;
4328  nTest += nRelRow;
4329  if (nTest >= mrPos.Row())
4330  return true;
4331  }
4332  else
4333  {
4334  SCROW nTest = mrPos.Row(); // top row.
4335  nTest += nRelRow;
4336  if (nTest <= nEndRow)
4337  return true;
4338  // If pointing below the formula, it's always included if going from first row.
4339  if (mFromFirstRow)
4340  return true;
4341  }
4342 
4343  return false;
4344  }
4345 
4346  // FIXME: another copy-paste
4347 
4348  // And this correspondingly checks whether an absolute row is inside the range of rows covered
4349  // by the formula group.
4350 
4351  bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4352  {
4353  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4354  return false;
4355 
4356  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4357 
4358  if (rRefPos.Row() < mrPos.Row())
4359  return false;
4360 
4361  // If pointing below the formula, it's always included if going from first row.
4362  if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4363  return false;
4364 
4365  return true;
4366  }
4367 
4368  // Checks if the doubleref engulfs all of formula group cells
4369  // Note : does not check if there is a partial overlap, that can be done by calling
4370  // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4371  bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4372  {
4373  if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4374  || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4375  {
4376  return false;
4377  }
4378 
4379  SCROW nStartRow = mrPos.Row();
4380  SCROW nEndRow = nStartRow + mnLen - 1;
4381  SCROW nRefStartRow = rAbs.aStart.Row();
4382  SCROW nRefEndRow = rAbs.aEnd.Row();
4383 
4384  if (bIsRef1RowRel && bIsRef2RowRel &&
4385  ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4386  ((nRefStartRow + mnLen - 1) <= nStartRow &&
4387  (nRefEndRow + mnLen - 1) >= nEndRow)))
4388  return true;
4389 
4390  if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4391  (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4392  return true;
4393 
4394  if (!bIsRef2RowRel &&
4395  nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4396  return true;
4397 
4398  // If going from first row, the referenced range must be entirely above the formula,
4399  // otherwise the formula would be included.
4400  if (mFromFirstRow && nRefEndRow >= nStartRow)
4401  return true;
4402 
4403  return false;
4404  }
4405 
4406  // FIXME: another copy-paste
4407  SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4408  {
4409  SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4410  nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4411  if (nLastRow < (nRow + nRowLen - 1))
4412  {
4413  // This can end up negative! Was that the original intent, or
4414  // is it accidental? Was it not like that originally but the
4415  // surrounding conditions changed?
4416  nRowLen = nLastRow - nRow + 1;
4417  // Anyway, let's assume it doesn't make sense to return a
4418  // negative or zero value here.
4419  if (nRowLen <= 0)
4420  nRowLen = 1;
4421  }
4422  else if (nLastRow == 0)
4423  // Column is empty.
4424  nRowLen = 1;
4425 
4426  return nRowLen;
4427  }
4428 
4429  bool DoIt()
4430  {
4431  // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4432 
4433  ScRangeList aRangeList;
4434 
4435  // Self references should be checked by considering the entire formula-group not just the provided span.
4436  bool bHasSelfReferences = false;
4437  bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4438 
4439  FormulaToken** pRPNArray = mrCode.GetCode();
4440  sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4441  for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4442  {
4443  auto p = pRPNArray[nTokenIdx];
4444  if (!bInDocShellRecalc)
4445  {
4446  // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4447  // of the result of the condition expression.
4448  // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4449  // in the document. So lets disable threading and stop dependency evaluation if
4450  // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4451  // for formulae with IF/IFS/SWITCH
4452  OpCode nOpCode = p->GetOpCode();
4453  if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4454  return false;
4455  }
4456 
4457  switch (p->GetType())
4458  {
4459  case svSingleRef:
4460  {
4461  ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4462  if( aRef.IsDeleted())
4463  return false;
4464  ScAddress aRefPos = aRef.toAbs(&mrDoc, mrPos);
4465 
4466  if (!mrDoc.TableExists(aRefPos.Tab()))
4467  return false; // or true?
4468 
4469  if (aRef.IsRowRel())
4470  {
4471  if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4472  {
4473  bHasSelfReferences = true;
4474  continue;
4475  }
4476 
4477  // Trim data array length to actual data range.
4478  SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4479 
4480  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4481  aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4482  }
4483  else
4484  {
4485  if (isSelfReferenceAbsolute(aRefPos))
4486  {
4487  bHasSelfReferences = true;
4488  continue;
4489  }
4490 
4491  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4492  }
4493  }
4494  break;
4495  case svDoubleRef:
4496  {
4497  ScComplexRefData aRef = *p->GetDoubleRef();
4498  if( aRef.IsDeleted())
4499  return false;
4500  ScRange aAbs = aRef.toAbs(&mrDoc, mrPos);
4501 
4502  // Multiple sheet
4503  if (aRef.Ref1.Tab() != aRef.Ref2.Tab())
4504  return false;
4505 
4506  bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4507  // Check for self reference.
4508  if (bIsRef1RowRel)
4509  {
4510  if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4511  {
4512  bHasSelfReferences = true;
4513  continue;
4514  }
4515  }
4516  else if (isSelfReferenceAbsolute(aAbs.aStart))
4517  {
4518  bHasSelfReferences = true;
4519  continue;
4520  }
4521 
4522  bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4523  if (bIsRef2RowRel)
4524  {
4525  if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4526  {
4527  bHasSelfReferences = true;
4528  continue;
4529  }
4530  }
4531  else if (isSelfReferenceAbsolute(aAbs.aEnd))
4532  {
4533  bHasSelfReferences = true;
4534  continue;
4535  }
4536 
4537  if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4538  {
4539  bHasSelfReferences = true;
4540  continue;
4541  }
4542 
4543  // The first row that will be referenced through the doubleref.
4544  SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4545  // The last row that will be referenced through the doubleref.
4546  SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4547  // Number of rows to be evaluated from nFirstRefRow.
4548  SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4549  assert(nArrayLength > 0);
4550 
4551  // Trim trailing empty rows.
4552  nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4553 
4554  aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4555  aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4556  }
4557  break;
4558  default:
4559  break;
4560  }
4561  }
4562 
4563  // Compute dependencies irrespective of the presence of any self references.
4564  // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4565  // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4566  for (size_t i = 0; i < aRangeList.size(); ++i)
4567  {
4568  const ScRange & rRange = aRangeList[i];
4569  assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4570  for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4571  {
4572  SCROW nStartRow = rRange.aStart.Row();
4573  SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4574  if( mFromFirstRow )
4575  { // include also all previous rows
4576  nLength += nStartRow;
4577  nStartRow = 0;
4578  }
4579  if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4580  nLength, mxGroup))
4581  return false;
4582  }
4583  }
4584 
4585  if (bHasSelfReferences)
4586  mxGroup->mbPartOfCycle = true;
4587 
4588  return !bHasSelfReferences;
4589  }
4590 };
4591 
4592 } // anonymous namespace
4593 
4594 bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset)
4595 {
4596  if (!mxGroup || !pCode)
4597  return false;
4598 
4599  auto aScope = sc::FormulaLogger::get().enterGroup(*pDocument, *this);
4600  ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
4601 
4602  if (mxGroup->mbPartOfCycle)
4603  {
4604  aScope.addMessage("This formula-group is part of a cycle");
4605  return false;
4606  }
4607 
4608  if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4609  {
4610  aScope.addMessage("group calc disabled");
4611  return false;
4612  }
4613 
4614  // Use SC_TEST_CALCULATION=opencl/threads to force calculation e.g. for unittests
4616  if (forceType == ForceCalculationCore
4617  || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4618  && forceType != ForceCalculationOpenCL
4619  && forceType != ForceCalculationThreads))
4620  {
4621  mxGroup->meCalcState = sc::GroupCalcDisabled;
4622  aScope.addGroupSizeThresholdMessage(*this);
4623  return false;
4624  }
4625 
4626  if (cMatrixFlag != ScMatrixMode::NONE)
4627  {
4628  mxGroup->meCalcState = sc::GroupCalcDisabled;
4629  aScope.addMessage("matrix skipped");
4630  return false;
4631  }
4632 
4633  if( forceType != ForceCalculationNone )
4634  {
4635  // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4636  // without it actually being in the document at the specified position.
4637  // That would confuse opencl/threading code, as they refer to the cell group
4638  // also using the position. This is normally not triggered (single cells
4639  // are normally not in a cell group), but if forced, check for this explicitly.
4640  if( pDocument->GetFormulaCell( aPos ) != this )
4641  {
4642  mxGroup->meCalcState = sc::GroupCalcDisabled;
4643  aScope.addMessage("cell not in document");
4644  return false;
4645  }
4646  }
4647 
4648  // Guard against endless recursion of Interpret() calls, for this to work
4649  // ScFormulaCell::InterpretFormulaGroup() must never be called through
4650  // anything else than ScFormulaCell::Interpret(), same as
4651  // ScFormulaCell::InterpretTail()
4652  RecursionCounter aRecursionCounter( rRecursionHelper, this);
4653 
4654  bool bDependencyComputed = false;
4655  bool bDependencyCheckFailed = false;
4656 
4657  // Get rid of -1's in offsets (defaults) or any invalid offsets.
4658  SCROW nMaxOffset = mxGroup->mnLength - 1;
4659  nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4660  nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4661 
4662  if (nEndOffset < nStartOffset)
4663  {
4664  nStartOffset = 0;
4665  nEndOffset = nMaxOffset;
4666  }
4667 
4668  // Preference order: First try OpenCL, then threading.
4669  // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4670  if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4671  return true;
4672 
4673  if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4674  return true;
4675 
4676  return false;
4677 }
4678 
4680  SCROW nStartOffset, SCROW nEndOffset,
4681  bool bCalcDependencyOnly)
4682 {
4683  ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
4684  // iterate over code in the formula ...
4685  // ensure all input is pre-calculated -
4686  // to avoid writing during the calculation
4687  if (bCalcDependencyOnly)
4688  {
4689  // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4690  // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4691  // (We can only reach here from a multi-group dependency evaluation attempt).
4692  // (These two have to be in pairs always for any given formula-group)
4693  ScDependantsCalculator aCalculator(*pDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4694  return aCalculator.DoIt();
4695  }
4696 
4697  bool bOKToParallelize = false;
4698  {
4699  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4700  if (mxGroup->mbPartOfCycle)
4701  {
4702  mxGroup->meCalcState = sc::GroupCalcDisabled;
4703  rScope.addMessage("found circular formula-group dependencies");
4704  return false;
4705  }
4706 
4707  ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4708  ScDependantsCalculator aCalculator(*pDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4709  bOKToParallelize = aCalculator.DoIt();
4710 
4711  }
4712 
4713  if (rRecursionHelper.IsInRecursionReturn())
4714  {
4715  mxGroup->meCalcState = sc::GroupCalcDisabled;
4716  rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4717  return false;
4718  }
4719 
4720  if (mxGroup->mbPartOfCycle)
4721  {
4722  mxGroup->meCalcState = sc::GroupCalcDisabled;
4723  rScope.addMessage("found circular formula-group dependencies");
4724  return false;
4725  }
4726 
4727  if (!rRecursionHelper.AreGroupsIndependent())
4728  {
4729  // This call resulted from a dependency calculation for a multigroup-threading attempt,
4730  // but found dependency among the groups.
4731  rScope.addMessage("multi-group-dependency failed");
4732  return false;
4733  }
4734 
4735  if (!bOKToParallelize)
4736  {
4737  mxGroup->meCalcState = sc::GroupCalcDisabled;
4738  rScope.addMessage("could not do new dependencies calculation thing");
4739  return false;
4740  }
4741 
4742  return true;
4743 }
4744 
4745 static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc,
4747  std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4748 {
4749  const SCROW nLen = xGroup->mnLength;
4750  const sal_Int32 nWt = xGroup->mnWeight;
4751  ScAddress aAddr(xGroup->mpTopCell->aPos);
4752 
4753  SCCOL nColRet = aAddr.Col();
4754 
4755  const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4756  if (bLeft)
4757  --nColRet;
4758  else
4759  ++nColRet;
4760 
4761  while (nColRet >= 0 && nColRet <= nMaxCol)
4762  {
4763  aAddr.SetCol(nColRet);
4764  const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4765  if (!pCell)
4766  break;
4767 
4768  if (!pCell->NeedsInterpret())
4769  break;
4770 
4771  const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4772  if (!xNGroup)
4773  break;
4774 
4775  if (!pCell->GetCode()->IsEnabledForThreading())
4776  break;
4777 
4778  if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4779  break;
4780 
4781  const SCROW nNLen = xNGroup->mnLength;
4782  const sal_Int32 nNWt = pCell->GetWeight();
4783  if (nNLen != nLen || nNWt != nWt)
4784  break;
4785 
4786  rFGSet.insert(xNGroup.get());
4787  rFGMap[nColRet] = xNGroup->mpTopCell;
4788 
4789  if (bLeft)
4790  --nColRet;
4791  else
4792  ++nColRet;
4793  }
4794 
4795  if (bLeft)
4796  ++nColRet;
4797  else
4798  --nColRet;
4799 
4800  return nColRet;
4801 }
4802 
4803 // To be called only from InterpretFormulaGroup().
4805  bool& bDependencyComputed,
4806  bool& bDependencyCheckFailed,
4807  SCROW nStartOffset,
4808  SCROW nEndOffset)
4809 {
4810  static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4811  if (!bDependencyCheckFailed && !bThreadingProhibited &&
4814  {
4815  if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4816  {
4817  bDependencyComputed = true;
4818  bDependencyCheckFailed = true;
4819  return false;
4820  }
4821 
4822  bDependencyComputed = true;
4823 
4824  const static bool bHyperThreadingActive = cpuid::hasHyperThreading();
4825 
4826  // Then do the threaded calculation
4827 
4828  class Executor : public comphelper::ThreadTask
4829  {
4830  private:
4831  const unsigned mnThisThread;
4832  const unsigned mnThreadsTotal;
4833  ScDocument* mpDocument;
4835  const ScAddress& mrTopPos;
4836  SCCOL mnStartCol;
4837  SCCOL mnEndCol;
4838  SCROW mnStartOffset;
4839  SCROW mnEndOffset;
4840 
4841  public:
4842  Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4843  unsigned nThisThread,
4844  unsigned nThreadsTotal,
4845  ScDocument* pDocument2,
4846  ScInterpreterContext* pContext,
4847  const ScAddress& rTopPos,
4848  SCCOL nStartCol,
4849  SCCOL nEndCol,
4850  SCROW nStartOff,
4851  SCROW nEndOff) :
4852  comphelper::ThreadTask(rTag),
4853  mnThisThread(nThisThread),
4854  mnThreadsTotal(nThreadsTotal),
4855  mpDocument(pDocument2),
4856  mpContext(pContext),
4857  mrTopPos(rTopPos),
4858  mnStartCol(nStartCol),
4859  mnEndCol(nEndCol),
4860  mnStartOffset(nStartOff),
4861  mnEndOffset(nEndOff)
4862  {
4863  }
4864 
4865  virtual void doWork() override
4866  {
4867  ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4868  mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4869  mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4871  }
4872 
4873  };
4874 
4875  SvNumberFormatter* pNonThreadedFormatter = pDocument->GetNonThreadedContext().GetFormatTable();
4876 
4878  sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4879 
4880  if ( bHyperThreadingActive && nThreadCount >= 2 )
4881  nThreadCount /= 2;
4882 
4883  SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4884 
4886  std::map<SCCOL, ScFormulaCell*> aFGMap;
4887  aFGSet.insert(mxGroup.get());
4888 
4889  ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper();
4890  SCCOL nColStart = aPos.Col();
4891  SCCOL nColEnd = nColStart;
4892  if (!rRecursionHelper.HasFormulaGroupSet() && pDocument->IsInDocShellRecalc())
4893  {
4894  nColStart = lcl_probeLeftOrRightFGs(mxGroup, *pDocument, aFGSet, aFGMap, true);
4895  nColEnd = lcl_probeLeftOrRightFGs(mxGroup, *pDocument, aFGSet, aFGMap, false);
4896  }
4897 
4898  if (nColStart != nColEnd)
4899  {
4900  ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4901  for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4902  {
4903  if (nCurrCol == aPos.Col())
4904  continue;
4905 
4906  bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4907  if (!bFGOK || !aGuard.AreGroupsIndependent())
4908  {
4909  nColEnd = nColStart = aPos.Col();
4910  break;
4911  }
4912  }
4913  }
4914 
4915  std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
4916  {
4919 
4921 
4922  // Start nThreadCount new threads
4923  std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4924  ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, *pDocument, pNonThreadedFormatter);
4925  ScInterpreterContext* context = nullptr;
4926 
4927  for (int i = 0; i < nThreadCount; ++i)
4928  {
4929  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4930  assert(!context->pInterpreter);
4931  aInterpreters[i].reset(new ScInterpreter(this, pDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
4932  context->pInterpreter = aInterpreters[i].get();
4934  rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, pDocument, context, mxGroup->mpTopCell->aPos,
4935  nColStart, nColEnd, nStartOffset, nEndOffset));
4936  }
4937 
4938  SAL_INFO("sc.threaded", "Waiting for threads to finish work");
4939  // Do not join the threads here. They will get joined in ScDocument destructor
4940  // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
4941  rThreadPool.waitUntilDone(aTag, false);
4942 
4944 
4945  for (int i = 0; i < nThreadCount; ++i)
4946  {
4947  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4948  // This is intentionally done in this main thread in order to avoid locking.
4950  context->pInterpreter = nullptr;
4951  }
4952 
4953  SAL_INFO("sc.threaded", "Done");
4954  }
4955 
4956  ScAddress aStartPos(mxGroup->mpTopCell->aPos);
4957  SCROW nSpanLen = nEndOffset - nStartOffset + 1;
4958  aStartPos.SetRow(aStartPos.Row() + nStartOffset);
4959  // Reuse one of the previously allocated interpreter objects here.
4960  pDocument->HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
4961  aStartPos.Tab(), aInterpreters[0].get());
4962 
4963  return true;
4964  }
4965 
4966  return false;
4967 }
4968 
4969 // To be called only from InterpretFormulaGroup().
4971  bool& bDependencyComputed,
4972  bool& bDependencyCheckFailed)
4973 {
4974  bool bCanVectorize = pCode->IsEnabledForOpenCL();
4975  switch (pCode->GetVectorState())
4976  {
4977  case FormulaVectorEnabled:
4979  break;
4980 
4981  // Not good.
4983  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4984  break;
4986  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4987  break;
4989  aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
4990  break;
4991  case FormulaVectorDisabled:
4992  case FormulaVectorUnknown:
4993  default:
4994  aScope.addMessage("group calc disabled due to vector state (unknown)");
4995  return false;
4996  }
4997 
4998  if (!bCanVectorize)
4999  return false;
5000 
5002  {
5003  aScope.addMessage("opencl not enabled");
5004  return false;
5005  }
5006 
5007  // TableOp does tricks with using a cell with different values, just bail out.
5009  return false;
5010 
5011  if (bDependencyCheckFailed)
5012  return false;
5013 
5014  if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
5015  {
5016  bDependencyComputed = true;
5017  bDependencyCheckFailed = true;
5018  return false;
5019  }
5020 
5021  bDependencyComputed = true;
5022 
5023  // TODO : Disable invariant formula group interpretation for now in order
5024  // to get implicit intersection to work.
5025  if (mxGroup->mbInvariant && false)
5027 
5028  int nMaxGroupLength = INT_MAX;
5029 
5030 #ifdef _WIN32
5031  // Heuristic: Certain old low-end OpenCL implementations don't
5032  // work for us with too large group lengths. 1000 was determined
5033  // empirically to be a good compromise.
5034  if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
5035  nMaxGroupLength = 1000;
5036 #endif
5037 
5038  if (std::getenv("SC_MAX_GROUP_LENGTH"))
5039  nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
5040 
5041  int nNumOnePlus;
5042  const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
5043 
5044  int nOffset = 0;
5045  int nCurChunkSize;
5046  ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
5047  for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
5048  {
5049  nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
5050 
5051  ScFormulaCellGroupRef xGroup;
5052 
5053  if (nNumParts == 1)
5054  xGroup = mxGroup;
5055  else
5056  {
5057  // Ugly hack
5058  xGroup = new ScFormulaCellGroup();
5059  xGroup->mpTopCell = mxGroup->mpTopCell;
5060  xGroup->mpTopCell->aPos = aOrigPos;
5061  xGroup->mpTopCell->aPos.IncRow(nOffset);
5062  xGroup->mbInvariant = mxGroup->mbInvariant;
5063  xGroup->mnLength = nCurChunkSize;
5064  xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
5065  }
5066 
5067  ScTokenArray aCode(pDocument);
5068  ScGroupTokenConverter aConverter(aCode, *pDocument, *this, xGroup->mpTopCell->aPos);
5069  // TODO avoid this extra compilation
5070  ScCompiler aComp( pDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
5071  aComp.CompileTokenArray();
5072  if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
5073  {
5075  {
5076  SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
5077 #ifdef DBG_UTIL
5078  for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
5079  {
5080  SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
5081  << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
5082  << "(" << int(opcode) << ")");
5083  }
5084 #endif
5085  }
5086  else
5087  SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
5088 
5089  mxGroup->meCalcState = sc::GroupCalcDisabled;
5090 
5091  // Undo the hack above
5092  if (nNumParts > 1)
5093  {
5094  mxGroup->mpTopCell->aPos = aOrigPos;
5095  xGroup->mpTopCell = nullptr;
5096  mxGroup->mpCode = std::move(xGroup->mpCode);
5097  }
5098 
5099  aScope.addMessage("group token conversion failed");
5100  return false;
5101  }
5102