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