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