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