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