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