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