1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 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 <interpre.hxx> 21 22 #include <scitems.hxx> 23 #include <editeng/langitem.hxx> 24 #include <editeng/justifyitem.hxx> 25 #include <o3tl/safeint.hxx> 26 #include <o3tl/temporary.hxx> 27 #include <osl/thread.h> 28 #include <unotools/textsearch.hxx> 29 #include <svl/numformat.hxx> 30 #include <svl/zforlist.hxx> 31 #include <svl/zformat.hxx> 32 #include <tools/urlobj.hxx> 33 #include <unotools/charclass.hxx> 34 #include <sfx2/docfile.hxx> 35 #include <sfx2/printer.hxx> 36 #include <unotools/collatorwrapper.hxx> 37 #include <unotools/transliterationwrapper.hxx> 38 #include <rtl/character.hxx> 39 #include <rtl/ustring.hxx> 40 #include <sal/log.hxx> 41 #include <osl/diagnose.h> 42 #include <unicode/uchar.h> 43 #include <unicode/regex.h> 44 #include <i18nlangtag/mslangid.hxx> 45 46 #include <patattr.hxx> 47 #include <global.hxx> 48 #include <document.hxx> 49 #include <dociter.hxx> 50 #include <docsh.hxx> 51 #include <formulacell.hxx> 52 #include <scmatrix.hxx> 53 #include <docoptio.hxx> 54 #include <attrib.hxx> 55 #include <jumpmatrix.hxx> 56 #include <cellkeytranslator.hxx> 57 #include <lookupcache.hxx> 58 #include <rangenam.hxx> 59 #include <rangeutl.hxx> 60 #include <compiler.hxx> 61 #include <externalrefmgr.hxx> 62 #include <doubleref.hxx> 63 #include <queryparam.hxx> 64 #include <queryentry.hxx> 65 #include <queryiter.hxx> 66 #include <tokenarray.hxx> 67 #include <compare.hxx> 68 69 #include <comphelper/processfactory.hxx> 70 #include <comphelper/random.hxx> 71 #include <comphelper/string.hxx> 72 #include <svl/sharedstringpool.hxx> 73 74 #include <stdlib.h> 75 #include <vector> 76 #include <memory> 77 #include <limits> 78 #include <string_view> 79 #include <cmath> 80 81 const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48 82 83 ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr; 84 85 using namespace formula; 86 using ::std::unique_ptr; 87 88 void ScInterpreter::ScIfJump() 89 { 90 const short* pJump = pCur->GetJump(); 91 short nJumpCount = pJump[ 0 ]; 92 MatrixJumpConditionToMatrix(); 93 switch ( GetStackType() ) 94 { 95 case svMatrix: 96 { 97 ScMatrixRef pMat = PopMatrix(); 98 if ( !pMat ) 99 PushIllegalParameter(); 100 else 101 { 102 FormulaConstTokenRef xNew; 103 ScTokenMatrixMap::const_iterator aMapIter; 104 // DoubleError handled by JumpMatrix 105 pMat->SetErrorInterpreter( nullptr); 106 SCSIZE nCols, nRows; 107 pMat->GetDimensions( nCols, nRows ); 108 if ( nCols == 0 || nRows == 0 ) 109 { 110 PushIllegalArgument(); 111 return; 112 } 113 else if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end()) 114 xNew = (*aMapIter).second; 115 else 116 { 117 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( 118 pCur->GetOpCode(), nCols, nRows)); 119 for ( SCSIZE nC=0; nC < nCols; ++nC ) 120 { 121 for ( SCSIZE nR=0; nR < nRows; ++nR ) 122 { 123 double fVal; 124 bool bTrue; 125 bool bIsValue = pMat->IsValue(nC, nR); 126 if (bIsValue) 127 { 128 fVal = pMat->GetDouble(nC, nR); 129 bIsValue = std::isfinite(fVal); 130 bTrue = bIsValue && (fVal != 0.0); 131 if (bTrue) 132 fVal = 1.0; 133 } 134 else 135 { 136 // Treat empty and empty path as 0, but string 137 // as error. ScMatrix::IsValueOrEmpty() returns 138 // true for any empty, empty path, empty cell, 139 // empty result. 140 bIsValue = pMat->IsValueOrEmpty(nC, nR); 141 bTrue = false; 142 fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue)); 143 } 144 if ( bTrue ) 145 { // TRUE 146 if( nJumpCount >= 2 ) 147 { // THEN path 148 pJumpMat->SetJump( nC, nR, fVal, 149 pJump[ 1 ], 150 pJump[ nJumpCount ]); 151 } 152 else 153 { // no parameter given for THEN 154 pJumpMat->SetJump( nC, nR, fVal, 155 pJump[ nJumpCount ], 156 pJump[ nJumpCount ]); 157 } 158 } 159 else 160 { // FALSE 161 if( nJumpCount == 3 && bIsValue ) 162 { // ELSE path 163 pJumpMat->SetJump( nC, nR, fVal, 164 pJump[ 2 ], 165 pJump[ nJumpCount ]); 166 } 167 else 168 { // no parameter given for ELSE, 169 // or DoubleError 170 pJumpMat->SetJump( nC, nR, fVal, 171 pJump[ nJumpCount ], 172 pJump[ nJumpCount ]); 173 } 174 } 175 } 176 } 177 xNew = new ScJumpMatrixToken( pJumpMat ); 178 GetTokenMatrixMap().emplace(pCur, xNew); 179 } 180 if (!xNew) 181 { 182 PushIllegalArgument(); 183 return; 184 } 185 PushTokenRef( xNew); 186 // set endpoint of path for main code line 187 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 188 } 189 } 190 break; 191 default: 192 { 193 const bool bCondition = GetBool(); 194 if (nGlobalError != FormulaError::NONE) 195 { // Propagate error, not THEN- or ELSE-path, jump behind. 196 PushError(nGlobalError); 197 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 198 } 199 else if ( bCondition ) 200 { // TRUE 201 if( nJumpCount >= 2 ) 202 { // THEN path 203 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); 204 } 205 else 206 { // no parameter given for THEN 207 nFuncFmtType = SvNumFormatType::LOGICAL; 208 PushInt(1); 209 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 210 } 211 } 212 else 213 { // FALSE 214 if( nJumpCount == 3 ) 215 { // ELSE path 216 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] ); 217 } 218 else 219 { // no parameter given for ELSE 220 nFuncFmtType = SvNumFormatType::LOGICAL; 221 PushInt(0); 222 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 223 } 224 } 225 } 226 } 227 } 228 229 /** Store a matrix value in another matrix in the context of that other matrix 230 is the result matrix of a jump matrix. All arguments must be valid and are 231 not checked. */ 232 static void lcl_storeJumpMatResult( 233 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR ) 234 { 235 if ( pMat->IsValue( nC, nR ) ) 236 { 237 double fVal = pMat->GetDouble( nC, nR ); 238 pJumpMat->PutResultDouble( fVal, nC, nR ); 239 } 240 else if ( pMat->IsEmpty( nC, nR ) ) 241 { 242 pJumpMat->PutResultEmpty( nC, nR ); 243 } 244 else 245 { 246 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR); 247 } 248 } 249 250 void ScInterpreter::ScIfError( bool bNAonly ) 251 { 252 const short* pJump = pCur->GetJump(); 253 short nJumpCount = pJump[ 0 ]; 254 if (!sp || nJumpCount != 2) 255 { 256 // Reset nGlobalError here to not propagate the old error, if any. 257 nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable); 258 PushError( nGlobalError); 259 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 260 return; 261 } 262 263 FormulaConstTokenRef xToken( pStack[ sp - 1 ] ); 264 bool bError = false; 265 FormulaError nOldGlobalError = nGlobalError; 266 nGlobalError = FormulaError::NONE; 267 268 MatrixJumpConditionToMatrix(); 269 switch (GetStackType()) 270 { 271 default: 272 Pop(); 273 // Act on implicitly propagated error, if any. 274 if (nOldGlobalError != FormulaError::NONE) 275 nGlobalError = nOldGlobalError; 276 if (nGlobalError != FormulaError::NONE) 277 bError = true; 278 break; 279 case svError: 280 PopError(); 281 bError = true; 282 break; 283 case svDoubleRef: 284 case svSingleRef: 285 { 286 ScAddress aAdr; 287 if (!PopDoubleRefOrSingleRef( aAdr)) 288 bError = true; 289 else 290 { 291 292 ScRefCellValue aCell(mrDoc, aAdr); 293 nGlobalError = GetCellErrCode(aCell); 294 if (nGlobalError != FormulaError::NONE) 295 bError = true; 296 } 297 } 298 break; 299 case svExternalSingleRef: 300 case svExternalDoubleRef: 301 { 302 double fVal; 303 svl::SharedString aStr; 304 // Handles also existing jump matrix case and sets error on 305 // elements. 306 GetDoubleOrStringFromMatrix( fVal, aStr); 307 if (nGlobalError != FormulaError::NONE) 308 bError = true; 309 } 310 break; 311 case svMatrix: 312 { 313 const ScMatrixRef pMat = PopMatrix(); 314 if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable))) 315 { 316 bError = true; 317 break; // switch 318 } 319 // If the matrix has no queried error at all we can simply use 320 // it as result and don't need to bother with jump matrix. 321 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(), 322 nErrorRow = ::std::numeric_limits<SCSIZE>::max(); 323 SCSIZE nCols, nRows; 324 pMat->GetDimensions( nCols, nRows ); 325 if (nCols == 0 || nRows == 0) 326 { 327 bError = true; 328 break; // switch 329 } 330 for (SCSIZE nC=0; nC < nCols && !bError; ++nC) 331 { 332 for (SCSIZE nR=0; nR < nRows && !bError; ++nR) 333 { 334 FormulaError nErr = pMat->GetError( nC, nR ); 335 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable)) 336 { 337 bError = true; 338 nErrorCol = nC; 339 nErrorRow = nR; 340 } 341 } 342 } 343 if (!bError) 344 break; // switch, we're done and have the result 345 346 FormulaConstTokenRef xNew; 347 ScTokenMatrixMap::const_iterator aMapIter; 348 if ((aMapIter = maTokenMatrixMap.find( pCur)) != maTokenMatrixMap.end()) 349 { 350 xNew = (*aMapIter).second; 351 } 352 else 353 { 354 const ScMatrix* pMatPtr = pMat.get(); 355 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( 356 pCur->GetOpCode(), nCols, nRows)); 357 // Init all jumps to no error to save single calls. Error 358 // is the exceptional condition. 359 const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult); 360 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] ); 361 // Up to first error position simply store results, no need 362 // to evaluate error conditions again. 363 SCSIZE nC = 0, nR = 0; 364 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ ) 365 { 366 for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR) 367 { 368 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR); 369 } 370 if (nC != nErrorCol && nR != nErrorRow) 371 ++nC; 372 } 373 // Now the mixed cases. 374 for ( ; nC < nCols; ++nC) 375 { 376 for ( ; nR < nRows; ++nR) 377 { 378 FormulaError nErr = pMat->GetError( nC, nR ); 379 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable)) 380 { // TRUE, THEN path 381 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] ); 382 } 383 else 384 { // FALSE, EMPTY path, store result instead 385 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR); 386 } 387 } 388 nR = 0; 389 } 390 xNew = new ScJumpMatrixToken( pJumpMat ); 391 GetTokenMatrixMap().emplace( pCur, xNew ); 392 } 393 nGlobalError = nOldGlobalError; 394 PushTokenRef( xNew ); 395 // set endpoint of path for main code line 396 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 397 return; 398 } 399 break; 400 } 401 402 if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable)) 403 { 404 // error, calculate 2nd argument 405 nGlobalError = FormulaError::NONE; 406 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); 407 } 408 else 409 { 410 // no error, push 1st argument and continue 411 nGlobalError = nOldGlobalError; 412 PushTokenRef( xToken); 413 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 414 } 415 } 416 417 void ScInterpreter::ScChooseJump() 418 { 419 // We have to set a jump, if there was none chosen because of an error set 420 // it to endpoint. 421 bool bHaveJump = false; 422 const short* pJump = pCur->GetJump(); 423 short nJumpCount = pJump[ 0 ]; 424 MatrixJumpConditionToMatrix(); 425 switch ( GetStackType() ) 426 { 427 case svMatrix: 428 { 429 ScMatrixRef pMat = PopMatrix(); 430 if ( !pMat ) 431 PushIllegalParameter(); 432 else 433 { 434 FormulaConstTokenRef xNew; 435 ScTokenMatrixMap::const_iterator aMapIter; 436 // DoubleError handled by JumpMatrix 437 pMat->SetErrorInterpreter( nullptr); 438 SCSIZE nCols, nRows; 439 pMat->GetDimensions( nCols, nRows ); 440 if ( nCols == 0 || nRows == 0 ) 441 PushIllegalParameter(); 442 else if ((aMapIter = maTokenMatrixMap.find( 443 pCur)) != maTokenMatrixMap.end()) 444 xNew = (*aMapIter).second; 445 else 446 { 447 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( 448 pCur->GetOpCode(), nCols, nRows)); 449 for ( SCSIZE nC=0; nC < nCols; ++nC ) 450 { 451 for ( SCSIZE nR=0; nR < nRows; ++nR ) 452 { 453 double fVal; 454 bool bIsValue = pMat->IsValue(nC, nR); 455 if ( bIsValue ) 456 { 457 fVal = pMat->GetDouble(nC, nR); 458 bIsValue = std::isfinite( fVal ); 459 if ( bIsValue ) 460 { 461 fVal = ::rtl::math::approxFloor( fVal); 462 if ( (fVal < 1) || (fVal >= nJumpCount)) 463 { 464 bIsValue = false; 465 fVal = CreateDoubleError( 466 FormulaError::IllegalArgument); 467 } 468 } 469 } 470 else 471 { 472 fVal = CreateDoubleError( FormulaError::NoValue); 473 } 474 if ( bIsValue ) 475 { 476 pJumpMat->SetJump( nC, nR, fVal, 477 pJump[ static_cast<short>(fVal) ], 478 pJump[ nJumpCount ]); 479 } 480 else 481 { 482 pJumpMat->SetJump( nC, nR, fVal, 483 pJump[ nJumpCount ], 484 pJump[ nJumpCount ]); 485 } 486 } 487 } 488 xNew = new ScJumpMatrixToken( pJumpMat ); 489 GetTokenMatrixMap().emplace(pCur, xNew); 490 } 491 if (xNew) 492 { 493 PushTokenRef( xNew); 494 // set endpoint of path for main code line 495 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 496 bHaveJump = true; 497 } 498 } 499 } 500 break; 501 default: 502 { 503 sal_Int16 nJumpIndex = GetInt16(); 504 if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount)) 505 { 506 aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] ); 507 bHaveJump = true; 508 } 509 else 510 PushIllegalArgument(); 511 } 512 } 513 if (!bHaveJump) 514 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); 515 } 516 517 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows ) 518 { 519 SCSIZE nJumpCols, nJumpRows; 520 SCSIZE nResCols, nResRows; 521 SCSIZE nAdjustCols, nAdjustRows; 522 pJumpM->GetDimensions( nJumpCols, nJumpRows ); 523 pJumpM->GetResMatDimensions( nResCols, nResRows ); 524 if (!(( nJumpCols == 1 && nParmCols > nResCols ) || 525 ( nJumpRows == 1 && nParmRows > nResRows ))) 526 return; 527 528 if ( nJumpCols == 1 && nJumpRows == 1 ) 529 { 530 nAdjustCols = std::max(nParmCols, nResCols); 531 nAdjustRows = std::max(nParmRows, nResRows); 532 } 533 else if ( nJumpCols == 1 ) 534 { 535 nAdjustCols = nParmCols; 536 nAdjustRows = nResRows; 537 } 538 else 539 { 540 nAdjustCols = nResCols; 541 nAdjustRows = nParmRows; 542 } 543 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows ); 544 } 545 546 bool ScInterpreter::JumpMatrix( short nStackLevel ) 547 { 548 pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix(); 549 bool bHasResMat = pJumpMatrix->HasResultMatrix(); 550 SCSIZE nC, nR; 551 if ( nStackLevel == 2 ) 552 { 553 if ( aCode.HasStacked() ) 554 aCode.Pop(); // pop what Jump() pushed 555 else 556 { 557 assert(!"pop goes the weasel"); 558 } 559 560 if ( !bHasResMat ) 561 { 562 Pop(); 563 SetError( FormulaError::UnknownStackVariable ); 564 } 565 else 566 { 567 pJumpMatrix->GetPos( nC, nR ); 568 switch ( GetStackType() ) 569 { 570 case svDouble: 571 { 572 double fVal = GetDouble(); 573 if ( nGlobalError != FormulaError::NONE ) 574 { 575 fVal = CreateDoubleError( nGlobalError ); 576 nGlobalError = FormulaError::NONE; 577 } 578 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 579 } 580 break; 581 case svString: 582 { 583 svl::SharedString aStr = GetString(); 584 if ( nGlobalError != FormulaError::NONE ) 585 { 586 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), 587 nC, nR); 588 nGlobalError = FormulaError::NONE; 589 } 590 else 591 pJumpMatrix->PutResultString(aStr, nC, nR); 592 } 593 break; 594 case svSingleRef: 595 { 596 FormulaConstTokenRef xRef = pStack[sp-1]; 597 ScAddress aAdr; 598 PopSingleRef( aAdr ); 599 if ( nGlobalError != FormulaError::NONE ) 600 { 601 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), 602 nC, nR); 603 nGlobalError = FormulaError::NONE; 604 } 605 else 606 { 607 ScRefCellValue aCell(mrDoc, aAdr); 608 if (aCell.hasEmptyValue()) 609 pJumpMatrix->PutResultEmpty( nC, nR ); 610 else if (aCell.hasNumeric()) 611 { 612 double fVal = GetCellValue(aAdr, aCell); 613 if ( nGlobalError != FormulaError::NONE ) 614 { 615 fVal = CreateDoubleError( 616 nGlobalError); 617 nGlobalError = FormulaError::NONE; 618 } 619 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 620 } 621 else 622 { 623 svl::SharedString aStr; 624 GetCellString(aStr, aCell); 625 if ( nGlobalError != FormulaError::NONE ) 626 { 627 pJumpMatrix->PutResultDouble( CreateDoubleError( 628 nGlobalError), nC, nR); 629 nGlobalError = FormulaError::NONE; 630 } 631 else 632 pJumpMatrix->PutResultString(aStr, nC, nR); 633 } 634 } 635 636 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16); 637 if (eReturnType == ParamClass::Reference) 638 { 639 /* TODO: What about error handling and do we actually 640 * need the result matrix above at all in this case? */ 641 ScComplexRefData aRef; 642 aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef()); 643 pJumpMatrix->GetRefList().push_back( aRef); 644 } 645 } 646 break; 647 case svDoubleRef: 648 { // upper left plus offset within matrix 649 FormulaConstTokenRef xRef = pStack[sp-1]; 650 double fVal; 651 ScRange aRange; 652 PopDoubleRef( aRange ); 653 if ( nGlobalError != FormulaError::NONE ) 654 { 655 fVal = CreateDoubleError( nGlobalError ); 656 nGlobalError = FormulaError::NONE; 657 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 658 } 659 else 660 { 661 // Do not modify the original range because we use it 662 // to adjust the size of the result matrix if necessary. 663 ScAddress aAdr( aRange.aStart); 664 sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC; 665 sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR; 666 if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) && 667 aRange.aEnd.Col() != aRange.aStart.Col()) 668 || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) && 669 aRange.aEnd.Row() != aRange.aStart.Row())) 670 { 671 fVal = CreateDoubleError( FormulaError::NotAvailable ); 672 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 673 } 674 else 675 { 676 // Replicate column and/or row of a vector if it is 677 // one. Note that this could be a range reference 678 // that in fact consists of only one cell, e.g. A1:A1 679 if (aRange.aEnd.Col() == aRange.aStart.Col()) 680 nCol = aRange.aStart.Col(); 681 if (aRange.aEnd.Row() == aRange.aStart.Row()) 682 nRow = aRange.aStart.Row(); 683 aAdr.SetCol( static_cast<SCCOL>(nCol) ); 684 aAdr.SetRow( static_cast<SCROW>(nRow) ); 685 ScRefCellValue aCell(mrDoc, aAdr); 686 if (aCell.hasEmptyValue()) 687 pJumpMatrix->PutResultEmpty( nC, nR ); 688 else if (aCell.hasNumeric()) 689 { 690 double fCellVal = GetCellValue(aAdr, aCell); 691 if ( nGlobalError != FormulaError::NONE ) 692 { 693 fCellVal = CreateDoubleError( 694 nGlobalError); 695 nGlobalError = FormulaError::NONE; 696 } 697 pJumpMatrix->PutResultDouble( fCellVal, nC, nR ); 698 } 699 else 700 { 701 svl::SharedString aStr; 702 GetCellString(aStr, aCell); 703 if ( nGlobalError != FormulaError::NONE ) 704 { 705 pJumpMatrix->PutResultDouble( CreateDoubleError( 706 nGlobalError), nC, nR); 707 nGlobalError = FormulaError::NONE; 708 } 709 else 710 pJumpMatrix->PutResultString(aStr, nC, nR); 711 } 712 } 713 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1; 714 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1; 715 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows ); 716 } 717 718 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16); 719 if (eReturnType == ParamClass::Reference) 720 { 721 /* TODO: What about error handling and do we actually 722 * need the result matrix above at all in this case? */ 723 pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef())); 724 } 725 } 726 break; 727 case svExternalSingleRef: 728 { 729 ScExternalRefCache::TokenRef pToken; 730 PopExternalSingleRef(pToken); 731 if (nGlobalError != FormulaError::NONE) 732 { 733 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR ); 734 nGlobalError = FormulaError::NONE; 735 } 736 else 737 { 738 switch (pToken->GetType()) 739 { 740 case svDouble: 741 pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR ); 742 break; 743 case svString: 744 pJumpMatrix->PutResultString( pToken->GetString(), nC, nR ); 745 break; 746 case svEmptyCell: 747 pJumpMatrix->PutResultEmpty( nC, nR ); 748 break; 749 default: 750 // svError was already handled (set by 751 // PopExternalSingleRef()) with nGlobalError 752 // above. 753 assert(!"unhandled svExternalSingleRef case"); 754 pJumpMatrix->PutResultDouble( CreateDoubleError( 755 FormulaError::UnknownStackVariable), nC, nR ); 756 } 757 } 758 } 759 break; 760 case svExternalDoubleRef: 761 case svMatrix: 762 { // match matrix offsets 763 double fVal; 764 ScMatrixRef pMat = GetMatrix(); 765 if ( nGlobalError != FormulaError::NONE ) 766 { 767 fVal = CreateDoubleError( nGlobalError ); 768 nGlobalError = FormulaError::NONE; 769 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 770 } 771 else if ( !pMat ) 772 { 773 fVal = CreateDoubleError( FormulaError::UnknownVariable ); 774 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 775 } 776 else 777 { 778 SCSIZE nCols, nRows; 779 pMat->GetDimensions( nCols, nRows ); 780 if ((nCols <= nC && nCols != 1) || 781 (nRows <= nR && nRows != 1)) 782 { 783 fVal = CreateDoubleError( FormulaError::NotAvailable ); 784 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 785 } 786 else 787 { 788 // GetMatrix() does SetErrorInterpreter() at the 789 // matrix, do not propagate an error from 790 // matrix->GetValue() as global error. 791 pMat->SetErrorInterpreter(nullptr); 792 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR); 793 } 794 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows ); 795 } 796 } 797 break; 798 case svError: 799 { 800 PopError(); 801 double fVal = CreateDoubleError( nGlobalError); 802 nGlobalError = FormulaError::NONE; 803 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 804 } 805 break; 806 default: 807 { 808 Pop(); 809 double fVal = CreateDoubleError( FormulaError::IllegalArgument); 810 pJumpMatrix->PutResultDouble( fVal, nC, nR ); 811 } 812 } 813 } 814 } 815 bool bCont = pJumpMatrix->Next( nC, nR ); 816 if ( bCont ) 817 { 818 double fBool; 819 short nStart, nNext, nStop; 820 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); 821 while ( bCont && nStart == nNext ) 822 { // push all results that have no jump path 823 if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) ) 824 { 825 // a false without path results in an empty path value 826 if ( fBool == 0.0 ) 827 pJumpMatrix->PutResultEmptyPath( nC, nR ); 828 else 829 pJumpMatrix->PutResultDouble( fBool, nC, nR ); 830 } 831 bCont = pJumpMatrix->Next( nC, nR ); 832 if ( bCont ) 833 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); 834 } 835 if ( bCont && nStart != nNext ) 836 { 837 const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters(); 838 for ( auto const & i : rParams ) 839 { 840 // This is not the current state of the interpreter, so 841 // push without error, and elements' errors are coded into 842 // double. 843 PushWithoutError(*i); 844 } 845 aCode.Jump( nStart, nNext, nStop ); 846 } 847 } 848 if ( !bCont ) 849 { // We're done with it, throw away jump matrix, keep result. 850 // For an intermediate result of Reference use the array of references 851 // if there are more than one reference and the current ForceArray 852 // context is ReferenceOrRefArray. 853 // Else (also for a final result of Reference) use the matrix. 854 // Treat the result of a jump command as final and use the matrix (see 855 // tdf#115493 for why). 856 if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray && 857 pJumpMatrix->GetRefList().size() > 1 && 858 ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference && 859 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) && 860 aCode.PeekNextOperator()) 861 { 862 FormulaTokenRef xRef = new ScRefListToken(true); 863 *(xRef->GetRefList()) = pJumpMatrix->GetRefList(); 864 pJumpMatrix = nullptr; 865 Pop(); 866 PushTokenRef( xRef); 867 maTokenMatrixMap.erase( pCur); 868 // There's no result matrix to remember in this case. 869 } 870 else 871 { 872 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix(); 873 pJumpMatrix = nullptr; 874 Pop(); 875 PushMatrix( pResMat ); 876 // Remove jump matrix from map and remember result matrix in case it 877 // could be reused in another path of the same condition. 878 maTokenMatrixMap.erase( pCur); 879 maTokenMatrixMap.emplace(pCur, pStack[sp-1]); 880 } 881 return true; 882 } 883 return false; 884 } 885 886 double ScInterpreter::Compare( ScQueryOp eOp ) 887 { 888 sc::Compare aComp; 889 aComp.meOp = eOp; 890 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase(); 891 for( short i = 1; i >= 0; i-- ) 892 { 893 sc::Compare::Cell& rCell = aComp.maCells[i]; 894 895 switch ( GetRawStackType() ) 896 { 897 case svEmptyCell: 898 Pop(); 899 rCell.mbEmpty = true; 900 break; 901 case svMissing: 902 case svDouble: 903 rCell.mfValue = GetDouble(); 904 rCell.mbValue = true; 905 break; 906 case svString: 907 rCell.maStr = GetString(); 908 rCell.mbValue = false; 909 break; 910 case svDoubleRef : 911 case svSingleRef : 912 { 913 ScAddress aAdr; 914 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 915 break; 916 ScRefCellValue aCell(mrDoc, aAdr); 917 if (aCell.hasEmptyValue()) 918 rCell.mbEmpty = true; 919 else if (aCell.hasString()) 920 { 921 svl::SharedString aStr; 922 GetCellString(aStr, aCell); 923 rCell.maStr = aStr; 924 rCell.mbValue = false; 925 } 926 else 927 { 928 rCell.mfValue = GetCellValue(aAdr, aCell); 929 rCell.mbValue = true; 930 } 931 } 932 break; 933 case svExternalSingleRef: 934 { 935 ScMatrixRef pMat = GetMatrix(); 936 if (!pMat) 937 { 938 SetError( FormulaError::IllegalParameter); 939 break; 940 } 941 942 SCSIZE nC, nR; 943 pMat->GetDimensions(nC, nR); 944 if (!nC || !nR) 945 { 946 SetError( FormulaError::IllegalParameter); 947 break; 948 } 949 if (pMat->IsEmpty(0, 0)) 950 rCell.mbEmpty = true; 951 else if (pMat->IsStringOrEmpty(0, 0)) 952 { 953 rCell.maStr = pMat->GetString(0, 0); 954 rCell.mbValue = false; 955 } 956 else 957 { 958 rCell.mfValue = pMat->GetDouble(0, 0); 959 rCell.mbValue = true; 960 } 961 } 962 break; 963 case svExternalDoubleRef: 964 // TODO: Find out how to handle this... 965 // Xcl generates a position dependent intersection using 966 // col/row, as it seems to do for all range references, not 967 // only in compare context. We'd need a general implementation 968 // for that behavior similar to svDoubleRef in scalar and array 969 // mode. Which also means we'd have to change all places where 970 // it currently is handled along with svMatrix. 971 default: 972 PopError(); 973 SetError( FormulaError::IllegalParameter); 974 break; 975 } 976 } 977 if( nGlobalError != FormulaError::NONE ) 978 return 0; 979 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; 980 return sc::CompareFunc(aComp); 981 } 982 983 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions ) 984 { 985 sc::Compare aComp; 986 aComp.meOp = eOp; 987 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase(); 988 sc::RangeMatrix aMat[2]; 989 ScAddress aAdr; 990 for( short i = 1; i >= 0; i-- ) 991 { 992 sc::Compare::Cell& rCell = aComp.maCells[i]; 993 994 switch (GetRawStackType()) 995 { 996 case svEmptyCell: 997 Pop(); 998 rCell.mbEmpty = true; 999 break; 1000 case svMissing: 1001 case svDouble: 1002 rCell.mfValue = GetDouble(); 1003 rCell.mbValue = true; 1004 break; 1005 case svString: 1006 rCell.maStr = GetString(); 1007 rCell.mbValue = false; 1008 break; 1009 case svSingleRef: 1010 { 1011 PopSingleRef( aAdr ); 1012 ScRefCellValue aCell(mrDoc, aAdr); 1013 if (aCell.hasEmptyValue()) 1014 rCell.mbEmpty = true; 1015 else if (aCell.hasString()) 1016 { 1017 svl::SharedString aStr; 1018 GetCellString(aStr, aCell); 1019 rCell.maStr = aStr; 1020 rCell.mbValue = false; 1021 } 1022 else 1023 { 1024 rCell.mfValue = GetCellValue(aAdr, aCell); 1025 rCell.mbValue = true; 1026 } 1027 } 1028 break; 1029 case svExternalSingleRef: 1030 case svExternalDoubleRef: 1031 case svDoubleRef: 1032 case svMatrix: 1033 aMat[i] = GetRangeMatrix(); 1034 if (!aMat[i].mpMat) 1035 SetError( FormulaError::IllegalParameter); 1036 else 1037 aMat[i].mpMat->SetErrorInterpreter(nullptr); 1038 // errors are transported as DoubleError inside matrix 1039 break; 1040 default: 1041 PopError(); 1042 SetError( FormulaError::IllegalParameter); 1043 break; 1044 } 1045 } 1046 1047 sc::RangeMatrix aRes; 1048 1049 if (nGlobalError != FormulaError::NONE) 1050 { 1051 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; 1052 return aRes; 1053 } 1054 1055 if (aMat[0].mpMat && aMat[1].mpMat) 1056 { 1057 SCSIZE nC0, nC1; 1058 SCSIZE nR0, nR1; 1059 aMat[0].mpMat->GetDimensions(nC0, nR0); 1060 aMat[1].mpMat->GetDimensions(nC1, nR1); 1061 SCSIZE nC = std::max( nC0, nC1 ); 1062 SCSIZE nR = std::max( nR0, nR1 ); 1063 aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true ); 1064 if (!aRes.mpMat) 1065 return aRes; 1066 for ( SCSIZE j=0; j<nC; j++ ) 1067 { 1068 for ( SCSIZE k=0; k<nR; k++ ) 1069 { 1070 SCSIZE nCol = j, nRow = k; 1071 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) && 1072 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow)) 1073 { 1074 for ( short i=1; i>=0; i-- ) 1075 { 1076 sc::Compare::Cell& rCell = aComp.maCells[i]; 1077 1078 if (aMat[i].mpMat->IsStringOrEmpty(j, k)) 1079 { 1080 rCell.mbValue = false; 1081 rCell.maStr = aMat[i].mpMat->GetString(j, k); 1082 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k); 1083 } 1084 else 1085 { 1086 rCell.mbValue = true; 1087 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k); 1088 rCell.mbEmpty = false; 1089 } 1090 } 1091 aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k); 1092 } 1093 else 1094 aRes.mpMat->PutError( FormulaError::NoValue, j, k); 1095 } 1096 } 1097 1098 switch (eOp) 1099 { 1100 case SC_EQUAL: 1101 aRes.mpMat->CompareEqual(); 1102 break; 1103 case SC_LESS: 1104 aRes.mpMat->CompareLess(); 1105 break; 1106 case SC_GREATER: 1107 aRes.mpMat->CompareGreater(); 1108 break; 1109 case SC_LESS_EQUAL: 1110 aRes.mpMat->CompareLessEqual(); 1111 break; 1112 case SC_GREATER_EQUAL: 1113 aRes.mpMat->CompareGreaterEqual(); 1114 break; 1115 case SC_NOT_EQUAL: 1116 aRes.mpMat->CompareNotEqual(); 1117 break; 1118 default: 1119 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp)); 1120 aRes.mpMat.reset(); 1121 return aRes; 1122 } 1123 } 1124 else if (aMat[0].mpMat || aMat[1].mpMat) 1125 { 1126 size_t i = ( aMat[0].mpMat ? 0 : 1); 1127 1128 aRes.mnCol1 = aMat[i].mnCol1; 1129 aRes.mnRow1 = aMat[i].mnRow1; 1130 aRes.mnTab1 = aMat[i].mnTab1; 1131 aRes.mnCol2 = aMat[i].mnCol2; 1132 aRes.mnRow2 = aMat[i].mnRow2; 1133 aRes.mnTab2 = aMat[i].mnTab2; 1134 1135 ScMatrix& rMat = *aMat[i].mpMat; 1136 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions); 1137 if (!aRes.mpMat) 1138 return aRes; 1139 } 1140 1141 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; 1142 return aRes; 1143 } 1144 1145 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions ) 1146 { 1147 SvNumFormatType nSaveCurFmtType = nCurFmtType; 1148 SvNumFormatType nSaveFuncFmtType = nFuncFmtType; 1149 PushMatrix( pMat); 1150 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem(); 1151 if (rItem.meType == ScQueryEntry::ByString) 1152 PushString(rItem.maString.getString()); 1153 else 1154 PushDouble(rItem.mfVal); 1155 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat; 1156 nCurFmtType = nSaveCurFmtType; 1157 nFuncFmtType = nSaveFuncFmtType; 1158 if (nGlobalError != FormulaError::NONE || !pResultMatrix) 1159 { 1160 SetError( FormulaError::IllegalParameter); 1161 return pResultMatrix; 1162 } 1163 1164 return pResultMatrix; 1165 } 1166 1167 void ScInterpreter::ScEqual() 1168 { 1169 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1170 { 1171 sc::RangeMatrix aMat = CompareMat(SC_EQUAL); 1172 if (!aMat.mpMat) 1173 { 1174 PushIllegalParameter(); 1175 return; 1176 } 1177 1178 PushMatrix(aMat); 1179 } 1180 else 1181 PushInt( int(Compare( SC_EQUAL) == 0) ); 1182 } 1183 1184 void ScInterpreter::ScNotEqual() 1185 { 1186 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1187 { 1188 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL); 1189 if (!aMat.mpMat) 1190 { 1191 PushIllegalParameter(); 1192 return; 1193 } 1194 1195 PushMatrix(aMat); 1196 } 1197 else 1198 PushInt( int(Compare( SC_NOT_EQUAL) != 0) ); 1199 } 1200 1201 void ScInterpreter::ScLess() 1202 { 1203 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1204 { 1205 sc::RangeMatrix aMat = CompareMat(SC_LESS); 1206 if (!aMat.mpMat) 1207 { 1208 PushIllegalParameter(); 1209 return; 1210 } 1211 1212 PushMatrix(aMat); 1213 } 1214 else 1215 PushInt( int(Compare( SC_LESS) < 0) ); 1216 } 1217 1218 void ScInterpreter::ScGreater() 1219 { 1220 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1221 { 1222 sc::RangeMatrix aMat = CompareMat(SC_GREATER); 1223 if (!aMat.mpMat) 1224 { 1225 PushIllegalParameter(); 1226 return; 1227 } 1228 1229 PushMatrix(aMat); 1230 } 1231 else 1232 PushInt( int(Compare( SC_GREATER) > 0) ); 1233 } 1234 1235 void ScInterpreter::ScLessEqual() 1236 { 1237 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1238 { 1239 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL); 1240 if (!aMat.mpMat) 1241 { 1242 PushIllegalParameter(); 1243 return; 1244 } 1245 1246 PushMatrix(aMat); 1247 } 1248 else 1249 PushInt( int(Compare( SC_LESS_EQUAL) <= 0) ); 1250 } 1251 1252 void ScInterpreter::ScGreaterEqual() 1253 { 1254 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) 1255 { 1256 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL); 1257 if (!aMat.mpMat) 1258 { 1259 PushIllegalParameter(); 1260 return; 1261 } 1262 1263 PushMatrix(aMat); 1264 } 1265 else 1266 PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) ); 1267 } 1268 1269 void ScInterpreter::ScAnd() 1270 { 1271 nFuncFmtType = SvNumFormatType::LOGICAL; 1272 short nParamCount = GetByte(); 1273 if ( !MustHaveParamCountMin( nParamCount, 1 ) ) 1274 return; 1275 1276 bool bHaveValue = false; 1277 bool bRes = true; 1278 size_t nRefInList = 0; 1279 while( nParamCount-- > 0) 1280 { 1281 if ( nGlobalError == FormulaError::NONE ) 1282 { 1283 switch ( GetStackType() ) 1284 { 1285 case svDouble : 1286 bHaveValue = true; 1287 bRes &= ( PopDouble() != 0.0 ); 1288 break; 1289 case svString : 1290 Pop(); 1291 SetError( FormulaError::NoValue ); 1292 break; 1293 case svSingleRef : 1294 { 1295 ScAddress aAdr; 1296 PopSingleRef( aAdr ); 1297 if ( nGlobalError == FormulaError::NONE ) 1298 { 1299 ScRefCellValue aCell(mrDoc, aAdr); 1300 if (aCell.hasNumeric()) 1301 { 1302 bHaveValue = true; 1303 bRes &= ( GetCellValue(aAdr, aCell) != 0.0 ); 1304 } 1305 // else: Xcl raises no error here 1306 } 1307 } 1308 break; 1309 case svDoubleRef: 1310 case svRefList: 1311 { 1312 ScRange aRange; 1313 PopDoubleRef( aRange, nParamCount, nRefInList); 1314 if ( nGlobalError == FormulaError::NONE ) 1315 { 1316 double fVal; 1317 FormulaError nErr = FormulaError::NONE; 1318 ScValueIterator aValIter( mrContext, aRange ); 1319 if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE ) 1320 { 1321 bHaveValue = true; 1322 do 1323 { 1324 bRes &= ( fVal != 0.0 ); 1325 } while ( (nErr == FormulaError::NONE) && 1326 aValIter.GetNext( fVal, nErr ) ); 1327 } 1328 SetError( nErr ); 1329 } 1330 } 1331 break; 1332 case svExternalSingleRef: 1333 case svExternalDoubleRef: 1334 case svMatrix: 1335 { 1336 ScMatrixRef pMat = GetMatrix(); 1337 if ( pMat ) 1338 { 1339 bHaveValue = true; 1340 double fVal = pMat->And(); 1341 FormulaError nErr = GetDoubleErrorValue( fVal ); 1342 if ( nErr != FormulaError::NONE ) 1343 { 1344 SetError( nErr ); 1345 bRes = false; 1346 } 1347 else 1348 bRes &= (fVal != 0.0); 1349 } 1350 // else: GetMatrix did set FormulaError::IllegalParameter 1351 } 1352 break; 1353 default: 1354 PopError(); 1355 SetError( FormulaError::IllegalParameter); 1356 } 1357 } 1358 else 1359 Pop(); 1360 } 1361 if ( bHaveValue ) 1362 PushInt( int(bRes) ); 1363 else 1364 PushNoValue(); 1365 } 1366 1367 void ScInterpreter::ScOr() 1368 { 1369 nFuncFmtType = SvNumFormatType::LOGICAL; 1370 short nParamCount = GetByte(); 1371 if ( !MustHaveParamCountMin( nParamCount, 1 ) ) 1372 return; 1373 1374 bool bHaveValue = false; 1375 bool bRes = false; 1376 size_t nRefInList = 0; 1377 while( nParamCount-- > 0) 1378 { 1379 if ( nGlobalError == FormulaError::NONE ) 1380 { 1381 switch ( GetStackType() ) 1382 { 1383 case svDouble : 1384 bHaveValue = true; 1385 bRes |= ( PopDouble() != 0.0 ); 1386 break; 1387 case svString : 1388 Pop(); 1389 SetError( FormulaError::NoValue ); 1390 break; 1391 case svSingleRef : 1392 { 1393 ScAddress aAdr; 1394 PopSingleRef( aAdr ); 1395 if ( nGlobalError == FormulaError::NONE ) 1396 { 1397 ScRefCellValue aCell(mrDoc, aAdr); 1398 if (aCell.hasNumeric()) 1399 { 1400 bHaveValue = true; 1401 bRes |= ( GetCellValue(aAdr, aCell) != 0.0 ); 1402 } 1403 // else: Xcl raises no error here 1404 } 1405 } 1406 break; 1407 case svDoubleRef: 1408 case svRefList: 1409 { 1410 ScRange aRange; 1411 PopDoubleRef( aRange, nParamCount, nRefInList); 1412 if ( nGlobalError == FormulaError::NONE ) 1413 { 1414 double fVal; 1415 FormulaError nErr = FormulaError::NONE; 1416 ScValueIterator aValIter( mrContext, aRange ); 1417 if ( aValIter.GetFirst( fVal, nErr ) ) 1418 { 1419 bHaveValue = true; 1420 do 1421 { 1422 bRes |= ( fVal != 0.0 ); 1423 } while ( (nErr == FormulaError::NONE) && 1424 aValIter.GetNext( fVal, nErr ) ); 1425 } 1426 SetError( nErr ); 1427 } 1428 } 1429 break; 1430 case svExternalSingleRef: 1431 case svExternalDoubleRef: 1432 case svMatrix: 1433 { 1434 bHaveValue = true; 1435 ScMatrixRef pMat = GetMatrix(); 1436 if ( pMat ) 1437 { 1438 bHaveValue = true; 1439 double fVal = pMat->Or(); 1440 FormulaError nErr = GetDoubleErrorValue( fVal ); 1441 if ( nErr != FormulaError::NONE ) 1442 { 1443 SetError( nErr ); 1444 bRes = false; 1445 } 1446 else 1447 bRes |= (fVal != 0.0); 1448 } 1449 // else: GetMatrix did set FormulaError::IllegalParameter 1450 } 1451 break; 1452 default: 1453 PopError(); 1454 SetError( FormulaError::IllegalParameter); 1455 } 1456 } 1457 else 1458 Pop(); 1459 } 1460 if ( bHaveValue ) 1461 PushInt( int(bRes) ); 1462 else 1463 PushNoValue(); 1464 } 1465 1466 void ScInterpreter::ScXor() 1467 { 1468 1469 nFuncFmtType = SvNumFormatType::LOGICAL; 1470 short nParamCount = GetByte(); 1471 if ( !MustHaveParamCountMin( nParamCount, 1 ) ) 1472 return; 1473 1474 bool bHaveValue = false; 1475 bool bRes = false; 1476 size_t nRefInList = 0; 1477 while( nParamCount-- > 0) 1478 { 1479 if ( nGlobalError == FormulaError::NONE ) 1480 { 1481 switch ( GetStackType() ) 1482 { 1483 case svDouble : 1484 bHaveValue = true; 1485 bRes ^= ( PopDouble() != 0.0 ); 1486 break; 1487 case svString : 1488 Pop(); 1489 SetError( FormulaError::NoValue ); 1490 break; 1491 case svSingleRef : 1492 { 1493 ScAddress aAdr; 1494 PopSingleRef( aAdr ); 1495 if ( nGlobalError == FormulaError::NONE ) 1496 { 1497 ScRefCellValue aCell(mrDoc, aAdr); 1498 if (aCell.hasNumeric()) 1499 { 1500 bHaveValue = true; 1501 bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 ); 1502 } 1503 /* TODO: set error? Excel doesn't have XOR, but 1504 * doesn't set an error in this case for AND and 1505 * OR. */ 1506 } 1507 } 1508 break; 1509 case svDoubleRef: 1510 case svRefList: 1511 { 1512 ScRange aRange; 1513 PopDoubleRef( aRange, nParamCount, nRefInList); 1514 if ( nGlobalError == FormulaError::NONE ) 1515 { 1516 double fVal; 1517 FormulaError nErr = FormulaError::NONE; 1518 ScValueIterator aValIter( mrContext, aRange ); 1519 if ( aValIter.GetFirst( fVal, nErr ) ) 1520 { 1521 bHaveValue = true; 1522 do 1523 { 1524 bRes ^= ( fVal != 0.0 ); 1525 } while ( (nErr == FormulaError::NONE) && 1526 aValIter.GetNext( fVal, nErr ) ); 1527 } 1528 SetError( nErr ); 1529 } 1530 } 1531 break; 1532 case svExternalSingleRef: 1533 case svExternalDoubleRef: 1534 case svMatrix: 1535 { 1536 bHaveValue = true; 1537 ScMatrixRef pMat = GetMatrix(); 1538 if ( pMat ) 1539 { 1540 bHaveValue = true; 1541 double fVal = pMat->Xor(); 1542 FormulaError nErr = GetDoubleErrorValue( fVal ); 1543 if ( nErr != FormulaError::NONE ) 1544 { 1545 SetError( nErr ); 1546 bRes = false; 1547 } 1548 else 1549 bRes ^= ( fVal != 0.0 ); 1550 } 1551 // else: GetMatrix did set FormulaError::IllegalParameter 1552 } 1553 break; 1554 default: 1555 PopError(); 1556 SetError( FormulaError::IllegalParameter); 1557 } 1558 } 1559 else 1560 Pop(); 1561 } 1562 if ( bHaveValue ) 1563 PushInt( int(bRes) ); 1564 else 1565 PushNoValue(); 1566 } 1567 1568 void ScInterpreter::ScNeg() 1569 { 1570 // Simple negation doesn't change current format type to number, keep 1571 // current type. 1572 nFuncFmtType = nCurFmtType; 1573 switch ( GetStackType() ) 1574 { 1575 case svMatrix : 1576 { 1577 ScMatrixRef pMat = GetMatrix(); 1578 if ( !pMat ) 1579 PushIllegalParameter(); 1580 else 1581 { 1582 SCSIZE nC, nR; 1583 pMat->GetDimensions( nC, nR ); 1584 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true ); 1585 if ( !pResMat ) 1586 PushIllegalArgument(); 1587 else 1588 { 1589 pMat->NegOp( *pResMat); 1590 PushMatrix( pResMat ); 1591 } 1592 } 1593 } 1594 break; 1595 default: 1596 PushDouble( -GetDouble() ); 1597 } 1598 } 1599 1600 void ScInterpreter::ScPercentSign() 1601 { 1602 nFuncFmtType = SvNumFormatType::PERCENT; 1603 const FormulaToken* pSaveCur = pCur; 1604 sal_uInt8 nSavePar = cPar; 1605 PushInt( 100 ); 1606 cPar = 2; 1607 FormulaByteToken aDivOp( ocDiv, cPar ); 1608 pCur = &aDivOp; 1609 ScDiv(); 1610 pCur = pSaveCur; 1611 cPar = nSavePar; 1612 } 1613 1614 void ScInterpreter::ScNot() 1615 { 1616 nFuncFmtType = SvNumFormatType::LOGICAL; 1617 switch ( GetStackType() ) 1618 { 1619 case svMatrix : 1620 { 1621 ScMatrixRef pMat = GetMatrix(); 1622 if ( !pMat ) 1623 PushIllegalParameter(); 1624 else 1625 { 1626 SCSIZE nC, nR; 1627 pMat->GetDimensions( nC, nR ); 1628 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true); 1629 if ( !pResMat ) 1630 PushIllegalArgument(); 1631 else 1632 { 1633 pMat->NotOp( *pResMat); 1634 PushMatrix( pResMat ); 1635 } 1636 } 1637 } 1638 break; 1639 default: 1640 PushInt( int(GetDouble() == 0.0) ); 1641 } 1642 } 1643 1644 void ScInterpreter::ScBitAnd() 1645 { 1646 1647 if ( !MustHaveParamCount( GetByte(), 2 ) ) 1648 return; 1649 1650 double num1 = ::rtl::math::approxFloor( GetDouble()); 1651 double num2 = ::rtl::math::approxFloor( GetDouble()); 1652 if ( (num1 >= n2power48) || (num1 < 0) || 1653 (num2 >= n2power48) || (num2 < 0)) 1654 PushIllegalArgument(); 1655 else 1656 PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2)); 1657 } 1658 1659 void ScInterpreter::ScBitOr() 1660 { 1661 1662 if ( !MustHaveParamCount( GetByte(), 2 ) ) 1663 return; 1664 1665 double num1 = ::rtl::math::approxFloor( GetDouble()); 1666 double num2 = ::rtl::math::approxFloor( GetDouble()); 1667 if ( (num1 >= n2power48) || (num1 < 0) || 1668 (num2 >= n2power48) || (num2 < 0)) 1669 PushIllegalArgument(); 1670 else 1671 PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2)); 1672 } 1673 1674 void ScInterpreter::ScBitXor() 1675 { 1676 1677 if ( !MustHaveParamCount( GetByte(), 2 ) ) 1678 return; 1679 1680 double num1 = ::rtl::math::approxFloor( GetDouble()); 1681 double num2 = ::rtl::math::approxFloor( GetDouble()); 1682 if ( (num1 >= n2power48) || (num1 < 0) || 1683 (num2 >= n2power48) || (num2 < 0)) 1684 PushIllegalArgument(); 1685 else 1686 PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2)); 1687 } 1688 1689 void ScInterpreter::ScBitLshift() 1690 { 1691 1692 if ( !MustHaveParamCount( GetByte(), 2 ) ) 1693 return; 1694 1695 double fShift = ::rtl::math::approxFloor( GetDouble()); 1696 double num = ::rtl::math::approxFloor( GetDouble()); 1697 if ((num >= n2power48) || (num < 0)) 1698 PushIllegalArgument(); 1699 else 1700 { 1701 double fRes; 1702 if (fShift < 0) 1703 fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift)); 1704 else if (fShift == 0) 1705 fRes = num; 1706 else 1707 fRes = num * pow( 2.0, fShift); 1708 PushDouble( fRes); 1709 } 1710 } 1711 1712 void ScInterpreter::ScBitRshift() 1713 { 1714 1715 if ( !MustHaveParamCount( GetByte(), 2 ) ) 1716 return; 1717 1718 double fShift = ::rtl::math::approxFloor( GetDouble()); 1719 double num = ::rtl::math::approxFloor( GetDouble()); 1720 if ((num >= n2power48) || (num < 0)) 1721 PushIllegalArgument(); 1722 else 1723 { 1724 double fRes; 1725 if (fShift < 0) 1726 fRes = num * pow( 2.0, -fShift); 1727 else if (fShift == 0) 1728 fRes = num; 1729 else 1730 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift)); 1731 PushDouble( fRes); 1732 } 1733 } 1734 1735 void ScInterpreter::ScPi() 1736 { 1737 PushDouble(M_PI); 1738 } 1739 1740 void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc, 1741 double fFirst, double fLast ) 1742 { 1743 if (bMatrixFormula) 1744 { 1745 SCCOL nCols = 0; 1746 SCROW nRows = 0; 1747 // In JumpMatrix context use its dimensions for the return matrix; the 1748 // formula cell range selected may differ, for example if the result is 1749 // to be transposed. 1750 if (GetStackType(1) == svJumpMatrix) 1751 { 1752 SCSIZE nC, nR; 1753 pStack[sp-1]->GetJumpMatrix()->GetDimensions( nC, nR); 1754 nCols = std::max<SCCOL>(0, static_cast<SCCOL>(nC)); 1755 nRows = std::max<SCROW>(0, static_cast<SCROW>(nR)); 1756 } 1757 else if (pMyFormulaCell) 1758 pMyFormulaCell->GetMatColsRows( nCols, nRows); 1759 1760 if (nCols == 1 && nRows == 1) 1761 { 1762 // For compatibility with existing 1763 // com.sun.star.sheet.FunctionAccess.callFunction() calls that per 1764 // default are executed in array context unless 1765 // FA.setPropertyValue("IsArrayFunction",False) was set, return a 1766 // scalar double instead of a 1x1 matrix object. tdf#128218 1767 PushDouble( RandomFunc( fFirst, fLast)); 1768 return; 1769 } 1770 1771 // ScViewFunc::EnterMatrix() might be asking for 1772 // ScFormulaCell::GetResultDimensions(), which here are none so create 1773 // a 1x1 matrix at least which exactly is the case when EnterMatrix() 1774 // asks for a not selected range. 1775 if (nCols == 0) 1776 nCols = 1; 1777 if (nRows == 0) 1778 nRows = 1; 1779 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true ); 1780 if (!pResMat) 1781 PushError( FormulaError::MatrixSize); 1782 else 1783 { 1784 for (SCCOL i=0; i < nCols; ++i) 1785 { 1786 for (SCROW j=0; j < nRows; ++j) 1787 { 1788 pResMat->PutDouble( RandomFunc( fFirst, fLast), 1789 static_cast<SCSIZE>(i), static_cast<SCSIZE>(j)); 1790 } 1791 } 1792 PushMatrix( pResMat); 1793 } 1794 } 1795 else 1796 { 1797 PushDouble( RandomFunc( fFirst, fLast)); 1798 } 1799 } 1800 1801 void ScInterpreter::ScRandom() 1802 { 1803 auto RandomFunc = []( double, double ) 1804 { 1805 return comphelper::rng::uniform_real_distribution(); 1806 }; 1807 ScRandomImpl( RandomFunc, 0.0, 0.0); 1808 } 1809 1810 void ScInterpreter::ScRandbetween() 1811 { 1812 if (!MustHaveParamCount( GetByte(), 2)) 1813 return; 1814 1815 // Same like scaddins/source/analysis/analysis.cxx 1816 // AnalysisAddIn::getRandbetween() 1817 double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up); 1818 double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up); 1819 if (nGlobalError != FormulaError::NONE || fMin > fMax) 1820 { 1821 PushIllegalArgument(); 1822 return; 1823 } 1824 fMax = std::nextafter( fMax+1, -DBL_MAX); 1825 auto RandomFunc = []( double fFirst, double fLast ) 1826 { 1827 return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast)); 1828 }; 1829 ScRandomImpl( RandomFunc, fMin, fMax); 1830 } 1831 1832 void ScInterpreter::ScTrue() 1833 { 1834 nFuncFmtType = SvNumFormatType::LOGICAL; 1835 PushInt(1); 1836 } 1837 1838 void ScInterpreter::ScFalse() 1839 { 1840 nFuncFmtType = SvNumFormatType::LOGICAL; 1841 PushInt(0); 1842 } 1843 1844 void ScInterpreter::ScDeg() 1845 { 1846 PushDouble(basegfx::rad2deg(GetDouble())); 1847 } 1848 1849 void ScInterpreter::ScRad() 1850 { 1851 PushDouble(basegfx::deg2rad(GetDouble())); 1852 } 1853 1854 void ScInterpreter::ScSin() 1855 { 1856 PushDouble(::rtl::math::sin(GetDouble())); 1857 } 1858 1859 void ScInterpreter::ScCos() 1860 { 1861 PushDouble(::rtl::math::cos(GetDouble())); 1862 } 1863 1864 void ScInterpreter::ScTan() 1865 { 1866 PushDouble(::rtl::math::tan(GetDouble())); 1867 } 1868 1869 void ScInterpreter::ScCot() 1870 { 1871 PushDouble(1.0 / ::rtl::math::tan(GetDouble())); 1872 } 1873 1874 void ScInterpreter::ScArcSin() 1875 { 1876 PushDouble(asin(GetDouble())); 1877 } 1878 1879 void ScInterpreter::ScArcCos() 1880 { 1881 PushDouble(acos(GetDouble())); 1882 } 1883 1884 void ScInterpreter::ScArcTan() 1885 { 1886 PushDouble(atan(GetDouble())); 1887 } 1888 1889 void ScInterpreter::ScArcCot() 1890 { 1891 PushDouble((M_PI_2) - atan(GetDouble())); 1892 } 1893 1894 void ScInterpreter::ScSinHyp() 1895 { 1896 PushDouble(sinh(GetDouble())); 1897 } 1898 1899 void ScInterpreter::ScCosHyp() 1900 { 1901 PushDouble(cosh(GetDouble())); 1902 } 1903 1904 void ScInterpreter::ScTanHyp() 1905 { 1906 PushDouble(tanh(GetDouble())); 1907 } 1908 1909 void ScInterpreter::ScCotHyp() 1910 { 1911 PushDouble(1.0 / tanh(GetDouble())); 1912 } 1913 1914 void ScInterpreter::ScArcSinHyp() 1915 { 1916 PushDouble( ::rtl::math::asinh( GetDouble())); 1917 } 1918 1919 void ScInterpreter::ScArcCosHyp() 1920 { 1921 double fVal = GetDouble(); 1922 if (fVal < 1.0) 1923 PushIllegalArgument(); 1924 else 1925 PushDouble( ::rtl::math::acosh( fVal)); 1926 } 1927 1928 void ScInterpreter::ScArcTanHyp() 1929 { 1930 double fVal = GetDouble(); 1931 if (fabs(fVal) >= 1.0) 1932 PushIllegalArgument(); 1933 else 1934 PushDouble(::atanh(fVal)); 1935 } 1936 1937 void ScInterpreter::ScArcCotHyp() 1938 { 1939 double nVal = GetDouble(); 1940 if (fabs(nVal) <= 1.0) 1941 PushIllegalArgument(); 1942 else 1943 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0))); 1944 } 1945 1946 void ScInterpreter::ScCosecant() 1947 { 1948 PushDouble(1.0 / ::rtl::math::sin(GetDouble())); 1949 } 1950 1951 void ScInterpreter::ScSecant() 1952 { 1953 PushDouble(1.0 / ::rtl::math::cos(GetDouble())); 1954 } 1955 1956 void ScInterpreter::ScCosecantHyp() 1957 { 1958 PushDouble(1.0 / sinh(GetDouble())); 1959 } 1960 1961 void ScInterpreter::ScSecantHyp() 1962 { 1963 PushDouble(1.0 / cosh(GetDouble())); 1964 } 1965 1966 void ScInterpreter::ScExp() 1967 { 1968 PushDouble(exp(GetDouble())); 1969 } 1970 1971 void ScInterpreter::ScSqrt() 1972 { 1973 double fVal = GetDouble(); 1974 if (fVal >= 0.0) 1975 PushDouble(sqrt(fVal)); 1976 else 1977 PushIllegalArgument(); 1978 } 1979 1980 void ScInterpreter::ScIsEmpty() 1981 { 1982 short nRes = 0; 1983 nFuncFmtType = SvNumFormatType::LOGICAL; 1984 switch ( GetRawStackType() ) 1985 { 1986 case svEmptyCell: 1987 { 1988 FormulaConstTokenRef p = PopToken(); 1989 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited()) 1990 nRes = 1; 1991 } 1992 break; 1993 case svDoubleRef : 1994 case svSingleRef : 1995 { 1996 ScAddress aAdr; 1997 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 1998 break; 1999 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that 2000 // may treat ="" in the referenced cell as blank for Excel 2001 // interoperability. 2002 ScRefCellValue aCell(mrDoc, aAdr); 2003 if (aCell.getType() == CELLTYPE_NONE) 2004 nRes = 1; 2005 } 2006 break; 2007 case svExternalSingleRef: 2008 case svExternalDoubleRef: 2009 case svMatrix: 2010 { 2011 ScMatrixRef pMat = GetMatrix(); 2012 if ( !pMat ) 2013 ; // nothing 2014 else if ( !pJumpMatrix ) 2015 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0; 2016 else 2017 { 2018 SCSIZE nCols, nRows, nC, nR; 2019 pMat->GetDimensions( nCols, nRows); 2020 pJumpMatrix->GetPos( nC, nR); 2021 if ( nC < nCols && nR < nRows ) 2022 nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0; 2023 // else: false, not empty (which is what Xcl does) 2024 } 2025 } 2026 break; 2027 default: 2028 Pop(); 2029 } 2030 nGlobalError = FormulaError::NONE; 2031 PushInt( nRes ); 2032 } 2033 2034 bool ScInterpreter::IsString() 2035 { 2036 nFuncFmtType = SvNumFormatType::LOGICAL; 2037 bool bRes = false; 2038 switch ( GetRawStackType() ) 2039 { 2040 case svString: 2041 Pop(); 2042 bRes = true; 2043 break; 2044 case svDoubleRef : 2045 case svSingleRef : 2046 { 2047 ScAddress aAdr; 2048 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2049 break; 2050 2051 ScRefCellValue aCell(mrDoc, aAdr); 2052 if (GetCellErrCode(aCell) == FormulaError::NONE) 2053 { 2054 switch (aCell.getType()) 2055 { 2056 case CELLTYPE_STRING : 2057 case CELLTYPE_EDIT : 2058 bRes = true; 2059 break; 2060 case CELLTYPE_FORMULA : 2061 bRes = (!aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty()); 2062 break; 2063 default: 2064 ; // nothing 2065 } 2066 } 2067 } 2068 break; 2069 case svExternalSingleRef: 2070 { 2071 ScExternalRefCache::TokenRef pToken; 2072 PopExternalSingleRef(pToken); 2073 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString) 2074 bRes = true; 2075 } 2076 break; 2077 case svExternalDoubleRef: 2078 case svMatrix: 2079 { 2080 ScMatrixRef pMat = GetMatrix(); 2081 if ( !pMat ) 2082 ; // nothing 2083 else if ( !pJumpMatrix ) 2084 bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0); 2085 else 2086 { 2087 SCSIZE nCols, nRows, nC, nR; 2088 pMat->GetDimensions( nCols, nRows); 2089 pJumpMatrix->GetPos( nC, nR); 2090 if ( nC < nCols && nR < nRows ) 2091 bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR); 2092 } 2093 } 2094 break; 2095 default: 2096 Pop(); 2097 } 2098 nGlobalError = FormulaError::NONE; 2099 return bRes; 2100 } 2101 2102 void ScInterpreter::ScIsString() 2103 { 2104 PushInt( int(IsString()) ); 2105 } 2106 2107 void ScInterpreter::ScIsNonString() 2108 { 2109 PushInt( int(!IsString()) ); 2110 } 2111 2112 void ScInterpreter::ScIsLogical() 2113 { 2114 bool bRes = false; 2115 switch ( GetStackType() ) 2116 { 2117 case svDoubleRef : 2118 case svSingleRef : 2119 { 2120 ScAddress aAdr; 2121 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2122 break; 2123 2124 ScRefCellValue aCell(mrDoc, aAdr); 2125 if (GetCellErrCode(aCell) == FormulaError::NONE) 2126 { 2127 if (aCell.hasNumeric()) 2128 { 2129 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell); 2130 bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL); 2131 } 2132 } 2133 } 2134 break; 2135 case svMatrix: 2136 { 2137 double fVal; 2138 svl::SharedString aStr; 2139 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr); 2140 bRes = (nMatValType == ScMatValType::Boolean); 2141 } 2142 break; 2143 default: 2144 PopError(); 2145 if ( nGlobalError == FormulaError::NONE ) 2146 bRes = ( nCurFmtType == SvNumFormatType::LOGICAL ); 2147 } 2148 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; 2149 nGlobalError = FormulaError::NONE; 2150 PushInt( int(bRes) ); 2151 } 2152 2153 void ScInterpreter::ScType() 2154 { 2155 short nType = 0; 2156 switch ( GetStackType() ) 2157 { 2158 case svDoubleRef : 2159 case svSingleRef : 2160 { 2161 ScAddress aAdr; 2162 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2163 break; 2164 2165 ScRefCellValue aCell(mrDoc, aAdr); 2166 if (GetCellErrCode(aCell) == FormulaError::NONE) 2167 { 2168 switch (aCell.getType()) 2169 { 2170 // NOTE: this is Xcl nonsense! 2171 case CELLTYPE_STRING : 2172 case CELLTYPE_EDIT : 2173 nType = 2; 2174 break; 2175 case CELLTYPE_VALUE : 2176 { 2177 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell); 2178 if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL) 2179 nType = 4; 2180 else 2181 nType = 1; 2182 } 2183 break; 2184 case CELLTYPE_NONE: 2185 // always 1, s. tdf#73078 2186 nType = 1; 2187 break; 2188 case CELLTYPE_FORMULA : 2189 nType = 8; 2190 break; 2191 default: 2192 PushIllegalArgument(); 2193 } 2194 } 2195 else 2196 nType = 16; 2197 } 2198 break; 2199 case svString: 2200 PopError(); 2201 if ( nGlobalError != FormulaError::NONE ) 2202 { 2203 nType = 16; 2204 nGlobalError = FormulaError::NONE; 2205 } 2206 else 2207 nType = 2; 2208 break; 2209 case svMatrix: 2210 PopMatrix(); 2211 if ( nGlobalError != FormulaError::NONE ) 2212 { 2213 nType = 16; 2214 nGlobalError = FormulaError::NONE; 2215 } 2216 else 2217 nType = 64; 2218 // we could return the type of one element if in JumpMatrix or 2219 // ForceArray mode, but Xcl doesn't ... 2220 break; 2221 default: 2222 PopError(); 2223 if ( nGlobalError != FormulaError::NONE ) 2224 { 2225 nType = 16; 2226 nGlobalError = FormulaError::NONE; 2227 } 2228 else 2229 nType = 1; 2230 } 2231 PushInt( nType ); 2232 } 2233 2234 static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat ) 2235 { 2236 return pFormat && pFormat->GetColor( 1 ); 2237 } 2238 2239 static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat ) 2240 { 2241 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1); 2242 } 2243 2244 namespace { 2245 2246 void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr) 2247 { 2248 rFmtStr = pFormatter->GetCalcCellReturn( nFormat); 2249 } 2250 2251 } 2252 2253 void ScInterpreter::ScCell() 2254 { // ATTRIBUTE ; [REF] 2255 sal_uInt8 nParamCount = GetByte(); 2256 if( !MustHaveParamCount( nParamCount, 1, 2 ) ) 2257 return; 2258 2259 ScAddress aCellPos( aPos ); 2260 if( nParamCount == 2 ) 2261 { 2262 switch (GetStackType()) 2263 { 2264 case svExternalSingleRef: 2265 case svExternalDoubleRef: 2266 { 2267 // Let's handle external reference separately... 2268 ScCellExternal(); 2269 return; 2270 } 2271 case svDoubleRef: 2272 { 2273 // Exceptionally not an intersecting position but top left. 2274 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL 2275 ScRange aRange; 2276 PopDoubleRef( aRange); 2277 aCellPos = aRange.aStart; 2278 } 2279 break; 2280 case svSingleRef: 2281 PopSingleRef( aCellPos); 2282 break; 2283 default: 2284 PopError(); 2285 SetError( FormulaError::NoRef); 2286 } 2287 } 2288 OUString aInfoType = GetString().getString(); 2289 if (nGlobalError != FormulaError::NONE) 2290 PushIllegalParameter(); 2291 else 2292 { 2293 ScRefCellValue aCell(mrDoc, aCellPos); 2294 2295 ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell); 2296 2297 // *** ADDRESS INFO *** 2298 if( aInfoType == "COL" ) 2299 { // column number (1-based) 2300 PushInt( aCellPos.Col() + 1 ); 2301 } 2302 else if( aInfoType == "ROW" ) 2303 { // row number (1-based) 2304 PushInt( aCellPos.Row() + 1 ); 2305 } 2306 else if( aInfoType == "SHEET" ) 2307 { // table number (1-based) 2308 PushInt( aCellPos.Tab() + 1 ); 2309 } 2310 else if( aInfoType == "ADDRESS" ) 2311 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW 2312 2313 // Follow the configurable string reference address syntax as also 2314 // used by INDIRECT() (and ADDRESS() for the sheet separator). 2315 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax; 2316 switch (eConv) 2317 { 2318 default: 2319 // Use the current address syntax if unspecified or says 2320 // one or the other or one we don't explicitly handle. 2321 eConv = mrDoc.GetAddressConvention(); 2322 break; 2323 case FormulaGrammar::CONV_OOO: 2324 case FormulaGrammar::CONV_XL_A1: 2325 case FormulaGrammar::CONV_XL_R1C1: 2326 // Use that. 2327 break; 2328 } 2329 2330 ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D; 2331 OUString aStr(aCellPos.Format(nFlags, &mrDoc, eConv)); 2332 PushString(aStr); 2333 } 2334 else if( aInfoType == "FILENAME" ) 2335 { 2336 SCTAB nTab = aCellPos.Tab(); 2337 OUString aFuncResult; 2338 if( nTab < mrDoc.GetTableCount() ) 2339 { 2340 if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE ) 2341 mrDoc.GetName( nTab, aFuncResult ); 2342 else 2343 { 2344 ScDocShell* pShell = mrDoc.GetDocumentShell(); 2345 if( pShell && pShell->GetMedium() ) 2346 { 2347 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject(); 2348 OUString aTabName; 2349 mrDoc.GetName( nTab, aTabName ); 2350 2351 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax; 2352 if (eConv == FormulaGrammar::CONV_UNSPECIFIED) 2353 eConv = mrDoc.GetAddressConvention(); 2354 2355 if (eConv == FormulaGrammar::CONV_XL_A1 || 2356 eConv == FormulaGrammar::CONV_XL_R1C1 || 2357 eConv == FormulaGrammar::CONV_XL_OOX) 2358 { 2359 // file name and table name: FILEPATH/[FILENAME]TABLE 2360 aFuncResult = rURLObj.GetPartBeforeLastName() 2361 + "[" + rURLObj.GetLastName(INetURLObject::DecodeMechanism::Unambiguous) 2362 + "]" + aTabName; 2363 } 2364 else 2365 { 2366 // file name and table name: 'FILEPATH/FILENAME'#$TABLE 2367 aFuncResult = "'" 2368 + rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous) 2369 + "'#$" + aTabName; 2370 } 2371 } 2372 } 2373 } 2374 PushString( aFuncResult ); 2375 } 2376 else if( aInfoType == "COORD" ) 2377 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW 2378 // Yes, passing tab as col is intentional! 2379 OUString aCellStr1 = 2380 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format( 2381 (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() ); 2382 OUString aCellStr2 = 2383 aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID), 2384 nullptr, mrDoc.GetAddressConvention()); 2385 OUString aFuncResult = aCellStr1 + ":" + aCellStr2; 2386 PushString( aFuncResult ); 2387 } 2388 2389 // *** CELL PROPERTIES *** 2390 else if( aInfoType == "CONTENTS" ) 2391 { // contents of the cell, no formatting 2392 if (aCell.hasString()) 2393 { 2394 svl::SharedString aStr; 2395 GetCellString(aStr, aCell); 2396 PushString( aStr ); 2397 } 2398 else 2399 PushDouble(GetCellValue(aCellPos, aCell)); 2400 } 2401 else if( aInfoType == "TYPE" ) 2402 { // b = blank; l = string (label); v = otherwise (value) 2403 sal_Unicode c; 2404 if (aCell.hasString()) 2405 c = 'l'; 2406 else 2407 c = aCell.hasNumeric() ? 'v' : 'b'; 2408 PushString( OUString(c) ); 2409 } 2410 else if( aInfoType == "WIDTH" ) 2411 { // column width (rounded off as count of zero characters in standard font and size) 2412 Printer* pPrinter = mrDoc.GetPrinter(); 2413 MapMode aOldMode( pPrinter->GetMapMode() ); 2414 vcl::Font aOldFont( pPrinter->GetFont() ); 2415 vcl::Font aDefFont; 2416 2417 pPrinter->SetMapMode(MapMode(MapUnit::MapTwip)); 2418 // font color doesn't matter here 2419 mrDoc.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont, pPrinter); 2420 pPrinter->SetFont(aDefFont); 2421 tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) ); 2422 assert(nZeroWidth != 0); 2423 pPrinter->SetFont( aOldFont ); 2424 pPrinter->SetMapMode( aOldMode ); 2425 int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth); 2426 PushInt( nZeroCount ); 2427 } 2428 else if( aInfoType == "PREFIX" ) 2429 { // ' = left; " = right; ^ = centered 2430 sal_Unicode c = 0; 2431 if (aCell.hasString()) 2432 { 2433 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY ); 2434 switch( pJustAttr->GetValue() ) 2435 { 2436 case SvxCellHorJustify::Standard: 2437 case SvxCellHorJustify::Left: 2438 case SvxCellHorJustify::Block: c = '\''; break; 2439 case SvxCellHorJustify::Center: c = '^'; break; 2440 case SvxCellHorJustify::Right: c = '"'; break; 2441 case SvxCellHorJustify::Repeat: c = '\\'; break; 2442 } 2443 } 2444 PushString( OUString(c) ); 2445 } 2446 else if( aInfoType == "PROTECT" ) 2447 { // 1 = cell locked 2448 const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION ); 2449 PushInt( pProtAttr->GetProtection() ? 1 : 0 ); 2450 } 2451 2452 // *** FORMATTING *** 2453 else if( aInfoType == "FORMAT" ) 2454 { // specific format code for standard formats 2455 OUString aFuncResult; 2456 sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos ); 2457 getFormatString(pFormatter, nFormat, aFuncResult); 2458 PushString( aFuncResult ); 2459 } 2460 else if( aInfoType == "COLOR" ) 2461 { // 1 = negative values are colored, otherwise 0 2462 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) ); 2463 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 ); 2464 } 2465 else if( aInfoType == "PARENTHESES" ) 2466 { // 1 = format string contains a '(' character, otherwise 0 2467 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) ); 2468 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 ); 2469 } 2470 else 2471 PushIllegalArgument(); 2472 } 2473 } 2474 2475 void ScInterpreter::ScCellExternal() 2476 { 2477 sal_uInt16 nFileId; 2478 OUString aTabName; 2479 ScSingleRefData aRef; 2480 ScExternalRefCache::TokenRef pToken; 2481 ScExternalRefCache::CellFormat aFmt; 2482 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt); 2483 if (nGlobalError != FormulaError::NONE) 2484 { 2485 PushError( nGlobalError); 2486 return; 2487 } 2488 2489 OUString aInfoType = GetString().getString(); 2490 if (nGlobalError != FormulaError::NONE) 2491 { 2492 PushError( nGlobalError); 2493 return; 2494 } 2495 2496 SCCOL nCol; 2497 SCROW nRow; 2498 SCTAB nTab; 2499 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like. 2500 SingleRefToVars(aRef, nCol, nRow, nTab); 2501 if (nGlobalError != FormulaError::NONE) 2502 { 2503 PushIllegalParameter(); 2504 return; 2505 } 2506 aRef.SetAbsTab(-1); // revert the value. 2507 2508 ScCellKeywordTranslator::transKeyword(aInfoType, &ScGlobal::GetLocale(), ocCell); 2509 ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager(); 2510 2511 if ( aInfoType == "COL" ) 2512 PushInt(nCol + 1); 2513 else if ( aInfoType == "ROW" ) 2514 PushInt(nRow + 1); 2515 else if ( aInfoType == "SHEET" ) 2516 { 2517 // For SHEET, No idea what number we should set, but let's always set 2518 // 1 if the external sheet exists, no matter what sheet. Excel does 2519 // the same. 2520 if (pRefMgr->getCacheTable(nFileId, aTabName, false)) 2521 PushInt(1); 2522 else 2523 SetError(FormulaError::NoName); 2524 } 2525 else if ( aInfoType == "ADDRESS" ) 2526 { 2527 // ODF 1.2 says we need to always display address using the ODF A1 grammar. 2528 ScTokenArray aArray(mrDoc); 2529 aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned 2530 ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1); 2531 OUString aStr; 2532 aComp.CreateStringFromTokenArray(aStr); 2533 PushString(aStr); 2534 } 2535 else if ( aInfoType == "FILENAME" ) 2536 { 2537 const OUString* p = pRefMgr->getExternalFileName(nFileId); 2538 if (!p) 2539 { 2540 // In theory this should never happen... 2541 SetError(FormulaError::NoName); 2542 return; 2543 } 2544 2545 OUString aBuf; 2546 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax; 2547 if (eConv == FormulaGrammar::CONV_UNSPECIFIED) 2548 eConv = mrDoc.GetAddressConvention(); 2549 2550 if (eConv == FormulaGrammar::CONV_XL_A1 || 2551 eConv == FormulaGrammar::CONV_XL_R1C1 || 2552 eConv == FormulaGrammar::CONV_XL_OOX) 2553 { 2554 // 'file URI/[FileName]SheetName 2555 sal_Int32 nPos = p->lastIndexOf('/'); 2556 aBuf = OUString::Concat(p->subView(0, nPos + 1)) 2557 + "[" + p->subView(nPos + 1) + "]" 2558 + aTabName; 2559 } 2560 else 2561 { 2562 // 'file URI'#$SheetName 2563 aBuf = "'" + *p + "'#$" + aTabName; 2564 } 2565 2566 PushString(aBuf); 2567 } 2568 else if ( aInfoType == "CONTENTS" ) 2569 { 2570 switch (pToken->GetType()) 2571 { 2572 case svString: 2573 PushString(pToken->GetString()); 2574 break; 2575 case svDouble: 2576 PushString(OUString::number(pToken->GetDouble())); 2577 break; 2578 case svError: 2579 PushString(ScGlobal::GetErrorString(pToken->GetError())); 2580 break; 2581 default: 2582 PushString(OUString()); 2583 } 2584 } 2585 else if ( aInfoType == "TYPE" ) 2586 { 2587 sal_Unicode c = 'v'; 2588 switch (pToken->GetType()) 2589 { 2590 case svString: 2591 c = 'l'; 2592 break; 2593 case svEmptyCell: 2594 c = 'b'; 2595 break; 2596 default: 2597 ; 2598 } 2599 PushString(OUString(c)); 2600 } 2601 else if ( aInfoType == "FORMAT" ) 2602 { 2603 OUString aFmtStr; 2604 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0; 2605 getFormatString(pFormatter, nFmt, aFmtStr); 2606 PushString(aFmtStr); 2607 } 2608 else if ( aInfoType == "COLOR" ) 2609 { 2610 // 1 = negative values are colored, otherwise 0 2611 int nVal = 0; 2612 if (aFmt.mbIsSet) 2613 { 2614 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex); 2615 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0; 2616 } 2617 PushInt(nVal); 2618 } 2619 else if ( aInfoType == "PARENTHESES" ) 2620 { 2621 // 1 = format string contains a '(' character, otherwise 0 2622 int nVal = 0; 2623 if (aFmt.mbIsSet) 2624 { 2625 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex); 2626 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0; 2627 } 2628 PushInt(nVal); 2629 } 2630 else 2631 PushIllegalParameter(); 2632 } 2633 2634 void ScInterpreter::ScIsRef() 2635 { 2636 nFuncFmtType = SvNumFormatType::LOGICAL; 2637 bool bRes = false; 2638 switch ( GetStackType() ) 2639 { 2640 case svSingleRef : 2641 { 2642 ScAddress aAdr; 2643 PopSingleRef( aAdr ); 2644 if ( nGlobalError == FormulaError::NONE ) 2645 bRes = true; 2646 } 2647 break; 2648 case svDoubleRef : 2649 { 2650 ScRange aRange; 2651 PopDoubleRef( aRange ); 2652 if ( nGlobalError == FormulaError::NONE ) 2653 bRes = true; 2654 } 2655 break; 2656 case svRefList : 2657 { 2658 FormulaConstTokenRef x = PopToken(); 2659 if ( nGlobalError == FormulaError::NONE ) 2660 bRes = !x->GetRefList()->empty(); 2661 } 2662 break; 2663 case svExternalSingleRef: 2664 { 2665 ScExternalRefCache::TokenRef pToken; 2666 PopExternalSingleRef(pToken); 2667 if (nGlobalError == FormulaError::NONE) 2668 bRes = true; 2669 } 2670 break; 2671 case svExternalDoubleRef: 2672 { 2673 ScExternalRefCache::TokenArrayRef pArray; 2674 PopExternalDoubleRef(pArray); 2675 if (nGlobalError == FormulaError::NONE) 2676 bRes = true; 2677 } 2678 break; 2679 default: 2680 Pop(); 2681 } 2682 nGlobalError = FormulaError::NONE; 2683 PushInt( int(bRes) ); 2684 } 2685 2686 void ScInterpreter::ScIsValue() 2687 { 2688 nFuncFmtType = SvNumFormatType::LOGICAL; 2689 bool bRes = false; 2690 switch ( GetRawStackType() ) 2691 { 2692 case svDouble: 2693 Pop(); 2694 bRes = true; 2695 break; 2696 case svDoubleRef : 2697 case svSingleRef : 2698 { 2699 ScAddress aAdr; 2700 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2701 break; 2702 2703 ScRefCellValue aCell(mrDoc, aAdr); 2704 if (GetCellErrCode(aCell) == FormulaError::NONE) 2705 { 2706 switch (aCell.getType()) 2707 { 2708 case CELLTYPE_VALUE : 2709 bRes = true; 2710 break; 2711 case CELLTYPE_FORMULA : 2712 bRes = (aCell.getFormula()->IsValue() && !aCell.getFormula()->IsEmpty()); 2713 break; 2714 default: 2715 ; // nothing 2716 } 2717 } 2718 } 2719 break; 2720 case svExternalSingleRef: 2721 { 2722 ScExternalRefCache::TokenRef pToken; 2723 PopExternalSingleRef(pToken); 2724 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble) 2725 bRes = true; 2726 } 2727 break; 2728 case svExternalDoubleRef: 2729 case svMatrix: 2730 { 2731 ScMatrixRef pMat = GetMatrix(); 2732 if ( !pMat ) 2733 ; // nothing 2734 else if ( !pJumpMatrix ) 2735 { 2736 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE) 2737 bRes = pMat->IsValue( 0, 0); 2738 } 2739 else 2740 { 2741 SCSIZE nCols, nRows, nC, nR; 2742 pMat->GetDimensions( nCols, nRows); 2743 pJumpMatrix->GetPos( nC, nR); 2744 if ( nC < nCols && nR < nRows ) 2745 if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE) 2746 bRes = pMat->IsValue( nC, nR); 2747 } 2748 } 2749 break; 2750 default: 2751 Pop(); 2752 } 2753 nGlobalError = FormulaError::NONE; 2754 PushInt( int(bRes) ); 2755 } 2756 2757 void ScInterpreter::ScIsFormula() 2758 { 2759 nFuncFmtType = SvNumFormatType::LOGICAL; 2760 bool bRes = false; 2761 switch ( GetStackType() ) 2762 { 2763 case svDoubleRef : 2764 if (IsInArrayContext()) 2765 { 2766 SCCOL nCol1, nCol2; 2767 SCROW nRow1, nRow2; 2768 SCTAB nTab1, nTab2; 2769 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 2770 if (nGlobalError != FormulaError::NONE) 2771 { 2772 PushError( nGlobalError); 2773 return; 2774 } 2775 if (nTab1 != nTab2) 2776 { 2777 PushIllegalArgument(); 2778 return; 2779 } 2780 2781 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1), 2782 static_cast<SCSIZE>(nRow2 - nRow1 + 1), true); 2783 if (!pResMat) 2784 { 2785 PushError( FormulaError::MatrixSize); 2786 return; 2787 } 2788 2789 /* TODO: we really should have a gap-aware cell iterator. */ 2790 SCSIZE i=0, j=0; 2791 ScAddress aAdr( 0, 0, nTab1); 2792 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 2793 { 2794 aAdr.SetCol(nCol); 2795 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 2796 { 2797 aAdr.SetRow(nRow); 2798 ScRefCellValue aCell(mrDoc, aAdr); 2799 pResMat->PutBoolean( (aCell.getType() == CELLTYPE_FORMULA), i,j); 2800 ++j; 2801 } 2802 ++i; 2803 j = 0; 2804 } 2805 2806 PushMatrix( pResMat); 2807 return; 2808 } 2809 [[fallthrough]]; 2810 case svSingleRef : 2811 { 2812 ScAddress aAdr; 2813 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2814 break; 2815 2816 bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA); 2817 } 2818 break; 2819 default: 2820 Pop(); 2821 } 2822 nGlobalError = FormulaError::NONE; 2823 PushInt( int(bRes) ); 2824 } 2825 2826 void ScInterpreter::ScFormula() 2827 { 2828 OUString aFormula; 2829 switch ( GetStackType() ) 2830 { 2831 case svDoubleRef : 2832 if (IsInArrayContext()) 2833 { 2834 SCCOL nCol1, nCol2; 2835 SCROW nRow1, nRow2; 2836 SCTAB nTab1, nTab2; 2837 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 2838 if (nGlobalError != FormulaError::NONE) 2839 break; 2840 2841 if (nTab1 != nTab2) 2842 { 2843 SetError( FormulaError::IllegalArgument); 2844 break; 2845 } 2846 2847 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true); 2848 if (!pResMat) 2849 break; 2850 2851 /* TODO: use a column iterator instead? */ 2852 SCSIZE i=0, j=0; 2853 ScAddress aAdr(0,0,nTab1); 2854 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 2855 { 2856 aAdr.SetCol(nCol); 2857 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 2858 { 2859 aAdr.SetRow(nRow); 2860 ScRefCellValue aCell(mrDoc, aAdr); 2861 switch (aCell.getType()) 2862 { 2863 case CELLTYPE_FORMULA : 2864 aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext); 2865 pResMat->PutString( mrStrPool.intern( aFormula), i,j); 2866 break; 2867 default: 2868 pResMat->PutError( FormulaError::NotAvailable, i,j); 2869 } 2870 ++j; 2871 } 2872 ++i; 2873 j = 0; 2874 } 2875 2876 PushMatrix( pResMat); 2877 return; 2878 } 2879 [[fallthrough]]; 2880 case svSingleRef : 2881 { 2882 ScAddress aAdr; 2883 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 2884 break; 2885 2886 ScRefCellValue aCell(mrDoc, aAdr); 2887 switch (aCell.getType()) 2888 { 2889 case CELLTYPE_FORMULA : 2890 aFormula = aCell.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext); 2891 break; 2892 default: 2893 SetError( FormulaError::NotAvailable ); 2894 } 2895 } 2896 break; 2897 default: 2898 PopError(); 2899 SetError( FormulaError::NotAvailable ); 2900 } 2901 PushString( aFormula ); 2902 } 2903 2904 void ScInterpreter::ScIsNV() 2905 { 2906 nFuncFmtType = SvNumFormatType::LOGICAL; 2907 bool bRes = false; 2908 switch ( GetStackType() ) 2909 { 2910 case svDoubleRef : 2911 case svSingleRef : 2912 { 2913 ScAddress aAdr; 2914 bool bOk = PopDoubleRefOrSingleRef( aAdr ); 2915 if ( nGlobalError == FormulaError::NotAvailable ) 2916 bRes = true; 2917 else if (bOk) 2918 { 2919 ScRefCellValue aCell(mrDoc, aAdr); 2920 FormulaError nErr = GetCellErrCode(aCell); 2921 bRes = (nErr == FormulaError::NotAvailable); 2922 } 2923 } 2924 break; 2925 case svExternalSingleRef: 2926 { 2927 ScExternalRefCache::TokenRef pToken; 2928 PopExternalSingleRef(pToken); 2929 if (nGlobalError == FormulaError::NotAvailable || 2930 (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable)) 2931 bRes = true; 2932 } 2933 break; 2934 case svExternalDoubleRef: 2935 case svMatrix: 2936 { 2937 ScMatrixRef pMat = GetMatrix(); 2938 if ( !pMat ) 2939 ; // nothing 2940 else if ( !pJumpMatrix ) 2941 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable); 2942 else 2943 { 2944 SCSIZE nCols, nRows, nC, nR; 2945 pMat->GetDimensions( nCols, nRows); 2946 pJumpMatrix->GetPos( nC, nR); 2947 if ( nC < nCols && nR < nRows ) 2948 bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable); 2949 } 2950 } 2951 break; 2952 default: 2953 PopError(); 2954 if ( nGlobalError == FormulaError::NotAvailable ) 2955 bRes = true; 2956 } 2957 nGlobalError = FormulaError::NONE; 2958 PushInt( int(bRes) ); 2959 } 2960 2961 void ScInterpreter::ScIsErr() 2962 { 2963 nFuncFmtType = SvNumFormatType::LOGICAL; 2964 bool bRes = false; 2965 switch ( GetStackType() ) 2966 { 2967 case svDoubleRef : 2968 case svSingleRef : 2969 { 2970 ScAddress aAdr; 2971 bool bOk = PopDoubleRefOrSingleRef( aAdr ); 2972 if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) ) 2973 bRes = true; 2974 else 2975 { 2976 ScRefCellValue aCell(mrDoc, aAdr); 2977 FormulaError nErr = GetCellErrCode(aCell); 2978 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); 2979 } 2980 } 2981 break; 2982 case svExternalSingleRef: 2983 { 2984 ScExternalRefCache::TokenRef pToken; 2985 PopExternalSingleRef(pToken); 2986 if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken || 2987 (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable)) 2988 bRes = true; 2989 } 2990 break; 2991 case svExternalDoubleRef: 2992 case svMatrix: 2993 { 2994 ScMatrixRef pMat = GetMatrix(); 2995 if ( nGlobalError != FormulaError::NONE || !pMat ) 2996 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat); 2997 else if ( !pJumpMatrix ) 2998 { 2999 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0); 3000 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); 3001 } 3002 else 3003 { 3004 SCSIZE nCols, nRows, nC, nR; 3005 pMat->GetDimensions( nCols, nRows); 3006 pJumpMatrix->GetPos( nC, nR); 3007 if ( nC < nCols && nR < nRows ) 3008 { 3009 FormulaError nErr = pMat->GetErrorIfNotString( nC, nR); 3010 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); 3011 } 3012 } 3013 } 3014 break; 3015 default: 3016 PopError(); 3017 if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable ) 3018 bRes = true; 3019 } 3020 nGlobalError = FormulaError::NONE; 3021 PushInt( int(bRes) ); 3022 } 3023 3024 void ScInterpreter::ScIsError() 3025 { 3026 nFuncFmtType = SvNumFormatType::LOGICAL; 3027 bool bRes = false; 3028 switch ( GetStackType() ) 3029 { 3030 case svDoubleRef : 3031 case svSingleRef : 3032 { 3033 ScAddress aAdr; 3034 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 3035 { 3036 bRes = true; 3037 break; 3038 } 3039 if ( nGlobalError != FormulaError::NONE ) 3040 bRes = true; 3041 else 3042 { 3043 ScRefCellValue aCell(mrDoc, aAdr); 3044 bRes = (GetCellErrCode(aCell) != FormulaError::NONE); 3045 } 3046 } 3047 break; 3048 case svExternalSingleRef: 3049 { 3050 ScExternalRefCache::TokenRef pToken; 3051 PopExternalSingleRef(pToken); 3052 if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError) 3053 bRes = true; 3054 } 3055 break; 3056 case svExternalDoubleRef: 3057 case svMatrix: 3058 { 3059 ScMatrixRef pMat = GetMatrix(); 3060 if ( nGlobalError != FormulaError::NONE || !pMat ) 3061 bRes = true; 3062 else if ( !pJumpMatrix ) 3063 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE); 3064 else 3065 { 3066 SCSIZE nCols, nRows, nC, nR; 3067 pMat->GetDimensions( nCols, nRows); 3068 pJumpMatrix->GetPos( nC, nR); 3069 if ( nC < nCols && nR < nRows ) 3070 bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE); 3071 } 3072 } 3073 break; 3074 default: 3075 PopError(); 3076 if ( nGlobalError != FormulaError::NONE ) 3077 bRes = true; 3078 } 3079 nGlobalError = FormulaError::NONE; 3080 PushInt( int(bRes) ); 3081 } 3082 3083 bool ScInterpreter::IsEven() 3084 { 3085 nFuncFmtType = SvNumFormatType::LOGICAL; 3086 bool bRes = false; 3087 double fVal = 0.0; 3088 switch ( GetStackType() ) 3089 { 3090 case svDoubleRef : 3091 case svSingleRef : 3092 { 3093 ScAddress aAdr; 3094 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 3095 break; 3096 3097 ScRefCellValue aCell(mrDoc, aAdr); 3098 FormulaError nErr = GetCellErrCode(aCell); 3099 if (nErr != FormulaError::NONE) 3100 SetError(nErr); 3101 else 3102 { 3103 switch (aCell.getType()) 3104 { 3105 case CELLTYPE_VALUE : 3106 fVal = GetCellValue(aAdr, aCell); 3107 bRes = true; 3108 break; 3109 case CELLTYPE_FORMULA : 3110 if (aCell.getFormula()->IsValue()) 3111 { 3112 fVal = GetCellValue(aAdr, aCell); 3113 bRes = true; 3114 } 3115 break; 3116 default: 3117 ; // nothing 3118 } 3119 } 3120 } 3121 break; 3122 case svDouble: 3123 { 3124 fVal = PopDouble(); 3125 bRes = true; 3126 } 3127 break; 3128 case svExternalSingleRef: 3129 { 3130 ScExternalRefCache::TokenRef pToken; 3131 PopExternalSingleRef(pToken); 3132 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble) 3133 { 3134 fVal = pToken->GetDouble(); 3135 bRes = true; 3136 } 3137 } 3138 break; 3139 case svExternalDoubleRef: 3140 case svMatrix: 3141 { 3142 ScMatrixRef pMat = GetMatrix(); 3143 if ( !pMat ) 3144 ; // nothing 3145 else if ( !pJumpMatrix ) 3146 { 3147 bRes = pMat->IsValue( 0, 0); 3148 if ( bRes ) 3149 fVal = pMat->GetDouble( 0, 0); 3150 } 3151 else 3152 { 3153 SCSIZE nCols, nRows, nC, nR; 3154 pMat->GetDimensions( nCols, nRows); 3155 pJumpMatrix->GetPos( nC, nR); 3156 if ( nC < nCols && nR < nRows ) 3157 { 3158 bRes = pMat->IsValue( nC, nR); 3159 if ( bRes ) 3160 fVal = pMat->GetDouble( nC, nR); 3161 } 3162 else 3163 SetError( FormulaError::NoValue); 3164 } 3165 } 3166 break; 3167 default: 3168 ; // nothing 3169 } 3170 if ( !bRes ) 3171 SetError( FormulaError::IllegalParameter); 3172 else 3173 bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 ); 3174 return bRes; 3175 } 3176 3177 void ScInterpreter::ScIsEven() 3178 { 3179 PushInt( int(IsEven()) ); 3180 } 3181 3182 void ScInterpreter::ScIsOdd() 3183 { 3184 PushInt( int(!IsEven()) ); 3185 } 3186 3187 void ScInterpreter::ScN() 3188 { 3189 FormulaError nErr = nGlobalError; 3190 nGlobalError = FormulaError::NONE; 3191 // Temporarily override the ConvertStringToValue() error for 3192 // GetCellValue() / GetCellValueOrZero() 3193 FormulaError nSErr = mnStringNoValueError; 3194 mnStringNoValueError = FormulaError::CellNoValue; 3195 double fVal = GetDouble(); 3196 mnStringNoValueError = nSErr; 3197 if (nErr != FormulaError::NONE) 3198 nGlobalError = nErr; // preserve previous error if any 3199 else if (nGlobalError == FormulaError::CellNoValue) 3200 nGlobalError = FormulaError::NONE; // reset temporary detection error 3201 PushDouble(fVal); 3202 } 3203 3204 void ScInterpreter::ScTrim() 3205 { 3206 // Doesn't only trim but also removes duplicated blanks within! 3207 OUString aVal = comphelper::string::strip(GetString().getString(), ' '); 3208 OUStringBuffer aStr; 3209 const sal_Unicode* p = aVal.getStr(); 3210 const sal_Unicode* const pEnd = p + aVal.getLength(); 3211 while ( p < pEnd ) 3212 { 3213 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine 3214 aStr.append(*p); 3215 p++; 3216 } 3217 PushString(aStr.makeStringAndClear()); 3218 } 3219 3220 void ScInterpreter::ScUpper() 3221 { 3222 OUString aString = ScGlobal::getCharClass().uppercase(GetString().getString()); 3223 PushString(aString); 3224 } 3225 3226 void ScInterpreter::ScProper() 3227 { 3228 //2do: what to do with I18N-CJK ?!? 3229 OUStringBuffer aStr(GetString().getString()); 3230 const sal_Int32 nLen = aStr.getLength(); 3231 if ( nLen > 0 ) 3232 { 3233 OUString aUpr(ScGlobal::getCharClass().uppercase(aStr.toString())); 3234 OUString aLwr(ScGlobal::getCharClass().lowercase(aStr.toString())); 3235 aStr[0] = aUpr[0]; 3236 sal_Int32 nPos = 1; 3237 while( nPos < nLen ) 3238 { 3239 OUString aTmpStr( aStr[nPos-1] ); 3240 if ( !ScGlobal::getCharClass().isLetter( aTmpStr, 0 ) ) 3241 aStr[nPos] = aUpr[nPos]; 3242 else 3243 aStr[nPos] = aLwr[nPos]; 3244 ++nPos; 3245 } 3246 } 3247 PushString(aStr.makeStringAndClear()); 3248 } 3249 3250 void ScInterpreter::ScLower() 3251 { 3252 OUString aString = ScGlobal::getCharClass().lowercase(GetString().getString()); 3253 PushString(aString); 3254 } 3255 3256 void ScInterpreter::ScLen() 3257 { 3258 OUString aStr = GetString().getString(); 3259 sal_Int32 nIdx = 0; 3260 sal_Int32 nCnt = 0; 3261 while ( nIdx < aStr.getLength() ) 3262 { 3263 aStr.iterateCodePoints( &nIdx ); 3264 ++nCnt; 3265 } 3266 PushDouble( nCnt ); 3267 } 3268 3269 void ScInterpreter::ScT() 3270 { 3271 switch ( GetStackType() ) 3272 { 3273 case svDoubleRef : 3274 case svSingleRef : 3275 { 3276 ScAddress aAdr; 3277 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 3278 { 3279 PushInt(0); 3280 return ; 3281 } 3282 bool bValue = false; 3283 ScRefCellValue aCell(mrDoc, aAdr); 3284 if (GetCellErrCode(aCell) == FormulaError::NONE) 3285 { 3286 switch (aCell.getType()) 3287 { 3288 case CELLTYPE_VALUE : 3289 bValue = true; 3290 break; 3291 case CELLTYPE_FORMULA : 3292 bValue = aCell.getFormula()->IsValue(); 3293 break; 3294 default: 3295 ; // nothing 3296 } 3297 } 3298 if ( bValue ) 3299 PushString(OUString()); 3300 else 3301 { 3302 // like GetString() 3303 svl::SharedString aStr; 3304 GetCellString(aStr, aCell); 3305 PushString(aStr); 3306 } 3307 } 3308 break; 3309 case svMatrix: 3310 case svExternalSingleRef: 3311 case svExternalDoubleRef: 3312 { 3313 double fVal; 3314 svl::SharedString aStr; 3315 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr); 3316 if (ScMatrix::IsValueType( nMatValType)) 3317 PushString(svl::SharedString::getEmptyString()); 3318 else 3319 PushString( aStr); 3320 } 3321 break; 3322 case svDouble : 3323 { 3324 PopError(); 3325 PushString( OUString() ); 3326 } 3327 break; 3328 case svString : 3329 ; // leave on stack 3330 break; 3331 default : 3332 PushError( FormulaError::UnknownOpCode); 3333 } 3334 } 3335 3336 void ScInterpreter::ScValue() 3337 { 3338 OUString aInputString; 3339 double fVal; 3340 3341 switch ( GetRawStackType() ) 3342 { 3343 case svMissing: 3344 case svEmptyCell: 3345 Pop(); 3346 PushInt(0); 3347 return; 3348 case svDouble: 3349 return; // leave on stack 3350 3351 case svSingleRef: 3352 case svDoubleRef: 3353 { 3354 ScAddress aAdr; 3355 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 3356 { 3357 PushInt(0); 3358 return; 3359 } 3360 ScRefCellValue aCell(mrDoc, aAdr); 3361 if (aCell.hasString()) 3362 { 3363 svl::SharedString aSS; 3364 GetCellString(aSS, aCell); 3365 aInputString = aSS.getString(); 3366 } 3367 else if (aCell.hasNumeric()) 3368 { 3369 PushDouble( GetCellValue(aAdr, aCell) ); 3370 return; 3371 } 3372 else 3373 { 3374 PushDouble(0.0); 3375 return; 3376 } 3377 } 3378 break; 3379 case svMatrix: 3380 { 3381 svl::SharedString aSS; 3382 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, 3383 aSS); 3384 aInputString = aSS.getString(); 3385 switch (nType) 3386 { 3387 case ScMatValType::Empty: 3388 fVal = 0.0; 3389 [[fallthrough]]; 3390 case ScMatValType::Value: 3391 case ScMatValType::Boolean: 3392 PushDouble( fVal); 3393 return; 3394 case ScMatValType::String: 3395 // evaluated below 3396 break; 3397 default: 3398 PushIllegalArgument(); 3399 } 3400 } 3401 break; 3402 default: 3403 aInputString = GetString().getString(); 3404 break; 3405 } 3406 3407 sal_uInt32 nFIndex = 0; // 0 for default locale 3408 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) 3409 PushDouble(fVal); 3410 else 3411 PushIllegalArgument(); 3412 } 3413 3414 // fdo#57180 3415 void ScInterpreter::ScNumberValue() 3416 { 3417 3418 sal_uInt8 nParamCount = GetByte(); 3419 if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) 3420 return; 3421 3422 OUString aInputString; 3423 OUString aGroupSeparator; 3424 sal_Unicode cDecimalSeparator = 0; 3425 3426 if ( nParamCount == 3 ) 3427 aGroupSeparator = GetString().getString(); 3428 3429 if ( nParamCount >= 2 ) 3430 { 3431 OUString aDecimalSeparator = GetString().getString(); 3432 if ( aDecimalSeparator.getLength() == 1 ) 3433 cDecimalSeparator = aDecimalSeparator[ 0 ]; 3434 else 3435 { 3436 PushIllegalArgument(); //if given, separator length must be 1 3437 return; 3438 } 3439 } 3440 3441 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 ) 3442 { 3443 PushIllegalArgument(); //decimal separator cannot appear in group separator 3444 return; 3445 } 3446 3447 switch (GetStackType()) 3448 { 3449 case svDouble: 3450 return; // leave on stack 3451 default: 3452 aInputString = GetString().getString(); 3453 } 3454 if ( nGlobalError != FormulaError::NONE ) 3455 { 3456 PushError( nGlobalError ); 3457 return; 3458 } 3459 if ( aInputString.isEmpty() ) 3460 { 3461 if ( maCalcConfig.mbEmptyStringAsZero ) 3462 PushDouble( 0.0 ); 3463 else 3464 PushNoValue(); 3465 return; 3466 } 3467 3468 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator ); 3469 if ( nDecSep != 0 ) 3470 { 3471 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString ); 3472 sal_Int32 nIndex = 0; 3473 while (nIndex < aGroupSeparator.getLength()) 3474 { 3475 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex ); 3476 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" ); 3477 } 3478 if ( nDecSep >= 0 ) 3479 aInputString = aTemporary + aInputString.subView( nDecSep ); 3480 else 3481 aInputString = aTemporary; 3482 } 3483 3484 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; ) 3485 { 3486 sal_Unicode c = aInputString[ i ]; 3487 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D ) 3488 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove spaces etc. 3489 } 3490 sal_Int32 nPercentCount = 0; 3491 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- ) 3492 { 3493 aInputString = aInputString.replaceAt( i, 1, u"" ); // remove and count trailing '%' 3494 nPercentCount++; 3495 } 3496 3497 rtl_math_ConversionStatus eStatus; 3498 sal_Int32 nParseEnd; 3499 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd ); 3500 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() ) 3501 { 3502 if (nPercentCount) 3503 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string 3504 PushDouble(fVal); 3505 return; 3506 } 3507 PushNoValue(); 3508 } 3509 3510 static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint ) 3511 { 3512 return ( !u_isISOControl(nCodePoint) /*not in Cc*/ 3513 && u_isdefined(nCodePoint) /*not in Cn*/ ); 3514 } 3515 3516 3517 void ScInterpreter::ScClean() 3518 { 3519 OUString aStr = GetString().getString(); 3520 3521 OUStringBuffer aBuf( aStr.getLength() ); 3522 sal_Int32 nIdx = 0; 3523 while ( nIdx < aStr.getLength() ) 3524 { 3525 sal_uInt32 c = aStr.iterateCodePoints( &nIdx ); 3526 if ( lcl_ScInterpreter_IsPrintable( c ) ) 3527 aBuf.appendUtf32( c ); 3528 } 3529 PushString( aBuf.makeStringAndClear() ); 3530 } 3531 3532 3533 void ScInterpreter::ScCode() 3534 { 3535 //2do: make it full range unicode? 3536 OUString aStr = GetString().getString(); 3537 if (aStr.isEmpty()) 3538 PushInt(0); 3539 else 3540 { 3541 //"classic" ByteString conversion flags 3542 const sal_uInt32 convertFlags = 3543 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE | 3544 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE | 3545 RTL_UNICODETOTEXT_FLAGS_FLUSH | 3546 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT | 3547 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT | 3548 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE; 3549 PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) ); 3550 } 3551 } 3552 3553 void ScInterpreter::ScChar() 3554 { 3555 //2do: make it full range unicode? 3556 double fVal = GetDouble(); 3557 if (fVal < 0.0 || fVal >= 256.0) 3558 PushIllegalArgument(); 3559 else 3560 { 3561 //"classic" ByteString conversion flags 3562 const sal_uInt32 convertFlags = 3563 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT | 3564 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT | 3565 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT; 3566 3567 char cEncodedChar = static_cast<char>(fVal); 3568 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags); 3569 PushString(aStr); 3570 } 3571 } 3572 3573 /* #i70213# fullwidth/halfwidth conversion provided by 3574 * Takashi Nakamoto <bluedwarf@ooo> 3575 * erAck: added Excel compatibility conversions as seen in issue's test case. */ 3576 3577 static OUString lcl_convertIntoHalfWidth( const OUString & rStr ) 3578 { 3579 // Make the initialization thread-safe. Since another function needs to be called, move it all to another 3580 // function and thread-safely initialize a static reference in this function. 3581 auto init = []() -> utl::TransliterationWrapper& 3582 { 3583 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE ); 3584 trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM ); 3585 return trans; 3586 }; 3587 static utl::TransliterationWrapper& aTrans( init()); 3588 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) ); 3589 } 3590 3591 static OUString lcl_convertIntoFullWidth( const OUString & rStr ) 3592 { 3593 auto init = []() -> utl::TransliterationWrapper& 3594 { 3595 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE ); 3596 trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM ); 3597 return trans; 3598 }; 3599 static utl::TransliterationWrapper& aTrans( init()); 3600 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) ); 3601 } 3602 3603 /* ODFF: 3604 * Summary: Converts half-width to full-width ASCII and katakana characters. 3605 * Semantics: Conversion is done for half-width ASCII and katakana characters, 3606 * other characters are simply copied from T to the result. This is the 3607 * complementary function to ASC. 3608 * For references regarding halfwidth and fullwidth characters see 3609 * http://www.unicode.org/reports/tr11/ 3610 * http://www.unicode.org/charts/charindex2.html#H 3611 * http://www.unicode.org/charts/charindex2.html#F 3612 */ 3613 void ScInterpreter::ScJis() 3614 { 3615 if (MustHaveParamCount( GetByte(), 1)) 3616 PushString( lcl_convertIntoFullWidth( GetString().getString())); 3617 } 3618 3619 /* ODFF: 3620 * Summary: Converts full-width to half-width ASCII and katakana characters. 3621 * Semantics: Conversion is done for full-width ASCII and katakana characters, 3622 * other characters are simply copied from T to the result. This is the 3623 * complementary function to JIS. 3624 */ 3625 void ScInterpreter::ScAsc() 3626 { 3627 if (MustHaveParamCount( GetByte(), 1)) 3628 PushString( lcl_convertIntoHalfWidth( GetString().getString())); 3629 } 3630 3631 void ScInterpreter::ScUnicode() 3632 { 3633 if ( MustHaveParamCount( GetByte(), 1 ) ) 3634 { 3635 OUString aStr = GetString().getString(); 3636 if (aStr.isEmpty()) 3637 PushIllegalParameter(); 3638 else 3639 { 3640 PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0)))); 3641 } 3642 } 3643 } 3644 3645 void ScInterpreter::ScUnichar() 3646 { 3647 if ( MustHaveParamCount( GetByte(), 1 ) ) 3648 { 3649 sal_uInt32 nCodePoint = GetUInt32(); 3650 if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint)) 3651 PushIllegalArgument(); 3652 else 3653 { 3654 OUString aStr( &nCodePoint, 1 ); 3655 PushString( aStr ); 3656 } 3657 } 3658 } 3659 3660 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent, 3661 const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp ) 3662 { 3663 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); 3664 if (!p || !p->IsArrayResult()) 3665 return false; 3666 3667 if (!xResMat) 3668 { 3669 // Create and init all elements with current value. 3670 assert(nMatRows > 0); 3671 xResMat = GetNewMat( 1, nMatRows, true); 3672 xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1); 3673 } 3674 else if (bDoMatOp) 3675 { 3676 // Current value and values from vector are operands 3677 // for each vector position. 3678 for (SCSIZE i=0; i < nMatRows; ++i) 3679 { 3680 MatOpFunc( i, fCurrent); 3681 } 3682 } 3683 return true; 3684 } 3685 3686 void ScInterpreter::ScMin( bool bTextAsZero ) 3687 { 3688 short nParamCount = GetByte(); 3689 if (!MustHaveParamCountMin( nParamCount, 1)) 3690 return; 3691 3692 ScMatrixRef xResMat; 3693 double nMin = ::std::numeric_limits<double>::max(); 3694 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin ) 3695 { 3696 double fVecRes = xResMat->GetDouble(0,i); 3697 if (fVecRes > fCurMin) 3698 xResMat->PutDouble( fCurMin, 0,i); 3699 }; 3700 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); 3701 size_t nRefArrayPos = std::numeric_limits<size_t>::max(); 3702 3703 double nVal = 0.0; 3704 ScAddress aAdr; 3705 ScRange aRange; 3706 size_t nRefInList = 0; 3707 while (nParamCount-- > 0) 3708 { 3709 switch (GetStackType()) 3710 { 3711 case svDouble : 3712 { 3713 nVal = GetDouble(); 3714 if (nMin > nVal) nMin = nVal; 3715 nFuncFmtType = SvNumFormatType::NUMBER; 3716 } 3717 break; 3718 case svSingleRef : 3719 { 3720 PopSingleRef( aAdr ); 3721 ScRefCellValue aCell(mrDoc, aAdr); 3722 if (aCell.hasNumeric()) 3723 { 3724 nVal = GetCellValue(aAdr, aCell); 3725 CurFmtToFuncFmt(); 3726 if (nMin > nVal) nMin = nVal; 3727 } 3728 else if (bTextAsZero && aCell.hasString()) 3729 { 3730 if ( nMin > 0.0 ) 3731 nMin = 0.0; 3732 } 3733 } 3734 break; 3735 case svRefList : 3736 { 3737 // bDoMatOp only for non-array value when switching to 3738 // ArrayRefList. 3739 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc, 3740 nRefArrayPos == std::numeric_limits<size_t>::max())) 3741 { 3742 nRefArrayPos = nRefInList; 3743 } 3744 } 3745 [[fallthrough]]; 3746 case svDoubleRef : 3747 { 3748 FormulaError nErr = FormulaError::NONE; 3749 PopDoubleRef( aRange, nParamCount, nRefInList); 3750 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero ); 3751 if (aValIter.GetFirst(nVal, nErr)) 3752 { 3753 if (nMin > nVal) 3754 nMin = nVal; 3755 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); 3756 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr)) 3757 { 3758 if (nMin > nVal) 3759 nMin = nVal; 3760 } 3761 SetError(nErr); 3762 } 3763 if (nRefArrayPos != std::numeric_limits<size_t>::max()) 3764 { 3765 // Update vector element with current value. 3766 MatOpFunc( nRefArrayPos, nMin); 3767 3768 // Reset. 3769 nMin = std::numeric_limits<double>::max(); 3770 nVal = 0.0; 3771 nRefArrayPos = std::numeric_limits<size_t>::max(); 3772 } 3773 } 3774 break; 3775 case svMatrix : 3776 case svExternalSingleRef: 3777 case svExternalDoubleRef: 3778 { 3779 ScMatrixRef pMat = GetMatrix(); 3780 if (pMat) 3781 { 3782 nFuncFmtType = SvNumFormatType::NUMBER; 3783 nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal)); 3784 if (nMin > nVal) 3785 nMin = nVal; 3786 } 3787 } 3788 break; 3789 case svString : 3790 { 3791 Pop(); 3792 if ( bTextAsZero ) 3793 { 3794 if ( nMin > 0.0 ) 3795 nMin = 0.0; 3796 } 3797 else 3798 SetError(FormulaError::IllegalParameter); 3799 } 3800 break; 3801 default : 3802 PopError(); 3803 SetError(FormulaError::IllegalParameter); 3804 } 3805 } 3806 3807 if (xResMat) 3808 { 3809 // Include value of last non-references-array type and calculate final result. 3810 if (nMin < std::numeric_limits<double>::max()) 3811 { 3812 for (SCSIZE i=0; i < nMatRows; ++i) 3813 { 3814 MatOpFunc( i, nMin); 3815 } 3816 } 3817 else 3818 { 3819 /* TODO: the awkward "no value is minimum 0.0" is likely the case 3820 * if a value is numeric_limits::max. Still, that could be a valid 3821 * minimum value as well, but nVal and nMin had been reset after 3822 * the last svRefList... so we may lie here. */ 3823 for (SCSIZE i=0; i < nMatRows; ++i) 3824 { 3825 double fVecRes = xResMat->GetDouble(0,i); 3826 if (fVecRes == std::numeric_limits<double>::max()) 3827 xResMat->PutDouble( 0.0, 0,i); 3828 } 3829 } 3830 PushMatrix( xResMat); 3831 } 3832 else 3833 { 3834 if (!std::isfinite(nVal)) 3835 PushError( GetDoubleErrorValue( nVal)); 3836 else if ( nVal < nMin ) 3837 PushDouble(0.0); // zero or only empty arguments 3838 else 3839 PushDouble(nMin); 3840 } 3841 } 3842 3843 void ScInterpreter::ScMax( bool bTextAsZero ) 3844 { 3845 short nParamCount = GetByte(); 3846 if (!MustHaveParamCountMin( nParamCount, 1)) 3847 return; 3848 3849 ScMatrixRef xResMat; 3850 double nMax = std::numeric_limits<double>::lowest(); 3851 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax ) 3852 { 3853 double fVecRes = xResMat->GetDouble(0,i); 3854 if (fVecRes < fCurMax) 3855 xResMat->PutDouble( fCurMax, 0,i); 3856 }; 3857 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); 3858 size_t nRefArrayPos = std::numeric_limits<size_t>::max(); 3859 3860 double nVal = 0.0; 3861 ScAddress aAdr; 3862 ScRange aRange; 3863 size_t nRefInList = 0; 3864 while (nParamCount-- > 0) 3865 { 3866 switch (GetStackType()) 3867 { 3868 case svDouble : 3869 { 3870 nVal = GetDouble(); 3871 if (nMax < nVal) nMax = nVal; 3872 nFuncFmtType = SvNumFormatType::NUMBER; 3873 } 3874 break; 3875 case svSingleRef : 3876 { 3877 PopSingleRef( aAdr ); 3878 ScRefCellValue aCell(mrDoc, aAdr); 3879 if (aCell.hasNumeric()) 3880 { 3881 nVal = GetCellValue(aAdr, aCell); 3882 CurFmtToFuncFmt(); 3883 if (nMax < nVal) nMax = nVal; 3884 } 3885 else if (bTextAsZero && aCell.hasString()) 3886 { 3887 if ( nMax < 0.0 ) 3888 nMax = 0.0; 3889 } 3890 } 3891 break; 3892 case svRefList : 3893 { 3894 // bDoMatOp only for non-array value when switching to 3895 // ArrayRefList. 3896 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc, 3897 nRefArrayPos == std::numeric_limits<size_t>::max())) 3898 { 3899 nRefArrayPos = nRefInList; 3900 } 3901 } 3902 [[fallthrough]]; 3903 case svDoubleRef : 3904 { 3905 FormulaError nErr = FormulaError::NONE; 3906 PopDoubleRef( aRange, nParamCount, nRefInList); 3907 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero ); 3908 if (aValIter.GetFirst(nVal, nErr)) 3909 { 3910 if (nMax < nVal) 3911 nMax = nVal; 3912 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); 3913 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr)) 3914 { 3915 if (nMax < nVal) 3916 nMax = nVal; 3917 } 3918 SetError(nErr); 3919 } 3920 if (nRefArrayPos != std::numeric_limits<size_t>::max()) 3921 { 3922 // Update vector element with current value. 3923 MatOpFunc( nRefArrayPos, nMax); 3924 3925 // Reset. 3926 nMax = std::numeric_limits<double>::lowest(); 3927 nVal = 0.0; 3928 nRefArrayPos = std::numeric_limits<size_t>::max(); 3929 } 3930 } 3931 break; 3932 case svMatrix : 3933 case svExternalSingleRef: 3934 case svExternalDoubleRef: 3935 { 3936 ScMatrixRef pMat = GetMatrix(); 3937 if (pMat) 3938 { 3939 nFuncFmtType = SvNumFormatType::NUMBER; 3940 nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal)); 3941 if (nMax < nVal) 3942 nMax = nVal; 3943 } 3944 } 3945 break; 3946 case svString : 3947 { 3948 Pop(); 3949 if ( bTextAsZero ) 3950 { 3951 if ( nMax < 0.0 ) 3952 nMax = 0.0; 3953 } 3954 else 3955 SetError(FormulaError::IllegalParameter); 3956 } 3957 break; 3958 default : 3959 PopError(); 3960 SetError(FormulaError::IllegalParameter); 3961 } 3962 } 3963 3964 if (xResMat) 3965 { 3966 // Include value of last non-references-array type and calculate final result. 3967 if (nMax > std::numeric_limits<double>::lowest()) 3968 { 3969 for (SCSIZE i=0; i < nMatRows; ++i) 3970 { 3971 MatOpFunc( i, nMax); 3972 } 3973 } 3974 else 3975 { 3976 /* TODO: the awkward "no value is maximum 0.0" is likely the case 3977 * if a value is numeric_limits::lowest. Still, that could be a 3978 * valid maximum value as well, but nVal and nMax had been reset 3979 * after the last svRefList... so we may lie here. */ 3980 for (SCSIZE i=0; i < nMatRows; ++i) 3981 { 3982 double fVecRes = xResMat->GetDouble(0,i); 3983 if (fVecRes == -std::numeric_limits<double>::max()) 3984 xResMat->PutDouble( 0.0, 0,i); 3985 } 3986 } 3987 PushMatrix( xResMat); 3988 } 3989 else 3990 { 3991 if (!std::isfinite(nVal)) 3992 PushError( GetDoubleErrorValue( nVal)); 3993 else if ( nVal > nMax ) 3994 PushDouble(0.0); // zero or only empty arguments 3995 else 3996 PushDouble(nMax); 3997 } 3998 } 3999 4000 void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) ) 4001 { 4002 short nParamCount = GetByte(); 4003 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); 4004 4005 struct ArrayRefListValue 4006 { 4007 std::vector<double> mvValues; 4008 KahanSum mfSum; 4009 ArrayRefListValue() = default; 4010 double get() const { return mfSum.get(); } 4011 }; 4012 std::vector<ArrayRefListValue> vArrayValues; 4013 4014 std::vector<double> values; 4015 KahanSum fSum = 0.0; 4016 double fVal = 0.0; 4017 ScAddress aAdr; 4018 ScRange aRange; 4019 size_t nRefInList = 0; 4020 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0) 4021 { 4022 switch (GetStackType()) 4023 { 4024 case svDouble : 4025 { 4026 fVal = GetDouble(); 4027 if (nGlobalError == FormulaError::NONE) 4028 { 4029 values.push_back(fVal); 4030 fSum += fVal; 4031 } 4032 } 4033 break; 4034 case svSingleRef : 4035 { 4036 PopSingleRef( aAdr ); 4037 ScRefCellValue aCell(mrDoc, aAdr); 4038 if (aCell.hasNumeric()) 4039 { 4040 fVal = GetCellValue(aAdr, aCell); 4041 if (nGlobalError == FormulaError::NONE) 4042 { 4043 values.push_back(fVal); 4044 fSum += fVal; 4045 } 4046 } 4047 else if (bTextAsZero && aCell.hasString()) 4048 { 4049 values.push_back(0.0); 4050 } 4051 } 4052 break; 4053 case svRefList : 4054 { 4055 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); 4056 if (p && p->IsArrayResult()) 4057 { 4058 size_t nRefArrayPos = nRefInList; 4059 if (vArrayValues.empty()) 4060 { 4061 // Create and init all elements with current value. 4062 assert(nMatRows > 0); 4063 vArrayValues.resize(nMatRows); 4064 for (ArrayRefListValue & it : vArrayValues) 4065 { 4066 it.mvValues = values; 4067 it.mfSum = fSum; 4068 } 4069 } 4070 else 4071 { 4072 // Current value and values from vector are operands 4073 // for each vector position. 4074 for (ArrayRefListValue & it : vArrayValues) 4075 { 4076 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end()); 4077 it.mfSum += fSum; 4078 } 4079 } 4080 ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos]; 4081 FormulaError nErr = FormulaError::NONE; 4082 PopDoubleRef( aRange, nParamCount, nRefInList); 4083 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero ); 4084 if (aValIter.GetFirst(fVal, nErr)) 4085 { 4086 do 4087 { 4088 rArrayValue.mvValues.push_back(fVal); 4089 rArrayValue.mfSum += fVal; 4090 } 4091 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr)); 4092 } 4093 if ( nErr != FormulaError::NONE ) 4094 { 4095 rArrayValue.mfSum = CreateDoubleError( nErr); 4096 } 4097 // Reset. 4098 std::vector<double>().swap(values); 4099 fSum = 0.0; 4100 break; 4101 } 4102 } 4103 [[fallthrough]]; 4104 case svDoubleRef : 4105 { 4106 FormulaError nErr = FormulaError::NONE; 4107 PopDoubleRef( aRange, nParamCount, nRefInList); 4108 ScValueIterator aValIter( mrContext, aRange, mnSubTotalFlags, bTextAsZero ); 4109 if (aValIter.GetFirst(fVal, nErr)) 4110 { 4111 do 4112 { 4113 values.push_back(fVal); 4114 fSum += fVal; 4115 } 4116 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr)); 4117 } 4118 if ( nErr != FormulaError::NONE ) 4119 { 4120 SetError(nErr); 4121 } 4122 } 4123 break; 4124 case svExternalSingleRef : 4125 case svExternalDoubleRef : 4126 case svMatrix : 4127 { 4128 ScMatrixRef pMat = GetMatrix(); 4129 if (pMat) 4130 { 4131 const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal); 4132 SCSIZE nC, nR; 4133 pMat->GetDimensions(nC, nR); 4134 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) 4135 { 4136 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) 4137 { 4138 if (!pMat->IsStringOrEmpty(nMatCol,nMatRow)) 4139 { 4140 fVal= pMat->GetDouble(nMatCol,nMatRow); 4141 if (nGlobalError == FormulaError::NONE) 4142 { 4143 values.push_back(fVal); 4144 fSum += fVal; 4145 } 4146 else if (bIgnoreErrVal) 4147 nGlobalError = FormulaError::NONE; 4148 } 4149 else if ( bTextAsZero ) 4150 { 4151 values.push_back(0.0); 4152 } 4153 } 4154 } 4155 } 4156 } 4157 break; 4158 case svString : 4159 { 4160 Pop(); 4161 if ( bTextAsZero ) 4162 { 4163 values.push_back(0.0); 4164 } 4165 else 4166 SetError(FormulaError::IllegalParameter); 4167 } 4168 break; 4169 default : 4170 PopError(); 4171 SetError(FormulaError::IllegalParameter); 4172 } 4173 } 4174 4175 if (!vArrayValues.empty()) 4176 { 4177 // Include value of last non-references-array type and calculate final result. 4178 if (!values.empty()) 4179 { 4180 for (auto & it : vArrayValues) 4181 { 4182 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end()); 4183 it.mfSum += fSum; 4184 } 4185 } 4186 ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true); 4187 for (SCSIZE r=0; r < nMatRows; ++r) 4188 { 4189 ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size(); 4190 if (!n) 4191 xResMat->PutError( FormulaError::DivisionByZero, 0, r); 4192 else 4193 { 4194 ArrayRefListValue& rArrayValue = vArrayValues[r]; 4195 double vSum = 0.0; 4196 const double vMean = rArrayValue.get() / n; 4197 for (::std::vector<double>::size_type i = 0; i < n; i++) 4198 vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) * 4199 ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean); 4200 xResMat->PutDouble( VarResult( vSum, n), 0, r); 4201 } 4202 } 4203 PushMatrix( xResMat); 4204 } 4205 else 4206 { 4207 ::std::vector<double>::size_type n = values.size(); 4208 if (!n) 4209 SetError( FormulaError::DivisionByZero); 4210 double vSum = 0.0; 4211 if (nGlobalError == FormulaError::NONE) 4212 { 4213 const double vMean = fSum.get() / n; 4214 for (::std::vector<double>::size_type i = 0; i < n; i++) 4215 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean); 4216 } 4217 PushDouble( VarResult( vSum, n)); 4218 } 4219 } 4220 4221 void ScInterpreter::ScVar( bool bTextAsZero ) 4222 { 4223 auto VarResult = []( double fVal, size_t nValCount ) 4224 { 4225 if (nValCount <= 1) 4226 return CreateDoubleError( FormulaError::DivisionByZero ); 4227 else 4228 return fVal / (nValCount - 1); 4229 }; 4230 GetStVarParams( bTextAsZero, VarResult ); 4231 } 4232 4233 void ScInterpreter::ScVarP( bool bTextAsZero ) 4234 { 4235 auto VarResult = []( double fVal, size_t nValCount ) 4236 { 4237 return sc::div( fVal, nValCount); 4238 }; 4239 GetStVarParams( bTextAsZero, VarResult ); 4240 4241 } 4242 4243 void ScInterpreter::ScStDev( bool bTextAsZero ) 4244 { 4245 auto VarResult = []( double fVal, size_t nValCount ) 4246 { 4247 if (nValCount <= 1) 4248 return CreateDoubleError( FormulaError::DivisionByZero ); 4249 else 4250 return sqrt( fVal / (nValCount - 1)); 4251 }; 4252 GetStVarParams( bTextAsZero, VarResult ); 4253 } 4254 4255 void ScInterpreter::ScStDevP( bool bTextAsZero ) 4256 { 4257 auto VarResult = []( double fVal, size_t nValCount ) 4258 { 4259 if (nValCount == 0) 4260 return CreateDoubleError( FormulaError::DivisionByZero ); 4261 else 4262 return sqrt( fVal / nValCount); 4263 }; 4264 GetStVarParams( bTextAsZero, VarResult ); 4265 4266 /* this was: PushDouble( sqrt( div( nVal, nValCount))); 4267 * 4268 * Besides that the special NAN gets lost in the call through sqrt(), 4269 * unxlngi6.pro then looped back and forth somewhere between div() and 4270 * ::rtl::math::setNan(). Tests showed that 4271 * 4272 * sqrt( div( 1, 0)); 4273 * 4274 * produced a loop, but 4275 * 4276 * double f1 = div( 1, 0); 4277 * sqrt( f1 ); 4278 * 4279 * was fine. There seems to be some compiler optimization problem. It does 4280 * not occur when compiled with debug=t. 4281 */ 4282 } 4283 4284 void ScInterpreter::ScColumns() 4285 { 4286 sal_uInt8 nParamCount = GetByte(); 4287 sal_uLong nVal = 0; 4288 SCCOL nCol1; 4289 SCROW nRow1; 4290 SCTAB nTab1; 4291 SCCOL nCol2; 4292 SCROW nRow2; 4293 SCTAB nTab2; 4294 while (nParamCount-- > 0) 4295 { 4296 switch ( GetStackType() ) 4297 { 4298 case svSingleRef: 4299 PopError(); 4300 nVal++; 4301 break; 4302 case svDoubleRef: 4303 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 4304 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * 4305 static_cast<sal_uLong>(nCol2 - nCol1 + 1); 4306 break; 4307 case svMatrix: 4308 { 4309 ScMatrixRef pMat = PopMatrix(); 4310 if (pMat) 4311 { 4312 SCSIZE nC, nR; 4313 pMat->GetDimensions(nC, nR); 4314 nVal += nC; 4315 } 4316 } 4317 break; 4318 case svExternalSingleRef: 4319 PopError(); 4320 nVal++; 4321 break; 4322 case svExternalDoubleRef: 4323 { 4324 sal_uInt16 nFileId; 4325 OUString aTabName; 4326 ScComplexRefData aRef; 4327 PopExternalDoubleRef( nFileId, aTabName, aRef); 4328 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 4329 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) * 4330 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1); 4331 } 4332 break; 4333 default: 4334 PopError(); 4335 SetError(FormulaError::IllegalParameter); 4336 } 4337 } 4338 PushDouble(static_cast<double>(nVal)); 4339 } 4340 4341 void ScInterpreter::ScRows() 4342 { 4343 sal_uInt8 nParamCount = GetByte(); 4344 sal_uLong nVal = 0; 4345 SCCOL nCol1; 4346 SCROW nRow1; 4347 SCTAB nTab1; 4348 SCCOL nCol2; 4349 SCROW nRow2; 4350 SCTAB nTab2; 4351 while (nParamCount-- > 0) 4352 { 4353 switch ( GetStackType() ) 4354 { 4355 case svSingleRef: 4356 PopError(); 4357 nVal++; 4358 break; 4359 case svDoubleRef: 4360 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 4361 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * 4362 static_cast<sal_uLong>(nRow2 - nRow1 + 1); 4363 break; 4364 case svMatrix: 4365 { 4366 ScMatrixRef pMat = PopMatrix(); 4367 if (pMat) 4368 { 4369 SCSIZE nC, nR; 4370 pMat->GetDimensions(nC, nR); 4371 nVal += nR; 4372 } 4373 } 4374 break; 4375 case svExternalSingleRef: 4376 PopError(); 4377 nVal++; 4378 break; 4379 case svExternalDoubleRef: 4380 { 4381 sal_uInt16 nFileId; 4382 OUString aTabName; 4383 ScComplexRefData aRef; 4384 PopExternalDoubleRef( nFileId, aTabName, aRef); 4385 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 4386 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) * 4387 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1); 4388 } 4389 break; 4390 default: 4391 PopError(); 4392 SetError(FormulaError::IllegalParameter); 4393 } 4394 } 4395 PushDouble(static_cast<double>(nVal)); 4396 } 4397 4398 void ScInterpreter::ScSheets() 4399 { 4400 sal_uInt8 nParamCount = GetByte(); 4401 sal_uLong nVal; 4402 if ( nParamCount == 0 ) 4403 nVal = mrDoc.GetTableCount(); 4404 else 4405 { 4406 nVal = 0; 4407 SCCOL nCol1; 4408 SCROW nRow1; 4409 SCTAB nTab1; 4410 SCCOL nCol2; 4411 SCROW nRow2; 4412 SCTAB nTab2; 4413 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0) 4414 { 4415 switch ( GetStackType() ) 4416 { 4417 case svSingleRef: 4418 case svExternalSingleRef: 4419 PopError(); 4420 nVal++; 4421 break; 4422 case svDoubleRef: 4423 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 4424 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1); 4425 break; 4426 case svExternalDoubleRef: 4427 { 4428 sal_uInt16 nFileId; 4429 OUString aTabName; 4430 ScComplexRefData aRef; 4431 PopExternalDoubleRef( nFileId, aTabName, aRef); 4432 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 4433 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1); 4434 } 4435 break; 4436 default: 4437 PopError(); 4438 SetError( FormulaError::IllegalParameter ); 4439 } 4440 } 4441 } 4442 PushDouble( static_cast<double>(nVal) ); 4443 } 4444 4445 void ScInterpreter::ScColumn() 4446 { 4447 sal_uInt8 nParamCount = GetByte(); 4448 if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) 4449 return; 4450 4451 double nVal = 0.0; 4452 if (nParamCount == 0) 4453 { 4454 nVal = aPos.Col() + 1; 4455 if (bMatrixFormula) 4456 { 4457 SCCOL nCols = 0; 4458 SCROW nRows = 0; 4459 if (pMyFormulaCell) 4460 pMyFormulaCell->GetMatColsRows( nCols, nRows); 4461 bool bMayBeScalar; 4462 if (nCols == 0) 4463 { 4464 // Happens if called via ScViewFunc::EnterMatrix() 4465 // ScFormulaCell::GetResultDimensions() as of course a 4466 // matrix result is not available yet. 4467 nCols = 1; 4468 bMayBeScalar = false; 4469 } 4470 else 4471 { 4472 bMayBeScalar = true; 4473 } 4474 if (!bMayBeScalar || nCols != 1 || nRows != 1) 4475 { 4476 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true ); 4477 if (pResMat) 4478 { 4479 for (SCCOL i=0; i < nCols; ++i) 4480 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0); 4481 PushMatrix( pResMat); 4482 return; 4483 } 4484 } 4485 } 4486 } 4487 else 4488 { 4489 switch ( GetStackType() ) 4490 { 4491 case svSingleRef : 4492 { 4493 SCCOL nCol1(0); 4494 SCROW nRow1(0); 4495 SCTAB nTab1(0); 4496 PopSingleRef( nCol1, nRow1, nTab1 ); 4497 nVal = static_cast<double>(nCol1 + 1); 4498 } 4499 break; 4500 case svExternalSingleRef : 4501 { 4502 sal_uInt16 nFileId; 4503 OUString aTabName; 4504 ScSingleRefData aRef; 4505 PopExternalSingleRef( nFileId, aTabName, aRef ); 4506 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); 4507 nVal = static_cast<double>( aAbsRef.Col() + 1 ); 4508 } 4509 break; 4510 4511 case svDoubleRef : 4512 case svExternalDoubleRef : 4513 { 4514 SCCOL nCol1; 4515 SCCOL nCol2; 4516 if ( GetStackType() == svDoubleRef ) 4517 { 4518 SCROW nRow1; 4519 SCTAB nTab1; 4520 SCROW nRow2; 4521 SCTAB nTab2; 4522 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 4523 } 4524 else 4525 { 4526 sal_uInt16 nFileId; 4527 OUString aTabName; 4528 ScComplexRefData aRef; 4529 PopExternalDoubleRef( nFileId, aTabName, aRef ); 4530 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 4531 nCol1 = aAbs.aStart.Col(); 4532 nCol2 = aAbs.aEnd.Col(); 4533 } 4534 if (nCol2 > nCol1) 4535 { 4536 ScMatrixRef pResMat = GetNewMat( 4537 static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true); 4538 if (pResMat) 4539 { 4540 for (SCCOL i = nCol1; i <= nCol2; i++) 4541 pResMat->PutDouble(static_cast<double>(i+1), 4542 static_cast<SCSIZE>(i-nCol1), 0); 4543 PushMatrix(pResMat); 4544 return; 4545 } 4546 } 4547 else 4548 nVal = static_cast<double>(nCol1 + 1); 4549 } 4550 break; 4551 default: 4552 SetError( FormulaError::IllegalParameter ); 4553 } 4554 } 4555 PushDouble( nVal ); 4556 } 4557 4558 void ScInterpreter::ScRow() 4559 { 4560 sal_uInt8 nParamCount = GetByte(); 4561 if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) 4562 return; 4563 4564 double nVal = 0.0; 4565 if (nParamCount == 0) 4566 { 4567 nVal = aPos.Row() + 1; 4568 if (bMatrixFormula) 4569 { 4570 SCCOL nCols = 0; 4571 SCROW nRows = 0; 4572 if (pMyFormulaCell) 4573 pMyFormulaCell->GetMatColsRows( nCols, nRows); 4574 bool bMayBeScalar; 4575 if (nRows == 0) 4576 { 4577 // Happens if called via ScViewFunc::EnterMatrix() 4578 // ScFormulaCell::GetResultDimensions() as of course a 4579 // matrix result is not available yet. 4580 nRows = 1; 4581 bMayBeScalar = false; 4582 } 4583 else 4584 { 4585 bMayBeScalar = true; 4586 } 4587 if (!bMayBeScalar || nCols != 1 || nRows != 1) 4588 { 4589 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true); 4590 if (pResMat) 4591 { 4592 for (SCROW i=0; i < nRows; i++) 4593 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i)); 4594 PushMatrix( pResMat); 4595 return; 4596 } 4597 } 4598 } 4599 } 4600 else 4601 { 4602 switch ( GetStackType() ) 4603 { 4604 case svSingleRef : 4605 { 4606 SCCOL nCol1(0); 4607 SCROW nRow1(0); 4608 SCTAB nTab1(0); 4609 PopSingleRef( nCol1, nRow1, nTab1 ); 4610 nVal = static_cast<double>(nRow1 + 1); 4611 } 4612 break; 4613 case svExternalSingleRef : 4614 { 4615 sal_uInt16 nFileId; 4616 OUString aTabName; 4617 ScSingleRefData aRef; 4618 PopExternalSingleRef( nFileId, aTabName, aRef ); 4619 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); 4620 nVal = static_cast<double>( aAbsRef.Row() + 1 ); 4621 } 4622 break; 4623 case svDoubleRef : 4624 case svExternalDoubleRef : 4625 { 4626 SCROW nRow1; 4627 SCROW nRow2; 4628 if ( GetStackType() == svDoubleRef ) 4629 { 4630 SCCOL nCol1; 4631 SCTAB nTab1; 4632 SCCOL nCol2; 4633 SCTAB nTab2; 4634 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 4635 } 4636 else 4637 { 4638 sal_uInt16 nFileId; 4639 OUString aTabName; 4640 ScComplexRefData aRef; 4641 PopExternalDoubleRef( nFileId, aTabName, aRef ); 4642 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 4643 nRow1 = aAbs.aStart.Row(); 4644 nRow2 = aAbs.aEnd.Row(); 4645 } 4646 if (nRow2 > nRow1) 4647 { 4648 ScMatrixRef pResMat = GetNewMat( 1, 4649 static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true); 4650 if (pResMat) 4651 { 4652 for (SCROW i = nRow1; i <= nRow2; i++) 4653 pResMat->PutDouble(static_cast<double>(i+1), 0, 4654 static_cast<SCSIZE>(i-nRow1)); 4655 PushMatrix(pResMat); 4656 return; 4657 } 4658 } 4659 else 4660 nVal = static_cast<double>(nRow1 + 1); 4661 } 4662 break; 4663 default: 4664 SetError( FormulaError::IllegalParameter ); 4665 } 4666 } 4667 PushDouble( nVal ); 4668 } 4669 4670 void ScInterpreter::ScSheet() 4671 { 4672 sal_uInt8 nParamCount = GetByte(); 4673 if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) 4674 return; 4675 4676 SCTAB nVal = 0; 4677 if ( nParamCount == 0 ) 4678 nVal = aPos.Tab() + 1; 4679 else 4680 { 4681 switch ( GetStackType() ) 4682 { 4683 case svString : 4684 { 4685 svl::SharedString aStr = PopString(); 4686 if ( mrDoc.GetTable(aStr.getString(), nVal)) 4687 ++nVal; 4688 else 4689 SetError( FormulaError::IllegalArgument ); 4690 } 4691 break; 4692 case svSingleRef : 4693 { 4694 SCCOL nCol1(0); 4695 SCROW nRow1(0); 4696 SCTAB nTab1(0); 4697 PopSingleRef(nCol1, nRow1, nTab1); 4698 nVal = nTab1 + 1; 4699 } 4700 break; 4701 case svDoubleRef : 4702 { 4703 SCCOL nCol1; 4704 SCROW nRow1; 4705 SCTAB nTab1; 4706 SCCOL nCol2; 4707 SCROW nRow2; 4708 SCTAB nTab2; 4709 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 4710 nVal = nTab1 + 1; 4711 } 4712 break; 4713 default: 4714 SetError( FormulaError::IllegalParameter ); 4715 } 4716 if ( nGlobalError != FormulaError::NONE ) 4717 nVal = 0; 4718 } 4719 PushDouble( static_cast<double>(nVal) ); 4720 } 4721 4722 namespace { 4723 4724 class VectorMatrixAccessor 4725 { 4726 public: 4727 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) : 4728 mrMat(rMat), mbColVec(bColVec) {} 4729 4730 bool IsEmpty(SCSIZE i) const 4731 { 4732 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0); 4733 } 4734 4735 bool IsEmptyPath(SCSIZE i) const 4736 { 4737 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0); 4738 } 4739 4740 bool IsValue(SCSIZE i) const 4741 { 4742 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0); 4743 } 4744 4745 bool IsStringOrEmpty(SCSIZE i) const 4746 { 4747 return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0); 4748 } 4749 4750 double GetDouble(SCSIZE i) const 4751 { 4752 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0); 4753 } 4754 4755 OUString GetString(SCSIZE i) const 4756 { 4757 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString(); 4758 } 4759 4760 SCSIZE GetElementCount() const 4761 { 4762 SCSIZE nC, nR; 4763 mrMat.GetDimensions(nC, nR); 4764 return mbColVec ? nR : nC; 4765 } 4766 4767 private: 4768 const ScMatrix& mrMat; 4769 bool mbColVec; 4770 }; 4771 4772 /** returns -1 when the matrix value is smaller than the query value, 0 when 4773 they are equal, and 1 when the matrix value is larger than the query 4774 value. */ 4775 sal_Int32 lcl_CompareMatrix2Query( 4776 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry) 4777 { 4778 if (rMat.IsEmpty(i)) 4779 { 4780 /* TODO: in case we introduced query for real empty this would have to 4781 * be changed! */ 4782 return -1; // empty always less than anything else 4783 } 4784 4785 /* FIXME: what is an empty path (result of IF(false;true_path) in 4786 * comparisons? */ 4787 4788 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString; 4789 if (rMat.IsValue(i)) 4790 { 4791 const double nVal1 = rMat.GetDouble(i); 4792 if (!std::isfinite(nVal1)) 4793 { 4794 // XXX Querying for error values is not required, otherwise we'd 4795 // need to check here. 4796 return 1; // error always greater than numeric or string 4797 } 4798 4799 if (bByString) 4800 return -1; // numeric always less than string 4801 4802 const double nVal2 = rEntry.GetQueryItem().mfVal; 4803 // XXX Querying for error values is not required, otherwise we'd need 4804 // to check here and move that check before the bByString check. 4805 if (nVal1 == nVal2) 4806 return 0; 4807 4808 return nVal1 < nVal2 ? -1 : 1; 4809 } 4810 4811 if (!bByString) 4812 return 1; // string always greater than numeric 4813 4814 OUString aStr1 = rMat.GetString(i); 4815 OUString aStr2 = rEntry.GetQueryItem().maString.getString(); 4816 4817 return ScGlobal::GetCollator().compareString(aStr1, aStr2); // case-insensitive 4818 } 4819 4820 /** returns the last item with the identical value as the original item 4821 value. */ 4822 void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat, 4823 SCSIZE nMatCount) 4824 { 4825 if (rMat.IsValue(rIndex)) 4826 { 4827 double nVal = rMat.GetDouble(rIndex); 4828 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) && 4829 nVal == rMat.GetDouble(rIndex+1)) 4830 ++rIndex; 4831 } 4832 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant! 4833 else if (rMat.IsEmptyPath(rIndex)) 4834 { 4835 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1)) 4836 ++rIndex; 4837 } 4838 else if (rMat.IsEmpty(rIndex)) 4839 { 4840 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1)) 4841 ++rIndex; 4842 } 4843 else if (rMat.IsStringOrEmpty(rIndex)) 4844 { 4845 OUString aStr( rMat.GetString(rIndex)); 4846 while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) && 4847 aStr == rMat.GetString(rIndex+1)) 4848 ++rIndex; 4849 } 4850 else 4851 { 4852 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type"); 4853 } 4854 } 4855 4856 } 4857 4858 void ScInterpreter::ScMatch() 4859 { 4860 4861 sal_uInt8 nParamCount = GetByte(); 4862 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 4863 return; 4864 4865 double fTyp; 4866 if (nParamCount == 3) 4867 fTyp = GetDouble(); 4868 else 4869 fTyp = 1.0; 4870 SCCOL nCol1 = 0; 4871 SCROW nRow1 = 0; 4872 SCTAB nTab1 = 0; 4873 SCCOL nCol2 = 0; 4874 SCROW nRow2 = 0; 4875 ScMatrixRef pMatSrc = nullptr; 4876 4877 switch (GetStackType()) 4878 { 4879 case svSingleRef: 4880 PopSingleRef( nCol1, nRow1, nTab1); 4881 nCol2 = nCol1; 4882 nRow2 = nRow1; 4883 break; 4884 case svDoubleRef: 4885 { 4886 SCTAB nTab2 = 0; 4887 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 4888 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2)) 4889 { 4890 PushIllegalParameter(); 4891 return; 4892 } 4893 } 4894 break; 4895 case svMatrix: 4896 case svExternalDoubleRef: 4897 { 4898 if (GetStackType() == svMatrix) 4899 pMatSrc = PopMatrix(); 4900 else 4901 PopExternalDoubleRef(pMatSrc); 4902 4903 if (!pMatSrc) 4904 { 4905 PushIllegalParameter(); 4906 return; 4907 } 4908 } 4909 break; 4910 default: 4911 PushIllegalParameter(); 4912 return; 4913 } 4914 4915 if (nGlobalError == FormulaError::NONE) 4916 { 4917 double fVal; 4918 ScQueryParam rParam; 4919 rParam.nCol1 = nCol1; 4920 rParam.nRow1 = nRow1; 4921 rParam.nCol2 = nCol2; 4922 rParam.nTab = nTab1; 4923 const ScComplexRefData* refData = nullptr; 4924 4925 ScQueryEntry& rEntry = rParam.GetEntry(0); 4926 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 4927 rEntry.bDoQuery = true; 4928 if (fTyp < 0.0) 4929 rEntry.eOp = SC_GREATER_EQUAL; 4930 else if (fTyp > 0.0) 4931 rEntry.eOp = SC_LESS_EQUAL; 4932 switch ( GetStackType() ) 4933 { 4934 case svDouble: 4935 { 4936 fVal = GetDouble(); 4937 rItem.mfVal = fVal; 4938 rItem.meType = ScQueryEntry::ByValue; 4939 } 4940 break; 4941 case svString: 4942 { 4943 rItem.meType = ScQueryEntry::ByString; 4944 rItem.maString = GetString(); 4945 } 4946 break; 4947 case svDoubleRef : 4948 refData = GetStackDoubleRef(); 4949 [[fallthrough]]; 4950 case svSingleRef : 4951 { 4952 ScAddress aAdr; 4953 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 4954 { 4955 PushInt(0); 4956 return ; 4957 } 4958 ScRefCellValue aCell(mrDoc, aAdr); 4959 if (aCell.hasNumeric()) 4960 { 4961 fVal = GetCellValue(aAdr, aCell); 4962 rItem.meType = ScQueryEntry::ByValue; 4963 rItem.mfVal = fVal; 4964 } 4965 else 4966 { 4967 GetCellString(rItem.maString, aCell); 4968 rItem.meType = ScQueryEntry::ByString; 4969 } 4970 } 4971 break; 4972 case svExternalSingleRef: 4973 { 4974 ScExternalRefCache::TokenRef pToken; 4975 PopExternalSingleRef(pToken); 4976 if (nGlobalError != FormulaError::NONE) 4977 { 4978 PushError( nGlobalError); 4979 return; 4980 } 4981 if (pToken->GetType() == svDouble) 4982 { 4983 rItem.meType = ScQueryEntry::ByValue; 4984 rItem.mfVal = pToken->GetDouble(); 4985 } 4986 else 4987 { 4988 rItem.meType = ScQueryEntry::ByString; 4989 rItem.maString = pToken->GetString(); 4990 } 4991 } 4992 break; 4993 case svExternalDoubleRef: 4994 case svMatrix : 4995 { 4996 svl::SharedString aStr; 4997 ScMatValType nType = GetDoubleOrStringFromMatrix( 4998 rItem.mfVal, aStr); 4999 rItem.maString = aStr; 5000 rItem.meType = ScMatrix::IsNonValueType(nType) ? 5001 ScQueryEntry::ByString : ScQueryEntry::ByValue; 5002 } 5003 break; 5004 default: 5005 { 5006 PushIllegalParameter(); 5007 return; 5008 } 5009 } 5010 if (rItem.meType == ScQueryEntry::ByString) 5011 { 5012 bool bIsVBAMode = mrDoc.IsInVBAMode(); 5013 5014 if ( bIsVBAMode ) 5015 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard; 5016 else 5017 rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc); 5018 } 5019 5020 if (pMatSrc) // The source data is matrix array. 5021 { 5022 SCSIZE nC, nR; 5023 pMatSrc->GetDimensions( nC, nR); 5024 if (nC > 1 && nR > 1) 5025 { 5026 // The source matrix must be a vector. 5027 PushIllegalParameter(); 5028 return; 5029 } 5030 5031 // Do not propagate errors from matrix while searching. 5032 pMatSrc->SetErrorInterpreter( nullptr); 5033 5034 SCSIZE nMatCount = (nC == 1) ? nR : nC; 5035 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1); 5036 5037 // simple serial search for equality mode (source data doesn't 5038 // need to be sorted). 5039 5040 if (rEntry.eOp == SC_EQUAL) 5041 { 5042 for (SCSIZE i = 0; i < nMatCount; ++i) 5043 { 5044 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0) 5045 { 5046 PushDouble(i+1); // found ! 5047 return; 5048 } 5049 } 5050 PushNA(); // not found 5051 return; 5052 } 5053 5054 // binary search for non-equality mode (the source data is 5055 // assumed to be sorted). 5056 5057 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL); 5058 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0; 5059 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) 5060 { 5061 SCSIZE nMid = nFirst + nLen/2; 5062 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry); 5063 if (nCmp == 0) 5064 { 5065 // exact match. find the last item with the same value. 5066 lcl_GetLastMatch( nMid, aMatAcc, nMatCount); 5067 PushDouble( nMid+1); 5068 return; 5069 } 5070 5071 if (nLen == 1) // first and last items are next to each other. 5072 { 5073 if (nCmp < 0) 5074 nHitIndex = bAscOrder ? nLast : nFirst; 5075 else 5076 nHitIndex = bAscOrder ? nFirst : nLast; 5077 break; 5078 } 5079 5080 if (nCmp < 0) 5081 { 5082 if (bAscOrder) 5083 nFirst = nMid; 5084 else 5085 nLast = nMid; 5086 } 5087 else 5088 { 5089 if (bAscOrder) 5090 nLast = nMid; 5091 else 5092 nFirst = nMid; 5093 } 5094 } 5095 5096 if (nHitIndex == nMatCount-1) // last item 5097 { 5098 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry); 5099 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0)) 5100 { 5101 // either the last item is an exact match or the real 5102 // hit is beyond the last item. 5103 PushDouble( nHitIndex+1); 5104 return; 5105 } 5106 } 5107 5108 if (nHitIndex > 0) // valid hit must be 2nd item or higher 5109 { 5110 if ( ! ( rItem.meType == ScQueryEntry::ByString && aMatAcc.IsValue( nHitIndex-1 ) ) && 5111 ! ( rItem.meType == ScQueryEntry::ByValue && !aMatAcc.IsValue( nHitIndex-1 ) ) ) 5112 PushDouble( nHitIndex); // non-exact match 5113 else 5114 PushNA(); 5115 return; 5116 } 5117 5118 PushNA(); 5119 return; 5120 } 5121 5122 // The source data is cell range. 5123 SCCOLROW nDelta = 0; 5124 if (nCol1 == nCol2) 5125 { // search row in column 5126 rParam.nRow2 = nRow2; 5127 rEntry.nField = nCol1; 5128 ScAddress aResultPos( nCol1, nRow1, nTab1); 5129 if (!LookupQueryWithCache( aResultPos, rParam, refData)) 5130 { 5131 PushNA(); 5132 return; 5133 } 5134 nDelta = aResultPos.Row() - nRow1; 5135 } 5136 else 5137 { // search column in row 5138 SCCOL nC; 5139 rParam.bByRow = false; 5140 rParam.nRow2 = nRow1; 5141 rEntry.nField = nCol1; 5142 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false); 5143 // Advance Entry.nField in Iterator if column changed 5144 aCellIter.SetAdvanceQueryParamEntryField( true ); 5145 if (fTyp == 0.0) 5146 { // EQUAL 5147 if ( aCellIter.GetFirst() ) 5148 nC = aCellIter.GetCol(); 5149 else 5150 { 5151 PushNA(); 5152 return; 5153 } 5154 } 5155 else 5156 { // <= or >= 5157 SCROW nR; 5158 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) ) 5159 { 5160 PushNA(); 5161 return; 5162 } 5163 } 5164 nDelta = nC - nCol1; 5165 } 5166 PushDouble(static_cast<double>(nDelta + 1)); 5167 } 5168 else 5169 PushIllegalParameter(); 5170 } 5171 5172 namespace { 5173 5174 bool isCellContentEmpty( const ScRefCellValue& rCell ) 5175 { 5176 switch (rCell.getType()) 5177 { 5178 case CELLTYPE_VALUE: 5179 case CELLTYPE_STRING: 5180 case CELLTYPE_EDIT: 5181 return false; 5182 case CELLTYPE_FORMULA: 5183 { 5184 // NOTE: Excel treats ="" in a referenced cell as blank in 5185 // COUNTBLANK() but not in ISBLANK(), which is inconsistent. 5186 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests 5187 // the cell content. 5188 // ODFF allows both for COUNTBLANK(). 5189 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in 5190 // COUNTBLANK(), we now do for Excel interoperability. 5191 /* TODO: introduce yet another compatibility option? */ 5192 sc::FormulaResultValue aRes = rCell.getFormula()->GetResult(); 5193 if (aRes.meType != sc::FormulaResultValue::String) 5194 return false; 5195 if (!aRes.maString.isEmpty()) 5196 return false; 5197 } 5198 break; 5199 default: 5200 ; 5201 } 5202 5203 return true; 5204 } 5205 5206 } 5207 5208 void ScInterpreter::ScCountEmptyCells() 5209 { 5210 if ( !MustHaveParamCount( GetByte(), 1 ) ) 5211 return; 5212 5213 const SCSIZE nMatRows = GetRefListArrayMaxSize(1); 5214 // There's either one RefList and nothing else, or none. 5215 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr); 5216 sal_uLong nMaxCount = 0, nCount = 0; 5217 switch (GetStackType()) 5218 { 5219 case svSingleRef : 5220 { 5221 nMaxCount = 1; 5222 ScAddress aAdr; 5223 PopSingleRef( aAdr ); 5224 ScRefCellValue aCell(mrDoc, aAdr); 5225 if (!isCellContentEmpty(aCell)) 5226 nCount = 1; 5227 } 5228 break; 5229 case svRefList : 5230 case svDoubleRef : 5231 { 5232 ScRange aRange; 5233 short nParam = 1; 5234 SCSIZE nRefListArrayPos = 0; 5235 size_t nRefInList = 0; 5236 while (nParam-- > 0) 5237 { 5238 nRefListArrayPos = nRefInList; 5239 PopDoubleRef( aRange, nParam, nRefInList); 5240 nMaxCount += 5241 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) * 5242 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) * 5243 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1); 5244 5245 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags); 5246 for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) 5247 { 5248 const ScRefCellValue& rCell = aIter.getRefCellValue(); 5249 if (!isCellContentEmpty(rCell)) 5250 ++nCount; 5251 } 5252 if (xResMat) 5253 { 5254 xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos); 5255 nMaxCount = nCount = 0; 5256 } 5257 } 5258 } 5259 break; 5260 case svMatrix: 5261 case svExternalSingleRef: 5262 case svExternalDoubleRef: 5263 { 5264 ScMatrixRef xMat = GetMatrix(); 5265 if (!xMat) 5266 SetError( FormulaError::IllegalParameter); 5267 else 5268 { 5269 SCSIZE nC, nR; 5270 xMat->GetDimensions( nC, nR); 5271 nMaxCount = nC * nR; 5272 // Numbers (implicit), strings and error values, ignore empty 5273 // strings as those if not entered in an inline array are the 5274 // result of a formula, to be par with a reference to formula 5275 // cell as *visual* blank, see isCellContentEmpty() above. 5276 nCount = xMat->Count( true, true, true); 5277 } 5278 } 5279 break; 5280 default : SetError(FormulaError::IllegalParameter); break; 5281 } 5282 if (xResMat) 5283 PushMatrix( xResMat); 5284 else 5285 PushDouble(nMaxCount - nCount); 5286 } 5287 5288 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc ) 5289 { 5290 sal_uInt8 nParamCount = GetByte(); 5291 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 5292 return; 5293 5294 SCCOL nCol3 = 0; 5295 SCROW nRow3 = 0; 5296 SCTAB nTab3 = 0; 5297 5298 ScMatrixRef pSumExtraMatrix; 5299 bool bSumExtraRange = (nParamCount == 3); 5300 if (bSumExtraRange) 5301 { 5302 // Save only the upperleft cell in case of cell range. The geometry 5303 // of the 3rd parameter is taken from the 1st parameter. 5304 5305 switch ( GetStackType() ) 5306 { 5307 case svDoubleRef : 5308 { 5309 SCCOL nColJunk = 0; 5310 SCROW nRowJunk = 0; 5311 SCTAB nTabJunk = 0; 5312 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk ); 5313 if ( nTabJunk != nTab3 ) 5314 { 5315 PushError( FormulaError::IllegalParameter); 5316 return; 5317 } 5318 } 5319 break; 5320 case svSingleRef : 5321 PopSingleRef( nCol3, nRow3, nTab3 ); 5322 break; 5323 case svMatrix: 5324 pSumExtraMatrix = PopMatrix(); 5325 // nCol3, nRow3, nTab3 remain 0 5326 break; 5327 case svExternalSingleRef: 5328 { 5329 pSumExtraMatrix = GetNewMat(1,1); 5330 ScExternalRefCache::TokenRef pToken; 5331 PopExternalSingleRef(pToken); 5332 if (nGlobalError != FormulaError::NONE) 5333 { 5334 PushError( nGlobalError); 5335 return; 5336 } 5337 5338 if (pToken->GetType() == svDouble) 5339 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0); 5340 else 5341 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0); 5342 } 5343 break; 5344 case svExternalDoubleRef: 5345 PopExternalDoubleRef(pSumExtraMatrix); 5346 break; 5347 default: 5348 PushError( FormulaError::IllegalParameter); 5349 return; 5350 } 5351 } 5352 5353 svl::SharedString aString; 5354 double fVal = 0.0; 5355 bool bIsString = true; 5356 switch ( GetStackType() ) 5357 { 5358 case svDoubleRef : 5359 case svSingleRef : 5360 { 5361 ScAddress aAdr; 5362 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 5363 { 5364 PushError( nGlobalError); 5365 return; 5366 } 5367 5368 ScRefCellValue aCell(mrDoc, aAdr); 5369 switch (aCell.getType()) 5370 { 5371 case CELLTYPE_VALUE : 5372 fVal = GetCellValue(aAdr, aCell); 5373 bIsString = false; 5374 break; 5375 case CELLTYPE_FORMULA : 5376 if (aCell.getFormula()->IsValue()) 5377 { 5378 fVal = GetCellValue(aAdr, aCell); 5379 bIsString = false; 5380 } 5381 else 5382 GetCellString(aString, aCell); 5383 break; 5384 case CELLTYPE_STRING : 5385 case CELLTYPE_EDIT : 5386 GetCellString(aString, aCell); 5387 break; 5388 default: 5389 fVal = 0.0; 5390 bIsString = false; 5391 } 5392 } 5393 break; 5394 case svString: 5395 aString = GetString(); 5396 break; 5397 case svMatrix : 5398 case svExternalDoubleRef: 5399 { 5400 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString); 5401 bIsString = ScMatrix::IsRealStringType( nType); 5402 } 5403 break; 5404 case svExternalSingleRef: 5405 { 5406 ScExternalRefCache::TokenRef pToken; 5407 PopExternalSingleRef(pToken); 5408 if (nGlobalError == FormulaError::NONE) 5409 { 5410 if (pToken->GetType() == svDouble) 5411 { 5412 fVal = pToken->GetDouble(); 5413 bIsString = false; 5414 } 5415 else 5416 aString = pToken->GetString(); 5417 } 5418 } 5419 break; 5420 default: 5421 { 5422 fVal = GetDouble(); 5423 bIsString = false; 5424 } 5425 } 5426 5427 KahanSum fSum = 0.0; 5428 double fRes = 0.0; 5429 double fCount = 0.0; 5430 short nParam = 1; 5431 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam); 5432 // There's either one RefList and nothing else, or none. 5433 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr); 5434 SCSIZE nRefListArrayPos = 0; 5435 size_t nRefInList = 0; 5436 while (nParam-- > 0) 5437 { 5438 SCCOL nCol1 = 0; 5439 SCROW nRow1 = 0; 5440 SCTAB nTab1 = 0; 5441 SCCOL nCol2 = 0; 5442 SCROW nRow2 = 0; 5443 SCTAB nTab2 = 0; 5444 ScMatrixRef pQueryMatrix; 5445 switch ( GetStackType() ) 5446 { 5447 case svRefList : 5448 if (bSumExtraRange) 5449 { 5450 /* TODO: this could resolve if all refs are of the same size */ 5451 SetError( FormulaError::IllegalParameter); 5452 } 5453 else 5454 { 5455 nRefListArrayPos = nRefInList; 5456 ScRange aRange; 5457 PopDoubleRef( aRange, nParam, nRefInList); 5458 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 5459 } 5460 break; 5461 case svDoubleRef : 5462 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 5463 break; 5464 case svSingleRef : 5465 PopSingleRef( nCol1, nRow1, nTab1 ); 5466 nCol2 = nCol1; 5467 nRow2 = nRow1; 5468 nTab2 = nTab1; 5469 break; 5470 case svMatrix: 5471 case svExternalSingleRef: 5472 case svExternalDoubleRef: 5473 { 5474 pQueryMatrix = GetMatrix(); 5475 if (!pQueryMatrix) 5476 { 5477 PushError( FormulaError::IllegalParameter); 5478 return; 5479 } 5480 nCol1 = 0; 5481 nRow1 = 0; 5482 nTab1 = 0; 5483 SCSIZE nC, nR; 5484 pQueryMatrix->GetDimensions( nC, nR); 5485 nCol2 = static_cast<SCCOL>(nC - 1); 5486 nRow2 = static_cast<SCROW>(nR - 1); 5487 nTab2 = 0; 5488 } 5489 break; 5490 default: 5491 SetError( FormulaError::IllegalParameter); 5492 } 5493 if ( nTab1 != nTab2 ) 5494 { 5495 SetError( FormulaError::IllegalParameter); 5496 } 5497 5498 if (bSumExtraRange) 5499 { 5500 // Take the range geometry of the 1st parameter and apply it to 5501 // the 3rd. If parts of the resulting range would point outside 5502 // the sheet, don't complain but silently ignore and simply cut 5503 // them away, this is what Xcl does :-/ 5504 5505 // For the cut-away part we also don't need to determine the 5506 // criteria match, so shrink the source range accordingly, 5507 // instead of the result range. 5508 SCCOL nColDelta = nCol2 - nCol1; 5509 SCROW nRowDelta = nRow2 - nRow1; 5510 SCCOL nMaxCol; 5511 SCROW nMaxRow; 5512 if (pSumExtraMatrix) 5513 { 5514 SCSIZE nC, nR; 5515 pSumExtraMatrix->GetDimensions( nC, nR); 5516 nMaxCol = static_cast<SCCOL>(nC - 1); 5517 nMaxRow = static_cast<SCROW>(nR - 1); 5518 } 5519 else 5520 { 5521 nMaxCol = mrDoc.MaxCol(); 5522 nMaxRow = mrDoc.MaxRow(); 5523 } 5524 if (nCol3 + nColDelta > nMaxCol) 5525 { 5526 SCCOL nNewDelta = nMaxCol - nCol3; 5527 nCol2 = nCol1 + nNewDelta; 5528 } 5529 5530 if (nRow3 + nRowDelta > nMaxRow) 5531 { 5532 SCROW nNewDelta = nMaxRow - nRow3; 5533 nRow2 = nRow1 + nNewDelta; 5534 } 5535 } 5536 else 5537 { 5538 nCol3 = nCol1; 5539 nRow3 = nRow1; 5540 nTab3 = nTab1; 5541 } 5542 5543 if (nGlobalError == FormulaError::NONE) 5544 { 5545 ScQueryParam rParam; 5546 rParam.nRow1 = nRow1; 5547 rParam.nRow2 = nRow2; 5548 5549 ScQueryEntry& rEntry = rParam.GetEntry(0); 5550 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 5551 rEntry.bDoQuery = true; 5552 if (!bIsString) 5553 { 5554 rItem.meType = ScQueryEntry::ByValue; 5555 rItem.mfVal = fVal; 5556 rEntry.eOp = SC_EQUAL; 5557 } 5558 else 5559 { 5560 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); 5561 if (rItem.meType == ScQueryEntry::ByString) 5562 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); 5563 } 5564 ScAddress aAdr; 5565 aAdr.SetTab( nTab3 ); 5566 rParam.nCol1 = nCol1; 5567 rParam.nCol2 = nCol2; 5568 rEntry.nField = nCol1; 5569 SCCOL nColDiff = nCol3 - nCol1; 5570 SCROW nRowDiff = nRow3 - nRow1; 5571 if (pQueryMatrix) 5572 { 5573 // Never case-sensitive. 5574 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType); 5575 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); 5576 if (nGlobalError != FormulaError::NONE || !pResultMatrix) 5577 { 5578 SetError( FormulaError::IllegalParameter); 5579 } 5580 5581 if (pSumExtraMatrix) 5582 { 5583 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 5584 { 5585 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 5586 { 5587 if (pResultMatrix->IsValue( nCol, nRow) && 5588 pResultMatrix->GetDouble( nCol, nRow)) 5589 { 5590 SCSIZE nC = nCol + nColDiff; 5591 SCSIZE nR = nRow + nRowDiff; 5592 if (pSumExtraMatrix->IsValue( nC, nR)) 5593 { 5594 fVal = pSumExtraMatrix->GetDouble( nC, nR); 5595 ++fCount; 5596 fSum += fVal; 5597 } 5598 } 5599 } 5600 } 5601 } 5602 else 5603 { 5604 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 5605 { 5606 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) 5607 { 5608 if (pResultMatrix->GetDouble( nCol, nRow)) 5609 { 5610 aAdr.SetCol( nCol + nColDiff); 5611 aAdr.SetRow( nRow + nRowDiff); 5612 ScRefCellValue aCell(mrDoc, aAdr); 5613 if (aCell.hasNumeric()) 5614 { 5615 fVal = GetCellValue(aAdr, aCell); 5616 ++fCount; 5617 fSum += fVal; 5618 } 5619 } 5620 } 5621 } 5622 } 5623 } 5624 else 5625 { 5626 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false); 5627 // Increment Entry.nField in iterator when switching to next column. 5628 aCellIter.SetAdvanceQueryParamEntryField( true ); 5629 if ( aCellIter.GetFirst() ) 5630 { 5631 if (pSumExtraMatrix) 5632 { 5633 do 5634 { 5635 SCSIZE nC = aCellIter.GetCol() + nColDiff; 5636 SCSIZE nR = aCellIter.GetRow() + nRowDiff; 5637 if (pSumExtraMatrix->IsValue( nC, nR)) 5638 { 5639 fVal = pSumExtraMatrix->GetDouble( nC, nR); 5640 ++fCount; 5641 fSum += fVal; 5642 } 5643 } while ( aCellIter.GetNext() ); 5644 } 5645 else 5646 { 5647 do 5648 { 5649 aAdr.SetCol( aCellIter.GetCol() + nColDiff); 5650 aAdr.SetRow( aCellIter.GetRow() + nRowDiff); 5651 ScRefCellValue aCell(mrDoc, aAdr); 5652 if (aCell.hasNumeric()) 5653 { 5654 fVal = GetCellValue(aAdr, aCell); 5655 ++fCount; 5656 fSum += fVal; 5657 } 5658 } while ( aCellIter.GetNext() ); 5659 } 5660 } 5661 } 5662 } 5663 else 5664 { 5665 PushError( FormulaError::IllegalParameter); 5666 return; 5667 } 5668 5669 switch( eFunc ) 5670 { 5671 case ifSUMIF: fRes = fSum.get(); break; 5672 case ifAVERAGEIF: fRes = div( fSum.get(), fCount ); break; 5673 } 5674 if (xResMat) 5675 { 5676 if (nGlobalError == FormulaError::NONE) 5677 xResMat->PutDouble( fRes, 0, nRefListArrayPos); 5678 else 5679 { 5680 xResMat->PutError( nGlobalError, 0, nRefListArrayPos); 5681 nGlobalError = FormulaError::NONE; 5682 } 5683 fRes = fCount = 0.0; 5684 fSum = 0; 5685 } 5686 } 5687 if (xResMat) 5688 PushMatrix( xResMat); 5689 else 5690 PushDouble( fRes); 5691 } 5692 5693 void ScInterpreter::ScSumIf() 5694 { 5695 IterateParametersIf( ifSUMIF); 5696 } 5697 5698 void ScInterpreter::ScAverageIf() 5699 { 5700 IterateParametersIf( ifAVERAGEIF); 5701 } 5702 5703 void ScInterpreter::ScCountIf() 5704 { 5705 if ( !MustHaveParamCount( GetByte(), 2 ) ) 5706 return; 5707 5708 svl::SharedString aString; 5709 double fVal = 0.0; 5710 bool bIsString = true; 5711 switch ( GetStackType() ) 5712 { 5713 case svDoubleRef : 5714 case svSingleRef : 5715 { 5716 ScAddress aAdr; 5717 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 5718 { 5719 PushInt(0); 5720 return ; 5721 } 5722 ScRefCellValue aCell(mrDoc, aAdr); 5723 switch (aCell.getType()) 5724 { 5725 case CELLTYPE_VALUE : 5726 fVal = GetCellValue(aAdr, aCell); 5727 bIsString = false; 5728 break; 5729 case CELLTYPE_FORMULA : 5730 if (aCell.getFormula()->IsValue()) 5731 { 5732 fVal = GetCellValue(aAdr, aCell); 5733 bIsString = false; 5734 } 5735 else 5736 GetCellString(aString, aCell); 5737 break; 5738 case CELLTYPE_STRING : 5739 case CELLTYPE_EDIT : 5740 GetCellString(aString, aCell); 5741 break; 5742 default: 5743 fVal = 0.0; 5744 bIsString = false; 5745 } 5746 } 5747 break; 5748 case svMatrix: 5749 case svExternalSingleRef: 5750 case svExternalDoubleRef: 5751 { 5752 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString); 5753 bIsString = ScMatrix::IsRealStringType( nType); 5754 } 5755 break; 5756 case svString: 5757 aString = GetString(); 5758 break; 5759 default: 5760 { 5761 fVal = GetDouble(); 5762 bIsString = false; 5763 } 5764 } 5765 double fCount = 0.0; 5766 short nParam = 1; 5767 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam); 5768 // There's either one RefList and nothing else, or none. 5769 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr); 5770 SCSIZE nRefListArrayPos = 0; 5771 size_t nRefInList = 0; 5772 while (nParam-- > 0) 5773 { 5774 SCCOL nCol1 = 0; 5775 SCROW nRow1 = 0; 5776 SCTAB nTab1 = 0; 5777 SCCOL nCol2 = 0; 5778 SCROW nRow2 = 0; 5779 SCTAB nTab2 = 0; 5780 ScMatrixRef pQueryMatrix; 5781 const ScComplexRefData* refData = nullptr; 5782 switch ( GetStackType() ) 5783 { 5784 case svRefList : 5785 nRefListArrayPos = nRefInList; 5786 [[fallthrough]]; 5787 case svDoubleRef : 5788 { 5789 refData = GetStackDoubleRef(nRefInList); 5790 ScRange aRange; 5791 PopDoubleRef( aRange, nParam, nRefInList); 5792 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 5793 } 5794 break; 5795 case svSingleRef : 5796 PopSingleRef( nCol1, nRow1, nTab1 ); 5797 nCol2 = nCol1; 5798 nRow2 = nRow1; 5799 nTab2 = nTab1; 5800 break; 5801 case svMatrix: 5802 case svExternalSingleRef: 5803 case svExternalDoubleRef: 5804 { 5805 pQueryMatrix = GetMatrix(); 5806 if (!pQueryMatrix) 5807 { 5808 PushIllegalParameter(); 5809 return; 5810 } 5811 nCol1 = 0; 5812 nRow1 = 0; 5813 nTab1 = 0; 5814 SCSIZE nC, nR; 5815 pQueryMatrix->GetDimensions( nC, nR); 5816 nCol2 = static_cast<SCCOL>(nC - 1); 5817 nRow2 = static_cast<SCROW>(nR - 1); 5818 nTab2 = 0; 5819 } 5820 break; 5821 default: 5822 PopError(); // Propagate it further 5823 PushIllegalParameter(); 5824 return ; 5825 } 5826 if ( nTab1 != nTab2 ) 5827 { 5828 PushIllegalParameter(); 5829 return; 5830 } 5831 if (nCol1 > nCol2) 5832 { 5833 PushIllegalParameter(); 5834 return; 5835 } 5836 if (nGlobalError == FormulaError::NONE) 5837 { 5838 ScQueryParam rParam; 5839 rParam.nRow1 = nRow1; 5840 rParam.nRow2 = nRow2; 5841 rParam.nTab = nTab1; 5842 5843 ScQueryEntry& rEntry = rParam.GetEntry(0); 5844 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 5845 rEntry.bDoQuery = true; 5846 if (!bIsString) 5847 { 5848 rItem.meType = ScQueryEntry::ByValue; 5849 rItem.mfVal = fVal; 5850 rEntry.eOp = SC_EQUAL; 5851 } 5852 else 5853 { 5854 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); 5855 if (rItem.meType == ScQueryEntry::ByString) 5856 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); 5857 } 5858 rParam.nCol1 = nCol1; 5859 rParam.nCol2 = nCol2; 5860 rEntry.nField = nCol1; 5861 if (pQueryMatrix) 5862 { 5863 // Never case-sensitive. 5864 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType); 5865 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); 5866 if (nGlobalError != FormulaError::NONE || !pResultMatrix) 5867 { 5868 PushIllegalParameter(); 5869 return; 5870 } 5871 5872 SCSIZE nSize = pResultMatrix->GetElementCount(); 5873 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex) 5874 { 5875 if (pResultMatrix->IsValue( nIndex) && 5876 pResultMatrix->GetDouble( nIndex)) 5877 ++fCount; 5878 } 5879 } 5880 else 5881 { 5882 if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc, rParam, nTab1, pMyFormulaCell, 5883 refData, mrContext)) 5884 { 5885 ScCountIfCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false); 5886 fCount += aCellIter.GetCount(); 5887 } 5888 else 5889 { 5890 ScCountIfCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false); 5891 fCount += aCellIter.GetCount(); 5892 } 5893 } 5894 } 5895 else 5896 { 5897 PushIllegalParameter(); 5898 return; 5899 } 5900 if (xResMat) 5901 { 5902 xResMat->PutDouble( fCount, 0, nRefListArrayPos); 5903 fCount = 0.0; 5904 } 5905 } 5906 if (xResMat) 5907 PushMatrix( xResMat); 5908 else 5909 PushDouble(fCount); 5910 } 5911 5912 void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) ) 5913 { 5914 sal_uInt8 nParamCount = GetByte(); 5915 sal_uInt8 nQueryCount = nParamCount / 2; 5916 5917 std::vector<sal_uInt8>& vConditions = mrContext.maConditions; 5918 // vConditions is cached, although it is clear'ed after every cell is interpreted, 5919 // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because 5920 // with a single InterpretTail() call it results in evaluation of all the cells in the 5921 // matrix formula. 5922 vConditions.clear(); 5923 5924 // Range-reduce optimization 5925 SCCOL nStartColDiff = 0; 5926 SCCOL nEndColDiff = 0; 5927 SCROW nStartRowDiff = 0; 5928 SCROW nEndRowDiff = 0; 5929 bool bRangeReduce = false; 5930 ScRange aMainRange; 5931 5932 bool bHasDoubleRefCriteriaRanges = true; 5933 // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs. 5934 // For COUNTIFS queries it's possible to range-reduce too, if the query is not supposed 5935 // to match empty cells (will be checked and undone later if needed), so simply treat 5936 // the first criteria range as the main range for purposes of detecting if this can be done. 5937 for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 ) 5938 { 5939 const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ]; 5940 if (pCriteriaRangeToken->GetType() != svDoubleRef ) 5941 { 5942 bHasDoubleRefCriteriaRanges = false; 5943 break; 5944 } 5945 } 5946 5947 // Probe the main range token, and try if we can shrink the range without altering results. 5948 const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ]; 5949 if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges) 5950 { 5951 const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef(); 5952 if (!pRefData->IsDeleted()) 5953 { 5954 DoubleRefToRange( *pRefData, aMainRange); 5955 if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab()) 5956 { 5957 // Shrink the range to actual data content. 5958 ScRange aSubRange = aMainRange; 5959 mrDoc.GetDataAreaSubrange(aSubRange); 5960 nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col(); 5961 nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row(); 5962 nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col(); 5963 nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row(); 5964 bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff; 5965 } 5966 } 5967 } 5968 5969 double fVal = 0.0; 5970 SCCOL nDimensionCols = 0; 5971 SCROW nDimensionRows = 0; 5972 const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount); 5973 std::vector<std::vector<sal_uInt8>> vRefArrayConditions; 5974 5975 while (nParamCount > 1 && nGlobalError == FormulaError::NONE) 5976 { 5977 // take criteria 5978 svl::SharedString aString; 5979 fVal = 0.0; 5980 bool bIsString = true; 5981 switch ( GetStackType() ) 5982 { 5983 case svDoubleRef : 5984 case svSingleRef : 5985 { 5986 ScAddress aAdr; 5987 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 5988 { 5989 PushError( nGlobalError); 5990 return; 5991 } 5992 5993 ScRefCellValue aCell(mrDoc, aAdr); 5994 switch (aCell.getType()) 5995 { 5996 case CELLTYPE_VALUE : 5997 fVal = GetCellValue(aAdr, aCell); 5998 bIsString = false; 5999 break; 6000 case CELLTYPE_FORMULA : 6001 if (aCell.getFormula()->IsValue()) 6002 { 6003 fVal = GetCellValue(aAdr, aCell); 6004 bIsString = false; 6005 } 6006 else 6007 GetCellString(aString, aCell); 6008 break; 6009 case CELLTYPE_STRING : 6010 case CELLTYPE_EDIT : 6011 GetCellString(aString, aCell); 6012 break; 6013 default: 6014 fVal = 0.0; 6015 bIsString = false; 6016 } 6017 } 6018 break; 6019 case svString: 6020 aString = GetString(); 6021 break; 6022 case svMatrix : 6023 case svExternalDoubleRef: 6024 { 6025 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString); 6026 bIsString = ScMatrix::IsRealStringType( nType); 6027 } 6028 break; 6029 case svExternalSingleRef: 6030 { 6031 ScExternalRefCache::TokenRef pToken; 6032 PopExternalSingleRef(pToken); 6033 if (nGlobalError == FormulaError::NONE) 6034 { 6035 if (pToken->GetType() == svDouble) 6036 { 6037 fVal = pToken->GetDouble(); 6038 bIsString = false; 6039 } 6040 else 6041 aString = pToken->GetString(); 6042 } 6043 } 6044 break; 6045 default: 6046 { 6047 fVal = GetDouble(); 6048 bIsString = false; 6049 } 6050 } 6051 6052 if (nGlobalError != FormulaError::NONE) 6053 { 6054 PushError( nGlobalError); 6055 return; // and bail out, no need to evaluate other arguments 6056 } 6057 6058 // take range 6059 short nParam = nParamCount; 6060 size_t nRefInList = 0; 6061 size_t nRefArrayPos = std::numeric_limits<size_t>::max(); 6062 SCCOL nCol1 = 0; 6063 SCROW nRow1 = 0; 6064 SCTAB nTab1 = 0; 6065 SCCOL nCol2 = 0; 6066 SCROW nRow2 = 0; 6067 SCTAB nTab2 = 0; 6068 ScMatrixRef pQueryMatrix; 6069 while (nParam-- == nParamCount) 6070 { 6071 const ScComplexRefData* refData = nullptr; 6072 switch ( GetStackType() ) 6073 { 6074 case svRefList : 6075 { 6076 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); 6077 if (p && p->IsArrayResult()) 6078 { 6079 if (nRefInList == 0) 6080 { 6081 if (vRefArrayConditions.empty()) 6082 vRefArrayConditions.resize( nRefArrayRows); 6083 if (!vConditions.empty()) 6084 { 6085 // Similar to other reference list array 6086 // handling, add/op the current value to 6087 // all array positions. 6088 for (auto & rVec : vRefArrayConditions) 6089 { 6090 if (rVec.empty()) 6091 rVec = vConditions; 6092 else 6093 { 6094 assert(rVec.size() == vConditions.size()); // see dimensions below 6095 for (size_t i=0, n = rVec.size(); i < n; ++i) 6096 { 6097 rVec[i] += vConditions[i]; 6098 } 6099 } 6100 } 6101 // Reset condition results. 6102 std::for_each( vConditions.begin(), vConditions.end(), 6103 [](sal_uInt8 & r){ r = 0.0; } ); 6104 } 6105 } 6106 nRefArrayPos = nRefInList; 6107 } 6108 refData = GetStackDoubleRef(nRefInList); 6109 ScRange aRange; 6110 PopDoubleRef( aRange, nParam, nRefInList); 6111 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 6112 } 6113 break; 6114 case svDoubleRef : 6115 refData = GetStackDoubleRef(); 6116 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); 6117 break; 6118 case svSingleRef : 6119 PopSingleRef( nCol1, nRow1, nTab1 ); 6120 nCol2 = nCol1; 6121 nRow2 = nRow1; 6122 nTab2 = nTab1; 6123 break; 6124 case svMatrix: 6125 case svExternalSingleRef: 6126 case svExternalDoubleRef: 6127 { 6128 pQueryMatrix = GetMatrix(); 6129 if (!pQueryMatrix) 6130 { 6131 PushError( FormulaError::IllegalParameter); 6132 return; 6133 } 6134 nCol1 = 0; 6135 nRow1 = 0; 6136 nTab1 = 0; 6137 SCSIZE nC, nR; 6138 pQueryMatrix->GetDimensions( nC, nR); 6139 nCol2 = static_cast<SCCOL>(nC - 1); 6140 nRow2 = static_cast<SCROW>(nR - 1); 6141 nTab2 = 0; 6142 } 6143 break; 6144 default: 6145 PushError( FormulaError::IllegalParameter); 6146 return; 6147 } 6148 if ( nTab1 != nTab2 ) 6149 { 6150 PushError( FormulaError::IllegalArgument); 6151 return; 6152 } 6153 6154 ScQueryParam rParam; 6155 ScQueryEntry& rEntry = rParam.GetEntry(0); 6156 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 6157 rEntry.bDoQuery = true; 6158 if (!bIsString) 6159 { 6160 rItem.meType = ScQueryEntry::ByValue; 6161 rItem.mfVal = fVal; 6162 rEntry.eOp = SC_EQUAL; 6163 } 6164 else 6165 { 6166 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); 6167 if (rItem.meType == ScQueryEntry::ByString) 6168 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); 6169 } 6170 6171 // Undo bRangeReduce if asked to match empty cells for COUNTIFS (which should be rare). 6172 assert(rEntry.GetQueryItems().size() == 1); 6173 const bool isCountIfs = (nParamCount % 2) == 0; 6174 if(isCountIfs && (rEntry.IsQueryByEmpty() || rItem.mbMatchEmpty) && bRangeReduce) 6175 { 6176 bRangeReduce = false; 6177 // All criteria ranges are svDoubleRef's, so only vConditions needs adjusting. 6178 assert(vRefArrayConditions.empty()); 6179 if(!vConditions.empty()) 6180 { 6181 std::vector<sal_uInt8> newConditions; 6182 SCCOL newDimensionCols = nCol2 - nCol1 + 1; 6183 SCROW newDimensionRows = nRow2 - nRow1 + 1; 6184 newConditions.reserve( newDimensionCols * newDimensionRows ); 6185 SCCOL col = nCol1; 6186 for(; col < nCol1 + nStartColDiff; ++col) 6187 newConditions.insert( newConditions.end(), newDimensionRows, 0 ); 6188 for(; col <= nCol2 - nStartColDiff; ++col) 6189 { 6190 newConditions.insert( newConditions.end(), nStartRowDiff, 0 ); 6191 SCCOL oldCol = col - ( nCol1 + nStartColDiff ); 6192 size_t nIndex = oldCol * nDimensionRows; 6193 if (nIndex < vConditions.size()) 6194 { 6195 auto it = vConditions.begin() + nIndex; 6196 newConditions.insert( newConditions.end(), it, it + nDimensionRows ); 6197 } 6198 else 6199 newConditions.insert( newConditions.end(), nDimensionRows, 0 ); 6200 newConditions.insert( newConditions.end(), -nEndRowDiff, 0 ); 6201 } 6202 for(; col <= nCol2; ++col) 6203 newConditions.insert( newConditions.end(), newDimensionRows, 0 ); 6204 assert( newConditions.size() == o3tl::make_unsigned( newDimensionCols * newDimensionRows )); 6205 vConditions = std::move( newConditions ); 6206 nDimensionCols = newDimensionCols; 6207 nDimensionRows = newDimensionRows; 6208 } 6209 } 6210 6211 if (bRangeReduce) 6212 { 6213 // All reference ranges must be of the same size as the main range. 6214 if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1 6215 || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1) 6216 { 6217 PushError ( FormulaError::IllegalArgument); 6218 return; 6219 } 6220 nCol1 += nStartColDiff; 6221 nRow1 += nStartRowDiff; 6222 6223 nCol2 += nEndColDiff; 6224 nRow2 += nEndRowDiff; 6225 } 6226 6227 // All reference ranges must be of same dimension and size. 6228 if (!nDimensionCols) 6229 nDimensionCols = nCol2 - nCol1 + 1; 6230 if (!nDimensionRows) 6231 nDimensionRows = nRow2 - nRow1 + 1; 6232 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1))) 6233 { 6234 PushError ( FormulaError::IllegalArgument); 6235 return; 6236 } 6237 6238 // recalculate matrix values 6239 if (nGlobalError != FormulaError::NONE) 6240 { 6241 PushError( nGlobalError); 6242 return; 6243 } 6244 6245 // initialize temporary result matrix 6246 if (vConditions.empty()) 6247 vConditions.resize( nDimensionCols * nDimensionRows, 0); 6248 6249 rParam.nRow1 = nRow1; 6250 rParam.nRow2 = nRow2; 6251 rParam.nCol1 = nCol1; 6252 rParam.nCol2 = nCol2; 6253 rEntry.nField = nCol1; 6254 SCCOL nColDiff = -nCol1; 6255 SCROW nRowDiff = -nRow1; 6256 if (pQueryMatrix) 6257 { 6258 // Never case-sensitive. 6259 sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType); 6260 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); 6261 if (nGlobalError != FormulaError::NONE || !pResultMatrix) 6262 { 6263 PushError( FormulaError::IllegalParameter); 6264 return; 6265 } 6266 6267 // result matrix is filled with boolean values. 6268 std::vector<double> aResValues; 6269 pResultMatrix->GetDoubleArray(aResValues); 6270 if (vConditions.size() != aResValues.size()) 6271 { 6272 PushError( FormulaError::IllegalParameter); 6273 return; 6274 } 6275 6276 std::vector<double>::const_iterator itThisRes = aResValues.begin(); 6277 for (auto& rCondition : vConditions) 6278 { 6279 rCondition += *itThisRes; 6280 ++itThisRes; 6281 } 6282 } 6283 else 6284 { 6285 if( ScQueryCellIteratorSortedCache::CanBeUsed( mrDoc, rParam, nTab1, pMyFormulaCell, 6286 refData, mrContext )) 6287 { 6288 ScQueryCellIteratorSortedCache aCellIter(mrDoc, mrContext, nTab1, rParam, false); 6289 // Increment Entry.nField in iterator when switching to next column. 6290 aCellIter.SetAdvanceQueryParamEntryField( true ); 6291 if ( aCellIter.GetFirst() ) 6292 { 6293 do 6294 { 6295 size_t nC = aCellIter.GetCol() + nColDiff; 6296 size_t nR = aCellIter.GetRow() + nRowDiff; 6297 ++vConditions[nC * nDimensionRows + nR]; 6298 } while ( aCellIter.GetNext() ); 6299 } 6300 } 6301 else 6302 { 6303 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, rParam, false); 6304 // Increment Entry.nField in iterator when switching to next column. 6305 aCellIter.SetAdvanceQueryParamEntryField( true ); 6306 if ( aCellIter.GetFirst() ) 6307 { 6308 do 6309 { 6310 size_t nC = aCellIter.GetCol() + nColDiff; 6311 size_t nR = aCellIter.GetRow() + nRowDiff; 6312 ++vConditions[nC * nDimensionRows + nR]; 6313 } while ( aCellIter.GetNext() ); 6314 } 6315 } 6316 } 6317 if (nRefArrayPos != std::numeric_limits<size_t>::max()) 6318 { 6319 // Apply condition result to reference list array result position. 6320 std::vector<sal_uInt8>& rVec = vRefArrayConditions[nRefArrayPos]; 6321 if (rVec.empty()) 6322 rVec = vConditions; 6323 else 6324 { 6325 assert(rVec.size() == vConditions.size()); // see dimensions above 6326 for (size_t i=0, n = rVec.size(); i < n; ++i) 6327 { 6328 rVec[i] += vConditions[i]; 6329 } 6330 } 6331 // Reset conditions vector. 6332 // When leaving an svRefList this has to be emptied not set to 6333 // 0.0 because it's checked when entering an svRefList. 6334 if (nRefInList == 0) 6335 std::vector<sal_uInt8>().swap( vConditions); 6336 else 6337 std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt8 & r){ r = 0; } ); 6338 } 6339 } 6340 nParamCount -= 2; 6341 } 6342 6343 if (!vRefArrayConditions.empty() && !vConditions.empty()) 6344 { 6345 // Add/op the last current value to all array positions. 6346 for (auto & rVec : vRefArrayConditions) 6347 { 6348 if (rVec.empty()) 6349 rVec = vConditions; 6350 else 6351 { 6352 assert(rVec.size() == vConditions.size()); // see dimensions above 6353 for (size_t i=0, n = rVec.size(); i < n; ++i) 6354 { 6355 rVec[i] += vConditions[i]; 6356 } 6357 } 6358 } 6359 } 6360 6361 if (nGlobalError != FormulaError::NONE) 6362 { 6363 PushError( nGlobalError); 6364 return; // bail out 6365 } 6366 6367 sc::ParamIfsResult aRes; 6368 ScMatrixRef xResMat; 6369 6370 // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS 6371 if (nParamCount == 1) 6372 { 6373 short nParam = nParamCount; 6374 size_t nRefInList = 0; 6375 size_t nRefArrayPos = std::numeric_limits<size_t>::max(); 6376 bool bRefArrayMain = false; 6377 while (nParam-- == nParamCount) 6378 { 6379 SCCOL nMainCol1 = 0; 6380 SCROW nMainRow1 = 0; 6381 SCTAB nMainTab1 = 0; 6382 SCCOL nMainCol2 = 0; 6383 SCROW nMainRow2 = 0; 6384 SCTAB nMainTab2 = 0; 6385 ScMatrixRef pMainMatrix; 6386 switch ( GetStackType() ) 6387 { 6388 case svRefList : 6389 { 6390 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); 6391 if (p && p->IsArrayResult()) 6392 { 6393 if (vRefArrayConditions.empty()) 6394 { 6395 // Replicate conditions if there wasn't a 6396 // reference list array for criteria 6397 // evaluation. 6398 vRefArrayConditions.resize( nRefArrayRows); 6399 for (auto & rVec : vRefArrayConditions) 6400 { 6401 rVec = vConditions; 6402 } 6403 } 6404 6405 bRefArrayMain = true; 6406 nRefArrayPos = nRefInList; 6407 } 6408 ScRange aRange; 6409 PopDoubleRef( aRange, nParam, nRefInList); 6410 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2); 6411 } 6412 break; 6413 case svDoubleRef : 6414 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 ); 6415 break; 6416 case svSingleRef : 6417 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 ); 6418 nMainCol2 = nMainCol1; 6419 nMainRow2 = nMainRow1; 6420 nMainTab2 = nMainTab1; 6421 break; 6422 case svMatrix: 6423 case svExternalSingleRef: 6424 case svExternalDoubleRef: 6425 { 6426 pMainMatrix = GetMatrix(); 6427 if (!pMainMatrix) 6428 { 6429 PushError( FormulaError::IllegalParameter); 6430 return; 6431 } 6432 nMainCol1 = 0; 6433 nMainRow1 = 0; 6434 nMainTab1 = 0; 6435 SCSIZE nC, nR; 6436 pMainMatrix->GetDimensions( nC, nR); 6437 nMainCol2 = static_cast<SCCOL>(nC - 1); 6438 nMainRow2 = static_cast<SCROW>(nR - 1); 6439 nMainTab2 = 0; 6440 } 6441 break; 6442 // Treat a scalar value as 1x1 matrix. 6443 case svDouble: 6444 pMainMatrix = GetNewMat(1,1); 6445 nMainCol1 = nMainCol2 = 0; 6446 nMainRow1 = nMainRow2 = 0; 6447 nMainTab1 = nMainTab2 = 0; 6448 pMainMatrix->PutDouble( GetDouble(), 0, 0); 6449 break; 6450 case svString: 6451 pMainMatrix = GetNewMat(1,1); 6452 nMainCol1 = nMainCol2 = 0; 6453 nMainRow1 = nMainRow2 = 0; 6454 nMainTab1 = nMainTab2 = 0; 6455 pMainMatrix->PutString( GetString(), 0, 0); 6456 break; 6457 default: 6458 PopError(); 6459 PushError( FormulaError::IllegalParameter); 6460 return; 6461 } 6462 if ( nMainTab1 != nMainTab2 ) 6463 { 6464 PushError( FormulaError::IllegalArgument); 6465 return; 6466 } 6467 6468 if (bRangeReduce) 6469 { 6470 nMainCol1 += nStartColDiff; 6471 nMainRow1 += nStartRowDiff; 6472 6473 nMainCol2 += nEndColDiff; 6474 nMainRow2 += nEndRowDiff; 6475 } 6476 6477 // All reference ranges must be of same dimension and size. 6478 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1))) 6479 { 6480 PushError ( FormulaError::IllegalArgument); 6481 return; 6482 } 6483 6484 if (nGlobalError != FormulaError::NONE) 6485 { 6486 PushError( nGlobalError); 6487 return; // bail out 6488 } 6489 6490 // end-result calculation 6491 6492 // This gets weird... if conditions were calculated using a 6493 // reference list array but the main calculation range is not a 6494 // reference list array, then the conditions of the array are 6495 // applied to the main range each in turn to form the array result. 6496 6497 size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos : 6498 (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0)); 6499 const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0); 6500 6501 if (nRefArrayMainPos == 0) 6502 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true ); 6503 6504 if (pMainMatrix) 6505 { 6506 std::vector<double> aMainValues; 6507 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's. 6508 6509 do 6510 { 6511 if (nRefArrayMainPos < vRefArrayConditions.size()) 6512 vConditions = vRefArrayConditions[nRefArrayMainPos]; 6513 6514 if (vConditions.size() != aMainValues.size()) 6515 { 6516 PushError( FormulaError::IllegalArgument); 6517 return; 6518 } 6519 6520 std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end(); 6521 std::vector<double>::const_iterator itMain = aMainValues.begin(); 6522 for (; itRes != itResEnd; ++itRes, ++itMain) 6523 { 6524 if (*itRes != nQueryCount) 6525 continue; 6526 6527 fVal = *itMain; 6528 if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN) 6529 continue; 6530 6531 ++aRes.mfCount; 6532 aRes.mfSum += fVal; 6533 if ( aRes.mfMin > fVal ) 6534 aRes.mfMin = fVal; 6535 if ( aRes.mfMax < fVal ) 6536 aRes.mfMax = fVal; 6537 } 6538 if (nRefArrayMainPos != std::numeric_limits<size_t>::max()) 6539 { 6540 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos); 6541 aRes = sc::ParamIfsResult(); 6542 } 6543 } 6544 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows); 6545 } 6546 else 6547 { 6548 ScAddress aAdr; 6549 aAdr.SetTab( nMainTab1 ); 6550 do 6551 { 6552 if (nRefArrayMainPos < vRefArrayConditions.size()) 6553 vConditions = vRefArrayConditions[nRefArrayMainPos]; 6554 6555 SAL_WARN_IF(nDimensionCols && nDimensionRows && vConditions.empty(), "sc", "ScInterpreter::IterateParametersIfs vConditions is empty"); 6556 if (!vConditions.empty()) 6557 { 6558 std::vector<sal_uInt8>::const_iterator itRes = vConditions.begin(); 6559 for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol) 6560 { 6561 for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes) 6562 { 6563 if (*itRes == nQueryCount) 6564 { 6565 aAdr.SetCol( nCol + nMainCol1); 6566 aAdr.SetRow( nRow + nMainRow1); 6567 ScRefCellValue aCell(mrDoc, aAdr); 6568 if (aCell.hasNumeric()) 6569 { 6570 fVal = GetCellValue(aAdr, aCell); 6571 ++aRes.mfCount; 6572 aRes.mfSum += fVal; 6573 if ( aRes.mfMin > fVal ) 6574 aRes.mfMin = fVal; 6575 if ( aRes.mfMax < fVal ) 6576 aRes.mfMax = fVal; 6577 } 6578 } 6579 } 6580 } 6581 } 6582 if (nRefArrayMainPos != std::numeric_limits<size_t>::max()) 6583 { 6584 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos); 6585 aRes = sc::ParamIfsResult(); 6586 } 6587 } 6588 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows); 6589 } 6590 } 6591 } 6592 else 6593 { 6594 // COUNTIFS only. 6595 if (vRefArrayConditions.empty()) 6596 { 6597 // The code below is this but optimized for most elements not matching. 6598 // for (auto const & rCond : vConditions) 6599 // if (rCond == nQueryCount) 6600 // ++aRes.mfCount; 6601 static_assert(sizeof(vConditions[0]) == 1); 6602 const sal_uInt8* pos = vConditions.data(); 6603 const sal_uInt8* end = pos + vConditions.size(); 6604 for(;;) 6605 { 6606 pos = static_cast< const sal_uInt8* >( memchr( pos, nQueryCount, end - pos )); 6607 if( pos == nullptr ) 6608 break; 6609 ++aRes.mfCount; 6610 ++pos; 6611 } 6612 } 6613 else 6614 { 6615 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true ); 6616 for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i) 6617 { 6618 double fCount = 0.0; 6619 for (auto const & rCond : vRefArrayConditions[i]) 6620 { 6621 if (rCond == nQueryCount) 6622 ++fCount; 6623 } 6624 xResMat->PutDouble( fCount, 0, i); 6625 } 6626 } 6627 } 6628 6629 if (xResMat) 6630 PushMatrix( xResMat); 6631 else 6632 PushDouble( ResultFunc( aRes)); 6633 } 6634 6635 void ScInterpreter::ScSumIfs() 6636 { 6637 // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE); 6638 sal_uInt8 nParamCount = GetByte(); 6639 6640 if (nParamCount < 3 || (nParamCount % 2 != 1)) 6641 { 6642 PushError( FormulaError::ParameterExpected); 6643 return; 6644 } 6645 6646 auto ResultFunc = []( const sc::ParamIfsResult& rRes ) 6647 { 6648 return rRes.mfSum.get(); 6649 }; 6650 IterateParametersIfs(ResultFunc); 6651 } 6652 6653 void ScInterpreter::ScAverageIfs() 6654 { 6655 sal_uInt8 nParamCount = GetByte(); 6656 6657 if (nParamCount < 3 || (nParamCount % 2 != 1)) 6658 { 6659 PushError( FormulaError::ParameterExpected); 6660 return; 6661 } 6662 6663 auto ResultFunc = []( const sc::ParamIfsResult& rRes ) 6664 { 6665 return sc::div( rRes.mfSum.get(), rRes.mfCount); 6666 }; 6667 IterateParametersIfs(ResultFunc); 6668 } 6669 6670 void ScInterpreter::ScCountIfs() 6671 { 6672 sal_uInt8 nParamCount = GetByte(); 6673 6674 if (nParamCount < 2 || (nParamCount % 2 != 0)) 6675 { 6676 PushError( FormulaError::ParameterExpected); 6677 return; 6678 } 6679 6680 auto ResultFunc = []( const sc::ParamIfsResult& rRes ) 6681 { 6682 return rRes.mfCount; 6683 }; 6684 IterateParametersIfs(ResultFunc); 6685 } 6686 6687 void ScInterpreter::ScMinIfs_MS() 6688 { 6689 sal_uInt8 nParamCount = GetByte(); 6690 6691 if (nParamCount < 3 || (nParamCount % 2 != 1)) 6692 { 6693 PushError( FormulaError::ParameterExpected); 6694 return; 6695 } 6696 6697 auto ResultFunc = []( const sc::ParamIfsResult& rRes ) 6698 { 6699 return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0; 6700 }; 6701 IterateParametersIfs(ResultFunc); 6702 } 6703 6704 6705 void ScInterpreter::ScMaxIfs_MS() 6706 { 6707 sal_uInt8 nParamCount = GetByte(); 6708 6709 if (nParamCount < 3 || (nParamCount % 2 != 1)) 6710 { 6711 PushError( FormulaError::ParameterExpected); 6712 return; 6713 } 6714 6715 auto ResultFunc = []( const sc::ParamIfsResult& rRes ) 6716 { 6717 return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0; 6718 }; 6719 IterateParametersIfs(ResultFunc); 6720 } 6721 6722 void ScInterpreter::ScLookup() 6723 { 6724 sal_uInt8 nParamCount = GetByte(); 6725 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 6726 return ; 6727 6728 ScMatrixRef pDataMat = nullptr, pResMat = nullptr; 6729 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0; 6730 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0; 6731 SCTAB nTab1 = 0, nResTab = 0; 6732 SCSIZE nLenMajor = 0; // length of major direction 6733 bool bVertical = true; // whether to lookup vertically or horizontally 6734 6735 // The third parameter, result array, double, string and reference. 6736 double fResVal = 0.0; 6737 svl::SharedString aResStr; 6738 StackVar eResArrayType = svUnknown; 6739 6740 if (nParamCount == 3) 6741 { 6742 eResArrayType = GetStackType(); 6743 switch (eResArrayType) 6744 { 6745 case svDoubleRef: 6746 { 6747 SCTAB nTabJunk; 6748 PopDoubleRef(nResCol1, nResRow1, nResTab, 6749 nResCol2, nResRow2, nTabJunk); 6750 if (nResTab != nTabJunk || 6751 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0)) 6752 { 6753 // The result array must be a vector. 6754 PushIllegalParameter(); 6755 return; 6756 } 6757 } 6758 break; 6759 case svSingleRef: 6760 PopSingleRef( nResCol1, nResRow1, nResTab); 6761 nResCol2 = nResCol1; 6762 nResRow2 = nResRow1; 6763 break; 6764 case svMatrix: 6765 case svExternalSingleRef: 6766 case svExternalDoubleRef: 6767 { 6768 pResMat = GetMatrix(); 6769 if (!pResMat) 6770 { 6771 PushIllegalParameter(); 6772 return; 6773 } 6774 SCSIZE nC, nR; 6775 pResMat->GetDimensions(nC, nR); 6776 if (nC != 1 && nR != 1) 6777 { 6778 // Result matrix must be a vector. 6779 PushIllegalParameter(); 6780 return; 6781 } 6782 } 6783 break; 6784 case svDouble: 6785 fResVal = GetDouble(); 6786 break; 6787 case svString: 6788 aResStr = GetString(); 6789 break; 6790 default: 6791 PushIllegalParameter(); 6792 return; 6793 } 6794 } 6795 6796 // For double, string and single reference. 6797 double fDataVal = 0.0; 6798 svl::SharedString aDataStr; 6799 ScAddress aDataAdr; 6800 bool bValueData = false; 6801 6802 // Get the data-result range and also determine whether this is vertical 6803 // lookup or horizontal lookup. 6804 6805 StackVar eDataArrayType = GetStackType(); 6806 switch (eDataArrayType) 6807 { 6808 case svDoubleRef: 6809 { 6810 SCTAB nTabJunk; 6811 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk); 6812 if (nTab1 != nTabJunk) 6813 { 6814 PushIllegalParameter(); 6815 return; 6816 } 6817 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1); 6818 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1; 6819 } 6820 break; 6821 case svMatrix: 6822 case svExternalSingleRef: 6823 case svExternalDoubleRef: 6824 { 6825 pDataMat = GetMatrix(); 6826 if (!pDataMat) 6827 { 6828 PushIllegalParameter(); 6829 return; 6830 } 6831 6832 SCSIZE nC, nR; 6833 pDataMat->GetDimensions(nC, nR); 6834 bVertical = (nR >= nC); 6835 nLenMajor = bVertical ? nR : nC; 6836 } 6837 break; 6838 case svDouble: 6839 { 6840 fDataVal = GetDouble(); 6841 bValueData = true; 6842 } 6843 break; 6844 case svString: 6845 { 6846 aDataStr = GetString(); 6847 } 6848 break; 6849 case svSingleRef: 6850 { 6851 PopSingleRef( aDataAdr ); 6852 ScRefCellValue aCell(mrDoc, aDataAdr); 6853 if (aCell.hasEmptyValue()) 6854 { 6855 // Empty cells aren't found anywhere, bail out early. 6856 SetError( FormulaError::NotAvailable); 6857 } 6858 else if (aCell.hasNumeric()) 6859 { 6860 fDataVal = GetCellValue(aDataAdr, aCell); 6861 bValueData = true; 6862 } 6863 else 6864 GetCellString(aDataStr, aCell); 6865 } 6866 break; 6867 default: 6868 SetError( FormulaError::IllegalParameter); 6869 } 6870 6871 if (nGlobalError != FormulaError::NONE) 6872 { 6873 PushError( nGlobalError); 6874 return; 6875 } 6876 6877 // Get the lookup value. 6878 6879 ScQueryParam aParam; 6880 ScQueryEntry& rEntry = aParam.GetEntry(0); 6881 if ( !FillEntry(rEntry) ) 6882 return; 6883 6884 if ( eDataArrayType == svDouble || eDataArrayType == svString || 6885 eDataArrayType == svSingleRef ) 6886 { 6887 // Delta position for a single value is always 0. 6888 6889 // Found if data <= query, but not if query is string and found data is 6890 // numeric or vice versa. This is how Excel does it but doesn't 6891 // document it. 6892 6893 bool bFound = false; 6894 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 6895 6896 if ( bValueData ) 6897 { 6898 if (rItem.meType == ScQueryEntry::ByString) 6899 bFound = false; 6900 else 6901 bFound = (fDataVal <= rItem.mfVal); 6902 } 6903 else 6904 { 6905 if (rItem.meType != ScQueryEntry::ByString) 6906 bFound = false; 6907 else 6908 bFound = (ScGlobal::GetCollator().compareString(aDataStr.getString(), rItem.maString.getString()) <= 0); 6909 } 6910 6911 if (!bFound) 6912 { 6913 PushNA(); 6914 return; 6915 } 6916 6917 if (pResMat) 6918 { 6919 if (pResMat->IsValue( 0, 0 )) 6920 PushDouble(pResMat->GetDouble( 0, 0 )); 6921 else 6922 PushString(pResMat->GetString(0, 0)); 6923 } 6924 else if (nParamCount == 3) 6925 { 6926 switch (eResArrayType) 6927 { 6928 case svDouble: 6929 PushDouble( fResVal ); 6930 break; 6931 case svString: 6932 PushString( aResStr ); 6933 break; 6934 case svDoubleRef: 6935 case svSingleRef: 6936 PushCellResultToken( true, ScAddress( nResCol1, nResRow1, nResTab), nullptr, nullptr); 6937 break; 6938 default: 6939 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data"); 6940 PushIllegalParameter(); 6941 } 6942 } 6943 else 6944 { 6945 switch (eDataArrayType) 6946 { 6947 case svDouble: 6948 PushDouble( fDataVal ); 6949 break; 6950 case svString: 6951 PushString( aDataStr ); 6952 break; 6953 case svSingleRef: 6954 PushCellResultToken( true, aDataAdr, nullptr, nullptr); 6955 break; 6956 default: 6957 assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data"); 6958 PushIllegalParameter(); 6959 } 6960 } 6961 return; 6962 } 6963 6964 // Now, perform the search to compute the delta position (nDelta). 6965 6966 if (pDataMat) 6967 { 6968 // Data array is given as a matrix. 6969 rEntry.bDoQuery = true; 6970 rEntry.eOp = SC_LESS_EQUAL; 6971 bool bFound = false; 6972 6973 SCSIZE nC, nR; 6974 pDataMat->GetDimensions(nC, nR); 6975 6976 // Do not propagate errors from matrix while copying to vector. 6977 pDataMat->SetErrorInterpreter( nullptr); 6978 6979 // Excel has an undocumented behaviour in that it seems to internally 6980 // sort an interim array (i.e. error values specifically #DIV/0! are 6981 // sorted to the end) or ignore error values that makes these "get last 6982 // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A) 6983 // see tdf#117016 6984 // Instead of sorting a million entries of which mostly only a bunch of 6985 // rows are filled and moving error values to the end which most are 6986 // already anyway, assume the matrix to be sorted except error values 6987 // and omit the coded DoubleError values. 6988 // Do this only for a numeric matrix (that includes errors coded as 6989 // doubles), which covers the case in question. 6990 /* TODO: it's unclear whether this really matches Excel behaviour in 6991 * all constellations or if there are cases that include unsorted error 6992 * values and thus yield arbitrary binary search results or something 6993 * different or whether there are cases where error values are also 6994 * omitted from mixed numeric/string arrays or if it's not an interim 6995 * matrix but a cell range reference instead. */ 6996 const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric()); 6997 6998 // In case of non-vector matrix, only search the first row or column. 6999 ScMatrixRef pDataMat2; 7000 std::vector<SCCOLROW> vIndex; 7001 if (bOmitErrorValues) 7002 { 7003 std::vector<double> vArray; 7004 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical); 7005 const SCSIZE nElements = aMatAcc.GetElementCount(); 7006 for (SCSIZE i=0; i < nElements; ++i) 7007 { 7008 const double fVal = aMatAcc.GetDouble(i); 7009 if (std::isfinite(fVal)) 7010 { 7011 vArray.push_back(fVal); 7012 vIndex.push_back(i); 7013 } 7014 } 7015 if (vArray.empty()) 7016 { 7017 PushNA(); 7018 return; 7019 } 7020 const size_t nElems = vArray.size(); 7021 if (nElems == nElements) 7022 { 7023 // No error value omitted, use as is. 7024 pDataMat2 = pDataMat; 7025 std::vector<SCCOLROW>().swap( vIndex); 7026 } 7027 else 7028 { 7029 nLenMajor = nElems; 7030 if (bVertical) 7031 { 7032 ScMatrixRef pTempMat = GetNewMat( 1, nElems, /*bEmpty*/true ); 7033 pTempMat->PutDoubleVector( vArray, 0, 0); 7034 pDataMat2 = pTempMat; 7035 } 7036 else 7037 { 7038 ScMatrixRef pTempMat = GetNewMat( nElems, 1, /*bEmpty*/true ); 7039 for (size_t i=0; i < nElems; ++i) 7040 pTempMat->PutDouble( vArray[i], i, 0); 7041 pDataMat2 = pTempMat; 7042 } 7043 } 7044 } 7045 else 7046 { 7047 // Just use as is with the VectorMatrixAccessor. 7048 pDataMat2 = pDataMat; 7049 } 7050 7051 // Do not propagate errors from matrix while searching. 7052 pDataMat2->SetErrorInterpreter( nullptr); 7053 7054 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical); 7055 7056 // binary search for non-equality mode (the source data is 7057 // assumed to be sorted in ascending order). 7058 7059 SCCOLROW nDelta = -1; 7060 7061 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0; 7062 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) 7063 { 7064 SCSIZE nMid = nFirst + nLen/2; 7065 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry); 7066 if (nCmp == 0) 7067 { 7068 // exact match. find the last item with the same value. 7069 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor); 7070 nDelta = nMid; 7071 bFound = true; 7072 break; 7073 } 7074 7075 if (nLen == 1) // first and last items are next to each other. 7076 { 7077 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1; 7078 // If already the 1st item is greater there's nothing found. 7079 bFound = (nDelta >= 0); 7080 break; 7081 } 7082 7083 if (nCmp < 0) 7084 nFirst = nMid; 7085 else 7086 nLast = nMid; 7087 } 7088 7089 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item 7090 { 7091 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry); 7092 if (nCmp <= 0) 7093 { 7094 // either the last item is an exact match or the real 7095 // hit is beyond the last item. 7096 nDelta += 1; 7097 bFound = true; 7098 } 7099 } 7100 else if (nDelta > 0) // valid hit must be 2nd item or higher 7101 { 7102 // non-exact match 7103 bFound = true; 7104 } 7105 7106 // With 0-9 < A-Z, if query is numeric and data found is string, or 7107 // vice versa, the (yet another undocumented) Excel behavior is to 7108 // return #N/A instead. 7109 7110 if (bFound) 7111 { 7112 if (!vIndex.empty()) 7113 nDelta = vIndex[nDelta]; 7114 7115 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical); 7116 SCCOLROW i = nDelta; 7117 SCSIZE n = aMatAcc.GetElementCount(); 7118 if (o3tl::make_unsigned(i) >= n) 7119 i = static_cast<SCCOLROW>(n); 7120 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString; 7121 if (bByString == aMatAcc.IsValue(i)) 7122 bFound = false; 7123 } 7124 7125 if (!bFound) 7126 { 7127 PushNA(); 7128 return; 7129 } 7130 7131 // Now that we've found the delta, push the result back to the cell. 7132 7133 if (pResMat) 7134 { 7135 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); 7136 // Result array is matrix. 7137 // Note this does not replicate the other dimension. 7138 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount()) 7139 { 7140 PushNA(); 7141 return; 7142 } 7143 if (aResMatAcc.IsValue(nDelta)) 7144 PushDouble(aResMatAcc.GetDouble(nDelta)); 7145 else 7146 PushString(aResMatAcc.GetString(nDelta)); 7147 } 7148 else if (nParamCount == 3) 7149 { 7150 /* TODO: the entire switch is a copy of the cell range search 7151 * result, factor out. */ 7152 switch (eResArrayType) 7153 { 7154 case svDoubleRef: 7155 case svSingleRef: 7156 { 7157 // Use the result array vector. Note that the result array is assumed 7158 // to be a vector (i.e. 1-dimensional array). 7159 7160 ScAddress aAdr; 7161 aAdr.SetTab(nResTab); 7162 bool bResVertical = (nResRow2 - nResRow1) > 0; 7163 if (bResVertical) 7164 { 7165 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); 7166 if (nTempRow > mrDoc.MaxRow()) 7167 { 7168 PushDouble(0); 7169 return; 7170 } 7171 aAdr.SetCol(nResCol1); 7172 aAdr.SetRow(nTempRow); 7173 } 7174 else 7175 { 7176 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); 7177 if (nTempCol > mrDoc.MaxCol()) 7178 { 7179 PushDouble(0); 7180 return; 7181 } 7182 aAdr.SetCol(nTempCol); 7183 aAdr.SetRow(nResRow1); 7184 } 7185 PushCellResultToken( true, aAdr, nullptr, nullptr); 7186 } 7187 break; 7188 case svDouble: 7189 case svString: 7190 { 7191 if (nDelta != 0) 7192 PushNA(); 7193 else 7194 { 7195 switch (eResArrayType) 7196 { 7197 case svDouble: 7198 PushDouble( fResVal ); 7199 break; 7200 case svString: 7201 PushString( aResStr ); 7202 break; 7203 default: 7204 ; // nothing 7205 } 7206 } 7207 } 7208 break; 7209 default: 7210 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search"); 7211 PushIllegalParameter(); 7212 } 7213 } 7214 else 7215 { 7216 // No result array. Use the data array to get the final value from. 7217 // Propagate errors from matrix again. 7218 pDataMat->SetErrorInterpreter( this); 7219 if (bVertical) 7220 { 7221 if (pDataMat->IsValue(nC-1, nDelta)) 7222 PushDouble(pDataMat->GetDouble(nC-1, nDelta)); 7223 else 7224 PushString(pDataMat->GetString(nC-1, nDelta)); 7225 } 7226 else 7227 { 7228 if (pDataMat->IsValue(nDelta, nR-1)) 7229 PushDouble(pDataMat->GetDouble(nDelta, nR-1)); 7230 else 7231 PushString(pDataMat->GetString(nDelta, nR-1)); 7232 } 7233 } 7234 7235 return; 7236 } 7237 7238 // Perform cell range search. 7239 7240 aParam.nCol1 = nCol1; 7241 aParam.nRow1 = nRow1; 7242 aParam.nCol2 = bVertical ? nCol1 : nCol2; 7243 aParam.nRow2 = bVertical ? nRow2 : nRow1; 7244 aParam.bByRow = bVertical; 7245 7246 rEntry.bDoQuery = true; 7247 rEntry.eOp = SC_LESS_EQUAL; 7248 rEntry.nField = nCol1; 7249 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 7250 if (rItem.meType == ScQueryEntry::ByString) 7251 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); 7252 7253 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false); 7254 SCCOL nC; 7255 SCROW nR; 7256 // Advance Entry.nField in iterator upon switching columns if 7257 // lookup in row. 7258 aCellIter.SetAdvanceQueryParamEntryField(!bVertical); 7259 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) ) 7260 { 7261 PushNA(); 7262 return; 7263 } 7264 7265 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1); 7266 7267 if (pResMat) 7268 { 7269 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); 7270 // Use the matrix result array. 7271 // Note this does not replicate the other dimension. 7272 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount()) 7273 { 7274 PushNA(); 7275 return; 7276 } 7277 if (aResMatAcc.IsValue(nDelta)) 7278 PushDouble(aResMatAcc.GetDouble(nDelta)); 7279 else 7280 PushString(aResMatAcc.GetString(nDelta)); 7281 } 7282 else if (nParamCount == 3) 7283 { 7284 /* TODO: the entire switch is a copy of the array search result, factor 7285 * out. */ 7286 switch (eResArrayType) 7287 { 7288 case svDoubleRef: 7289 case svSingleRef: 7290 { 7291 // Use the result array vector. Note that the result array is assumed 7292 // to be a vector (i.e. 1-dimensional array). 7293 7294 ScAddress aAdr; 7295 aAdr.SetTab(nResTab); 7296 bool bResVertical = (nResRow2 - nResRow1) > 0; 7297 if (bResVertical) 7298 { 7299 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); 7300 if (nTempRow > mrDoc.MaxRow()) 7301 { 7302 PushDouble(0); 7303 return; 7304 } 7305 aAdr.SetCol(nResCol1); 7306 aAdr.SetRow(nTempRow); 7307 } 7308 else 7309 { 7310 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); 7311 if (nTempCol > mrDoc.MaxCol()) 7312 { 7313 PushDouble(0); 7314 return; 7315 } 7316 aAdr.SetCol(nTempCol); 7317 aAdr.SetRow(nResRow1); 7318 } 7319 PushCellResultToken( true, aAdr, nullptr, nullptr); 7320 } 7321 break; 7322 case svDouble: 7323 case svString: 7324 { 7325 if (nDelta != 0) 7326 PushNA(); 7327 else 7328 { 7329 switch (eResArrayType) 7330 { 7331 case svDouble: 7332 PushDouble( fResVal ); 7333 break; 7334 case svString: 7335 PushString( aResStr ); 7336 break; 7337 default: 7338 ; // nothing 7339 } 7340 } 7341 } 7342 break; 7343 default: 7344 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search"); 7345 PushIllegalParameter(); 7346 } 7347 } 7348 else 7349 { 7350 // Regardless of whether or not the result array exists, the last 7351 // array is always used as the "result" array. 7352 7353 ScAddress aAdr; 7354 aAdr.SetTab(nTab1); 7355 if (bVertical) 7356 { 7357 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta); 7358 if (nTempRow > mrDoc.MaxRow()) 7359 { 7360 PushDouble(0); 7361 return; 7362 } 7363 aAdr.SetCol(nCol2); 7364 aAdr.SetRow(nTempRow); 7365 } 7366 else 7367 { 7368 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta); 7369 if (nTempCol > mrDoc.MaxCol()) 7370 { 7371 PushDouble(0); 7372 return; 7373 } 7374 aAdr.SetCol(nTempCol); 7375 aAdr.SetRow(nRow2); 7376 } 7377 PushCellResultToken(true, aAdr, nullptr, nullptr); 7378 } 7379 } 7380 7381 void ScInterpreter::ScHLookup() 7382 { 7383 CalculateLookup(true); 7384 } 7385 7386 void ScInterpreter::CalculateLookup(bool bHLookup) 7387 { 7388 sal_uInt8 nParamCount = GetByte(); 7389 if (!MustHaveParamCount(nParamCount, 3, 4)) 7390 return; 7391 7392 // Optional 4th argument to declare whether or not the range is sorted. 7393 bool bSorted = true; 7394 if (nParamCount == 4) 7395 bSorted = GetBool(); 7396 7397 // Index of column to search. 7398 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0; 7399 7400 ScMatrixRef pMat = nullptr; 7401 SCSIZE nC = 0, nR = 0; 7402 SCCOL nCol1 = 0; 7403 SCROW nRow1 = 0; 7404 SCTAB nTab1 = 0; 7405 SCCOL nCol2 = 0; 7406 SCROW nRow2 = 0; 7407 const ScComplexRefData* refData = nullptr; 7408 StackVar eType = GetStackType(); 7409 if (eType == svDoubleRef) 7410 { 7411 refData = GetStackDoubleRef(0); 7412 SCTAB nTab2; 7413 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 7414 if (nTab1 != nTab2) 7415 { 7416 PushIllegalParameter(); 7417 return; 7418 } 7419 } 7420 else if (eType == svSingleRef) 7421 { 7422 PopSingleRef(nCol1, nRow1, nTab1); 7423 nCol2 = nCol1; 7424 nRow2 = nRow1; 7425 } 7426 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef) 7427 { 7428 pMat = GetMatrix(); 7429 7430 if (pMat) 7431 pMat->GetDimensions(nC, nR); 7432 else 7433 { 7434 PushIllegalParameter(); 7435 return; 7436 } 7437 } 7438 else 7439 { 7440 PushIllegalParameter(); 7441 return; 7442 } 7443 7444 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) ) 7445 { 7446 PushIllegalArgument(); 7447 return; 7448 } 7449 7450 SCROW nZIndex = static_cast<SCROW>(fIndex); 7451 SCCOL nSpIndex = static_cast<SCCOL>(fIndex); 7452 7453 if (!pMat) 7454 { 7455 nZIndex += nRow1; // value row 7456 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column 7457 } 7458 7459 if (nGlobalError != FormulaError::NONE) 7460 { 7461 PushIllegalParameter(); 7462 return; 7463 } 7464 7465 ScQueryParam aParam; 7466 aParam.nCol1 = nCol1; 7467 aParam.nRow1 = nRow1; 7468 if ( bHLookup ) 7469 { 7470 aParam.nCol2 = nCol2; 7471 aParam.nRow2 = nRow1; // search only in the first row 7472 aParam.bByRow = false; 7473 } 7474 else 7475 { 7476 aParam.nCol2 = nCol1; // search only in the first column 7477 aParam.nRow2 = nRow2; 7478 aParam.nTab = nTab1; 7479 } 7480 7481 ScQueryEntry& rEntry = aParam.GetEntry(0); 7482 rEntry.bDoQuery = true; 7483 if ( bSorted ) 7484 rEntry.eOp = SC_LESS_EQUAL; 7485 if ( !FillEntry(rEntry) ) 7486 return; 7487 7488 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 7489 if (rItem.meType == ScQueryEntry::ByString) 7490 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); 7491 if (pMat) 7492 { 7493 SCSIZE nMatCount = bHLookup ? nC : nR; 7494 SCSIZE nDelta = SCSIZE_MAX; 7495 if (rItem.meType == ScQueryEntry::ByString) 7496 { 7497 //!!!!!!! 7498 //TODO: enable regex on matrix strings 7499 //!!!!!!! 7500 svl::SharedString aParamStr = rItem.maString; 7501 if ( bSorted ) 7502 { 7503 CollatorWrapper& rCollator = ScGlobal::GetCollator(); 7504 for (SCSIZE i = 0; i < nMatCount; i++) 7505 { 7506 if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)) 7507 { 7508 sal_Int32 nRes = 7509 rCollator.compareString( 7510 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString()); 7511 if (nRes <= 0) 7512 nDelta = i; 7513 else if (i>0) // #i2168# ignore first mismatch 7514 i = nMatCount+1; 7515 } 7516 else 7517 nDelta = i; 7518 } 7519 } 7520 else 7521 { 7522 if (bHLookup) 7523 { 7524 for (SCSIZE i = 0; i < nMatCount; i++) 7525 { 7526 if (pMat->IsStringOrEmpty(i, 0)) 7527 { 7528 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase()) 7529 { 7530 nDelta = i; 7531 i = nMatCount + 1; 7532 } 7533 } 7534 } 7535 } 7536 else 7537 { 7538 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0); 7539 } 7540 } 7541 } 7542 else 7543 { 7544 if ( bSorted ) 7545 { 7546 // #i2168# ignore strings 7547 for (SCSIZE i = 0; i < nMatCount; i++) 7548 { 7549 if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))) 7550 { 7551 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal) 7552 nDelta = i; 7553 else 7554 i = nMatCount+1; 7555 } 7556 } 7557 } 7558 else 7559 { 7560 if (bHLookup) 7561 { 7562 for (SCSIZE i = 0; i < nMatCount; i++) 7563 { 7564 if (! pMat->IsStringOrEmpty(i, 0) ) 7565 { 7566 if ( pMat->GetDouble(i,0) == rItem.mfVal) 7567 { 7568 nDelta = i; 7569 i = nMatCount + 1; 7570 } 7571 } 7572 } 7573 } 7574 else 7575 { 7576 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0); 7577 } 7578 } 7579 } 7580 if ( nDelta != SCSIZE_MAX ) 7581 { 7582 SCSIZE nX = static_cast<SCSIZE>(nSpIndex); 7583 SCSIZE nY = nDelta; 7584 SCSIZE nXs = 0; 7585 SCSIZE nYs = nY; 7586 if ( bHLookup ) 7587 { 7588 nX = nDelta; 7589 nY = static_cast<SCSIZE>(nZIndex); 7590 nXs = nX; 7591 nYs = 0; 7592 } 7593 assert( nX < nC && nY < nR ); 7594 if (!(rItem.meType == ScQueryEntry::ByString && pMat->IsValue( nXs, nYs))) 7595 { 7596 if (pMat->IsStringOrEmpty( nX, nY)) 7597 PushString(pMat->GetString( nX, nY).getString()); 7598 else 7599 PushDouble(pMat->GetDouble( nX, nY)); 7600 } 7601 else 7602 PushNA(); 7603 return; 7604 } 7605 else 7606 PushNA(); 7607 } 7608 else 7609 { 7610 rEntry.nField = nCol1; 7611 bool bFound = false; 7612 SCCOL nCol = 0; 7613 SCROW nRow = 0; 7614 if ( bSorted ) 7615 rEntry.eOp = SC_LESS_EQUAL; 7616 if ( bHLookup ) 7617 { 7618 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab1, aParam, false); 7619 // advance Entry.nField in Iterator upon switching columns 7620 aCellIter.SetAdvanceQueryParamEntryField( true ); 7621 if ( bSorted ) 7622 { 7623 SCROW nRow1_temp; 7624 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp ); 7625 } 7626 else if ( aCellIter.GetFirst() ) 7627 { 7628 bFound = true; 7629 nCol = aCellIter.GetCol(); 7630 } 7631 nRow = nZIndex; 7632 } 7633 else 7634 { 7635 ScAddress aResultPos( nCol1, nRow1, nTab1); 7636 bFound = LookupQueryWithCache( aResultPos, aParam, refData); 7637 nRow = aResultPos.Row(); 7638 nCol = nSpIndex; 7639 } 7640 7641 if ( bFound ) 7642 { 7643 ScAddress aAdr( nCol, nRow, nTab1 ); 7644 PushCellResultToken( true, aAdr, nullptr, nullptr); 7645 } 7646 else 7647 PushNA(); 7648 } 7649 } 7650 7651 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry) 7652 { 7653 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 7654 switch ( GetStackType() ) 7655 { 7656 case svDouble: 7657 { 7658 rItem.meType = ScQueryEntry::ByValue; 7659 rItem.mfVal = GetDouble(); 7660 } 7661 break; 7662 case svString: 7663 { 7664 rItem.meType = ScQueryEntry::ByString; 7665 rItem.maString = GetString(); 7666 } 7667 break; 7668 case svDoubleRef : 7669 case svSingleRef : 7670 { 7671 ScAddress aAdr; 7672 if ( !PopDoubleRefOrSingleRef( aAdr ) ) 7673 { 7674 PushInt(0); 7675 return false; 7676 } 7677 ScRefCellValue aCell(mrDoc, aAdr); 7678 if (aCell.hasNumeric()) 7679 { 7680 rItem.meType = ScQueryEntry::ByValue; 7681 rItem.mfVal = GetCellValue(aAdr, aCell); 7682 } 7683 else 7684 { 7685 GetCellString(rItem.maString, aCell); 7686 rItem.meType = ScQueryEntry::ByString; 7687 } 7688 } 7689 break; 7690 case svExternalDoubleRef: 7691 case svExternalSingleRef: 7692 case svMatrix: 7693 { 7694 svl::SharedString aStr; 7695 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr); 7696 rItem.maString = aStr; 7697 rItem.meType = ScMatrix::IsNonValueType(nType) ? 7698 ScQueryEntry::ByString : ScQueryEntry::ByValue; 7699 } 7700 break; 7701 default: 7702 { 7703 PushIllegalParameter(); 7704 return false; 7705 } 7706 } // switch ( GetStackType() ) 7707 return true; 7708 } 7709 7710 void ScInterpreter::ScVLookup() 7711 { 7712 CalculateLookup(false); 7713 } 7714 7715 void ScInterpreter::ScSubTotal() 7716 { 7717 sal_uInt8 nParamCount = GetByte(); 7718 if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 2 ) ) 7719 return; 7720 7721 // We must fish the 1st parameter deep from the stack! And push it on top. 7722 const FormulaToken* p = pStack[ sp - nParamCount ]; 7723 PushWithoutError( *p ); 7724 sal_Int32 nFunc = GetInt32(); 7725 mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered; 7726 if (nFunc > 100) 7727 { 7728 // For opcodes 101 through 111, we need to skip hidden cells. 7729 // Other than that these opcodes are identical to 1 through 11. 7730 mnSubTotalFlags |= SubtotalFlags::IgnoreHidden; 7731 nFunc -= 100; 7732 } 7733 7734 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 ) 7735 PushIllegalArgument(); // simulate return on stack, not SetError(...) 7736 else 7737 { 7738 cPar = nParamCount - 1; 7739 switch( nFunc ) 7740 { 7741 case SUBTOTAL_FUNC_AVE : ScAverage(); break; 7742 case SUBTOTAL_FUNC_CNT : ScCount(); break; 7743 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break; 7744 case SUBTOTAL_FUNC_MAX : ScMax(); break; 7745 case SUBTOTAL_FUNC_MIN : ScMin(); break; 7746 case SUBTOTAL_FUNC_PROD : ScProduct(); break; 7747 case SUBTOTAL_FUNC_STD : ScStDev(); break; 7748 case SUBTOTAL_FUNC_STDP : ScStDevP(); break; 7749 case SUBTOTAL_FUNC_SUM : ScSum(); break; 7750 case SUBTOTAL_FUNC_VAR : ScVar(); break; 7751 case SUBTOTAL_FUNC_VARP : ScVarP(); break; 7752 default : PushIllegalArgument(); break; 7753 } 7754 } 7755 mnSubTotalFlags = SubtotalFlags::NONE; 7756 // Get rid of the 1st (fished) parameter. 7757 FormulaConstTokenRef xRef( PopToken()); 7758 Pop(); 7759 PushTokenRef( xRef); 7760 } 7761 7762 void ScInterpreter::ScAggregate() 7763 { 7764 sal_uInt8 nParamCount = GetByte(); 7765 if ( !MustHaveParamCountMinWithStackCheck( nParamCount, 3 ) ) 7766 return; 7767 7768 const FormulaError nErr = nGlobalError; 7769 nGlobalError = FormulaError::NONE; 7770 7771 // fish the 1st parameter from the stack and push it on top. 7772 const FormulaToken* p = pStack[ sp - nParamCount ]; 7773 PushWithoutError( *p ); 7774 sal_Int32 nFunc = GetInt32(); 7775 // fish the 2nd parameter from the stack and push it on top. 7776 const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ]; 7777 PushWithoutError( *p2 ); 7778 sal_Int32 nOption = GetInt32(); 7779 7780 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 ) 7781 { 7782 nGlobalError = nErr; 7783 PushIllegalArgument(); 7784 } 7785 else 7786 { 7787 switch ( nOption) 7788 { 7789 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions 7790 mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg; 7791 break; 7792 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions 7793 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg; 7794 break; 7795 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions 7796 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg; 7797 break; 7798 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions 7799 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg; 7800 break; 7801 case 4 : // ignore nothing 7802 mnSubTotalFlags = SubtotalFlags::NONE; 7803 break; 7804 case 5 : // ignore hidden rows 7805 mnSubTotalFlags = SubtotalFlags::IgnoreHidden ; 7806 break; 7807 case 6 : // ignore error values 7808 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ; 7809 break; 7810 case 7 : // ignore hidden rows and error values 7811 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ; 7812 break; 7813 default : 7814 nGlobalError = nErr; 7815 PushIllegalArgument(); 7816 return; 7817 } 7818 7819 if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE) 7820 nGlobalError = nErr; 7821 7822 cPar = nParamCount - 2; 7823 switch ( nFunc ) 7824 { 7825 case AGGREGATE_FUNC_AVE : ScAverage(); break; 7826 case AGGREGATE_FUNC_CNT : ScCount(); break; 7827 case AGGREGATE_FUNC_CNT2 : ScCount2(); break; 7828 case AGGREGATE_FUNC_MAX : ScMax(); break; 7829 case AGGREGATE_FUNC_MIN : ScMin(); break; 7830 case AGGREGATE_FUNC_PROD : ScProduct(); break; 7831 case AGGREGATE_FUNC_STD : ScStDev(); break; 7832 case AGGREGATE_FUNC_STDP : ScStDevP(); break; 7833 case AGGREGATE_FUNC_SUM : ScSum(); break; 7834 case AGGREGATE_FUNC_VAR : ScVar(); break; 7835 case AGGREGATE_FUNC_VARP : ScVarP(); break; 7836 case AGGREGATE_FUNC_MEDIAN : ScMedian(); break; 7837 case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break; 7838 case AGGREGATE_FUNC_LARGE : ScLarge(); break; 7839 case AGGREGATE_FUNC_SMALL : ScSmall(); break; 7840 case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break; 7841 case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break; 7842 case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break; 7843 case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break; 7844 default: 7845 nGlobalError = nErr; 7846 PushIllegalArgument(); 7847 break; 7848 } 7849 mnSubTotalFlags = SubtotalFlags::NONE; 7850 } 7851 FormulaConstTokenRef xRef( PopToken()); 7852 // Get rid of the 1st and 2nd (fished) parameters. 7853 Pop(); 7854 Pop(); 7855 PushTokenRef( xRef); 7856 } 7857 7858 std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField ) 7859 { 7860 bool bAllowMissingField = false; 7861 if ( rMissingField ) 7862 { 7863 bAllowMissingField = true; 7864 rMissingField = false; 7865 } 7866 if ( GetByte() == 3 ) 7867 { 7868 // First, get the query criteria range. 7869 ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() ); 7870 if (!pQueryRef) 7871 return nullptr; 7872 7873 bool bByVal = true; 7874 double nVal = 0.0; 7875 svl::SharedString aStr; 7876 ScRange aMissingRange; 7877 bool bRangeFake = false; 7878 switch (GetStackType()) 7879 { 7880 case svDouble : 7881 nVal = ::rtl::math::approxFloor( GetDouble() ); 7882 if ( bAllowMissingField && nVal == 0.0 ) 7883 rMissingField = true; // fake missing parameter 7884 break; 7885 case svString : 7886 bByVal = false; 7887 aStr = GetString(); 7888 break; 7889 case svSingleRef : 7890 { 7891 ScAddress aAdr; 7892 PopSingleRef( aAdr ); 7893 ScRefCellValue aCell(mrDoc, aAdr); 7894 if (aCell.hasNumeric()) 7895 nVal = GetCellValue(aAdr, aCell); 7896 else 7897 { 7898 bByVal = false; 7899 GetCellString(aStr, aCell); 7900 } 7901 } 7902 break; 7903 case svDoubleRef : 7904 if ( bAllowMissingField ) 7905 { // fake missing parameter for old SO compatibility 7906 bRangeFake = true; 7907 PopDoubleRef( aMissingRange ); 7908 } 7909 else 7910 { 7911 PopError(); 7912 SetError( FormulaError::IllegalParameter ); 7913 } 7914 break; 7915 case svMissing : 7916 PopError(); 7917 if ( bAllowMissingField ) 7918 rMissingField = true; 7919 else 7920 SetError( FormulaError::IllegalParameter ); 7921 break; 7922 default: 7923 PopError(); 7924 SetError( FormulaError::IllegalParameter ); 7925 } 7926 7927 if (nGlobalError != FormulaError::NONE) 7928 return nullptr; 7929 7930 unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() ); 7931 7932 if (nGlobalError != FormulaError::NONE || !pDBRef) 7933 return nullptr; 7934 7935 if ( bRangeFake ) 7936 { 7937 // range parameter must match entire database range 7938 if (pDBRef->isRangeEqual(aMissingRange)) 7939 rMissingField = true; 7940 else 7941 SetError( FormulaError::IllegalParameter ); 7942 } 7943 7944 if (nGlobalError != FormulaError::NONE) 7945 return nullptr; 7946 7947 SCCOL nField = pDBRef->getFirstFieldColumn(); 7948 if (rMissingField) 7949 ; // special case 7950 else if (bByVal) 7951 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal)); 7952 else 7953 { 7954 FormulaError nErr = FormulaError::NONE; 7955 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr); 7956 SetError(nErr); 7957 } 7958 7959 if (!mrDoc.ValidCol(nField)) 7960 return nullptr; 7961 7962 unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) ); 7963 7964 if (pParam) 7965 { 7966 // An allowed missing field parameter sets the result field 7967 // to any of the query fields, just to be able to return 7968 // some cell from the iterator. 7969 if ( rMissingField ) 7970 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField); 7971 pParam->mnField = nField; 7972 7973 SCSIZE nCount = pParam->GetEntryCount(); 7974 for ( SCSIZE i=0; i < nCount; i++ ) 7975 { 7976 ScQueryEntry& rEntry = pParam->GetEntry(i); 7977 if (!rEntry.bDoQuery) 7978 break; 7979 7980 ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 7981 sal_uInt32 nIndex = 0; 7982 OUString aQueryStr = rItem.maString.getString(); 7983 bool bNumber = pFormatter->IsNumberFormat( 7984 aQueryStr, nIndex, rItem.mfVal); 7985 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString; 7986 7987 if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal) 7988 pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc); 7989 } 7990 return pParam; 7991 } 7992 } 7993 return nullptr; 7994 } 7995 7996 void ScInterpreter::DBIterator( ScIterFunc eFunc ) 7997 { 7998 double fRes = 0; 7999 KahanSum fErg = 0; 8000 sal_uLong nCount = 0; 8001 bool bMissingField = false; 8002 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); 8003 if (pQueryParam) 8004 { 8005 if (!pQueryParam->IsValidFieldIndex()) 8006 { 8007 SetError(FormulaError::NoValue); 8008 return; 8009 } 8010 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); 8011 ScDBQueryDataIterator::Value aValue; 8012 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) 8013 { 8014 switch( eFunc ) 8015 { 8016 case ifPRODUCT: fRes = 1; break; 8017 case ifMAX: fRes = -MAXDOUBLE; break; 8018 case ifMIN: fRes = MAXDOUBLE; break; 8019 default: ; // nothing 8020 } 8021 8022 do 8023 { 8024 nCount++; 8025 switch( eFunc ) 8026 { 8027 case ifAVERAGE: 8028 case ifSUM: 8029 fErg += aValue.mfValue; 8030 break; 8031 case ifSUMSQ: 8032 fErg += aValue.mfValue * aValue.mfValue; 8033 break; 8034 case ifPRODUCT: 8035 fRes *= aValue.mfValue; 8036 break; 8037 case ifMAX: 8038 if( aValue.mfValue > fRes ) fRes = aValue.mfValue; 8039 break; 8040 case ifMIN: 8041 if( aValue.mfValue < fRes ) fRes = aValue.mfValue; 8042 break; 8043 default: ; // nothing 8044 } 8045 } 8046 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); 8047 } 8048 SetError(aValue.mnError); 8049 } 8050 else 8051 SetError( FormulaError::IllegalParameter); 8052 switch( eFunc ) 8053 { 8054 case ifCOUNT: fRes = nCount; break; 8055 case ifSUM: fRes = fErg.get(); break; 8056 case ifSUMSQ: fRes = fErg.get(); break; 8057 case ifAVERAGE: fRes = div(fErg.get(), nCount); break; 8058 default: ; // nothing 8059 } 8060 PushDouble( fRes ); 8061 } 8062 8063 void ScInterpreter::ScDBSum() 8064 { 8065 DBIterator( ifSUM ); 8066 } 8067 8068 void ScInterpreter::ScDBCount() 8069 { 8070 bool bMissingField = true; 8071 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); 8072 if (pQueryParam) 8073 { 8074 sal_uLong nCount = 0; 8075 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL ) 8076 { // count all matching records 8077 // TODO: currently the QueryIterators only return cell pointers of 8078 // existing cells, so if a query matches an empty cell there's 8079 // nothing returned, and therefore not counted! 8080 // Since this has ever been the case and this code here only came 8081 // into existence to fix #i6899 and it never worked before we'll 8082 // have to live with it until we reimplement the iterators to also 8083 // return empty cells, which would mean to adapt all callers of 8084 // iterators. 8085 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get()); 8086 p->nCol2 = p->nCol1; // Don't forget to select only one column. 8087 SCTAB nTab = p->nTab; 8088 // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField, 8089 // so the source range has to be restricted, like before the introduction 8090 // of ScDBQueryParamBase. 8091 p->nCol1 = p->nCol2 = p->mnField; 8092 ScQueryCellIteratorDirect aCellIter(mrDoc, mrContext, nTab, *p, true); 8093 if ( aCellIter.GetFirst() ) 8094 { 8095 do 8096 { 8097 nCount++; 8098 } while ( aCellIter.GetNext() ); 8099 } 8100 } 8101 else 8102 { // count only matching records with a value in the "result" field 8103 if (!pQueryParam->IsValidFieldIndex()) 8104 { 8105 SetError(FormulaError::NoValue); 8106 return; 8107 } 8108 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); 8109 ScDBQueryDataIterator::Value aValue; 8110 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) 8111 { 8112 do 8113 { 8114 nCount++; 8115 } 8116 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); 8117 } 8118 SetError(aValue.mnError); 8119 } 8120 PushDouble( nCount ); 8121 } 8122 else 8123 PushIllegalParameter(); 8124 } 8125 8126 void ScInterpreter::ScDBCount2() 8127 { 8128 bool bMissingField = true; 8129 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); 8130 if (pQueryParam) 8131 { 8132 if (!pQueryParam->IsValidFieldIndex()) 8133 { 8134 SetError(FormulaError::NoValue); 8135 return; 8136 } 8137 sal_uLong nCount = 0; 8138 pQueryParam->mbSkipString = false; 8139 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); 8140 ScDBQueryDataIterator::Value aValue; 8141 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) 8142 { 8143 do 8144 { 8145 nCount++; 8146 } 8147 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); 8148 } 8149 SetError(aValue.mnError); 8150 PushDouble( nCount ); 8151 } 8152 else 8153 PushIllegalParameter(); 8154 } 8155 8156 void ScInterpreter::ScDBAverage() 8157 { 8158 DBIterator( ifAVERAGE ); 8159 } 8160 8161 void ScInterpreter::ScDBMax() 8162 { 8163 DBIterator( ifMAX ); 8164 } 8165 8166 void ScInterpreter::ScDBMin() 8167 { 8168 DBIterator( ifMIN ); 8169 } 8170 8171 void ScInterpreter::ScDBProduct() 8172 { 8173 DBIterator( ifPRODUCT ); 8174 } 8175 8176 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount ) 8177 { 8178 std::vector<double> values; 8179 KahanSum vSum = 0.0; 8180 KahanSum fSum = 0.0; 8181 8182 rValCount = 0.0; 8183 bool bMissingField = false; 8184 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); 8185 if (pQueryParam) 8186 { 8187 if (!pQueryParam->IsValidFieldIndex()) 8188 { 8189 SetError(FormulaError::NoValue); 8190 return; 8191 } 8192 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); 8193 ScDBQueryDataIterator::Value aValue; 8194 if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE) 8195 { 8196 do 8197 { 8198 rValCount++; 8199 values.push_back(aValue.mfValue); 8200 fSum += aValue.mfValue; 8201 } 8202 while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue)); 8203 } 8204 SetError(aValue.mnError); 8205 } 8206 else 8207 SetError( FormulaError::IllegalParameter); 8208 8209 double vMean = fSum.get() / values.size(); 8210 8211 for (double v : values) 8212 vSum += (v - vMean) * (v - vMean); 8213 8214 rVal = vSum.get(); 8215 } 8216 8217 void ScInterpreter::ScDBStdDev() 8218 { 8219 double fVal, fCount; 8220 GetDBStVarParams( fVal, fCount ); 8221 PushDouble( sqrt(fVal/(fCount-1))); 8222 } 8223 8224 void ScInterpreter::ScDBStdDevP() 8225 { 8226 double fVal, fCount; 8227 GetDBStVarParams( fVal, fCount ); 8228 PushDouble( sqrt(fVal/fCount)); 8229 } 8230 8231 void ScInterpreter::ScDBVar() 8232 { 8233 double fVal, fCount; 8234 GetDBStVarParams( fVal, fCount ); 8235 PushDouble(fVal/(fCount-1)); 8236 } 8237 8238 void ScInterpreter::ScDBVarP() 8239 { 8240 double fVal, fCount; 8241 GetDBStVarParams( fVal, fCount ); 8242 PushDouble(fVal/fCount); 8243 } 8244 8245 static bool lcl_IsTableStructuredRef(const OUString& sRefStr, sal_Int32& nIndex) 8246 { 8247 nIndex = ScGlobal::FindUnquoted(sRefStr, '['); 8248 return (nIndex > 0 && ScGlobal::FindUnquoted(sRefStr, ']', nIndex + 1) > nIndex); 8249 } 8250 8251 void ScInterpreter::ScIndirect() 8252 { 8253 sal_uInt8 nParamCount = GetByte(); 8254 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 8255 return; 8256 8257 // Reference address syntax for INDIRECT is configurable. 8258 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax; 8259 if (eConv == FormulaGrammar::CONV_UNSPECIFIED) 8260 // Use the current address syntax if unspecified. 8261 eConv = mrDoc.GetAddressConvention(); 8262 8263 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible 8264 // to determine which syntax to use during doc import 8265 bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1); 8266 8267 if (nParamCount == 2 && 0.0 == GetDouble() ) 8268 { 8269 // Overwrite the config and try Excel R1C1. 8270 eConv = FormulaGrammar::CONV_XL_R1C1; 8271 bTryXlA1 = false; 8272 } 8273 8274 svl::SharedString sSharedRefStr = GetString(); 8275 const OUString & sRefStr = sSharedRefStr.getString(); 8276 if (sRefStr.isEmpty()) 8277 { 8278 // Bail out early for empty cells, rely on "we do have a string" below. 8279 PushError( FormulaError::NoRef); 8280 return; 8281 } 8282 8283 const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos ); 8284 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos ); 8285 SCTAB nTab = aPos.Tab(); 8286 8287 bool bTableRefNamed = false; 8288 sal_Int32 nTableRefNamedIndex = -1; 8289 OUString sTabRefStr; 8290 8291 // Named expressions and DB range names need to be tried first, as older 1K 8292 // columns allowed names that would now match a 16k columns cell address. 8293 do 8294 { 8295 ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString( sRefStr, nTab, mrDoc, eConv); 8296 if (!pData) 8297 break; 8298 8299 // We need this in order to obtain a good range. 8300 pData->ValidateTabRefs(); 8301 8302 ScRange aRange; 8303 8304 // This is the usual way to treat named ranges containing 8305 // relative references. 8306 if (!pData->IsReference(aRange, aPos)) 8307 { 8308 sTabRefStr = pData->GetSymbol(); 8309 bTableRefNamed = lcl_IsTableStructuredRef(sTabRefStr, nTableRefNamedIndex); 8310 // if bTableRefNamed is true, we have a name that maps to a table structured reference. 8311 // Such a case is handled below. 8312 break; 8313 } 8314 8315 if (aRange.aStart == aRange.aEnd) 8316 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), 8317 aRange.aStart.Tab()); 8318 else 8319 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), 8320 aRange.aStart.Tab(), aRange.aEnd.Col(), 8321 aRange.aEnd.Row(), aRange.aEnd.Tab()); 8322 8323 // success! 8324 return; 8325 } 8326 while (false); 8327 8328 do 8329 { 8330 if (bTableRefNamed) 8331 break; 8332 8333 const OUString & aName( sSharedRefStr.getIgnoreCaseString() ); 8334 ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs(); 8335 const ScDBData* pData = rDBs.findByUpperName( aName); 8336 if (!pData) 8337 break; 8338 8339 ScRange aRange; 8340 pData->GetArea( aRange); 8341 8342 // In Excel, specifying a table name without [] resolves to the 8343 // same as with [], a range that excludes header and totals 8344 // rows and contains only data rows. Do the same. 8345 if (pData->HasHeader()) 8346 aRange.aStart.IncRow(); 8347 if (pData->HasTotals()) 8348 aRange.aEnd.IncRow(-1); 8349 8350 if (aRange.aStart.Row() > aRange.aEnd.Row()) 8351 break; 8352 8353 if (aRange.aStart == aRange.aEnd) 8354 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), 8355 aRange.aStart.Tab()); 8356 else 8357 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), 8358 aRange.aStart.Tab(), aRange.aEnd.Col(), 8359 aRange.aEnd.Row(), aRange.aEnd.Tab()); 8360 8361 // success! 8362 return; 8363 } 8364 while (false); 8365 8366 ScRefAddress aRefAd, aRefAd2; 8367 ScAddress::ExternalInfo aExtInfo; 8368 if ( !bTableRefNamed && 8369 (ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) || 8370 ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, 8371 aRefAd2, aDetailsXlA1, &aExtInfo) ) ) ) 8372 { 8373 if (aExtInfo.mbExternal) 8374 { 8375 PushExternalDoubleRef( 8376 aExtInfo.mnFileId, aExtInfo.maTabName, 8377 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(), 8378 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab()); 8379 } 8380 else 8381 PushDoubleRef( aRefAd, aRefAd2); 8382 } 8383 else if ( !bTableRefNamed && 8384 (ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) || 8385 ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd, 8386 aDetailsXlA1, &aExtInfo) ) ) ) 8387 { 8388 if (aExtInfo.mbExternal) 8389 { 8390 PushExternalSingleRef( 8391 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab()); 8392 } 8393 else 8394 PushSingleRef( aRefAd); 8395 } 8396 else 8397 { 8398 // It may be even a TableRef or an external name. 8399 // Anything else that resolves to one reference could be added 8400 // here, but we don't want to compile every arbitrary string. This 8401 // is already nasty enough... 8402 sal_Int32 nIndex = bTableRefNamed ? nTableRefNamedIndex : -1; 8403 bool bTableRef = bTableRefNamed; 8404 if (!bTableRefNamed) 8405 bTableRef = lcl_IsTableStructuredRef(sRefStr, nIndex); 8406 bool bExternalName = false; // External references would had been consumed above already. 8407 if (!bTableRef) 8408 { 8409 // This is our own file name reference representation centric.. but 8410 // would work also for XL '[doc]'!name and also for 8411 // '[doc]Sheet1'!name ... sickos. 8412 if (sRefStr[0] == '\'') 8413 { 8414 // Minimum 'a'#name or 'a'!name 8415 // bTryXlA1 means try both, first our own. 8416 if (bTryXlA1 || eConv == FormulaGrammar::CONV_OOO) 8417 { 8418 nIndex = ScGlobal::FindUnquoted( sRefStr, '#'); 8419 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'') 8420 { 8421 bExternalName = true; 8422 eConv = FormulaGrammar::CONV_OOO; 8423 } 8424 } 8425 if (!bExternalName && (bTryXlA1 || eConv != FormulaGrammar::CONV_OOO)) 8426 { 8427 nIndex = ScGlobal::FindUnquoted( sRefStr, '!'); 8428 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'') 8429 { 8430 bExternalName = true; 8431 } 8432 } 8433 } 8434 8435 } 8436 if (bExternalName || bTableRef) 8437 { 8438 do 8439 { 8440 ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar()); 8441 aComp.SetRefConvention( eConv); // must be after grammar 8442 std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString(bTableRefNamed ? sTabRefStr : sRefStr)); 8443 8444 if (pTokArr->GetCodeError() != FormulaError::NONE || !pTokArr->GetLen()) 8445 break; 8446 8447 // Whatever... use only the specific case. 8448 if (bExternalName) 8449 { 8450 const formula::FormulaToken* pTok = pTokArr->FirstToken(); 8451 if (!pTok || pTok->GetType() != svExternalName) 8452 break; 8453 } 8454 else if (!pTokArr->HasOpCode( ocTableRef)) 8455 break; 8456 8457 aComp.CompileTokenArray(); 8458 8459 // A syntactically valid reference will generate exactly 8460 // one RPN token, a reference or error. Discard everything 8461 // else as error. 8462 if (pTokArr->GetCodeLen() != 1) 8463 break; 8464 8465 ScTokenRef xTok( pTokArr->FirstRPNToken()); 8466 if (!xTok) 8467 break; 8468 8469 switch (xTok->GetType()) 8470 { 8471 case svSingleRef: 8472 case svDoubleRef: 8473 case svExternalSingleRef: 8474 case svExternalDoubleRef: 8475 case svError: 8476 PushTokenRef( xTok); 8477 // success! 8478 return; 8479 default: 8480 ; // nothing 8481 } 8482 } 8483 while (false); 8484 } 8485 8486 PushError( FormulaError::NoRef); 8487 } 8488 } 8489 8490 void ScInterpreter::ScAddressFunc() 8491 { 8492 OUString sTabStr; 8493 8494 sal_uInt8 nParamCount = GetByte(); 8495 if( !MustHaveParamCount( nParamCount, 2, 5 ) ) 8496 return; 8497 8498 if( nParamCount >= 5 ) 8499 sTabStr = GetString().getString(); 8500 8501 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default 8502 if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0)) 8503 eConv = FormulaGrammar::CONV_XL_R1C1; 8504 else 8505 { 8506 // If A1 syntax is requested then the actual sheet separator and format 8507 // convention depends on the syntax configured for INDIRECT to match 8508 // that, and if it is unspecified then the document's address syntax. 8509 FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax; 8510 if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED) 8511 eForceConv = mrDoc.GetAddressConvention(); 8512 if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1) 8513 eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1 8514 } 8515 8516 ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default 8517 if( nParamCount >= 3 ) 8518 { 8519 sal_Int32 n = GetInt32WithDefault(1); 8520 switch ( n ) 8521 { 8522 default : 8523 PushNoValue(); 8524 return; 8525 8526 case 5: 8527 case 1 : break; // default 8528 case 6: 8529 case 2 : nFlags = ScRefFlags::ROW_ABS; break; 8530 case 7: 8531 case 3 : nFlags = ScRefFlags::COL_ABS; break; 8532 case 8: 8533 case 4 : nFlags = ScRefFlags::ZERO; break; // both relative 8534 } 8535 } 8536 nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID; 8537 8538 SCCOL nCol = static_cast<SCCOL>(GetInt16()); 8539 SCROW nRow = static_cast<SCROW>(GetInt32()); 8540 if( eConv == FormulaGrammar::CONV_XL_R1C1 ) 8541 { 8542 // YUCK! The XL interface actually treats rel R1C1 refs differently 8543 // than A1 8544 if( !(nFlags & ScRefFlags::COL_ABS) ) 8545 nCol += aPos.Col() + 1; 8546 if( !(nFlags & ScRefFlags::ROW_ABS) ) 8547 nRow += aPos.Row() + 1; 8548 } 8549 8550 --nCol; 8551 --nRow; 8552 if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow)) 8553 { 8554 PushIllegalArgument(); 8555 return; 8556 } 8557 8558 const ScAddress::Details aDetails( eConv, aPos ); 8559 const ScAddress aAdr( nCol, nRow, 0); 8560 OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails)); 8561 8562 if( nParamCount >= 5 && !sTabStr.isEmpty() ) 8563 { 8564 OUString aDoc; 8565 if (eConv == FormulaGrammar::CONV_OOO) 8566 { 8567 // Isolate Tab from 'Doc'#Tab 8568 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr); 8569 if (nPos != -1) 8570 { 8571 if (sTabStr[nPos+1] == '$') 8572 ++nPos; // also split 'Doc'#$Tab 8573 aDoc = sTabStr.copy( 0, nPos+1); 8574 sTabStr = sTabStr.copy( nPos+1); 8575 } 8576 } 8577 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may 8578 * need some extra handling to isolate Tab from Doc. */ 8579 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'")) 8580 ScCompiler::CheckTabQuotes( sTabStr, eConv); 8581 if (!aDoc.isEmpty()) 8582 sTabStr = aDoc + sTabStr; 8583 sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ? 8584 std::u16string_view(u"!") : std::u16string_view(u"."); 8585 sTabStr += aRefStr; 8586 PushString( sTabStr ); 8587 } 8588 else 8589 PushString( aRefStr ); 8590 } 8591 8592 void ScInterpreter::ScOffset() 8593 { 8594 sal_uInt8 nParamCount = GetByte(); 8595 if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) 8596 return; 8597 8598 bool bNewWidth = false; 8599 bool bNewHeight = false; 8600 sal_Int32 nColNew = 1, nRowNew = 1; 8601 if (nParamCount == 5) 8602 { 8603 if (IsMissing()) 8604 PopError(); 8605 else 8606 { 8607 nColNew = GetInt32(); 8608 bNewWidth = true; 8609 } 8610 } 8611 if (nParamCount >= 4) 8612 { 8613 if (IsMissing()) 8614 PopError(); 8615 else 8616 { 8617 nRowNew = GetInt32(); 8618 bNewHeight = true; 8619 } 8620 } 8621 sal_Int32 nColPlus = GetInt32(); 8622 sal_Int32 nRowPlus = GetInt32(); 8623 if (nGlobalError != FormulaError::NONE) 8624 { 8625 PushError( nGlobalError); 8626 return; 8627 } 8628 if (nColNew <= 0 || nRowNew <= 0) 8629 { 8630 PushIllegalArgument(); 8631 return; 8632 } 8633 SCCOL nCol1(0); 8634 SCROW nRow1(0); 8635 SCTAB nTab1(0); 8636 SCCOL nCol2(0); 8637 SCROW nRow2(0); 8638 SCTAB nTab2(0); 8639 switch (GetStackType()) 8640 { 8641 case svSingleRef: 8642 { 8643 PopSingleRef(nCol1, nRow1, nTab1); 8644 if (!bNewWidth && !bNewHeight) 8645 { 8646 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus); 8647 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus); 8648 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1)) 8649 PushIllegalArgument(); 8650 else 8651 PushSingleRef(nCol1, nRow1, nTab1); 8652 } 8653 else 8654 { 8655 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus); 8656 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus); 8657 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1); 8658 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1); 8659 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || 8660 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2)) 8661 PushIllegalArgument(); 8662 else 8663 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); 8664 } 8665 break; 8666 } 8667 case svExternalSingleRef: 8668 { 8669 sal_uInt16 nFileId; 8670 OUString aTabName; 8671 ScSingleRefData aRef; 8672 PopExternalSingleRef(nFileId, aTabName, aRef); 8673 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); 8674 nCol1 = aAbsRef.Col(); 8675 nRow1 = aAbsRef.Row(); 8676 nTab1 = aAbsRef.Tab(); 8677 8678 if (!bNewWidth && !bNewHeight) 8679 { 8680 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus); 8681 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus); 8682 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1)) 8683 PushIllegalArgument(); 8684 else 8685 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1); 8686 } 8687 else 8688 { 8689 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus); 8690 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus); 8691 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1); 8692 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1); 8693 nTab2 = nTab1; 8694 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || 8695 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2)) 8696 PushIllegalArgument(); 8697 else 8698 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 8699 } 8700 break; 8701 } 8702 case svDoubleRef: 8703 { 8704 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 8705 if (!bNewWidth) 8706 nColNew = nCol2 - nCol1 + 1; 8707 if (!bNewHeight) 8708 nRowNew = nRow2 - nRow1 + 1; 8709 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus); 8710 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus); 8711 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1); 8712 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1); 8713 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || 8714 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2) 8715 PushIllegalArgument(); 8716 else 8717 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); 8718 break; 8719 } 8720 case svExternalDoubleRef: 8721 { 8722 sal_uInt16 nFileId; 8723 OUString aTabName; 8724 ScComplexRefData aRef; 8725 PopExternalDoubleRef(nFileId, aTabName, aRef); 8726 ScRange aAbs = aRef.toAbs(mrDoc, aPos); 8727 nCol1 = aAbs.aStart.Col(); 8728 nRow1 = aAbs.aStart.Row(); 8729 nTab1 = aAbs.aStart.Tab(); 8730 nCol2 = aAbs.aEnd.Col(); 8731 nRow2 = aAbs.aEnd.Row(); 8732 nTab2 = aAbs.aEnd.Tab(); 8733 if (!bNewWidth) 8734 nColNew = nCol2 - nCol1 + 1; 8735 if (!bNewHeight) 8736 nRowNew = nRow2 - nRow1 + 1; 8737 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus); 8738 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus); 8739 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1); 8740 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1); 8741 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || 8742 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2) 8743 PushIllegalArgument(); 8744 else 8745 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 8746 break; 8747 } 8748 default: 8749 PushIllegalParameter(); 8750 break; 8751 } // end switch 8752 } 8753 8754 void ScInterpreter::ScIndex() 8755 { 8756 sal_uInt8 nParamCount = GetByte(); 8757 if ( !MustHaveParamCount( nParamCount, 1, 4 ) ) 8758 return; 8759 8760 sal_uInt32 nArea; 8761 size_t nAreaCount; 8762 SCCOL nCol; 8763 SCROW nRow; 8764 if (nParamCount == 4) 8765 nArea = GetUInt32(); 8766 else 8767 nArea = 1; 8768 bool bColMissing; 8769 if (nParamCount >= 3) 8770 { 8771 bColMissing = IsMissing(); 8772 nCol = static_cast<SCCOL>(GetInt16()); 8773 } 8774 else 8775 { 8776 bColMissing = false; 8777 nCol = 0; 8778 } 8779 if (nParamCount >= 2) 8780 nRow = static_cast<SCROW>(GetInt32()); 8781 else 8782 nRow = 0; 8783 if (GetStackType() == svRefList) 8784 nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0); 8785 else 8786 nAreaCount = 1; // one reference or array or whatever 8787 if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount) 8788 { 8789 PushError( FormulaError::NoRef); 8790 return; 8791 } 8792 else if (nArea < 1 || nCol < 0 || nRow < 0) 8793 { 8794 PushIllegalArgument(); 8795 return; 8796 } 8797 switch (GetStackType()) 8798 { 8799 case svMatrix: 8800 case svExternalSingleRef: 8801 case svExternalDoubleRef: 8802 { 8803 if (nArea != 1) 8804 SetError(FormulaError::IllegalArgument); 8805 sal_uInt16 nOldSp = sp; 8806 ScMatrixRef pMat = GetMatrix(); 8807 if (pMat) 8808 { 8809 SCSIZE nC, nR; 8810 pMat->GetDimensions(nC, nR); 8811 8812 // Access one element of a vector independent of col/row 8813 // orientation. Excel documentation does not mention, but 8814 // i62850 had a .xls example of a row vector accessed by 8815 // row number returning one element. This 8816 // INDEX(row_vector;element) behaves the same as 8817 // INDEX(row_vector;0;element) and thus contradicts Excel 8818 // documentation where the second parameter is always 8819 // row_num. 8820 // 8821 // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a 8822 // one-dimensional row vector, Row is optional, which 8823 // effectively makes Row act as the column offset into the 8824 // vector". Guess the first Row is a typo and should read 8825 // Column instead. 8826 8827 const bool bRowVectorSpecial = (nParamCount == 2 || bColMissing); 8828 const bool bRowVectorElement = (nR == 1 && (nCol != 0 || (bRowVectorSpecial && nRow != 0))); 8829 const bool bVectorElement = (bRowVectorElement || (nC == 1 && nRow != 0)); 8830 8831 if (nC == 0 || nR == 0 || 8832 (!bVectorElement && (o3tl::make_unsigned(nCol) > nC || 8833 o3tl::make_unsigned(nRow) > nR))) 8834 PushError( FormulaError::NoRef); 8835 else if (nCol == 0 && nRow == 0) 8836 sp = nOldSp; 8837 else if (bVectorElement) 8838 { 8839 // Vectors here don't replicate to the other dimension. 8840 SCSIZE nElement, nOtherDimension; 8841 if (bRowVectorElement && !bRowVectorSpecial) 8842 { 8843 nElement = o3tl::make_unsigned(nCol); 8844 nOtherDimension = o3tl::make_unsigned(nRow); 8845 } 8846 else 8847 { 8848 nElement = o3tl::make_unsigned(nRow); 8849 nOtherDimension = o3tl::make_unsigned(nCol); 8850 } 8851 8852 if (nElement == 0 || nElement > nC * nR || nOtherDimension > 1) 8853 PushError( FormulaError::NoRef); 8854 else 8855 { 8856 --nElement; 8857 if (pMat->IsStringOrEmpty( nElement)) 8858 PushString( pMat->GetString(nElement).getString()); 8859 else 8860 PushDouble( pMat->GetDouble( nElement)); 8861 } 8862 } 8863 else if (nCol == 0) 8864 { 8865 ScMatrixRef pResMat = GetNewMat(nC, 1, /*bEmpty*/true); 8866 if (pResMat) 8867 { 8868 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1); 8869 for (SCSIZE i = 0; i < nC; i++) 8870 if (!pMat->IsStringOrEmpty(i, nRowMinus1)) 8871 pResMat->PutDouble(pMat->GetDouble(i, 8872 nRowMinus1), i, 0); 8873 else 8874 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0); 8875 8876 PushMatrix(pResMat); 8877 } 8878 else 8879 PushError( FormulaError::NoRef); 8880 } 8881 else if (nRow == 0) 8882 { 8883 ScMatrixRef pResMat = GetNewMat(1, nR, /*bEmpty*/true); 8884 if (pResMat) 8885 { 8886 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1); 8887 for (SCSIZE i = 0; i < nR; i++) 8888 if (!pMat->IsStringOrEmpty(nColMinus1, i)) 8889 pResMat->PutDouble(pMat->GetDouble(nColMinus1, 8890 i), i); 8891 else 8892 pResMat->PutString(pMat->GetString(nColMinus1, i), i); 8893 PushMatrix(pResMat); 8894 } 8895 else 8896 PushError( FormulaError::NoRef); 8897 } 8898 else 8899 { 8900 if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1), 8901 static_cast<SCSIZE>(nRow-1))) 8902 PushDouble( pMat->GetDouble( 8903 static_cast<SCSIZE>(nCol-1), 8904 static_cast<SCSIZE>(nRow-1))); 8905 else 8906 PushString( pMat->GetString( 8907 static_cast<SCSIZE>(nCol-1), 8908 static_cast<SCSIZE>(nRow-1)).getString()); 8909 } 8910 } 8911 } 8912 break; 8913 case svSingleRef: 8914 { 8915 SCCOL nCol1 = 0; 8916 SCROW nRow1 = 0; 8917 SCTAB nTab1 = 0; 8918 PopSingleRef( nCol1, nRow1, nTab1); 8919 if (nCol > 1 || nRow > 1) 8920 PushError( FormulaError::NoRef); 8921 else 8922 PushSingleRef( nCol1, nRow1, nTab1); 8923 } 8924 break; 8925 case svDoubleRef: 8926 case svRefList: 8927 { 8928 SCCOL nCol1 = 0; 8929 SCROW nRow1 = 0; 8930 SCTAB nTab1 = 0; 8931 SCCOL nCol2 = 0; 8932 SCROW nRow2 = 0; 8933 SCTAB nTab2 = 0; 8934 bool bRowArray = false; 8935 if (GetStackType() == svRefList) 8936 { 8937 FormulaConstTokenRef xRef = PopToken(); 8938 if (nGlobalError != FormulaError::NONE || !xRef) 8939 { 8940 PushError( FormulaError::NoRef); 8941 return; 8942 } 8943 ScRange aRange( ScAddress::UNINITIALIZED); 8944 DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange); 8945 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 8946 if ( nParamCount == 2 && nRow1 == nRow2 ) 8947 bRowArray = true; 8948 } 8949 else 8950 { 8951 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); 8952 if ( nParamCount == 2 && nRow1 == nRow2 ) 8953 bRowArray = true; 8954 } 8955 if ( nTab1 != nTab2 || 8956 (nCol > 0 && nCol1+nCol-1 > nCol2) || 8957 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) || 8958 ( nRow > nCol2 - nCol1 + 1 && bRowArray )) 8959 PushError( FormulaError::NoRef); 8960 else if (nCol == 0 && nRow == 0) 8961 { 8962 if ( nCol1 == nCol2 && nRow1 == nRow2 ) 8963 PushSingleRef( nCol1, nRow1, nTab1 ); 8964 else 8965 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 ); 8966 } 8967 else if (nRow == 0) 8968 { 8969 if ( nRow1 == nRow2 ) 8970 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 ); 8971 else 8972 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1, 8973 nCol1+nCol-1, nRow2, nTab1 ); 8974 } 8975 else if (nCol == 0) 8976 { 8977 if ( nCol1 == nCol2 ) 8978 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 ); 8979 else if ( bRowArray ) 8980 { 8981 nCol =static_cast<SCCOL>(nRow); 8982 nRow = 1; 8983 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); 8984 } 8985 else 8986 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1, 8987 nCol2, nRow1+nRow-1, nTab1); 8988 } 8989 else 8990 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); 8991 } 8992 break; 8993 default: 8994 PopError(); 8995 PushError( FormulaError::NoRef); 8996 } 8997 } 8998 8999 void ScInterpreter::ScMultiArea() 9000 { 9001 // Legacy support, convert to RefList 9002 sal_uInt8 nParamCount = GetByte(); 9003 if (MustHaveParamCountMin( nParamCount, 1)) 9004 { 9005 while (nGlobalError == FormulaError::NONE && nParamCount-- > 1) 9006 { 9007 ScUnionFunc(); 9008 } 9009 } 9010 } 9011 9012 void ScInterpreter::ScAreas() 9013 { 9014 sal_uInt8 nParamCount = GetByte(); 9015 if (!MustHaveParamCount( nParamCount, 1)) 9016 return; 9017 9018 size_t nCount = 0; 9019 switch (GetStackType()) 9020 { 9021 case svSingleRef: 9022 { 9023 FormulaConstTokenRef xT = PopToken(); 9024 ValidateRef( *xT->GetSingleRef()); 9025 ++nCount; 9026 } 9027 break; 9028 case svDoubleRef: 9029 { 9030 FormulaConstTokenRef xT = PopToken(); 9031 ValidateRef( *xT->GetDoubleRef()); 9032 ++nCount; 9033 } 9034 break; 9035 case svRefList: 9036 { 9037 FormulaConstTokenRef xT = PopToken(); 9038 ValidateRef( *(xT->GetRefList())); 9039 nCount += xT->GetRefList()->size(); 9040 } 9041 break; 9042 default: 9043 SetError( FormulaError::IllegalParameter); 9044 } 9045 PushDouble( double(nCount)); 9046 } 9047 9048 void ScInterpreter::ScCurrency() 9049 { 9050 sal_uInt8 nParamCount = GetByte(); 9051 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 9052 return; 9053 9054 OUString aStr; 9055 double fDec; 9056 if (nParamCount == 2) 9057 { 9058 fDec = ::rtl::math::approxFloor(GetDouble()); 9059 if (fDec < -15.0 || fDec > 15.0) 9060 { 9061 PushIllegalArgument(); 9062 return; 9063 } 9064 } 9065 else 9066 fDec = 2.0; 9067 double fVal = GetDouble(); 9068 double fFac; 9069 if ( fDec != 0.0 ) 9070 fFac = pow( double(10), fDec ); 9071 else 9072 fFac = 1.0; 9073 if (fVal < 0.0) 9074 fVal = ceil(fVal*fFac-0.5)/fFac; 9075 else 9076 fVal = floor(fVal*fFac+0.5)/fFac; 9077 const Color* pColor = nullptr; 9078 if ( fDec < 0.0 ) 9079 fDec = 0.0; 9080 sal_uLong nIndex = pFormatter->GetStandardFormat( 9081 SvNumFormatType::CURRENCY, 9082 ScGlobal::eLnge); 9083 if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) ) 9084 { 9085 OUString sFormatString = pFormatter->GenerateFormat( 9086 nIndex, 9087 ScGlobal::eLnge, 9088 true, // with thousands separator 9089 false, // not red 9090 static_cast<sal_uInt16>(fDec));// decimal places 9091 if (!pFormatter->GetPreviewString(sFormatString, 9092 fVal, 9093 aStr, 9094 &pColor, 9095 ScGlobal::eLnge)) 9096 SetError(FormulaError::IllegalArgument); 9097 } 9098 else 9099 { 9100 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor); 9101 } 9102 PushString(aStr); 9103 } 9104 9105 void ScInterpreter::ScReplace() 9106 { 9107 if ( !MustHaveParamCount( GetByte(), 4 ) ) 9108 return; 9109 9110 OUString aNewStr = GetString().getString(); 9111 sal_Int32 nCount = GetStringPositionArgument(); 9112 sal_Int32 nPos = GetStringPositionArgument(); 9113 OUString aOldStr = GetString().getString(); 9114 if (nPos < 1 || nCount < 0) 9115 PushIllegalArgument(); 9116 else 9117 { 9118 sal_Int32 nLen = aOldStr.getLength(); 9119 if (nPos > nLen + 1) 9120 nPos = nLen + 1; 9121 if (nCount > nLen - nPos + 1) 9122 nCount = nLen - nPos + 1; 9123 sal_Int32 nIdx = 0; 9124 sal_Int32 nCnt = 0; 9125 while ( nIdx < nLen && nPos > nCnt + 1 ) 9126 { 9127 aOldStr.iterateCodePoints( &nIdx ); 9128 ++nCnt; 9129 } 9130 sal_Int32 nStart = nIdx; 9131 while ( nIdx < nLen && nPos + nCount - 1 > nCnt ) 9132 { 9133 aOldStr.iterateCodePoints( &nIdx ); 9134 ++nCnt; 9135 } 9136 if ( CheckStringResultLen( aOldStr, aNewStr.getLength() - (nIdx - nStart) ) ) 9137 aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, aNewStr ); 9138 PushString( aOldStr ); 9139 } 9140 } 9141 9142 void ScInterpreter::ScFixed() 9143 { 9144 sal_uInt8 nParamCount = GetByte(); 9145 if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) 9146 return; 9147 9148 OUString aStr; 9149 double fDec; 9150 bool bThousand; 9151 if (nParamCount == 3) 9152 bThousand = !GetBool(); // Param true: no thousands separator 9153 else 9154 bThousand = true; 9155 if (nParamCount >= 2) 9156 { 9157 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 )); 9158 if (fDec < -15.0 || fDec > 15.0) 9159 { 9160 PushIllegalArgument(); 9161 return; 9162 } 9163 } 9164 else 9165 fDec = 2.0; 9166 double fVal = GetDouble(); 9167 double fFac; 9168 if ( fDec != 0.0 ) 9169 fFac = pow( double(10), fDec ); 9170 else 9171 fFac = 1.0; 9172 if (fVal < 0.0) 9173 fVal = ceil(fVal*fFac-0.5)/fFac; 9174 else 9175 fVal = floor(fVal*fFac+0.5)/fFac; 9176 const Color* pColor = nullptr; 9177 if (fDec < 0.0) 9178 fDec = 0.0; 9179 sal_uLong nIndex = pFormatter->GetStandardFormat( 9180 SvNumFormatType::NUMBER, 9181 ScGlobal::eLnge); 9182 OUString sFormatString = pFormatter->GenerateFormat( 9183 nIndex, 9184 ScGlobal::eLnge, 9185 bThousand, // with thousands separator 9186 false, // not red 9187 static_cast<sal_uInt16>(fDec));// decimal places 9188 if (!pFormatter->GetPreviewString(sFormatString, 9189 fVal, 9190 aStr, 9191 &pColor, 9192 ScGlobal::eLnge)) 9193 PushIllegalArgument(); 9194 else 9195 PushString(aStr); 9196 } 9197 9198 void ScInterpreter::ScFind() 9199 { 9200 sal_uInt8 nParamCount = GetByte(); 9201 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 9202 return; 9203 9204 sal_Int32 nCnt; 9205 if (nParamCount == 3) 9206 nCnt = GetDouble(); 9207 else 9208 nCnt = 1; 9209 OUString sStr = GetString().getString(); 9210 if (nCnt < 1 || nCnt > sStr.getLength()) 9211 PushNoValue(); 9212 else 9213 { 9214 sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1); 9215 if (nPos == -1) 9216 PushNoValue(); 9217 else 9218 { 9219 sal_Int32 nIdx = 0; 9220 nCnt = 0; 9221 while ( nIdx < nPos ) 9222 { 9223 sStr.iterateCodePoints( &nIdx ); 9224 ++nCnt; 9225 } 9226 PushDouble( static_cast<double>(nCnt + 1) ); 9227 } 9228 } 9229 } 9230 9231 void ScInterpreter::ScExact() 9232 { 9233 nFuncFmtType = SvNumFormatType::LOGICAL; 9234 if ( MustHaveParamCount( GetByte(), 2 ) ) 9235 { 9236 svl::SharedString s1 = GetString(); 9237 svl::SharedString s2 = GetString(); 9238 PushInt( int(s1.getData() == s2.getData()) ); 9239 } 9240 } 9241 9242 void ScInterpreter::ScLeft() 9243 { 9244 sal_uInt8 nParamCount = GetByte(); 9245 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 9246 return; 9247 9248 sal_Int32 n; 9249 if (nParamCount == 2) 9250 { 9251 n = GetStringPositionArgument(); 9252 if (n < 0) 9253 { 9254 PushIllegalArgument(); 9255 return ; 9256 } 9257 } 9258 else 9259 n = 1; 9260 OUString aStr = GetString().getString(); 9261 sal_Int32 nIdx = 0; 9262 sal_Int32 nCnt = 0; 9263 while ( nIdx < aStr.getLength() && n > nCnt++ ) 9264 aStr.iterateCodePoints( &nIdx ); 9265 aStr = aStr.copy( 0, nIdx ); 9266 PushString( aStr ); 9267 } 9268 9269 namespace { 9270 9271 struct UBlockScript { 9272 UBlockCode from; 9273 UBlockCode to; 9274 }; 9275 9276 } 9277 9278 const UBlockScript scriptList[] = { 9279 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO}, 9280 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES}, 9281 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT }, 9282 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS}, 9283 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS}, 9284 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS}, 9285 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT}, 9286 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES} 9287 }; 9288 static bool IsDBCS(sal_Unicode currentChar) 9289 { 9290 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian 9291 if( (currentChar == 0x005c || currentChar == 0x20ac) && 9292 (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE) ) 9293 return true; 9294 sal_uInt16 i; 9295 bool bRet = false; 9296 UBlockCode block = ublock_getCode(currentChar); 9297 for ( i = 0; i < SAL_N_ELEMENTS(scriptList); i++) { 9298 if (block <= scriptList[i].to) break; 9299 } 9300 bRet = (i < SAL_N_ELEMENTS(scriptList) && block >= scriptList[i].from); 9301 return bRet; 9302 } 9303 static sal_Int32 lcl_getLengthB( std::u16string_view str, sal_Int32 nPos ) 9304 { 9305 sal_Int32 index = 0; 9306 sal_Int32 length = 0; 9307 while ( index < nPos ) 9308 { 9309 if (IsDBCS(str[index])) 9310 length += 2; 9311 else 9312 length++; 9313 index++; 9314 } 9315 return length; 9316 } 9317 static sal_Int32 getLengthB(std::u16string_view str) 9318 { 9319 if(str.empty()) 9320 return 0; 9321 else 9322 return lcl_getLengthB( str, str.size() ); 9323 } 9324 void ScInterpreter::ScLenB() 9325 { 9326 PushDouble( getLengthB(GetString().getString()) ); 9327 } 9328 static OUString lcl_RightB(const OUString &rStr, sal_Int32 n) 9329 { 9330 if( n < getLengthB(rStr) ) 9331 { 9332 OUStringBuffer aBuf(rStr); 9333 sal_Int32 index = aBuf.getLength(); 9334 while(index-- >= 0) 9335 { 9336 if(0 == n) 9337 { 9338 aBuf.remove( 0, index + 1); 9339 break; 9340 } 9341 if(-1 == n) 9342 { 9343 aBuf.remove( 0, index + 2 ); 9344 aBuf.insert( 0, " "); 9345 break; 9346 } 9347 if(IsDBCS(aBuf[index])) 9348 n -= 2; 9349 else 9350 n--; 9351 } 9352 return aBuf.makeStringAndClear(); 9353 } 9354 return rStr; 9355 } 9356 void ScInterpreter::ScRightB() 9357 { 9358 sal_uInt8 nParamCount = GetByte(); 9359 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 9360 return; 9361 9362 sal_Int32 n; 9363 if (nParamCount == 2) 9364 { 9365 n = GetStringPositionArgument(); 9366 if (n < 0) 9367 { 9368 PushIllegalArgument(); 9369 return ; 9370 } 9371 } 9372 else 9373 n = 1; 9374 OUString aStr(lcl_RightB(GetString().getString(), n)); 9375 PushString( aStr ); 9376 } 9377 static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n) 9378 { 9379 if( n < getLengthB(rStr) ) 9380 { 9381 OUStringBuffer aBuf(rStr); 9382 sal_Int32 index = -1; 9383 while(index++ < aBuf.getLength()) 9384 { 9385 if(0 == n) 9386 { 9387 aBuf.truncate(index); 9388 break; 9389 } 9390 if(-1 == n) 9391 { 9392 aBuf.truncate( index - 1 ); 9393 aBuf.append(" "); 9394 break; 9395 } 9396 if(IsDBCS(aBuf[index])) 9397 n -= 2; 9398 else 9399 n--; 9400 } 9401 return aBuf.makeStringAndClear(); 9402 } 9403 return rStr; 9404 } 9405 void ScInterpreter::ScLeftB() 9406 { 9407 sal_uInt8 nParamCount = GetByte(); 9408 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 9409 return; 9410 9411 sal_Int32 n; 9412 if (nParamCount == 2) 9413 { 9414 n = GetStringPositionArgument(); 9415 if (n < 0) 9416 { 9417 PushIllegalArgument(); 9418 return ; 9419 } 9420 } 9421 else 9422 n = 1; 9423 OUString aStr(lcl_LeftB(GetString().getString(), n)); 9424 PushString( aStr ); 9425 } 9426 void ScInterpreter::ScMidB() 9427 { 9428 if ( !MustHaveParamCount( GetByte(), 3 ) ) 9429 return; 9430 9431 const sal_Int32 nCount = GetStringPositionArgument(); 9432 const sal_Int32 nStart = GetStringPositionArgument(); 9433 OUString aStr = GetString().getString(); 9434 if (nStart < 1 || nCount < 0) 9435 PushIllegalArgument(); 9436 else 9437 { 9438 9439 aStr = lcl_LeftB(aStr, nStart + nCount - 1); 9440 sal_Int32 nCnt = getLengthB(aStr) - nStart + 1; 9441 aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0)); 9442 PushString(aStr); 9443 } 9444 } 9445 9446 void ScInterpreter::ScReplaceB() 9447 { 9448 if ( !MustHaveParamCount( GetByte(), 4 ) ) 9449 return; 9450 9451 OUString aNewStr = GetString().getString(); 9452 const sal_Int32 nCount = GetStringPositionArgument(); 9453 const sal_Int32 nPos = GetStringPositionArgument(); 9454 OUString aOldStr = GetString().getString(); 9455 int nLen = getLengthB( aOldStr ); 9456 if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen) 9457 PushIllegalArgument(); 9458 else 9459 { 9460 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as 9461 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount) 9462 OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 ); 9463 OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1); 9464 9465 PushString( aStr1 + aNewStr + aStr3 ); 9466 } 9467 } 9468 9469 void ScInterpreter::ScFindB() 9470 { 9471 sal_uInt8 nParamCount = GetByte(); 9472 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 9473 return; 9474 9475 sal_Int32 nStart; 9476 if ( nParamCount == 3 ) 9477 nStart = GetStringPositionArgument(); 9478 else 9479 nStart = 1; 9480 OUString aStr = GetString().getString(); 9481 int nLen = getLengthB( aStr ); 9482 OUString asStr = GetString().getString(); 9483 int nsLen = getLengthB( asStr ); 9484 if ( nStart < 1 || nStart > nLen - nsLen + 1 ) 9485 PushIllegalArgument(); 9486 else 9487 { 9488 // create a string from sStr starting at nStart 9489 OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 ); 9490 // search aBuf for asStr 9491 sal_Int32 nPos = aBuf.indexOf( asStr, 0 ); 9492 if ( nPos == -1 ) 9493 PushNoValue(); 9494 else 9495 { 9496 // obtain byte value of nPos 9497 int nBytePos = lcl_getLengthB( aBuf, nPos ); 9498 PushDouble( nBytePos + nStart ); 9499 } 9500 } 9501 } 9502 9503 void ScInterpreter::ScSearchB() 9504 { 9505 sal_uInt8 nParamCount = GetByte(); 9506 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 9507 return; 9508 9509 sal_Int32 nStart; 9510 if ( nParamCount == 3 ) 9511 { 9512 nStart = GetStringPositionArgument(); 9513 if( nStart < 1 ) 9514 { 9515 PushIllegalArgument(); 9516 return; 9517 } 9518 } 9519 else 9520 nStart = 1; 9521 OUString aStr = GetString().getString(); 9522 sal_Int32 nLen = getLengthB( aStr ); 9523 OUString asStr = GetString().getString(); 9524 sal_Int32 nsLen = nStart - 1; 9525 if( nsLen >= nLen ) 9526 PushNoValue(); 9527 else 9528 { 9529 // create a string from sStr starting at nStart 9530 OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) ); 9531 // search aSubStr for asStr 9532 sal_Int32 nPos = 0; 9533 sal_Int32 nEndPos = aSubStr.getLength(); 9534 utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc ); 9535 utl::SearchParam sPar( asStr, eSearchType, false, '~', false ); 9536 utl::TextSearch sT( sPar, ScGlobal::getCharClass() ); 9537 if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) ) 9538 PushNoValue(); 9539 else 9540 { 9541 // obtain byte value of nPos 9542 int nBytePos = lcl_getLengthB( aSubStr, nPos ); 9543 PushDouble( nBytePos + nStart ); 9544 } 9545 } 9546 } 9547 9548 void ScInterpreter::ScRight() 9549 { 9550 sal_uInt8 nParamCount = GetByte(); 9551 if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) 9552 return; 9553 9554 sal_Int32 n; 9555 if (nParamCount == 2) 9556 { 9557 n = GetStringPositionArgument(); 9558 if (n < 0) 9559 { 9560 PushIllegalArgument(); 9561 return ; 9562 } 9563 } 9564 else 9565 n = 1; 9566 OUString aStr = GetString().getString(); 9567 sal_Int32 nLen = aStr.getLength(); 9568 if ( nLen <= n ) 9569 PushString( aStr ); 9570 else 9571 { 9572 sal_Int32 nIdx = nLen; 9573 sal_Int32 nCnt = 0; 9574 while ( nIdx > 0 && n > nCnt ) 9575 { 9576 aStr.iterateCodePoints( &nIdx, -1 ); 9577 ++nCnt; 9578 } 9579 aStr = aStr.copy( nIdx, nLen - nIdx ); 9580 PushString( aStr ); 9581 } 9582 } 9583 9584 void ScInterpreter::ScSearch() 9585 { 9586 sal_uInt8 nParamCount = GetByte(); 9587 if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) 9588 return; 9589 9590 sal_Int32 nStart; 9591 if (nParamCount == 3) 9592 { 9593 nStart = GetStringPositionArgument(); 9594 if( nStart < 1 ) 9595 { 9596 PushIllegalArgument(); 9597 return; 9598 } 9599 } 9600 else 9601 nStart = 1; 9602 OUString sStr = GetString().getString(); 9603 OUString SearchStr = GetString().getString(); 9604 sal_Int32 nPos = nStart - 1; 9605 sal_Int32 nEndPos = sStr.getLength(); 9606 if( nPos >= nEndPos ) 9607 PushNoValue(); 9608 else 9609 { 9610 utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc ); 9611 utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false); 9612 utl::TextSearch sT( sPar, ScGlobal::getCharClass() ); 9613 bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos); 9614 if (!bBool) 9615 PushNoValue(); 9616 else 9617 { 9618 sal_Int32 nIdx = 0; 9619 sal_Int32 nCnt = 0; 9620 while ( nIdx < nPos ) 9621 { 9622 sStr.iterateCodePoints( &nIdx ); 9623 ++nCnt; 9624 } 9625 PushDouble( static_cast<double>(nCnt + 1) ); 9626 } 9627 } 9628 } 9629 9630 void ScInterpreter::ScRegex() 9631 { 9632 const sal_uInt8 nParamCount = GetByte(); 9633 if (!MustHaveParamCount( nParamCount, 2, 4)) 9634 return; 9635 9636 // Flags are supported only for replacement, search match flags can be 9637 // individually and much more flexible set in the regular expression 9638 // pattern using (?ismwx-ismwx) 9639 bool bGlobalReplacement = false; 9640 sal_Int32 nOccurrence = 1; // default first occurrence, if any 9641 if (nParamCount == 4) 9642 { 9643 // Argument can be either string or double. 9644 double fOccurrence; 9645 svl::SharedString aFlagsString; 9646 bool bDouble; 9647 if (!IsMissing()) 9648 bDouble = GetDoubleOrString( fOccurrence, aFlagsString); 9649 else 9650 { 9651 // For an omitted argument keep the default. 9652 PopError(); 9653 bDouble = true; 9654 fOccurrence = nOccurrence; 9655 } 9656 if (nGlobalError != FormulaError::NONE) 9657 { 9658 PushError( nGlobalError); 9659 return; 9660 } 9661 if (bDouble) 9662 { 9663 if (!CheckStringPositionArgument( fOccurrence)) 9664 { 9665 PushError( FormulaError::IllegalArgument); 9666 return; 9667 } 9668 nOccurrence = static_cast<sal_Int32>(fOccurrence); 9669 } 9670 else 9671 { 9672 const OUString aFlags( aFlagsString.getString()); 9673 // Empty flags string is valid => no flag set. 9674 if (aFlags.getLength() > 1) 9675 { 9676 // Only one flag supported. 9677 PushIllegalArgument(); 9678 return; 9679 } 9680 if (aFlags.getLength() == 1) 9681 { 9682 if (aFlags.indexOf('g') >= 0) 9683 bGlobalReplacement = true; 9684 else 9685 { 9686 // Unsupported flag. 9687 PushIllegalArgument(); 9688 return; 9689 } 9690 } 9691 } 9692 } 9693 9694 bool bReplacement = false; 9695 OUString aReplacement; 9696 if (nParamCount >= 3) 9697 { 9698 // A missing argument is not an empty string to replace the match. 9699 // nOccurrence==0 forces no replacement, so simply discard the 9700 // argument. 9701 if (IsMissing() || nOccurrence == 0) 9702 PopError(); 9703 else 9704 { 9705 aReplacement = GetString().getString(); 9706 bReplacement = true; 9707 } 9708 } 9709 // If bGlobalReplacement==true and bReplacement==false then 9710 // bGlobalReplacement is silently ignored. 9711 9712 const OUString aExpression = GetString().getString(); 9713 const OUString aText = GetString().getString(); 9714 9715 if (nGlobalError != FormulaError::NONE) 9716 { 9717 PushError( nGlobalError); 9718 return; 9719 } 9720 9721 // 0-th match or replacement is none, return original string early. 9722 if (nOccurrence == 0) 9723 { 9724 PushString( aText); 9725 return; 9726 } 9727 9728 const icu::UnicodeString aIcuExpression( 9729 false, reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength()); 9730 UErrorCode status = U_ZERO_ERROR; 9731 icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status); 9732 if (U_FAILURE(status)) 9733 { 9734 // Invalid regex. 9735 PushIllegalArgument(); 9736 return; 9737 } 9738 // Guard against pathological patterns, limit steps of engine, see 9739 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00 9740 aRegexMatcher.setTimeLimit( 23*1000, status); 9741 9742 const icu::UnicodeString aIcuText(false, reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength()); 9743 aRegexMatcher.reset( aIcuText); 9744 9745 if (!bReplacement) 9746 { 9747 // Find n-th occurrence. 9748 sal_Int32 nCount = 0; 9749 while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence) 9750 ; 9751 if (U_FAILURE(status)) 9752 { 9753 // Some error. 9754 PushIllegalArgument(); 9755 return; 9756 } 9757 // n-th match found? 9758 if (nCount != nOccurrence) 9759 { 9760 PushError( FormulaError::NotAvailable); 9761 return; 9762 } 9763 // Extract matched text. 9764 icu::UnicodeString aMatch( aRegexMatcher.group( status)); 9765 if (U_FAILURE(status)) 9766 { 9767 // Some error. 9768 PushIllegalArgument(); 9769 return; 9770 } 9771 OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length()); 9772 PushString( aResult); 9773 return; 9774 } 9775 9776 const icu::UnicodeString aIcuReplacement( 9777 false, reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength()); 9778 icu::UnicodeString aReplaced; 9779 if (bGlobalReplacement) 9780 // Replace all occurrences of match with replacement. 9781 aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status); 9782 else if (nOccurrence == 1) 9783 // Replace first occurrence of match with replacement. 9784 aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status); 9785 else 9786 { 9787 // Replace n-th occurrence of match with replacement. 9788 sal_Int32 nCount = 0; 9789 while (aRegexMatcher.find(status) && U_SUCCESS(status)) 9790 { 9791 // XXX NOTE: After several RegexMatcher::find() the 9792 // RegexMatcher::appendReplacement() still starts at the 9793 // beginning (or after the last appendReplacement() position 9794 // which is none here) and copies the original text up to the 9795 // current found match and then replaces the found match. 9796 if (++nCount == nOccurrence) 9797 { 9798 aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status); 9799 break; 9800 } 9801 } 9802 aRegexMatcher.appendTail( aReplaced); 9803 } 9804 if (U_FAILURE(status)) 9805 { 9806 // Some error, e.g. extraneous $1 without group. 9807 PushIllegalArgument(); 9808 return; 9809 } 9810 OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length()); 9811 PushString( aResult); 9812 } 9813 9814 void ScInterpreter::ScMid() 9815 { 9816 if ( !MustHaveParamCount( GetByte(), 3 ) ) 9817 return; 9818 9819 const sal_Int32 nSubLen = GetStringPositionArgument(); 9820 const sal_Int32 nStart = GetStringPositionArgument(); 9821 OUString aStr = GetString().getString(); 9822 if ( nStart < 1 || nSubLen < 0 ) 9823 PushIllegalArgument(); 9824 else if (nStart > kScInterpreterMaxStrLen || nSubLen > kScInterpreterMaxStrLen) 9825 PushError(FormulaError::StringOverflow); 9826 else 9827 { 9828 sal_Int32 nLen = aStr.getLength(); 9829 sal_Int32 nIdx = 0; 9830 sal_Int32 nCnt = 0; 9831 while ( nIdx < nLen && nStart - 1 > nCnt ) 9832 { 9833 aStr.iterateCodePoints( &nIdx ); 9834 ++nCnt; 9835 } 9836 sal_Int32 nIdx0 = nIdx; //start position 9837 9838 while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt ) 9839 { 9840 aStr.iterateCodePoints( &nIdx ); 9841 ++nCnt; 9842 } 9843 aStr = aStr.copy( nIdx0, nIdx - nIdx0 ); 9844 PushString( aStr ); 9845 } 9846 } 9847 9848 void ScInterpreter::ScText() 9849 { 9850 if ( !MustHaveParamCount( GetByte(), 2 ) ) 9851 return; 9852 9853 OUString sFormatString = GetString().getString(); 9854 svl::SharedString aStr; 9855 bool bString = false; 9856 double fVal = 0.0; 9857 switch (GetStackType()) 9858 { 9859 case svError: 9860 PopError(); 9861 break; 9862 case svDouble: 9863 fVal = PopDouble(); 9864 break; 9865 default: 9866 { 9867 FormulaConstTokenRef xTok( PopToken()); 9868 if (nGlobalError == FormulaError::NONE) 9869 { 9870 PushTokenRef( xTok); 9871 // Temporarily override the ConvertStringToValue() 9872 // error for GetCellValue() / GetCellValueOrZero() 9873 FormulaError nSErr = mnStringNoValueError; 9874 mnStringNoValueError = FormulaError::NotNumericString; 9875 fVal = GetDouble(); 9876 mnStringNoValueError = nSErr; 9877 if (nGlobalError == FormulaError::NotNumericString) 9878 { 9879 // Not numeric. 9880 nGlobalError = FormulaError::NONE; 9881 PushTokenRef( xTok); 9882 aStr = GetString(); 9883 bString = true; 9884 } 9885 } 9886 } 9887 } 9888 if (nGlobalError != FormulaError::NONE) 9889 PushError( nGlobalError); 9890 else if (sFormatString.isEmpty()) 9891 { 9892 // Mimic the Excel behaviour that 9893 // * anything numeric returns an empty string 9894 // * text convertible to numeric returns an empty string 9895 // * any other text returns that text 9896 // Conversion was detected above. 9897 if (bString) 9898 PushString( aStr); 9899 else 9900 PushString( OUString()); 9901 } 9902 else 9903 { 9904 OUString aResult; 9905 const Color* pColor = nullptr; 9906 LanguageType eCellLang; 9907 const ScPatternAttr* pPattern = mrDoc.GetPattern( 9908 aPos.Col(), aPos.Row(), aPos.Tab() ); 9909 if ( pPattern ) 9910 eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue(); 9911 else 9912 eCellLang = ScGlobal::eLnge; 9913 if (bString) 9914 { 9915 if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(), 9916 aResult, &pColor, eCellLang)) 9917 PushIllegalArgument(); 9918 else 9919 PushString( aResult); 9920 } 9921 else 9922 { 9923 if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal, 9924 aResult, &pColor, eCellLang)) 9925 PushIllegalArgument(); 9926 else 9927 PushString( aResult); 9928 } 9929 } 9930 } 9931 9932 void ScInterpreter::ScSubstitute() 9933 { 9934 sal_uInt8 nParamCount = GetByte(); 9935 if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) 9936 return; 9937 9938 sal_Int32 nCnt; 9939 if (nParamCount == 4) 9940 { 9941 nCnt = GetStringPositionArgument(); 9942 if (nCnt < 1) 9943 { 9944 PushIllegalArgument(); 9945 return; 9946 } 9947 } 9948 else 9949 nCnt = 0; 9950 OUString sNewStr = GetString().getString(); 9951 OUString sOldStr = GetString().getString(); 9952 OUString sStr = GetString().getString(); 9953 sal_Int32 nPos = 0; 9954 sal_Int32 nCount = 0; 9955 std::optional<OUStringBuffer> oResult; 9956 for (sal_Int32 nEnd = sStr.indexOf(sOldStr); nEnd >= 0; nEnd = sStr.indexOf(sOldStr, nEnd)) 9957 { 9958 if (nCnt == 0 || ++nCount == nCnt) // Found a replacement cite 9959 { 9960 if (!oResult) // Only allocate buffer when needed 9961 oResult.emplace(sStr.getLength() + sNewStr.getLength() - sOldStr.getLength()); 9962 9963 oResult->append(sStr.subView(nPos, nEnd - nPos)); // Copy leading unchanged text 9964 if (!CheckStringResultLen(*oResult, sNewStr.getLength())) 9965 return PushError(GetError()); 9966 oResult->append(sNewStr); // Copy the replacement 9967 nPos = nEnd + sOldStr.getLength(); 9968 if (nCnt > 0) // Found the single replacement site - end the loop 9969 break; 9970 } 9971 nEnd += sOldStr.getLength(); 9972 } 9973 if (oResult) // If there were prior replacements, copy the rest, otherwise use original 9974 oResult->append(sStr.subView(nPos, sStr.getLength() - nPos)); 9975 PushString(oResult ? oResult->makeStringAndClear() : sStr); 9976 } 9977 9978 void ScInterpreter::ScRept() 9979 { 9980 if ( !MustHaveParamCount( GetByte(), 2 ) ) 9981 return; 9982 9983 sal_Int32 nCnt = GetStringPositionArgument(); 9984 OUString aStr = GetString().getString(); 9985 if (nCnt < 0) 9986 PushIllegalArgument(); 9987 else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen) 9988 { 9989 PushError( FormulaError::StringOverflow ); 9990 } 9991 else if (nCnt == 0) 9992 PushString( OUString() ); 9993 else 9994 { 9995 const sal_Int32 nLen = aStr.getLength(); 9996 OUStringBuffer aRes(nCnt*nLen); 9997 while( nCnt-- ) 9998 aRes.append(aStr); 9999 PushString( aRes.makeStringAndClear() ); 10000 } 10001 } 10002 10003 void ScInterpreter::ScConcat() 10004 { 10005 sal_uInt8 nParamCount = GetByte(); 10006 10007 //reverse order of parameter stack to simplify processing 10008 ReverseStack(nParamCount); 10009 10010 OUStringBuffer aRes; 10011 while( nParamCount-- > 0) 10012 { 10013 OUString aStr = GetString().getString(); 10014 if (CheckStringResultLen(aRes, aStr.getLength())) 10015 aRes.append(aStr); 10016 else 10017 break; 10018 } 10019 PushString( aRes.makeStringAndClear() ); 10020 } 10021 10022 FormulaError ScInterpreter::GetErrorType() 10023 { 10024 FormulaError nErr; 10025 FormulaError nOldError = nGlobalError; 10026 nGlobalError = FormulaError::NONE; 10027 switch ( GetStackType() ) 10028 { 10029 case svRefList : 10030 { 10031 FormulaConstTokenRef x = PopToken(); 10032 if (nGlobalError != FormulaError::NONE) 10033 nErr = nGlobalError; 10034 else 10035 { 10036 const ScRefList* pRefList = x->GetRefList(); 10037 size_t n = pRefList->size(); 10038 if (!n) 10039 nErr = FormulaError::NoRef; 10040 else if (n > 1) 10041 nErr = FormulaError::NoValue; 10042 else 10043 { 10044 ScRange aRange; 10045 DoubleRefToRange( (*pRefList)[0], aRange); 10046 if (nGlobalError != FormulaError::NONE) 10047 nErr = nGlobalError; 10048 else 10049 { 10050 ScAddress aAdr; 10051 if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) 10052 nErr = mrDoc.GetErrCode( aAdr ); 10053 else 10054 nErr = nGlobalError; 10055 } 10056 } 10057 } 10058 } 10059 break; 10060 case svDoubleRef : 10061 { 10062 ScRange aRange; 10063 PopDoubleRef( aRange ); 10064 if ( nGlobalError != FormulaError::NONE ) 10065 nErr = nGlobalError; 10066 else 10067 { 10068 ScAddress aAdr; 10069 if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) 10070 nErr = mrDoc.GetErrCode( aAdr ); 10071 else 10072 nErr = nGlobalError; 10073 } 10074 } 10075 break; 10076 case svSingleRef : 10077 { 10078 ScAddress aAdr; 10079 PopSingleRef( aAdr ); 10080 if ( nGlobalError != FormulaError::NONE ) 10081 nErr = nGlobalError; 10082 else 10083 nErr = mrDoc.GetErrCode( aAdr ); 10084 } 10085 break; 10086 default: 10087 PopError(); 10088 nErr = nGlobalError; 10089 } 10090 nGlobalError = nOldError; 10091 return nErr; 10092 } 10093 10094 void ScInterpreter::ScErrorType() 10095 { 10096 FormulaError nErr = GetErrorType(); 10097 if ( nErr != FormulaError::NONE ) 10098 { 10099 nGlobalError = FormulaError::NONE; 10100 PushDouble( static_cast<double>(nErr) ); 10101 } 10102 else 10103 { 10104 PushNA(); 10105 } 10106 } 10107 10108 void ScInterpreter::ScErrorType_ODF() 10109 { 10110 FormulaError nErr = GetErrorType(); 10111 sal_uInt16 nErrType; 10112 10113 switch ( nErr ) 10114 { 10115 case FormulaError::NoCode : // #NULL! 10116 nErrType = 1; 10117 break; 10118 case FormulaError::DivisionByZero : // #DIV/0! 10119 nErrType = 2; 10120 break; 10121 case FormulaError::NoValue : // #VALUE! 10122 nErrType = 3; 10123 break; 10124 case FormulaError::NoRef : // #REF! 10125 nErrType = 4; 10126 break; 10127 case FormulaError::NoName : // #NAME? 10128 nErrType = 5; 10129 break; 10130 case FormulaError::IllegalFPOperation : // #NUM! 10131 nErrType = 6; 10132 break; 10133 case FormulaError::NotAvailable : // #N/A 10134 nErrType = 7; 10135 break; 10136 /* 10137 #GETTING_DATA is a message that can appear in Excel when a large or 10138 complex worksheet is being calculated. In Excel 2007 and newer, 10139 operations are grouped so more complicated cells may finish after 10140 earlier ones do. While the calculations are still processing, the 10141 unfinished cells may display #GETTING_DATA. 10142 Because the message is temporary and disappears when the calculations 10143 complete, this isn’t a true error. 10144 No calc error code known (yet). 10145 10146 case : // GETTING_DATA 10147 nErrType = 8; 10148 break; 10149 */ 10150 default : 10151 nErrType = 0; 10152 break; 10153 } 10154 10155 if ( nErrType ) 10156 { 10157 nGlobalError =FormulaError::NONE; 10158 PushDouble( nErrType ); 10159 } 10160 else 10161 PushNA(); 10162 } 10163 10164 static bool MayBeRegExp( std::u16string_view rStr ) 10165 { 10166 if ( rStr.empty() || (rStr.size() == 1 && rStr[0] != '.') ) 10167 return false; // single meta characters can not be a regexp 10168 // First two characters are wildcard '?' and '*' characters. 10169 std::u16string_view cre(u"?*+.[]^$\\<>()|"); 10170 return rStr.find_first_of(cre) != std::u16string_view::npos; 10171 } 10172 10173 static bool MayBeWildcard( std::u16string_view rStr ) 10174 { 10175 // Wildcards with '~' escape, if there are no wildcards then an escaped 10176 // character does not make sense, but it modifies the search pattern in an 10177 // Excel compatible wildcard search... 10178 std::u16string_view cw(u"*?~"); 10179 return rStr.find_first_of(cw) != std::u16string_view::npos; 10180 } 10181 10182 utl::SearchParam::SearchType ScInterpreter::DetectSearchType( std::u16string_view rStr, const ScDocument& rDoc ) 10183 { 10184 const auto eType = rDoc.GetDocOptions().GetFormulaSearchType(); 10185 if ((eType == utl::SearchParam::SearchType::Wildcard && MayBeWildcard(rStr)) 10186 || (eType == utl::SearchParam::SearchType::Regexp && MayBeRegExp(rStr))) 10187 return eType; 10188 return utl::SearchParam::SearchType::Normal; 10189 } 10190 10191 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, ScInterpreterContext& rContext, 10192 const ScQueryParam & rParam, const ScQueryEntry & rEntry, const ScFormulaCell* cell, 10193 const ScComplexRefData* refData ) 10194 { 10195 if (rEntry.eOp != SC_EQUAL) 10196 { 10197 // range lookup <= or >= 10198 SCCOL nCol; 10199 SCROW nRow; 10200 ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false); 10201 if( aCellIter.FindEqualOrSortedLastInRange( nCol, nRow )) 10202 { 10203 o_rResultPos.SetCol( nCol); 10204 o_rResultPos.SetRow( nRow); 10205 return true; 10206 } 10207 } 10208 else // EQUAL 10209 { 10210 if( ScQueryCellIteratorSortedCache::CanBeUsed( rDoc, rParam, rParam.nTab, cell, refData, rContext )) 10211 { 10212 ScQueryCellIteratorSortedCache aCellIter( rDoc, rContext, rParam.nTab, rParam, false); 10213 if (aCellIter.GetFirst()) 10214 { 10215 o_rResultPos.SetCol( aCellIter.GetCol()); 10216 o_rResultPos.SetRow( aCellIter.GetRow()); 10217 return true; 10218 } 10219 } 10220 else 10221 { 10222 ScQueryCellIteratorDirect aCellIter( rDoc, rContext, rParam.nTab, rParam, false); 10223 if (aCellIter.GetFirst()) 10224 { 10225 o_rResultPos.SetCol( aCellIter.GetCol()); 10226 o_rResultPos.SetRow( aCellIter.GetRow()); 10227 return true; 10228 } 10229 } 10230 } 10231 return false; 10232 } 10233 10234 // tdf#121052: 10235 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted) 10236 // [SearchCriterion] is the value searched for in the first column of the array. 10237 // [RangeArray] is the reference, which is to comprise at least two columns. 10238 // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1. 10239 // 10240 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup(): 10241 // Value referenced by [SearchCriterion] is empty. 10242 // lcl_getPrevRowWithEmptyValueLookup() performs following checks: 10243 // - if we run query with "exact match" mode (i.e. VLOOKUP) 10244 // - and if we already have the same lookup done before but for another row 10245 // which is also had empty [SearchCriterion] 10246 // 10247 // then 10248 // we could say, that for current row we could reuse results of the cached call which was done for the row2 10249 // In this case we return row index, which is >= 0. 10250 // 10251 // Elsewhere 10252 // -1 is returned, which will lead to default behavior => 10253 // complete lookup will be done in RangeArray inside lcl_LookupQuery() method. 10254 // 10255 // This method was added only for speed up to avoid several useless complete 10256 // lookups inside [RangeArray] for searching empty strings. 10257 // 10258 static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache, 10259 const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam) 10260 { 10261 // is lookup value empty? 10262 const ScQueryEntry& rEntry = rParam.GetEntry(0); 10263 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); 10264 if (! rItem.maString.getString().isEmpty()) 10265 return -1; // not found 10266 10267 // try to find the row index for which we have already performed lookup 10268 // and have some result of it inside cache 10269 return rCache.lookup( rCriteria ); 10270 } 10271 10272 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, 10273 const ScQueryParam & rParam, const ScComplexRefData* refData ) const 10274 { 10275 bool bFound = false; 10276 const ScQueryEntry& rEntry = rParam.GetEntry(0); 10277 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField); 10278 OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match"); 10279 // At least all volatile functions that generate indirect references have 10280 // to force non-cached lookup. 10281 /* TODO: We could further classify volatile functions into reference 10282 * generating and not reference generating functions to have to force less 10283 * direct lookups here. We could even further attribute volatility per 10284 * parameter so it would affect only the lookup range parameter. */ 10285 if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE) 10286 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData); 10287 else 10288 { 10289 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab, 10290 rParam.nCol2, rParam.nRow2, rParam.nTab); 10291 ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext ); 10292 ScLookupCache::QueryCriteria aCriteria( rEntry); 10293 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos, 10294 aCriteria, aPos); 10295 10296 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells 10297 // This check was added only for speed up to avoid several useless complete 10298 // lookups inside [RangeArray] for searching empty strings. 10299 if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery()) 10300 { 10301 const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam); 10302 if (nPrevRowWithEmptyValueLookup >= 0) 10303 { 10304 // make the same lookup using cache with different row index 10305 // (this lookup was already cached) 10306 ScAddress aPosPrev(aPos); 10307 aPosPrev.SetRow(nPrevRowWithEmptyValueLookup); 10308 10309 eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev ); 10310 } 10311 } 10312 10313 switch (eCacheResult) 10314 { 10315 case ScLookupCache::NOT_CACHED : 10316 case ScLookupCache::CRITERIA_DIFFERENT : 10317 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry, pMyFormulaCell, refData); 10318 if (eCacheResult == ScLookupCache::NOT_CACHED) 10319 rCache.insert( o_rResultPos, aCriteria, aPos, bFound); 10320 break; 10321 case ScLookupCache::FOUND : 10322 bFound = true; 10323 break; 10324 case ScLookupCache::NOT_AVAILABLE : 10325 ; // nothing, bFound remains FALSE 10326 break; 10327 } 10328 } 10329 return bFound; 10330 } 10331 10332 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 10333
