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