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
2336}
2337
2339{
2340 if( !pCode->GetCodeLen() )
2341 return;
2342
2343 if ( !pCode->IsRecalcModeAlways() )
2345
2346 std::unique_ptr<ScInterpreter> pScopedInterpreter;
2347 if (pInterpreter)
2348 pInterpreter->Init(this, aPos, *pCode);
2349 else
2350 {
2351 pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode ));
2352 pInterpreter = pScopedInterpreter.get();
2353 }
2354
2355 switch (pInterpreter->GetVolatileType())
2356 {
2358 // The formula contains a volatile macro.
2362 break;
2365 {
2366 // The formula was previously volatile, but no more.
2369 }
2370 else
2371 {
2372 // non-volatile formula. End listening to the area in case
2373 // it's listening due to macro module change.
2375 }
2377 break;
2378 default:
2379 ;
2380 }
2381}
2382
2384{
2385 bCompile = bVal;
2386}
2387
2389{
2391 if (pMat)
2392 pMat->SetMatColsRows( nCols, nRows );
2393 else if (nCols || nRows)
2394 {
2395 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows));
2396 // Setting the new token actually forces an empty result at this top
2397 // left cell, so have that recalculated.
2398 SetDirty();
2399 }
2400}
2401
2402void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const
2403{
2405 if (pMat)
2406 pMat->GetMatColsRows( nCols, nRows);
2407 else
2408 {
2409 nCols = 0;
2410 nRows = 0;
2411 }
2412}
2413
2415{
2416 bInChangeTrack = bVal;
2417}
2418
2419void ScFormulaCell::Notify( const SfxHint& rHint )
2420{
2422 return;
2423
2424 const SfxHintId nHint = rHint.GetId();
2425 if (nHint == SfxHintId::ScReference)
2426 {
2427 const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint);
2428
2429 switch (rRefHint.getType())
2430 {
2432 {
2433 const sc::RefColReorderHint& rRefColReorder =
2434 static_cast<const sc::RefColReorderHint&>(rRefHint);
2435 if (!IsShared() || IsSharedTop())
2437 aPos, rRefColReorder.getTab(),
2438 rRefColReorder.getStartRow(),
2439 rRefColReorder.getEndRow(),
2440 rRefColReorder.getColMap());
2441 }
2442 break;
2444 {
2445 const sc::RefRowReorderHint& rRefRowReorder =
2446 static_cast<const sc::RefRowReorderHint&>(rRefHint);
2447 if (!IsShared() || IsSharedTop())
2449 aPos, rRefRowReorder.getTab(),
2450 rRefRowReorder.getStartColumn(),
2451 rRefRowReorder.getEndColumn(),
2452 rRefRowReorder.getRowMap());
2453 }
2454 break;
2456 {
2458 }
2459 break;
2461 {
2463 }
2464 break;
2465 default:
2466 ;
2467 }
2468
2469 return;
2470 }
2471
2473 return;
2474
2475 if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged)))
2476 return;
2477
2478 bool bForceTrack = false;
2479 if ( nHint == SfxHintId::ScTableOpDirty )
2480 {
2481 bForceTrack = !bTableOpDirty;
2482 if ( !bTableOpDirty )
2483 {
2485 bTableOpDirty = true;
2486 }
2487 }
2488 else
2489 {
2490 bForceTrack = !bDirty;
2491 SetDirtyVar();
2492 }
2493 // Don't remove from FormulaTree to put in FormulaTrack to
2494 // put in FormulaTree again and again, only if necessary.
2495 // Any other means except ScRecalcMode::ALWAYS by which a cell could
2496 // be in FormulaTree if it would notify other cells through
2497 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!?
2498 // Yes. The new TableOpDirty made it necessary to have a
2499 // forced mode where formulas may still be in FormulaTree from
2500 // TableOpDirty but have to notify dependents for normal dirty.
2501 if ( (bForceTrack || !rDocument.IsInFormulaTree( this )
2503 && !rDocument.IsInFormulaTrack( this ) )
2505}
2506
2508{
2509 switch (rQuery.getId())
2510 {
2512 {
2513 sc::RefQueryFormulaGroup& rRefQuery =
2514 static_cast<sc::RefQueryFormulaGroup&>(rQuery);
2515 if (IsShared())
2516 rRefQuery.add(aPos);
2517 }
2518 break;
2519 default:
2520 ;
2521 }
2522}
2523
2524void ScFormulaCell::SetDirty( bool bDirtyFlag )
2525{
2526 if (IsInChangeTrack())
2527 return;
2528
2530 {
2531 SetDirtyVar();
2532 rDocument.SetStreamValid(aPos.Tab(), false);
2533 return;
2534 }
2535
2536 // Avoid multiple formula tracking in Load() and in CompileAll()
2537 // after CopyScenario() and CopyBlockFromClip().
2538 // If unconditional formula tracking is needed, set bDirty=false
2539 // before calling SetDirty(), for example in CompileTokenArray().
2540 if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) )
2541 {
2542 if( bDirtyFlag )
2543 SetDirtyVar();
2545
2546 // While loading a document listeners have not been established yet.
2547 // Tracking would remove this cell from the FormulaTrack and add it to
2548 // the FormulaTree, once in there it would be assumed that its
2549 // dependents already had been tracked and it would be skipped on a
2550 // subsequent notify. Postpone tracking until all listeners are set.
2553 }
2554
2555 rDocument.SetStreamValid(aPos.Tab(), false);
2556}
2557
2559{
2560 bDirty = true;
2561 mbPostponedDirty = false;
2562 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
2563 {
2564 mxGroup->meCalcState = sc::GroupCalcEnabled;
2565 mxGroup->mbPartOfCycle = false;
2566 }
2567
2568 // mark the sheet of this cell to be calculated
2569 //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() );
2570}
2571
2573{
2574 bDirty = true;
2577}
2578
2580{
2581 bTableOpDirty = false;
2582}
2583
2585{
2586 if ( IsInChangeTrack() )
2587 return;
2588
2590 bTableOpDirty = true;
2591 else
2592 {
2593 if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) )
2594 {
2595 if ( !bTableOpDirty )
2596 {
2598 bTableOpDirty = true;
2599 }
2601 rDocument.TrackFormulas( SfxHintId::ScTableOpDirty );
2602 }
2603 }
2604}
2605
2607{
2609}
2610
2612{
2613 aResult.SetToken(pToken);
2614}
2615
2617{
2618 return aResult.GetString();
2619}
2620
2622{
2624}
2625
2627{
2628 aResult.SetMatrix(nCols, nRows, pMat, pUL);
2629}
2630
2632{
2633 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is
2634 * used whether it is solely for transport of a simple result error and get
2635 * rid of that abuse. */
2636 pCode->SetCodeError( n );
2637 // Hard set errors are transported as result type value per convention,
2638 // e.g. via clipboard. ScFormulaResult::IsValue() and
2639 // ScFormulaResult::GetDouble() handle that.
2641}
2642
2644{
2646}
2647
2649{
2650 if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL )
2651 SetDirtyVar();
2652 pCode->AddRecalcMode( nBits );
2653}
2654
2656{
2658}
2659
2661{
2663}
2664
2666{
2668}
2669
2670void ScFormulaCell::SetHybridFormula( const OUString& r,
2671 const formula::FormulaGrammar::Grammar eGrammar )
2672{
2673 aResult.SetHybridFormula( r); eTempGrammar = eGrammar;
2674}
2675
2677{
2678 return aResult.GetHybridFormula();
2679}
2680
2681// Dynamically create the URLField on a mouse-over action on a hyperlink() cell.
2682void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText )
2683{
2684 OUString aCellString;
2685
2686 const Color* pColor;
2687
2688 // Cell Text uses the Cell format while the URL uses
2689 // the default format for the type.
2690 const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos );
2692
2693 const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat( *pFormatter, nCellFormat, SvNumFormatType::NUMBER);
2694
2695 if ( IsValue() )
2696 {
2697 double fValue = GetValue();
2698 pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor );
2699 }
2700 else
2701 {
2702 aCellString = GetString().getString();
2703 pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor );
2704 }
2706 if (xMat)
2707 {
2708 // determine if the matrix result is a string or value.
2709 if (!xMat->IsValue(0, 1))
2710 rURL = xMat->GetString(0, 1).getString();
2711 else
2712 pFormatter->GetOutputString(
2713 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor);
2714 }
2715
2716 if(rURL.isEmpty())
2717 {
2718 if(IsValue())
2719 pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor );
2720 else
2721 pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor );
2722 }
2723}
2724
2726{
2727 if (!IsValue())
2728 return aResult.IsMultiline();
2729 return false;
2730}
2731
2733{
2734 return pCode && pCode->IsHyperLink();
2735}
2736
2737std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject()
2738{
2739 OUString aCellText;
2740 OUString aURL;
2741 GetURLResult( aURL, aCellText );
2742
2744}
2745
2747{
2750}
2751
2753{
2756}
2757
2759{
2761 return aResult.IsValue();
2762}
2763
2765{
2767 if (pCode->GetCodeError() != FormulaError::NONE)
2768 return false;
2769
2770 return aResult.IsValueNoError();
2771}
2772
2774{
2775 if (NeedsInterpret())
2776 // false if the cell is dirty & needs to be interpreted.
2777 return false;
2778
2779 if (pCode->GetCodeError() != FormulaError::NONE)
2780 return false;
2781
2782 return aResult.IsValueNoError();
2783}
2784
2786{
2788 return GetRawValue();
2789}
2790
2792{
2794 return GetRawString();
2795}
2796
2798{
2799 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2800 aResult.GetResultError() == FormulaError::NONE)
2801 return aResult.GetDouble();
2802 return 0.0;
2803}
2804
2806{
2807 if ((pCode->GetCodeError() == FormulaError::NONE) &&
2808 aResult.GetResultError() == FormulaError::NONE)
2809 return aResult.GetString();
2810
2812}
2813
2815{
2816 if ( rDocument.GetAutoCalc() )
2817 {
2819 // Was stored !bDirty but an accompanying matrix cell was bDirty?
2821 Interpret();
2822 }
2823 return aResult.GetMatrix().get();
2824}
2825
2827{
2828 switch ( cMatrixFlag )
2829 {
2831 rPos = aPos;
2832 return true;
2834 {
2837 if( t )
2838 {
2839 ScSingleRefData& rRef = *t->GetSingleRef();
2840 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
2841 if (rDoc.ValidAddress(aAbs))
2842 {
2843 rPos = aAbs;
2844 return true;
2845 }
2846 }
2847 }
2848 break;
2849 default: break;
2850 }
2851 return false;
2852}
2853
2855{
2856 switch ( cMatrixFlag )
2857 {
2860 {
2861 static thread_local SCCOL nC;
2862 static thread_local SCROW nR;
2863 ScAddress aOrg;
2864 if ( !GetMatrixOrigin( rDoc, aOrg ) )
2866 if ( aOrg != rOrgPos )
2867 { // First time or a different matrix than last time.
2868 rOrgPos = aOrg;
2869 const ScFormulaCell* pFCell;
2871 pFCell = rDocument.GetFormulaCell(aOrg);
2872 else
2873 pFCell = this; // this ScMatrixMode::Formula
2874 // There's only one this, don't compare pFCell==this.
2875 if (pFCell && pFCell->cMatrixFlag == ScMatrixMode::Formula)
2876 {
2877 pFCell->GetMatColsRows( nC, nR );
2878 if ( nC == 0 || nR == 0 )
2879 {
2880 // No ScMatrixFormulaCellToken available yet, calculate new.
2881 nC = 1;
2882 nR = 1;
2883 ScAddress aTmpOrg;
2884 ScFormulaCell* pCell;
2885 ScAddress aAdr( aOrg );
2886 aAdr.IncCol();
2887 bool bCont = true;
2888 do
2889 {
2890 pCell = rDocument.GetFormulaCell(aAdr);
2891 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2892 pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2893 {
2894 nC++;
2895 aAdr.IncCol();
2896 }
2897 else
2898 bCont = false;
2899 } while ( bCont );
2900 aAdr = aOrg;
2901 aAdr.IncRow();
2902 bCont = true;
2903 do
2904 {
2905 pCell = rDocument.GetFormulaCell(aAdr);
2906 if (pCell && pCell->cMatrixFlag == ScMatrixMode::Reference &&
2907 pCell->GetMatrixOrigin(rDocument, aTmpOrg) && aTmpOrg == aOrg)
2908 {
2909 nR++;
2910 aAdr.IncRow();
2911 }
2912 else
2913 bCont = false;
2914 } while ( bCont );
2915
2916 const_cast<ScFormulaCell*>(pFCell)->SetMatColsRows(nC, nR);
2917 }
2918 }
2919 else
2920 {
2921#if OSL_DEBUG_LEVEL > 0
2922 SAL_WARN( "sc", "broken Matrix, no MatFormula at origin, Pos: "
2924 << ", MatOrg: "
2926#endif
2928 }
2929 }
2930 // here we are, healthy and clean, somewhere in between
2931 SCCOL dC = aPos.Col() - aOrg.Col();
2932 SCROW dR = aPos.Row() - aOrg.Row();
2934 if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR )
2935 {
2936 if ( dC == 0 )
2937 nEdges |= sc::MatrixEdge::Left;
2938 if ( dC+1 == nC )
2939 nEdges |= sc::MatrixEdge::Right;
2940 if ( dR == 0 )
2941 nEdges |= sc::MatrixEdge::Top;
2942 if ( dR+1 == nR )
2943 nEdges |= sc::MatrixEdge::Bottom;
2944 if ( nEdges == sc::MatrixEdge::Nothing )
2945 nEdges = sc::MatrixEdge::Inside;
2946 }
2947 else
2948 {
2949 SAL_WARN( "sc", "broken Matrix, Pos: "
2951 << ", MatOrg: "
2953 << ", MatCols: " << static_cast<sal_Int32>( nC )
2954 << ", MatRows: " << static_cast<sal_Int32>( nR )
2955 << ", DiffCols: " << static_cast<sal_Int32>( dC )
2956 << ", DiffRows: " << static_cast<sal_Int32>( dR ));
2957 }
2958 return nEdges;
2959 }
2960 default:
2962 }
2963}
2964
2966{
2968
2969 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors
2970 * and not also abused for signaling other error conditions we could bail
2971 * out even before attempting to interpret broken code. */
2973 if (nErr != FormulaError::NONE)
2974 return nErr;
2975 return aResult.GetResultError();
2976}
2977
2979{
2981 if (nErr != FormulaError::NONE)
2982 return nErr;
2983 return aResult.GetResultError();
2984}
2985
2987{
2989
2990 rErr = pCode->GetCodeError();
2991 if (rErr != FormulaError::NONE)
2992 return true;
2993
2994 return aResult.GetErrorOrDouble(rErr, rVal);
2995}
2996
2998{
3000
3002 if (nErr != FormulaError::NONE)
3003 return sc::FormulaResultValue(nErr);
3004
3005 return aResult.GetResult();
3006}
3007
3009{
3011 if (nErr != FormulaError::NONE)
3012 return sc::FormulaResultValue(nErr);
3013
3014 return aResult.GetResult();
3015}
3016
3018{
3021 if( p && !aIter.GetNextReferenceRPN() ) // only one!
3022 {
3023 SingleDoubleRefProvider aProv( *p );
3024 r.aStart = aProv.Ref1.toAbs(rDocument, aPos);
3025 r.aEnd = aProv.Ref2.toAbs(rDocument, aPos);
3026 return true;
3027 }
3028 else
3029 return false;
3030}
3031
3032bool
3034{
3035 /* If there appears just one reference in the formula, it's the same
3036 as HasOneReference(). If there are more of them, they can denote
3037 one range if they are (sole) arguments of one function.
3038 Union of these references must form one range and their
3039 intersection must be empty set.
3040 */
3041
3042 // Detect the simple case of exactly one reference in advance without all
3043 // overhead.
3044 // #i107741# Doing so actually makes outlines using SUBTOTAL(x;reference)
3045 // work again, where the function does not have only references.
3046 if (HasOneReference( rRange))
3047 return true;
3048
3049 // Get first reference, if any
3051 formula::FormulaToken* const pFirstReference(aIter.GetNextReferenceRPN());
3052 if (pFirstReference)
3053 {
3054 // Collect all consecutive references, starting by the one
3055 // already found
3056 std::vector<formula::FormulaToken*> aReferences { pFirstReference };
3057 FormulaToken* pToken(aIter.NextRPN());
3058 FormulaToken* pFunction(nullptr);
3059 while (pToken)
3060 {
3061 if (lcl_isReference(*pToken))
3062 {
3063 aReferences.push_back(pToken);
3064 pToken = aIter.NextRPN();
3065 }
3066 else
3067 {
3068 if (pToken->IsFunction())
3069 {
3070 pFunction = pToken;
3071 }
3072 break;
3073 }
3074 }
3075 if (pFunction && !aIter.GetNextReferenceRPN()
3076 && (pFunction->GetParamCount() == aReferences.size()))
3077 {
3078 return lcl_refListFormsOneRange(rDocument, aPos, aReferences, rRange);
3079 }
3080 }
3081 return false;
3082}
3083
3085{
3086 RelNameRef eRelNameRef = RelNameRef::NONE;
3089 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
3090 {
3091 switch (t->GetType())
3092 {
3094 if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE)
3095 eRelNameRef = RelNameRef::SINGLE;
3096 break;
3098 if (t->GetDoubleRef()->Ref1.IsRelName() || t->GetDoubleRef()->Ref2.IsRelName())
3099 // May originate from individual cell names, in which case
3100 // it needs recompilation.
3101 return RelNameRef::DOUBLE;
3102 /* TODO: have an extra flag at ScComplexRefData if range was
3103 * extended? or too cumbersome? might narrow recompilation to
3104 * only needed cases.
3105 * */
3106 break;
3107 default:
3108 ; // nothing
3109 }
3110 }
3111 return eRelNameRef;
3112}
3113
3115{
3116 if (rCxt.meMode != URM_INSDEL)
3117 // Just in case...
3118 return false;
3119
3120 if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
3121 // No movement.
3122 return false;
3123
3124 if (!rCxt.maRange.Contains(aPos))
3125 return false;
3126
3127 // This formula cell itself is being shifted during cell range
3128 // insertion or deletion. Update its position.
3130 if (!aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
3131 {
3132 assert(!"can't move ScFormulaCell");
3133 }
3134
3135 return true;
3136}
3137
3138namespace {
3139
3143bool checkCompileColRowName(
3144 const sc::RefUpdateContext& rCxt, ScDocument& rDoc, const ScTokenArray& rCode,
3145 const ScAddress& aOldPos, const ScAddress& aPos, bool bValChanged)
3146{
3147 switch (rCxt.meMode)
3148 {
3149 case URM_INSDEL:
3150 {
3151 if (rCxt.mnColDelta <= 0 && rCxt.mnRowDelta <= 0)
3152 return false;
3153
3156 ScRangePairList* pColList = rDoc.GetColNameRanges();
3157 ScRangePairList* pRowList = rDoc.GetRowNameRanges();
3158 while ((t = aIter.GetNextColRowName()) != nullptr)
3159 {
3160 ScSingleRefData& rRef = *t->GetSingleRef();
3161 if (rCxt.mnRowDelta > 0 && rRef.IsColRel())
3162 { // ColName
3163 ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3164 ScRangePair* pR = pColList->Find( aAdr );
3165 if ( pR )
3166 { // defined
3167 if (pR->GetRange(1).aStart.Row() == rCxt.maRange.aStart.Row())
3168 return true;
3169 }
3170 else
3171 { // on the fly
3172 if (aAdr.Row() + 1 == rCxt.maRange.aStart.Row())
3173 return true;
3174 }
3175 }
3176 if (rCxt.mnColDelta > 0 && rRef.IsRowRel())
3177 { // RowName
3178 ScAddress aAdr = rRef.toAbs(rDoc, aPos);
3179 ScRangePair* pR = pRowList->Find( aAdr );
3180 if ( pR )
3181 { // defined
3182 if ( pR->GetRange(1).aStart.Col() == rCxt.maRange.aStart.Col())
3183 return true;
3184 }
3185 else
3186 { // on the fly
3187 if (aAdr.Col() + 1 == rCxt.maRange.aStart.Col())
3188 return true;
3189 }
3190 }
3191 }
3192 }
3193 break;
3194 case URM_MOVE:
3195 { // Recompile for Move/D&D when ColRowName was moved or this Cell
3196 // points to one and was moved.
3197 bool bMoved = (aPos != aOldPos);
3198 if (bMoved)
3199 return true;
3200
3202 const formula::FormulaToken* t = aIter.GetNextColRowName();
3203 for (; t; t = aIter.GetNextColRowName())
3204 {
3205 const ScSingleRefData& rRef = *t->GetSingleRef();
3206 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
3207 if (rDoc.ValidAddress(aAbs))
3208 {
3209 if (rCxt.maRange.Contains(aAbs))
3210 return true;
3211 }
3212 }
3213 }
3214 break;
3215 case URM_COPY:
3216 return bValChanged;
3217 default:
3218 ;
3219 }
3220
3221 return false;
3222}
3223
3224void setOldCodeToUndo(
3225 ScDocument& rUndoDoc, const ScAddress& aUndoPos, const ScTokenArray* pOldCode, FormulaGrammar::Grammar eTempGrammar, ScMatrixMode cMatrixFlag)
3226{
3227 // Copy the cell to aUndoPos, which is its current position in the document,
3228 // so this works when UpdateReference is called before moving the cells
3229 // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference
3230 // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed).
3231
3232 // If there is already a formula cell in the undo document, don't overwrite it,
3233 // the first (oldest) is the important cell.
3234 if (rUndoDoc.GetCellType(aUndoPos) == CELLTYPE_FORMULA)
3235 return;
3236
3237 ScFormulaCell* pFCell =
3238 new ScFormulaCell(
3239 rUndoDoc, aUndoPos, pOldCode ? *pOldCode : ScTokenArray(rUndoDoc), eTempGrammar, cMatrixFlag);
3240
3241 pFCell->SetResultToken(nullptr); // to recognize it as changed later (Cut/Paste!)
3242 rUndoDoc.SetFormulaCell(aUndoPos, pFCell);
3243}
3244
3245}
3246
3248 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3249{
3250 if (rCxt.meMode != URM_INSDEL)
3251 // Just in case...
3252 return false;
3253
3254 bool bCellStateChanged = false;
3255 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3256 if ( pUndoCellPos )
3257 aUndoPos = *pUndoCellPos;
3258 ScAddress aOldPos( aPos );
3259 bCellStateChanged = UpdatePosOnShift(rCxt);
3260
3261 // Check presence of any references or column row names.
3262 bool bHasRefs = pCode->HasReferences();
3263 bool bHasColRowNames = false;
3264 if (!bHasRefs)
3265 {
3266 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3267 bHasRefs = bHasColRowNames;
3268 }
3269 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3270
3271 if (!bHasRefs && !bOnRefMove)
3272 // This formula cell contains no references, nor needs recalculating
3273 // on reference update. Bail out.
3274 return bCellStateChanged;
3275
3276 std::unique_ptr<ScTokenArray> pOldCode;
3277 if (pUndoDoc)
3278 pOldCode = pCode->Clone();
3279
3280 bool bValChanged = false;
3281 bool bRefModified = false;
3282 bool bRecompile = bCompile;
3283
3284 if (bHasRefs)
3285 {
3286 // Update cell or range references.
3287 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(rCxt, aOldPos);
3288 bRefModified = aRes.mbReferenceModified;
3289 bValChanged = aRes.mbValueChanged;
3290 if (aRes.mbNameModified)
3291 bRecompile = true;
3292 }
3293
3294 if (bValChanged || bRefModified)
3295 bCellStateChanged = true;
3296
3297 if (bOnRefMove)
3298 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3299 bOnRefMove = (bValChanged || (aPos != aOldPos) || bRefModified);
3300
3301 bool bNewListening = false;
3302 bool bInDeleteUndo = false;
3303
3304 if (bHasRefs)
3305 {
3306 // Upon Insert ColRowNames have to be recompiled in case the
3307 // insertion occurs right in front of the range.
3308 if (bHasColRowNames && !bRecompile)
3309 bRecompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3310
3311 ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3312 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3313
3314 // RelNameRefs are always moved
3315 bool bHasRelName = false;
3316 if (!bRecompile)
3317 {
3318 RelNameRef eRelNameRef = HasRelNameReference();
3319 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3320 bRecompile = (eRelNameRef == RelNameRef::DOUBLE);
3321 }
3322 // Reference changed and new listening needed?
3323 // Except in Insert/Delete without specialities.
3324 bNewListening = (bRefModified || bRecompile
3325 || (bValChanged && bInDeleteUndo) || bHasRelName);
3326
3327 if ( bNewListening )
3328 EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3329 }
3330
3331 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3332 bool bNeedDirty = (bValChanged || bRecompile || bOnRefMove);
3333
3334 if (pUndoDoc && (bValChanged || bOnRefMove))
3335 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3336
3337 bCompile |= bRecompile;
3338 if (bCompile)
3339 {
3340 CompileTokenArray( bNewListening ); // no Listening
3341 bNeedDirty = true;
3342 }
3343
3344 if ( !bInDeleteUndo )
3345 { // In ChangeTrack Delete-Reject listeners are established in
3346 // InsertCol/InsertRow
3347 if ( bNewListening )
3348 {
3349 // Inserts/Deletes re-establish listeners after all
3350 // UpdateReference calls.
3351 // All replaced shared formula listeners have to be
3352 // established after an Insert or Delete. Do nothing here.
3353 SetNeedsListening( true);
3354 }
3355 }
3356
3357 if (bNeedDirty)
3358 { // Cut off references, invalid or similar?
3359 // Postpone SetDirty() until all listeners have been re-established in
3360 // Inserts/Deletes.
3361 mbPostponedDirty = true;
3362 }
3363
3364 return bCellStateChanged;
3365}
3366
3368 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3369{
3370 if (rCxt.meMode != URM_MOVE)
3371 return false;
3372
3373 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3374 if ( pUndoCellPos )
3375 aUndoPos = *pUndoCellPos;
3376 ScAddress aOldPos( aPos );
3377
3378 bool bCellInMoveTarget = rCxt.maRange.Contains(aPos);
3379
3380 if ( bCellInMoveTarget )
3381 {
3382 // The cell is being moved or copied to a new position. I guess the
3383 // position has been updated prior to this call? Determine
3384 // its original position before the move which will be used to adjust
3385 // relative references later.
3386 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3387 }
3388
3389 // Check presence of any references or column row names.
3390 bool bHasRefs = pCode->HasReferences();
3391 bool bHasColRowNames = false;
3392 if (!bHasRefs)
3393 {
3394 bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3395 bHasRefs = bHasColRowNames;
3396 }
3397 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3398
3399 if (!bHasRefs && !bOnRefMove)
3400 // This formula cell contains no references, nor needs recalculating
3401 // on reference update. Bail out.
3402 return false;
3403
3404 bool bCellStateChanged = false;
3405 std::unique_ptr<ScTokenArray> pOldCode;
3406 if (pUndoDoc)
3407 pOldCode = pCode->Clone();
3408
3409 bool bValChanged = false;
3410 bool bRefModified = false;
3411
3412 if (bHasRefs)
3413 {
3414 // Update cell or range references.
3415 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(rCxt, aOldPos, aPos);
3416 bRefModified = aRes.mbReferenceModified || aRes.mbNameModified;
3417 bValChanged = aRes.mbValueChanged;
3418 if (aRes.mbNameModified)
3419 // Re-compile to get the RPN token regenerated to reflect updated names.
3420 bCompile = true;
3421 }
3422
3423 if (bValChanged || bRefModified)
3424 bCellStateChanged = true;
3425
3426 if (bOnRefMove)
3427 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3428 bOnRefMove = (bValChanged || (aPos != aOldPos));
3429
3430 bool bColRowNameCompile = false;
3431 bool bHasRelName = false;
3432 bool bNewListening = false;
3433 bool bInDeleteUndo = false;
3434
3435 if (bHasRefs)
3436 {
3437 // Upon Insert ColRowNames have to be recompiled in case the
3438 // insertion occurs right in front of the range.
3439 if (bHasColRowNames)
3440 bColRowNameCompile = checkCompileColRowName(rCxt, rDocument, *pCode, aOldPos, aPos, bValChanged);
3441
3442 ScChangeTrack* pChangeTrack = rDocument.GetChangeTrack();
3443 bInDeleteUndo = (pChangeTrack && pChangeTrack->IsInDeleteUndo());
3444
3445 // RelNameRefs are always moved
3446 RelNameRef eRelNameRef = HasRelNameReference();
3447 bHasRelName = (eRelNameRef != RelNameRef::NONE);
3448 bCompile |= (eRelNameRef == RelNameRef::DOUBLE);
3449 // Reference changed and new listening needed?
3450 // Except in Insert/Delete without specialties.
3451 bNewListening = (bRefModified || bColRowNameCompile
3452 || bValChanged || bHasRelName)
3453 // #i36299# Don't duplicate action during cut&paste / drag&drop
3454 // on a cell in the range moved, start/end listeners is done
3455 // via ScDocument::DeleteArea() and ScDocument::CopyFromClip().
3457
3458 if ( bNewListening )
3459 EndListeningTo(rDocument, pOldCode.get(), aOldPos);
3460 }
3461
3462 bool bNeedDirty = false;
3463 // NeedDirty for changes except for Copy and Move/Insert without RelNames
3464 if ( bRefModified || bColRowNameCompile ||
3465 (bValChanged && bHasRelName ) || bOnRefMove)
3466 bNeedDirty = true;
3467
3468 if (pUndoDoc && !bCellInMoveTarget && (bValChanged || bRefModified || bOnRefMove))
3469 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3470
3471 bValChanged = false;
3472
3473 bCompile = (bCompile || bValChanged || bColRowNameCompile);
3474 if ( bCompile )
3475 {
3476 CompileTokenArray( bNewListening ); // no Listening
3477 bNeedDirty = true;
3478 }
3479
3480 if ( !bInDeleteUndo )
3481 { // In ChangeTrack Delete-Reject listeners are established in
3482 // InsertCol/InsertRow
3483 if ( bNewListening )
3484 {
3486 }
3487 }
3488
3489 if (bNeedDirty)
3490 { // Cut off references, invalid or similar?
3491 sc::AutoCalcSwitch aACSwitch(rDocument, false);
3492 SetDirty();
3493 }
3494
3495 return bCellStateChanged;
3496}
3497
3499 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3500{
3501 if (rCxt.meMode != URM_COPY)
3502 return false;
3503
3504 ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc
3505 if ( pUndoCellPos )
3506 aUndoPos = *pUndoCellPos;
3507 ScAddress aOldPos( aPos );
3508
3509 if (rCxt.maRange.Contains(aPos))
3510 {
3511 // The cell is being moved or copied to a new position. I guess the
3512 // position has been updated prior to this call? Determine
3513 // its original position before the move which will be used to adjust
3514 // relative references later.
3515 aOldPos.Set(aPos.Col() - rCxt.mnColDelta, aPos.Row() - rCxt.mnRowDelta, aPos.Tab() - rCxt.mnTabDelta);
3516 }
3517
3518 // Check presence of any references or column row names.
3519 bool bHasRefs = pCode->HasReferences();
3520 bool bHasColRowNames = (formula::FormulaTokenArrayPlainIterator(*pCode).GetNextColRowName() != nullptr);
3521 bHasRefs = bHasRefs || bHasColRowNames;
3522 bool bOnRefMove = pCode->IsRecalcModeOnRefMove();
3523
3524 if (!bHasRefs && !bOnRefMove)
3525 // This formula cell contains no references, nor needs recalculating
3526 // on reference update. Bail out.
3527 return false;
3528
3529 std::unique_ptr<ScTokenArray> pOldCode;
3530 if (pUndoDoc)
3531 pOldCode = pCode->Clone();
3532
3533 if (bOnRefMove)
3534 // Cell may reference itself, e.g. ocColumn, ocRow without parameter
3535 bOnRefMove = (aPos != aOldPos);
3536
3537 bool bNeedDirty = bOnRefMove;
3538
3539 if (pUndoDoc && bOnRefMove)
3540 setOldCodeToUndo(*pUndoDoc, aUndoPos, pOldCode.get(), eTempGrammar, cMatrixFlag);
3541
3542 if (bCompile)
3543 {
3544 CompileTokenArray(); // no Listening
3545 bNeedDirty = true;
3546 }
3547
3548 if (bNeedDirty)
3549 { // Cut off references, invalid or similar?
3550 sc::AutoCalcSwitch aACSwitch(rDocument, false);
3551 SetDirty();
3552 }
3553
3554 return false;
3555}
3556
3558 const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, const ScAddress* pUndoCellPos )
3559{
3560 if (rDocument.IsClipOrUndo())
3561 return false;
3562
3563 if (mxGroup && mxGroup->mpTopCell != this)
3564 {
3565 // This is not a top cell of a formula group. Don't update references.
3566
3567 switch (rCxt.meMode)
3568 {
3569 case URM_INSDEL:
3570 return UpdatePosOnShift(rCxt);
3571 default:
3572 ;
3573 }
3574 return false;
3575 }
3576
3577 switch (rCxt.meMode)
3578 {
3579 case URM_INSDEL:
3580 return UpdateReferenceOnShift(rCxt, pUndoDoc, pUndoCellPos);
3581 case URM_MOVE:
3582 return UpdateReferenceOnMove(rCxt, pUndoDoc, pUndoCellPos);
3583 case URM_COPY:
3584 return UpdateReferenceOnCopy(rCxt, pUndoDoc, pUndoCellPos);
3585 default:
3586 ;
3587 }
3588
3589 return false;
3590}
3591
3593{
3594 // Adjust tokens only when it's not grouped or grouped top cell.
3595 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3596 bool bPosChanged = (rCxt.mnInsertPos <= aPos.Tab());
3598 {
3599 if (bPosChanged)
3600 aPos.IncTab(rCxt.mnSheets);
3601
3602 return;
3603 }
3604
3606 ScAddress aOldPos = aPos;
3607 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateInsertTab!
3608 if (bPosChanged)
3609 aPos.IncTab(rCxt.mnSheets);
3610
3611 if (!bAdjustCode)
3612 return;
3613
3615 if (aRes.mbNameModified)
3616 // Re-compile after new sheet(s) have been inserted.
3617 bCompile = true;
3618
3619 // no StartListeningTo because the new sheets have not been inserted yet.
3620}
3621
3623{
3624 // Adjust tokens only when it's not grouped or grouped top cell.
3625 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3626 bool bPosChanged = (aPos.Tab() >= rCxt.mnDeletePos + rCxt.mnSheets);
3628 {
3629 if (bPosChanged)
3630 aPos.IncTab(-1*rCxt.mnSheets);
3631 return;
3632 }
3633
3635 // IncTab _after_ EndListeningTo and _before_ Compiler UpdateDeleteTab!
3636 ScAddress aOldPos = aPos;
3637 if (bPosChanged)
3638 aPos.IncTab(-1*rCxt.mnSheets);
3639
3640 if (!bAdjustCode)
3641 return;
3642
3644 if (aRes.mbNameModified)
3645 // Re-compile after sheet(s) have been deleted.
3646 bCompile = true;
3647}
3648
3650{
3651 // Adjust tokens only when it's not grouped or grouped top cell.
3652 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3653
3655 {
3656 aPos.SetTab(nTabNo);
3657 return;
3658 }
3659
3661 ScAddress aOldPos = aPos;
3662 // SetTab _after_ EndListeningTo and _before_ Compiler UpdateMoveTab !
3663 aPos.SetTab(nTabNo);
3664
3665 // no StartListeningTo because pTab[nTab] not yet correct!
3666
3667 if (!bAdjustCode)
3668 return;
3669
3671 if (aRes.mbNameModified)
3672 // Re-compile after sheet(s) have been deleted.
3673 bCompile = true;
3674}
3675
3677{
3678 if (rDocument.IsClipOrUndo())
3679 return;
3680
3681 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3682 if (!bAdjustCode)
3683 return;
3684
3687 while (p)
3688 {
3689 ScSingleRefData& rRef1 = *p->GetSingleRef();
3690 if (!rRef1.IsTabRel() && nTable <= rRef1.Tab())
3691 rRef1.IncTab(1);
3692 if (p->GetType() == formula::svDoubleRef)
3693 {
3694 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3695 if (!rRef2.IsTabRel() && nTable <= rRef2.Tab())
3696 rRef2.IncTab(1);
3697 }
3698 p = aIter.GetNextReferenceRPN();
3699 }
3700}
3701
3703{
3704 if (rDocument.IsClipOrUndo())
3705 return false;
3706
3707 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this;
3708 if (!bAdjustCode)
3709 return false;
3710
3711 bool bRet = false;
3714 while (p)
3715 {
3716 ScSingleRefData& rRef1 = *p->GetSingleRef();
3717 if (!rRef1.IsTabRel())
3718 {
3719 if (nTable != rRef1.Tab())
3720 bRet = true;
3721 else if (nTable != aPos.Tab())
3722 rRef1.SetAbsTab(aPos.Tab());
3723 }
3724 if (p->GetType() == formula::svDoubleRef)
3725 {
3726 ScSingleRefData& rRef2 = p->GetDoubleRef()->Ref2;
3727 if (!rRef2.IsTabRel())
3728 {
3729 if(nTable != rRef2.Tab())
3730 bRet = true;
3731 else if (nTable != aPos.Tab())
3732 rRef2.SetAbsTab(aPos.Tab());
3733 }
3734 }
3735 p = aIter.GetNextReferenceRPN();
3736 }
3737 return bRet;
3738}
3739
3740void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse )
3741{
3742 if ( bForceIfNameInUse && !bCompile )
3744 if ( bCompile )
3745 pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled
3747}
3748
3750{
3751 // References to or over filtered rows are not adjusted
3752 // analog to the normal (non-transposed) case
3753 SCCOLROW nTemp = rRef.Col();
3754 rRef.SetRelCol(rRef.Row());
3755 rRef.SetRelRow(nTemp);
3756}
3757
3758// Reference transposition is only called in Clipboard Document
3760{
3761 bool bFound = false;
3764 while ( ( t = aIter.GetNextReference() ) != nullptr )
3765 {
3766 ScSingleRefData& rRef1 = *t->GetSingleRef();
3767 if ( rRef1.IsColRel() && rRef1.IsRowRel() )
3768 {
3769 bool bDouble = (t->GetType() == formula::svDoubleRef);
3770 ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef()->Ref2 : rRef1);
3771 if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) )
3772 {
3774
3775 if ( bDouble )
3777
3778 bFound = true;
3779 }
3780 }
3781 }
3782
3783 if (bFound)
3784 bCompile = true;
3785}
3786
3787void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
3788 ScDocument* pUndoDoc )
3789{
3791
3792 ScAddress aOldPos = aPos;
3793 bool bPosChanged = false; // Whether this cell has been moved
3794
3795 // Dest range is transposed
3796 ScRange aDestRange( rDest, ScAddress(
3797 static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()),
3798 static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()),
3799 rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) );
3800
3801 // cell within range
3802 if ( aDestRange.Contains( aOldPos ) )
3803 {
3804 // References of these cells were not changed by ScTokenArray::AdjustReferenceOnMove()
3805 // Count back Positions
3806 SCCOL nRelPosX = aOldPos.Col();
3807 SCROW nRelPosY = aOldPos.Row();
3808 SCTAB nRelPosZ = aOldPos.Tab();
3809 ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, rDocument, aDestRange, rSource.aStart );
3810 aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ );
3811 bPosChanged = true;
3812 }
3813
3814 std::unique_ptr<ScTokenArray> pOld;
3815 if (pUndoDoc)
3816 pOld = pCode->Clone();
3817 bool bRefChanged = false;
3818
3821 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3822 {
3823 if( t->GetOpCode() == ocName )
3824 {
3825 const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3826 if (pName && pName->IsModified())
3827 bRefChanged = true;
3828 }
3829 else if( t->GetType() != svIndex )
3830 {
3832 ScComplexRefData& rRef = aMod.Ref();
3833 ScRange aAbs = rRef.toAbs(rDocument, aOldPos);
3834 bool bMod = (ScRefUpdate::UpdateTranspose(rDocument, rSource, rDest, aAbs) != UR_NOTHING || bPosChanged);
3835 if (bMod)
3836 {
3837 rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos); // based on the new anchor position.
3838 bRefChanged = true;
3839
3840 // Absolute sheet reference => set 3D flag.
3841 // More than one sheet referenced => has to have both 3D flags.
3842 // If end part has 3D flag => start part must have it too.
3843 // The same behavior as in ScTokenArray::AdjustReferenceOnMove() is used for 3D-Flags.
3844 rRef.Ref2.SetFlag3D(aAbs.aStart.Tab() != aAbs.aEnd.Tab() || !rRef.Ref2.IsTabRel());
3845 rRef.Ref1.SetFlag3D(
3846 (rSource.aStart.Tab() != rDest.Tab() && !bPosChanged)
3847 || !rRef.Ref1.IsTabRel() || rRef.Ref2.IsFlag3D());
3848 }
3849 }
3850 }
3851
3852 if (bRefChanged)
3853 {
3854 if (pUndoDoc)
3855 {
3856 // Similar to setOldCodeToUndo(), but it cannot be used due to the check
3857 // pUndoDoc->GetCellType(aPos) == CELLTYPE_FORMULA
3858 ScFormulaCell* pFCell = new ScFormulaCell(
3859 *pUndoDoc, aPos, pOld ? *pOld : ScTokenArray(*pUndoDoc), eTempGrammar, cMatrixFlag);
3860
3861 pFCell->aResult.SetToken( nullptr); // to recognize it as changed later (Cut/Paste!)
3862 pUndoDoc->SetFormulaCell(aPos, pFCell);
3863 }
3864
3865 bCompile = true;
3866 CompileTokenArray(); // also call StartListeningTo
3867 SetDirty();
3868 }
3869 else
3870 StartListeningTo( rDocument ); // Listener as previous
3871}
3872
3873void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3874{
3876
3877 bool bRefChanged = false;
3878
3881
3882 while( (t = aIter.GetNextReferenceOrName()) != nullptr )
3883 {
3884 if( t->GetOpCode() == ocName )
3885 {
3886 const ScRangeData* pName = rDocument.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex());
3887 if (pName && pName->IsModified())
3888 bRefChanged = true;
3889 }
3890 else if( t->GetType() != svIndex )
3891 {
3893 ScComplexRefData& rRef = aMod.Ref();
3894 ScRange aAbs = rRef.toAbs(rDocument, aPos);
3895 bool bMod = (ScRefUpdate::UpdateGrow(rArea, nGrowX, nGrowY, aAbs) != UR_NOTHING);
3896 if (bMod)
3897 {
3898 rRef.SetRange(rDocument.GetSheetLimits(), aAbs, aPos);
3899 bRefChanged = true;
3900 }
3901 }
3902 }
3903
3904 if (bRefChanged)
3905 {
3906 bCompile = true;
3907 CompileTokenArray(); // Also call StartListeningTo
3908 SetDirty();
3909 }
3910 else
3911 StartListeningTo( rDocument ); // Listener as previous
3912}
3913
3914// See also ScDocument::FindRangeNamesReferencingSheet()
3915static void lcl_FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes, const ScTokenArray* pCode, const ScDocument& rDoc,
3916 int nRecursion)
3917{
3918 FormulaTokenArrayPlainIterator aIter(*pCode);
3919 for (FormulaToken* p = aIter.First(); p; p = aIter.Next())
3920 {
3921 if (p->GetOpCode() == ocName)
3922 {
3923 sal_uInt16 nTokenIndex = p->GetIndex();
3924 SCTAB nTab = p->GetSheet();
3925 rIndexes.setUpdatedName( nTab, nTokenIndex);
3926
3927 if (nRecursion < 126) // whatever... 42*3
3928 {
3929 ScRangeData* pSubName = rDoc.FindRangeNameBySheetAndIndex( nTab, nTokenIndex);
3930 if (pSubName)
3931 lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), rDoc, nRecursion+1);
3932 }
3933 }
3934 }
3935}
3936
3938{
3939 lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0);
3940}
3941
3943{
3944 bChanged = b;
3945}
3946
3947void ScFormulaCell::SetCode( std::unique_ptr<ScTokenArray> pNew )
3948{
3949 assert(!mxGroup); // Don't call this if it's shared.
3950 delete pCode;
3951 pCode = pNew.release(); // takes ownership.
3952}
3953
3955{
3956 bRunning = bVal;
3957}
3958
3960{
3962 for( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3963 {
3964 OpCode eOp = p->GetOpCode();
3965 if ( eOp == ocDBArea || eOp == ocTableRef )
3966 {
3967 bCompile = true;
3968 CompileTokenArray(rCxt);
3969 SetDirty();
3970 break;
3971 }
3972 }
3973}
3974
3976{
3978 for ( FormulaToken* p = aIter.First(); p; p = aIter.Next() )
3979 {
3980 if ( p->GetOpCode() == ocColRowName )
3981 {
3982 bCompile = true;
3983 CompileTokenArray(rCxt);
3984 SetDirty();
3985 break;
3986 }
3987 }
3988}
3989
3994
3996{
3997 if (mxGroup)
3998 {
3999 // You can't create a new group if the cell is already a part of a group.
4000 // Is this a sign of some inconsistent or incorrect data structures? Or normal?
4001 SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
4002 return ScFormulaCellGroupRef();
4003 }
4004
4005 mxGroup.reset(new ScFormulaCellGroup);
4006 mxGroup->mpTopCell = this;
4007 mxGroup->mbInvariant = bInvariant;
4008 mxGroup->mnLength = nLen;
4009 mxGroup->mpCode = std::move(*pCode); // Move this to the shared location.
4010 delete pCode;
4011 pCode = &*mxGroup->mpCode;
4012 return mxGroup;
4013}
4014
4016{
4017 if (!xRef)
4018 {
4019 // Make this cell a non-grouped cell.
4020 if (mxGroup)
4021 pCode = mxGroup->mpCode->Clone().release();
4022
4023 mxGroup = xRef;
4024 return;
4025 }
4026
4027 // Group object has shared token array.
4028 if (!mxGroup)
4029 // Currently not shared. Delete the existing token array first.
4030 delete pCode;
4031
4032 mxGroup = xRef;
4033 pCode = &*mxGroup->mpCode;
4034 mxGroup->mnWeight = 0; // invalidate
4035}
4036
4038{
4039 // no Matrix formulae yet.
4041 return NotEqual;
4042
4043 // are these formulas at all similar ?
4044 if ( GetHash() != rOther.GetHash() )
4045 return NotEqual;
4046
4047 if (!pCode->IsShareable() || !rOther.pCode->IsShareable())
4048 return NotEqual;
4049
4050 FormulaToken **pThis = pCode->GetCode();
4051 sal_uInt16 nThisLen = pCode->GetCodeLen();
4052 FormulaToken **pOther = rOther.pCode->GetCode();
4053 sal_uInt16 nOtherLen = rOther.pCode->GetCodeLen();
4054
4055 if ( !pThis || !pOther )
4056 {
4057 // Error: no compiled code for cells !"
4058 return NotEqual;
4059 }
4060
4061 if ( nThisLen != nOtherLen )
4062 return NotEqual;
4063
4064 // No tokens can be an error cell so check error code, otherwise we could
4065 // end up with a series of equal error values instead of individual error
4066 // values. Also if for any reason different errors are set even if all
4067 // tokens are equal, the cells are not equal.
4068 if (pCode->GetCodeError() != rOther.pCode->GetCodeError())
4069 return NotEqual;
4070
4071 bool bInvariant = true;
4072
4073 // check we are basically the same function
4074 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4075 {
4076 formula::FormulaToken *pThisTok = pThis[i];
4077 formula::FormulaToken *pOtherTok = pOther[i];
4078
4079 if ( pThisTok->GetType() != pOtherTok->GetType() ||
4080 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4081 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4082 {
4083 // Incompatible type, op-code or param counts.
4084 return NotEqual;
4085 }
4086
4087 switch (pThisTok->GetType())
4088 {
4089 case formula::svMatrix:
4092 // Ignoring matrix and external references for now.
4093 return NotEqual;
4094
4096 {
4097 // Single cell reference.
4098 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4099 if (rRef != *pOtherTok->GetSingleRef())
4100 return NotEqual;
4101
4102 if (rRef.IsRowRel())
4103 bInvariant = false;
4104 }
4105 break;
4107 {
4108 // Range reference.
4109 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4110 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4111 if (rRef1 != *pOtherTok->GetSingleRef())
4112 return NotEqual;
4113
4114 if (rRef2 != *pOtherTok->GetSingleRef2())
4115 return NotEqual;
4116
4117 if (rRef1.IsRowRel())
4118 bInvariant = false;
4119
4120 if (rRef2.IsRowRel())
4121 bInvariant = false;
4122 }
4123 break;
4124 case formula::svDouble:
4125 {
4126 if(!rtl::math::approxEqual(pThisTok->GetDouble(), pOtherTok->GetDouble()))
4127 return NotEqual;
4128 }
4129 break;
4130 case formula::svString:
4131 {
4132 if(pThisTok->GetString() != pOtherTok->GetString())
4133 return NotEqual;
4134 }
4135 break;
4136 case formula::svIndex:
4137 {
4138 if(pThisTok->GetIndex() != pOtherTok->GetIndex() || pThisTok->GetSheet() != pOtherTok->GetSheet())
4139 return NotEqual;
4140 }
4141 break;
4142 case formula::svByte:
4143 {
4144 if(pThisTok->GetByte() != pOtherTok->GetByte())
4145 return NotEqual;
4146 }
4147 break;
4149 {
4150 if (pThisTok->GetExternal() != pOtherTok->GetExternal())
4151 return NotEqual;
4152
4153 if (pThisTok->GetByte() != pOtherTok->GetByte())
4154 return NotEqual;
4155 }
4156 break;
4157 case formula::svError:
4158 {
4159 if (pThisTok->GetError() != pOtherTok->GetError())
4160 return NotEqual;
4161 }
4162 break;
4163 default:
4164 ;
4165 }
4166 }
4167
4168 // If still the same, check lexical names as different names may result in
4169 // identical RPN code.
4170
4171 pThis = pCode->GetArray();
4172 nThisLen = pCode->GetLen();
4173 pOther = rOther.pCode->GetArray();
4174 nOtherLen = rOther.pCode->GetLen();
4175
4176 if ( !pThis || !pOther )
4177 {
4178 // Error: no code for cells !"
4179 return NotEqual;
4180 }
4181
4182 if ( nThisLen != nOtherLen )
4183 return NotEqual;
4184
4185 for ( sal_uInt16 i = 0; i < nThisLen; i++ )
4186 {
4187 formula::FormulaToken *pThisTok = pThis[i];
4188 formula::FormulaToken *pOtherTok = pOther[i];
4189
4190 if ( pThisTok->GetType() != pOtherTok->GetType() ||
4191 pThisTok->GetOpCode() != pOtherTok->GetOpCode() ||
4192 pThisTok->GetParamCount() != pOtherTok->GetParamCount() )
4193 {
4194 // Incompatible type, op-code or param counts.
4195 return NotEqual;
4196 }
4197
4198 switch (pThisTok->GetType())
4199 {
4200 // ScCompiler::HandleIIOpCode() may optimize some refs only in RPN code,
4201 // resulting in identical RPN references that could lead to creating
4202 // a formula group from formulas that should not be merged into a group,
4203 // so check also the formula itself.
4205 {
4206 // Single cell reference.
4207 const ScSingleRefData& rRef = *pThisTok->GetSingleRef();
4208 if (rRef != *pOtherTok->GetSingleRef())
4209 return NotEqual;
4210
4211 if (rRef.IsRowRel())
4212 bInvariant = false;
4213 }
4214 break;
4216 {
4217 // Range reference.
4218 const ScSingleRefData& rRef1 = *pThisTok->GetSingleRef();
4219 const ScSingleRefData& rRef2 = *pThisTok->GetSingleRef2();
4220 if (rRef1 != *pOtherTok->GetSingleRef())
4221 return NotEqual;
4222
4223 if (rRef2 != *pOtherTok->GetSingleRef2())
4224 return NotEqual;
4225
4226 if (rRef1.IsRowRel())
4227 bInvariant = false;
4228
4229 if (rRef2.IsRowRel())
4230 bInvariant = false;
4231 }
4232 break;
4233 // All index tokens are names. Different categories already had
4234 // different OpCode values.
4235 case formula::svIndex:
4236 {
4237 if (pThisTok->GetIndex() != pOtherTok->GetIndex())
4238 return NotEqual;
4239 switch (pThisTok->GetOpCode())
4240 {
4241 case ocTableRef:
4242 // nothing, sheet value assumed as -1, silence
4243 // ScTableRefToken::GetSheet() SAL_WARN about
4244 // unhandled
4245 ;
4246 break;
4247 default: // ocName, ocDBArea
4248 if (pThisTok->GetSheet() != pOtherTok->GetSheet())
4249 return NotEqual;
4250 }
4251 }
4252 break;
4253 default:
4254 ;
4255 }
4256 }
4257
4258 return bInvariant ? EqualInvariant : EqualRelativeRef;
4259}
4260
4261namespace {
4262
4263// Split N into optimally equal-sized pieces, each not larger than K.
4264// Return value P is number of pieces. A returns the number of pieces
4265// one larger than N/P, 0..P-1.
4266
4267int splitup(int N, int K, int& A)
4268{
4269 assert(N > 0);
4270 assert(K > 0);
4271
4272 A = 0;
4273
4274 if (N <= K)
4275 return 1;
4276
4277 const int ideal_num_parts = N / K;
4278 if (ideal_num_parts * K == N)
4279 return ideal_num_parts;
4280
4281 const int num_parts = ideal_num_parts + 1;
4282 const int nominal_part_size = N / num_parts;
4283
4284 A = N - num_parts * nominal_part_size;
4285
4286 return num_parts;
4287}
4288
4289struct ScDependantsCalculator
4290{
4291 ScDocument& mrDoc;
4292 const ScTokenArray& mrCode;
4293 const ScFormulaCellGroupRef& mxGroup;
4294 const SCROW mnLen;
4295 const ScAddress& mrPos;
4296 const bool mFromFirstRow;
4297 const SCROW mnStartOffset;
4298 const SCROW mnEndOffset;
4299 const SCROW mnSpanLen;
4300
4301 ScDependantsCalculator(ScDocument& rDoc, const ScTokenArray& rCode, const ScFormulaCell& rCell,
4302 const ScAddress& rPos, bool fromFirstRow, SCROW nStartOffset, SCROW nEndOffset) :
4303 mrDoc(rDoc),
4304 mrCode(rCode),
4305 mxGroup(rCell.GetCellGroup()),
4306 mnLen(mxGroup->mnLength),
4307 mrPos(rPos),
4308 // ScColumn::FetchVectorRefArray() always fetches data from row 0, even if the data is used
4309 // only from further rows. This data fetching could also lead to Interpret() calls, so
4310 // in OpenCL mode the formula in practice depends on those cells too.
4311 mFromFirstRow(fromFirstRow),
4312 mnStartOffset(nStartOffset),
4313 mnEndOffset(nEndOffset),
4314 mnSpanLen(nEndOffset - nStartOffset + 1)
4315 {
4316 }
4317
4318 // FIXME: copy-pasted from ScGroupTokenConverter. factor out somewhere else
4319 // (note already modified a bit, mFromFirstRow)
4320
4321 // I think what this function does is to check whether the relative row reference nRelRow points
4322 // to a row that is inside the range of rows covered by the formula group.
4323
4324 bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
4325 {
4326 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4327 return false;
4328
4329 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4330
4331 if (nRelRow <= 0)
4332 {
4333 SCROW nTest = nEndRow;
4334 nTest += nRelRow;
4335 if (nTest >= mrPos.Row())
4336 return true;
4337 }
4338 else
4339 {
4340 SCROW nTest = mrPos.Row(); // top row.
4341 nTest += nRelRow;
4342 if (nTest <= nEndRow)
4343 return true;
4344 // If pointing below the formula, it's always included if going from first row.
4345 if (mFromFirstRow)
4346 return true;
4347 }
4348
4349 return false;
4350 }
4351
4352 // FIXME: another copy-paste
4353
4354 // And this correspondingly checks whether an absolute row is inside the range of rows covered
4355 // by the formula group.
4356
4357 bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
4358 {
4359 if (rRefPos.Col() != mrPos.Col() || rRefPos.Tab() != mrPos.Tab())
4360 return false;
4361
4362 SCROW nEndRow = mrPos.Row() + mnLen - 1;
4363
4364 if (rRefPos.Row() < mrPos.Row())
4365 return false;
4366
4367 // If pointing below the formula, it's always included if going from first row.
4368 if (rRefPos.Row() > nEndRow && !mFromFirstRow)
4369 return false;
4370
4371 return true;
4372 }
4373
4374 // Checks if the doubleref engulfs all of formula group cells
4375 // Note : does not check if there is a partial overlap, that can be done by calling
4376 // isSelfReference[Absolute|Relative]() on both the start and end of the double ref
4377 bool isDoubleRefSpanGroupRange(const ScRange& rAbs, bool bIsRef1RowRel, bool bIsRef2RowRel)
4378 {
4379 if (rAbs.aStart.Col() > mrPos.Col() || rAbs.aEnd.Col() < mrPos.Col()
4380 || rAbs.aStart.Tab() > mrPos.Tab() || rAbs.aEnd.Tab() < mrPos.Tab())
4381 {
4382 return false;
4383 }
4384
4385 SCROW nStartRow = mrPos.Row();
4386 SCROW nEndRow = nStartRow + mnLen - 1;
4387 SCROW nRefStartRow = rAbs.aStart.Row();
4388 SCROW nRefEndRow = rAbs.aEnd.Row();
4389
4390 if (bIsRef1RowRel && bIsRef2RowRel &&
4391 ((nRefStartRow <= nStartRow && nRefEndRow >= nEndRow) ||
4392 ((nRefStartRow + mnLen - 1) <= nStartRow &&
4393 (nRefEndRow + mnLen - 1) >= nEndRow)))
4394 return true;
4395
4396 if (!bIsRef1RowRel && nRefStartRow <= nStartRow &&
4397 (nRefEndRow >= nEndRow || (nRefEndRow + mnLen - 1) >= nEndRow))
4398 return true;
4399
4400 if (!bIsRef2RowRel &&
4401 nRefStartRow <= nStartRow && nRefEndRow >= nEndRow)
4402 return true;
4403
4404 // If going from first row, the referenced range must be entirely above the formula,
4405 // otherwise the formula would be included.
4406 if (mFromFirstRow && nRefEndRow >= nStartRow)
4407 return true;
4408
4409 return false;
4410 }
4411
4412 // FIXME: another copy-paste
4413 SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
4414 {
4415 SCROW nLastRow = nRow + nRowLen - 1; // current last row.
4416 nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
4417 if (nLastRow < (nRow + nRowLen - 1))
4418 {
4419 // This can end up negative! Was that the original intent, or
4420 // is it accidental? Was it not like that originally but the
4421 // surrounding conditions changed?
4422 nRowLen = nLastRow - nRow + 1;
4423 // Anyway, let's assume it doesn't make sense to return a
4424 // negative or zero value here.
4425 if (nRowLen <= 0)
4426 nRowLen = 1;
4427 }
4428 else if (nLastRow == 0)
4429 // Column is empty.
4430 nRowLen = 1;
4431
4432 return nRowLen;
4433 }
4434
4435 bool DoIt()
4436 {
4437 // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx
4438
4439 ScRangeList aRangeList;
4440
4441 // Self references should be checked by considering the entire formula-group not just the provided span.
4442 bool bHasSelfReferences = false;
4443 bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc();
4444
4445 FormulaToken** pRPNArray = mrCode.GetCode();
4446 sal_uInt16 nCodeLen = mrCode.GetCodeLen();
4447 for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx)
4448 {
4449 auto p = pRPNArray[nTokenIdx];
4450 if (!bInDocShellRecalc)
4451 {
4452 // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective
4453 // of the result of the condition expression.
4454 // This is a perf problem if we *don't* intent on recalc'ing all dirty cells
4455 // in the document. So lets disable threading and stop dependency evaluation if
4456 // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc()
4457 // for formulae with IF/IFS/SWITCH
4458 OpCode nOpCode = p->GetOpCode();
4459 if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS)
4460 return false;
4461 }
4462
4463 switch (p->GetType())
4464 {
4465 case svSingleRef:
4466 {
4467 ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1
4468 if( aRef.IsDeleted())
4469 return false;
4470 ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos);
4471
4472 if (!mrDoc.HasTable(aRefPos.Tab()))
4473 return false; // or true?
4474
4475 if (aRef.IsRowRel())
4476 {
4477 if (isSelfReferenceRelative(aRefPos, aRef.Row()))
4478 {
4479 bHasSelfReferences = true;
4480 continue;
4481 }
4482
4483 // Trim data array length to actual data range.
4484 SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen);
4485
4486 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(),
4487 aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab()));
4488 }
4489 else
4490 {
4491 if (isSelfReferenceAbsolute(aRefPos))
4492 {
4493 bHasSelfReferences = true;
4494 continue;
4495 }
4496
4497 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab()));
4498 }
4499 }
4500 break;
4501 case svDoubleRef:
4502 {
4503 ScComplexRefData aRef = *p->GetDoubleRef();
4504 if( aRef.IsDeleted())
4505 return false;
4506 ScRange aAbs = aRef.toAbs(mrDoc, mrPos);
4507
4508 // Multiple sheet
4509 if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
4510 return false;
4511
4512 bool bIsRef1RowRel = aRef.Ref1.IsRowRel();
4513 // Check for self reference.
4514 if (bIsRef1RowRel)
4515 {
4516 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
4517 {
4518 bHasSelfReferences = true;
4519 continue;
4520 }
4521 }
4522 else if (isSelfReferenceAbsolute(aAbs.aStart))
4523 {
4524 bHasSelfReferences = true;
4525 continue;
4526 }
4527
4528 bool bIsRef2RowRel = aRef.Ref2.IsRowRel();
4529 if (bIsRef2RowRel)
4530 {
4531 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
4532 {
4533 bHasSelfReferences = true;
4534 continue;
4535 }
4536 }
4537 else if (isSelfReferenceAbsolute(aAbs.aEnd))
4538 {
4539 bHasSelfReferences = true;
4540 continue;
4541 }
4542
4543 if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel))
4544 {
4545 bHasSelfReferences = true;
4546 continue;
4547 }
4548
4549 // The first row that will be referenced through the doubleref.
4550 SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row();
4551 // The last row that will be referenced through the doubleref.
4552 SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row();
4553 // Number of rows to be evaluated from nFirstRefRow.
4554 SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1;
4555 assert(nArrayLength > 0);
4556
4557 // Trim trailing empty rows.
4558 nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength);
4559
4560 aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(),
4561 aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab()));
4562 }
4563 break;
4564 default:
4565 break;
4566 }
4567 }
4568
4569 // Compute dependencies irrespective of the presence of any self references.
4570 // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now.
4571 // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications.
4572 for (size_t i = 0; i < aRangeList.size(); ++i)
4573 {
4574 const ScRange & rRange = aRangeList[i];
4575 assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
4576 for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++)
4577 {
4578 SCROW nStartRow = rRange.aStart.Row();
4579 SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
4580 if( mFromFirstRow )
4581 { // include also all previous rows
4582 nLength += nStartRow;
4583 nStartRow = 0;
4584 }
4585 if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()),
4586 nLength, mxGroup))
4587 return false;
4588 }
4589 }
4590
4591 if (bHasSelfReferences)
4592 mxGroup->mbPartOfCycle = true;
4593
4594 return !bHasSelfReferences;
4595 }
4596};
4597
4598} // anonymous namespace
4599
4601{
4602 if (!mxGroup || !pCode)
4603 return false;
4604
4605 auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this);
4606 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4607
4608 if (mxGroup->mbPartOfCycle)
4609 {
4610 aScope.addMessage("This formula-group is part of a cycle");
4611 return false;
4612 }
4613
4614 if (mxGroup->meCalcState == sc::GroupCalcDisabled)
4615 {
4616 static constexpr OUStringLiteral MESSAGE = u"group calc disabled";
4617 aScope.addMessage(MESSAGE);
4618 return false;
4619 }
4620
4621 // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests
4623 if (forceType == ForceCalculationCore
4624 || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize
4625 && forceType != ForceCalculationOpenCL
4626 && forceType != ForceCalculationThreads))
4627 {
4628 mxGroup->meCalcState = sc::GroupCalcDisabled;
4629 aScope.addGroupSizeThresholdMessage(*this);
4630 return false;
4631 }
4632
4634 {
4635 mxGroup->meCalcState = sc::GroupCalcDisabled;
4636 aScope.addMessage("matrix skipped");
4637 return false;
4638 }
4639
4640 if( forceType != ForceCalculationNone )
4641 {
4642 // ScConditionEntry::Interpret() creates a temporary cell and interprets it
4643 // without it actually being in the document at the specified position.
4644 // That would confuse opencl/threading code, as they refer to the cell group
4645 // also using the position. This is normally not triggered (single cells
4646 // are normally not in a cell group), but if forced, check for this explicitly.
4647 if( rDocument.GetFormulaCell( aPos ) != this )
4648 {
4649 mxGroup->meCalcState = sc::GroupCalcDisabled;
4650 aScope.addMessage("cell not in document");
4651 return false;
4652 }
4653 }
4654
4655 // Get rid of -1's in offsets (defaults) or any invalid offsets.
4656 SCROW nMaxOffset = mxGroup->mnLength - 1;
4657 nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset);
4658 nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset);
4659
4660 if (nEndOffset < nStartOffset)
4661 {
4662 nStartOffset = 0;
4663 nEndOffset = nMaxOffset;
4664 }
4665
4666 if (nEndOffset == nStartOffset && forceType == ForceCalculationNone)
4667 return false; // Do not use threads for a single row.
4668
4669 // Guard against endless recursion of Interpret() calls, for this to work
4670 // ScFormulaCell::InterpretFormulaGroup() must never be called through
4671 // anything else than ScFormulaCell::Interpret(), same as
4672 // ScFormulaCell::InterpretTail()
4673 RecursionCounter aRecursionCounter( rRecursionHelper, this);
4674
4675 bool bDependencyComputed = false;
4676 bool bDependencyCheckFailed = false;
4677
4678 // Preference order: First try OpenCL, then threading.
4679 // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default.
4680 if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed))
4681 return true;
4682
4683 if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset))
4684 return true;
4685
4686 return false;
4687}
4688
4690 SCROW nStartOffset, SCROW nEndOffset,
4691 bool bCalcDependencyOnly)
4692{
4693 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4694 // iterate over code in the formula ...
4695 // ensure all input is pre-calculated -
4696 // to avoid writing during the calculation
4697 if (bCalcDependencyOnly)
4698 {
4699 // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding
4700 // "ScFormulaGroupCycleCheckGuard" for this formula-group.
4701 // (We can only reach here from a multi-group dependency evaluation attempt).
4702 // (These two have to be in pairs always for any given formula-group)
4703 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4704 return aCalculator.DoIt();
4705 }
4706
4707 bool bOKToParallelize = false;
4708 {
4709 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this);
4710 if (mxGroup->mbPartOfCycle)
4711 {
4712 mxGroup->meCalcState = sc::GroupCalcDisabled;
4713 rScope.addMessage("found circular formula-group dependencies");
4714 return false;
4715 }
4716
4717 ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper);
4718 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset);
4719 bOKToParallelize = aCalculator.DoIt();
4720
4721 }
4722
4723 if (rRecursionHelper.IsInRecursionReturn())
4724 {
4725 mxGroup->meCalcState = sc::GroupCalcDisabled;
4726 rScope.addMessage("Recursion limit reached, cannot thread this formula group now");
4727 return false;
4728 }
4729
4730 if (mxGroup->mbPartOfCycle)
4731 {
4732 mxGroup->meCalcState = sc::GroupCalcDisabled;
4733 rScope.addMessage("found circular formula-group dependencies");
4734 return false;
4735 }
4736
4737 if (!rRecursionHelper.AreGroupsIndependent())
4738 {
4739 // This call resulted from a dependency calculation for a multigroup-threading attempt,
4740 // but found dependency among the groups.
4741 rScope.addMessage("multi-group-dependency failed");
4742 return false;
4743 }
4744
4745 if (!bOKToParallelize)
4746 {
4747 mxGroup->meCalcState = sc::GroupCalcDisabled;
4748 rScope.addMessage("could not do new dependencies calculation thing");
4749 return false;
4750 }
4751
4752 return true;
4753}
4754
4757 std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft)
4758{
4759 const SCROW nLen = xGroup->mnLength;
4760 const sal_Int32 nWt = xGroup->mnWeight;
4761 ScAddress aAddr(xGroup->mpTopCell->aPos);
4762
4763 SCCOL nColRet = aAddr.Col();
4764
4765 const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1;
4766 if (bLeft)
4767 --nColRet;
4768 else
4769 ++nColRet;
4770
4771 while (nColRet >= 0 && nColRet <= nMaxCol)
4772 {
4773 aAddr.SetCol(nColRet);
4774 const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr);
4775 if (!pCell)
4776 break;
4777
4778 if (!pCell->NeedsInterpret())
4779 break;
4780
4781 const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup();
4782 if (!xNGroup)
4783 break;
4784
4785 if (!pCell->GetCode()->IsEnabledForThreading())
4786 break;
4787
4788 if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row())
4789 break;
4790
4791 const SCROW nNLen = xNGroup->mnLength;
4792 const sal_Int32 nNWt = pCell->GetWeight();
4793 if (nNLen != nLen || nNWt != nWt)
4794 break;
4795
4796 rFGSet.insert(xNGroup.get());
4797 rFGMap[nColRet] = xNGroup->mpTopCell;
4798
4799 if (bLeft)
4800 --nColRet;
4801 else
4802 ++nColRet;
4803 }
4804
4805 if (bLeft)
4806 ++nColRet;
4807 else
4808 --nColRet;
4809
4810 return nColRet;
4811}
4812
4813// To be called only from InterpretFormulaGroup().
4815 bool& bDependencyComputed,
4816 bool& bDependencyCheckFailed,
4817 SCROW nStartOffset,
4818 SCROW nEndOffset)
4819{
4820 static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
4821 if (!bDependencyCheckFailed && !bThreadingProhibited &&
4824 {
4825 if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset))
4826 {
4827 bDependencyComputed = true;
4828 bDependencyCheckFailed = true;
4829 return false;
4830 }
4831
4832 bDependencyComputed = true;
4833
4834 // Then do the threaded calculation
4835
4836 class Executor : public comphelper::ThreadTask
4837 {
4838 private:
4839 const unsigned mnThisThread;
4840 const unsigned mnThreadsTotal;
4841 ScDocument* mpDocument;
4843 const ScAddress& mrTopPos;
4844 SCCOL mnStartCol;
4845 SCCOL mnEndCol;
4846 SCROW mnStartOffset;
4847 SCROW mnEndOffset;
4848
4849 public:
4850 Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag,
4851 unsigned nThisThread,
4852 unsigned nThreadsTotal,
4853 ScDocument* pDocument2,
4854 ScInterpreterContext* pContext,
4855 const ScAddress& rTopPos,
4856 SCCOL nStartCol,
4857 SCCOL nEndCol,
4858 SCROW nStartOff,
4859 SCROW nEndOff) :
4861 mnThisThread(nThisThread),
4862 mnThreadsTotal(nThreadsTotal),
4863 mpDocument(pDocument2),
4864 mpContext(pContext),
4865 mrTopPos(rTopPos),
4866 mnStartCol(nStartCol),
4867 mnEndCol(nEndCol),
4868 mnStartOffset(nStartOff),
4869 mnEndOffset(nEndOff)
4870 {
4871 }
4872
4873 virtual void doWork() override
4874 {
4875 ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(),
4876 mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab());
4877 mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal);
4878 }
4879
4880 };
4881
4883
4885 sal_Int32 nThreadCount = rThreadPool.getWorkerCount();
4886
4887 SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads");
4888
4890 std::map<SCCOL, ScFormulaCell*> aFGMap;
4891 aFGSet.insert(mxGroup.get());
4892
4893 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper();
4894 SCCOL nColStart = aPos.Col();
4895 SCCOL nColEnd = nColStart;
4896 if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc())
4897 {
4898 nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true);
4899 nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false);
4900 }
4901
4902 if (nColStart != nColEnd)
4903 {
4904 ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet);
4905 for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol)
4906 {
4907 if (nCurrCol == aPos.Col())
4908 continue;
4909
4910 bool bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, true);
4911 if (!bFGOK || !aGuard.AreGroupsIndependent())
4912 {
4913 nColEnd = nColStart =