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& mrDoc;
91  sal_uInt32 mnGroup;
92  sal_uInt16 mnRecursion;
93 
94  DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) :
95  maPos(rPos),
96  mrDoc(rDoc),
97  mnGroup(nGroup),
98  mnRecursion(rDoc.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.mrDoc) +
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.mrDoc));
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& rDoc )
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, rDoc, 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& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
234 {
235  return rData.toAbs(rDoc, rPos).Col();
236 }
237 
238 SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
239 {
240  return rData.toAbs(rDoc, rPos).Row();
241 }
242 
243 SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData)
244 {
245  return rData.toAbs(rDoc, rPos).Tab();
246 }
247 
250 bool
251 lcl_checkRangeDimension(
252  const ScDocument& rDoc,
253  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
254  const DimensionSelector aWhich)
255 {
256  return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) &&
257  aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2);
258 }
259 
260 bool
261 lcl_checkRangeDimensions(
262  const ScDocument& rDoc,
263  const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2,
264  bool& bCol, bool& bRow, bool& bTab)
265 {
266  const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol));
267  const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow));
268  const bool bSameTabs(lcl_checkRangeDimension(rDoc, 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& rDoc, 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(rDoc, 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(rDoc, 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& mrDoc;
316  ScAddress maPos;
317  DimensionSelector maFunc;
318 public:
319  LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) :
320  mrDoc(rDoc), 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(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1);
327  }
328 };
329 
335 class AdjacentByReference
336 {
337  const ScDocument& mrDoc;
338  ScAddress maPos;
339  DimensionSelector maFunc;
340 public:
341  AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) :
342  mrDoc(rDoc), 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(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1;
349  }
350 };
351 
352 bool
353 lcl_checkIfAdjacent(
354  const ScDocument& rDoc,
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(rDoc, rPos, aWhich));
363 }
364 
365 void
366 lcl_fillRangeFromRefList(
367  const ScDocument& rDoc,
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(rDoc, aPos);
373  const ScSingleRefData aEnd(
374  SingleDoubleRefProvider(*rReferences.back()).Ref2);
375  rRange.aEnd = aEnd.toAbs(rDoc, aPos);
376 }
377 
378 bool
379 lcl_refListFormsOneRange(
380  const ScDocument& rDoc,
381  const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences,
382  ScRange& rRange)
383 {
384  if (rReferences.size() == 1)
385  {
386  lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange);
387  return true;
388  }
389 
390  bool bCell(false);
391  bool bRow(false);
392  bool bTab(false);
393  if (lcl_checkRangeDimensions(rDoc, 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(rDoc, rPos, aWhich));
416  if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich))
417  {
418  lcl_fillRangeFromRefList(rDoc, 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& rOldDoc,
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 (!rOldDoc.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& rOldDoc)
454 {
455  ScDBCollection* pOldDBCollection = rOldDoc.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& rDoc, 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(rDoc)),
626  rDocument(rDoc),
627  pPrevious(nullptr),
628  pNext(nullptr),
629  pPreviousTrack(nullptr),
630  pNextTrack(nullptr),
631  aPos(rPos)
632 {
633 }
634 
635 ScFormulaCell::ScFormulaCell( ScDocument& rDoc, 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  rDocument( rDoc ),
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(rDoc);
669 }
670 
672  ScDocument& rDoc, 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  rDocument( rDoc ),
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)
719 
720  pCode->GenHash();
721 }
722 
724  ScDocument& rDoc, 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  rDocument( rDoc ),
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)
767 
768  pCode->GenHash();
769 }
770 
772  ScDocument& rDoc, 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(rDoc)),
794  rDocument( rDoc ),
795  pPrevious(nullptr),
796  pNext(nullptr),
797  pPreviousTrack(nullptr),
798  pNextTrack(nullptr),
799  aPos(rPos)
800 {
801  if (bSubTotal)
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  rDocument( 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 && !rDocument.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.rDocument.IsClipboard();
846 
847  //update ScNameTokens
848  if (!rDocument.IsClipOrUndo() || rDoc.IsUndo())
849  {
850  if (!rDocument.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.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal);
860  else if (eOpCode == ocDBArea || eOpCode == ocTableRef)
861  adjustDBRange(pToken, rDoc, rCell.rDocument);
862  }
863  }
864 
865  bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool();
866  if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal))
867  {
868  pCode->ReadjustAbsolute3DReferences(rCell.rDocument, rDoc, rCell.aPos);
869  }
870 
871  pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs );
872  }
873 
874  if (!rDocument.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)
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, rDocument, aPos);
956 }
957 
958 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const
959 {
960  return new ScFormulaCell(*this, rDocument, 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(rDocument, aPos);
995  if (rDocument.ValidAddress(aAbs))
996  pCell = rDocument.GetFormulaCell(aAbs);
997 
998  if (pCell)
999  {
1000  pCell->GetFormula( rBuffer, eGrammar, pContext );
1001  return;
1002  }
1003  else
1004  {
1005  ScCompiler aComp( rDocument, 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( rDocument, 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(rDocument, aPos);
1061  if (rDocument.ValidAddress(aAbs))
1062  pCell = rDocument.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 ( rDocument.IsClipOrUndo() )
1129  return;
1130  bool bWasInFormulaTree = rDocument.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( rDocument, 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  rDocument.PutInFormulaTree( this );
1158 }
1159 
1161  sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening )
1162 {
1163  if ( rDocument.IsClipOrUndo() )
1164  return;
1165  bool bWasInFormulaTree = rDocument.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  rDocument.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 && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE )
1203  {
1204  // RPN length may get changed
1205  bool bWasInFormulaTree = rDocument.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  rDocument.PutInFormulaTree( this );
1228 
1229  if (bSubTotal)
1230  rDocument.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 && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE)
1243  {
1244  // RPN length may get changed
1245  bool bWasInFormulaTree = rDocument.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  rDocument.PutInFormulaTree( this );
1268 
1269  if (bSubTotal)
1270  rDocument.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 = rDocument.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  rDocument.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 = rDocument.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  rDocument.AddSubTotalCell(this);
1338 
1339  bDoCompile = false;
1340  pCode = pPreviousCell->pCode;
1341  if (pPreviousCell->mbIsExtRef)
1342  rDocument.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  rDocument.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)
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  rDocument.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 && rDocument.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 = rDocument.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, rDocument);
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 ( rDocument.IsInDdeLinkUpdate() )
1578  return bGroupInterpreted;
1579 
1580  if (bRunning)
1581  {
1582  if (!rDocument.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  if (rRecursionHelper.GetList().size() > 1)
1715  {
1716  ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell;
1717  if (pLastCell != this)
1718  {
1721  pLastCell->InterpretTail(
1722  *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE);
1724  }
1725  }
1726  // Start at 1, init things.
1727  rRecursionHelper.StartIteration();
1728  // Mark all cells being in iteration. Reset results to
1729  // original values, formula cells have been interpreted
1730  // already, discard that step.
1731  for (ScFormulaRecursionList::const_iterator aIter(
1732  rRecursionHelper.GetIterationStart()); aIter !=
1733  rRecursionHelper.GetIterationEnd(); ++aIter)
1734  {
1735  ScFormulaCell* pIterCell = (*aIter).pCell;
1736  pIterCell->aResult = (*aIter).aPreviousResult;
1737  pIterCell->bIsIterCell = true;
1738  }
1739  }
1740  bIterationFromRecursion = false;
1741  sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount();
1742  for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone;
1743  rRecursionHelper.IncIteration())
1744  {
1745  rDone = false;
1746  bool bFirst = true;
1747  for ( ScFormulaRecursionList::iterator aIter(
1748  rRecursionHelper.GetIterationStart()); aIter !=
1749  rRecursionHelper.GetIterationEnd() &&
1750  !rRecursionHelper.IsInReturn(); ++aIter)
1751  {
1752  ScFormulaCell* pIterCell = (*aIter).pCell;
1753  if (pIterCell->IsDirtyOrInTableOpDirty() &&
1754  rRecursionHelper.GetIteration() !=
1755  pIterCell->GetSeenInIteration())
1756  {
1757  (*aIter).aPreviousResult = pIterCell->aResult;
1760  pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION);
1762  }
1763  if (bFirst)
1764  {
1765  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1766  bFirst = false;
1767  }
1768  else if (rDone)
1769  {
1770  rDone = !pIterCell->IsDirtyOrInTableOpDirty();
1771  }
1772  }
1773  if (rRecursionHelper.IsInReturn())
1774  {
1775  bResumeIteration = true;
1776  break; // for
1777  // Don't increment iteration.
1778  }
1779  }
1780  if (!bResumeIteration)
1781  {
1782  if (rDone)
1783  {
1784  for (ScFormulaRecursionList::const_iterator aIter(
1785  rRecursionHelper.GetIterationStart());
1786  aIter != rRecursionHelper.GetIterationEnd();
1787  ++aIter)
1788  {
1789  ScFormulaCell* pIterCell = (*aIter).pCell;
1790  pIterCell->bIsIterCell = false;
1791  pIterCell->nSeenInIteration = 0;
1792  pIterCell->bRunning = (*aIter).bOldRunning;
1793  }
1794  }
1795  else
1796  {
1797  for (ScFormulaRecursionList::const_iterator aIter(
1798  rRecursionHelper.GetIterationStart());
1799  aIter != rRecursionHelper.GetIterationEnd();
1800  ++aIter)
1801  {
1802  ScFormulaCell* pIterCell = (*aIter).pCell;
1803  pIterCell->bIsIterCell = false;
1804  pIterCell->nSeenInIteration = 0;
1805  pIterCell->bRunning = (*aIter).bOldRunning;
1806  pIterCell->ResetDirty();
1807  // The difference to Excel is that Excel does not
1808  // produce an error for non-convergence thus a
1809  // delta of 0.001 still works to execute the
1810  // maximum number of iterations and display the
1811  // results no matter if the result anywhere reached
1812  // near delta, but also never indicates whether the
1813  // result actually makes sense in case of
1814  // non-counter context. Calc does check the delta
1815  // in every case. If we wanted to support what
1816  // Excel does then add another option "indicate
1817  // non-convergence error" (default on) and execute
1818  // the following block only if set.
1819 #if 1
1820  // If one cell didn't converge, all cells of this
1821  // circular dependency don't, no matter whether
1822  // single cells did.
1823  pIterCell->aResult.SetResultError( FormulaError::NoConvergence);
1824  pIterCell->bChanged = true;
1825 #endif
1826  }
1827  }
1828  // End this iteration and remove entries.
1829  rRecursionHelper.EndIteration();
1830  bResumeIteration = rRecursionHelper.IsDoingIteration();
1831  }
1832  }
1833  if (rRecursionHelper.IsInRecursionReturn() &&
1834  rRecursionHelper.GetRecursionCount() == 0 &&
1835  !rRecursionHelper.IsDoingRecursion())
1836  {
1837  bIterationFromRecursion = false;
1838  // Iterate over cells known so far, start with the last cell
1839  // encountered, inserting new cells if another recursion limit
1840  // is reached. Repeat until solved.
1841  rRecursionHelper.SetDoingRecursion( true);
1842  do
1843  {
1844  rRecursionHelper.SetInRecursionReturn( false);
1845  for (ScFormulaRecursionList::const_iterator aIter(
1846  rRecursionHelper.GetIterationStart());
1847  !rRecursionHelper.IsInReturn() && aIter !=
1848  rRecursionHelper.GetIterationEnd(); ++aIter)
1849  {
1850  ScFormulaCell* pCell = (*aIter).pCell;
1851  if (pCell->IsDirtyOrInTableOpDirty())
1852  {
1855  pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL);
1857  if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell())
1858  pCell->bRunning = (*aIter).bOldRunning;
1859  }
1860  }
1861  } while (rRecursionHelper.IsInRecursionReturn());
1862  rRecursionHelper.SetDoingRecursion( false);
1863  if (rRecursionHelper.IsInIterationReturn())
1864  {
1865  if (!bResumeIteration)
1866  bIterationFromRecursion = true;
1867  }
1868  else if (bResumeIteration ||
1869  rRecursionHelper.IsDoingIteration())
1870  rRecursionHelper.GetList().erase(
1871  rRecursionHelper.GetIterationStart(),
1872  rRecursionHelper.GetLastIterationStart());
1873  else
1874  rRecursionHelper.Clear();
1875  }
1876  } while (bIterationFromRecursion || bResumeIteration);
1877  }
1878 
1879 #if DEBUG_CALCULATION
1881  if (nErr != FormulaError::NONE)
1882  aDC.storeResultError( nErr);
1883  else if (aResult.IsValue())
1884  aDC.storeResult( aResult.GetDouble());
1885  else
1886  aDC.storeResult( aResult.GetString());
1887 #endif
1888 
1889  return bGroupInterpreted;
1890 }
1891 
1893 {
1894  RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this);
1895  // TODO If this cell is not an iteration cell, add it to the list of iteration cells?
1896  if(bIsIterCell)
1898  if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE )
1899  {
1900  // #i11719# no RPN and no error and no token code but result string present
1901  // => interpretation of this cell during name-compilation and unknown names
1902  // => can't exchange underlying code array in CompileTokenArray() /
1903  // Compile() because interpreter's token iterator would crash or pCode
1904  // would be deleted twice if this cell was interpreted during
1905  // compilation.
1906  // This should only be a temporary condition and, since we set an
1907  // error, if ran into it again we'd bump into the dirty-clearing
1908  // condition further down.
1909  if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
1910  {
1911  pCode->SetCodeError( FormulaError::NoCode );
1912  // This is worth an assertion; if encountered in daily work
1913  // documents we might need another solution. Or just confirm correctness.
1914  return;
1915  }
1917  }
1918 
1919  if( pCode->GetCodeLen() )
1920  {
1921  std::unique_ptr<ScInterpreter> pScopedInterpreter;
1922  ScInterpreter* pInterpreter;
1923  if (rContext.pInterpreter)
1924  {
1925  pInterpreter = rContext.pInterpreter;
1926  pInterpreter->Init(this, aPos, *pCode);
1927  }
1928  else
1929  {
1930  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode ));
1931  pInterpreter = pScopedInterpreter.get();
1932  }
1933 
1934  FormulaError nOldErrCode = aResult.GetResultError();
1935  if ( nSeenInIteration == 0 )
1936  { // Only the first time
1937  // With bChanged=false, if a newly compiled cell has a result of
1938  // 0.0, no change is detected and the cell will not be repainted.
1939  // bChanged = false;
1940  aResult.SetResultError( FormulaError::NONE );
1941  }
1942 
1943  switch ( aResult.GetResultError() )
1944  {
1945  case FormulaError::CircularReference : // will be determined again if so
1946  aResult.SetResultError( FormulaError::NONE );
1947  break;
1948  default: break;
1949  }
1950 
1951  bool bOldRunning = bRunning;
1952  bRunning = true;
1953  pInterpreter->Interpret();
1955  {
1956  if (nSeenInIteration > 0)
1957  --nSeenInIteration; // retry when iteration is resumed
1958 
1959  if ( aResult.GetType() == formula::svUnknown )
1960  aResult.SetToken( pInterpreter->GetResultToken().get() );
1961 
1962  return;
1963  }
1964  bRunning = bOldRunning;
1965 
1966  // The result may be invalid or depend on another invalid result, just abort
1967  // without updating the cell value. Since the dirty flag will not be reset,
1968  // the proper value will be computed later.
1970  return;
1971 
1972  // #i102616# For single-sheet saving consider only content changes, not format type,
1973  // because format type isn't set on loading (might be changed later)
1974  bool bContentChanged = false;
1975 
1976  // Do not create a HyperLink() cell if the formula results in an error.
1977  if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink())
1978  pCode->SetHyperLink(false);
1979 
1980  if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference)
1981  {
1982  bChanged = true;
1983 
1984  if (pInterpreter->GetError() == FormulaError::RetryCircular)
1985  {
1986  // Array formula matrix calculation corner case. Keep dirty
1987  // state, do not remove from formula tree or anything else, but
1988  // store FormulaError::CircularReference in case this cell does not get
1989  // recalculated.
1990  aResult.SetResultError( FormulaError::CircularReference);
1991  return;
1992  }
1993 
1994  ResetDirty();
1995  }
1996 
1997  if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty())
1998  {
1999  bool bIsValue = aResult.IsValue(); // the previous type
2000  // Did it converge?
2001  if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs(
2002  pInterpreter->GetNumResult() - aResult.GetDouble()) <=
2004  (!bIsValue && pInterpreter->GetResultType() == svString &&
2005  pInterpreter->GetStringResult() == aResult.GetString()))
2006  {
2007  // A convergence in the first iteration doesn't necessarily
2008  // mean that it's done, it may be as not all related cells
2009  // of a circle changed their values yet. If the set really
2010  // converges it will do so also during the next iteration. This
2011  // fixes situations like of #i44115#. If this wasn't wanted an
2012  // initial "uncalculated" value would be needed for all cells
2013  // of a circular dependency => graph needed before calculation.
2014  if (nSeenInIteration > 1 ||
2016  {
2017  ResetDirty();
2018  }
2019  }
2020  }
2021 
2022  // New error code?
2023  if( pInterpreter->GetError() != nOldErrCode )
2024  {
2025  bChanged = true;
2026  // bContentChanged only has to be set if the file content would be changed
2027  if ( aResult.GetCellResultType() != svUnknown )
2028  bContentChanged = true;
2029  }
2030 
2031  ScFormulaResult aNewResult( pInterpreter->GetResultToken().get());
2032 
2033  // For IF() and other jumps or changed formatted source data the result
2034  // format may change for different runs, e.g. =IF(B1,B1) with first
2035  // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23
2036  // displayed as TRUE. Do not force a general format though if
2037  // mbNeedsNumberFormat is set (because there was a general format..).
2038  // Note that nFormatType may be out of sync here if a format was
2039  // applied or cleared after the last run, but obtaining the current
2040  // format always just to check would be expensive. There may be
2041  // cases where the format should be changed but is not. If that turns
2042  // out to be a real problem then obtain the current format type after
2043  // the initial check when needed.
2044  bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat &&
2046 
2047  // We have some requirements additionally to IsCompatible().
2048  // * Do not apply a NumberFormat::LOGICAL if the result value is not
2049  // 1.0 or 0.0
2050  // * Do not override an already set numeric number format if the result
2051  // is of type NumberFormat::LOGICAL, it could be user applied.
2052  // On the other hand, for an empty jump path instead of FALSE an
2053  // unexpected for example 0% could be displayed. YMMV.
2054  // * Never override a non-standard number format that indicates user
2055  // applied.
2056  // * NumberFormat::TEXT does not force a change.
2057  if (bForceNumberFormat)
2058  {
2059  sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
2060  const SvNumFormatType nRetType = pInterpreter->GetRetFormatType();
2061  if (nRetType == SvNumFormatType::LOGICAL)
2062  {
2063  double fVal = aNewResult.GetDouble();
2064  if (fVal != 1.0 && fVal != 0.0)
2065  bForceNumberFormat = false;
2066  else
2067  {
2068  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2069  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2070  switch (nFormatType)
2071  {
2072  case SvNumFormatType::PERCENT:
2073  case SvNumFormatType::CURRENCY:
2074  case SvNumFormatType::SCIENTIFIC:
2075  case SvNumFormatType::FRACTION:
2076  bForceNumberFormat = false;
2077  break;
2078  case SvNumFormatType::NUMBER:
2079  if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2080  bForceNumberFormat = false;
2081  break;
2082  default: break;
2083  }
2084  }
2085  }
2086  else if (nRetType == SvNumFormatType::TEXT)
2087  {
2088  bForceNumberFormat = false;
2089  }
2090  if (bForceNumberFormat)
2091  {
2092  if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND)
2093  {
2094  nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos);
2095  nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex);
2096  }
2097  if (nOldFormatIndex !=
2098  ScGlobal::GetStandardFormat( *rContext.GetFormatTable(), nOldFormatIndex, nFormatType))
2099  bForceNumberFormat = false;
2100  }
2101  }
2102 
2103  if( mbNeedsNumberFormat || bForceNumberFormat )
2104  {
2105  bool bSetFormat = true;
2106  const SvNumFormatType nOldFormatType = nFormatType;
2107  nFormatType = pInterpreter->GetRetFormatType();
2108  sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex();
2109 
2110  if (nFormatType == SvNumFormatType::TEXT)
2111  {
2112  // Don't set text format as hard format.
2113  bSetFormat = false;
2114  }
2115  else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE)
2116  {
2117  // In a matrix range do not set an (inherited) logical format
2118  // as hard format if the value does not represent a strict TRUE
2119  // or FALSE value. But do set for a top left error value so
2120  // following matrix cells can inherit for non-error values.
2121  // This solves a problem with IF() expressions in array context
2122  // where incidentally the top left element results in logical
2123  // type but some others don't. It still doesn't solve the
2124  // reverse case though, where top left is not logical type but
2125  // some other elements should be. We'd need to transport type
2126  // or format information on arrays.
2127  StackVar eNewCellResultType = aNewResult.GetCellResultType();
2128  if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference)
2129  {
2130  if (eNewCellResultType != svDouble)
2131  {
2132  bSetFormat = false;
2133  nFormatType = nOldFormatType; // that? or number?
2134  }
2135  else
2136  {
2137  double fVal = aNewResult.GetDouble();
2138  if (fVal != 1.0 && fVal != 0.0)
2139  {
2140  bSetFormat = false;
2141  nFormatType = SvNumFormatType::NUMBER;
2142  }
2143  }
2144  }
2145  }
2146 
2147  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0)))
2148  nFormatIndex = ScGlobal::GetStandardFormat(*rContext.GetFormatTable(),
2149  nFormatIndex, nFormatType);
2150 
2151  // Do not replace a General format (which was the reason why
2152  // mbNeedsNumberFormat was set) with a General format.
2153  // 1. setting a format has quite some overhead in the
2154  // ScPatternAttr/ScAttrArray handling, even if identical.
2155  // 2. the General formats may be of different locales.
2156  // XXX if mbNeedsNumberFormat was set even if the current format
2157  // was not General then we'd have to obtain the current format here
2158  // and check at least the types.
2159  if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0)))
2160  {
2161  // set number format explicitly
2163  rDocument.SetNumberFormat( aPos, nFormatIndex );
2164  else
2165  {
2166  // SetNumberFormat() is not thread-safe (modifies ScAttrArray), delay the work
2167  // to the main thread. Since thread calculations operate on formula groups,
2168  // it's enough to store just the row.
2169  DelayedSetNumberFormat data = { aPos.Col(), aPos.Row(), nFormatIndex };
2170  rContext.maDelayedSetNumberFormat.push_back( data );
2171  }
2172  bChanged = true;
2173  }
2174 
2175  // Currently (2019-05-10) nothing else can cope with a duration
2176  // format type, change to time as it was before.
2177  if (nFormatType == SvNumFormatType::DURATION)
2178  nFormatType = SvNumFormatType::TIME;
2179 
2180  mbNeedsNumberFormat = false;
2181  }
2182 
2183  // In case of changes just obtain the result, no temporary and
2184  // comparison needed anymore.
2185  if (bChanged)
2186  {
2187  // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving
2188  // Also handle special cases of initial results after loading.
2189  if ( !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2190  {
2192  StackVar eNew = aNewResult.GetCellResultType();
2193  if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) )
2194  {
2195  // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0
2196  // -> no change
2197  }
2198  else
2199  {
2200  if ( eOld == svHybridCell ) // string result from SetFormulaResultString?
2201  eOld = svString; // ScHybridCellToken has a valid GetString method
2202 
2203  // #i106045# use approxEqual to compare with stored value
2204  bContentChanged = (eOld != eNew ||
2205  (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) ||
2206  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2207  }
2208  }
2209 
2210  aResult.SetToken( pInterpreter->GetResultToken().get() );
2211  }
2212  else
2213  {
2215  StackVar eNew = aNewResult.GetCellResultType();
2216  bChanged = (eOld != eNew ||
2217  (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) ||
2218  (eNew == svString && aResult.GetString() != aNewResult.GetString()));
2219 
2220  // #i102616# handle special cases of initial results after loading
2221  // (only if the sheet is still marked unchanged)
2222  if ( bChanged && !bContentChanged && rDocument.IsStreamValid(aPos.Tab()) )
2223  {
2224  if ((eOld == svUnknown && (eNew == svError || (eNew == svDouble && aNewResult.GetDouble() == 0.0))) ||
2225  ((eOld == svHybridCell) &&
2226  eNew == svString && aResult.GetString() == aNewResult.GetString()) ||
2227  (eOld == svDouble && eNew == svDouble &&
2228  rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble())))
2229  {
2230  // no change, see above
2231  }
2232  else
2233  bContentChanged = true;
2234  }
2235 
2236  aResult.Assign( aNewResult);
2237  }
2238 
2239  // Precision as shown?
2240  if ( aResult.IsValue() && pInterpreter->GetError() == FormulaError::NONE
2242  && nFormatType != SvNumFormatType::DATE
2243  && nFormatType != SvNumFormatType::TIME
2244  && nFormatType != SvNumFormatType::DATETIME )
2245  {
2246  sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos );
2248  aResult.GetDouble(), nFormat, &rContext));
2249  }
2250  if (eTailParam == SCITP_NORMAL)
2251  {
2252  ResetDirty();
2253  }
2254  if( aResult.GetMatrix() )
2255  {
2256  // If the formula wasn't entered as a matrix formula, live on with
2257  // the upper left corner and let reference counting delete the matrix.
2260  }
2261  if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) )
2262  {
2263  // Coded double error may occur via filter import.
2265  aResult.SetResultError( nErr);
2266  bChanged = bContentChanged = true;
2267  }
2268 
2269  if (bContentChanged && rDocument.IsStreamValid(aPos.Tab()))
2270  {
2271  // pass bIgnoreLock=true, because even if called from pending row height update,
2272  // a changed result must still reset the stream flag
2273  rDocument.SetStreamValid(aPos.Tab(), false, true);
2274  }
2277 
2278  // FORCED cells also immediately tested for validity (start macro possibly)
2279 
2280  if ( pCode->IsRecalcModeForced() )
2281  {
2282  sal_uLong nValidation = rDocument.GetAttr(
2283  aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA )->GetValue();
2284  if ( nValidation )
2285  {
2286  const ScValidationData* pData = rDocument.GetValidationEntry( nValidation );
2287  ScRefCellValue aTmpCell(this);
2288  if ( pData && !pData->IsDataValid(aTmpCell, aPos))
2289  pData->DoCalcError( this );
2290  }
2291  }
2292 
2293  // Reschedule slows the whole thing down considerably, thus only execute on percent change
2295  {
2297  if (pProgress && pProgress->Enabled())
2298  {
2299  pProgress->SetStateCountDownOnPercent(
2301  }
2302 
2303  switch (pInterpreter->GetVolatileType())
2304  {
2306  // Volatile via built-in volatile functions. No actions needed.
2307  break;
2309  // The formula contains a volatile macro.
2313  break;
2315  if (pCode->IsRecalcModeAlways())
2316  {
2317  // The formula was previously volatile, but no more.
2320  }
2321  else
2322  {
2323  // non-volatile formula. End listening to the area in case
2324  // it's listening due to macro module change.
2326  }
2328  break;
2329  default:
2330  ;
2331  }
2332  }
2333  }
2334  else
2335  {
2336  // Cells with compiler errors should not be marked dirty forever
2337  OSL_ENSURE( pCode->GetCodeError() != FormulaError::NONE, "no RPN code and no errors ?!?!" );
2338  ResetDirty();
2339  }
2340 }
2341 
2343 {
2344  if( !pCode->GetCodeLen() )
2345  return;
2346 
2347  if ( !pCode->IsRecalcModeAlways() )
2349 
2350  std::unique_ptr<ScInterpreter> pScopedInterpreter;
2351  if (pInterpreter)
2352  pInterpreter->Init(this, aPos, *pCode);
2353  else
2354  {
2355  pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
2356  pInterpreter = pScopedInterpreter.get();
2357  }
2358 
2359  switch (pInterpreter->GetVolatileType())
2360  {
2362  // The formula contains a volatile macro.
2366  break;
2368  if (pCode->IsRecalcModeAlways())
2369  {
2370  // The formula was previously volatile, but no more.
2373  }
2374  else
2375  {
2376  // non-volatile formula. End listening to the area in case
2377  // it's listening due to macro module change.
2379  }
2381  break;
2382  default:
2383  ;
2384  }
2385 }
2386 
2387 void ScFormulaCell::SetCompile( bool bVal )
2388 {
2389  bCompile = bVal;
2390 }
2391 
2393 {
2395  if (pMat)
2396  pMat->SetMatColsRows( nCols, nRows );
2397  else if (nCols || nRows)
2398  {
2399  aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2400  // Setting the new token actually forces an empty result at this top
2401  // left cell, so have that recalculated.
2402  SetDirty();
2403  }
2404 }
2405 
2406 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2407 {
2409  if (pMat)
2410  pMat->GetMatColsRows( nCols, nRows);
2411  else
2412  {
2413  nCols = 0;
2414  nRows = 0;
2415  }
2416 }
2417 
2419 {
2420  bInChangeTrack = bVal;
2421 }
2422 
2423 void ScFormulaCell::Notify( const SfxHint& rHint )
2424 {
2425  if (rDocument.IsInDtorClear())
2426  return;
2427 
2428  const SfxHintId nHint = rHint.GetId();
2429  if (nHint == SfxHintId::ScReference)
2430  {
2431  const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2432 
2433  switch (rRefHint.getType())
2434  {
2436  {
2437  const sc::RefColReorderHint& rRefColReorder =
2438  static_cast<const sc::RefColReorderHint&>(rRefHint);
2439  if (!IsShared() || IsSharedTop())
2441  aPos, rRefColReorder.getTab(),
2442  rRefColReorder.getStartRow(),
2443  rRefColReorder.getEndRow(),
2444  rRefColReorder.getColMap());
2445  }
2446  break;
2448  {
2449  const sc::RefRowReorderHint& rRefRowReorder =
2450  static_cast<const sc::RefRowReorderHint&>(rRefHint);
2451  if (!IsShared() || IsSharedTop())
2453  aPos, rRefRowReorder.getTab(),
2454  rRefRowReorder.getStartColumn(),
2455  rRefRowReorder.getEndColumn(),
2456  rRefRowReorder.getRowMap());
2457  }
2458  break;
2460  {
2462  }
2463  break;
2465  {
2467  }
2468  break;
2469  default:
2470  ;
2471  }
2472 
2473  return;
2474  }
2475 
2477  return;
2478 
2479  if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
2480  return;
2481 
2482  bool bForceTrack = false;
2483  if ( nHint == SfxHintId::ScTableOpDirty )
2484  {
2485  bForceTrack = !bTableOpDirty;
2486  if ( !bTableOpDirty )
2487  {
2489  bTableOpDirty = true;
2490  }
2491  }
2492  else
2493  {
2494  bForceTrack = !bDirty;
2495  SetDirtyVar();
2496  }
2497  // Don't remove from FormulaTree to put in FormulaTrack to
2498  // put in FormulaTree again and again, only if necessary.
2499  // Any other means except ScRecalcMode::ALWAYS by which a cell could
2500  // be in FormulaTree if it would notify other cells through
2501  // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2502  // Yes. The new TableOpDirty made it necessary to have a
2503  // forced mode where formulas may still be in FormulaTree from
2504  // TableOpDirty but have to notify dependents for normal dirty.
2505  if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
2506  || pCode->IsRecalcModeAlways())
2507  && !rDocument.IsInFormulaTrack( this ) )
2509 }
2510 
2512 {
2513  switch (rQuery.getId())
2514  {
2516  {
2517  sc::RefQueryFormulaGroup& rRefQuery =
2518  static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2519  if (IsShared())
2520  rRefQuery.add(aPos);
2521  }
2522  break;
2523  default:
2524  ;
2525  }
2526 }
2527 
2528 void ScFormulaCell::SetDirty( bool bDirtyFlag )
2529 {
2530  if (IsInChangeTrack())
2531  return;
2532 
2534  {
2535  SetDirtyVar();
2536  rDocument.SetStreamValid(aPos.Tab(), false);
2537  return;
2538  }
2539 
2540  // Avoid multiple formula tracking in Load() and in CompileAll()
2541  // after CopyScenario() and CopyBlockFromClip().
2542  // If unconditional formula tracking is needed, set bDirty=false
2543  // before calling SetDirty(), for example in CompileTokenArray().
2544  if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
2545  {
2546  if( bDirtyFlag )
2547  SetDirtyVar();
2549 
2550  // While loading a document listeners have not been established yet.
2551  // Tracking would remove this cell from the FormulaTrack and add it to
2552  // the FormulaTree, once in there it would be assumed that its
2553  // dependents already had been tracked and it would be skipped on a
2554  // subsequent notify. Postpone tracking until all listeners are set.
2555  if (!rDocument.IsImportingXML())
2557  }
2558 
2559  rDocument.SetStreamValid(aPos.Tab(), false);
2560 }
2561 
2563 {
2564  bDirty = true;
2565  mbPostponedDirty = false;
2566  if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2567  {
2568  mxGroup->meCalcState = sc::GroupCalcEnabled;
2569  mxGroup->mbPartOfCycle = false;
2570  }
2571 
2572  // mark the sheet of this cell to be calculated
2573  //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
2574 }
2575 
2577 {
2578  bDirty = true;
2580  rDocument.PutInFormulaTree( this );
2581 }
2582 
2584 {
2585  bTableOpDirty = false;
2586 }
2587 
2589 {
2590  if ( IsInChangeTrack() )
2591  return;
2592 
2594  bTableOpDirty = true;
2595  else
2596  {
2597  if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
2598  {
2599  if ( !bTableOpDirty )
2600  {
2602  bTableOpDirty = true;
2603  }
2605  rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
2606  }
2607  }
2608 }
2609 
2611 {
2612  aResult.SetDouble(n);
2613 }
2614 
2616 {
2617  aResult.SetToken(pToken);
2618 }
2619 
2621 {
2622  return aResult.GetString();
2623 }
2624 
2626 {
2628 }
2629 
2631 {
2632  aResult.SetMatrix(nCols, nRows, pMat, pUL);
2633 }
2634 
2636 {
2637  /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2638  * used whether it is solely for transport of a simple result error and get
2639  * rid of that abuse. */
2640  pCode->SetCodeError( n );
2641  // Hard set errors are transported as result type value per convention,
2642  // e.g. via clipboard. ScFormulaResult::IsValue() and
2643  // ScFormulaResult::GetDouble() handle that.
2644  aResult.SetResultError( n );
2645 }
2646 
2648 {
2649  aResult.SetResultError( n );
2650 }
2651 
2653 {
2654  if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2655  SetDirtyVar();
2656  if ( nBits & ScRecalcMode::ONLOAD_ONCE )
2657  { // OnLoadOnce is used only to set Dirty after filter import.
2658  nBits = (nBits & ~ScRecalcMode::EMask) | ScRecalcMode::NORMAL;
2659  }
2660  pCode->AddRecalcMode( nBits );
2661 }
2662 
2664 {
2666 }
2667 
2669 {
2671 }
2672 
2674 {
2676 }
2677 
2678 void ScFormulaCell::SetHybridFormula( const OUString& r,
2679  const formula::FormulaGrammar::Grammar eGrammar )
2680 {
2681  aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2682 }
2683 
2684 const OUString& ScFormulaCell::GetHybridFormula() const
2685 {
2686  return aResult.GetHybridFormula();
2687 }
2688 
2689 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2690 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2691 {
2692  OUString aCellString;
2693 
2694  const Color* pColor;
2695 
2696  // Cell Text uses the Cell format while the URL uses
2697  // the default format for the type.
2698  const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
2699  SvNumberFormatter* pFormatter = rDocument.GetFormatTable();
2700 
2701  const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2702 
2703  if ( IsValue() )
2704  {
2705  double fValue = GetValue();
2706  pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2707  }
2708  else
2709  {
2710  aCellString = GetString().getString();
2711  pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2712  }
2714  if (xMat)
2715  {
2716  // determine if the matrix result is a string or value.
2717  if (!xMat->IsValue(0, 1))
2718  rURL = xMat->GetString(0, 1).getString();
2719  else
2720  pFormatter->GetOutputString(
2721  xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2722  }
2723 
2724  if(rURL.isEmpty())
2725  {
2726  if(IsValue())
2727  pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2728  else
2729  pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2730  }
2731 }
2732 
2734 {
2735  if (!IsValue())
2736  return aResult.IsMultiline();
2737  return false;
2738 }
2739 
2741 {
2742  return pCode && pCode->IsHyperLink();
2743 }
2744 
2745 std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2746 {
2747  OUString aCellText;
2748  OUString aURL;
2749  GetURLResult( aURL, aCellText );
2750 
2751  return ScEditUtil::CreateURLObjectFromURL( rDocument, aURL, aCellText );
2752 }
2753 
2755 {
2756  MaybeInterpret();
2758 }
2759 
2761 {
2762  MaybeInterpret();
2764 }
2765 
2767 {
2768  MaybeInterpret();
2769  return aResult.IsValue();
2770 }
2771 
2773 {
2774  MaybeInterpret();
2775  if (pCode->GetCodeError() != FormulaError::NONE)
2776  return false;
2777 
2778  return aResult.IsValueNoError();
2779 }
2780 
2782 {
2783  if (NeedsInterpret())
2784  // false if the cell is dirty & needs to be interpreted.
2785  return false;
2786 
2787  if (pCode->GetCodeError() != FormulaError::NONE)
2788  return false;
2789 
2790  return aResult.IsValueNoError();
2791 }
2792 
2794 {
2795  MaybeInterpret();
2796  return GetRawValue();
2797 }
2798 
2800 {
2801  MaybeInterpret();
2802  return GetRawString();
2803 }
2804 
2806 {
2807  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2808  aResult.GetResultError() == FormulaError::NONE)
2809  return aResult.GetDouble();
2810  return 0.0;
2811 }
2812 
2814 {
2815  if ((pCode->GetCodeError() == FormulaError::NONE) &&
2816  aResult.GetResultError() == FormulaError::NONE)
2817  return aResult.GetString();
2818 
2820 }
2821 
2823 {
2824  if ( rDocument.GetAutoCalc() )
2825  {
2827  // Was stored !bDirty but an accompanying matrix cell was bDirty?
2829  Interpret();
2830  }
2831  return aResult.GetMatrix().get();
2832 }
2833 
2834 bool ScFormulaCell::GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const
2835 {
2836  switch ( cMatrixFlag )
2837  {
2838  case ScMatrixMode::Formula :
2839  rPos = aPos;
2840  return true;
2842  {
2845  if( t )
2846  {
2847  ScSingleRefData& rRef = *t->GetSingleRef();
2848  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
2849  if (rDoc.ValidAddress(aAbs))
2850  {
2851  rPos = aAbs;
2852  return true;
2853  }
2854  }
2855  }
2856  break;
2857  default: break;
2858  }
2859  return false;
2860 }
2861 
2862 sc::MatrixEdge ScFormulaCell::GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const
2863 {
2864  switch ( cMatrixFlag )
2865  {
2866  case ScMatrixMode::Formula :
2868  {
2869  static thread_local SCCOL nC;
2870  static thread_local SCROW nR;
2871  ScAddress aOrg;
2872  if ( !GetMatrixOrigin( rDoc, aOrg ) )
2873  return sc::MatrixEdge::Nothing;
2874  if ( aOrg != rOrgPos )
2875  { // First time or a different matrix than last time.
2876  rOrgPos = aOrg;
2877  const ScFormulaCell* pFCell;
2879  pFCell = rDocument.GetFormulaCell(aOrg);
2880  else
2881  pFCell = this; // this ScMatrixMode::Formula
2882  // There's only one this, don't compare pFCell==this.
2883  if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2884  {
2885  pFCell->GetMatColsRows( nC, nR );
2886  if ( nC == 0 || nR == 0 )
2887  {
2888  // No ScMatrixFormulaCellToken available yet, calculate new.
2889  nC = 1;
2890  nR = 1;
2891  ScAddress aTmpOrg;
2892  ScFormulaCell* pCell;
2893  ScAddress aAdr( aOrg );
2894  aAdr.IncCol();
2895  bool bCont = true;
2896  do
2897  {
2898  pCell = rDocument.GetFormulaCell(aAdr);
2899  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2900  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2901  {
2902  nC++;
2903  aAdr.IncCol();
2904  }
2905  else
2906  bCont = false;
2907  } while ( bCont );
2908  aAdr = aOrg;
2909  aAdr.IncRow();
2910  bCont = true;
2911  do
2912  {
2913  pCell = rDocument.GetFormulaCell(aAdr);
2914  if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2915  pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2916  {
2917  nR++;
2918  aAdr.IncRow();
2919  }
2920  else
2921  bCont = false;
2922  } while ( bCont );
2923 
2924  const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2925  }
2926  }
2927  else
2928  {
2929 #if OSL_DEBUG_LEVEL > 0
2930  SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2932  << ", MatOrg: "
2934 #endif
2935  return sc::MatrixEdge::Nothing;
2936  }
2937  }
2938  // here we are, healthy and clean, somewhere in between
2939  SCCOL dC = aPos.Col() - aOrg.Col();
2940  SCROW dR = aPos.Row() - aOrg.Row();
2942  if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2943  {
2944  if ( dC == 0 )
2945  nEdges |= sc::MatrixEdge::Left;
2946  if ( dC+1 == nC )
2947  nEdges |= sc::MatrixEdge::Right;
2948  if ( dR == 0 )
2949  nEdges |= sc::MatrixEdge::Top;
2950  if ( dR+1 == nR )
2951  nEdges |= sc::MatrixEdge::Bottom;
2952  if ( nEdges == sc::MatrixEdge::Nothing )
2953  nEdges = sc::MatrixEdge::Inside;
2954  }
2955  else
2956  {
2957  SAL_WARN( "sc", "broken Matrix, Pos: "
2959  << ", MatOrg: "
2961  << ", MatCols: " << static_cast<sal_Int32>( nC )
2962  << ", MatRows: " << static_cast<sal_Int32>( nR )
2963  << ", DiffCols: " << static_cast<sal_Int32>( dC )
2964  << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2965  }
2966  return nEdges;
2967  }
2968  default:
2969  return sc::MatrixEdge::Nothing;
2970  }
2971 }
2972 
2974 {
2975  MaybeInterpret();
2976 
2977  /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2978  * and not also abused for signaling other error conditions we could bail
2979  * out even before attempting to interpret broken code. */
2980  FormulaError nErr = pCode->GetCodeError();
2981  if (nErr != FormulaError::NONE)
2982  return nErr;
2983  return aResult.GetResultError();
2984 }
2985 
2987 {
2988  FormulaError nErr = pCode->GetCodeError();
2989  if (nErr != FormulaError::NONE)
2990  return nErr;
2991  return aResult.GetResultError();
2992 }
2993 
2995 {
2996  MaybeInterpret();
2997 
2998  rErr = pCode->GetCodeError();
2999  if (rErr != FormulaError::NONE)
3000  return true;
3001 
3002  return aResult.GetErrorOrDouble(rErr, rVal);
3003 }
3004 
3006 {
3007  MaybeInterpret();
3008 
3009  FormulaError nErr = pCode->GetCodeError();
3010  if (nErr != FormulaError::NONE)
3011  return sc::FormulaResultValue(nErr);
3012 
3013  return aResult.GetResult();
3014 }
3015 
3017 {
3018  FormulaError nErr = pCode->GetCodeError();
3019  if (nErr != FormulaError::NONE)
3020  return sc::FormulaResultValue(nErr);
3021 
3022  return aResult.GetResult();
3023 }
3024 
3026 {
3029  if( p && !aIter.GetNextReferenceRPN() ) // only one!
3030  {
3031  SingleDoubleRefProvider aProv( *p );
3032  r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
3033  r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
3034  return true;
3035  }
3036  else
3037  return false;
3038 }
3039 
3040 bool
3042 {
3043  /* If there appears just one reference in the formula, it's the same
3044  as HasOneReference(). If there are more of them, they can denote
3045  one range if they are (sole) arguments of one function.
3046  Union of these references must form one range and their
3047  intersection must be empty set.
3048  */
3049 
3050  // Detect the simple case of exactly one reference in advance without all
3051  // overhead.
3052  // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
3053  // work again, where the function does not have only references.
3054  if (HasOneReference( rRange))
3055  return true;
3056 
3057  // Get first reference, if any
3059  formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
3060  if (pFirstReference)
3061  {
3062  // Collect all consecutive references, starting by the one
3063  // already found
3064  std::vector<formula::FormulaToken*> aReferences;
3065  aReferences.push_back(pFirstReference);
3066  FormulaToken* pToken(aIter.NextRPN());
3067  FormulaToken* pFunction(nullptr);
3068  while (pToken)
3069  {
3070  if (lcl_isReference(*pToken))
3071  {
3072  aReferences.push_back(pToken);
3073  pToken = aIter.NextRPN();
3074  }
3075  else
3076  {
3077  if (pToken->IsFunction())
3078  {
3079  pFunction = pToken;
3080  }
3081  break;
3082  }
3083  }
3084  if (pFunction && !aIter.GetNextReferenceRPN()
3085  && (pFunction->GetParamCount() == aReferences.size()))
3086  {
3087  return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
3088  }
3089  }
3090  return false;
3091 }
3092 
3094 {
3095  RelNameRef eRelNameRef = RelNameRef::NONE;
3098  while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3099  {
3100  switch (t->GetType())
3101  {
3102  case formula::svSingleRef:
3103  if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3104  eRelNameRef = RelNameRef::SINGLE;
3105  break;
3106  case formula::svDoubleRef:
3107  if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3108  // May originate from individual cell names, in which case
3109  // it needs recompilation.
3110  return RelNameRef::DOUBLE;
3111  /* TODO: have an extra flag at ScComplexRefData if range was
3112  * extended? or too cumbersome? might narrow recompilation to
3113  * only needed cases.
3114  * */
3115  break;
3116  default:
3117  ; // nothing
3118  }
3119  }
3120  return eRelNameRef;
3121 }
3122 
3124 {
3125  if (rCxt.meMode != URM_INSDEL)
3126  // Just in case...
3127  return false;
3128 
3129  if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3130  // No movement.
3131  return false;
3132 
3133  if (!rCxt.maRange.In(aPos))
3134  return false;
3135 
3136  // This formula cell itself is being shifted during cell range
3137  // insertion or deletion. Update its position.
3138  ScAddress aErrorPos( ScAddress::UNINITIALIZED );
3139  if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos))
3140  {
3141  assert(!"can't move ScFormulaCell");
3142  }
3143 
3144  return true;
3145 }
3146 
3147 namespace {
3148 
3152 bool checkCompileColRowName(
3153  const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3154  const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3155 {
3156  switch (rCxt.meMode)
3157  {
3158  case URM_INSDEL:
3159  {
3160  if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3161  return false;
3162 
3165  ScRangePairList* pColList = rDoc.GetColNameRanges();
3166  ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3167  while ((t = aIter.GetNextColRowName()) != nullptr)
3168  {
3169  ScSingleRefData& rRef = *t->GetSingleRef();
3170  if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3171  { // ColName
3172  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3173  ScRangePair* pR = pColList->Find( aAdr );
3174  if ( pR )
3175  { // defined
3176  if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3177  return true;
3178  }
3179  else
3180  { // on the fly
3181  if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3182  return true;
3183  }
3184  }
3185  if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3186  { // RowName
3187  ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3188  ScRangePair* pR = pRowList->Find( aAdr );
3189  if ( pR )
3190  { // defined
3191  if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3192  return true;
3193  }
3194  else
3195  { // on the fly
3196  if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3197  return true;
3198  }
3199  }
3200  }
3201  }
3202  break;
3203  case URM_MOVE:
3204  { // Recompile for Move/D&D when ColRowName was moved or this Cell
3205  // points to one and was moved.
3206  bool bMoved = (aPos != aOldPos);
3207  if (bMoved)
3208  return true;
3209 
3211  const formula::FormulaToken* t = aIter.GetNextColRowName();
3212  for (; t; t = aIter.GetNextColRowName())
3213  {
3214  const ScSingleRefData& rRef = *t->GetSingleRef();
3215  ScAddress aAbs = rRef.toAbs(rDoc, aPos);
3216  if (rDoc.ValidAddress(aAbs))
3217  {
3218  if (rCxt.maRange.In(aAbs))
3219  return true;
3220  }
3221  }
3222  }
3223  break;
3224  case URM_COPY:
3225  return bValChanged;
3226  default:
3227  ;
3228  }
3229 
3230  return false;
3231 }
3232 
3233 void setOldCodeToUndo(
3234  ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3235 {
3236  // Copy the cell to aUndoPos, which is its current position in the document,
3237  // so this works when UpdateReference is called before moving the cells
3238  // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3239  // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3240 
3241  // If there is already a formula cell in the undo document, don't overwrite it,
3242  // the first (oldest) is the important cell.
3243  if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3244  return;
3245 
3246  ScFormulaCell* pFCell =
3247  new ScFormulaCell(
3248  rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
3249 
3250  pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3251  rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
3252 }
3253 
3254 }
3255 
3257  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3258 {
3259  if (rCxt.meMode != URM_INSDEL)
3260  // Just in case...
3261  return false;
3262 
3263  bool bCellStateChanged = false;
3264  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3265  if ( pUndoCellPos )
3266  aUndoPos = *pUndoCellPos;
3267  ScAddress aOldPos( aPos );
3268  bCellStateChanged = UpdatePosOnShift(rCxt);
3269 
3270  // Check presence of any references or column row names.
3271  bool bHasRefs = pCode->HasReferences();
3272  bool bHasColRowNames = false;
3273  if (!bHasRefs)
3274  {
3275  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3276  bHasRefs = bHasColRowNames;
3277  }
3278  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3279 
3280  if (!bHasRefs && !bOnRefMove)
3281  // This formula cell contains no references, nor needs recalculating
3282  // on reference update. Bail out.
3283  return bCellStateChanged;
3284 
3285  std::unique_ptr<ScTokenArray> pOldCode;
3286  if (pUndoDoc)
3287  pOldCode = pCode->Clone();
3288 
3289  bool bValChanged = false;
3290  bool bRefModified = false;
3291  bool bRecompile = bCompile;
3292 
3293  if (bHasRefs)
3294  {
3295  // Update cell or range references.
3296  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3297  bRefModified = aRes.mbReferenceModified;
3298  bValChanged = aRes.mbValueChanged;
3299  if (aRes.mbNameModified)
3300  bRecompile = true;
3301  }
3302 
3303  if (bValChanged || bRefModified)
3304  bCellStateChanged = true;
3305 
3306  if (bOnRefMove)
3307  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3308  bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3309 
3310  bool bNewListening = false;
3311  bool bInDeleteUndo = false;
3312 
3313  if (bHasRefs)
3314  {
3315  // Upon Insert ColRowNames have to be recompiled in case the
3316  // insertion occurs right in front of the range.
3317  if (bHasColRowNames && !bRecompile)
3318  bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3319 
3320  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3321  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3322 
3323  // RelNameRefs are always moved
3324  bool bHasRelName = false;
3325  if (!bRecompile)
3326  {
3327  RelNameRef eRelNameRef = HasRelNameReference();
3328  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3329  bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3330  }
3331  // Reference changed and new listening needed?
3332  // Except in Insert/Delete without specialities.
3333  bNewListening = (bRefModified || bRecompile
3334  || (bValChanged && bInDeleteUndo) || bHasRelName);
3335 
3336  if ( bNewListening )
3337  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3338  }
3339 
3340  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3341  bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3342 
3343  if (pUndoDoc && (bValChanged || bOnRefMove))
3344  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3345 
3346  bCompile |= bRecompile;
3347  if (bCompile)
3348  {
3349  CompileTokenArray( bNewListening ); // no Listening
3350  bNeedDirty = true;
3351  }
3352 
3353  if ( !bInDeleteUndo )
3354  { // In ChangeTrack Delete-Reject listeners are established in
3355  // InsertCol/InsertRow
3356  if ( bNewListening )
3357  {
3358  // Inserts/Deletes re-establish listeners after all
3359  // UpdateReference calls.
3360  // All replaced shared formula listeners have to be
3361  // established after an Insert or Delete. Do nothing here.
3362  SetNeedsListening( true);
3363  }
3364  }
3365 
3366  if (bNeedDirty)
3367  { // Cut off references, invalid or similar?
3368  // Postpone SetDirty() until all listeners have been re-established in
3369  // Inserts/Deletes.
3370  mbPostponedDirty = true;
3371  }
3372 
3373  return bCellStateChanged;
3374 }
3375 
3377  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3378 {
3379  if (rCxt.meMode != URM_MOVE)
3380  return false;
3381 
3382  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3383  if ( pUndoCellPos )
3384  aUndoPos = *pUndoCellPos;
3385  ScAddress aOldPos( aPos );
3386 
3387  bool bCellInMoveTarget = rCxt.maRange.In(aPos);
3388 
3389  if ( bCellInMoveTarget )
3390  {
3391  // The cell is being moved or copied to a new position. I guess the
3392  // position has been updated prior to this call? Determine
3393  // its original position before the move which will be used to adjust
3394  // relative references later.
3395  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3396  }
3397 
3398  // Check presence of any references or column row names.
3399  bool bHasRefs = pCode->HasReferences();
3400  bool bHasColRowNames = false;
3401  if (!bHasRefs)
3402  {
3403  bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3404  bHasRefs = bHasColRowNames;
3405  }
3406  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3407 
3408  if (!bHasRefs && !bOnRefMove)
3409  // This formula cell contains no references, nor needs recalculating
3410  // on reference update. Bail out.
3411  return false;
3412 
3413  bool bCellStateChanged = false;
3414  std::unique_ptr<ScTokenArray> pOldCode;
3415  if (pUndoDoc)
3416  pOldCode = pCode->Clone();
3417 
3418  bool bValChanged = false;
3419  bool bRefModified = false;
3420 
3421  if (bHasRefs)
3422  {
3423  // Update cell or range references.
3424  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3425  bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3426  bValChanged = aRes.mbValueChanged;
3427  if (aRes.mbNameModified)
3428  // Re-compile to get the RPN token regenerated to reflect updated names.
3429  bCompile = true;
3430  }
3431 
3432  if (bValChanged || bRefModified)
3433  bCellStateChanged = true;
3434 
3435  if (bOnRefMove)
3436  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3437  bOnRefMove = (bValChanged || (aPos != aOldPos));
3438 
3439  bool bColRowNameCompile = false;
3440  bool bHasRelName = false;
3441  bool bNewListening = false;
3442  bool bInDeleteUndo = false;
3443 
3444  if (bHasRefs)
3445  {
3446  // Upon Insert ColRowNames have to be recompiled in case the
3447  // insertion occurs right in front of the range.
3448  if (bHasColRowNames)
3449  bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3450 
3451  ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3452  bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3453 
3454  // RelNameRefs are always moved
3455  RelNameRef eRelNameRef = HasRelNameReference();
3456  bHasRelName = (eRelNameRef != RelNameRef::NONE);
3457  bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3458  // Reference changed and new listening needed?
3459  // Except in Insert/Delete without specialties.
3460  bNewListening = (bRefModified || bColRowNameCompile
3461  || bValChanged || bHasRelName)
3462  // #i36299# Don't duplicate action during cut&paste / drag&drop
3463  // on a cell in the range moved, start/end listeners is done
3464  // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3465  && !(rDocument.IsInsertingFromOtherDoc() && rCxt.maRange.In(aPos));
3466 
3467  if ( bNewListening )
3468  EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3469  }
3470 
3471  bool bNeedDirty = false;
3472  // NeedDirty for changes except for Copy and Move/Insert without RelNames
3473  if ( bRefModified || bColRowNameCompile ||
3474  (bValChanged && bHasRelName ) || bOnRefMove)
3475  bNeedDirty = true;
3476 
3477  if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3478  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3479 
3480  bValChanged = false;
3481 
3482  bCompile = (bCompile || bValChanged || bColRowNameCompile);
3483  if ( bCompile )
3484  {
3485  CompileTokenArray( bNewListening ); // no Listening
3486  bNeedDirty = true;
3487  }
3488 
3489  if ( !bInDeleteUndo )
3490  { // In ChangeTrack Delete-Reject listeners are established in
3491  // InsertCol/InsertRow
3492  if ( bNewListening )
3493  {
3495  }
3496  }
3497 
3498  if (bNeedDirty)
3499  { // Cut off references, invalid or similar?
3500  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3501  SetDirty();
3502  }
3503 
3504  return bCellStateChanged;
3505 }
3506 
3508  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3509 {
3510  if (rCxt.meMode != URM_COPY)
3511  return false;
3512 
3513  ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3514  if ( pUndoCellPos )
3515  aUndoPos = *pUndoCellPos;
3516  ScAddress aOldPos( aPos );
3517 
3518  if (rCxt.maRange.In(aPos))
3519  {
3520  // The cell is being moved or copied to a new position. I guess the
3521  // position has been updated prior to this call? Determine
3522  // its original position before the move which will be used to adjust
3523  // relative references later.
3524  aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3525  }
3526 
3527  // Check presence of any references or column row names.
3528  bool bHasRefs = pCode->HasReferences();
3529  bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3530  bHasRefs = bHasRefs || bHasColRowNames;
3531  bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3532 
3533  if (!bHasRefs && !bOnRefMove)
3534  // This formula cell contains no references, nor needs recalculating
3535  // on reference update. Bail out.
3536  return false;
3537 
3538  std::unique_ptr<ScTokenArray> pOldCode;
3539  if (pUndoDoc)
3540  pOldCode = pCode->Clone();
3541 
3542  if (bOnRefMove)
3543  // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3544  bOnRefMove = (aPos != aOldPos);
3545 
3546  bool bNeedDirty = bOnRefMove;
3547 
3548  if (pUndoDoc && bOnRefMove)
3549  setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3550 
3551  if (bCompile)
3552  {
3553  CompileTokenArray(); // no Listening
3554  bNeedDirty = true;
3555  }
3556 
3557  if (bNeedDirty)
3558  { // Cut off references, invalid or similar?
3559  sc::AutoCalcSwitch aACSwitch(rDocument, false);
3560  SetDirty();
3561  }
3562 
3563  return false;
3564 }
3565 
3567  const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3568 {
3569  if (rDocument.IsClipOrUndo())
3570  return false;
3571 
3572  if (mxGroup && mxGroup->mpTopCell != this)
3573  {
3574  // This is not a top cell of a formula group. Don't update references.
3575 
3576  switch (rCxt.meMode)
3577  {
3578  case URM_INSDEL:
3579  return UpdatePosOnShift(rCxt);
3580  break;
3581  default:
3582  ;
3583  }
3584  return false;
3585  }
3586 
3587  switch (rCxt.meMode)
3588  {
3589  case URM_INSDEL:
3590  return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3591  case URM_MOVE:
3592  return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3593  case URM_COPY:
3594  return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3595  default:
3596  ;
3597  }
3598 
3599  return false;
3600 }
3601 
3603 {
3604  // Adjust tokens only when it's not grouped or grouped top cell.
3605  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3606  bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3608  {
3609  if (bPosChanged)
3610  aPos.IncTab(rCxt.mnSheets);
3611 
3612  return;
3613  }
3614 
3616  ScAddress aOldPos = aPos;
3617  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3618  if (bPosChanged)
3619  aPos.IncTab(rCxt.mnSheets);
3620 
3621  if (!bAdjustCode)
3622  return;
3623 
3625  if (aRes.mbNameModified)
3626  // Re-compile after new sheet(s) have been inserted.
3627  bCompile = true;
3628 
3629  // no StartListeningTo because the new sheets have not been inserted yet.
3630 }
3631 
3633 {
3634  // Adjust tokens only when it's not grouped or grouped top cell.
3635  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3636  bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3638  {
3639  if (bPosChanged)
3640  aPos.IncTab(-1*rCxt.mnSheets);
3641  return;
3642  }
3643 
3645  // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3646  ScAddress aOldPos = aPos;
3647  if (bPosChanged)
3648  aPos.IncTab(-1*rCxt.mnSheets);
3649 
3650  if (!bAdjustCode)
3651  return;
3652 
3653  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos);
3654  if (aRes.mbNameModified)
3655  // Re-compile after sheet(s) have been deleted.
3656  bCompile = true;
3657 }
3658 
3660 {
3661  // Adjust tokens only when it's not grouped or grouped top cell.
3662  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3663 
3665  {
3666  aPos.SetTab(nTabNo);
3667  return;
3668  }
3669 
3671  ScAddress aOldPos = aPos;
3672  // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3673  aPos.SetTab(nTabNo);
3674 
3675  // no StartListeningTo because pTab[nTab] not yet correct!
3676 
3677  if (!bAdjustCode)
3678  return;
3679 
3680  sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos);
3681  if (aRes.mbNameModified)
3682  // Re-compile after sheet(s) have been deleted.
3683  bCompile = true;
3684 }
3685 
3687 {
3688  if (rDocument.IsClipOrUndo())
3689  return;
3690 
3691  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3692  if (!bAdjustCode)
3693  return;
3694 
3697  while (p)
3698  {
3699  ScSingleRefData& rRef1 = *p->GetSingleRef();
3700  if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3701  rRef1.IncTab(1);
3702  if (p->GetType() == formula::svDoubleRef)
3703  {
3704  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3705  if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3706  rRef2.IncTab(1);
3707  }
3708  p = aIter.GetNextReferenceRPN();
3709  }
3710 }
3711 
3713 {
3714  if (rDocument.IsClipOrUndo())
3715  return false;
3716 
3717  bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3718  if (!bAdjustCode)
3719  return false;
3720 
3721  bool bRet = false;
3724  while (p)
3725  {
3726  ScSingleRefData& rRef1 = *p->GetSingleRef();
3727  if (!rRef1.IsTabRel())
3728  {
3729  if (nTable != rRef1.Tab())
3730  bRet = true;
3731  else if (nTable != aPos.Tab())
3732  rRef1.SetAbsTab(aPos.Tab());
3733  }
3734  if (p->GetType() == formula::svDoubleRef)
3735  {
3736  ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3737  if (!rRef2.IsTabRel())
3738  {
3739  if(nTable != rRef2.Tab())
3740  bRet = true;
3741  else if (nTable != aPos.Tab())
3742  rRef2.SetAbsTab(aPos.Tab());
3743  }
3744  }
3745  p = aIter.GetNextReferenceRPN();
3746  }
3747  return bRet;
3748 }
3749 
3750 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3751 {
3752  if ( bForceIfNameInUse && !bCompile )
3754  if ( bCompile )
3755  pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3757 }
3758 
3759 // Reference transposition is only called in Clipboard Document
3761 {
3762  bool bFound = false;
3765  while ( ( t = aIter.GetNextReference() ) != nullptr )
3766  {
3767  ScSingleRefData& rRef1 = *t->GetSingleRef();
3768  if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3769  {
3770  bool bDouble = (t->GetType() == formula::svDoubleRef);
3771  ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3772  if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3773  {
3774  SCCOLROW nTemp;
3775 
3776  nTemp = rRef1.Col();
3777  rRef1.SetRelCol(rRef1.Row());
3778  rRef1.SetRelRow(nTemp);
3779 
3780  if ( bDouble )
3781  {
3782  nTemp = rRef2.Col();
3783  rRef2.SetRelCol(rRef2.Row());
3784  rRef2.SetRelRow(nTemp);
3785  }
3786 
3787  bFound = true;
3788  }
3789  }
3790  }
3791 
3792  if (bFound)
3793  bCompile = true;
3794 }
3795 
3796 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3797  ScDocument* pUndoDoc )
3798 {
3800 
3801  ScAddress aOldPos = aPos;
3802  bool bPosChanged = false; // Whether this cell has been moved
3803 
3804  ScRange aDestRange( rDest, ScAddress(
3805  static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3806  static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3807  rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3808  if ( aDestRange.In( aOldPos ) )
3809  {
3810  // Count back Positions
3811  SCCOL nRelPosX = aOldPos.Col();
3812  SCROW nRelPosY = aOldPos.Row();
3813  SCTAB nRelPosZ = aOldPos.Tab();
3814  ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
3815  aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3816  bPosChanged = true;
3817  }
3818 
3819  std::unique_ptr<ScTokenArray> pOld;
3820  if (pUndoDoc)
3821  pOld = pCode->Clone();
3822  bool bRefChanged = false;
3823 
3826  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3827  {
3828  if( t->GetOpCode() == ocName )
3829  {
3831  if (pName && pName->IsModified())
3832  bRefChanged = true;
3833  }
3834  else if( t->GetType() != svIndex )
3835  {
3836  SingleDoubleRefModifier aMod(*t);
3837  ScComplexRefData& rRef = aMod.Ref();
3838  ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
3839  bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3840  if (bMod)
3841  {
3842  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3843  bRefChanged = true;
3844  }
3845  }
3846  }
3847 
3848  if (bRefChanged)
3849  {
3850  if (pUndoDoc)
3851  {
3852  ScFormulaCell* pFCell = new ScFormulaCell(
3853  *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
3854 
3855  pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3856  pUndoDoc->SetFormulaCell(aPos, pFCell);
3857  }
3858 
3859  bCompile = true;
3860  CompileTokenArray(); // also call StartListeningTo
3861  SetDirty();
3862  }
3863  else
3864  StartListeningTo( rDocument ); // Listener as previous
3865 }
3866 
3867 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3868 {
3870 
3871  bool bRefChanged = false;
3872 
3875 
3876  while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3877  {
3878  if( t->GetOpCode() == ocName )
3879  {
3881  if (pName && pName->IsModified())
3882  bRefChanged = true;
3883  }
3884  else if( t->GetType() != svIndex )
3885  {
3886  SingleDoubleRefModifier aMod(*t);
3887  ScComplexRefData& rRef = aMod.Ref();
3888  ScRange aAbs = rRef.toAbs(rDocument, aPos);
3889  bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3890  if (bMod)
3891  {
3892  rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
3893  bRefChanged = true;
3894  }
3895  }
3896  }
3897 
3898  if (bRefChanged)
3899  {
3900  bCompile = true;
3901  CompileTokenArray(); // Also call StartListeningTo
3902  SetDirty();
3903  }
3904  else
3905  StartListeningTo( rDocument ); // Listener as previous
3906 }
3907 
3908 // See also ScDocument::FindRangeNamesReferencingSheet()
3909 static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
3910  int nRecursion)
3911 {
3912  FormulaTokenArrayPlainIterator aIter(*pCode);
3913  for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3914  {
3915  if (p->GetOpCode() == ocName)
3916  {
3917  sal_uInt16 nTokenIndex = p->GetIndex();
3918  SCTAB nTab = p->GetSheet();
3919  rIndexes.setUpdatedName( nTab, nTokenIndex);
3920 
3921  if (nRecursion < 126) // whatever... 42*3
3922  {
3923  ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3924  if (pSubName)
3925  lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
3926  }
3927  }
3928  }
3929 }
3930 
3932 {
3933  lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
3934 }
3935 
3937 {
3938  bChanged = b;
3939 }
3940 
3941 void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3942 {
3943  assert(!mxGroup); // Don't call this if it's shared.
3944  delete pCode;
3945  pCode = pNew.release(); // takes ownership.
3946 }
3947 
3948 void ScFormulaCell::SetRunning( bool bVal )
3949 {
3950  bRunning = bVal;
3951 }
3952 
3954 {
3956  for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3957  {
3958  OpCode eOp = p->GetOpCode();
3959  if ( eOp == ocDBArea || eOp == ocTableRef )
3960  {
3961  bCompile = true;
3962  CompileTokenArray(rCxt);
3963  SetDirty();
3964  break;
3965  }
3966  }
3967 }
3968 
3970 {
3972  for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3973  {
3974  if ( p->GetOpCode() == ocColRowName )
3975  {
3976  bCompile = true;
3977  CompileTokenArray(rCxt);
3978  SetDirty();
3979  break;
3980  }
3981  }
3982 }
3983 
3988 
3990 {
3991  if (mxGroup)
3992  {
3993  // You can't create a new group if the cell is already a part of a group.
3994  // Is this a sign of some inconsistent or incorrect data structures? Or normal?
3995  SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
3996  return ScFormulaCellGroupRef();
3997  }
3998 
3999  mxGroup.reset(new ScFormulaCellGroup);
4000  mxGroup->mpTopCell = this;
4001  mxGroup->mbInvariant = bInvariant;
4002  mxGroup->mnLength = nLen;
4003  mxGroup->mpCode.reset(pCode); // Move this to the shared location.
4004  return mxGroup;
4005 }
4006 
4008 {
4009  if (!xRef)
4010  {
4011  // Make this cell a non-grouped cell.
4012  if (mxGroup)
4013  pCode = mxGroup->mpCode->Clone().release();
4014 
4015  mxGroup = xRef;
4016  return;
4017  }
4018 
4019  // Group object has shared token array.
4020  if (!mxGroup)
4021  // Currently not shared. Delete the existing token array first.
4022  delete pCode;
4023 
4024  mxGroup = xRef;
4025  pCode = mxGroup->mpCode.get();
4026  mxGroup->mnWeight = 0; // invalidate
4027 }
4028 
4030 {
4031  // no Matrix formulae yet.
4032  if ( GetMatrixFlag() != ScMatrixMode::NONE )
4033  return NotEqual;
4034 
4035  // are these formulas at all similar ?
4036  if ( GetHash() != rOther.GetHash() )
4037  return NotEqual;
4038 
4039  if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4040  return NotEqual;
4041 
4042  FormulaToken **pThis = pCode->GetCode();
4043  sal_uInt16 nThisLen = pCode->GetCodeLen();
4044  FormulaToken **pOther = rOther.pCode->GetCode();
4045  sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4046 
4047  if ( !pThis || !pOther )
4048  {
4049  // Error: no compiled code for cells !"
4050  return NotEqual;
4051  }
4052 
4053  if ( nThisLen != nOtherLen )
4054  return NotEqual;
4055 
4056  // No tokens can be an error cell so check error code, otherwise we could
4057  // end up with a series of equal error values instead of individual error
4058  // values. Also if for any reason different errors are set even if all
4059  // tokens are equal, the cells are not equal.
4060  if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4061  return NotEqual;
4062 
4063  bool bInvariant = true;
4064 
4065  // check we are basically the same function
4066  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4067  {
4068  formula::FormulaToken *pThisTok = pThis[i];
4069  formula::FormulaToken *pOtherTok = pOther[i];
4070 
4071  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4072  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4073  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4074  {
4075  // Incompatible type, op-code or param counts.
4076  return NotEqual;
4077  }
4078 
4079  switch (pThisTok->GetType())
4080  {
4081  case formula::svMatrix:
4084  // Ignoring matrix and external references for now.
4085  return NotEqual;
4086 
4087  case formula::svSingleRef:
4088  {
4089  // Single cell reference.
4090  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4091  if (rRef != *pOtherTok->GetSingleRef())
4092  return NotEqual;
4093 
4094  if (rRef.IsRowRel())
4095  bInvariant = false;
4096  }
4097  break;
4098  case formula::svDoubleRef:
4099  {
4100  // Range reference.
4101  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4102  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4103  if (rRef1 != *pOtherTok->GetSingleRef())
4104  return NotEqual;
4105 
4106  if (rRef2 != *pOtherTok->GetSingleRef2())
4107  return NotEqual;
4108 
4109  if (rRef1.IsRowRel())
4110  bInvariant = false;
4111 
4112  if (rRef2.IsRowRel())
4113  bInvariant = false;
4114  }
4115  break;
4116  case formula::svDouble:
4117  {
4118  if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4119  return NotEqual;
4120  }
4121  break;
4122  case formula::svString:
4123  {
4124  if(pThisTok->GetString() != pOtherTok->GetString())
4125  return NotEqual;
4126  }
4127  break;
4128  case formula::svIndex:
4129  {
4130  if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4131  return NotEqual;
4132  }
4133  break;
4134  case formula::svByte:
4135  {
4136  if(pThisTok->GetByte() != pOtherTok->GetByte())
4137  return NotEqual;
4138  }
4139  break;
4140  case formula::svExternal:
4141  {
4142  if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4143  return NotEqual;
4144 
4145  if (pThisTok->GetByte() != pOtherTok->GetByte())
4146  return NotEqual;
4147  }
4148  break;
4149  case formula::svError:
4150  {
4151  if (pThisTok->GetError() != pOtherTok->GetError())
4152  return NotEqual;
4153  }
4154  break;
4155  default:
4156  ;
4157  }
4158  }
4159 
4160  // If still the same, check lexical names as different names may result in
4161  // identical RPN code.
4162 
4163  pThis = pCode->GetArray();
4164  nThisLen = pCode->GetLen();
4165  pOther = rOther.pCode->GetArray();
4166  nOtherLen = rOther.pCode->GetLen();
4167 
4168  if ( !pThis || !pOther )
4169  {
4170  // Error: no code for cells !"
4171  return NotEqual;
4172  }
4173 
4174  if ( nThisLen != nOtherLen )
4175  return NotEqual;
4176 
4177  for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4178  {
4179  formula::FormulaToken *pThisTok = pThis[i];
4180  formula::FormulaToken *pOtherTok = pOther[i];
4181 
4182  if ( pThisTok->GetType() != pOtherTok->GetType() ||
4183  pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4184  pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4185  {
4186  // Incompatible type, op-code or param counts.
4187  return NotEqual;
4188  }
4189 
4190  switch (pThisTok->GetType())
4191  {
4192  // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4193  // resulting in identical RPN references that could lead to creating
4194  // a formula group from formulas that should not be merged into a group,
4195  // so check also the formula itself.
4196  case formula::svSingleRef:
4197  {
4198  // Single cell reference.
4199  const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4200  if (rRef != *pOtherTok->GetSingleRef())
4201  return NotEqual;
4202 
4203  if (rRef.IsRowRel())
4204  bInvariant = false;
4205  }
4206  break;
4207  case formula::svDoubleRef:
4208  {
4209  // Range reference.
4210  const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4211  const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4212  if (rRef1 != *pOtherTok->GetSingleRef())
4213  return NotEqual;
4214 
4215  if (rRef2 != *pOtherTok->GetSingleRef2())
4216  return NotEqual;
4217 
4218  if (rRef1.IsRowRel())
4219  bInvariant = false;
4220 
4221  if (rRef2.IsRowRel())
4222  bInvariant = false;
4223  }
4224  break;
4225  // All index tokens are names. Different categories already had
4226  // different OpCode values.
4227  case formula::svIndex:
4228  {
4229  if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4230  return NotEqual;
4231  switch (pThisTok->GetOpCode())
4232  {
4233  case ocTableRef:
4234  // nothing, sheet value assumed as -1, silence
4235  // ScTableRefToken::GetSheet() SAL_WARN about
4236  // unhandled
4237  ;
4238  break;
4239  default: // ocName, ocDBArea
4240  if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4241  return NotEqual;
4242  }
4243  }
4244  break;
4245  default:
4246  ;
4247  }
4248  }
4249 
4250  return bInvariant ? EqualInvariant : EqualRelativeRef;
4251 }
4252 
4253 namespace {
4254 
4255 // Split N into optimally equal-sized pieces, each not larger than K.
4256 // Return value P is number of pieces. A returns the number of pieces
4257 // one larger than N/P, 0..P-1.
4258 
4259 int splitup(int N, int K, int& A)
4260 {
4261  assert(N > 0);
4262  assert(K > 0);
4263 
4264  A = 0;
4265 
4266  if (N <= K)
4267  return 1;
4268 
4269  const int ideal_num_parts = N / K;
4270  if (ideal_num_parts * K == N)
4271  return ideal_num_parts;
4272 
4273  const int num_parts = ideal_num_parts + 1;
4274  const int nominal_part_size = N / num_parts;
4275 
4276  A = N - num_parts * nominal_part_size;
4277 
4278  return num_parts;
4279 }
4280 
4281 struct ScDependantsCalculator
4282 {
4283  ScDocument& mrDoc;
4284  const ScTokenArray& mrCode;
4285  const ScFormulaCellGroupRef& mxGroup;
4286  const SCROW mnLen;
4287  const ScAddress& mrPos;
4288  const bool mFromFirstRow;
4289  const SCROW mnStartOffset;
4290  const SCROW mnEndOffset;
4291  const SCROW mnSpanLen;
4292 
4293  ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4294  const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4295  mrDoc(rDoc),
4296  mrCode(rCode),
4297  mxGroup(rCell.GetCellGroup()),
4298  mnLen(mxGroup->mnLength),
4299  mrPos(rPos),
4300  // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4301  // only from further rows. This data fetching could also lead to Interpret() calls, so
4302  // in OpenCL mode the formula in practice depends on those cells too.
4303  mFromFirstRow(fromFirstRow),
4304  mnStartOffset(nStartOffset),
4305  mnEndOffset(nEndOffset),
4306  mnSpanLen(nEndOffset - nStartOffset + 1)
4307  {
4308  }
4309 
4310  // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4311  // (note already modified a bit, mFromFirstRow)
4312 
4313  // I think what this function does is to check whether the relative row reference nRelRow points
4314  // to a row that is inside the range of rows covered by the formula group.
4315 
4316  bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4317  {
4318  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4319  return false;
4320 
4321  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4322 
4323  if (nRelRow <= 0)
4324  {
4325  SCROW nTest = nEndRow;
4326  nTest += nRelRow;
4327  if (nTest >= mrPos.Row())
4328  return true;
4329  }
4330  else
4331  {
4332  SCROW nTest = mrPos.Row(); // top row.
4333  nTest += nRelRow;
4334  if (nTest <= nEndRow)
4335  return true;
4336  // If pointing below the formula, it's always included if going from first row.
4337  if (mFromFirstRow)
4338  return true;
4339  }
4340 
4341  return false;
4342  }
4343 
4344  // FIXME: another copy-paste
4345 
4346  // And this correspondingly checks whether an absolute row is inside the range of rows covered
4347  // by the formula group.
4348 
4349  bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4350  {
4351  if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4352  return false;
4353 
4354  SCROW nEndRow = mrPos.Row() + mnLen - 1;
4355 
4356  if (rRefPos.Row() < mrPos.Row())
4357  return false;
4358 
4359  // If pointing below the formula, it's always included if going from first row.
4360  if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4361  return false;
4362 
4363  return true;
4364  }
4365 
4366  // Checks if the doubleref engulfs all of formula group cells
4367  // Note : does not check if there is a partial overlap, that can be done by calling
4368  // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4369  bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4370  {
4371  if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4372  || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4373  {
4374  return false;
4375  }
4376 
4377  SCROW nStartRow = mrPos.Row();
4378  SCROW nEndRow = nStartRow + mnLen - 1;
4379  SCROW nRefStartRow = rAbs.aStart.Row();
4380  SCROW nRefEndRow = rAbs.aEnd.Row();
4381 
4382  if (bIsRef1RowRel && bIsRef2RowRel &&
4383  ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4384  ((nRefStartRow + mnLen - 1) <= nStartRow &&
4385  (nRefEndRow + mnLen - 1) >= nEndRow)))
4386  return true;
4387 
4388  if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4389  (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4390  return true;
4391 
4392  if (!bIsRef2RowRel &&
4393  nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4394  return true;
4395 
4396  // If going from first row, the referenced range must be entirely above the formula,
4397  // otherwise the formula would be included.
4398  if (mFromFirstRow && nRefEndRow >= nStartRow)
4399  return true;
4400 
4401  return false;
4402  }
4403 
4404  // FIXME: another copy-paste
4405  SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4406  {
4407  SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4408  nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4409  if (nLastRow < (nRow + nRowLen - 1))
4410  {
4411  // This can end up negative! Was that the original intent, or
4412  // is it accidental? Was it not like that originally but the
4413  // surrounding conditions changed?
4414  nRowLen = nLastRow - nRow + 1;
4415  // Anyway, let's assume it doesn't make sense to return a
4416  // negative or zero value here.
4417  if (nRowLen <= 0)
4418  nRowLen = 1;
4419  }
4420  else if (nLastRow == 0)
4421  // Column is empty.
4422  nRowLen = 1;
4423 
4424  return nRowLen;
4425  }
4426 
4427  bool DoIt()
4428  {
4429  // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4430 
4431  ScRangeList aRangeList;
4432 
4433  // Self references should be checked by considering the entire formula-group not just the provided span.
4434  bool bHasSelfReferences = false;
4435  bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4436 
4437  FormulaToken** pRPNArray = mrCode.GetCode();
4438  sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4439  for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4440  {
4441  auto p = pRPNArray[nTokenIdx];
4442  if (!bInDocShellRecalc)
4443  {
4444  // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4445  // of the result of the condition expression.
4446  // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4447  // in the document. So lets disable threading and stop dependency evaluation if
4448  // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4449  // for formulae with IF/IFS/SWITCH
4450  OpCode nOpCode = p->GetOpCode();
4451  if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4452  return false;
4453  }
4454 
4455  switch (p->GetType())
4456  {
4457  case svSingleRef:
4458  {
4459  ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4460  if( aRef.IsDeleted())
4461  return false;
4462  ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
4463 
4464  if (!mrDoc.TableExists(aRefPos.Tab()))
4465  return false; // or true?
4466 
4467  if (aRef.IsRowRel())
4468  {
4469  if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4470  {
4471  bHasSelfReferences = true;
4472  continue;
4473  }
4474 
4475  // Trim data array length to actual data range.
4476  SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4477 
4478  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4479  aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4480  }
4481  else
4482  {
4483  if (isSelfReferenceAbsolute(aRefPos))
4484  {
4485  bHasSelfReferences = true;
4486  continue;
4487  }
4488 
4489  aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4490  }
4491  }
4492  break;
4493  case svDoubleRef:
4494  {
4495  ScComplexRefData aRef = *p->GetDoubleRef();
4496  if( aRef.IsDeleted())
4497  return false;
4498  ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
4499 
4500  // Multiple sheet
4501  if (aRef.Ref1.Tab() != aRef.Ref2.Tab())
4502  return false;
4503 
4504  bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4505  // Check for self reference.
4506  if (bIsRef1RowRel)
4507  {
4508  if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4509  {
4510  bHasSelfReferences = true;
4511  continue;
4512  }
4513  }
4514  else if (isSelfReferenceAbsolute(aAbs.aStart))
4515  {
4516  bHasSelfReferences = true;
4517  continue;
4518  }
4519 
4520  bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4521  if (bIsRef2RowRel)
4522  {
4523  if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4524  {
4525  bHasSelfReferences = true;
4526  continue;
4527  }
4528  }
4529  else if (isSelfReferenceAbsolute(aAbs.aEnd))
4530  {
4531  bHasSelfReferences = true;
4532  continue;
4533  }
4534 
4535  if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4536  {
4537  bHasSelfReferences = true;
4538  continue;
4539  }
4540 
4541  // The first row that will be referenced through the doubleref.
4542  SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4543  // The last row that will be referenced through the doubleref.
4544  SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4545  // Number of rows to be evaluated from nFirstRefRow.
4546  SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4547  assert(nArrayLength > 0);
4548 
4549  // Trim trailing empty rows.
4550  nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4551 
4552  aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4553  aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4554  }
4555  break;
4556  default:
4557  break;
4558  }
4559  }
4560 
4561  // Compute dependencies irrespective of the presence of any self references.
4562  // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4563  // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4564  for (size_t i = 0; i < aRangeList.size(); ++i)
4565  {
4566  const ScRange & rRange = aRangeList[i];
4567  assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4568  for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4569  {
4570  SCROW nStartRow = rRange.aStart.Row();
4571  SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4572  if( mFromFirstRow )
4573  { // include also all previous rows
4574  nLength += nStartRow;
4575  nStartRow = 0;
4576  }
4577  if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4578  nLength, mxGroup))
4579  return false;
4580  }
4581  }
4582 
4583  if (bHasSelfReferences)
4584  mxGroup->mbPartOfCycle = true;
4585 
4586  return !bHasSelfReferences;
4587  }
4588 };
4589 
4590 } // anonymous namespace
4591 
4592 bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset)
4593 {
4594  if (!mxGroup || !pCode)
4595  return false;
4596 
4597  auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
4598  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4599 
4600  if (mxGroup->mbPartOfCycle)
4601  {
4602  aScope.addMessage("This formula-group is part of a cycle");
4603  return false;
4604  }
4605 
4606  if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4607  {
4608  aScope.addMessage("group calc disabled");
4609  return false;
4610  }
4611 
4612  // Use SC_TEST_CALCULATION=opencl/threads to force calculation e.g. for unittests
4614  if (forceType == ForceCalculationCore
4615  || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4616  && forceType != ForceCalculationOpenCL
4617  && forceType != ForceCalculationThreads))
4618  {
4619  mxGroup->meCalcState = sc::GroupCalcDisabled;
4620  aScope.addGroupSizeThresholdMessage(*this);
4621  return false;
4622  }
4623 
4624  if (cMatrixFlag != ScMatrixMode::NONE)
4625  {
4626  mxGroup->meCalcState = sc::GroupCalcDisabled;
4627  aScope.addMessage("matrix skipped");
4628  return false;
4629  }
4630 
4631  if( forceType != ForceCalculationNone )
4632  {
4633  // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4634  // without it actually being in the document at the specified position.
4635  // That would confuse opencl/threading code, as they refer to the cell group
4636  // also using the position. This is normally not triggered (single cells
4637  // are normally not in a cell group), but if forced, check for this explicitly.
4638  if( rDocument.GetFormulaCell( aPos ) != this )
4639  {
4640  mxGroup->meCalcState = sc::GroupCalcDisabled;
4641  aScope.addMessage("cell not in document");
4642  return false;
4643  }
4644  }
4645 
4646  // Guard against endless recursion of Interpret() calls, for this to work
4647  // ScFormulaCell::InterpretFormulaGroup() must never be called through
4648  // anything else than ScFormulaCell::Interpret(), same as
4649  // ScFormulaCell::InterpretTail()
4650  RecursionCounter aRecursionCounter( rRecursionHelper, this);
4651 
4652  bool bDependencyComputed = false;
4653  bool bDependencyCheckFailed = false;
4654 
4655  // Get rid of -1's in offsets (defaults) or any invalid offsets.
4656  SCROW nMaxOffset = mxGroup->mnLength - 1;
4657  nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4658  nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4659 
4660  if (nEndOffset < nStartOffset)
4661  {
4662  nStartOffset = 0;
4663  nEndOffset = nMaxOffset;
4664  }
4665 
4666  // Preference order: First try OpenCL, then threading.
4667  // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4668  if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4669  return true;
4670 
4671  if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4672  return true;
4673 
4674  return false;
4675 }
4676 
4678  SCROW nStartOffset, SCROW nEndOffset,
4679  bool bCalcDependencyOnly)
4680 {
4681  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4682  // iterate over code in the formula ...
4683  // ensure all input is pre-calculated -
4684  // to avoid writing during the calculation
4685  if (bCalcDependencyOnly)
4686  {
4687  // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4688  // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4689  // (We can only reach here from a multi-group dependency evaluation attempt).
4690  // (These two have to be in pairs always for any given formula-group)
4691  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4692  return aCalculator.DoIt();
4693  }
4694 
4695  bool bOKToParallelize = false;
4696  {
4697  ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4698  if (mxGroup->mbPartOfCycle)
4699  {
4700  mxGroup->meCalcState = sc::GroupCalcDisabled;
4701  rScope.addMessage("found circular formula-group dependencies");
4702  return false;
4703  }
4704 
4705  ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4706  ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4707  bOKToParallelize = aCalculator.DoIt();
4708 
4709  }
4710 
4711  if (rRecursionHelper.IsInRecursionReturn())
4712  {
4713  mxGroup->meCalcState = sc::GroupCalcDisabled;
4714  rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4715  return false;
4716  }
4717 
4718  if (mxGroup->mbPartOfCycle)
4719  {
4720  mxGroup->meCalcState = sc::GroupCalcDisabled;
4721  rScope.addMessage("found circular formula-group dependencies");
4722  return false;
4723  }
4724 
4725  if (!rRecursionHelper.AreGroupsIndependent())
4726  {
4727  // This call resulted from a dependency calculation for a multigroup-threading attempt,
4728  // but found dependency among the groups.
4729  rScope.addMessage("multi-group-dependency failed");
4730  return false;
4731  }
4732 
4733  if (!bOKToParallelize)
4734  {
4735  mxGroup->meCalcState = sc::GroupCalcDisabled;
4736  rScope.addMessage("could not do new dependencies calculation thing");
4737  return false;
4738  }
4739 
4740  return true;
4741 }
4742 
4743 static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc,
4745  std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4746 {
4747  const SCROW nLen = xGroup->mnLength;
4748  const sal_Int32 nWt = xGroup->mnWeight;
4749  ScAddress aAddr(xGroup->mpTopCell->aPos);
4750 
4751  SCCOL nColRet = aAddr.Col();
4752 
4753  const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4754  if (bLeft)
4755  --nColRet;
4756  else
4757  ++nColRet;
4758 
4759  while (nColRet >= 0 && nColRet <= nMaxCol)
4760  {
4761  aAddr.SetCol(nColRet);
4762  const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4763  if (!pCell)
4764  break;
4765 
4766  if (!pCell->NeedsInterpret())
4767  break;
4768 
4769  const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4770  if (!xNGroup)
4771  break;
4772 
4773  if (!pCell->GetCode()->IsEnabledForThreading())
4774  break;
4775 
4776  if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4777  break;
4778 
4779  const SCROW nNLen = xNGroup->mnLength;
4780  const sal_Int32 nNWt = pCell->GetWeight();
4781  if (nNLen != nLen || nNWt != nWt)
4782  break;
4783 
4784  rFGSet.insert(xNGroup.get());
4785  rFGMap[nColRet] = xNGroup->mpTopCell;
4786 
4787  if (bLeft)
4788  --nColRet;
4789  else
4790  ++nColRet;
4791  }
4792 
4793  if (bLeft)
4794  ++nColRet;
4795  else
4796  --nColRet;
4797 
4798  return nColRet;
4799 }
4800 
4801 // To be called only from InterpretFormulaGroup().
4803  bool& bDependencyComputed,
4804  bool& bDependencyCheckFailed,
4805  SCROW nStartOffset,
4806  SCROW nEndOffset)
4807 {
4808  static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4809  if (!bDependencyCheckFailed && !bThreadingProhibited &&
4812  {
4813  if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4814  {
4815  bDependencyComputed = true;
4816  bDependencyCheckFailed = true;
4817  return false;
4818  }
4819 
4820  bDependencyComputed = true;
4821 
4822  const static bool bHyperThreadingActive = cpuid::hasHyperThreading();
4823 
4824  // Then do the threaded calculation
4825 
4826  class Executor : public comphelper::ThreadTask
4827  {
4828  private:
4829  const unsigned mnThisThread;
4830  const unsigned mnThreadsTotal;
4831  ScDocument* mpDocument;
4833  const ScAddress& mrTopPos;
4834  SCCOL mnStartCol;
4835  SCCOL mnEndCol;
4836  SCROW mnStartOffset;
4837  SCROW mnEndOffset;
4838 
4839  public:
4840  Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4841  unsigned nThisThread,
4842  unsigned nThreadsTotal,
4843  ScDocument* pDocument2,
4844  ScInterpreterContext* pContext,
4845  const ScAddress& rTopPos,
4846  SCCOL nStartCol,
4847  SCCOL nEndCol,
4848  SCROW nStartOff,
4849  SCROW nEndOff) :
4850  comphelper::ThreadTask(rTag),
4851  mnThisThread(nThisThread),
4852  mnThreadsTotal(nThreadsTotal),
4853  mpDocument(pDocument2),
4854  mpContext(pContext),
4855  mrTopPos(rTopPos),
4856  mnStartCol(nStartCol),
4857  mnEndCol(nEndCol),
4858  mnStartOffset(nStartOff),
4859  mnEndOffset(nEndOff)
4860  {
4861  }
4862 
4863  virtual void doWork() override
4864  {
4865  ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4866  mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4867  mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4869  }
4870 
4871  };
4872 
4873  SvNumberFormatter* pNonThreadedFormatter = rDocument.GetNonThreadedContext().GetFormatTable();
4874 
4876  sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4877 
4878  if ( bHyperThreadingActive && nThreadCount >= 2 )
4879  nThreadCount /= 2;
4880 
4881  SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4882 
4884  std::map<SCCOL, ScFormulaCell*> aFGMap;
4885  aFGSet.insert(mxGroup.get());
4886 
4887  ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4888  SCCOL nColStart = aPos.Col();
4889  SCCOL nColEnd = nColStart;
4890  if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
4891  {
4892  nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
4893  nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
4894  }
4895 
4896  if (nColStart != nColEnd)
4897  {
4898  ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4899  for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4900  {
4901  if (nCurrCol == aPos.Col())
4902  continue;
4903 
4904  bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4905  if (!bFGOK || !aGuard.AreGroupsIndependent())
4906  {
4907  nColEnd = nColStart = aPos.Col();
4908  break;
4909  }
4910  }
4911  }
4912 
4913  std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount);
4914  {
4917 
4919 
4920  // Start nThreadCount new threads
4921  std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag();
4922  ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter);
4923  ScInterpreterContext* context = nullptr;
4924 
4925  for (int i = 0; i < nThreadCount; ++i)
4926  {
4927  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4928  assert(!context->pInterpreter);
4929  aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true));
4930  context->pInterpreter = aInterpreters[i].get();
4932  rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos,
4933  nColStart, nColEnd, nStartOffset, nEndOffset));
4934  }
4935 
4936  SAL_INFO("sc.threaded", "Waiting for threads to finish work");
4937  // Do not join the threads here. They will get joined in ScDocument destructor
4938  // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone).
4939  rThreadPool.waitUntilDone(aTag, false);
4940 
4942 
4943  for (int i = 0; i < nThreadCount; ++i)
4944  {
4945  context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i);
4946  // This is intentionally done in this main thread in order to avoid locking.
4948  context->pInterpreter = nullptr;
4949  }
4950 
4951  SAL_INFO("sc.threaded", "Done");
4952  }
4953 
4954  ScAddress aStartPos(mxGroup->mpTopCell->aPos);
4955  SCROW nSpanLen = nEndOffset - nStartOffset + 1;
4956  aStartPos.SetRow(aStartPos.Row() + nStartOffset);
4957  // Reuse one of the previously allocated interpreter objects here.
4958  rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen,
4959  aStartPos.Tab(), aInterpreters[0].get());
4960 
4961  return true;
4962  }
4963 
4964  return false;
4965 }
4966 
4967 // To be called only from InterpretFormulaGroup().
4969  bool& bDependencyComputed,
4970  bool& bDependencyCheckFailed)
4971 {
4972  bool bCanVectorize = pCode->IsEnabledForOpenCL();
4973  switch (pCode->GetVectorState())
4974  {
4975  case FormulaVectorEnabled:
4977  break;
4978 
4979  // Not good.
4981  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)");
4982  break;
4984  aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)");
4985  break;
4987  aScope.addMessage("group calc disabled due to vector state (opcode not in subset)");
4988  break;
4989  case FormulaVectorDisabled:
4990  case FormulaVectorUnknown:
4991  default:
4992  aScope.addMessage("group calc disabled due to vector state (unknown)");
4993  return false;
4994  }
4995 
4996  if (!bCanVectorize)
4997  return false;
4998 
5000  {
5001  aScope.addMessage("opencl not enabled");
5002  return false;
5003  }
5004 
5005  // TableOp does tricks with using a cell with different values, just bail out.
5007  return false;
5008 
5009  if (bDependencyCheckFailed)
5010  return false;
5011 
5012  if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1))
5013  {
5014  bDependencyComputed = true;
5015  bDependencyCheckFailed = true;
5016  return false;
5017  }
5018 
5019  bDependencyComputed = true;
5020 
5021  // TODO : Disable invariant formula group interpretation for now in order
5022  // to get implicit intersection to work.
5023  if (mxGroup->mbInvariant && false)
5025 
5026  int nMaxGroupLength = INT_MAX;
5027 
5028 #ifdef _WIN32
5029  // Heuristic: Certain old low-end OpenCL implementations don't
5030  // work for us with too large group lengths. 1000 was determined
5031  // empirically to be a good compromise.
5032  if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance)
5033  nMaxGroupLength = 1000;
5034 #endif
5035 
5036  if (std::getenv("SC_MAX_GROUP_LENGTH"))
5037  nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH"));
5038 
5039  int nNumOnePlus;
5040  const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus);
5041 
5042  int nOffset = 0;
5043  int nCurChunkSize;
5044  ScAddress aOrigPos = mxGroup->mpTopCell->aPos;
5045  for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize)
5046  {
5047  nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0);
5048 
5049  ScFormulaCellGroupRef xGroup;
5050 
5051  if (nNumParts == 1)
5052  xGroup = mxGroup;
5053  else
5054  {
5055  // Ugly hack
5056  xGroup = new ScFormulaCellGroup();
5057  xGroup->mpTopCell = mxGroup->mpTopCell;
5058  xGroup->mpTopCell->aPos = aOrigPos;
5059  xGroup->mpTopCell->aPos.IncRow(nOffset);
5060  xGroup->mbInvariant = mxGroup->mbInvariant;
5061  xGroup->mnLength = nCurChunkSize;
5062  xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer
5063  }
5064 
5065  ScTokenArray aCode(rDocument);
5066  ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos);
5067  // TODO avoid this extra compilation
5068  ScCompiler aComp( rDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
5069  aComp.CompileTokenArray();
5070  if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
5071  {
5073  {
5074  SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
5075 #ifdef DBG_UTIL
5076  for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
5077  {
5078  SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
5079  << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
5080  << "(" << int(opcode) << ")");
5081  }
5082 #endif
5083  }
5084  else
5085  SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
5086 
5087  mxGroup->meCalcState = sc::GroupCalcDisabled;
5088 
5089  // Undo the hack above
5090  if (nNumParts > 1)
5091  {
5092  mxGroup->mpTopCell->aPos = aOrigPos;
5093  xGroup->mpTopCell = nullptr;
5094  mxGroup->mpCode = std::move(xGroup->mpCode);
5095  }
5096 
5097  aScope.addMessage("group token conversion failed");
5098  return false;
5099  }
5100 
5101  // The converted code does not have RPN tokens yet. The interpreter will
5102  // generate them.
5103  xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning;
5105 
<