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