1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <config_feature_opencl.h> 21 22 #include <sal/config.h> 23 #include <sal/log.hxx> 24 #include <osl/diagnose.h> 25 26 #include <cassert> 27 #include <cstdlib> 28 29 #include <formulacell.hxx> 30 #include <grouptokenconverter.hxx> 31 32 #include <compiler.hxx> 33 #include <document.hxx> 34 #include <cellvalue.hxx> 35 #include <interpre.hxx> 36 #include <macromgr.hxx> 37 #include <refupdat.hxx> 38 #include <recursionhelper.hxx> 39 #include <docoptio.hxx> 40 #include <rangenam.hxx> 41 #include <rangelst.hxx> 42 #include <dbdata.hxx> 43 #include <progress.hxx> 44 #include <scmatrix.hxx> 45 #include <rechead.hxx> 46 #include <scitems.hxx> 47 #include <validat.hxx> 48 #include <editutil.hxx> 49 #include <chgtrack.hxx> 50 #include <tokenarray.hxx> 51 52 #include <comphelper/threadpool.hxx> 53 #include <editeng/editobj.hxx> 54 #include <formula/errorcodes.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> 62 #include <tokenstringcontext.hxx> 63 #include <refhint.hxx> 64 #include <listenerquery.hxx> 65 #include <listenerqueryids.hxx> 66 #include <grouparealistener.hxx> 67 #include <formulalogger.hxx> 68 #include <com/sun/star/sheet/FormulaLanguage.hpp> 69 70 #if HAVE_FEATURE_OPENCL 71 #include <opencl/openclwrapper.hxx> 72 #endif 73 74 #include <memory> 75 #include <map> 76 77 using namespace formula; 78 79 #define DEBUG_CALCULATION 0 80 #if DEBUG_CALCULATION 81 static bool bDebugCalculationActive = false; // Set to true for global active init, 82 static ScAddress aDebugCalculationTriggerAddress(1,2,0); // or on cell Sheet1.B3, whatever you like 83 84 struct DebugCalculationEntry 85 { 86 ScAddress maPos; 87 OUString maResult; 88 const ScDocument& mrDoc; 89 sal_uInt32 mnGroup; 90 sal_uInt16 mnRecursion; 91 92 DebugCalculationEntry( const ScAddress& rPos, ScDocument& rDoc, sal_uInt32 nGroup ) : 93 maPos(rPos), 94 mrDoc(rDoc), 95 mnGroup(nGroup), 96 mnRecursion(rDoc.GetRecursionHelper().GetRecursionCount()) 97 { 98 } 99 }; 100 101 /** Debug/dump formula cell calculation chain. 102 Either, somewhere set aDC.mbActive=true, or 103 aDC.maTrigger=ScAddress(col,row,tab) of interest from where to start. 104 This does not work for deep recursion > MAXRECURSION, the results are 105 somewhat... funny... ;) 106 */ 107 static struct DebugCalculation 108 { 109 std::vector< DebugCalculationEntry > mvPos; 110 std::vector< DebugCalculationEntry > mvResults; 111 ScAddress maTrigger; 112 sal_uInt32 mnGroup; 113 bool mbActive; 114 bool mbSwitchOff; 115 bool mbPrint; 116 bool mbPrintResults; 117 118 DebugCalculation() : mnGroup(0), mbActive(bDebugCalculationActive), mbSwitchOff(false), 119 mbPrint(true), mbPrintResults(false) {} 120 121 /** Print chain in encountered dependency order. */ 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 133 /** Print chain results. */ 134 void printResults() const 135 { 136 for (auto const& it : mvResults) 137 { 138 OUString aStr( it.maPos.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, &it.mrDoc)); 139 aStr += " (" + it.maResult + ")"; 140 fprintf( stderr, "%s, ", aStr.toUtf8().getStr()); 141 } 142 fprintf( stderr, "%s", "END\n"); 143 } 144 145 void storeResult( const svl::SharedString& rStr ) 146 { 147 if (mbActive && !mvPos.empty()) 148 mvPos.back().maResult = "\"" + rStr.getString() + "\""; 149 } 150 151 void storeResult( const double& fVal ) 152 { 153 if (mbActive && !mvPos.empty()) 154 mvPos.back().maResult = rtl::math::doubleToUString( fVal, rtl_math_StringFormat_G, 2, '.', true); 155 } 156 157 void storeResultError( FormulaError nErr ) 158 { 159 if (mbActive && !mvPos.empty()) 160 mvPos.back().maResult = "Err:" + OUString::number( int( nErr )); 161 } 162 163 void enterGroup() 164 { 165 ++mnGroup; 166 } 167 168 void leaveGroup() 169 { 170 --mnGroup; 171 } 172 173 } aDC; 174 175 struct DebugCalculationStacker 176 { 177 DebugCalculationStacker( const ScAddress& rPos, ScDocument& rDoc ) 178 { 179 if (!aDC.mbActive && rPos == aDC.maTrigger) 180 aDC.mbActive = aDC.mbSwitchOff = true; 181 if (aDC.mbActive) 182 { 183 aDC.mvPos.push_back( DebugCalculationEntry( rPos, rDoc, aDC.mnGroup)); 184 aDC.mbPrint = true; 185 } 186 } 187 188 ~DebugCalculationStacker() 189 { 190 if (aDC.mbActive) 191 { 192 if (!aDC.mvPos.empty()) 193 { 194 if (aDC.mbPrint) 195 { 196 aDC.print(); 197 aDC.mbPrint = false; 198 } 199 if (aDC.mbPrintResults) 200 { 201 // Store results until final result is available, reversing order. 202 aDC.mvResults.push_back( aDC.mvPos.back()); 203 } 204 aDC.mvPos.pop_back(); 205 if (aDC.mbPrintResults && aDC.mvPos.empty()) 206 { 207 aDC.printResults(); 208 std::vector< DebugCalculationEntry >().swap( aDC.mvResults); 209 } 210 if (aDC.mbSwitchOff && aDC.mvPos.empty()) 211 aDC.mbActive = false; 212 } 213 } 214 } 215 }; 216 #endif 217 218 namespace { 219 220 // More or less arbitrary, of course all recursions must fit into available 221 // stack space (which is what on all systems we don't know yet?). Choosing a 222 // lower value may be better than trying a much higher value that also isn't 223 // sufficient but temporarily leads to high memory consumption. On the other 224 // hand, if the value fits all recursions, execution is quicker as no resumes 225 // are necessary. Could be made a configurable option. 226 // Allow for a year's calendar (366). 227 const sal_uInt16 MAXRECURSION = 400; 228 229 typedef SCCOLROW(*DimensionSelector)(const ScDocument&, const ScAddress&, const ScSingleRefData&); 230 231 SCCOLROW lcl_GetCol(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData) 232 { 233 return rData.toAbs(rDoc, rPos).Col(); 234 } 235 236 SCCOLROW lcl_GetRow(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData) 237 { 238 return rData.toAbs(rDoc, rPos).Row(); 239 } 240 241 SCCOLROW lcl_GetTab(const ScDocument& rDoc, const ScAddress& rPos, const ScSingleRefData& rData) 242 { 243 return rData.toAbs(rDoc, rPos).Tab(); 244 } 245 246 /** Check if both references span the same range in selected dimension. 247 */ 248 bool 249 lcl_checkRangeDimension( 250 const ScDocument& rDoc, 251 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2, 252 const DimensionSelector aWhich) 253 { 254 return aWhich(rDoc, rPos, rRef1.Ref1) == aWhich(rDoc, rPos, rRef2.Ref1) && 255 aWhich(rDoc, rPos, rRef1.Ref2) == aWhich(rDoc, rPos, rRef2.Ref2); 256 } 257 258 bool 259 lcl_checkRangeDimensions( 260 const ScDocument& rDoc, 261 const ScAddress& rPos, const SingleDoubleRefProvider& rRef1, const SingleDoubleRefProvider& rRef2, 262 bool& bCol, bool& bRow, bool& bTab) 263 { 264 const bool bSameCols(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetCol)); 265 const bool bSameRows(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetRow)); 266 const bool bSameTabs(lcl_checkRangeDimension(rDoc, rPos, rRef1, rRef2, lcl_GetTab)); 267 268 // Test if exactly two dimensions are equal 269 if (int(bSameCols) + int(bSameRows) + int(bSameTabs) == 2) 270 { 271 bCol = !bSameCols; 272 bRow = !bSameRows; 273 bTab = !bSameTabs; 274 return true; 275 } 276 return false; 277 } 278 279 /** Check if references in given reference list can possibly 280 form a range. To do that, two of their dimensions must be the same. 281 */ 282 bool 283 lcl_checkRangeDimensions( 284 const ScDocument& rDoc, const ScAddress& rPos, 285 const std::vector<formula::FormulaToken*>::const_iterator& rBegin, 286 const std::vector<formula::FormulaToken*>::const_iterator& rEnd, 287 bool& bCol, bool& bRow, bool& bTab) 288 { 289 std::vector<formula::FormulaToken*>::const_iterator aCur(rBegin); 290 ++aCur; 291 const SingleDoubleRefProvider aRef(**rBegin); 292 bool bOk(false); 293 { 294 const SingleDoubleRefProvider aRefCur(**aCur); 295 bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bCol, bRow, bTab); 296 } 297 while (bOk && aCur != rEnd) 298 { 299 const SingleDoubleRefProvider aRefCur(**aCur); 300 bool bColTmp(false); 301 bool bRowTmp(false); 302 bool bTabTmp(false); 303 bOk = lcl_checkRangeDimensions(rDoc, rPos, aRef, aRefCur, bColTmp, bRowTmp, bTabTmp); 304 bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp); 305 ++aCur; 306 } 307 308 return bOk && aCur == rEnd; 309 } 310 311 class LessByReference 312 { 313 const ScDocument& mrDoc; 314 ScAddress maPos; 315 DimensionSelector maFunc; 316 public: 317 LessByReference(const ScDocument& rDoc, const ScAddress& rPos, const DimensionSelector& rFunc) : 318 mrDoc(rDoc), maPos(rPos), maFunc(rFunc) {} 319 320 bool operator() (const formula::FormulaToken* pRef1, const formula::FormulaToken* pRef2) 321 { 322 const SingleDoubleRefProvider aRef1(*pRef1); 323 const SingleDoubleRefProvider aRef2(*pRef2); 324 return maFunc(mrDoc, maPos, aRef1.Ref1) < maFunc(mrDoc, maPos, aRef2.Ref1); 325 } 326 }; 327 328 /** 329 * Returns true if range denoted by token p2 starts immediately after range 330 * denoted by token p1. Dimension, in which the comparison takes place, is 331 * given by maFunc. 332 */ 333 class AdjacentByReference 334 { 335 const ScDocument& mrDoc; 336 ScAddress maPos; 337 DimensionSelector maFunc; 338 public: 339 AdjacentByReference(const ScDocument& rDoc, const ScAddress& rPos, DimensionSelector aFunc) : 340 mrDoc(rDoc), maPos(rPos), maFunc(aFunc) {} 341 342 bool operator() (const formula::FormulaToken* p1, const formula::FormulaToken* p2) 343 { 344 const SingleDoubleRefProvider aRef1(*p1); 345 const SingleDoubleRefProvider aRef2(*p2); 346 return maFunc(mrDoc, maPos, aRef2.Ref1) - maFunc(mrDoc, maPos, aRef1.Ref2) == 1; 347 } 348 }; 349 350 bool 351 lcl_checkIfAdjacent( 352 const ScDocument& rDoc, 353 const ScAddress& rPos, const std::vector<formula::FormulaToken*>& rReferences, const DimensionSelector aWhich) 354 { 355 auto aBegin(rReferences.cbegin()); 356 auto aEnd(rReferences.cend()); 357 auto aBegin1(aBegin); 358 ++aBegin1; 359 --aEnd; 360 return std::equal(aBegin, aEnd, aBegin1, AdjacentByReference(rDoc, rPos, aWhich)); 361 } 362 363 void 364 lcl_fillRangeFromRefList( 365 const ScDocument& rDoc, 366 const ScAddress& aPos, const std::vector<formula::FormulaToken*>& rReferences, ScRange& rRange) 367 { 368 const ScSingleRefData aStart( 369 SingleDoubleRefProvider(*rReferences.front()).Ref1); 370 rRange.aStart = aStart.toAbs(rDoc, aPos); 371 const ScSingleRefData aEnd( 372 SingleDoubleRefProvider(*rReferences.back()).Ref2); 373 rRange.aEnd = aEnd.toAbs(rDoc, aPos); 374 } 375 376 bool 377 lcl_refListFormsOneRange( 378 const ScDocument& rDoc, 379 const ScAddress& rPos, std::vector<formula::FormulaToken*>& rReferences, 380 ScRange& rRange) 381 { 382 if (rReferences.size() == 1) 383 { 384 lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange); 385 return true; 386 } 387 388 bool bCell(false); 389 bool bRow(false); 390 bool bTab(false); 391 if (lcl_checkRangeDimensions(rDoc, rPos, rReferences.begin(), rReferences.end(), bCell, bRow, bTab)) 392 { 393 DimensionSelector aWhich; 394 if (bCell) 395 { 396 aWhich = lcl_GetCol; 397 } 398 else if (bRow) 399 { 400 aWhich = lcl_GetRow; 401 } 402 else if (bTab) 403 { 404 aWhich = lcl_GetTab; 405 } 406 else 407 { 408 OSL_FAIL( "lcl_checkRangeDimensions shouldn't allow that!"); 409 aWhich = lcl_GetRow; // initialize to avoid warning 410 } 411 412 // Sort the references by start of range 413 std::sort(rReferences.begin(), rReferences.end(), LessByReference(rDoc, rPos, aWhich)); 414 if (lcl_checkIfAdjacent(rDoc, rPos, rReferences, aWhich)) 415 { 416 lcl_fillRangeFromRefList(rDoc, rPos, rReferences, rRange); 417 return true; 418 } 419 } 420 return false; 421 } 422 423 bool lcl_isReference(const FormulaToken& rToken) 424 { 425 return 426 rToken.GetType() == svSingleRef || 427 rToken.GetType() == svDoubleRef; 428 } 429 430 void adjustRangeName(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc, 431 const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal) 432 { 433 ScRangeData* pRangeData = nullptr; 434 SCTAB nSheet = pToken->GetSheet(); 435 sal_uInt16 nIndex = pToken->GetIndex(); 436 if (!rOldDoc.CopyAdjustRangeName( nSheet, nIndex, pRangeData, rNewDoc, rNewPos, rOldPos, bGlobalNamesToLocal, true)) 437 return; // nothing to do 438 439 if (!pRangeData) 440 { 441 // If this happened we have a real problem. 442 pToken->SetIndex(0); 443 assert(!"inserting the range name should not fail"); 444 return; 445 } 446 447 pToken->SetIndex(nIndex); 448 pToken->SetSheet(nSheet); 449 } 450 451 void adjustDBRange(formula::FormulaToken* pToken, ScDocument& rNewDoc, const ScDocument& rOldDoc) 452 { 453 ScDBCollection* pOldDBCollection = rOldDoc.GetDBCollection(); 454 if (!pOldDBCollection) 455 return;//strange error case, don't do anything 456 ScDBCollection::NamedDBs& aOldNamedDBs = pOldDBCollection->getNamedDBs(); 457 ScDBData* pDBData = aOldNamedDBs.findByIndex(pToken->GetIndex()); 458 if (!pDBData) 459 return; //invalid index 460 OUString aDBName = pDBData->GetUpperName(); 461 462 //search in new document 463 ScDBCollection* pNewDBCollection = rNewDoc.GetDBCollection(); 464 if (!pNewDBCollection) 465 { 466 rNewDoc.SetDBCollection(std::unique_ptr<ScDBCollection>(new ScDBCollection(rNewDoc))); 467 pNewDBCollection = rNewDoc.GetDBCollection(); 468 } 469 ScDBCollection::NamedDBs& aNewNamedDBs = pNewDBCollection->getNamedDBs(); 470 ScDBData* pNewDBData = aNewNamedDBs.findByUpperName(aDBName); 471 if (!pNewDBData) 472 { 473 pNewDBData = new ScDBData(*pDBData); 474 bool ins = aNewNamedDBs.insert(std::unique_ptr<ScDBData>(pNewDBData)); 475 assert(ins); (void)ins; 476 } 477 pToken->SetIndex(pNewDBData->GetIndex()); 478 } 479 480 } 481 482 bool AreaListenerKey::operator < ( const AreaListenerKey& r ) const 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 504 ScFormulaCellGroup::ScFormulaCellGroup() : 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 517 ScFormulaCellGroup::~ScFormulaCellGroup() 518 { 519 } 520 521 void ScFormulaCellGroup::setCode( const ScTokenArray& rCode ) 522 { 523 mpCode = rCode.CloneValue(); 524 mbInvariant = mpCode->IsInvariant(); 525 mpCode->GenHash(); 526 } 527 528 void ScFormulaCellGroup::compileCode( 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); 538 mbSubTotal = aComp.CompileTokenArray(); 539 mnFormatType = aComp.GetNumFormatType(); 540 } 541 else 542 { 543 mbSubTotal = mpCode->HasOpCodeRPN( ocSubTotal ) || mpCode->HasOpCodeRPN( ocAggregate ); 544 } 545 } 546 547 sc::FormulaGroupAreaListener* ScFormulaCellGroup::getAreaListener( 548 ScFormulaCell** ppTopCell, const ScRange& rRange, bool bStartFixed, bool bEndFixed ) 549 { 550 AreaListenerKey aKey(rRange, bStartFixed, bEndFixed); 551 552 AreaListenersType::iterator it = m_AreaListeners.lower_bound(aKey); 553 if (it == m_AreaListeners.end() || m_AreaListeners.key_comp()(aKey, it->first)) 554 { 555 // Insert a new one. 556 it = m_AreaListeners.emplace_hint( 557 it, std::piecewise_construct, 558 std::forward_as_tuple(aKey), 559 std::forward_as_tuple( 560 rRange, (*ppTopCell)->GetDocument(), (*ppTopCell)->aPos, mnLength, bStartFixed, bEndFixed)); 561 } 562 563 return &it->second; 564 } 565 566 void ScFormulaCellGroup::endAllGroupListening( ScDocument& rDoc ) 567 { 568 for (auto& rEntry : m_AreaListeners) 569 { 570 sc::FormulaGroupAreaListener& rListener = rEntry.second; 571 ScRange aListenRange = rListener.getListeningRange(); 572 // This "always listen" special range is never grouped. 573 bool bGroupListening = (aListenRange != BCA_LISTEN_ALWAYS); 574 rDoc.EndListeningArea(aListenRange, bGroupListening, &rListener); 575 } 576 577 m_AreaListeners.clear(); 578 } 579 580 ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos ) : 581 bDirty(false), 582 bTableOpDirty(false), 583 bChanged(false), 584 bRunning(false), 585 bCompile(false), 586 bSubTotal(false), 587 bIsIterCell(false), 588 bInChangeTrack(false), 589 bNeedListening(false), 590 mbNeedsNumberFormat(false), 591 mbAllowNumberFormatChange(false), 592 mbPostponedDirty(false), 593 mbIsExtRef(false), 594 mbSeenInPath(false), 595 mbFreeFlying(false), 596 cMatrixFlag(ScMatrixMode::NONE), 597 nSeenInIteration(0), 598 nFormatType(SvNumFormatType::NUMBER), 599 eTempGrammar(formula::FormulaGrammar::GRAM_DEFAULT), 600 pCode(new ScTokenArray(rDoc)), 601 rDocument(rDoc), 602 pPrevious(nullptr), 603 pNext(nullptr), 604 pPreviousTrack(nullptr), 605 pNextTrack(nullptr), 606 aPos(rPos) 607 { 608 } 609 610 ScFormulaCell::ScFormulaCell( ScDocument& rDoc, const ScAddress& rPos, 611 const OUString& rFormula, 612 const FormulaGrammar::Grammar eGrammar, 613 ScMatrixMode cMatInd ) : 614 bDirty( true ), // -> Because of the use of the Auto Pilot Function was: cMatInd != 0 615 bTableOpDirty( false ), 616 bChanged( false ), 617 bRunning( false ), 618 bCompile( false ), 619 bSubTotal( false ), 620 bIsIterCell( false ), 621 bInChangeTrack( false ), 622 bNeedListening( false ), 623 mbNeedsNumberFormat( false ), 624 mbAllowNumberFormatChange(false), 625 mbPostponedDirty(false), 626 mbIsExtRef(false), 627 mbSeenInPath(false), 628 mbFreeFlying(false), 629 cMatrixFlag ( cMatInd ), 630 nSeenInIteration(0), 631 nFormatType ( SvNumFormatType::NUMBER ), 632 eTempGrammar( eGrammar), 633 pCode( nullptr ), 634 rDocument( rDoc ), 635 pPrevious(nullptr), 636 pNext(nullptr), 637 pPreviousTrack(nullptr), 638 pNextTrack(nullptr), 639 aPos(rPos) 640 { 641 Compile( rFormula, true, eGrammar ); // bNoListening, Insert does that 642 if (!pCode) 643 // We need to have a non-NULL token array instance at all times. 644 pCode = new ScTokenArray(rDoc); 645 } 646 647 ScFormulaCell::ScFormulaCell( 648 ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<ScTokenArray> pArray, 649 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) : 650 bDirty( true ), 651 bTableOpDirty( false ), 652 bChanged( false ), 653 bRunning( false ), 654 bCompile( false ), 655 bSubTotal( false ), 656 bIsIterCell( false ), 657 bInChangeTrack( false ), 658 bNeedListening( false ), 659 mbNeedsNumberFormat( false ), 660 mbAllowNumberFormatChange(false), 661 mbPostponedDirty(false), 662 mbIsExtRef(false), 663 mbSeenInPath(false), 664 mbFreeFlying(false), 665 cMatrixFlag ( cMatInd ), 666 nSeenInIteration(0), 667 nFormatType ( SvNumFormatType::NUMBER ), 668 eTempGrammar( eGrammar), 669 pCode(pArray.release()), 670 rDocument( rDoc ), 671 pPrevious(nullptr), 672 pNext(nullptr), 673 pPreviousTrack(nullptr), 674 pNextTrack(nullptr), 675 aPos(rPos) 676 { 677 assert(pCode); // Never pass a NULL pointer here. 678 679 pCode->Finalize(); // Reduce memory usage if needed. 680 681 // Generate RPN token array. 682 if (pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen()) 683 { 684 ScCompiler aComp(rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE); 685 bSubTotal = aComp.CompileTokenArray(); 686 nFormatType = aComp.GetNumFormatType(); 687 } 688 else 689 { 690 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) ) 691 bSubTotal = true; 692 } 693 694 if (bSubTotal) 695 rDocument.AddSubTotalCell(this); 696 697 pCode->GenHash(); 698 } 699 700 ScFormulaCell::ScFormulaCell( 701 ScDocument& rDoc, const ScAddress& rPos, const ScTokenArray& rArray, 702 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cMatInd ) : 703 bDirty( true ), 704 bTableOpDirty( false ), 705 bChanged( false ), 706 bRunning( false ), 707 bCompile( false ), 708 bSubTotal( false ), 709 bIsIterCell( false ), 710 bInChangeTrack( false ), 711 bNeedListening( false ), 712 mbNeedsNumberFormat( false ), 713 mbAllowNumberFormatChange(false), 714 mbPostponedDirty(false), 715 mbIsExtRef(false), 716 mbSeenInPath(false), 717 mbFreeFlying(false), 718 cMatrixFlag ( cMatInd ), 719 nSeenInIteration(0), 720 nFormatType ( SvNumFormatType::NUMBER ), 721 eTempGrammar( eGrammar), 722 pCode(new ScTokenArray(rArray)), // also implicitly does Finalize() on the array 723 rDocument( rDoc ), 724 pPrevious(nullptr), 725 pNext(nullptr), 726 pPreviousTrack(nullptr), 727 pNextTrack(nullptr), 728 aPos(rPos) 729 { 730 // RPN array generation 731 if( pCode->GetLen() && pCode->GetCodeError() == FormulaError::NONE && !pCode->GetCodeLen() ) 732 { 733 ScCompiler aComp( rDocument, aPos, *pCode, eTempGrammar, true, cMatrixFlag != ScMatrixMode::NONE ); 734 bSubTotal = aComp.CompileTokenArray(); 735 nFormatType = aComp.GetNumFormatType(); 736 } 737 else 738 { 739 if ( pCode->HasOpCodeRPN( ocSubTotal ) || pCode->HasOpCodeRPN( ocAggregate ) ) 740 bSubTotal = true; 741 } 742 743 if (bSubTotal) 744 rDocument.AddSubTotalCell(this); 745 746 pCode->GenHash(); 747 } 748 749 ScFormulaCell::ScFormulaCell( 750 ScDocument& rDoc, const ScAddress& rPos, const ScFormulaCellGroupRef& xGroup, 751 const FormulaGrammar::Grammar eGrammar, ScMatrixMode cInd ) : 752 mxGroup(xGroup), 753 bDirty(true), 754 bTableOpDirty( false ), 755 bChanged( false ), 756 bRunning( false ), 757 bCompile( false ), 758 bSubTotal(xGroup->mbSubTotal), 759 bIsIterCell( false ), 760 bInChangeTrack( false ), 761 bNeedListening( false ), 762 mbNeedsNumberFormat( false ), 763 mbAllowNumberFormatChange(false), 764 mbPostponedDirty(false), 765 mbIsExtRef(false), 766 mbSeenInPath(false), 767 mbFreeFlying(false), 768 cMatrixFlag ( cInd ), 769 nSeenInIteration(0), 770 nFormatType(xGroup->mnFormatType), 771 eTempGrammar( eGrammar), 772 pCode(xGroup->mpCode ? &*xGroup->mpCode : new ScTokenArray(rDoc)), 773 rDocument( rDoc ), 774 pPrevious(nullptr), 775 pNext(nullptr), 776 pPreviousTrack(nullptr), 777 pNextTrack(nullptr), 778 aPos(rPos) 779 { 780 if (bSubTotal) 781 rDocument.AddSubTotalCell(this); 782 } 783 784 ScFormulaCell::ScFormulaCell(const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, ScCloneFlags nCloneFlags) : 785 bDirty( rCell.bDirty ), 786 bTableOpDirty( false ), 787 bChanged( rCell.bChanged ), 788 bRunning( false ), 789 bCompile( rCell.bCompile ), 790 bSubTotal( rCell.bSubTotal ), 791 bIsIterCell( false ), 792 bInChangeTrack( false ), 793 bNeedListening( false ), 794 mbNeedsNumberFormat( rCell.mbNeedsNumberFormat ), 795 mbAllowNumberFormatChange(false), 796 mbPostponedDirty(false), 797 mbIsExtRef(false), 798 mbSeenInPath(false), 799 mbFreeFlying(false), 800 cMatrixFlag ( rCell.cMatrixFlag ), 801 nSeenInIteration(0), 802 nFormatType( rCell.nFormatType ), 803 aResult( rCell.aResult ), 804 eTempGrammar( rCell.eTempGrammar), 805 rDocument( rDoc ), 806 pPrevious(nullptr), 807 pNext(nullptr), 808 pPreviousTrack(nullptr), 809 pNextTrack(nullptr), 810 aPos(rPos) 811 { 812 pCode = rCell.pCode->Clone().release(); 813 814 // set back any errors and recompile 815 // not in the Clipboard - it must keep the received error flag 816 // Special Length=0: as bad cells are generated, then they are also retained 817 if ( pCode->GetCodeError() != FormulaError::NONE && !rDocument.IsClipboard() && pCode->GetLen() ) 818 { 819 pCode->SetCodeError( FormulaError::NONE ); 820 bCompile = true; 821 } 822 // Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference ! 823 bool bCompileLater = false; 824 bool bClipMode = rCell.rDocument.IsClipboard(); 825 826 //update ScNameTokens 827 if (!rDocument.IsClipOrUndo() || rDoc.IsUndo()) 828 { 829 if (!rDocument.IsClipboardSource() || aPos.Tab() != rCell.aPos.Tab()) 830 { 831 bool bGlobalNamesToLocal = ((nCloneFlags & ScCloneFlags::NamesToLocal) != ScCloneFlags::Default); 832 formula::FormulaToken* pToken = nullptr; 833 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 834 while((pToken = aIter.GetNextName())!= nullptr) 835 { 836 OpCode eOpCode = pToken->GetOpCode(); 837 if (eOpCode == ocName) 838 adjustRangeName(pToken, rDoc, rCell.rDocument, aPos, rCell.aPos, bGlobalNamesToLocal); 839 else if (eOpCode == ocDBArea || eOpCode == ocTableRef) 840 adjustDBRange(pToken, rDoc, rCell.rDocument); 841 } 842 } 843 844 bool bCopyBetweenDocs = rDocument.GetPool() != rCell.rDocument.GetPool(); 845 if (bCopyBetweenDocs && !(nCloneFlags & ScCloneFlags::NoMakeAbsExternal)) 846 { 847 pCode->ReadjustAbsolute3DReferences(rCell.rDocument, rDoc, rCell.aPos); 848 } 849 850 pCode->AdjustAbsoluteRefs( rCell.rDocument, rCell.aPos, aPos, bCopyBetweenDocs ); 851 } 852 853 if (!rDocument.IsClipOrUndo()) 854 { 855 if (&rDocument.GetSharedStringPool() != &rCell.rDocument.GetSharedStringPool()) 856 pCode->ReinternStrings( rDocument.GetSharedStringPool()); 857 pCode->AdjustReferenceOnCopy( aPos); 858 } 859 860 if( !bCompile ) 861 { // Name references with references and ColRowNames 862 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 863 for (;;) 864 { 865 formula::FormulaToken* t = aIter.GetNextReferenceOrName(); 866 if (!t || bCompile) 867 break; 868 if ( t->IsExternalRef() ) 869 { 870 // External name, cell, and area references. 871 bCompile = true; 872 } 873 else if ( t->GetType() == svIndex ) 874 { 875 const ScRangeData* pRangeData = rDoc.FindRangeNameBySheetAndIndex( t->GetSheet(), t->GetIndex()); 876 if( pRangeData ) 877 { 878 if( pRangeData->HasReferences() ) 879 bCompile = true; 880 } 881 else 882 bCompile = true; // invalid reference! 883 } 884 else if ( t->GetOpCode() == ocColRowName ) 885 { 886 bCompile = true; // new lookup needed 887 bCompileLater = bClipMode; 888 } 889 } 890 } 891 if( bCompile ) 892 { 893 if ( !bCompileLater && bClipMode ) 894 { 895 // Merging ranges needs the actual positions after UpdateReference. 896 // ColRowNames and TableRefs need new lookup after positions are 897 // adjusted. 898 bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName) || 899 pCode->HasOpCode( ocTableRef); 900 } 901 if ( !bCompileLater ) 902 { 903 // bNoListening, not at all if in Clipboard/Undo, 904 // and not from Clipboard either, instead after Insert(Clone) and UpdateReference. 905 CompileTokenArray( true ); 906 } 907 } 908 909 if( nCloneFlags & ScCloneFlags::StartListening ) 910 StartListeningTo( rDoc ); 911 912 if (bSubTotal) 913 rDocument.AddSubTotalCell(this); 914 } 915 916 ScFormulaCell::~ScFormulaCell() 917 { 918 rDocument.RemoveFromFormulaTrack( this ); 919 rDocument.RemoveFromFormulaTree( this ); 920 rDocument.RemoveSubTotalCell(this); 921 if (pCode->HasOpCode(ocMacro)) 922 rDocument.GetMacroManager()->RemoveDependentCell(this); 923 924 if (rDocument.HasExternalRefManager()) 925 rDocument.GetExternalRefManager()->removeRefCell(this); 926 927 if (!mxGroup || !mxGroup->mpCode) 928 // Formula token is not shared. 929 delete pCode; 930 931 if (mxGroup && mxGroup->mpTopCell == this) 932 mxGroup->mpTopCell = nullptr; 933 } 934 935 ScFormulaCell* ScFormulaCell::Clone() const 936 { 937 return new ScFormulaCell(*this, rDocument, aPos); 938 } 939 940 ScFormulaCell* ScFormulaCell::Clone( const ScAddress& rPos ) const 941 { 942 return new ScFormulaCell(*this, rDocument, rPos, ScCloneFlags::Default); 943 } 944 945 size_t ScFormulaCell::GetHash() const 946 { 947 return pCode->GetHash(); 948 } 949 950 OUString ScFormulaCell::GetFormula( const FormulaGrammar::Grammar eGrammar, ScInterpreterContext* pContext ) const 951 { 952 if( pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen() ) 953 { 954 return ScGlobal::GetErrorString(pCode->GetCodeError()); 955 } 956 OUStringBuffer buffer; 957 if( cMatrixFlag == ScMatrixMode::Reference ) 958 { 959 // Reference to another cell that contains a matrix formula. 960 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 961 formula::FormulaToken* p = aIter.GetNextReferenceRPN(); 962 if( p ) 963 { 964 /* FIXME: original GetFormula() code obtained 965 * pCell only if (!IsInChangeTrack()), 966 * GetEnglishFormula() omitted that test. 967 * Can we live without in all cases? */ 968 ScFormulaCell* pCell = nullptr; 969 ScSingleRefData& rRef = *p->GetSingleRef(); 970 ScAddress aAbs = rRef.toAbs(rDocument, aPos); 971 if (rDocument.ValidAddress(aAbs)) 972 pCell = rDocument.GetFormulaCell(aAbs); 973 974 if (pCell) 975 { 976 return pCell->GetFormula( eGrammar, pContext ); 977 } 978 else 979 { 980 ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext ); 981 aComp.CreateStringFromTokenArray( buffer ); 982 } 983 } 984 else 985 { 986 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix"); 987 } 988 } 989 else 990 { 991 ScCompiler aComp( rDocument, aPos, *pCode, eGrammar, false, false, pContext ); 992 aComp.CreateStringFromTokenArray( buffer ); 993 } 994 995 buffer.insert( 0, '='); 996 if( cMatrixFlag != ScMatrixMode::NONE ) 997 { 998 buffer.insert( 0, '{'); 999 buffer.append( '}'); 1000 } 1001 return buffer.makeStringAndClear(); 1002 } 1003 1004 OUString ScFormulaCell::GetFormula( sc::CompileFormulaContext& rCxt, ScInterpreterContext* pContext ) const 1005 { 1006 OUStringBuffer aBuf; 1007 if (pCode->GetCodeError() != FormulaError::NONE && !pCode->GetLen()) 1008 { 1009 ScTokenArray aCode(rCxt.getDoc()); 1010 aCode.AddToken( FormulaErrorToken( pCode->GetCodeError())); 1011 ScCompiler aComp(rCxt, aPos, aCode, false, false, pContext); 1012 aComp.CreateStringFromTokenArray(aBuf); 1013 return aBuf.makeStringAndClear(); 1014 } 1015 else if( cMatrixFlag == ScMatrixMode::Reference ) 1016 { 1017 // Reference to another cell that contains a matrix formula. 1018 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 1019 formula::FormulaToken* p = aIter.GetNextReferenceRPN(); 1020 if( p ) 1021 { 1022 /* FIXME: original GetFormula() code obtained 1023 * pCell only if (!IsInChangeTrack()), 1024 * GetEnglishFormula() omitted that test. 1025 * Can we live without in all cases? */ 1026 ScFormulaCell* pCell = nullptr; 1027 ScSingleRefData& rRef = *p->GetSingleRef(); 1028 ScAddress aAbs = rRef.toAbs(rDocument, aPos); 1029 if (rDocument.ValidAddress(aAbs)) 1030 pCell = rDocument.GetFormulaCell(aAbs); 1031 1032 if (pCell) 1033 { 1034 return pCell->GetFormula(rCxt); 1035 } 1036 else 1037 { 1038 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext); 1039 aComp.CreateStringFromTokenArray(aBuf); 1040 } 1041 } 1042 else 1043 { 1044 OSL_FAIL("ScFormulaCell::GetFormula: not a matrix"); 1045 } 1046 } 1047 else 1048 { 1049 ScCompiler aComp(rCxt, aPos, *pCode, false, false, pContext); 1050 aComp.CreateStringFromTokenArray(aBuf); 1051 } 1052 1053 aBuf.insert( 0, '='); 1054 if( cMatrixFlag != ScMatrixMode::NONE ) 1055 { 1056 aBuf.insert( 0, '{'); 1057 aBuf.append( '}'); 1058 } 1059 1060 return aBuf.makeStringAndClear(); 1061 } 1062 1063 void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ) 1064 { 1065 MaybeInterpret(); 1066 1067 if (pCode->GetCodeError() == FormulaError::NONE && aResult.GetType() == svMatrixCell) 1068 { 1069 const ScMatrix* pMat = aResult.GetToken()->GetMatrix(); 1070 if (pMat) 1071 { 1072 pMat->GetDimensions( rCols, rRows ); 1073 if (pCode->IsHyperLink()) 1074 { 1075 // Row 2 element is the URL that is not to be displayed and the 1076 // result dimension not to be extended. 1077 assert(rRows == 2); 1078 rRows = 1; 1079 } 1080 return; 1081 } 1082 } 1083 rCols = 0; 1084 rRows = 0; 1085 } 1086 1087 void ScFormulaCell::ResetDirty() { bDirty = bTableOpDirty = mbPostponedDirty = false; } 1088 void ScFormulaCell::SetNeedsListening( bool bVar ) { bNeedListening = bVar; } 1089 1090 void ScFormulaCell::SetNeedsDirty( bool bVar ) 1091 { 1092 mbPostponedDirty = bVar; 1093 } 1094 1095 void ScFormulaCell::SetNeedNumberFormat( bool bVal ) 1096 { 1097 mbNeedsNumberFormat = mbAllowNumberFormatChange = bVal; 1098 } 1099 1100 void ScFormulaCell::Compile( const OUString& rFormula, bool bNoListening, 1101 const FormulaGrammar::Grammar eGrammar ) 1102 { 1103 if ( rDocument.IsClipOrUndo() ) 1104 return; 1105 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this ); 1106 if ( bWasInFormulaTree ) 1107 rDocument.RemoveFromFormulaTree( this ); 1108 // pCode may not deleted for queries, but must be empty 1109 if ( pCode ) 1110 pCode->Clear(); 1111 ScTokenArray* pCodeOld = pCode; 1112 ScCompiler aComp( rDocument, aPos, eGrammar); 1113 pCode = aComp.CompileString( rFormula ).release(); 1114 assert(!mxGroup); 1115 delete pCodeOld; 1116 if( pCode->GetCodeError() == FormulaError::NONE ) 1117 { 1118 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() ) 1119 { // not recursive CompileTokenArray/Compile/CompileTokenArray 1120 if ( rFormula[0] == '=' ) 1121 pCode->AddBad( rFormula.copy(1) ); 1122 else 1123 pCode->AddBad( rFormula ); 1124 } 1125 bCompile = true; 1126 CompileTokenArray( bNoListening ); 1127 } 1128 else 1129 bChanged = true; 1130 1131 if ( bWasInFormulaTree ) 1132 rDocument.PutInFormulaTree( this ); 1133 } 1134 1135 void ScFormulaCell::Compile( 1136 sc::CompileFormulaContext& rCxt, const OUString& rFormula, bool bNoListening ) 1137 { 1138 if ( rDocument.IsClipOrUndo() ) 1139 return; 1140 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this ); 1141 if ( bWasInFormulaTree ) 1142 rDocument.RemoveFromFormulaTree( this ); 1143 // pCode may not deleted for queries, but must be empty 1144 if ( pCode ) 1145 pCode->Clear(); 1146 ScTokenArray* pCodeOld = pCode; 1147 ScCompiler aComp(rCxt, aPos); 1148 pCode = aComp.CompileString( rFormula ).release(); 1149 assert(!mxGroup); 1150 delete pCodeOld; 1151 if( pCode->GetCodeError() == FormulaError::NONE ) 1152 { 1153 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() && rFormula == aResult.GetHybridFormula() ) 1154 { // not recursive CompileTokenArray/Compile/CompileTokenArray 1155 if ( rFormula[0] == '=' ) 1156 pCode->AddBad( rFormula.copy(1) ); 1157 else 1158 pCode->AddBad( rFormula ); 1159 } 1160 bCompile = true; 1161 CompileTokenArray(rCxt, bNoListening); 1162 } 1163 else 1164 bChanged = true; 1165 1166 if ( bWasInFormulaTree ) 1167 rDocument.PutInFormulaTree( this ); 1168 } 1169 1170 void ScFormulaCell::CompileTokenArray( bool bNoListening ) 1171 { 1172 // Not already compiled? 1173 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() ) 1174 { 1175 Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar); 1176 } 1177 else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE ) 1178 { 1179 // RPN length may get changed 1180 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this ); 1181 if ( bWasInFormulaTree ) 1182 rDocument.RemoveFromFormulaTree( this ); 1183 1184 // Loading from within filter? No listening yet! 1185 if( rDocument.IsInsertingFromOtherDoc() ) 1186 bNoListening = true; 1187 1188 if( !bNoListening && pCode->GetCodeLen() ) 1189 EndListeningTo( rDocument ); 1190 ScCompiler aComp(rDocument, aPos, *pCode, rDocument.GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE); 1191 bSubTotal = aComp.CompileTokenArray(); 1192 if( pCode->GetCodeError() == FormulaError::NONE ) 1193 { 1194 nFormatType = aComp.GetNumFormatType(); 1195 bChanged = true; 1196 aResult.SetToken( nullptr); 1197 bCompile = false; 1198 if ( !bNoListening ) 1199 StartListeningTo( rDocument ); 1200 } 1201 if ( bWasInFormulaTree ) 1202 rDocument.PutInFormulaTree( this ); 1203 1204 if (bSubTotal) 1205 rDocument.AddSubTotalCell(this); 1206 } 1207 } 1208 1209 void ScFormulaCell::CompileTokenArray( sc::CompileFormulaContext& rCxt, bool bNoListening ) 1210 { 1211 // Not already compiled? 1212 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() ) 1213 { 1214 rCxt.setGrammar(eTempGrammar); 1215 Compile(rCxt, aResult.GetHybridFormula(), bNoListening); 1216 } 1217 else if( bCompile && !rDocument.IsClipOrUndo() && pCode->GetCodeError() == FormulaError::NONE) 1218 { 1219 // RPN length may get changed 1220 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this ); 1221 if ( bWasInFormulaTree ) 1222 rDocument.RemoveFromFormulaTree( this ); 1223 1224 // Loading from within filter? No listening yet! 1225 if( rDocument.IsInsertingFromOtherDoc() ) 1226 bNoListening = true; 1227 1228 if( !bNoListening && pCode->GetCodeLen() ) 1229 EndListeningTo( rDocument ); 1230 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE); 1231 bSubTotal = aComp.CompileTokenArray(); 1232 if( pCode->GetCodeError() == FormulaError::NONE ) 1233 { 1234 nFormatType = aComp.GetNumFormatType(); 1235 bChanged = true; 1236 aResult.SetToken( nullptr); 1237 bCompile = false; 1238 if ( !bNoListening ) 1239 StartListeningTo( rDocument ); 1240 } 1241 if ( bWasInFormulaTree ) 1242 rDocument.PutInFormulaTree( this ); 1243 1244 if (bSubTotal) 1245 rDocument.AddSubTotalCell(this); 1246 } 1247 } 1248 1249 void ScFormulaCell::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress ) 1250 { 1251 if ( cMatrixFlag == ScMatrixMode::Reference ) 1252 { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula 1253 // just establish listeners 1254 StartListeningTo( rDocument ); 1255 return ; 1256 } 1257 1258 // Error constant formula cell stays as is. 1259 if (!pCode->GetLen() && pCode->GetCodeError() != FormulaError::NONE) 1260 return; 1261 1262 // Compilation changes RPN count, remove and reinsert to FormulaTree if it 1263 // was in to update its count. 1264 bool bWasInFormulaTree = rDocument.IsInFormulaTree( this); 1265 if (bWasInFormulaTree) 1266 rDocument.RemoveFromFormulaTree( this); 1267 rCxt.setGrammar(eTempGrammar); 1268 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE); 1269 OUString aFormula, aFormulaNmsp; 1270 aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp ); 1271 rDocument.DecXMLImportedFormulaCount( aFormula.getLength() ); 1272 rProgress.SetStateCountDownOnPercent( rDocument.GetXMLImportedFormulaCount() ); 1273 // pCode may not deleted for queries, but must be empty 1274 pCode->Clear(); 1275 1276 bool bDoCompile = true; 1277 1278 if ( !mxGroup && aFormulaNmsp.isEmpty() ) // optimization 1279 { 1280 ScAddress aPreviousCell( aPos ); 1281 aPreviousCell.IncRow( -1 ); 1282 ScFormulaCell *pPreviousCell = rDocument.GetFormulaCell( aPreviousCell ); 1283 if (pPreviousCell && pPreviousCell->GetCode()->IsShareable()) 1284 { 1285 // Build formula string using the tokens from the previous cell, 1286 // but use the current cell position. 1287 ScCompiler aBackComp( rCxt, aPos, *(pPreviousCell->pCode) ); 1288 OUStringBuffer aShouldBeBuf; 1289 aBackComp.CreateStringFromTokenArray( aShouldBeBuf ); 1290 1291 // The initial '=' is optional in ODFF. 1292 const sal_Int32 nLeadingEqual = (aFormula.getLength() > 0 && aFormula[0] == '=') ? 1 : 0; 1293 if (aFormula.getLength() == aShouldBeBuf.getLength() + nLeadingEqual && 1294 aFormula.match( aShouldBeBuf, nLeadingEqual)) 1295 { 1296 // Put them in the same formula group. 1297 ScFormulaCellGroupRef xGroup = pPreviousCell->GetCellGroup(); 1298 if (!xGroup) // Last cell is not grouped yet. Start a new group. 1299 xGroup = pPreviousCell->CreateCellGroup(1, false); 1300 ++xGroup->mnLength; 1301 SetCellGroup( xGroup ); 1302 1303 // Do setup here based on previous cell. 1304 1305 nFormatType = pPreviousCell->nFormatType; 1306 bSubTotal = pPreviousCell->bSubTotal; 1307 bChanged = true; 1308 bCompile = false; 1309 1310 if (bSubTotal) 1311 rDocument.AddSubTotalCell(this); 1312 1313 bDoCompile = false; 1314 pCode = pPreviousCell->pCode; 1315 if (pPreviousCell->mbIsExtRef) 1316 rDocument.GetExternalRefManager()->insertRefCellFromTemplate( pPreviousCell, this ); 1317 } 1318 } 1319 } 1320 1321 if (bDoCompile) 1322 { 1323 ScTokenArray* pCodeOld = pCode; 1324 pCode = aComp.CompileString( aFormula, aFormulaNmsp ).release(); 1325 assert(!mxGroup); 1326 delete pCodeOld; 1327 1328 if( pCode->GetCodeError() == FormulaError::NONE ) 1329 { 1330 if ( !pCode->GetLen() ) 1331 { 1332 if ( !aFormula.isEmpty() && aFormula[0] == '=' ) 1333 pCode->AddBad( aFormula.copy( 1 ) ); 1334 else 1335 pCode->AddBad( aFormula ); 1336 } 1337 bSubTotal = aComp.CompileTokenArray(); 1338 if( pCode->GetCodeError() == FormulaError::NONE ) 1339 { 1340 nFormatType = aComp.GetNumFormatType(); 1341 bChanged = true; 1342 bCompile = false; 1343 } 1344 1345 if (bSubTotal) 1346 rDocument.AddSubTotalCell(this); 1347 } 1348 else 1349 bChanged = true; 1350 } 1351 1352 // After loading, it must be known if ocDde/ocWebservice is in any formula 1353 // (for external links warning, CompileXML is called at the end of loading XML file) 1354 rDocument.CheckLinkFormulaNeedingCheck(*pCode); 1355 1356 //volatile cells must be added here for import 1357 if( !pCode->IsRecalcModeNormal() || pCode->IsRecalcModeForced()) 1358 { 1359 // During load, only those cells that are marked explicitly dirty get 1360 // recalculated. So we need to set it dirty here. 1361 SetDirtyVar(); 1362 rDocument.AppendToFormulaTrack(this); 1363 // Do not call TrackFormulas() here, not all listeners may have been 1364 // established, postponed until ScDocument::CompileXML() finishes. 1365 } 1366 else if (bWasInFormulaTree) 1367 rDocument.PutInFormulaTree(this); 1368 } 1369 1370 void ScFormulaCell::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening ) 1371 { 1372 bool bNewCompiled = false; 1373 // If a Calc 1.0-doc is read, we have a result, but no token array 1374 if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() ) 1375 { 1376 rCxt.setGrammar(eTempGrammar); 1377 Compile(rCxt, aResult.GetHybridFormula(), true); 1378 aResult.SetToken( nullptr); 1379 bDirty = true; 1380 bNewCompiled = true; 1381 } 1382 // The RPN array is not created when a Calc 3.0-Doc has been read as the Range Names exist until now. 1383 if( pCode->GetLen() && !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE ) 1384 { 1385 ScCompiler aComp(rCxt, aPos, *pCode, true, cMatrixFlag != ScMatrixMode::NONE); 1386 bSubTotal = aComp.CompileTokenArray(); 1387 nFormatType = aComp.GetNumFormatType(); 1388 bDirty = true; 1389 bCompile = false; 1390 bNewCompiled = true; 1391 1392 if (bSubTotal) 1393 rDocument.AddSubTotalCell(this); 1394 } 1395 1396 // On OS/2 with broken FPU exception, we can somehow store /0 without Err503. Later on in 1397 // the BLC Lib NumberFormatter crashes when doing a fabs (NAN) (# 32739 #). 1398 // We iron this out here for all systems, such that we also have an Err503 here. 1399 if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) ) 1400 { 1401 OSL_FAIL("Formula cell INFINITY!!! Where does this document come from?"); 1402 aResult.SetResultError( FormulaError::IllegalFPOperation ); 1403 bDirty = true; 1404 } 1405 1406 // DoubleRefs for binary operators were always a Matrix before version v5.0. 1407 // Now this is only the case when in an array formula, otherwise it's an implicit intersection 1408 if ( ScDocument::GetSrcVersion() < SC_MATRIX_DOUBLEREF && 1409 GetMatrixFlag() == ScMatrixMode::NONE && pCode->HasMatrixDoubleRefOps() ) 1410 { 1411 cMatrixFlag = ScMatrixMode::Formula; 1412 SetMatColsRows( 1, 1); 1413 } 1414 1415 // Do the cells need to be calculated? After Load cells can contain an error code, and then start 1416 // the listener and Recalculate (if needed) if not ScRecalcMode::NORMAL 1417 if( !bNewCompiled || pCode->GetCodeError() == FormulaError::NONE ) 1418 { 1419 if (bStartListening) 1420 StartListeningTo(rDocument); 1421 1422 if( !pCode->IsRecalcModeNormal() ) 1423 bDirty = true; 1424 } 1425 if ( pCode->IsRecalcModeAlways() ) 1426 { // random(), today(), now() always stay in the FormulaTree, so that they are calculated 1427 // for each F9 1428 bDirty = true; 1429 } 1430 // No SetDirty yet, as no all Listeners are known yet (only in SetDirtyAfterLoad) 1431 } 1432 1433 bool ScFormulaCell::MarkUsedExternalReferences() 1434 { 1435 return pCode && rDocument.MarkUsedExternalReferences(*pCode, aPos); 1436 } 1437 1438 namespace { 1439 class RecursionCounter 1440 { 1441 ScRecursionHelper& rRec; 1442 bool bStackedInIteration; 1443 #if defined DBG_UTIL && !defined NDEBUG 1444 const ScFormulaCell* cell; 1445 #endif 1446 public: 1447 RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) 1448 : rRec(r) 1449 #if defined DBG_UTIL && !defined NDEBUG 1450 , cell(p) 1451 #endif 1452 { 1453 bStackedInIteration = rRec.IsDoingIteration(); 1454 if (bStackedInIteration) 1455 rRec.GetRecursionInIterationStack().push( p); 1456 rRec.IncRecursionCount(); 1457 } 1458 ~RecursionCounter() 1459 { 1460 rRec.DecRecursionCount(); 1461 if (bStackedInIteration) 1462 { 1463 #if defined DBG_UTIL && !defined NDEBUG 1464 assert(rRec.GetRecursionInIterationStack().top() == cell); 1465 #endif 1466 rRec.GetRecursionInIterationStack().pop(); 1467 } 1468 } 1469 }; 1470 1471 // Forced calculation: OpenCL and threads require formula groups, so force even single cells to be a "group". 1472 // Remove the group again at the end, since there are some places throughout the code 1473 // that do not handle well groups with just 1 cell. Remove the groups only when the recursion level 1474 // reaches 0 again (groups contain some info such as disabling threading because of cycles, so removing 1475 // a group immediately would remove the info), for this reason affected cells are stored in the recursion 1476 // helper. 1477 struct TemporaryCellGroupMaker 1478 { 1479 TemporaryCellGroupMaker( ScFormulaCell* cell, bool enable ) 1480 : mCell( cell ) 1481 , mEnabled( enable ) 1482 { 1483 if( mEnabled && mCell->GetCellGroup() == nullptr ) 1484 { 1485 mCell->CreateCellGroup( 1, false ); 1486 mCell->GetDocument().GetRecursionHelper().AddTemporaryGroupCell( mCell ); 1487 } 1488 } 1489 ~TemporaryCellGroupMaker() COVERITY_NOEXCEPT_FALSE 1490 { 1491 if( mEnabled ) 1492 mCell->GetDocument().GetRecursionHelper().CleanTemporaryGroupCells(); 1493 } 1494 ScFormulaCell* mCell; 1495 const bool mEnabled; 1496 }; 1497 1498 } // namespace 1499 1500 bool ScFormulaCell::Interpret(SCROW nStartOffset, SCROW nEndOffset) 1501 { 1502 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper(); 1503 bool bGroupInterpreted = false; 1504 1505 // The result would possibly depend on a cell without a valid value, bail out 1506 // the entire dependency computation. 1507 if (rRecursionHelper.IsAbortingDependencyComputation()) 1508 return false; 1509 1510 if ((mxGroup && !rRecursionHelper.CheckFGIndependence(mxGroup.get())) || !rRecursionHelper.AreGroupsIndependent()) 1511 return bGroupInterpreted; 1512 1513 static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType(); 1514 TemporaryCellGroupMaker cellGroupMaker( this, forceType != ForceCalculationNone && forceType != ForceCalculationCore ); 1515 1516 ScFormulaCell* pTopCell = mxGroup ? mxGroup->mpTopCell : this; 1517 1518 if (pTopCell->mbSeenInPath && rRecursionHelper.GetDepComputeLevel() && 1519 rRecursionHelper.AnyCycleMemberInDependencyEvalMode(pTopCell)) 1520 { 1521 // This call arose from a dependency calculation and we just found a cycle. 1522 // This will mark all elements in the cycle as parts-of-cycle. 1523 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pTopCell); 1524 // Reaching here does not necessarily mean a circular reference, so don't set Err:522 here yet. 1525 // If there is a genuine circular reference, it will be marked so when all groups 1526 // in the cycle get out of dependency evaluation mode. 1527 // But returning without calculation a new value means other cells depending 1528 // on this one would use a possibly invalid value, so ensure the dependency 1529 // computation is aborted without resetting the dirty flag of any cell. 1530 rRecursionHelper.AbortDependencyComputation(); 1531 return bGroupInterpreted; 1532 } 1533 1534 #if DEBUG_CALCULATION 1535 static bool bDebugCalculationInit = true; 1536 if (bDebugCalculationInit) 1537 { 1538 aDC.maTrigger = aDebugCalculationTriggerAddress; 1539 aDC.mbPrintResults = true; 1540 bDebugCalculationInit = false; 1541 } 1542 DebugCalculationStacker aDebugEntry(aPos, rDocument); 1543 #endif 1544 1545 if (!IsDirtyOrInTableOpDirty() || rRecursionHelper.IsInReturn()) 1546 return bGroupInterpreted; // no double/triple processing 1547 1548 //FIXME: 1549 // If the call originates from a Reschedule in DdeLink update, leave dirty 1550 // Better: Do a Dde Link Update without Reschedule or do it completely asynchronously! 1551 if ( rDocument.IsInDdeLinkUpdate() ) 1552 return bGroupInterpreted; 1553 1554 if (bRunning) 1555 { 1556 if (!rDocument.GetDocOptions().IsIter()) 1557 { 1558 aResult.SetResultError( FormulaError::CircularReference ); 1559 return bGroupInterpreted; 1560 } 1561 1562 if (aResult.GetResultError() == FormulaError::CircularReference) 1563 aResult.SetResultError( FormulaError::NONE ); 1564 1565 // Start or add to iteration list. 1566 if (!rRecursionHelper.IsDoingIteration() || 1567 !rRecursionHelper.GetRecursionInIterationStack().top()->bIsIterCell) 1568 rRecursionHelper.SetInIterationReturn( true); 1569 1570 return bGroupInterpreted; 1571 } 1572 // no multiple interprets for GetErrCode, IsValue, GetValue and 1573 // different entry point recursions. Would also lead to premature 1574 // convergence in iterations. 1575 if (rRecursionHelper.GetIteration() && nSeenInIteration == 1576 rRecursionHelper.GetIteration()) 1577 return bGroupInterpreted; 1578 1579 bool bOldRunning = bRunning; 1580 if (rRecursionHelper.GetRecursionCount() > MAXRECURSION) 1581 { 1582 bRunning = true; 1583 rRecursionHelper.SetInRecursionReturn( true); 1584 } 1585 else 1586 { 1587 rDocument.IncInterpretLevel(); 1588 1589 #if DEBUG_CALCULATION 1590 aDC.enterGroup(); 1591 #endif 1592 bool bPartOfCycleBefore = mxGroup && mxGroup->mbPartOfCycle; 1593 bGroupInterpreted = InterpretFormulaGroup(nStartOffset, nEndOffset); 1594 bool bPartOfCycleAfter = mxGroup && mxGroup->mbPartOfCycle; 1595 1596 #if DEBUG_CALCULATION 1597 aDC.leaveGroup(); 1598 #endif 1599 if (!bGroupInterpreted) 1600 { 1601 // This call resulted from a dependency calculation for a multigroup-threading attempt, 1602 // but found dependency among the groups. 1603 if (!rRecursionHelper.AreGroupsIndependent()) 1604 { 1605 rDocument.DecInterpretLevel(); 1606 return bGroupInterpreted; 1607 } 1608 // Dependency calc inside InterpretFormulaGroup() failed due to 1609 // detection of a cycle and there are parent FG's in the cycle. 1610 // Skip InterpretTail() in such cases, only run InterpretTail for the "cycle-starting" FG 1611 if (!bPartOfCycleBefore && bPartOfCycleAfter && rRecursionHelper.AnyParentFGInCycle()) 1612 { 1613 rDocument.DecInterpretLevel(); 1614 return bGroupInterpreted; 1615 } 1616 1617 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this); 1618 ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable()); 1619 InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL); 1620 } 1621 1622 rDocument.DecInterpretLevel(); 1623 } 1624 1625 // While leaving a recursion or iteration stack, insert its cells to the 1626 // recursion list in reverse order. 1627 if (rRecursionHelper.IsInReturn()) 1628 { 1629 bool bFreeFlyingInserted = false; 1630 if (rRecursionHelper.GetRecursionCount() > 0 || !rRecursionHelper.IsDoingRecursion()) 1631 { 1632 rRecursionHelper.Insert( this, bOldRunning, aResult); 1633 bFreeFlyingInserted = mbFreeFlying; 1634 } 1635 bool bIterationFromRecursion = false; 1636 bool bResumeIteration = false; 1637 do 1638 { 1639 if ((rRecursionHelper.IsInIterationReturn() && 1640 rRecursionHelper.GetRecursionCount() == 0 && 1641 !rRecursionHelper.IsDoingIteration()) || 1642 bIterationFromRecursion || bResumeIteration) 1643 { 1644 bool & rDone = rRecursionHelper.GetConvergingReference(); 1645 rDone = false; 1646 if (!bIterationFromRecursion && bResumeIteration) 1647 { 1648 bResumeIteration = false; 1649 // Resuming iteration expands the range. 1650 ScFormulaRecursionList::const_iterator aOldStart( 1651 rRecursionHelper.GetLastIterationStart()); 1652 rRecursionHelper.ResumeIteration(); 1653 // Mark new cells being in iteration. 1654 for (ScFormulaRecursionList::const_iterator aIter( 1655 rRecursionHelper.GetIterationStart()); aIter != 1656 aOldStart; ++aIter) 1657 { 1658 ScFormulaCell* pIterCell = (*aIter).pCell; 1659 pIterCell->bIsIterCell = true; 1660 } 1661 // Mark older cells dirty again, in case they converted 1662 // without accounting for all remaining cells in the circle 1663 // that weren't touched so far, e.g. conditional. Restore 1664 // backupped result. 1665 sal_uInt16 nIteration = rRecursionHelper.GetIteration(); 1666 for (ScFormulaRecursionList::const_iterator aIter( 1667 aOldStart); aIter != 1668 rRecursionHelper.GetIterationEnd(); ++aIter) 1669 { 1670 ScFormulaCell* pIterCell = (*aIter).pCell; 1671 if (pIterCell->nSeenInIteration == nIteration) 1672 { 1673 if (!pIterCell->bDirty || aIter == aOldStart) 1674 { 1675 pIterCell->aResult = (*aIter).aPreviousResult; 1676 } 1677 --pIterCell->nSeenInIteration; 1678 } 1679 pIterCell->bDirty = true; 1680 } 1681 } 1682 else 1683 { 1684 bResumeIteration = false; 1685 // Close circle once. If 'this' is self-referencing only 1686 // (e.g. counter or self-adder) then it is already 1687 // implicitly closed. 1688 /* TODO: does this even make sense anymore? The last cell 1689 * added above with rRecursionHelper.Insert() should always 1690 * be 'this', shouldn't it? */ 1691 if (rRecursionHelper.GetList().size() > 1) 1692 { 1693 ScFormulaCell* pLastCell = rRecursionHelper.GetList().back().pCell; 1694 if (pLastCell != this) 1695 { 1696 rDocument.IncInterpretLevel(); 1697 ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable()); 1698 pLastCell->InterpretTail( 1699 *aContextGetterGuard.GetInterpreterContext(), SCITP_CLOSE_ITERATION_CIRCLE); 1700 rDocument.DecInterpretLevel(); 1701 } 1702 } 1703 // Start at 1, init things. 1704 rRecursionHelper.StartIteration(); 1705 // Mark all cells being in iteration. Reset results to 1706 // original values, formula cells have been interpreted 1707 // already, discard that step. 1708 for (ScFormulaRecursionList::const_iterator aIter( 1709 rRecursionHelper.GetIterationStart()); aIter != 1710 rRecursionHelper.GetIterationEnd(); ++aIter) 1711 { 1712 ScFormulaCell* pIterCell = (*aIter).pCell; 1713 pIterCell->aResult = (*aIter).aPreviousResult; 1714 pIterCell->bIsIterCell = true; 1715 } 1716 } 1717 bIterationFromRecursion = false; 1718 sal_uInt16 nIterMax = rDocument.GetDocOptions().GetIterCount(); 1719 for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone; 1720 rRecursionHelper.IncIteration()) 1721 { 1722 rDone = false; 1723 bool bFirst = true; 1724 for ( ScFormulaRecursionList::iterator aIter( 1725 rRecursionHelper.GetIterationStart()); aIter != 1726 rRecursionHelper.GetIterationEnd() && 1727 !rRecursionHelper.IsInReturn(); ++aIter) 1728 { 1729 ScFormulaCell* pIterCell = (*aIter).pCell; 1730 if (pIterCell->IsDirtyOrInTableOpDirty() && 1731 rRecursionHelper.GetIteration() != 1732 pIterCell->GetSeenInIteration()) 1733 { 1734 (*aIter).aPreviousResult = pIterCell->aResult; 1735 rDocument.IncInterpretLevel(); 1736 ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable()); 1737 pIterCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_FROM_ITERATION); 1738 rDocument.DecInterpretLevel(); 1739 } 1740 if (bFirst) 1741 { 1742 rDone = !pIterCell->IsDirtyOrInTableOpDirty(); 1743 bFirst = false; 1744 } 1745 else if (rDone) 1746 { 1747 rDone = !pIterCell->IsDirtyOrInTableOpDirty(); 1748 } 1749 } 1750 if (rRecursionHelper.IsInReturn()) 1751 { 1752 bResumeIteration = true; 1753 break; // for 1754 // Don't increment iteration. 1755 } 1756 } 1757 if (!bResumeIteration) 1758 { 1759 if (rDone) 1760 { 1761 for (ScFormulaRecursionList::const_iterator aIter( 1762 rRecursionHelper.GetIterationStart()); 1763 aIter != rRecursionHelper.GetIterationEnd(); 1764 ++aIter) 1765 { 1766 ScFormulaCell* pIterCell = (*aIter).pCell; 1767 pIterCell->bIsIterCell = false; 1768 pIterCell->nSeenInIteration = 0; 1769 pIterCell->bRunning = (*aIter).bOldRunning; 1770 } 1771 } 1772 else 1773 { 1774 for (ScFormulaRecursionList::const_iterator aIter( 1775 rRecursionHelper.GetIterationStart()); 1776 aIter != rRecursionHelper.GetIterationEnd(); 1777 ++aIter) 1778 { 1779 ScFormulaCell* pIterCell = (*aIter).pCell; 1780 pIterCell->bIsIterCell = false; 1781 pIterCell->nSeenInIteration = 0; 1782 pIterCell->bRunning = (*aIter).bOldRunning; 1783 pIterCell->ResetDirty(); 1784 // The difference to Excel is that Excel does not 1785 // produce an error for non-convergence thus a 1786 // delta of 0.001 still works to execute the 1787 // maximum number of iterations and display the 1788 // results no matter if the result anywhere reached 1789 // near delta, but also never indicates whether the 1790 // result actually makes sense in case of 1791 // non-counter context. Calc does check the delta 1792 // in every case. If we wanted to support what 1793 // Excel does then add another option "indicate 1794 // non-convergence error" (default on) and execute 1795 // the following block only if set. 1796 #if 1 1797 // If one cell didn't converge, all cells of this 1798 // circular dependency don't, no matter whether 1799 // single cells did. 1800 pIterCell->aResult.SetResultError( FormulaError::NoConvergence); 1801 pIterCell->bChanged = true; 1802 #endif 1803 } 1804 } 1805 // End this iteration and remove entries. 1806 rRecursionHelper.EndIteration(); 1807 bResumeIteration = rRecursionHelper.IsDoingIteration(); 1808 } 1809 } 1810 if (rRecursionHelper.IsInRecursionReturn() && 1811 rRecursionHelper.GetRecursionCount() == 0 && 1812 !rRecursionHelper.IsDoingRecursion()) 1813 { 1814 bIterationFromRecursion = false; 1815 // Iterate over cells known so far, start with the last cell 1816 // encountered, inserting new cells if another recursion limit 1817 // is reached. Repeat until solved. 1818 rRecursionHelper.SetDoingRecursion( true); 1819 do 1820 { 1821 rRecursionHelper.SetInRecursionReturn( false); 1822 for (ScFormulaRecursionList::const_iterator aIter( 1823 rRecursionHelper.GetIterationStart()); 1824 !rRecursionHelper.IsInReturn() && aIter != 1825 rRecursionHelper.GetIterationEnd(); ++aIter) 1826 { 1827 ScFormulaCell* pCell = (*aIter).pCell; 1828 if (pCell->IsDirtyOrInTableOpDirty()) 1829 { 1830 rDocument.IncInterpretLevel(); 1831 ScInterpreterContextGetterGuard aContextGetterGuard(rDocument, rDocument.GetFormatTable()); 1832 pCell->InterpretTail( *aContextGetterGuard.GetInterpreterContext(), SCITP_NORMAL); 1833 rDocument.DecInterpretLevel(); 1834 if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell()) 1835 pCell->bRunning = (*aIter).bOldRunning; 1836 } 1837 } 1838 } while (rRecursionHelper.IsInRecursionReturn()); 1839 rRecursionHelper.SetDoingRecursion( false); 1840 if (rRecursionHelper.IsInIterationReturn()) 1841 { 1842 if (!bResumeIteration) 1843 bIterationFromRecursion = true; 1844 } 1845 else if (bResumeIteration || 1846 rRecursionHelper.IsDoingIteration()) 1847 rRecursionHelper.GetList().erase( 1848 rRecursionHelper.GetIterationStart(), 1849 rRecursionHelper.GetLastIterationStart()); 1850 else 1851 rRecursionHelper.Clear(); 1852 } 1853 } while (bIterationFromRecursion || bResumeIteration); 1854 1855 if (bFreeFlyingInserted) 1856 { 1857 // Remove this from recursion list, it may get deleted. 1858 // It additionally also should mean that the recursion/iteration 1859 // ends here as it must had been triggered by this free-flying 1860 // out-of-sheets cell 1861 const bool bOnlyThis = (rRecursionHelper.GetList().size() == 1); 1862 rRecursionHelper.GetList().remove_if([this](const ScFormulaRecursionEntry& r){return r.pCell == this;}); 1863 if (bOnlyThis) 1864 { 1865 assert(rRecursionHelper.GetList().empty()); 1866 if (rRecursionHelper.GetList().empty()) 1867 rRecursionHelper.EndIteration(); 1868 } 1869 } 1870 } 1871 1872 #if DEBUG_CALCULATION 1873 FormulaError nErr = aResult.GetResultError(); 1874 if (nErr != FormulaError::NONE) 1875 aDC.storeResultError( nErr); 1876 else if (aResult.IsValue()) 1877 aDC.storeResult( aResult.GetDouble()); 1878 else 1879 aDC.storeResult( aResult.GetString()); 1880 #endif 1881 1882 return bGroupInterpreted; 1883 } 1884 1885 void ScFormulaCell::InterpretTail( ScInterpreterContext& rContext, ScInterpretTailParameter eTailParam ) 1886 { 1887 RecursionCounter aRecursionCounter( rDocument.GetRecursionHelper(), this); 1888 // TODO If this cell is not an iteration cell, add it to the list of iteration cells? 1889 if(bIsIterCell) 1890 nSeenInIteration = rDocument.GetRecursionHelper().GetIteration(); 1891 if( !pCode->GetCodeLen() && pCode->GetCodeError() == FormulaError::NONE ) 1892 { 1893 // #i11719# no RPN and no error and no token code but result string present 1894 // => interpretation of this cell during name-compilation and unknown names 1895 // => can't exchange underlying code array in CompileTokenArray() / 1896 // Compile() because interpreter's token iterator would crash or pCode 1897 // would be deleted twice if this cell was interpreted during 1898 // compilation. 1899 // This should only be a temporary condition and, since we set an 1900 // error, if ran into it again we'd bump into the dirty-clearing 1901 // condition further down. 1902 if ( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() ) 1903 { 1904 pCode->SetCodeError( FormulaError::NoCode ); 1905 // This is worth an assertion; if encountered in daily work 1906 // documents we might need another solution. Or just confirm correctness. 1907 return; 1908 } 1909 CompileTokenArray(); 1910 } 1911 1912 if( pCode->GetCodeLen() ) 1913 { 1914 std::unique_ptr<ScInterpreter> pScopedInterpreter; 1915 ScInterpreter* pInterpreter; 1916 if (rContext.pInterpreter) 1917 { 1918 pInterpreter = rContext.pInterpreter; 1919 pInterpreter->Init(this, aPos, *pCode); 1920 } 1921 else 1922 { 1923 pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rContext, aPos, *pCode )); 1924 pInterpreter = pScopedInterpreter.get(); 1925 } 1926 1927 FormulaError nOldErrCode = aResult.GetResultError(); 1928 if ( nSeenInIteration == 0 ) 1929 { // Only the first time 1930 // With bChanged=false, if a newly compiled cell has a result of 1931 // 0.0, no change is detected and the cell will not be repainted. 1932 // bChanged = false; 1933 aResult.SetResultError( FormulaError::NONE ); 1934 } 1935 1936 switch ( aResult.GetResultError() ) 1937 { 1938 case FormulaError::CircularReference : // will be determined again if so 1939 aResult.SetResultError( FormulaError::NONE ); 1940 break; 1941 default: break; 1942 } 1943 1944 bool bOldRunning = bRunning; 1945 bRunning = true; 1946 pInterpreter->Interpret(); 1947 if (rDocument.GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE) 1948 { 1949 if (nSeenInIteration > 0) 1950 --nSeenInIteration; // retry when iteration is resumed 1951 1952 if ( aResult.GetType() == formula::svUnknown ) 1953 aResult.SetToken( pInterpreter->GetResultToken().get() ); 1954 1955 return; 1956 } 1957 bRunning = bOldRunning; 1958 1959 // The result may be invalid or depend on another invalid result, just abort 1960 // without updating the cell value. Since the dirty flag will not be reset, 1961 // the proper value will be computed later. 1962 if(rDocument.GetRecursionHelper().IsAbortingDependencyComputation()) 1963 return; 1964 1965 // #i102616# For single-sheet saving consider only content changes, not format type, 1966 // because format type isn't set on loading (might be changed later) 1967 bool bContentChanged = false; 1968 1969 // Do not create a HyperLink() cell if the formula results in an error. 1970 if( pInterpreter->GetError() != FormulaError::NONE && pCode->IsHyperLink()) 1971 pCode->SetHyperLink(false); 1972 1973 if( pInterpreter->GetError() != FormulaError::NONE && pInterpreter->GetError() != FormulaError::CircularReference) 1974 { 1975 bChanged = true; 1976 1977 if (pInterpreter->GetError() == FormulaError::RetryCircular) 1978 { 1979 // Array formula matrix calculation corner case. Keep dirty 1980 // state, do not remove from formula tree or anything else, but 1981 // store FormulaError::CircularReference in case this cell does not get 1982 // recalculated. 1983 aResult.SetResultError( FormulaError::CircularReference); 1984 return; 1985 } 1986 1987 ResetDirty(); 1988 } 1989 1990 if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty()) 1991 { 1992 bool bIsValue = aResult.IsValue(); // the previous type 1993 // Did it converge? 1994 if ((bIsValue && pInterpreter->GetResultType() == svDouble && fabs( 1995 pInterpreter->GetNumResult() - aResult.GetDouble()) <= 1996 rDocument.GetDocOptions().GetIterEps()) || 1997 (!bIsValue && pInterpreter->GetResultType() == svString && 1998 pInterpreter->GetStringResult() == aResult.GetString())) 1999 { 2000 // A convergence in the first iteration doesn't necessarily 2001 // mean that it's done, it may be as not all related cells 2002 // of a circle changed their values yet. If the set really 2003 // converges it will do so also during the next iteration. This 2004 // fixes situations like of #i44115#. If this wasn't wanted an 2005 // initial "uncalculated" value would be needed for all cells 2006 // of a circular dependency => graph needed before calculation. 2007 if (nSeenInIteration > 1 || 2008 rDocument.GetDocOptions().GetIterCount() == 1) 2009 { 2010 ResetDirty(); 2011 } 2012 } 2013 } 2014 2015 // New error code? 2016 if( pInterpreter->GetError() != nOldErrCode ) 2017 { 2018 bChanged = true; 2019 // bContentChanged only has to be set if the file content would be changed 2020 if ( aResult.GetCellResultType() != svUnknown ) 2021 bContentChanged = true; 2022 } 2023 2024 ScFormulaResult aNewResult( pInterpreter->GetResultToken().get()); 2025 2026 // For IF() and other jumps or changed formatted source data the result 2027 // format may change for different runs, e.g. =IF(B1,B1) with first 2028 // B1:0 boolean FALSE next B1:23 numeric 23, we don't want the 23 2029 // displayed as TRUE. Do not force a general format though if 2030 // mbNeedsNumberFormat is set (because there was a general format..). 2031 // Note that nFormatType may be out of sync here if a format was 2032 // applied or cleared after the last run, but obtaining the current 2033 // format always just to check would be expensive. There may be 2034 // cases where the format should be changed but is not. If that turns 2035 // out to be a real problem then obtain the current format type after 2036 // the initial check when needed. 2037 bool bForceNumberFormat = (mbAllowNumberFormatChange && !mbNeedsNumberFormat && 2038 !SvNumberFormatter::IsCompatible( nFormatType, pInterpreter->GetRetFormatType())); 2039 2040 // We have some requirements additionally to IsCompatible(). 2041 // * Do not apply a NumberFormat::LOGICAL if the result value is not 2042 // 1.0 or 0.0 2043 // * Do not override an already set numeric number format if the result 2044 // is of type NumberFormat::LOGICAL, it could be user applied. 2045 // On the other hand, for an empty jump path instead of FALSE an 2046 // unexpected for example 0% could be displayed. YMMV. 2047 // * Never override a non-standard number format that indicates user 2048 // applied. 2049 // * NumberFormat::TEXT does not force a change. 2050 if (bForceNumberFormat) 2051 { 2052 sal_uInt32 nOldFormatIndex = NUMBERFORMAT_ENTRY_NOT_FOUND; 2053 const SvNumFormatType nRetType = pInterpreter->GetRetFormatType(); 2054 if (nRetType == SvNumFormatType::LOGICAL) 2055 { 2056 double fVal = aNewResult.GetDouble(); 2057 if (fVal != 1.0 && fVal != 0.0) 2058 bForceNumberFormat = false; 2059 else 2060 { 2061 nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos); 2062 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex); 2063 switch (nFormatType) 2064 { 2065 case SvNumFormatType::PERCENT: 2066 case SvNumFormatType::CURRENCY: 2067 case SvNumFormatType::SCIENTIFIC: 2068 case SvNumFormatType::FRACTION: 2069 bForceNumberFormat = false; 2070 break; 2071 case SvNumFormatType::NUMBER: 2072 if ((nOldFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) != 0) 2073 bForceNumberFormat = false; 2074 break; 2075 default: break; 2076 } 2077 } 2078 } 2079 else if (nRetType == SvNumFormatType::TEXT) 2080 { 2081 bForceNumberFormat = false; 2082 } 2083 if (bForceNumberFormat) 2084 { 2085 if (nOldFormatIndex == NUMBERFORMAT_ENTRY_NOT_FOUND) 2086 { 2087 nOldFormatIndex = rDocument.GetNumberFormat( rContext, aPos); 2088 nFormatType = rContext.GetFormatTable()->GetType( nOldFormatIndex); 2089 } 2090 if (nOldFormatIndex != 2091 ScGlobal::GetStandardFormat(rContext, nOldFormatIndex, nFormatType)) 2092 bForceNumberFormat = false; 2093 } 2094 } 2095 2096 if( mbNeedsNumberFormat || bForceNumberFormat ) 2097 { 2098 bool bSetFormat = true; 2099 const SvNumFormatType nOldFormatType = nFormatType; 2100 nFormatType = pInterpreter->GetRetFormatType(); 2101 sal_uInt32 nFormatIndex = pInterpreter->GetRetFormatIndex(); 2102 2103 if (nFormatType == SvNumFormatType::TEXT) 2104 { 2105 // Don't set text format as hard format. 2106 bSetFormat = false; 2107 } 2108 else if (nFormatType == SvNumFormatType::LOGICAL && cMatrixFlag != ScMatrixMode::NONE) 2109 { 2110 // In a matrix range do not set an (inherited) logical format 2111 // as hard format if the value does not represent a strict TRUE 2112 // or FALSE value. But do set for a top left error value so 2113 // following matrix cells can inherit for non-error values. 2114 // This solves a problem with IF() expressions in array context 2115 // where incidentally the top left element results in logical 2116 // type but some others don't. It still doesn't solve the 2117 // reverse case though, where top left is not logical type but 2118 // some other elements should be. We'd need to transport type 2119 // or format information on arrays. 2120 StackVar eNewCellResultType = aNewResult.GetCellResultType(); 2121 if (eNewCellResultType != svError || cMatrixFlag == ScMatrixMode::Reference) 2122 { 2123 if (eNewCellResultType != svDouble) 2124 { 2125 bSetFormat = false; 2126 nFormatType = nOldFormatType; // that? or number? 2127 } 2128 else 2129 { 2130 double fVal = aNewResult.GetDouble(); 2131 if (fVal != 1.0 && fVal != 0.0) 2132 { 2133 bSetFormat = false; 2134 nFormatType = SvNumFormatType::NUMBER; 2135 } 2136 } 2137 } 2138 } 2139 2140 if (bSetFormat && (bForceNumberFormat || ((nFormatIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0))) 2141 nFormatIndex = ScGlobal::GetStandardFormat(rContext, 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 2155 if (!rDocument.IsThreadedGroupCalcInProgress()) 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 { 2184 StackVar eOld = aResult.GetCellResultType(); 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 { 2207 StackVar eOld = aResult.GetCellResultType(); 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 2234 && rDocument.GetDocOptions().IsCalcAsShown() 2235 && nFormatType != SvNumFormatType::DATE 2236 && nFormatType != SvNumFormatType::TIME 2237 && nFormatType != SvNumFormatType::DATETIME ) 2238 { 2239 sal_uInt32 nFormat = rDocument.GetNumberFormat( rContext, aPos ); 2240 aResult.SetDouble( rDocument.RoundValueAsShown( 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. 2251 if( cMatrixFlag != ScMatrixMode::Formula && !pCode->IsHyperLink() ) 2252 aResult.SetToken( aResult.GetCellResultToken().get()); 2253 } 2254 if ( aResult.IsValue() && !std::isfinite( aResult.GetDouble() ) ) 2255 { 2256 // Coded double error may occur via filter import. 2257 FormulaError nErr = GetDoubleErrorValue( aResult.GetDouble()); 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 } 2268 if ( !rDocument.IsThreadedGroupCalcInProgress() && !pCode->IsRecalcModeAlways() ) 2269 rDocument.RemoveFromFormulaTree( this ); 2270 2271 // FORCED cells also immediately tested for validity (start macro possibly) 2272 2273 if ( pCode->IsRecalcModeForced() ) 2274 { 2275 sal_uInt32 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 2287 if (!rDocument.IsThreadedGroupCalcInProgress()) 2288 { 2289 ScProgress *pProgress = ScProgress::GetInterpretProgress(); 2290 if (pProgress && pProgress->Enabled()) 2291 { 2292 pProgress->SetStateCountDownOnPercent( 2293 rDocument.GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); 2294 } 2295 2296 switch (pInterpreter->GetVolatileType()) 2297 { 2298 case ScInterpreter::VOLATILE: 2299 // Volatile via built-in volatile functions. No actions needed. 2300 break; 2301 case ScInterpreter::VOLATILE_MACRO: 2302 // The formula contains a volatile macro. 2303 pCode->SetExclusiveRecalcModeAlways(); 2304 rDocument.PutInFormulaTree(this); 2305 StartListeningTo(rDocument); 2306 break; 2307 case ScInterpreter::NOT_VOLATILE: 2308 if (pCode->IsRecalcModeAlways()) 2309 { 2310 // The formula was previously volatile, but no more. 2311 EndListeningTo(rDocument); 2312 pCode->SetExclusiveRecalcModeNormal(); 2313 } 2314 else 2315 { 2316 // non-volatile formula. End listening to the area in case 2317 // it's listening due to macro module change. 2318 rDocument.EndListeningArea(BCA_LISTEN_ALWAYS, false, this); 2319 } 2320 rDocument.RemoveFromFormulaTree(this); 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 pCode->ClearRecalcModeMustAfterImport(); 2335 } 2336 2337 void ScFormulaCell::HandleStuffAfterParallelCalculation(ScInterpreter* pInterpreter) 2338 { 2339 aResult.HandleStuffAfterParallelCalculation(); 2340 2341 if( !pCode->GetCodeLen() ) 2342 return; 2343 2344 if ( !pCode->IsRecalcModeAlways() ) 2345 rDocument.RemoveFromFormulaTree( this ); 2346 2347 std::unique_ptr<ScInterpreter> pScopedInterpreter; 2348 if (pInterpreter) 2349 pInterpreter->Init(this, aPos, *pCode); 2350 else 2351 { 2352 pScopedInterpreter.reset(new ScInterpreter( this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode )); 2353 pInterpreter = pScopedInterpreter.get(); 2354 } 2355 2356 switch (pInterpreter->GetVolatileType()) 2357 { 2358 case ScInterpreter::VOLATILE_MACRO: 2359 // The formula contains a volatile macro. 2360 pCode->SetExclusiveRecalcModeAlways(); 2361 rDocument.PutInFormulaTree(this); 2362 StartListeningTo(rDocument); 2363 break; 2364 case ScInterpreter::NOT_VOLATILE: 2365 if (pCode->IsRecalcModeAlways()) 2366 { 2367 // The formula was previously volatile, but no more. 2368 EndListeningTo(rDocument); 2369 pCode->SetExclusiveRecalcModeNormal(); 2370 } 2371 else 2372 { 2373 // non-volatile formula. End listening to the area in case 2374 // it's listening due to macro module change. 2375 rDocument.EndListeningArea(BCA_LISTEN_ALWAYS, false, this); 2376 } 2377 rDocument.RemoveFromFormulaTree(this); 2378 break; 2379 default: 2380 ; 2381 } 2382 } 2383 2384 void ScFormulaCell::SetCompile( bool bVal ) 2385 { 2386 bCompile = bVal; 2387 } 2388 2389 void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows ) 2390 { 2391 ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst(); 2392 if (pMat) 2393 pMat->SetMatColsRows( nCols, nRows ); 2394 else if (nCols || nRows) 2395 { 2396 aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows)); 2397 // Setting the new token actually forces an empty result at this top 2398 // left cell, so have that recalculated. 2399 SetDirty(); 2400 } 2401 } 2402 2403 void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const 2404 { 2405 const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken(); 2406 if (pMat) 2407 pMat->GetMatColsRows( nCols, nRows); 2408 else 2409 { 2410 nCols = 0; 2411 nRows = 0; 2412 } 2413 } 2414 2415 void ScFormulaCell::SetInChangeTrack( bool bVal ) 2416 { 2417 bInChangeTrack = bVal; 2418 } 2419 2420 void ScFormulaCell::Notify( const SfxHint& rHint ) 2421 { 2422 if (rDocument.IsInDtorClear()) 2423 return; 2424 2425 const SfxHintId nHint = rHint.GetId(); 2426 if (nHint == SfxHintId::ScReference) 2427 { 2428 const sc::RefHint& rRefHint = static_cast<const sc::RefHint&>(rHint); 2429 2430 switch (rRefHint.getType()) 2431 { 2432 case sc::RefHint::ColumnReordered: 2433 { 2434 const sc::RefColReorderHint& rRefColReorder = 2435 static_cast<const sc::RefColReorderHint&>(rRefHint); 2436 if (!IsShared() || IsSharedTop()) 2437 pCode->MoveReferenceColReorder( 2438 aPos, rRefColReorder.getTab(), 2439 rRefColReorder.getStartRow(), 2440 rRefColReorder.getEndRow(), 2441 rRefColReorder.getColMap()); 2442 } 2443 break; 2444 case sc::RefHint::RowReordered: 2445 { 2446 const sc::RefRowReorderHint& rRefRowReorder = 2447 static_cast<const sc::RefRowReorderHint&>(rRefHint); 2448 if (!IsShared() || IsSharedTop()) 2449 pCode->MoveReferenceRowReorder( 2450 aPos, rRefRowReorder.getTab(), 2451 rRefRowReorder.getStartColumn(), 2452 rRefRowReorder.getEndColumn(), 2453 rRefRowReorder.getRowMap()); 2454 } 2455 break; 2456 case sc::RefHint::StartListening: 2457 { 2458 StartListeningTo(rDocument); 2459 } 2460 break; 2461 case sc::RefHint::StopListening: 2462 { 2463 EndListeningTo(rDocument); 2464 } 2465 break; 2466 default: 2467 ; 2468 } 2469 2470 return; 2471 } 2472 2473 if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF ) 2474 return; 2475 2476 if (!(nHint == SfxHintId::ScDataChanged || nHint == SfxHintId::ScTableOpDirty || (bSubTotal && nHint == SfxHintId::ScHiddenRowsChanged))) 2477 return; 2478 2479 bool bForceTrack = false; 2480 if ( nHint == SfxHintId::ScTableOpDirty ) 2481 { 2482 bForceTrack = !bTableOpDirty; 2483 if ( !bTableOpDirty ) 2484 { 2485 rDocument.AddTableOpFormulaCell( this ); 2486 bTableOpDirty = true; 2487 } 2488 } 2489 else 2490 { 2491 bForceTrack = !bDirty; 2492 SetDirtyVar(); 2493 } 2494 // Don't remove from FormulaTree to put in FormulaTrack to 2495 // put in FormulaTree again and again, only if necessary. 2496 // Any other means except ScRecalcMode::ALWAYS by which a cell could 2497 // be in FormulaTree if it would notify other cells through 2498 // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!? 2499 // Yes. The new TableOpDirty made it necessary to have a 2500 // forced mode where formulas may still be in FormulaTree from 2501 // TableOpDirty but have to notify dependents for normal dirty. 2502 if ( (bForceTrack || !rDocument.IsInFormulaTree( this ) 2503 || pCode->IsRecalcModeAlways()) 2504 && !rDocument.IsInFormulaTrack( this ) ) 2505 rDocument.AppendToFormulaTrack( this ); 2506 } 2507 2508 void ScFormulaCell::Query( SvtListener::QueryBase& rQuery ) const 2509 { 2510 switch (rQuery.getId()) 2511 { 2512 case SC_LISTENER_QUERY_FORMULA_GROUP_POS: 2513 { 2514 sc::RefQueryFormulaGroup& rRefQuery = 2515 static_cast<sc::RefQueryFormulaGroup&>(rQuery); 2516 if (IsShared()) 2517 rRefQuery.add(aPos); 2518 } 2519 break; 2520 default: 2521 ; 2522 } 2523 } 2524 2525 void ScFormulaCell::SetDirty( bool bDirtyFlag ) 2526 { 2527 if (IsInChangeTrack()) 2528 return; 2529 2530 if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF ) 2531 { 2532 SetDirtyVar(); 2533 rDocument.SetStreamValid(aPos.Tab(), false); 2534 return; 2535 } 2536 2537 // Avoid multiple formula tracking in Load() and in CompileAll() 2538 // after CopyScenario() and CopyBlockFromClip(). 2539 // If unconditional formula tracking is needed, set bDirty=false 2540 // before calling SetDirty(), for example in CompileTokenArray(). 2541 if ( !bDirty || mbPostponedDirty || !rDocument.IsInFormulaTree( this ) ) 2542 { 2543 if( bDirtyFlag ) 2544 SetDirtyVar(); 2545 rDocument.AppendToFormulaTrack( this ); 2546 2547 // While loading a document listeners have not been established yet. 2548 // Tracking would remove this cell from the FormulaTrack and add it to 2549 // the FormulaTree, once in there it would be assumed that its 2550 // dependents already had been tracked and it would be skipped on a 2551 // subsequent notify. Postpone tracking until all listeners are set. 2552 if (!rDocument.IsImportingXML() && !rDocument.IsInsertingFromOtherDoc()) 2553 rDocument.TrackFormulas(); 2554 } 2555 2556 rDocument.SetStreamValid(aPos.Tab(), false); 2557 } 2558 2559 void ScFormulaCell::SetDirtyVar() 2560 { 2561 bDirty = true; 2562 mbPostponedDirty = false; 2563 if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning) 2564 { 2565 mxGroup->meCalcState = sc::GroupCalcEnabled; 2566 mxGroup->mbPartOfCycle = false; 2567 } 2568 2569 // mark the sheet of this cell to be calculated 2570 //#FIXME do we need to revert this remnant of old fake vba events? rDocument.AddCalculateTable( aPos.Tab() ); 2571 } 2572 2573 void ScFormulaCell::SetDirtyAfterLoad() 2574 { 2575 bDirty = true; 2576 if ( rDocument.GetHardRecalcState() == ScDocument::HardRecalcState::OFF ) 2577 rDocument.PutInFormulaTree( this ); 2578 } 2579 2580 void ScFormulaCell::ResetTableOpDirtyVar() 2581 { 2582 bTableOpDirty = false; 2583 } 2584 2585 void ScFormulaCell::SetTableOpDirty() 2586 { 2587 if ( IsInChangeTrack() ) 2588 return; 2589 2590 if ( rDocument.GetHardRecalcState() != ScDocument::HardRecalcState::OFF ) 2591 bTableOpDirty = true; 2592 else 2593 { 2594 if ( !bTableOpDirty || !rDocument.IsInFormulaTree( this ) ) 2595 { 2596 if ( !bTableOpDirty ) 2597 { 2598 rDocument.AddTableOpFormulaCell( this ); 2599 bTableOpDirty = true; 2600 } 2601 rDocument.AppendToFormulaTrack( this ); 2602 rDocument.TrackFormulas( SfxHintId::ScTableOpDirty ); 2603 } 2604 } 2605 } 2606 2607 void ScFormulaCell::SetResultDouble( double n ) 2608 { 2609 aResult.SetDouble(n); 2610 } 2611 2612 void ScFormulaCell::SetResultToken( const formula::FormulaToken* pToken ) 2613 { 2614 aResult.SetToken(pToken); 2615 } 2616 2617 const svl::SharedString & ScFormulaCell::GetResultString() const 2618 { 2619 return aResult.GetString(); 2620 } 2621 2622 bool ScFormulaCell::HasHybridStringResult() const 2623 { 2624 return aResult.GetType() == formula::svHybridCell && !aResult.GetString().isEmpty(); 2625 } 2626 2627 void ScFormulaCell::SetResultMatrix( SCCOL nCols, SCROW nRows, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) 2628 { 2629 aResult.SetMatrix(nCols, nRows, pMat, pUL); 2630 } 2631 2632 void ScFormulaCell::SetErrCode( FormulaError n ) 2633 { 2634 /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is 2635 * used whether it is solely for transport of a simple result error and get 2636 * rid of that abuse. */ 2637 pCode->SetCodeError( n ); 2638 // Hard set errors are transported as result type value per convention, 2639 // e.g. via clipboard. ScFormulaResult::IsValue() and 2640 // ScFormulaResult::GetDouble() handle that. 2641 aResult.SetResultError( n ); 2642 } 2643 2644 void ScFormulaCell::SetResultError( FormulaError n ) 2645 { 2646 aResult.SetResultError( n ); 2647 } 2648 2649 void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits ) 2650 { 2651 if ( (nBits & ScRecalcMode::EMask) != ScRecalcMode::NORMAL ) 2652 SetDirtyVar(); 2653 pCode->AddRecalcMode( nBits ); 2654 } 2655 2656 void ScFormulaCell::SetHybridDouble( double n ) 2657 { 2658 aResult.SetHybridDouble( n); 2659 } 2660 2661 void ScFormulaCell::SetHybridString( const svl::SharedString& r ) 2662 { 2663 aResult.SetHybridString( r); 2664 } 2665 2666 void ScFormulaCell::SetHybridEmptyDisplayedAsString() 2667 { 2668 aResult.SetHybridEmptyDisplayedAsString(); 2669 } 2670 2671 void ScFormulaCell::SetHybridFormula( const OUString& r, 2672 const formula::FormulaGrammar::Grammar eGrammar ) 2673 { 2674 aResult.SetHybridFormula( r); eTempGrammar = eGrammar; 2675 } 2676 2677 OUString ScFormulaCell::GetHybridFormula() const 2678 { 2679 return aResult.GetHybridFormula(); 2680 } 2681 2682 // Dynamically create the URLField on a mouse-over action on a hyperlink() cell. 2683 void ScFormulaCell::GetURLResult( OUString& rURL, OUString& rCellText ) 2684 { 2685 OUString aCellString; 2686 2687 const Color* pColor; 2688 2689 // Cell Text uses the Cell format while the URL uses 2690 // the default format for the type. 2691 const sal_uInt32 nCellFormat = rDocument.GetNumberFormat( aPos ); 2692 ScInterpreterContext& rContext = rDocument.GetNonThreadedContext(); 2693 2694 const sal_uInt32 nURLFormat = ScGlobal::GetStandardFormat(rContext, nCellFormat, SvNumFormatType::NUMBER); 2695 2696 if ( IsValue() ) 2697 { 2698 double fValue = GetValue(); 2699 rContext.NFGetOutputString( fValue, nCellFormat, rCellText, &pColor ); 2700 } 2701 else 2702 { 2703 aCellString = GetString().getString(); 2704 rContext.NFGetOutputString( aCellString, nCellFormat, rCellText, &pColor ); 2705 } 2706 ScConstMatrixRef xMat( aResult.GetMatrix()); 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 rContext.NFGetOutputString( 2714 xMat->GetDouble(0, 1), nURLFormat, rURL, &pColor); 2715 } 2716 2717 if(rURL.isEmpty()) 2718 { 2719 if(IsValue()) 2720 rContext.NFGetOutputString( GetValue(), nURLFormat, rURL, &pColor ); 2721 else 2722 rContext.NFGetOutputString( aCellString, nURLFormat, rURL, &pColor ); 2723 } 2724 } 2725 2726 bool ScFormulaCell::IsMultilineResult() 2727 { 2728 if (!IsValue()) 2729 return aResult.IsMultiline(); 2730 return false; 2731 } 2732 2733 bool ScFormulaCell::IsHyperLinkCell() const 2734 { 2735 return pCode && pCode->IsHyperLink(); 2736 } 2737 2738 std::unique_ptr<EditTextObject> ScFormulaCell::CreateURLObject() 2739 { 2740 OUString aCellText; 2741 OUString aURL; 2742 GetURLResult( aURL, aCellText ); 2743 2744 return ScEditUtil::CreateURLObjectFromURL( rDocument, aURL, aCellText ); 2745 } 2746 2747 bool ScFormulaCell::IsEmpty() 2748 { 2749 MaybeInterpret(); 2750 return aResult.GetCellResultType() == formula::svEmptyCell; 2751 } 2752 2753 bool ScFormulaCell::IsEmptyDisplayedAsString() 2754 { 2755 MaybeInterpret(); 2756 return aResult.IsEmptyDisplayedAsString(); 2757 } 2758 2759 bool ScFormulaCell::IsValue() 2760 { 2761 MaybeInterpret(); 2762 return aResult.IsValue(); 2763 } 2764 2765 bool ScFormulaCell::IsValueNoError() 2766 { 2767 MaybeInterpret(); 2768 if (pCode->GetCodeError() != FormulaError::NONE) 2769 return false; 2770 2771 return aResult.IsValueNoError(); 2772 } 2773 2774 bool ScFormulaCell::IsValueNoError() const 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 2786 double ScFormulaCell::GetValue() 2787 { 2788 MaybeInterpret(); 2789 return GetRawValue(); 2790 } 2791 2792 const svl::SharedString & ScFormulaCell::GetString() 2793 { 2794 MaybeInterpret(); 2795 return GetRawString(); 2796 } 2797 2798 double ScFormulaCell::GetRawValue() const 2799 { 2800 if ((pCode->GetCodeError() == FormulaError::NONE) && 2801 aResult.GetResultError() == FormulaError::NONE) 2802 return aResult.GetDouble(); 2803 return 0.0; 2804 } 2805 2806 const svl::SharedString & ScFormulaCell::GetRawString() const 2807 { 2808 if ((pCode->GetCodeError() == FormulaError::NONE) && 2809 aResult.GetResultError() == FormulaError::NONE) 2810 return aResult.GetString(); 2811 2812 return svl::SharedString::getEmptyString(); 2813 } 2814 2815 const ScMatrix* ScFormulaCell::GetMatrix() 2816 { 2817 if ( rDocument.GetAutoCalc() ) 2818 { 2819 if( IsDirtyOrInTableOpDirty() 2820 // Was stored !bDirty but an accompanying matrix cell was bDirty? 2821 || (!bDirty && cMatrixFlag == ScMatrixMode::Formula && !aResult.GetMatrix())) 2822 Interpret(); 2823 } 2824 return aResult.GetMatrix().get(); 2825 } 2826 2827 bool ScFormulaCell::GetMatrixOrigin( const ScDocument& rDoc, ScAddress& rPos ) const 2828 { 2829 switch ( cMatrixFlag ) 2830 { 2831 case ScMatrixMode::Formula : 2832 rPos = aPos; 2833 return true; 2834 case ScMatrixMode::Reference : 2835 { 2836 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 2837 formula::FormulaToken* t = aIter.GetNextReferenceRPN(); 2838 if( t ) 2839 { 2840 ScSingleRefData& rRef = *t->GetSingleRef(); 2841 ScAddress aAbs = rRef.toAbs(rDoc, aPos); 2842 if (rDoc.ValidAddress(aAbs)) 2843 { 2844 rPos = aAbs; 2845 return true; 2846 } 2847 } 2848 } 2849 break; 2850 default: break; 2851 } 2852 return false; 2853 } 2854 2855 sc::MatrixEdge ScFormulaCell::GetMatrixEdge( const ScDocument& rDoc, ScAddress& rOrgPos ) const 2856 { 2857 switch ( cMatrixFlag ) 2858 { 2859 case ScMatrixMode::Formula : 2860 case ScMatrixMode::Reference : 2861 { 2862 static thread_local SCCOL nC; 2863 static thread_local SCROW nR; 2864 ScAddress aOrg; 2865 if ( !GetMatrixOrigin( rDoc, aOrg ) ) 2866 return sc::MatrixEdge::Nothing; 2867 if ( aOrg != rOrgPos ) 2868 { // First time or a different matrix than last time. 2869 rOrgPos = aOrg; 2870 const ScFormulaCell* pFCell; 2871 if ( cMatrixFlag == ScMatrixMode::Reference ) 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: " 2924 << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument) 2925 << ", MatOrg: " 2926 << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument) ); 2927 #endif 2928 return sc::MatrixEdge::Nothing; 2929 } 2930 } 2931 // here we are, healthy and clean, somewhere in between 2932 SCCOL dC = aPos.Col() - aOrg.Col(); 2933 SCROW dR = aPos.Row() - aOrg.Row(); 2934 sc::MatrixEdge nEdges = sc::MatrixEdge::Nothing; 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: " 2951 << aPos.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument) 2952 << ", MatOrg: " 2953 << aOrg.Format(ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID, &rDocument) 2954 << ", MatCols: " << static_cast<sal_Int32>( nC ) 2955 << ", MatRows: " << static_cast<sal_Int32>( nR ) 2956 << ", DiffCols: " << static_cast<sal_Int32>( dC ) 2957 << ", DiffRows: " << static_cast<sal_Int32>( dR )); 2958 } 2959 return nEdges; 2960 } 2961 default: 2962 return sc::MatrixEdge::Nothing; 2963 } 2964 } 2965 2966 FormulaError ScFormulaCell::GetErrCode() 2967 { 2968 MaybeInterpret(); 2969 2970 /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors 2971 * and not also abused for signaling other error conditions we could bail 2972 * out even before attempting to interpret broken code. */ 2973 FormulaError nErr = pCode->GetCodeError(); 2974 if (nErr != FormulaError::NONE) 2975 return nErr; 2976 return aResult.GetResultError(); 2977 } 2978 2979 FormulaError ScFormulaCell::GetRawError() const 2980 { 2981 FormulaError nErr = pCode->GetCodeError(); 2982 if (nErr != FormulaError::NONE) 2983 return nErr; 2984 return aResult.GetResultError(); 2985 } 2986 2987 bool ScFormulaCell::GetErrorOrValue( FormulaError& rErr, double& rVal ) 2988 { 2989 MaybeInterpret(); 2990 2991 rErr = pCode->GetCodeError(); 2992 if (rErr != FormulaError::NONE) 2993 return true; 2994 2995 return aResult.GetErrorOrDouble(rErr, rVal); 2996 } 2997 2998 sc::FormulaResultValue ScFormulaCell::GetResult() 2999 { 3000 MaybeInterpret(); 3001 3002 FormulaError nErr = pCode->GetCodeError(); 3003 if (nErr != FormulaError::NONE) 3004 return sc::FormulaResultValue(nErr); 3005 3006 return aResult.GetResult(); 3007 } 3008 3009 sc::FormulaResultValue ScFormulaCell::GetResult() const 3010 { 3011 FormulaError nErr = pCode->GetCodeError(); 3012 if (nErr != FormulaError::NONE) 3013 return sc::FormulaResultValue(nErr); 3014 3015 return aResult.GetResult(); 3016 } 3017 3018 bool ScFormulaCell::HasOneReference( ScRange& r ) const 3019 { 3020 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3021 formula::FormulaToken* p = aIter.GetNextReferenceRPN(); 3022 if( p && !aIter.GetNextReferenceRPN() ) // only one! 3023 { 3024 SingleDoubleRefProvider aProv( *p ); 3025 r.aStart = aProv.Ref1.toAbs(rDocument, aPos); 3026 r.aEnd = aProv.Ref2.toAbs(rDocument, aPos); 3027 return true; 3028 } 3029 else 3030 return false; 3031 } 3032 3033 bool 3034 ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const 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 3051 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 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 3085 ScFormulaCell::RelNameRef ScFormulaCell::HasRelNameReference() const 3086 { 3087 RelNameRef eRelNameRef = RelNameRef::NONE; 3088 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3089 formula::FormulaToken* t; 3090 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr ) 3091 { 3092 switch (t->GetType()) 3093 { 3094 case formula::svSingleRef: 3095 if (t->GetSingleRef()->IsRelName() && eRelNameRef == RelNameRef::NONE) 3096 eRelNameRef = RelNameRef::SINGLE; 3097 break; 3098 case formula::svDoubleRef: 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 3115 bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt ) 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. 3130 ScAddress aErrorPos( ScAddress::UNINITIALIZED ); 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 3139 namespace { 3140 3141 /** 3142 * Check if we need to re-compile column or row names. 3143 */ 3144 bool 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 3155 formula::FormulaTokenArrayPlainIterator aIter(rCode); 3156 formula::FormulaToken* t; 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 3202 formula::FormulaTokenArrayPlainIterator aIter(rCode); 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 3225 void 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 3248 bool ScFormulaCell::UpdateReferenceOnShift( 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 3368 bool ScFormulaCell::UpdateReferenceOnMove( 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(). 3457 && !(rDocument.IsInsertingFromOtherDoc() && rCxt.maRange.Contains(aPos)); 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 { 3486 StartListeningTo( rDocument ); 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 3499 bool ScFormulaCell::UpdateReferenceOnCopy( 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 3558 bool ScFormulaCell::UpdateReference( 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 3593 void ScFormulaCell::UpdateInsertTab( const sc::RefUpdateInsertTabContext& rCxt ) 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()); 3598 if (rDocument.IsClipOrUndo() || !pCode->HasReferences()) 3599 { 3600 if (bPosChanged) 3601 aPos.IncTab(rCxt.mnSheets); 3602 3603 return; 3604 } 3605 3606 EndListeningTo( rDocument ); 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 3615 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnInsertedTab(rCxt, aOldPos); 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 3623 void ScFormulaCell::UpdateDeleteTab( const sc::RefUpdateDeleteTabContext& rCxt ) 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); 3628 if (rDocument.IsClipOrUndo() || !pCode->HasReferences()) 3629 { 3630 if (bPosChanged) 3631 aPos.IncTab(-1*rCxt.mnSheets); 3632 return; 3633 } 3634 3635 EndListeningTo( rDocument ); 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 3644 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnDeletedTab(rCxt, aOldPos); 3645 if (aRes.mbNameModified) 3646 // Re-compile after sheet(s) have been deleted. 3647 bCompile = true; 3648 } 3649 3650 void ScFormulaCell::UpdateMoveTab( const sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo ) 3651 { 3652 // Adjust tokens only when it's not grouped or grouped top cell. 3653 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this; 3654 3655 if (!pCode->HasReferences() || rDocument.IsClipOrUndo()) 3656 { 3657 aPos.SetTab(nTabNo); 3658 return; 3659 } 3660 3661 EndListeningTo(rDocument); 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 3671 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMovedTab(rCxt, aOldPos); 3672 if (aRes.mbNameModified) 3673 // Re-compile after sheet(s) have been deleted. 3674 bCompile = true; 3675 } 3676 3677 void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) 3678 { 3679 if (rDocument.IsClipOrUndo()) 3680 return; 3681 3682 bool bAdjustCode = !mxGroup || mxGroup->mpTopCell == this; 3683 if (!bAdjustCode) 3684 return; 3685 3686 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3687 formula::FormulaToken* p = aIter.GetNextReferenceRPN(); 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 3703 bool ScFormulaCell::TestTabRefAbs(SCTAB nTable) 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; 3713 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3714 formula::FormulaToken* p = aIter.GetNextReferenceRPN(); 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 3741 void ScFormulaCell::UpdateCompile( bool bForceIfNameInUse ) 3742 { 3743 if ( bForceIfNameInUse && !bCompile ) 3744 bCompile = pCode->HasNameOrColRowName(); 3745 if ( bCompile ) 3746 pCode->SetCodeError( FormulaError::NONE ); // make sure it will really be compiled 3747 CompileTokenArray(); 3748 } 3749 3750 static void lcl_TransposeReference(ScSingleRefData& rRef) 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 3760 void ScFormulaCell::TransposeReference() 3761 { 3762 bool bFound = false; 3763 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3764 formula::FormulaToken* t; 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 { 3774 lcl_TransposeReference(rRef1); 3775 3776 if ( bDouble ) 3777 lcl_TransposeReference(rRef2); 3778 3779 bFound = true; 3780 } 3781 } 3782 } 3783 3784 if (bFound) 3785 bCompile = true; 3786 } 3787 3788 void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, 3789 ScDocument* pUndoDoc ) 3790 { 3791 EndListeningTo( rDocument ); 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 3820 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3821 formula::FormulaToken* t; 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 { 3832 SingleDoubleRefModifier aMod(*t); 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 3874 void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) 3875 { 3876 EndListeningTo( rDocument ); 3877 3878 bool bRefChanged = false; 3879 3880 formula::FormulaTokenArrayPlainIterator aIter(*pCode); 3881 formula::FormulaToken* t; 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 { 3893 SingleDoubleRefModifier aMod(*t); 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() 3916 static 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 3938 void ScFormulaCell::FindRangeNamesInUse(sc::UpdatedRangeNames& rIndexes) const 3939 { 3940 lcl_FindRangeNamesInUse( rIndexes, pCode, rDocument, 0); 3941 } 3942 3943 void ScFormulaCell::SetChanged(bool b) 3944 { 3945 bChanged = b; 3946 } 3947 3948 void 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 3955 void ScFormulaCell::SetRunning( bool bVal ) 3956 { 3957 bRunning = bVal; 3958 } 3959 3960 void ScFormulaCell::CompileDBFormula( sc::CompileFormulaContext& rCxt ) 3961 { 3962 FormulaTokenArrayPlainIterator aIter(*pCode); 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 3976 void ScFormulaCell::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt ) 3977 { 3978 FormulaTokenArrayPlainIterator aIter(*pCode); 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 3991 void ScFormulaCell::SetPrevious( ScFormulaCell* pF ) { pPrevious = pF; } 3992 void ScFormulaCell::SetNext( ScFormulaCell* pF ) { pNext = pF; } 3993 void ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF ) { pPreviousTrack = pF; } 3994 void ScFormulaCell::SetNextTrack( ScFormulaCell* pF ) { pNextTrack = pF; } 3995 3996 ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant ) 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 4016 void ScFormulaCell::SetCellGroup( const ScFormulaCellGroupRef &xRef ) 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 4038 ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( const ScFormulaCell& rOther ) const 4039 { 4040 // no Matrix formulae yet. 4041 if ( GetMatrixFlag() != ScMatrixMode::NONE ) 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: 4091 case formula::svExternalSingleRef: 4092 case formula::svExternalDoubleRef: 4093 // Ignoring matrix and external references for now. 4094 return NotEqual; 4095 4096 case formula::svSingleRef: 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; 4107 case formula::svDoubleRef: 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; 4149 case formula::svExternal: 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. 4205 case formula::svSingleRef: 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; 4216 case formula::svDoubleRef: 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 4262 namespace { 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 4268 int 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 4290 struct 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 // Because Lookup will extend the Result Vector under certain circumstances listed at: 4437 // https://wiki.documentfoundation.org/Documentation/Calc_Functions/LOOKUP 4438 // then if the Lookup has a Result Vector only accept the Lookup for parallelization 4439 // of the Result Vector has the same dimensions as the Search Vector. 4440 bool LookupResultVectorMismatch(sal_Int32 nTokenIdx) 4441 { 4442 if (nTokenIdx >= 3) 4443 { 4444 FormulaToken** pRPNArray = mrCode.GetCode(); 4445 if (pRPNArray[nTokenIdx - 1]->GetOpCode() == ocPush && // <- result vector 4446 pRPNArray[nTokenIdx - 2]->GetOpCode() == ocPush && // <- search vector 4447 pRPNArray[nTokenIdx - 2]->GetType() == svDoubleRef && 4448 pRPNArray[nTokenIdx - 3]->GetOpCode() == ocPush) // <- search criterion 4449 { 4450 auto res = pRPNArray[nTokenIdx - 1]; 4451 // If Result vector is just a single cell reference 4452 // LOOKUP extends it as a column vector. 4453 if (res->GetType() == svSingleRef) 4454 return true; 4455 4456 // If Result vector is a cell range and the match position 4457 // falls outside its length, it gets automatically extended 4458 // to the length of Search vector, but in the direction of 4459 // Result vector. 4460 if (res->GetType() == svDoubleRef) 4461 { 4462 ScComplexRefData aRef1 = *res->GetDoubleRef(); 4463 ScComplexRefData aRef2 = *pRPNArray[nTokenIdx - 2]->GetDoubleRef(); 4464 ScRange resultRange = aRef1.toAbs(mrDoc, mrPos); 4465 ScRange sourceRange = aRef2.toAbs(mrDoc, mrPos); 4466 4467 SCROW nResultRows = resultRange.aEnd.Row() - resultRange.aStart.Row(); 4468 SCROW nSourceRows = sourceRange.aEnd.Row() - sourceRange.aStart.Row(); 4469 if (nResultRows != nSourceRows) 4470 return true; 4471 4472 SCCOL nResultCols = resultRange.aEnd.Col() - resultRange.aStart.Col(); 4473 SCCOL nSourceCols = sourceRange.aEnd.Col() - sourceRange.aStart.Col(); 4474 if (nResultCols != nSourceCols) 4475 return true; 4476 } 4477 } 4478 } 4479 return false; 4480 } 4481 4482 bool DoIt(ScRangeList* pSuccessfulDependencies, ScAddress* pDirtiedAddress) 4483 { 4484 // Partially from ScGroupTokenConverter::convert in sc/source/core/data/grouptokenconverter.cxx 4485 4486 ScRangeList aRangeList; 4487 4488 // Self references should be checked by considering the entire formula-group not just the provided span. 4489 bool bHasSelfReferences = false; 4490 bool bInDocShellRecalc = mrDoc.IsInDocShellRecalc(); 4491 4492 FormulaToken** pRPNArray = mrCode.GetCode(); 4493 sal_uInt16 nCodeLen = mrCode.GetCodeLen(); 4494 for (sal_Int32 nTokenIdx = nCodeLen-1; nTokenIdx >= 0; --nTokenIdx) 4495 { 4496 auto p = pRPNArray[nTokenIdx]; 4497 if (!bInDocShellRecalc) 4498 { 4499 // The dependency evaluator evaluates all arguments of IF/IFS/SWITCH irrespective 4500 // of the result of the condition expression. 4501 // This is a perf problem if we *don't* intent on recalc'ing all dirty cells 4502 // in the document. So lets disable threading and stop dependency evaluation if 4503 // the call did not originate from ScDocShell::DoRecalc()/ScDocShell::DoHardRecalc() 4504 // for formulae with IF/IFS/SWITCH 4505 OpCode nOpCode = p->GetOpCode(); 4506 if (nOpCode == ocIf || nOpCode == ocIfs_MS || nOpCode == ocSwitch_MS) 4507 return false; 4508 } 4509 4510 if (p->GetOpCode() == ocLookup && LookupResultVectorMismatch(nTokenIdx)) 4511 { 4512 SAL_INFO("sc.core.formulacell", "Lookup Result Vector size doesn't match Search Vector"); 4513 return false; 4514 } 4515 4516 if (p->GetOpCode() == ocRange) 4517 { 4518 // We are just looking at svSingleRef/svDoubleRef, so we will miss that ocRange constructs 4519 // a range from its arguments, and only examining the individual args doesn't capture the 4520 // true range of dependencies 4521 SAL_WARN("sc.core.formulacell", "dynamic range, dropping as candidate for parallelizing"); 4522 return false; 4523 } 4524 4525 switch (p->GetType()) 4526 { 4527 case svSingleRef: 4528 { 4529 ScSingleRefData aRef = *p->GetSingleRef(); // =Sheet1!A1 4530 if( aRef.IsDeleted()) 4531 return false; 4532 ScAddress aRefPos = aRef.toAbs(mrDoc, mrPos); 4533 4534 if (!mrDoc.HasTable(aRefPos.Tab())) 4535 return false; // or true? 4536 4537 if (aRef.IsRowRel()) 4538 { 4539 if (isSelfReferenceRelative(aRefPos, aRef.Row())) 4540 { 4541 bHasSelfReferences = true; 4542 continue; 4543 } 4544 4545 // Trim data array length to actual data range. 4546 SCROW nTrimLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row() + mnStartOffset, mnSpanLen); 4547 4548 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row() + mnStartOffset, aRefPos.Tab(), 4549 aRefPos.Col(), aRefPos.Row() + mnStartOffset + nTrimLen - 1, aRefPos.Tab())); 4550 } 4551 else 4552 { 4553 if (isSelfReferenceAbsolute(aRefPos)) 4554 { 4555 bHasSelfReferences = true; 4556 continue; 4557 } 4558 4559 aRangeList.Join(ScRange(aRefPos.Col(), aRefPos.Row(), aRefPos.Tab())); 4560 } 4561 } 4562 break; 4563 case svDoubleRef: 4564 { 4565 ScComplexRefData aRef = *p->GetDoubleRef(); 4566 if( aRef.IsDeleted()) 4567 return false; 4568 ScRange aAbs = aRef.toAbs(mrDoc, mrPos); 4569 4570 // Multiple sheet 4571 if (aAbs.aStart.Tab() != aAbs.aEnd.Tab()) 4572 return false; 4573 4574 bool bIsRef1RowRel = aRef.Ref1.IsRowRel(); 4575 // Check for self reference. 4576 if (bIsRef1RowRel) 4577 { 4578 if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row())) 4579 { 4580 bHasSelfReferences = true; 4581 continue; 4582 } 4583 } 4584 else if (isSelfReferenceAbsolute(aAbs.aStart)) 4585 { 4586 bHasSelfReferences = true; 4587 continue; 4588 } 4589 4590 bool bIsRef2RowRel = aRef.Ref2.IsRowRel(); 4591 if (bIsRef2RowRel) 4592 { 4593 if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row())) 4594 { 4595 bHasSelfReferences = true; 4596 continue; 4597 } 4598 } 4599 else if (isSelfReferenceAbsolute(aAbs.aEnd)) 4600 { 4601 bHasSelfReferences = true; 4602 continue; 4603 } 4604 4605 if (isDoubleRefSpanGroupRange(aAbs, bIsRef1RowRel, bIsRef2RowRel)) 4606 { 4607 bHasSelfReferences = true; 4608 continue; 4609 } 4610 4611 SCROW nFirstRefStartRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row(); 4612 SCROW nLastRefEndRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row(); 4613 4614 SCROW nFirstRefEndRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnEndOffset : aAbs.aStart.Row(); 4615 SCROW nLastRefStartRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnStartOffset : aAbs.aEnd.Row(); 4616 4617 // The first row that will be referenced through the doubleref. 4618 SCROW nFirstRefRow = std::min(nFirstRefStartRow, nLastRefStartRow); 4619 // The last row that will be referenced through the doubleref. 4620 SCROW nLastRefRow = std::max(nLastRefEndRow, nFirstRefEndRow); 4621 4622 // Number of rows to be evaluated from nFirstRefRow. 4623 SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1; 4624 assert(nArrayLength > 0); 4625 4626 // Trim trailing empty rows. 4627 nArrayLength = trimLength(aAbs.aStart.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), nFirstRefRow, nArrayLength); 4628 4629 aRangeList.Join(ScRange(aAbs.aStart.Col(), nFirstRefRow, aAbs.aStart.Tab(), 4630 aAbs.aEnd.Col(), nFirstRefRow + nArrayLength - 1, aAbs.aEnd.Tab())); 4631 } 4632 break; 4633 default: 4634 break; 4635 } 4636 } 4637 4638 // Compute dependencies irrespective of the presence of any self references. 4639 // These dependencies would get computed via InterpretTail anyway when we disable group calc, so lets do it now. 4640 // The advantage is that the FG's get marked for cycles early if present, and can avoid lots of complications. 4641 for (size_t i = 0; i < aRangeList.size(); ++i) 4642 { 4643 const ScRange & rRange = aRangeList[i]; 4644 assert(rRange.aStart.Tab() == rRange.aEnd.Tab()); 4645 for (auto nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); nCol++) 4646 { 4647 SCROW nStartRow = rRange.aStart.Row(); 4648 SCROW nLength = rRange.aEnd.Row() - rRange.aStart.Row() + 1; 4649 if( mFromFirstRow ) 4650 { // include also all previous rows 4651 nLength += nStartRow; 4652 nStartRow = 0; 4653 } 4654 if (!mrDoc.HandleRefArrayForParallelism(ScAddress(nCol, nStartRow, rRange.aStart.Tab()), 4655 nLength, mxGroup, pDirtiedAddress)) 4656 return false; 4657 } 4658 } 4659 4660 if (bHasSelfReferences) 4661 mxGroup->mbPartOfCycle = true; 4662 4663 if (pSuccessfulDependencies && !bHasSelfReferences) 4664 *pSuccessfulDependencies = aRangeList; 4665 4666 return !bHasSelfReferences; 4667 } 4668 }; 4669 4670 } // anonymous namespace 4671 4672 bool ScFormulaCell::InterpretFormulaGroup(SCROW nStartOffset, SCROW nEndOffset) 4673 { 4674 if (!mxGroup || !pCode) 4675 return false; 4676 4677 auto aScope = sc::FormulaLogger::get().enterGroup(rDocument, *this); 4678 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper(); 4679 4680 if (mxGroup->mbPartOfCycle) 4681 { 4682 aScope.addMessage("This formula-group is part of a cycle"); 4683 return false; 4684 } 4685 4686 if (mxGroup->meCalcState == sc::GroupCalcDisabled) 4687 { 4688 static constexpr OUStringLiteral MESSAGE = u"group calc disabled"; 4689 aScope.addMessage(MESSAGE); 4690 return false; 4691 } 4692 4693 // Use SC_FORCE_CALCULATION=opencl/threads to force calculation e.g. for unittests 4694 static ForceCalculationType forceType = ScCalcConfig::getForceCalculationType(); 4695 if (forceType == ForceCalculationCore 4696 || ( GetWeight() < ScInterpreter::GetGlobalConfig().mnOpenCLMinimumFormulaGroupSize 4697 && forceType != ForceCalculationOpenCL 4698 && forceType != ForceCalculationThreads)) 4699 { 4700 mxGroup->meCalcState = sc::GroupCalcDisabled; 4701 aScope.addGroupSizeThresholdMessage(*this); 4702 return false; 4703 } 4704 4705 if (cMatrixFlag != ScMatrixMode::NONE) 4706 { 4707 mxGroup->meCalcState = sc::GroupCalcDisabled; 4708 aScope.addMessage("matrix skipped"); 4709 return false; 4710 } 4711 4712 if( forceType != ForceCalculationNone ) 4713 { 4714 // ScConditionEntry::Interpret() creates a temporary cell and interprets it 4715 // without it actually being in the document at the specified position. 4716 // That would confuse opencl/threading code, as they refer to the cell group 4717 // also using the position. This is normally not triggered (single cells 4718 // are normally not in a cell group), but if forced, check for this explicitly. 4719 if( rDocument.GetFormulaCell( aPos ) != this ) 4720 { 4721 mxGroup->meCalcState = sc::GroupCalcDisabled; 4722 aScope.addMessage("cell not in document"); 4723 return false; 4724 } 4725 } 4726 4727 // Get rid of -1's in offsets (defaults) or any invalid offsets. 4728 SCROW nMaxOffset = mxGroup->mnLength - 1; 4729 nStartOffset = nStartOffset < 0 ? 0 : std::min(nStartOffset, nMaxOffset); 4730 nEndOffset = nEndOffset < 0 ? nMaxOffset : std::min(nEndOffset, nMaxOffset); 4731 4732 if (nEndOffset < nStartOffset) 4733 { 4734 nStartOffset = 0; 4735 nEndOffset = nMaxOffset; 4736 } 4737 4738 if (nEndOffset == nStartOffset && forceType == ForceCalculationNone) 4739 return false; // Do not use threads for a single row. 4740 4741 // Guard against endless recursion of Interpret() calls, for this to work 4742 // ScFormulaCell::InterpretFormulaGroup() must never be called through 4743 // anything else than ScFormulaCell::Interpret(), same as 4744 // ScFormulaCell::InterpretTail() 4745 RecursionCounter aRecursionCounter( rRecursionHelper, this); 4746 4747 bool bDependencyComputed = false; 4748 bool bDependencyCheckFailed = false; 4749 4750 // Preference order: First try OpenCL, then threading. 4751 // TODO: Do formula-group span computation for OCL too if nStartOffset/nEndOffset are non default. 4752 if( InterpretFormulaGroupOpenCL(aScope, bDependencyComputed, bDependencyCheckFailed)) 4753 return true; 4754 4755 if( InterpretFormulaGroupThreading(aScope, bDependencyComputed, bDependencyCheckFailed, nStartOffset, nEndOffset)) 4756 return true; 4757 4758 return false; 4759 } 4760 4761 bool ScFormulaCell::CheckComputeDependencies(sc::FormulaLogger::GroupScope& rScope, bool fromFirstRow, 4762 SCROW nStartOffset, SCROW nEndOffset, 4763 bool bCalcDependencyOnly, 4764 ScRangeList* pSuccessfulDependencies, 4765 ScAddress* pDirtiedAddress) 4766 { 4767 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper(); 4768 // iterate over code in the formula ... 4769 // ensure all input is pre-calculated - 4770 // to avoid writing during the calculation 4771 if (bCalcDependencyOnly) 4772 { 4773 // Lets not use "ScFormulaGroupDependencyComputeGuard" here as there is no corresponding 4774 // "ScFormulaGroupCycleCheckGuard" for this formula-group. 4775 // (We can only reach here from a multi-group dependency evaluation attempt). 4776 // (These two have to be in pairs always for any given formula-group) 4777 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset); 4778 return aCalculator.DoIt(pSuccessfulDependencies, pDirtiedAddress); 4779 } 4780 4781 bool bOKToParallelize = false; 4782 { 4783 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, this); 4784 if (mxGroup->mbPartOfCycle) 4785 { 4786 mxGroup->meCalcState = sc::GroupCalcDisabled; 4787 rScope.addMessage("found circular formula-group dependencies"); 4788 return false; 4789 } 4790 4791 ScFormulaGroupDependencyComputeGuard aDepComputeGuard(rRecursionHelper); 4792 ScDependantsCalculator aCalculator(rDocument, *pCode, *this, mxGroup->mpTopCell->aPos, fromFirstRow, nStartOffset, nEndOffset); 4793 bOKToParallelize = aCalculator.DoIt(pSuccessfulDependencies, pDirtiedAddress); 4794 4795 } 4796 4797 if (rRecursionHelper.IsInRecursionReturn()) 4798 { 4799 mxGroup->meCalcState = sc::GroupCalcDisabled; 4800 rScope.addMessage("Recursion limit reached, cannot thread this formula group now"); 4801 return false; 4802 } 4803 4804 if (mxGroup->mbPartOfCycle) 4805 { 4806 mxGroup->meCalcState = sc::GroupCalcDisabled; 4807 rScope.addMessage("found circular formula-group dependencies"); 4808 return false; 4809 } 4810 4811 if (!rRecursionHelper.AreGroupsIndependent()) 4812 { 4813 // This call resulted from a dependency calculation for a multigroup-threading attempt, 4814 // but found dependency among the groups. 4815 rScope.addMessage("multi-group-dependency failed"); 4816 return false; 4817 } 4818 4819 if (!bOKToParallelize) 4820 { 4821 mxGroup->meCalcState = sc::GroupCalcDisabled; 4822 rScope.addMessage("could not do new dependencies calculation thing"); 4823 return false; 4824 } 4825 4826 return true; 4827 } 4828 4829 static SCCOL lcl_probeLeftOrRightFGs(const ScFormulaCellGroupRef& xGroup, const ScDocument& rDoc, 4830 o3tl::sorted_vector<ScFormulaCellGroup*>& rFGSet, 4831 std::map<SCCOL, ScFormulaCell*>& rFGMap, bool bLeft) 4832 { 4833 const SCROW nLen = xGroup->mnLength; 4834 const sal_Int32 nWt = xGroup->mnWeight; 4835 ScAddress aAddr(xGroup->mpTopCell->aPos); 4836 4837 SCCOL nColRet = aAddr.Col(); 4838 4839 const SCCOL nMaxCol = rDoc.GetAllocatedColumnsCount(aAddr.Tab()) - 1; 4840 if (bLeft) 4841 --nColRet; 4842 else 4843 ++nColRet; 4844 4845 while (nColRet >= 0 && nColRet <= nMaxCol) 4846 { 4847 aAddr.SetCol(nColRet); 4848 const ScFormulaCell* pCell = rDoc.GetFormulaCell(aAddr); 4849 if (!pCell) 4850 break; 4851 4852 if (!pCell->NeedsInterpret()) 4853 break; 4854 4855 const ScFormulaCellGroupRef& xNGroup = pCell->GetCellGroup(); 4856 if (!xNGroup) 4857 break; 4858 4859 if (!pCell->GetCode()->IsEnabledForThreading()) 4860 break; 4861 4862 if (xNGroup->mpTopCell->aPos.Row() != aAddr.Row()) 4863 break; 4864 4865 const SCROW nNLen = xNGroup->mnLength; 4866 const sal_Int32 nNWt = pCell->GetWeight(); 4867 if (nNLen != nLen || nNWt != nWt) 4868 break; 4869 4870 rFGSet.insert(xNGroup.get()); 4871 rFGMap[nColRet] = xNGroup->mpTopCell; 4872 4873 if (bLeft) 4874 --nColRet; 4875 else 4876 ++nColRet; 4877 } 4878 4879 if (bLeft) 4880 ++nColRet; 4881 else 4882 --nColRet; 4883 4884 return nColRet; 4885 } 4886 4887 // To be called only from InterpretFormulaGroup(). 4888 bool ScFormulaCell::InterpretFormulaGroupThreading(sc::FormulaLogger::GroupScope& aScope, 4889 bool& bDependencyComputed, 4890 bool& bDependencyCheckFailed, 4891 SCROW nStartOffset, 4892 SCROW nEndOffset) 4893 { 4894 static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION"); 4895 if (!bDependencyCheckFailed && !bThreadingProhibited && 4896 pCode->IsEnabledForThreading() && 4897 ScCalcConfig::isThreadingEnabled()) 4898 { 4899 ScRangeList aOrigDependencies; 4900 if(!bDependencyComputed && !CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, false, &aOrigDependencies)) 4901 { 4902 bDependencyComputed = true; 4903 bDependencyCheckFailed = true; 4904 return false; 4905 } 4906 4907 bDependencyComputed = true; 4908 4909 // Then do the threaded calculation 4910 4911 class Executor : public comphelper::ThreadTask 4912 { 4913 private: 4914 const unsigned mnThisThread; 4915 const unsigned mnThreadsTotal; 4916 ScDocument* mpDocument; 4917 ScInterpreterContext* mpContext; 4918 const ScAddress& mrTopPos; 4919 SCCOL mnStartCol; 4920 SCCOL mnEndCol; 4921 SCROW mnStartOffset; 4922 SCROW mnEndOffset; 4923 4924 public: 4925 Executor(const std::shared_ptr<comphelper::ThreadTaskTag>& rTag, 4926 unsigned nThisThread, 4927 unsigned nThreadsTotal, 4928 ScDocument* pDocument2, 4929 ScInterpreterContext* pContext, 4930 const ScAddress& rTopPos, 4931 SCCOL nStartCol, 4932 SCCOL nEndCol, 4933 SCROW nStartOff, 4934 SCROW nEndOff) : 4935 comphelper::ThreadTask(rTag), 4936 mnThisThread(nThisThread), 4937 mnThreadsTotal(nThreadsTotal), 4938 mpDocument(pDocument2), 4939 mpContext(pContext), 4940 mrTopPos(rTopPos), 4941 mnStartCol(nStartCol), 4942 mnEndCol(nEndCol), 4943 mnStartOffset(nStartOff), 4944 mnEndOffset(nEndOff) 4945 { 4946 } 4947 4948 virtual void doWork() override 4949 { 4950 ScRange aCalcRange(mnStartCol, mrTopPos.Row() + mnStartOffset, mrTopPos.Tab(), 4951 mnEndCol, mrTopPos.Row() + mnEndOffset, mrTopPos.Tab()); 4952 mpDocument->CalculateInColumnInThread(*mpContext, aCalcRange, mnThisThread, mnThreadsTotal); 4953 } 4954 4955 }; 4956 4957 SvNumberFormatter* pNonThreadedFormatter = rDocument.GetNonThreadedContext().GetFormatTable(); 4958 4959 comphelper::ThreadPool& rThreadPool(comphelper::ThreadPool::getSharedOptimalPool()); 4960 sal_Int32 nThreadCount = rThreadPool.getWorkerCount(); 4961 4962 SAL_INFO("sc.threaded", "Running " << nThreadCount << " threads"); 4963 4964 o3tl::sorted_vector<ScFormulaCellGroup*> aFGSet; 4965 std::map<SCCOL, ScFormulaCell*> aFGMap; 4966 aFGSet.insert(mxGroup.get()); 4967 4968 ScRecursionHelper& rRecursionHelper = rDocument.GetRecursionHelper(); 4969 SCCOL nColStart = aPos.Col(); 4970 SCCOL nColEnd = nColStart; 4971 if (!rRecursionHelper.HasFormulaGroupSet() && rDocument.IsInDocShellRecalc()) 4972 { 4973 nColStart = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, true); 4974 nColEnd = lcl_probeLeftOrRightFGs(mxGroup, rDocument, aFGSet, aFGMap, false); 4975 } 4976 4977 bool bFGOK = true; 4978 ScAddress aDirtiedAddress(ScAddress::INITIALIZE_INVALID); 4979 if (nColStart != nColEnd) 4980 { 4981 ScCheckIndependentFGGuard aGuard(rRecursionHelper, &aFGSet); 4982 for (SCCOL nCurrCol = nColStart; nCurrCol <= nColEnd; ++nCurrCol) 4983 { 4984 if (nCurrCol == aPos.Col()) 4985 continue; 4986 4987 bFGOK = aFGMap[nCurrCol]->CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset, 4988 true, nullptr, &aDirtiedAddress); 4989 if (!bFGOK || !aGuard.AreGroupsIndependent()) 4990 { 4991 nColEnd = nColStart = aPos.Col(); 4992 break; 4993 } 4994 } 4995 } 4996 4997 // tdf#156677 it is possible that if a check of a column in the new range fails that the check has 4998 // now left a cell that the original range depended on in a Dirty state. So if the dirtied cell 4999 // was part of the original dependencies re-run the initial CheckComputeDependencies to fix it. 5000 if (!bFGOK && aDirtiedAddress.IsValid() && aOrigDependencies.Find(aDirtiedAddress)) 5001 { 5002 SAL_WARN("sc.core.formulacell", "rechecking dependencies due to a dirtied cell during speculative probe"); 5003 const bool bRedoEntryCheckSucceeded = CheckComputeDependencies(aScope, false, nStartOffset, nEndOffset); 5004 assert(bRedoEntryCheckSucceeded && "if it worked on the original range it should work again on that range"); 5005 (void)bRedoEntryCheckSucceeded; 5006 } 5007 5008 std::vector<std::unique_ptr<ScInterpreter>> aInterpreters(nThreadCount); 5009 { 5010 assert(!rDocument.IsThreadedGroupCalcInProgress()); 5011 rDocument.SetThreadedGroupCalcInProgress(true); 5012 5013 ScMutationDisable aGuard(rDocument, ScMutationGuardFlags::CORE); 5014 5015 // Here we turn off ref-counting for the contents of pCode on the basis 5016 // that pCode is not modified by interpreting and when interpreting is 5017 // complete all token refcounts will be back to their initial ref count 5018 FormulaToken** pArray = pCode->GetArray(); 5019 for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i) 5020 pArray[i]->SetRefCntPolicy(RefCntPolicy::None); 5021 5022 // Start nThreadCount new threads 5023 std::shared_ptr<comphelper::ThreadTaskTag> aTag = comphelper::ThreadPool::createThreadTaskTag(); 5024 ScThreadedInterpreterContextGetterGuard aContextGetterGuard(nThreadCount, rDocument, pNonThreadedFormatter); 5025 ScInterpreterContext* context = nullptr; 5026 5027 for (int i = 0; i < nThreadCount; ++i) 5028 { 5029 context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i); 5030 assert(!context->pInterpreter); 5031 aInterpreters[i].reset(new ScInterpreter(this, rDocument, *context, mxGroup->mpTopCell->aPos, *pCode, true)); 5032 context->pInterpreter = aInterpreters[i].get(); 5033 rDocument.SetupContextFromNonThreadedContext(*context, i); 5034 rThreadPool.pushTask(std::make_unique<Executor>(aTag, i, nThreadCount, &rDocument, context, mxGroup->mpTopCell->aPos, 5035 nColStart, nColEnd, nStartOffset, nEndOffset)); 5036 } 5037 5038 SAL_INFO("sc.threaded", "Waiting for threads to finish work"); 5039 // Do not join the threads here. They will get joined in ScDocument destructor 5040 // if they don't get joined from elsewhere before (via ThreadPool::waitUntilDone). 5041 rThreadPool.waitUntilDone(aTag, false); 5042 5043 // Drop any caches that reference Tokens before restoring ref counting policy 5044 for (int i = 0; i < nThreadCount; ++i) 5045 aInterpreters[i]->DropTokenCaches(); 5046 5047 for (sal_uInt16 i = 0, n = pCode->GetLen(); i < n; ++i) 5048 pArray[i]->SetRefCntPolicy(RefCntPolicy::ThreadSafe); 5049 5050 rDocument.SetThreadedGroupCalcInProgress(false); 5051 5052 for (int i = 0; i < nThreadCount; ++i) 5053 { 5054 context = aContextGetterGuard.GetInterpreterContextForThreadIdx(i); 5055 // This is intentionally done in this main thread in order to avoid locking. 5056 rDocument.MergeContextBackIntoNonThreadedContext(*context, i); 5057 context->pInterpreter = nullptr; 5058 } 5059 5060 SAL_INFO("sc.threaded", "Done"); 5061 } 5062 5063 ScAddress aStartPos(mxGroup->mpTopCell->aPos); 5064 SCROW nSpanLen = nEndOffset - nStartOffset + 1; 5065 aStartPos.SetRow(aStartPos.Row() + nStartOffset); 5066 // Reuse one of the previously allocated interpreter objects here. 5067 rDocument.HandleStuffAfterParallelCalculation(nColStart, nColEnd, aStartPos.Row(), nSpanLen, 5068 aStartPos.Tab(), aInterpreters[0].get()); 5069 5070 return true; 5071 } 5072 5073 return false; 5074 } 5075 5076 // To be called only from InterpretFormulaGroup(). 5077 bool ScFormulaCell::InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& aScope, 5078 bool& bDependencyComputed, 5079 bool& bDependencyCheckFailed) 5080 { 5081 bool bCanVectorize = pCode->IsEnabledForOpenCL(); 5082 switch (pCode->GetVectorState()) 5083 { 5084 case FormulaVectorEnabled: 5085 case FormulaVectorCheckReference: 5086 break; 5087 5088 // Not good. 5089 case FormulaVectorDisabledByOpCode: 5090 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting opcode)"); 5091 break; 5092 case FormulaVectorDisabledByStackVariable: 5093 aScope.addMessage("group calc disabled due to vector state (non-vector-supporting stack variable)"); 5094 break; 5095 case FormulaVectorDisabledNotInSubSet: 5096 aScope.addMessage("group calc disabled due to vector state (opcode not in subset)"); 5097 break; 5098 case FormulaVectorDisabled: 5099 case FormulaVectorUnknown: 5100 default: 5101 aScope.addMessage("group calc disabled due to vector state (unknown)"); 5102 return false; 5103 } 5104 5105 if (!bCanVectorize) 5106 return false; 5107 5108 if (!ScCalcConfig::isOpenCLEnabled()) 5109 { 5110 aScope.addMessage("opencl not enabled"); 5111 return false; 5112 } 5113 5114 // TableOp does tricks with using a cell with different values, just bail out. 5115 if(rDocument.IsInInterpreterTableOp()) 5116 return false; 5117 5118 if (bDependencyCheckFailed) 5119 return false; 5120 5121 if(!bDependencyComputed && !CheckComputeDependencies(aScope, true, 0, mxGroup->mnLength - 1)) 5122 { 5123 bDependencyComputed = true; 5124 bDependencyCheckFailed = true; 5125 return false; 5126 } 5127 5128 bDependencyComputed = true; 5129 5130 // TODO : Disable invariant formula group interpretation for now in order 5131 // to get implicit intersection to work. 5132 if (mxGroup->mbInvariant && false) 5133 return InterpretInvariantFormulaGroup(); 5134 5135 int nMaxGroupLength = INT_MAX; 5136 5137 #ifdef _WIN32 5138 // Heuristic: Certain old low-end OpenCL implementations don't 5139 // work for us with too large group lengths. 1000 was determined 5140 // empirically to be a good compromise. 5141 if (openclwrapper::gpuEnv.mbNeedsTDRAvoidance) 5142 nMaxGroupLength = 1000; 5143 #endif 5144 5145 if (std::getenv("SC_MAX_GROUP_LENGTH")) 5146 nMaxGroupLength = std::atoi(std::getenv("SC_MAX_GROUP_LENGTH")); 5147 5148 int nNumOnePlus; 5149 const int nNumParts = splitup(GetSharedLength(), nMaxGroupLength, nNumOnePlus); 5150 5151 int nOffset = 0; 5152 int nCurChunkSize; 5153 ScAddress aOrigPos = mxGroup->mpTopCell->aPos; 5154 for (int i = 0; i < nNumParts; i++, nOffset += nCurChunkSize) 5155 { 5156 nCurChunkSize = GetSharedLength()/nNumParts + (i < nNumOnePlus ? 1 : 0); 5157 5158 ScFormulaCellGroupRef xGroup; 5159 5160 if (nNumParts == 1) 5161 xGroup = mxGroup; 5162 else 5163 { 5164 // Ugly hack 5165 xGroup = new ScFormulaCellGroup(); 5166 xGroup->mpTopCell = mxGroup->mpTopCell; 5167 xGroup->mpTopCell->aPos = aOrigPos; 5168 xGroup->mpTopCell->aPos.IncRow(nOffset); 5169 xGroup->mbInvariant = mxGroup->mbInvariant; 5170 xGroup->mnLength = nCurChunkSize; 5171 xGroup->mpCode = std::move(mxGroup->mpCode); // temporarily transfer 5172 } 5173 5174 ScTokenArray aCode(rDocument); 5175 ScGroupTokenConverter aConverter(aCode, rDocument, *this, xGroup->mpTopCell->aPos); 5176 // TODO avoid this extra compilation 5177 ScCompiler aComp( rDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE ); 5178 aComp.CompileTokenArray(); 5179 if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope)) 5180 { 5181 if(aComp.HasUnhandledPossibleImplicitIntersections()) 5182 { 5183 SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling"); 5184 #ifdef DBG_UTIL 5185 for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes()) 5186 { 5187 SAL_INFO("sc.opencl", "unhandled implicit intersection opcode " 5188 << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode) 5189 << "(" << int(opcode) << ")"); 5190 } 5191 #endif 5192 } 5193 else 5194 SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling"); 5195 5196 mxGroup->meCalcState = sc::GroupCalcDisabled; 5197 5198 // Undo the hack above 5199 if (nNumParts > 1) 5200 { 5201 mxGroup->mpTopCell->aPos = aOrigPos; 5202 xGroup->mpTopCell = nullptr; 5203 mxGroup->mpCode = std::move(xGroup->mpCode); 5204 } 5205 5206 aScope.addMessage("group token conversion failed"); 5207 return false; 5208 } 5209 5210 // The converted code does not have RPN tokens yet. The interpreter will 5211 // generate them. 5212 xGroup->meCalcState = mxGroup->meCalcState = sc::GroupCalcRunning; 5213 sc::FormulaGroupInterpreter *pInterpreter = sc::FormulaGroupInterpreter::getStatic(); 5214 5215 if (pInterpreter == nullptr || 5216 !pInterpreter->interpret(rDocument, xGroup->mpTopCell->aPos, xGroup, aCode)) 5217 { 5218 SAL_INFO("sc.opencl", "interpreting group " << mxGroup->mpTopCell->aPos 5219 << " (state " << static_cast<int>(mxGroup->meCalcState) << ") failed, disabling"); 5220 mxGroup->meCalcState = sc::GroupCalcDisabled; 5221 5222 // Undo the hack above 5223 if (nNumParts > 1) 5224 { 5225 mxGroup->mpTopCell->aPos = aOrigPos; 5226 xGroup->mpTopCell = nullptr; 5227 mxGroup->mpCode = std::move(xGroup->mpCode); 5228 } 5229 5230 aScope.addMessage("group interpretation unsuccessful"); 5231 return false; 5232 } 5233 5234 aScope.setCalcComplete(); 5235 5236 if (nNumParts > 1) 5237 { 5238 xGroup->mpTopCell = nullptr; 5239 mxGroup->mpCode = std::move(xGroup->mpCode); 5240 } 5241 } 5242 5243 if (nNumParts > 1) 5244 mxGroup->mpTopCell->aPos = aOrigPos; 5245 mxGroup->meCalcState = sc::GroupCalcEnabled; 5246 return true; 5247 } 5248 5249 bool ScFormulaCell::InterpretInvariantFormulaGroup() 5250 { 5251 if (pCode->GetVectorState() == FormulaVectorCheckReference) 5252 { 5253 // An invariant group should only have absolute row references, and no 5254 // external references are allowed. 5255 5256 ScTokenArray aCode(rDocument); 5257 FormulaTokenArrayPlainIterator aIter(*pCode); 5258 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) 5259 { 5260 switch (p->GetType()) 5261 { 5262 case svSingleRef: 5263 { 5264 ScSingleRefData aRef = *p->GetSingleRef(); 5265 ScAddress aRefPos = aRef.toAbs(rDocument, aPos); 5266 formula::FormulaTokenRef pNewToken = rDocument.ResolveStaticReference(aRefPos); 5267 if (!pNewToken) 5268 return false; 5269 5270 aCode.AddToken(*pNewToken); 5271 } 5272 break; 5273 case svDoubleRef: 5274 { 5275 ScComplexRefData aRef = *p->GetDoubleRef(); 5276 ScRange aRefRange = aRef.toAbs(rDocument, aPos); 5277 formula::FormulaTokenRef pNewToken = rDocument.ResolveStaticReference(aRefRange); 5278 if (!pNewToken) 5279 return false; 5280 5281 aCode.AddToken(*pNewToken); 5282 } 5283 break; 5284 default: 5285 aCode.AddToken(*p); 5286 } 5287 } 5288 5289 ScCompiler aComp(rDocument, aPos, aCode, rDocument.GetGrammar(), true, cMatrixFlag != ScMatrixMode::NONE); 5290 aComp.CompileTokenArray(); // Create RPN token array. 5291 ScInterpreter aInterpreter(this, rDocument, rDocument.GetNonThreadedContext(), aPos, aCode); 5292 aInterpreter.Interpret(); 5293 aResult.SetToken(aInterpreter.GetResultToken().get()); 5294 } 5295 else 5296 { 5297 // Formula contains no references. 5298 ScInterpreter aInterpreter(this, rDocument, rDocument.GetNonThreadedContext(), aPos, *pCode); 5299 aInterpreter.Interpret(); 5300 aResult.SetToken(aInterpreter.GetResultToken().get()); 5301 } 5302 5303 for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ ) 5304 { 5305 ScAddress aTmpPos = aPos; 5306 aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i); 5307 ScFormulaCell* pCell = rDocument.GetFormulaCell(aTmpPos); 5308 if (!pCell) 5309 { 5310 SAL_WARN("sc.core.formulacell", "GetFormulaCell not found"); 5311 continue; 5312 } 5313 5314 // FIXME: this set of horrors is unclear to me ... certainly 5315 // the above GetCell is profoundly nasty & slow ... 5316 // Ensure the cell truly has a result: 5317 pCell->aResult = aResult; 5318 pCell->ResetDirty(); 5319 pCell->SetChanged(true); 5320 } 5321 5322 return true; 5323 } 5324 5325 namespace { 5326 5327 void startListeningArea( 5328 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken) 5329 { 5330 const ScSingleRefData& rRef1 = *rToken.GetSingleRef(); 5331 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2(); 5332 ScAddress aCell1 = rRef1.toAbs(rDoc, rPos); 5333 ScAddress aCell2 = rRef2.toAbs(rDoc, rPos); 5334 if (!(aCell1.IsValid() && aCell2.IsValid())) 5335 return; 5336 5337 if (rToken.GetOpCode() == ocColRowNameAuto) 5338 { // automagically 5339 if ( rRef1.IsColRel() ) 5340 { // ColName 5341 aCell2.SetRow(rDoc.MaxRow()); 5342 } 5343 else 5344 { // RowName 5345 aCell2.SetCol(rDoc.MaxCol()); 5346 } 5347 } 5348 rDoc.StartListeningArea(ScRange(aCell1, aCell2), false, pCell); 5349 } 5350 5351 } 5352 5353 void ScFormulaCell::StartListeningTo( ScDocument& rDoc ) 5354 { 5355 if (mxGroup) 5356 mxGroup->endAllGroupListening(rDoc); 5357 5358 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack()) 5359 return; 5360 5361 rDoc.SetDetectiveDirty(true); // It has changed something 5362 5363 ScTokenArray* pArr = GetCode(); 5364 if( pArr->IsRecalcModeAlways() ) 5365 { 5366 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this); 5367 SetNeedsListening( false); 5368 return; 5369 } 5370 5371 formula::FormulaTokenArrayPlainIterator aIter(*pArr); 5372 formula::FormulaToken* t; 5373 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr ) 5374 { 5375 switch (t->GetType()) 5376 { 5377 case svSingleRef: 5378 { 5379 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos); 5380 if (aCell.IsValid()) 5381 rDoc.StartListeningCell(aCell, this); 5382 } 5383 break; 5384 case svDoubleRef: 5385 startListeningArea(this, rDoc, aPos, *t); 5386 break; 5387 default: 5388 ; // nothing 5389 } 5390 } 5391 SetNeedsListening( false); 5392 } 5393 5394 void ScFormulaCell::StartListeningTo( sc::StartListeningContext& rCxt ) 5395 { 5396 ScDocument& rDoc = rCxt.getDoc(); 5397 5398 if (mxGroup) 5399 mxGroup->endAllGroupListening(rDoc); 5400 5401 if (rDoc.IsClipOrUndo() || rDoc.GetNoListening() || IsInChangeTrack()) 5402 return; 5403 5404 rDoc.SetDetectiveDirty(true); // It has changed something 5405 5406 ScTokenArray* pArr = GetCode(); 5407 if( pArr->IsRecalcModeAlways() ) 5408 { 5409 rDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, this); 5410 SetNeedsListening( false); 5411 return; 5412 } 5413 5414 formula::FormulaTokenArrayPlainIterator aIter(*pArr); 5415 formula::FormulaToken* t; 5416 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr ) 5417 { 5418 switch (t->GetType()) 5419 { 5420 case svSingleRef: 5421 { 5422 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aPos); 5423 if (aCell.IsValid()) 5424 rDoc.StartListeningCell(rCxt, aCell, *this); 5425 } 5426 break; 5427 case svDoubleRef: 5428 startListeningArea(this, rDoc, aPos, *t); 5429 break; 5430 default: 5431 ; // nothing 5432 } 5433 } 5434 SetNeedsListening( false); 5435 } 5436 5437 namespace { 5438 5439 void endListeningArea( 5440 ScFormulaCell* pCell, ScDocument& rDoc, const ScAddress& rPos, const formula::FormulaToken& rToken) 5441 { 5442 const ScSingleRefData& rRef1 = *rToken.GetSingleRef(); 5443 const ScSingleRefData& rRef2 = *rToken.GetSingleRef2(); 5444 ScAddress aCell1 = rRef1.toAbs(rDoc, rPos); 5445 ScAddress aCell2 = rRef2.toAbs(rDoc, rPos); 5446 if (!(aCell1.IsValid() && aCell2.IsValid())) 5447 return; 5448 5449 if (rToken.GetOpCode() == ocColRowNameAuto) 5450 { // automagically 5451 if ( rRef1.IsColRel() ) 5452 { // ColName 5453 aCell2.SetRow(rDoc.MaxRow()); 5454 } 5455 else 5456 { // RowName 5457 aCell2.SetCol(rDoc.MaxCol()); 5458 } 5459 } 5460 5461 rDoc.EndListeningArea(ScRange(aCell1, aCell2), false, pCell); 5462 } 5463 5464 } 5465 5466 void ScFormulaCell::EndListeningTo( ScDocument& rDoc, ScTokenArray* pArr, 5467 ScAddress aCellPos ) 5468 { 5469 if (mxGroup) 5470 mxGroup->endAllGroupListening(rDoc); 5471 5472 if (rDoc.IsClipOrUndo() || IsInChangeTrack()) 5473 return; 5474 5475 if (!HasBroadcaster()) 5476 return; 5477 5478 rDoc.SetDetectiveDirty(true); // It has changed something 5479 5480 if ( GetCode()->IsRecalcModeAlways() ) 5481 { 5482 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this); 5483 return; 5484 } 5485 5486 if (!pArr) 5487 { 5488 pArr = GetCode(); 5489 aCellPos = aPos; 5490 } 5491 formula::FormulaTokenArrayPlainIterator aIter(*pArr); 5492 formula::FormulaToken* t; 5493 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr ) 5494 { 5495 switch (t->GetType()) 5496 { 5497 case svSingleRef: 5498 { 5499 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos); 5500 if (aCell.IsValid()) 5501 rDoc.EndListeningCell(aCell, this); 5502 } 5503 break; 5504 case svDoubleRef: 5505 endListeningArea(this, rDoc, aCellPos, *t); 5506 break; 5507 default: 5508 ; // nothing 5509 } 5510 } 5511 } 5512 5513 void ScFormulaCell::EndListeningTo( sc::EndListeningContext& rCxt ) 5514 { 5515 if (mxGroup) 5516 mxGroup->endAllGroupListening(rCxt.getDoc()); 5517 5518 if (rCxt.getDoc().IsClipOrUndo() || IsInChangeTrack()) 5519 return; 5520 5521 if (!HasBroadcaster()) 5522 return; 5523 5524 ScDocument& rDoc = rCxt.getDoc(); 5525 rDoc.SetDetectiveDirty(true); // It has changed something 5526 5527 ScTokenArray* pArr = rCxt.getOldCode(); 5528 ScAddress aCellPos = rCxt.getOldPosition(aPos); 5529 if (!pArr) 5530 pArr = pCode; 5531 5532 if (pArr->IsRecalcModeAlways()) 5533 { 5534 rDoc.EndListeningArea(BCA_LISTEN_ALWAYS, false, this); 5535 return; 5536 } 5537 5538 formula::FormulaTokenArrayPlainIterator aIter(*pArr); 5539 formula::FormulaToken* t; 5540 while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr ) 5541 { 5542 switch (t->GetType()) 5543 { 5544 case svSingleRef: 5545 { 5546 ScAddress aCell = t->GetSingleRef()->toAbs(rDocument, aCellPos); 5547 if (aCell.IsValid()) 5548 rDoc.EndListeningCell(rCxt, aCell, *this); 5549 } 5550 break; 5551 case svDoubleRef: 5552 endListeningArea(this, rDoc, aCellPos, *t); 5553 break; 5554 default: 5555 ; // nothing 5556 } 5557 } 5558 } 5559 5560 bool ScFormulaCell::IsShared() const 5561 { 5562 return bool(mxGroup); 5563 } 5564 5565 bool ScFormulaCell::IsSharedTop() const 5566 { 5567 if (!mxGroup) 5568 return false; 5569 5570 return mxGroup->mpTopCell == this; 5571 } 5572 5573 SCROW ScFormulaCell::GetSharedTopRow() const 5574 { 5575 return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1; 5576 } 5577 5578 SCROW ScFormulaCell::GetSharedLength() const 5579 { 5580 return mxGroup ? mxGroup->mnLength : 0; 5581 } 5582 5583 sal_Int32 ScFormulaCell::GetWeight() const 5584 { 5585 if (!mxGroup) 5586 return 1; 5587 5588 if (mxGroup->mnWeight > 0) 5589 return mxGroup->mnWeight; 5590 5591 double nSharedCodeWeight = GetSharedCode()->GetWeight(); 5592 double nResult = nSharedCodeWeight * GetSharedLength(); 5593 if (nResult < SAL_MAX_INT32) 5594 mxGroup->mnWeight = nResult; 5595 else 5596 mxGroup->mnWeight = SAL_MAX_INT32; 5597 5598 return mxGroup->mnWeight; 5599 } 5600 5601 ScTokenArray* ScFormulaCell::GetSharedCode() 5602 { 5603 return mxGroup ? &*mxGroup->mpCode : nullptr; 5604 } 5605 5606 const ScTokenArray* ScFormulaCell::GetSharedCode() const 5607 { 5608 return mxGroup ? &*mxGroup->mpCode : nullptr; 5609 } 5610 5611 void ScFormulaCell::SyncSharedCode() 5612 { 5613 if (!mxGroup) 5614 // Not a shared formula cell. 5615 return; 5616 5617 pCode = &*mxGroup->mpCode; 5618 } 5619 5620 #if DUMP_COLUMN_STORAGE 5621 5622 void ScFormulaCell::Dump() const 5623 { 5624 cout << "-- formula cell (" << aPos.Format(ScRefFlags::VALID | ScRefFlags::TAB_3D, &rDocument) << ")" << endl; 5625 cout << " * shared: " << (mxGroup ? "true" : "false") << endl; 5626 if (mxGroup) 5627 { 5628 cout << " * shared length: " << mxGroup->mnLength << endl; 5629 cout << " * shared calc state: " << mxGroup->meCalcState << endl; 5630 } 5631 5632 sc::TokenStringContext aCxt(rDocument, rDocument.GetGrammar()); 5633 cout << " * code: " << pCode->CreateString(aCxt, aPos) << endl; 5634 5635 FormulaError nErrCode = pCode->GetCodeError(); 5636 cout << " * code error: "; 5637 if (nErrCode == FormulaError::NONE) 5638 cout << "(none)"; 5639 else 5640 { 5641 OUString aStr = ScGlobal::GetErrorString(nErrCode); 5642 cout << " * code error: " << aStr << " (" << int(nErrCode) << ")"; 5643 } 5644 cout << endl; 5645 5646 cout << " * result: "; 5647 sc::FormulaResultValue aRV = aResult.GetResult(); 5648 switch (aRV.meType) 5649 { 5650 case sc::FormulaResultValue::Value: 5651 cout << aRV.mfValue << " (value)"; 5652 break; 5653 case sc::FormulaResultValue::String: 5654 cout << aRV.maString.getString() << " (string)"; 5655 break; 5656 case sc::FormulaResultValue::Error: 5657 cout << ScGlobal::GetErrorString(aRV.mnError) << " (error: " << int(aRV.mnError) << ")"; 5658 break; 5659 case sc::FormulaResultValue::Invalid: 5660 cout << "(invalid)"; 5661 break; 5662 default: 5663 ; 5664 } 5665 cout << endl; 5666 } 5667 5668 #endif 5669 5670 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 5671
