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