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