diff options
Diffstat (limited to 'sc/source/core/tool/interpr1.cxx')
-rw-r--r-- | sc/source/core/tool/interpr1.cxx | 7758 |
1 files changed, 7758 insertions, 0 deletions
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx new file mode 100644 index 000000000000..c4a8cdd346cd --- /dev/null +++ b/sc/source/core/tool/interpr1.cxx @@ -0,0 +1,7758 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <editeng/langitem.hxx> +#include <editeng/justifyitem.hxx> +#include <svx/algitem.hxx> +#include <unotools/textsearch.hxx> +#include <svl/zforlist.hxx> +#include <svl/zformat.hxx> +#include <tools/urlobj.hxx> +#include <unotools/charclass.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/printer.hxx> +#include <unotools/collatorwrapper.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <rtl/ustring.hxx> +#include <rtl/logfile.hxx> + +#include "interpre.hxx" +#include "patattr.hxx" +#include "global.hxx" +#include "document.hxx" +#include "dociter.hxx" +#include "cell.hxx" +#include "scmatrix.hxx" +#include "docoptio.hxx" +#include "globstr.hrc" +#include "attrib.hxx" +#include "jumpmatrix.hxx" + +#include <comphelper/processfactory.hxx> + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <vector> +#include <memory> +#include "cellkeytranslator.hxx" +#include "lookupcache.hxx" +#include "rangenam.hxx" +#include "compiler.hxx" +#include "externalrefmgr.hxx" +#include <basic/sbstar.hxx> +#include "doubleref.hxx" +#include "queryparam.hxx" + +#define SC_DOUBLE_MAXVALUE 1.7e307 + +IMPL_FIXEDMEMPOOL_NEWDEL( ScTokenStack, 8, 4 ) +IMPL_FIXEDMEMPOOL_NEWDEL( ScInterpreter, 32, 16 ) + +ScTokenStack* ScInterpreter::pGlobalStack = NULL; +sal_Bool ScInterpreter::bGlobalStackInUse = false; + +using namespace formula; +using ::std::auto_ptr; + +//----------------------------------------------------------------------------- +// Functions +//----------------------------------------------------------------------------- + + +void ScInterpreter::ScIfJump() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIfJump" ); + const short* pJump = pCur->GetJump(); + short nJumpCount = pJump[ 0 ]; + MatrixDoubleRefToMatrix(); + switch ( GetStackType() ) + { + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + FormulaTokenRef xNew; + ScTokenMatrixMap::const_iterator aMapIter; + // DoubleError handled by JumpMatrix + pMat->SetErrorInterpreter( NULL); + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ( nCols == 0 || nRows == 0 ) + PushIllegalArgument(); + else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( + pCur)) != pTokenMatrixMap->end())) + xNew = (*aMapIter).second; + else + { + ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows ); + for ( SCSIZE nC=0; nC < nCols; ++nC ) + { + for ( SCSIZE nR=0; nR < nRows; ++nR ) + { + double fVal; + bool bTrue; + bool bIsValue = pMat->IsValue(nC, nR); + if (bIsValue) + { + fVal = pMat->GetDouble(nC, nR); + bIsValue = ::rtl::math::isFinite(fVal); + bTrue = bIsValue && (fVal != 0.0); + if (bTrue) + fVal = 1.0; + } + else + { + // Treat empty and empty path as 0, but string + // as error. + bIsValue = (!pMat->IsString(nC, nR) || pMat->IsEmpty(nC, nR)); + bTrue = false; + fVal = (bIsValue ? 0.0 : CreateDoubleError( errNoValue)); + } + if ( bTrue ) + { // TRUE + if( nJumpCount >= 2 ) + { // THEN path + pJumpMat->SetJump( nC, nR, fVal, + pJump[ 1 ], + pJump[ nJumpCount ]); + } + else + { // no parameter given for THEN + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + else + { // FALSE + if( nJumpCount == 3 && bIsValue ) + { // ELSE path + pJumpMat->SetJump( nC, nR, fVal, + pJump[ 2 ], + pJump[ nJumpCount ]); + } + else + { // no parameter given for ELSE, + // or DoubleError + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + } + } + xNew = new ScJumpMatrixToken( pJumpMat ); + GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(pCur, xNew)); + } + PushTempToken( xNew.get()); + // set endpoint of path for main code line + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + break; + default: + { + if ( GetBool() ) + { // TRUE + if( nJumpCount >= 2 ) + { // THEN path + aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); + } + else + { // no parameter given for THEN + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(1); + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + else + { // FALSE + if( nJumpCount == 3 ) + { // ELSE path + aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] ); + } + else + { // no parameter given for ELSE + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(0); + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + } + } + } + } +} + + +void ScInterpreter::ScChoseJump() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChoseJump" ); + // We have to set a jump, if there was none chosen because of an error set + // it to endpoint. + bool bHaveJump = false; + const short* pJump = pCur->GetJump(); + short nJumpCount = pJump[ 0 ]; + MatrixDoubleRefToMatrix(); + switch ( GetStackType() ) + { + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + FormulaTokenRef xNew; + ScTokenMatrixMap::const_iterator aMapIter; + // DoubleError handled by JumpMatrix + pMat->SetErrorInterpreter( NULL); + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ( nCols == 0 || nRows == 0 ) + PushIllegalParameter(); + else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( + pCur)) != pTokenMatrixMap->end())) + xNew = (*aMapIter).second; + else + { + ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows ); + for ( SCSIZE nC=0; nC < nCols; ++nC ) + { + for ( SCSIZE nR=0; nR < nRows; ++nR ) + { + double fVal; + bool bIsValue = pMat->IsValue(nC, nR); + if ( bIsValue ) + { + fVal = pMat->GetDouble(nC, nR); + bIsValue = ::rtl::math::isFinite( fVal ); + if ( bIsValue ) + { + fVal = ::rtl::math::approxFloor( fVal); + if ( (fVal < 1) || (fVal >= nJumpCount)) + { + bIsValue = false; + fVal = CreateDoubleError( + errIllegalArgument); + } + } + } + else + { + fVal = CreateDoubleError( errNoValue); + } + if ( bIsValue ) + { + pJumpMat->SetJump( nC, nR, fVal, + pJump[ (short)fVal ], + pJump[ nJumpCount ]); + } + else + { + pJumpMat->SetJump( nC, nR, fVal, + pJump[ nJumpCount ], + pJump[ nJumpCount ]); + } + } + } + xNew = new ScJumpMatrixToken( pJumpMat ); + GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( + pCur, xNew)); + } + PushTempToken( xNew.get()); + // set endpoint of path for main code line + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); + bHaveJump = true; + } + } + break; + default: + { + double nJumpIndex = ::rtl::math::approxFloor( GetDouble() ); + if (!nGlobalError && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount)) + { + aCode.Jump( pJump[ (short) nJumpIndex ], pJump[ nJumpCount ] ); + bHaveJump = true; + } + else + PushIllegalArgument(); + } + } + if (!bHaveJump) + aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); +} + +void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, ScMatrixRef& pResMat, SCSIZE nParmCols, SCSIZE nParmRows ) +{ + SCSIZE nJumpCols, nJumpRows; + SCSIZE nResCols, nResRows; + SCSIZE nAdjustCols, nAdjustRows; + pJumpM->GetDimensions( nJumpCols, nJumpRows ); + pJumpM->GetResMatDimensions( nResCols, nResRows ); + if (( nJumpCols == 1 && nParmCols > nResCols ) || + ( nJumpRows == 1 && nParmRows > nResRows )) + { + if ( nJumpCols == 1 && nJumpRows == 1 ) + { + nAdjustCols = nParmCols > nResCols ? nParmCols : nResCols; + nAdjustRows = nParmRows > nResRows ? nParmRows : nResRows; + } + else if ( nJumpCols == 1 ) + { + nAdjustCols = nParmCols; + nAdjustRows = nResRows; + } + else + { + nAdjustCols = nResCols; + nAdjustRows = nParmRows; + } + pJumpM->SetNewResMat( nAdjustCols, nAdjustRows ); + pResMat = pJumpM->GetResultMatrix(); + } +} + +bool ScInterpreter::JumpMatrix( short nStackLevel ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::JumpMatrix" ); + pJumpMatrix = static_cast<ScToken*>(pStack[sp-nStackLevel])->GetJumpMatrix(); + ScMatrixRef pResMat = pJumpMatrix->GetResultMatrix(); + SCSIZE nC, nR; + if ( nStackLevel == 2 ) + { + if ( aCode.HasStacked() ) + aCode.Pop(); // pop what Jump() pushed + else + { + DBG_ERRORFILE( "ScInterpreter::JumpMatrix: pop goes the weasel" ); + } + + if ( !pResMat ) + { + Pop(); + SetError( errUnknownStackVariable ); + } + else + { + pJumpMatrix->GetPos( nC, nR ); + switch ( GetStackType() ) + { + case svDouble: + { + double fVal = GetDouble(); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + } + pResMat->PutDouble( fVal, nC, nR ); + } + break; + case svString: + { + const String& rStr = GetString(); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( nGlobalError), + nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( rStr, nC, nR ); + } + break; + case svSingleRef: + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( nGlobalError), + nC, nR); + nGlobalError = 0; + } + else + { + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + pResMat->PutEmpty( nC, nR ); + else if (HasCellValueData( pCell)) + { + double fVal = GetCellValue( aAdr, pCell); + if ( nGlobalError ) + { + fVal = CreateDoubleError( + nGlobalError); + nGlobalError = 0; + } + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + String aStr; + GetCellString( aStr, pCell ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( + nGlobalError), nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( aStr, nC, nR); + } + } + } + break; + case svDoubleRef: + { // upper left plus offset within matrix + double fVal; + ScRange aRange; + PopDoubleRef( aRange ); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + // Do not modify the original range because we use it + // to adjust the size of the result matrix if necessary. + ScAddress aAdr( aRange.aStart); + sal_uLong nCol = (sal_uLong)aAdr.Col() + nC; + sal_uLong nRow = (sal_uLong)aAdr.Row() + nR; + if ((nCol > static_cast<sal_uLong>(aRange.aEnd.Col()) && + aRange.aEnd.Col() != aRange.aStart.Col()) + || (nRow > static_cast<sal_uLong>(aRange.aEnd.Row()) && + aRange.aEnd.Row() != aRange.aStart.Row())) + { + fVal = CreateDoubleError( NOTAVAILABLE ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + // Replicate column and/or row of a vector if it is + // one. Note that this could be a range reference + // that in fact consists of only one cell, e.g. A1:A1 + if (aRange.aEnd.Col() == aRange.aStart.Col()) + nCol = aRange.aStart.Col(); + if (aRange.aEnd.Row() == aRange.aStart.Row()) + nRow = aRange.aStart.Row(); + aAdr.SetCol( static_cast<SCCOL>(nCol) ); + aAdr.SetRow( static_cast<SCROW>(nRow) ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + pResMat->PutEmpty( nC, nR ); + else if (HasCellValueData( pCell)) + { + double fCellVal = GetCellValue( aAdr, pCell); + if ( nGlobalError ) + { + fCellVal = CreateDoubleError( + nGlobalError); + nGlobalError = 0; + } + pResMat->PutDouble( fCellVal, nC, nR ); + } + else + { + String aStr; + GetCellString( aStr, pCell ); + if ( nGlobalError ) + { + pResMat->PutDouble( CreateDoubleError( + nGlobalError), nC, nR); + nGlobalError = 0; + } + else + pResMat->PutString( aStr, nC, nR ); + } + } + SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1; + SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1; + lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nParmCols, nParmRows ); + } + } + break; + case svMatrix: + { // match matrix offsets + double fVal; + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError ) + { + fVal = CreateDoubleError( nGlobalError ); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + else if ( !pMat ) + { + fVal = CreateDoubleError( errUnknownVariable ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + SCSIZE nCols, nRows; + pMat->GetDimensions( nCols, nRows ); + if ((nCols <= nC && nCols != 1) || + (nRows <= nR && nRows != 1)) + { + fVal = CreateDoubleError( NOTAVAILABLE ); + pResMat->PutDouble( fVal, nC, nR ); + } + else + { + if ( pMat->IsValue( nC, nR ) ) + { + fVal = pMat->GetDouble( nC, nR ); + pResMat->PutDouble( fVal, nC, nR ); + } + else if ( pMat->IsEmpty( nC, nR ) ) + pResMat->PutEmpty( nC, nR ); + else + { + const String& rStr = pMat->GetString( nC, nR ); + pResMat->PutString( rStr, nC, nR ); + } + } + lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nCols, nRows ); + } + } + break; + case svError: + { + PopError(); + double fVal = CreateDoubleError( nGlobalError); + nGlobalError = 0; + pResMat->PutDouble( fVal, nC, nR ); + } + break; + default: + { + Pop(); + double fVal = CreateDoubleError( errIllegalArgument); + pResMat->PutDouble( fVal, nC, nR ); + } + } + } + } + bool bCont = pJumpMatrix->Next( nC, nR ); + if ( bCont ) + { + double fBool; + short nStart, nNext, nStop; + pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); + while ( bCont && nStart == nNext ) + { // push all results that have no jump path + if ( pResMat ) + { + // a sal_False without path results in an empty path value + if ( fBool == 0.0 ) + pResMat->PutEmptyPath( nC, nR ); + else + pResMat->PutDouble( fBool, nC, nR ); + } + bCont = pJumpMatrix->Next( nC, nR ); + if ( bCont ) + pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); + } + if ( bCont && nStart != nNext ) + { + const ScTokenVec* pParams = pJumpMatrix->GetJumpParameters(); + if ( pParams ) + { + for ( ScTokenVec::const_iterator i = pParams->begin(); + i != pParams->end(); ++i ) + { + // This is not the current state of the interpreter, so + // push without error, and elements' errors are coded into + // double. + PushWithoutError( *(*i)); + } + } + aCode.Jump( nStart, nNext, nStop ); + } + } + if ( !bCont ) + { // we're done with it, throw away jump matrix, keep result + pJumpMatrix = NULL; + Pop(); + PushMatrix( pResMat ); + // Remove jump matrix from map and remember result matrix in case it + // could be reused in another path of the same condition. + if (pTokenMatrixMap) + { + pTokenMatrixMap->erase( pCur); + pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur, + pStack[sp-1])); + } + return true; + } + return false; +} + + +ScCompareOptions::ScCompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg ) : + aQueryEntry(rEntry), + bRegEx(bReg), + bMatchWholeCell(pDoc->GetDocOptions().IsMatchWholeCell()), + bIgnoreCase(true) +{ + bRegEx = (bRegEx && (aQueryEntry.eOp == SC_EQUAL || aQueryEntry.eOp == SC_NOT_EQUAL)); + // Interpreter functions usually are case insensitive, except the simple + // comparison operators, for which these options aren't used. Override in + // struct if needed. +} + + +double ScInterpreter::CompareFunc( const ScCompare& rComp, ScCompareOptions* pOptions ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CompareFunc" ); + // Keep DoubleError if encountered + // #i40539# if bEmpty is set, bVal/nVal are uninitialized + if ( !rComp.bEmpty[0] && rComp.bVal[0] && !::rtl::math::isFinite( rComp.nVal[0])) + return rComp.nVal[0]; + if ( !rComp.bEmpty[1] && rComp.bVal[1] && !::rtl::math::isFinite( rComp.nVal[1])) + return rComp.nVal[1]; + + double fRes = 0; + if ( rComp.bEmpty[ 0 ] ) + { + if ( rComp.bEmpty[ 1 ] ) + ; // empty cell == empty cell, fRes 0 + else if( rComp.bVal[ 1 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 1 ], 0.0 ) ) + { + if ( rComp.nVal[ 1 ] < 0.0 ) + fRes = 1; // empty cell > -x + else + fRes = -1; // empty cell < x + } + // else: empty cell == 0.0 + } + else + { + if ( rComp.pVal[ 1 ]->Len() ) + fRes = -1; // empty cell < "..." + // else: empty cell == "" + } + } + else if ( rComp.bEmpty[ 1 ] ) + { + if( rComp.bVal[ 0 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], 0.0 ) ) + { + if ( rComp.nVal[ 0 ] < 0.0 ) + fRes = -1; // -x < empty cell + else + fRes = 1; // x > empty cell + } + // else: empty cell == 0.0 + } + else + { + if ( rComp.pVal[ 0 ]->Len() ) + fRes = 1; // "..." > empty cell + // else: "" == empty cell + } + } + else if( rComp.bVal[ 0 ] ) + { + if( rComp.bVal[ 1 ] ) + { + if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], rComp.nVal[ 1 ] ) ) + { + if( rComp.nVal[ 0 ] - rComp.nVal[ 1 ] < 0 ) + fRes = -1; + else + fRes = 1; + } + } + else + fRes = -1; // number is less than string + } + else if( rComp.bVal[ 1 ] ) + fRes = 1; // number is less than string + else + { + // Both strings. + if (pOptions) + { + // All similar to Sctable::ValidQuery(), *rComp.pVal[1] actually + // is/must be identical to *rEntry.pStr, which is essential for + // regex to work through GetSearchTextPtr(). + ScQueryEntry& rEntry = pOptions->aQueryEntry; + DBG_ASSERT( *rComp.pVal[1] == *rEntry.pStr, "ScInterpreter::CompareFunc: broken options"); + if (pOptions->bRegEx) + { + xub_StrLen nStart = 0; + xub_StrLen nStop = rComp.pVal[0]->Len(); + bool bMatch = rEntry.GetSearchTextPtr( + !pOptions->bIgnoreCase)->SearchFrwrd( *rComp.pVal[0], + &nStart, &nStop); + if (bMatch && pOptions->bMatchWholeCell && (nStart != 0 || nStop != rComp.pVal[0]->Len())) + bMatch = false; // RegEx must match entire string. + fRes = (bMatch ? 0 : 1); + } + else if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) + { + ::utl::TransliterationWrapper* pTransliteration = + (pOptions->bIgnoreCase ? ScGlobal::GetpTransliteration() : + ScGlobal::GetCaseTransliteration()); + bool bMatch; + if (pOptions->bMatchWholeCell) + bMatch = pTransliteration->isEqual( *rComp.pVal[0], *rComp.pVal[1]); + else + { + String aCell( pTransliteration->transliterate( + *rComp.pVal[0], ScGlobal::eLnge, 0, + rComp.pVal[0]->Len(), NULL)); + String aQuer( pTransliteration->transliterate( + *rComp.pVal[1], ScGlobal::eLnge, 0, + rComp.pVal[1]->Len(), NULL)); + bMatch = (aCell.Search( aQuer ) != STRING_NOTFOUND); + } + fRes = (bMatch ? 0 : 1); + } + else if (pOptions->bIgnoreCase) + fRes = (double) ScGlobal::GetCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + else + fRes = (double) ScGlobal::GetCaseCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + } + else if (pDok->GetDocOptions().IsIgnoreCase()) + fRes = (double) ScGlobal::GetCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + else + fRes = (double) ScGlobal::GetCaseCollator()->compareString( + *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] ); + } + return fRes; +} + + +double ScInterpreter::Compare() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::Compare" ); + String aVal1, aVal2; + ScCompare aComp( &aVal1, &aVal2 ); + for( short i = 1; i >= 0; i-- ) + { + switch ( GetRawStackType() ) + { + case svEmptyCell: + Pop(); + aComp.bEmpty[ i ] = sal_True; + break; + case svMissing: + case svDouble: + aComp.nVal[ i ] = GetDouble(); + aComp.bVal[ i ] = sal_True; + break; + case svString: + *aComp.pVal[ i ] = GetString(); + aComp.bVal[ i ] = false; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + aComp.bEmpty[ i ] = sal_True; + else if (HasCellStringData( pCell)) + { + GetCellString( *aComp.pVal[ i ], pCell); + aComp.bVal[ i ] = false; + } + else + { + aComp.nVal[ i ] = GetCellValue( aAdr, pCell ); + aComp.bVal[ i ] = sal_True; + } + } + break; + case svExternalSingleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (!pMat) + { + SetError( errIllegalParameter); + break; + } + + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (!nC || !nR) + { + SetError( errIllegalParameter); + break; + } + if (pMat->IsEmpty(0, 0)) + aComp.bEmpty[i] = true; + else if (pMat->IsString(0, 0)) + { + *aComp.pVal[i] = pMat->GetString(0, 0); + aComp.bVal[i] = false; + } + else + { + aComp.nVal[i] = pMat->GetDouble(0, 0); + aComp.bVal[i] = true; + } + } + break; + case svExternalDoubleRef: + // TODO: Find out how to handle this... + default: + SetError( errIllegalParameter); + break; + } + } + if( nGlobalError ) + return 0; + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + return CompareFunc( aComp ); +} + + +ScMatrixRef ScInterpreter::CompareMat( ScCompareOptions* pOptions ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CompareMat" ); + String aVal1, aVal2; + ScCompare aComp( &aVal1, &aVal2 ); + ScMatrixRef pMat[2]; + ScAddress aAdr; + for( short i = 1; i >= 0; i-- ) + { + switch (GetRawStackType()) + { + case svEmptyCell: + Pop(); + aComp.bEmpty[ i ] = sal_True; + break; + case svMissing: + case svDouble: + aComp.nVal[ i ] = GetDouble(); + aComp.bVal[ i ] = sal_True; + break; + case svString: + *aComp.pVal[ i ] = GetString(); + aComp.bVal[ i ] = false; + break; + case svSingleRef: + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellEmptyData( pCell)) + aComp.bEmpty[ i ] = sal_True; + else if (HasCellStringData( pCell)) + { + GetCellString( *aComp.pVal[ i ], pCell); + aComp.bVal[ i ] = false; + } + else + { + aComp.nVal[ i ] = GetCellValue( aAdr, pCell ); + aComp.bVal[ i ] = sal_True; + } + } + break; + case svDoubleRef: + case svMatrix: + pMat[ i ] = GetMatrix(); + if ( !pMat[ i ] ) + SetError( errIllegalParameter); + else + pMat[i]->SetErrorInterpreter( NULL); + // errors are transported as DoubleError inside matrix + break; + default: + SetError( errIllegalParameter); + break; + } + } + ScMatrixRef pResMat = NULL; + if( !nGlobalError ) + { + if ( pMat[0] && pMat[1] ) + { + SCSIZE nC0, nC1; + SCSIZE nR0, nR1; + pMat[0]->GetDimensions( nC0, nR0 ); + pMat[1]->GetDimensions( nC1, nR1 ); + SCSIZE nC = Max( nC0, nC1 ); + SCSIZE nR = Max( nR0, nR1 ); + pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + return NULL; + for ( SCSIZE j=0; j<nC; j++ ) + { + for ( SCSIZE k=0; k<nR; k++ ) + { + SCSIZE nCol = j, nRow = k; + if ( pMat[0]->ValidColRowOrReplicated( nCol, nRow ) && + pMat[1]->ValidColRowOrReplicated( nCol, nRow )) + { + for ( short i=1; i>=0; i-- ) + { + if ( pMat[i]->IsString(j,k) ) + { + aComp.bVal[i] = false; + *aComp.pVal[i] = pMat[i]->GetString(j,k); + aComp.bEmpty[i] = pMat[i]->IsEmpty(j,k); + } + else + { + aComp.bVal[i] = sal_True; + aComp.nVal[i] = pMat[i]->GetDouble(j,k); + aComp.bEmpty[i] = false; + } + } + pResMat->PutDouble( CompareFunc( aComp, pOptions ), j,k ); + } + else + pResMat->PutString( ScGlobal::GetRscString(STR_NO_VALUE), j,k ); + } + } + } + else if ( pMat[0] || pMat[1] ) + { + short i = ( pMat[0] ? 0 : 1); + SCSIZE nC, nR; + pMat[i]->GetDimensions( nC, nR ); + pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + return NULL; + + for (SCSIZE j = 0; j < nC; ++j) + { + for (SCSIZE k = 0; k < nR; ++k) + { + if ( pMat[i]->IsValue(j,k) ) + { + aComp.bVal[i] = true; + aComp.nVal[i] = pMat[i]->GetDouble(j,k); + aComp.bEmpty[i] = false; + } + else + { + aComp.bVal[i] = false; + *aComp.pVal[i] = pMat[i]->GetString(j,k); + aComp.bEmpty[i] = pMat[i]->IsEmpty(j,k); + } + pResMat->PutDouble( CompareFunc(aComp, pOptions), j, k); + } + } + } + } + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + return pResMat; +} + + +ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, ScCompareOptions& rOptions ) +{ + short nSaveCurFmtType = nCurFmtType; + short nSaveFuncFmtType = nFuncFmtType; + PushMatrix( pMat); + if (rOptions.aQueryEntry.bQueryByString) + PushString( *rOptions.aQueryEntry.pStr); + else + PushDouble( rOptions.aQueryEntry.nVal); + ScMatrixRef pResultMatrix = CompareMat( &rOptions); + nCurFmtType = nSaveCurFmtType; + nFuncFmtType = nSaveFuncFmtType; + if (nGlobalError || !pResultMatrix) + { + SetError( errIllegalParameter); + return pResultMatrix; + } + + switch (rOptions.aQueryEntry.eOp) + { + case SC_EQUAL: + pResultMatrix->CompareEqual(); + break; + case SC_LESS: + pResultMatrix->CompareLess(); + break; + case SC_GREATER: + pResultMatrix->CompareGreater(); + break; + case SC_LESS_EQUAL: + pResultMatrix->CompareLessEqual(); + break; + case SC_GREATER_EQUAL: + pResultMatrix->CompareGreaterEqual(); + break; + case SC_NOT_EQUAL: + pResultMatrix->CompareNotEqual(); + break; + default: + SetError( errIllegalArgument); + OSL_TRACE( "ScInterpreter::QueryMat: unhandled comparison operator: %d", (int)rOptions.aQueryEntry.eOp); + } + return pResultMatrix; +} + + +void ScInterpreter::ScEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() == 0 ); +} + + +void ScInterpreter::ScNotEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNotEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareNotEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() != 0 ); +} + + +void ScInterpreter::ScLess() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLess" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareLess(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() < 0 ); +} + + +void ScInterpreter::ScGreater() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGreater" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareGreater(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() > 0 ); +} + + +void ScInterpreter::ScLessEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLessEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareLessEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() <= 0 ); +} + + +void ScInterpreter::ScGreaterEqual() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScGreaterEqual" ); + if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) + { + ScMatrixRef pMat = CompareMat(); + if ( !pMat ) + PushIllegalParameter(); + else + { + pMat->CompareGreaterEqual(); + PushMatrix( pMat ); + } + } + else + PushInt( Compare() >= 0 ); +} + + +void ScInterpreter::ScAnd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAnd" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + sal_Bool bHaveValue = false; + short nRes = sal_True; + size_t nRefInList = 0; + while( nParamCount-- > 0) + { + if ( !nGlobalError ) + { + switch ( GetStackType() ) + { + case svDouble : + bHaveValue = sal_True; + nRes &= ( PopDouble() != 0.0 ); + break; + case svString : + Pop(); + SetError( errNoValue ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData( pCell ) ) + { + bHaveValue = sal_True; + nRes &= ( GetCellValue( aAdr, pCell ) != 0.0 ); + } + // else: Xcl raises no error here + } + } + break; + case svDoubleRef: + case svRefList: + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( !nGlobalError ) + { + double fVal; + sal_uInt16 nErr = 0; + ScValueIterator aValIter( pDok, aRange ); + if ( aValIter.GetFirst( fVal, nErr ) ) + { + bHaveValue = sal_True; + do + { + nRes &= ( fVal != 0.0 ); + } while ( (nErr == 0) && + aValIter.GetNext( fVal, nErr ) ); + } + SetError( nErr ); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = GetMatrix(); + if ( pMat ) + { + bHaveValue = sal_True; + double fVal = pMat->And(); + sal_uInt16 nErr = GetDoubleErrorValue( fVal ); + if ( nErr ) + { + SetError( nErr ); + nRes = false; + } + else + nRes &= (fVal != 0.0); + } + // else: GetMatrix did set errIllegalParameter + } + break; + default: + Pop(); + SetError( errIllegalParameter); + } + } + else + Pop(); + } + if ( bHaveValue ) + PushInt( nRes ); + else + PushNoValue(); + } +} + + +void ScInterpreter::ScOr() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOr" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 1 ) ) + { + sal_Bool bHaveValue = false; + short nRes = false; + size_t nRefInList = 0; + while( nParamCount-- > 0) + { + if ( !nGlobalError ) + { + switch ( GetStackType() ) + { + case svDouble : + bHaveValue = sal_True; + nRes |= ( PopDouble() != 0.0 ); + break; + case svString : + Pop(); + SetError( errNoValue ); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + { + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData( pCell ) ) + { + bHaveValue = sal_True; + nRes |= ( GetCellValue( aAdr, pCell ) != 0.0 ); + } + // else: Xcl raises no error here + } + } + break; + case svDoubleRef: + case svRefList: + { + ScRange aRange; + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( !nGlobalError ) + { + double fVal; + sal_uInt16 nErr = 0; + ScValueIterator aValIter( pDok, aRange ); + if ( aValIter.GetFirst( fVal, nErr ) ) + { + bHaveValue = sal_True; + do + { + nRes |= ( fVal != 0.0 ); + } while ( (nErr == 0) && + aValIter.GetNext( fVal, nErr ) ); + } + SetError( nErr ); + } + } + break; + case svMatrix: + { + bHaveValue = sal_True; + ScMatrixRef pMat = GetMatrix(); + if ( pMat ) + { + bHaveValue = sal_True; + double fVal = pMat->Or(); + sal_uInt16 nErr = GetDoubleErrorValue( fVal ); + if ( nErr ) + { + SetError( nErr ); + nRes = false; + } + else + nRes |= (fVal != 0.0); + } + // else: GetMatrix did set errIllegalParameter + } + break; + default: + Pop(); + SetError( errIllegalParameter); + } + } + else + Pop(); + } + if ( bHaveValue ) + PushInt( nRes ); + else + PushNoValue(); + } +} + + +void ScInterpreter::ScNeg() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNeg" ); + // Simple negation doesn't change current format type to number, keep + // current type. + nFuncFmtType = nCurFmtType; + switch ( GetStackType() ) + { + case svMatrix : + { + ScMatrixRef pMat = GetMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + SCSIZE nC, nR; + pMat->GetDimensions( nC, nR ); + ScMatrixRef pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + PushIllegalArgument(); + else + { + for (SCSIZE i = 0; i < nC; ++i) + { + for (SCSIZE j = 0; j < nR; ++j) + { + if ( pMat->IsValueOrEmpty(i,j) ) + pResMat->PutDouble( -pMat->GetDouble(i,j), i, j ); + else + pResMat->PutString( + ScGlobal::GetRscString( STR_NO_VALUE ), i, j ); + } + } + PushMatrix( pResMat ); + } + } + } + break; + default: + PushDouble( -GetDouble() ); + } +} + + +void ScInterpreter::ScPercentSign() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPercentSign" ); + nFuncFmtType = NUMBERFORMAT_PERCENT; + const FormulaToken* pSaveCur = pCur; + sal_uInt8 nSavePar = cPar; + PushInt( 100 ); + cPar = 2; + FormulaByteToken aDivOp( ocDiv, cPar ); + pCur = &aDivOp; + ScDiv(); + pCur = pSaveCur; + cPar = nSavePar; +} + + +void ScInterpreter::ScNot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScNot" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + switch ( GetStackType() ) + { + case svMatrix : + { + ScMatrixRef pMat = GetMatrix(); + if ( !pMat ) + PushIllegalParameter(); + else + { + SCSIZE nC, nR; + pMat->GetDimensions( nC, nR ); + ScMatrixRef pResMat = GetNewMat( nC, nR); + if ( !pResMat ) + PushIllegalArgument(); + else + { + for (SCSIZE i = 0; i < nC; ++i) + { + for (SCSIZE j = 0; j < nR; ++j) + { + if ( pMat->IsValueOrEmpty(i,j) ) + pResMat->PutDouble( (pMat->GetDouble(i,j) == 0.0), i, j ); + else + pResMat->PutString( + ScGlobal::GetRscString( STR_NO_VALUE ), i, j ); + } + } + PushMatrix( pResMat ); + } + } + } + break; + default: + PushInt( GetDouble() == 0.0 ); + } +} + + +void ScInterpreter::ScPi() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPi" ); + PushDouble(F_PI); +} + + +void ScInterpreter::ScRandom() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRandom" ); + PushDouble((double)rand() / ((double)RAND_MAX+1.0)); +} + + +void ScInterpreter::ScTrue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrue" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(1); +} + + +void ScInterpreter::ScFalse() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFalse" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + PushInt(0); +} + + +void ScInterpreter::ScDeg() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDeg" ); + PushDouble((GetDouble() / F_PI) * 180.0); +} + + +void ScInterpreter::ScRad() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRad" ); + PushDouble(GetDouble() * (F_PI / 180)); +} + + +void ScInterpreter::ScSin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSin" ); + PushDouble(::rtl::math::sin(GetDouble())); +} + + +void ScInterpreter::ScCos() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCos" ); + PushDouble(::rtl::math::cos(GetDouble())); +} + + +void ScInterpreter::ScTan() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTan" ); + PushDouble(::rtl::math::tan(GetDouble())); +} + + +void ScInterpreter::ScCot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCot" ); + PushDouble(1.0 / ::rtl::math::tan(GetDouble())); +} + + +void ScInterpreter::ScArcSin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcSin" ); + PushDouble(asin(GetDouble())); +} + + +void ScInterpreter::ScArcCos() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCos" ); + PushDouble(acos(GetDouble())); +} + + +void ScInterpreter::ScArcTan() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTan" ); + PushDouble(atan(GetDouble())); +} + + +void ScInterpreter::ScArcCot() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCot" ); + PushDouble((F_PI2) - atan(GetDouble())); +} + + +void ScInterpreter::ScSinHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSinHyp" ); + PushDouble(sinh(GetDouble())); +} + + +void ScInterpreter::ScCosHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCosHyp" ); + PushDouble(cosh(GetDouble())); +} + + +void ScInterpreter::ScTanHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTanHyp" ); + PushDouble(tanh(GetDouble())); +} + + +void ScInterpreter::ScCotHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCotHyp" ); + PushDouble(1.0 / tanh(GetDouble())); +} + + +void ScInterpreter::ScArcSinHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcSinHyp" ); + PushDouble( ::rtl::math::asinh( GetDouble())); +} + +void ScInterpreter::ScArcCosHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCosHyp" ); + double fVal = GetDouble(); + if (fVal < 1.0) + PushIllegalArgument(); + else + PushDouble( ::rtl::math::acosh( fVal)); +} + +void ScInterpreter::ScArcTanHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcTanHyp" ); + double fVal = GetDouble(); + if (fabs(fVal) >= 1.0) + PushIllegalArgument(); + else + PushDouble( ::rtl::math::atanh( fVal)); +} + + +void ScInterpreter::ScArcCotHyp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScArcCotHyp" ); + double nVal = GetDouble(); + if (fabs(nVal) <= 1.0) + PushIllegalArgument(); + else + PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0))); +} + + +void ScInterpreter::ScExp() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExp" ); + PushDouble(exp(GetDouble())); +} + + +void ScInterpreter::ScSqrt() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSqrt" ); + double fVal = GetDouble(); + if (fVal >= 0.0) + PushDouble(sqrt(fVal)); + else + PushIllegalArgument(); +} + + +void ScInterpreter::ScIsEmpty() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsEmpty" ); + short nRes = 0; + nFuncFmtType = NUMBERFORMAT_LOGICAL; + switch ( GetRawStackType() ) + { + case svEmptyCell: + { + FormulaTokenRef p = PopToken(); + if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited()) + nRes = 1; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + // NOTE: this could test also on inherited emptiness, but then the + // cell tested wouldn't be empty. Must correspond with + // ScCountEmptyCells(). + // if (HasCellEmptyData( GetCell( aAdr))) + CellType eCellType = GetCellType( GetCell( aAdr ) ); + if((eCellType == CELLTYPE_NONE) || (eCellType == CELLTYPE_NOTE)) + nRes = 1; + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = pMat->IsEmpty( 0, 0); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = pMat->IsEmpty( nC, nR); + // else: sal_False, not empty (which is what Xcl does) + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +short ScInterpreter::IsString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsString" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetRawStackType() ) + { + case svString: + Pop(); + nRes = 1; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + nRes = 1; + break; + case CELLTYPE_FORMULA : + nRes = !((ScFormulaCell*)pCell)->IsValue() && + !((ScFormulaCell*)pCell)->IsEmpty(); + break; + default: + ; // nothing + } + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = pMat->IsString(0, 0) && !pMat->IsEmpty(0, 0); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = pMat->IsString( nC, nR) && !pMat->IsEmpty( nC, nR); + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + return nRes; +} + + +void ScInterpreter::ScIsString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsString" ); + PushInt( IsString() ); +} + + +void ScInterpreter::ScIsNonString() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsNonString" ); + PushInt( !IsString() ); +} + + +void ScInterpreter::ScIsLogical() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsLogical" ); + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + if (HasCellValueData(pCell)) + { + sal_uLong nFormat = GetCellNumberFormat( aAdr, pCell ); + nRes = ( pFormatter->GetType(nFormat) + == NUMBERFORMAT_LOGICAL); + } + } + } + break; + case svMatrix: + // TODO: we don't have type information for arrays except + // numerical/string. + // Fall thru + default: + PopError(); + if ( !nGlobalError ) + nRes = ( nCurFmtType == NUMBERFORMAT_LOGICAL ); + } + nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScType" ); + short nType = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + // NOTE: this is Xcl nonsense! + case CELLTYPE_NOTE : + nType = 1; // empty cell is value (0) + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + nType = 2; + break; + case CELLTYPE_VALUE : + { + sal_uLong nFormat = GetCellNumberFormat( aAdr, pCell ); + if (pFormatter->GetType(nFormat) + == NUMBERFORMAT_LOGICAL) + nType = 4; + else + nType = 1; + } + break; + case CELLTYPE_FORMULA : + nType = 8; + break; + default: + PushIllegalArgument(); + } + } + else + nType = 16; + } + break; + case svString: + PopError(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 2; + break; + case svMatrix: + PopMatrix(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 64; + // we could return the type of one element if in JumpMatrix or + // ForceArray mode, but Xcl doesn't ... + break; + default: + PopError(); + if ( nGlobalError ) + { + nType = 16; + nGlobalError = 0; + } + else + nType = 1; + } + PushInt( nType ); +} + + +inline sal_Bool lcl_FormatHasNegColor( const SvNumberformat* pFormat ) +{ + return pFormat && pFormat->GetColor( 1 ); +} + + +inline sal_Bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat ) +{ + return pFormat && (pFormat->GetFormatstring().Search( '(' ) != STRING_NOTFOUND); +} + + +void ScInterpreter::ScCell() +{ // ATTRIBUTE ; [REF] + sal_uInt8 nParamCount = GetByte(); + if( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + ScAddress aCellPos( aPos ); + sal_Bool bError = false; + if( nParamCount == 2 ) + bError = !PopDoubleRefOrSingleRef( aCellPos ); + String aInfoType( GetString() ); + if( bError || nGlobalError ) + PushIllegalParameter(); + else + { + String aFuncResult; + ScBaseCell* pCell = GetCell( aCellPos ); + + ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell); + +// *** ADDRESS INFO *** + if( aInfoType.EqualsAscii( "COL" ) ) + { // column number (1-based) + PushInt( aCellPos.Col() + 1 ); + } + else if( aInfoType.EqualsAscii( "ROW" ) ) + { // row number (1-based) + PushInt( aCellPos.Row() + 1 ); + } + else if( aInfoType.EqualsAscii( "SHEET" ) ) + { // table number (1-based) + PushInt( aCellPos.Tab() + 1 ); + } + else if( aInfoType.EqualsAscii( "ADDRESS" ) ) + { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW + sal_uInt16 nFlags = (aCellPos.Tab() == aPos.Tab()) ? (SCA_ABS) : (SCA_ABS_3D); + aCellPos.Format( aFuncResult, nFlags, pDok, pDok->GetAddressConvention() ); + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "FILENAME" ) ) + { // file name and table name: 'FILENAME'#$TABLE + SCTAB nTab = aCellPos.Tab(); + if( nTab < pDok->GetTableCount() ) + { + if( pDok->GetLinkMode( nTab ) == SC_LINK_VALUE ) + pDok->GetName( nTab, aFuncResult ); + else + { + SfxObjectShell* pShell = pDok->GetDocumentShell(); + if( pShell && pShell->GetMedium() ) + { + aFuncResult = (sal_Unicode) '\''; + const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject(); + aFuncResult += String( rURLObj.GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ) ); + aFuncResult.AppendAscii( "'#$" ); + String aTabName; + pDok->GetName( nTab, aTabName ); + aFuncResult += aTabName; + } + } + } + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "COORD" ) ) + { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW + // Yes, passing tab as col is intentional! + ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format( + aFuncResult, (SCA_COL_ABSOLUTE|SCA_VALID_COL), NULL, pDok->GetAddressConvention() ); + aFuncResult += ':'; + String aCellStr; + aCellPos.Format( aCellStr, (SCA_COL_ABSOLUTE|SCA_VALID_COL|SCA_ROW_ABSOLUTE|SCA_VALID_ROW), + NULL, pDok->GetAddressConvention() ); + aFuncResult += aCellStr; + PushString( aFuncResult ); + } + +// *** CELL PROPERTIES *** + else if( aInfoType.EqualsAscii( "CONTENTS" ) ) + { // contents of the cell, no formatting + if( pCell && pCell->HasStringData() ) + { + GetCellString( aFuncResult, pCell ); + PushString( aFuncResult ); + } + else + PushDouble( GetCellValue( aCellPos, pCell ) ); + } + else if( aInfoType.EqualsAscii( "TYPE" ) ) + { // b = blank; l = string (label); v = otherwise (value) + if( HasCellStringData( pCell ) ) + aFuncResult = 'l'; + else + aFuncResult = HasCellValueData( pCell ) ? 'v' : 'b'; + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "WIDTH" ) ) + { // column width (rounded off as count of zero characters in standard font and size) + Printer* pPrinter = pDok->GetPrinter(); + MapMode aOldMode( pPrinter->GetMapMode() ); + Font aOldFont( pPrinter->GetFont() ); + Font aDefFont; + + pPrinter->SetMapMode( MAP_TWIP ); + // font color doesn't matter here + pDok->GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter ); + pPrinter->SetFont( aDefFont ); + long nZeroWidth = pPrinter->GetTextWidth( String( '0' ) ); + pPrinter->SetFont( aOldFont ); + pPrinter->SetMapMode( aOldMode ); + int nZeroCount = (int)(pDok->GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth); + PushInt( nZeroCount ); + } + else if( aInfoType.EqualsAscii( "PREFIX" ) ) + { // ' = left; " = right; ^ = centered + if( HasCellStringData( pCell ) ) + { + const SvxHorJustifyItem* pJustAttr = (const SvxHorJustifyItem*) + pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_HOR_JUSTIFY ); + switch( pJustAttr->GetValue() ) + { + case SVX_HOR_JUSTIFY_STANDARD: + case SVX_HOR_JUSTIFY_LEFT: + case SVX_HOR_JUSTIFY_BLOCK: aFuncResult = '\''; break; + case SVX_HOR_JUSTIFY_CENTER: aFuncResult = '^'; break; + case SVX_HOR_JUSTIFY_RIGHT: aFuncResult = '"'; break; + case SVX_HOR_JUSTIFY_REPEAT: aFuncResult = '\\'; break; + } + } + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "PROTECT" ) ) + { // 1 = cell locked + const ScProtectionAttr* pProtAttr = (const ScProtectionAttr*) + pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_PROTECTION ); + PushInt( pProtAttr->GetProtection() ? 1 : 0 ); + } + +// *** FORMATTING *** + else if( aInfoType.EqualsAscii( "FORMAT" ) ) + { // specific format code for standard formats + sal_uLong nFormat = pDok->GetNumberFormat( aCellPos ); + sal_Bool bAppendPrec = sal_True; + sal_uInt16 nPrec, nLeading; + sal_Bool bThousand, bIsRed; + pFormatter->GetFormatSpecialInfo( nFormat, bThousand, bIsRed, nPrec, nLeading ); + + switch( pFormatter->GetType( nFormat ) ) + { + case NUMBERFORMAT_NUMBER: aFuncResult = (bThousand ? ',' : 'F'); break; + case NUMBERFORMAT_CURRENCY: aFuncResult = 'C'; break; + case NUMBERFORMAT_SCIENTIFIC: aFuncResult = 'S'; break; + case NUMBERFORMAT_PERCENT: aFuncResult = 'P'; break; + default: + { + bAppendPrec = false; + switch( pFormatter->GetIndexTableOffset( nFormat ) ) + { + case NF_DATE_SYSTEM_SHORT: + case NF_DATE_SYS_DMMMYY: + case NF_DATE_SYS_DDMMYY: + case NF_DATE_SYS_DDMMYYYY: + case NF_DATE_SYS_DMMMYYYY: + case NF_DATE_DIN_DMMMYYYY: + case NF_DATE_SYS_DMMMMYYYY: + case NF_DATE_DIN_DMMMMYYYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D1" ) ); break; + case NF_DATE_SYS_DDMMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D2" ) ); break; + case NF_DATE_SYS_MMYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D3" ) ); break; + case NF_DATETIME_SYSTEM_SHORT_HHMM: + case NF_DATETIME_SYS_DDMMYYYY_HHMMSS: + aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D4" ) ); break; + case NF_DATE_DIN_MMDD: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D5" ) ); break; + case NF_TIME_HHMMSSAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D6" ) ); break; + case NF_TIME_HHMMAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D7" ) ); break; + case NF_TIME_HHMMSS: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D8" ) ); break; + case NF_TIME_HHMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D9" ) ); break; + default: aFuncResult = 'G'; + } + } + } + if( bAppendPrec ) + aFuncResult += String::CreateFromInt32( nPrec ); + const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat ); + if( lcl_FormatHasNegColor( pFormat ) ) + aFuncResult += '-'; + if( lcl_FormatHasOpenPar( pFormat ) ) + aFuncResult.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "()" ) ); + PushString( aFuncResult ); + } + else if( aInfoType.EqualsAscii( "COLOR" ) ) + { // 1 = negative values are colored, otherwise 0 + const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) ); + PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 ); + } + else if( aInfoType.EqualsAscii( "PARENTHESES" ) ) + { // 1 = format string contains a '(' character, otherwise 0 + const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) ); + PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 ); + } + else + PushIllegalArgument(); + } + } +} + + +void ScInterpreter::ScIsRef() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCell" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( !nGlobalError ) + nRes = 1; + } + break; + case svDoubleRef : + { + ScRange aRange; + PopDoubleRef( aRange ); + if ( !nGlobalError ) + nRes = 1; + } + break; + case svRefList : + { + FormulaTokenRef x = PopToken(); + if ( !nGlobalError ) + nRes = !static_cast<ScToken*>(x.get())->GetRefList()->empty(); + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsValue" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetRawStackType() ) + { + case svDouble: + Pop(); + nRes = 1; + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + if (GetCellErrCode( pCell ) == 0) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + nRes = 1; + break; + case CELLTYPE_FORMULA : + nRes = ((ScFormulaCell*)pCell)->IsValue() && + !((ScFormulaCell*)pCell)->IsEmpty(); + break; + default: + ; // nothing + } + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + { + if (pMat->GetErrorIfNotString( 0, 0) == 0) + nRes = pMat->IsValue( 0, 0); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + if (pMat->GetErrorIfNotString( nC, nR) == 0) + nRes = pMat->IsValue( nC, nR); + } + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsFormula() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsFormula" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + nRes = (GetCellType( GetCell( aAdr ) ) == CELLTYPE_FORMULA); + } + break; + default: + Pop(); + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScFormula() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFormula" ); + String aFormula; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->GetFormula( aFormula ); + break; + default: + SetError( NOTAVAILABLE ); + } + } + break; + default: + Pop(); + SetError( NOTAVAILABLE ); + } + PushString( aFormula ); +} + + + +void ScInterpreter::ScIsNV() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsNV" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + PopDoubleRefOrSingleRef( aAdr ); + if ( nGlobalError == NOTAVAILABLE ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + sal_uInt16 nErr = GetCellErrCode( pCell ); + nRes = (nErr == NOTAVAILABLE); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + nRes = (pMat->GetErrorIfNotString( 0, 0) == NOTAVAILABLE); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = (pMat->GetErrorIfNotString( nC, nR) == NOTAVAILABLE); + } + } + break; + default: + PopError(); + if ( nGlobalError == NOTAVAILABLE ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsErr() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsErr" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + PopDoubleRefOrSingleRef( aAdr ); + if ( nGlobalError && nGlobalError != NOTAVAILABLE ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + sal_uInt16 nErr = GetCellErrCode( pCell ); + nRes = (nErr && nErr != NOTAVAILABLE); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError || !pMat ) + nRes = ((nGlobalError && nGlobalError != NOTAVAILABLE) || !pMat); + else if ( !pJumpMatrix ) + { + sal_uInt16 nErr = pMat->GetErrorIfNotString( 0, 0); + nRes = (nErr && nErr != NOTAVAILABLE); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + { + sal_uInt16 nErr = pMat->GetErrorIfNotString( nC, nR); + nRes = (nErr && nErr != NOTAVAILABLE); + } + } + } + break; + default: + PopError(); + if ( nGlobalError && nGlobalError != NOTAVAILABLE ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +void ScInterpreter::ScIsError() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsError" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + nRes = 1; + break; + } + if ( nGlobalError ) + nRes = 1; + else + { + ScBaseCell* pCell = GetCell( aAdr ); + nRes = (GetCellErrCode( pCell ) != 0); + } + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( nGlobalError || !pMat ) + nRes = 1; + else if ( !pJumpMatrix ) + nRes = (pMat->GetErrorIfNotString( 0, 0) != 0); + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + nRes = (pMat->GetErrorIfNotString( nC, nR) != 0); + } + } + break; + default: + PopError(); + if ( nGlobalError ) + nRes = 1; + } + nGlobalError = 0; + PushInt( nRes ); +} + + +short ScInterpreter::IsEven() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IsEven" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + short nRes = 0; + double fVal = 0.0; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + break; + ScBaseCell* pCell = GetCell( aAdr ); + sal_uInt16 nErr = GetCellErrCode( pCell ); + if (nErr != 0) + SetError(nErr); + else + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + nRes = 1; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + nRes = 1; + } + break; + default: + ; // nothing + } + } + } + break; + case svDouble: + { + fVal = PopDouble(); + nRes = 1; + } + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if ( !pMat ) + ; // nothing + else if ( !pJumpMatrix ) + { + nRes = pMat->IsValue( 0, 0); + if ( nRes ) + fVal = pMat->GetDouble( 0, 0); + } + else + { + SCSIZE nCols, nRows, nC, nR; + pMat->GetDimensions( nCols, nRows); + pJumpMatrix->GetPos( nC, nR); + if ( nC < nCols && nR < nRows ) + { + nRes = pMat->IsValue( nC, nR); + if ( nRes ) + fVal = pMat->GetDouble( nC, nR); + } + else + SetError( errNoValue); + } + } + break; + default: + ; // nothing + } + if ( !nRes ) + SetError( errIllegalParameter); + else + nRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 ); + return nRes; +} + + +void ScInterpreter::ScIsEven() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsEven" ); + PushInt( IsEven() ); +} + + +void ScInterpreter::ScIsOdd() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIsOdd" ); + PushInt( !IsEven() ); +} + + +void ScInterpreter::ScN() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScN" ); + sal_uInt16 nErr = nGlobalError; + nGlobalError = 0; + // Temporarily override the ConvertStringToValue() error for + // GetCellValue() / GetCellValueOrZero() + sal_uInt16 nSErr = mnStringNoValueError; + mnStringNoValueError = errCellNoValue; + double fVal = GetDouble(); + mnStringNoValueError = nSErr; + if ( nGlobalError == NOTAVAILABLE || nGlobalError == errCellNoValue ) + nGlobalError = 0; // N(#NA) and N("text") are ok + if ( !nGlobalError && nErr != NOTAVAILABLE ) + nGlobalError = nErr; + PushDouble( fVal ); +} + + +void ScInterpreter::ScTrim() +{ // Doesn't only trim but writes out twice! + String aVal( GetString() ); + aVal.EraseLeadingChars(); + aVal.EraseTrailingChars(); + String aStr; + register const sal_Unicode* p = aVal.GetBuffer(); + register const sal_Unicode* const pEnd = p + aVal.Len(); + while ( p < pEnd ) + { + if ( *p != ' ' || p[-1] != ' ' ) // ' ' can't be first, -1 is fine too + aStr += *p; + p++; + } + PushString( aStr ); +} + + +void ScInterpreter::ScUpper() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTrim" ); + String aString = GetString(); + ScGlobal::pCharClass->toUpper(aString); + PushString(aString); +} + + +void ScInterpreter::ScPropper() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScPropper" ); +//2do: what to do with I18N-CJK ?!? + String aStr( GetString() ); + const xub_StrLen nLen = aStr.Len(); + // #i82487# don't try to write to empty string's BufferAccess + // (would crash now that the empty string is const) + if ( nLen > 0 ) + { + String aUpr( ScGlobal::pCharClass->upper( aStr ) ); + String aLwr( ScGlobal::pCharClass->lower( aStr ) ); + register sal_Unicode* pStr = aStr.GetBufferAccess(); + const sal_Unicode* pUpr = aUpr.GetBuffer(); + const sal_Unicode* pLwr = aLwr.GetBuffer(); + *pStr = *pUpr; + String aTmpStr( 'x' ); + xub_StrLen nPos = 1; + while( nPos < nLen ) + { + aTmpStr.SetChar( 0, pStr[nPos-1] ); + if ( !ScGlobal::pCharClass->isLetter( aTmpStr, 0 ) ) + pStr[nPos] = pUpr[nPos]; + else + pStr[nPos] = pLwr[nPos]; + nPos++; + } + aStr.ReleaseBufferAccess( nLen ); + } + PushString( aStr ); +} + + +void ScInterpreter::ScLower() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLower" ); + String aString( GetString() ); + ScGlobal::pCharClass->toLower(aString); + PushString(aString); +} + + +void ScInterpreter::ScLen() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLen" ); + String aStr( GetString() ); + PushDouble( aStr.Len() ); +} + + +void ScInterpreter::ScT() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScT" ); + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + sal_Bool bValue = false; + ScBaseCell* pCell = GetCell( aAdr ); + if ( GetCellErrCode( pCell ) == 0 ) + { + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + bValue = sal_True; + break; + case CELLTYPE_FORMULA : + bValue = ((ScFormulaCell*)pCell)->IsValue(); + break; + default: + ; // nothing + } + } + if ( bValue ) + PushString( EMPTY_STRING ); + else + { + // like GetString() + GetCellString( aTempStr, pCell ); + PushString( aTempStr ); + } + } + break; + case svDouble : + { + PopError(); + PushString( EMPTY_STRING ); + } + break; + case svString : + ; // leave on stack + break; + default : + PushError( errUnknownOpCode); + } +} + + +void ScInterpreter::ScValue() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScValue" ); + String aInputString; + double fVal; + + switch ( GetRawStackType() ) + { + case svMissing: + case svEmptyCell: + Pop(); + PushInt(0); + return; + case svDouble: + return; // leave on stack + //break; + + case svSingleRef: + case svDoubleRef: + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return; + } + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell && pCell->HasStringData() ) + GetCellString( aInputString, pCell ); + else if ( pCell && pCell->HasValueData() ) + { + PushDouble( GetCellValue(aAdr, pCell) ); + return; + } + else + { + PushDouble(0.0); + return; + } + } + break; + case svMatrix: + { + ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, + aInputString); + switch (nType) + { + case SC_MATVAL_EMPTY: + fVal = 0.0; + // fallthru + case SC_MATVAL_VALUE: + case SC_MATVAL_BOOLEAN: + PushDouble( fVal); + return; + //break; + case SC_MATVAL_STRING: + // evaluated below + break; + default: + PushIllegalArgument(); + } + } + break; + default: + aInputString = GetString(); + break; + } + + sal_uInt32 nFIndex = 0; // 0 for default locale + if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) + PushDouble(fVal); + else + PushIllegalArgument(); +} + + +//2do: this should be a proper unicode string method +inline sal_Bool lcl_ScInterpreter_IsPrintable( sal_Unicode c ) +{ + return 0x20 <= c && c != 0x7f; +} + +void ScInterpreter::ScClean() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScClean" ); + String aStr( GetString() ); + for ( xub_StrLen i = 0; i < aStr.Len(); i++ ) + { + if ( !lcl_ScInterpreter_IsPrintable( aStr.GetChar( i ) ) ) + aStr.Erase(i,1); + } + PushString(aStr); +} + + +void ScInterpreter::ScCode() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCode" ); +//2do: make it full range unicode? + const String& rStr = GetString(); + PushInt( (sal_uChar) ByteString::ConvertFromUnicode( rStr.GetChar(0), gsl_getSystemTextEncoding() ) ); +} + + +void ScInterpreter::ScChar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScChar" ); +//2do: make it full range unicode? + double fVal = GetDouble(); + if (fVal < 0.0 || fVal >= 256.0) + PushIllegalArgument(); + else + { + String aStr( '0' ); + aStr.SetChar( 0, ByteString::ConvertToUnicode( (sal_Char) fVal, gsl_getSystemTextEncoding() ) ); + PushString( aStr ); + } +} + + +/* #i70213# fullwidth/halfwidth conversion provided by + * Takashi Nakamoto <bluedwarf@ooo> + * erAck: added Excel compatibility conversions as seen in issue's test case. */ + +static ::rtl::OUString lcl_convertIntoHalfWidth( const ::rtl::OUString & rStr ) +{ + static bool bFirstASCCall = true; + static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 ); + + if( bFirstASCCall ) + { + aTrans.loadModuleByImplName( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "FULLWIDTH_HALFWIDTH_LIKE_ASC" )), LANGUAGE_SYSTEM ); + bFirstASCCall = false; + } + + return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL ); +} + + +static ::rtl::OUString lcl_convertIntoFullWidth( const ::rtl::OUString & rStr ) +{ + static bool bFirstJISCall = true; + static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 ); + + if( bFirstJISCall ) + { + aTrans.loadModuleByImplName( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "HALFWIDTH_FULLWIDTH_LIKE_JIS" )), LANGUAGE_SYSTEM ); + bFirstJISCall = false; + } + + return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL ); +} + + +/* ODFF: + * Summary: Converts half-width to full-width ASCII and katakana characters. + * Semantics: Conversion is done for half-width ASCII and katakana characters, + * other characters are simply copied from T to the result. This is the + * complementary function to ASC. + * For references regarding halfwidth and fullwidth characters see + * http://www.unicode.org/reports/tr11/ + * http://www.unicode.org/charts/charindex2.html#H + * http://www.unicode.org/charts/charindex2.html#F + */ +void ScInterpreter::ScJis() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScJis" ); + if (MustHaveParamCount( GetByte(), 1)) + PushString( lcl_convertIntoFullWidth( GetString())); +} + + +/* ODFF: + * Summary: Converts full-width to half-width ASCII and katakana characters. + * Semantics: Conversion is done for full-width ASCII and katakana characters, + * other characters are simply copied from T to the result. This is the + * complementary function to JIS. + */ +void ScInterpreter::ScAsc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAsc" ); + if (MustHaveParamCount( GetByte(), 1)) + PushString( lcl_convertIntoHalfWidth( GetString())); +} + +void ScInterpreter::ScUnicode() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnicode" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + const rtl::OUString& rStr = GetString(); + if (rStr.getLength() <= 0) + PushIllegalParameter(); + else + { + sal_Int32 i = 0; + PushDouble( rStr.iterateCodePoints(&i) ); + } + } +} + +void ScInterpreter::ScUnichar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScUnichar" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + double dVal = ::rtl::math::approxFloor( GetDouble() ); + if ((dVal < 0x000000) || (dVal > 0x10FFFF)) + PushIllegalArgument(); + else + { + sal_uInt32 nCodePoint = static_cast<sal_uInt32>( dVal ); + rtl::OUString aStr( &nCodePoint, 1 ); + PushString( aStr ); + } + } +} + + +void ScInterpreter::ScMin( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMin" ); + short nParamCount = GetByte(); + if (!MustHaveParamCountMin( nParamCount, 1)) + return; + double nMin = ::std::numeric_limits<double>::max(); + double nVal = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + nVal = GetDouble(); + if (nMin > nVal) nMin = nVal; + nFuncFmtType = NUMBERFORMAT_NUMBER; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + nVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + if (nMin > nVal) nMin = nVal; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + } + break; + case svDoubleRef : + case svRefList : + { + sal_uInt16 nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(nVal, nErr)) + { + if (nMin > nVal) + nMin = nVal; + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + while ((nErr == 0) && aValIter.GetNext(nVal, nErr)) + { + if (nMin > nVal) + nMin = nVal; + } + SetError(nErr); + } + } + break; + case svMatrix : + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + nFuncFmtType = NUMBERFORMAT_NUMBER; + pMat->GetDimensions(nC, nR); + if (pMat->IsNumeric()) + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMin > nVal) nMin = nVal; + } + } + else + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMin > nVal) nMin = nVal; + } + else if ( bTextAsZero ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + if ( nMin > 0.0 ) + nMin = 0.0; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + if ( nVal < nMin ) + PushDouble(0.0); + else + PushDouble(nMin); +} + +void ScInterpreter::ScMax( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMax" ); + short nParamCount = GetByte(); + if (!MustHaveParamCountMin( nParamCount, 1)) + return; + double nMax = -(::std::numeric_limits<double>::max()); + double nVal = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + nVal = GetDouble(); + if (nMax < nVal) nMax = nVal; + nFuncFmtType = NUMBERFORMAT_NUMBER; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + nVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + if (nMax < nVal) nMax = nVal; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + } + break; + case svDoubleRef : + case svRefList : + { + sal_uInt16 nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(nVal, nErr)) + { + if (nMax < nVal) + nMax = nVal; + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + while ((nErr == 0) && aValIter.GetNext(nVal, nErr)) + { + if (nMax < nVal) + nMax = nVal; + } + SetError(nErr); + } + } + break; + case svMatrix : + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + nFuncFmtType = NUMBERFORMAT_NUMBER; + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + if (pMat->IsNumeric()) + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMax < nVal) nMax = nVal; + } + } + else + { + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + nVal = pMat->GetDouble(nMatCol,nMatRow); + if (nMax < nVal) nMax = nVal; + } + else if ( bTextAsZero ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + if ( nMax < 0.0 ) + nMax = 0.0; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + if ( nVal > nMax ) + PushDouble(0.0); + else + PushDouble(nMax); +} +#if defined(WIN) && defined(MSC) +#pragma optimize("",on) +#endif + +namespace { + +void IterateMatrix( + const ScMatrixRef& pMat, ScIterFunc eFunc, sal_Bool bTextAsZero, + sal_uLong& rCount, short& rFuncFmtType, double& fRes, double& fMem, bool& bNull) +{ + if (!pMat) + return; + + rFuncFmtType = NUMBERFORMAT_NUMBER; + switch (eFunc) + { + case ifAVERAGE: + case ifSUM: + { + ScMatrix::IterateResult aRes = pMat->Sum(bTextAsZero); + if (bNull) + { + bNull = false; + fMem = aRes.mfFirst; + fRes += aRes.mfRest; + } + else + fRes += aRes.mfFirst + aRes.mfRest; + rCount += aRes.mnCount; + } + break; + case ifCOUNT: + rCount += pMat->Count(bTextAsZero); + break; + case ifCOUNT2: + rCount += pMat->Count(true); + break; + case ifPRODUCT: + { + ScMatrix::IterateResult aRes = pMat->Product(bTextAsZero); + fRes *= aRes.mfRest; + rCount += aRes.mnCount; + } + break; + case ifSUMSQ: + { + ScMatrix::IterateResult aRes = pMat->SumSquare(bTextAsZero); + fRes += aRes.mfRest; + rCount += aRes.mnCount; + } + break; + default: + ; + } +} + +} + +double ScInterpreter::IterateParameters( ScIterFunc eFunc, sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::IterateParameters" ); + short nParamCount = GetByte(); + double fRes = ( eFunc == ifPRODUCT ) ? 1.0 : 0.0; + double fVal = 0.0; + double fMem = 0.0; // first numeric value. + bool bNull = true; + sal_uLong nCount = 0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + nGlobalError = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + + case svString: + { + if( eFunc == ifCOUNT ) + { + String aStr( PopString() ); + sal_uInt32 nFIndex = 0; // damit default Land/Spr. + if ( bTextAsZero || pFormatter->IsNumberFormat(aStr, nFIndex, fVal)) + nCount++; + } + else + { + switch ( eFunc ) + { + case ifAVERAGE: + case ifSUM: + case ifSUMSQ: + case ifPRODUCT: + { + if ( bTextAsZero ) + { + Pop(); + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + else + { + while (nParamCount-- > 0) + Pop(); + SetError( errNoValue ); + } + } + break; + default: + Pop(); + nCount++; + } + } + } + break; + case svDouble : + fVal = GetDouble(); + nCount++; + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + default: ; // nothing + } + nFuncFmtType = NUMBERFORMAT_NUMBER; + break; + case svExternalSingleRef: + { + ScExternalRefCache::TokenRef pToken; + ScExternalRefCache::CellFormat aFmt; + PopExternalSingleRef(pToken, &aFmt); + if (nGlobalError && (eFunc == ifCOUNT2 || eFunc == ifCOUNT)) + { + nGlobalError = 0; + if ( eFunc == ifCOUNT2 ) + ++nCount; + break; + } + + if (!pToken) + break; + + StackVar eType = pToken->GetType(); + if (eFunc == ifCOUNT2) + { + if (eType != formula::svEmptyCell) + nCount++; + if (nGlobalError) + nGlobalError = 0; + } + else if (eType == formula::svDouble) + { + nCount++; + fVal = pToken->GetDouble(); + if (aFmt.mbIsSet) + { + nFuncFmtType = aFmt.mnType; + nFuncFmtIndex = aFmt.mnIndex; + } + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + case ifCOUNT: + if ( nGlobalError ) + { + nGlobalError = 0; + nCount--; + } + break; + default: ; // nothing + } + } + else if (bTextAsZero && eType == formula::svString) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + { + nGlobalError = 0; + if ( eFunc == ifCOUNT2 ) + ++nCount; + break; + } + ScBaseCell* pCell = GetCell( aAdr ); + if ( pCell ) + { + if( eFunc == ifCOUNT2 ) + { + CellType eCellType = pCell->GetCellType(); + if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE) + nCount++; + if ( nGlobalError ) + nGlobalError = 0; + } + else if ( pCell->HasValueData() ) + { + nCount++; + fVal = GetCellValue( aAdr, pCell ); + CurFmtToFuncFmt(); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fRes += fVal; + break; + case ifSUMSQ: fRes += fVal * fVal; break; + case ifPRODUCT: fRes *= fVal; break; + case ifCOUNT: + if ( nGlobalError ) + { + nGlobalError = 0; + nCount--; + } + break; + default: ; // nothing + } + } + else if ( bTextAsZero && pCell->HasStringData() ) + { + nCount++; + if ( eFunc == ifPRODUCT ) + fRes = 0.0; + } + } + } + break; + case svDoubleRef : + case svRefList : + { + PopDoubleRef( aRange, nParamCount, nRefInList); + if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) ) + { + nGlobalError = 0; + if ( eFunc == ifCOUNT2 ) + ++nCount; + break; + } + if( eFunc == ifCOUNT2 ) + { + ScBaseCell* pCell; + ScCellIterator aIter( pDok, aRange, glSubTotal ); + if ( (pCell = aIter.GetFirst()) != NULL ) + { + do + { + CellType eType = pCell->GetCellType(); + if( eType != CELLTYPE_NONE && eType != CELLTYPE_NOTE ) + nCount++; + } + while ( (pCell = aIter.GetNext()) != NULL ); + } + if ( nGlobalError ) + nGlobalError = 0; + } + else + { + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + sal_uInt16 nErr = 0; + if (aValIter.GetFirst(fVal, nErr)) + { + // placed the loop on the inside for performance reasons: + aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + do + { + SetError(nErr); + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fRes += fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifSUMSQ: + do + { + SetError(nErr); + fRes += fVal * fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifPRODUCT: + do + { + SetError(nErr); + fRes *= fVal; + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + case ifCOUNT: + do + { + if ( !nErr ) + nCount++; + } + while (aValIter.GetNext(fVal, nErr)); + break; + default: ; // nothing + } + SetError( nErr ); + } + } + } + break; + case svExternalDoubleRef: + { + ScMatrixRef pMat; + PopExternalDoubleRef(pMat); + if (nGlobalError) + break; + + IterateMatrix(pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem, bNull); + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + IterateMatrix(pMat, eFunc, bTextAsZero, nCount, nFuncFmtType, fRes, fMem, bNull); + } + break; + case svError: + { + Pop(); + if ( eFunc == ifCOUNT ) + { + nGlobalError = 0; + } + else if ( eFunc == ifCOUNT2 ) + { + nCount++; + nGlobalError = 0; + } + } + break; + default : + while (nParamCount-- > 0) + PopError(); + SetError(errIllegalParameter); + } + } + switch( eFunc ) + { + case ifSUM: fRes = ::rtl::math::approxAdd( fRes, fMem ); break; + case ifAVERAGE: fRes = div(::rtl::math::approxAdd( fRes, fMem ), nCount); break; + case ifCOUNT2: + case ifCOUNT: fRes = nCount; break; + case ifPRODUCT: if ( !nCount ) fRes = 0.0; break; + default: ; // nothing + } + // Bei Summen etc. macht ein sal_Bool-Ergebnis keinen Sinn + // und Anzahl ist immer Number (#38345#) + if( eFunc == ifCOUNT || nFuncFmtType == NUMBERFORMAT_LOGICAL ) + nFuncFmtType = NUMBERFORMAT_NUMBER; + return fRes; +} + + +void ScInterpreter::ScSumSQ() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumSQ" ); + PushDouble( IterateParameters( ifSUMSQ ) ); +} + + +void ScInterpreter::ScSum() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSum" ); + PushDouble( IterateParameters( ifSUM ) ); +} + + +void ScInterpreter::ScProduct() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScProduct" ); + PushDouble( IterateParameters( ifPRODUCT ) ); +} + + +void ScInterpreter::ScAverage( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAverage" ); + PushDouble( IterateParameters( ifAVERAGE, bTextAsZero ) ); +} + + +void ScInterpreter::ScCount() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCount" ); + PushDouble( IterateParameters( ifCOUNT ) ); +} + + +void ScInterpreter::ScCount2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCount2" ); + PushDouble( IterateParameters( ifCOUNT2 ) ); +} + + +void ScInterpreter::GetStVarParams( double& rVal, double& rValCount, + sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetStVarParams" ); + short nParamCount = GetByte(); + + std::vector<double> values; + double fSum = 0.0; + double vSum = 0.0; + double vMean = 0.0; + double fVal = 0.0; + rValCount = 0.0; + ScAddress aAdr; + ScRange aRange; + size_t nRefInList = 0; + while (nParamCount-- > 0) + { + switch (GetStackType()) + { + case svDouble : + { + fVal = GetDouble(); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + break; + case svSingleRef : + { + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + else if ( bTextAsZero && HasCellStringData( pCell ) ) + { + values.push_back(0.0); + rValCount++; + } + } + break; + case svDoubleRef : + case svRefList : + { + sal_uInt16 nErr = 0; + PopDoubleRef( aRange, nParamCount, nRefInList); + ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero ); + if (aValIter.GetFirst(fVal, nErr)) + { + do + { + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + while ((nErr == 0) && aValIter.GetNext(fVal, nErr)); + } + } + break; + case svMatrix : + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) + { + for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) + { + if (!pMat->IsString(nMatCol,nMatRow)) + { + fVal= pMat->GetDouble(nMatCol,nMatRow); + values.push_back(fVal); + fSum += fVal; + rValCount++; + } + else if ( bTextAsZero ) + { + values.push_back(0.0); + rValCount++; + } + } + } + } + } + break; + case svString : + { + Pop(); + if ( bTextAsZero ) + { + values.push_back(0.0); + rValCount++; + } + else + SetError(errIllegalParameter); + } + break; + default : + Pop(); + SetError(errIllegalParameter); + } + } + + ::std::vector<double>::size_type n = values.size(); + vMean = fSum / n; + for (::std::vector<double>::size_type i = 0; i < n; i++) + vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean); + rVal = vSum; +} + + +void ScInterpreter::ScVar( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVar" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + + if (nValCount <= 1.0) + PushError( errDivisionByZero ); + else + PushDouble( nVal / (nValCount - 1.0)); +} + + +void ScInterpreter::ScVarP( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVarP" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + + PushDouble( div( nVal, nValCount)); +} + + +void ScInterpreter::ScStDev( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStDev" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + if (nValCount <= 1.0) + PushError( errDivisionByZero ); + else + PushDouble( sqrt( nVal / (nValCount - 1.0))); +} + + +void ScInterpreter::ScStDevP( sal_Bool bTextAsZero ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScStDevP" ); + double nVal; + double nValCount; + GetStVarParams( nVal, nValCount, bTextAsZero ); + if (nValCount == 0.0) + PushError( errDivisionByZero ); + else + PushDouble( sqrt( nVal / nValCount)); + + /* this was: PushDouble( sqrt( div( nVal, nValCount))); + * + * Besides that the special NAN gets lost in the call through sqrt(), + * unxlngi6.pro then looped back and forth somewhere between div() and + * ::rtl::math::setNan(). Tests showed that + * + * sqrt( div( 1, 0)); + * + * produced a loop, but + * + * double f1 = div( 1, 0); + * sqrt( f1 ); + * + * was fine. There seems to be some compiler optimization problem. It does + * not occur when compiled with debug=t. + */ +} + + +void ScInterpreter::ScColumns() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScColumns" ); + sal_uInt8 nParamCount = GetByte(); + sal_uLong nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * + static_cast<sal_uLong>(nCol2 - nCol1 + 1); + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + nVal += nC; + } + } + break; + default: + PopError(); + SetError(errIllegalParameter); + } + } + PushDouble((double)nVal); +} + + +void ScInterpreter::ScRows() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRows" ); + sal_uInt8 nParamCount = GetByte(); + sal_uLong nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * + static_cast<sal_uLong>(nRow2 - nRow1 + 1); + break; + case svMatrix: + { + ScMatrixRef pMat = PopMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + nVal += nR; + } + } + break; + default: + PopError(); + SetError(errIllegalParameter); + } + } + PushDouble((double)nVal); +} + +void ScInterpreter::ScTables() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTables" ); + sal_uInt8 nParamCount = GetByte(); + sal_uLong nVal; + if ( nParamCount == 0 ) + nVal = pDok->GetTableCount(); + else + { + nVal = 0; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + while (nParamCount-- > 0) + { + switch ( GetStackType() ) + { + case svSingleRef: + PopError(); + nVal++; + break; + case svDoubleRef: + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1); + break; + case svMatrix: + PopError(); + nVal++; + break; + default: + PopError(); + SetError( errIllegalParameter ); + } + } + } + PushDouble( (double) nVal ); +} + + +void ScInterpreter::ScColumn() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScColumn" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + double nVal = 0; + if (nParamCount == 0) + { + nVal = aPos.Col() + 1; + if (bMatrixFormula) + { + SCCOL nCols; + SCROW nRows; + pMyFormulaCell->GetMatColsRows( nCols, nRows); + ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1); + if (pResMat) + { + for (SCCOL i=0; i < nCols; ++i) + pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0); + PushMatrix( pResMat); + return; + } + } + } + else + { + switch ( GetStackType() ) + { + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = (double) (nCol1 + 1); + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if (nCol2 > nCol1) + { + ScMatrixRef pResMat = GetNewMat( + static_cast<SCSIZE>(nCol2-nCol1+1), 1); + if (pResMat) + { + for (SCCOL i = nCol1; i <= nCol2; i++) + pResMat->PutDouble((double)(i+1), + static_cast<SCSIZE>(i-nCol1), 0); + PushMatrix(pResMat); + return; + } + else + nVal = 0.0; + } + else + nVal = (double) (nCol1 + 1); + } + break; + default: + SetError( errIllegalParameter ); + nVal = 0.0; + } + } + PushDouble( nVal ); + } +} + + +void ScInterpreter::ScRow() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRow" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + double nVal = 0; + if (nParamCount == 0) + { + nVal = aPos.Row() + 1; + if (bMatrixFormula) + { + SCCOL nCols; + SCROW nRows; + pMyFormulaCell->GetMatColsRows( nCols, nRows); + ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows)); + if (pResMat) + { + for (SCROW i=0; i < nRows; i++) + pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i)); + PushMatrix( pResMat); + return; + } + } + } + else + { + switch ( GetStackType() ) + { + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = (double) (nRow1 + 1); + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if (nRow2 > nRow1) + { + ScMatrixRef pResMat = GetNewMat( 1, + static_cast<SCSIZE>(nRow2-nRow1+1)); + if (pResMat) + { + for (SCROW i = nRow1; i <= nRow2; i++) + pResMat->PutDouble((double)(i+1), 0, + static_cast<SCSIZE>(i-nRow1)); + PushMatrix(pResMat); + return; + } + else + nVal = 0.0; + } + else + nVal = (double) (nRow1 + 1); + } + break; + default: + SetError( errIllegalParameter ); + nVal = 0.0; + } + } + PushDouble( nVal ); + } +} + +void ScInterpreter::ScTable() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScTable" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 0, 1 ) ) + { + SCTAB nVal = 0; + if ( nParamCount == 0 ) + nVal = aPos.Tab() + 1; + else + { + switch ( GetStackType() ) + { + case svString : + { + String aStr( PopString() ); + if ( pDok->GetTable( aStr, nVal ) ) + ++nVal; + else + SetError( errIllegalArgument ); + } + break; + case svSingleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + PopSingleRef( nCol1, nRow1, nTab1 ); + nVal = nTab1 + 1; + } + break; + case svDoubleRef : + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + nVal = nTab1 + 1; + } + break; + default: + SetError( errIllegalParameter ); + } + if ( nGlobalError ) + nVal = 0; + } + PushDouble( (double) nVal ); + } +} + +namespace { + +class VectorMatrixAccessor +{ +public: + VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) : + mrMat(rMat), mbColVec(bColVec) {} + + bool IsEmpty(SCSIZE i) const + { + return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0); + } + + bool IsEmptyPath(SCSIZE i) const + { + return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0); + } + + bool IsValue(SCSIZE i) const + { + return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0); + } + + bool IsString(SCSIZE i) const + { + return mbColVec ? mrMat.IsString(0, i) : mrMat.IsString(i, 0); + } + + double GetDouble(SCSIZE i) const + { + return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0); + } + + const String& GetString(SCSIZE i) const + { + return mbColVec ? mrMat.GetString(0, i) : mrMat.GetString(i, 0); + } + + SCSIZE GetElementCount() const + { + SCSIZE nC, nR; + mrMat.GetDimensions(nC, nR); + return mbColVec ? nR : nC; + } + +private: + const ScMatrix& mrMat; + bool mbColVec; +}; + +/** returns -1 when the matrix value is smaller than the query value, 0 when + they are equal, and 1 when the matrix value is larger than the query + value. */ +static sal_Int32 lcl_CompareMatrix2Query( + SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry) +{ + if (rMat.IsEmpty(i)) + { + /* TODO: in case we introduced query for real empty this would have to + * be changed! */ + return -1; // empty always less than anything else + } + + /* FIXME: what is an empty path (result of IF(false;true_path) in + * comparisons? */ + + if (rMat.IsValue(i)) + { + if (rEntry.bQueryByString) + return -1; // numeric always less than string + + const double nVal1 = rMat.GetDouble(i); + const double nVal2 = rEntry.nVal; + if (nVal1 == nVal2) + return 0; + + return nVal1 < nVal2 ? -1 : 1; + } + + if (!rEntry.bQueryByString) + return 1; // string always greater than numeric + + if (!rEntry.pStr) + // this should not happen! + return 1; + + const String& rStr1 = rMat.GetString(i); + const String& rStr2 = *rEntry.pStr; + + return ScGlobal::GetCollator()->compareString( rStr1, rStr2); // case-insensitive +} + +/** returns the last item with the identical value as the original item + value. */ +static void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat, + SCSIZE nMatCount, bool bReverse) +{ + if (rMat.IsValue(rIndex)) + { + double nVal = rMat.GetDouble(rIndex); + if (bReverse) + while (rIndex > 0 && rMat.IsValue(rIndex-1) && + nVal == rMat.GetDouble(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) && + nVal == rMat.GetDouble(rIndex+1)) + ++rIndex; + } + //! Order of IsEmptyPath, IsEmpty, IsString is significant! + else if (rMat.IsEmptyPath(rIndex)) + { + if (bReverse) + while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1)) + ++rIndex; + } + else if (rMat.IsEmpty(rIndex)) + { + if (bReverse) + while (rIndex > 0 && rMat.IsEmpty(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1)) + ++rIndex; + } + else if (rMat.IsString(rIndex)) + { + String aStr( rMat.GetString(rIndex)); + if (bReverse) + while (rIndex > 0 && rMat.IsString(rIndex-1) && + aStr == rMat.GetString(rIndex-1)) + --rIndex; + else + while (rIndex < nMatCount-1 && rMat.IsString(rIndex+1) && + aStr == rMat.GetString(rIndex+1)) + ++rIndex; + } + else + { + DBG_ERRORFILE("lcl_GetLastMatch: unhandled matrix type"); + } +} + +} + +void ScInterpreter::ScMatch() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMatch" ); + + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + double fTyp; + if (nParamCount == 3) + fTyp = GetDouble(); + else + fTyp = 1.0; + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2 = 0; + ScMatrixRef pMatSrc = NULL; + + switch (GetStackType()) + { + case svDoubleRef: + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2)) + { + PushIllegalParameter(); + return; + } + } + break; + case svMatrix: + case svExternalDoubleRef: + { + if (GetStackType() == svMatrix) + pMatSrc = PopMatrix(); + else + PopExternalDoubleRef(pMatSrc); + + if (!pMatSrc) + { + PushIllegalParameter(); + return; + } + } + break; + default: + PushIllegalParameter(); + return; + } + + if (nGlobalError == 0) + { + double fVal; + String sStr; + ScQueryParam rParam; + rParam.nCol1 = nCol1; + rParam.nRow1 = nRow1; + rParam.nCol2 = nCol2; + rParam.nTab = nTab1; + rParam.bMixedComparison = sal_True; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = sal_True; + if (fTyp < 0.0) + rEntry.eOp = SC_GREATER_EQUAL; + else if (fTyp > 0.0) + rEntry.eOp = SC_LESS_EQUAL; + switch ( GetStackType() ) + { + case svDouble: + { + fVal = GetDouble(); + rEntry.bQueryByString = false; + rEntry.nVal = fVal; + } + break; + case svString: + { + sStr = GetString(); + rEntry.bQueryByString = sal_True; + *rEntry.pStr = sStr; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + fVal = GetCellValue( aAdr, pCell ); + rEntry.bQueryByString = false; + rEntry.nVal = fVal; + } + else + { + GetCellString(sStr, pCell); + rEntry.bQueryByString = sal_True; + *rEntry.pStr = sStr; + } + } + break; + case svExternalSingleRef: + { + ScExternalRefCache::TokenRef pToken; + PopExternalSingleRef(pToken); + if (!pToken) + { + PushInt(0); + return; + } + if (pToken->GetType() == svDouble) + { + rEntry.bQueryByString = false; + rEntry.nVal = pToken->GetDouble(); + } + else + { + rEntry.bQueryByString = true; + *rEntry.pStr = pToken->GetString(); + } + } + break; + case svExternalDoubleRef: + // TODO: Implement this. + PushIllegalParameter(); + return; + break; + case svMatrix : + { + ScMatValType nType = GetDoubleOrStringFromMatrix( + rEntry.nVal, *rEntry.pStr); + rEntry.bQueryByString = ScMatrix::IsNonValueType( nType); + } + break; + default: + { + PushIllegalParameter(); + return; + } + } + if ( rEntry.bQueryByString ) + { + sal_Bool bIsVBAMode = false; + if ( pDok ) + bIsVBAMode = pDok->IsInVBAMode(); + + // #TODO handle MSO wildcards + if ( bIsVBAMode ) + rParam.bRegExp = false; + else + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + + if (pMatSrc) // The source data is matrix array. + { + SCSIZE nC, nR; + pMatSrc->GetDimensions( nC, nR); + if (nC > 1 && nR > 1) + { + // The source matrix must be a vector. + PushIllegalParameter(); + return; + } + SCSIZE nMatCount = (nC == 1) ? nR : nC; + VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1); + + // simple serial search for equality mode (source data doesn't + // need to be sorted). + + if (rEntry.eOp == SC_EQUAL) + { + for (SCSIZE i = 0; i < nMatCount; ++i) + { + if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0) + { + PushDouble(i+1); // found ! + return; + } + } + PushNA(); // not found + return; + } + + // binary search for non-equality mode (the source data is + // assumed to be sorted). + + bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL); + SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0; + for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) + { + SCSIZE nMid = nFirst + nLen/2; + sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry); + if (nCmp == 0) + { + // exact match. find the last item with the same value. + lcl_GetLastMatch( nMid, aMatAcc, nMatCount, !bAscOrder); + PushDouble( nMid+1); + return; + } + + if (nLen == 1) // first and last items are next to each other. + { + if (nCmp < 0) + nHitIndex = bAscOrder ? nLast : nFirst; + else + nHitIndex = bAscOrder ? nFirst : nLast; + break; + } + + if (nCmp < 0) + { + if (bAscOrder) + nFirst = nMid; + else + nLast = nMid; + } + else + { + if (bAscOrder) + nLast = nMid; + else + nFirst = nMid; + } + } + + if (nHitIndex == nMatCount-1) // last item + { + sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry); + if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0)) + { + // either the last item is an exact match or the real + // hit is beyond the last item. + PushDouble( nHitIndex+1); + return; + } + } + + if (nHitIndex > 0) // valid hit must be 2nd item or higher + { + PushDouble( nHitIndex); // non-exact match + return; + } + + PushNA(); + return; + } + + SCCOLROW nDelta = 0; + if (nCol1 == nCol2) + { // search row in column + rParam.nRow2 = nRow2; + rEntry.nField = nCol1; + ScAddress aResultPos( nCol1, nRow1, nTab1); + if (!LookupQueryWithCache( aResultPos, rParam)) + { + PushNA(); + return; + } + nDelta = aResultPos.Row() - nRow1; + } + else + { // search column in row + SCCOL nC; + rParam.bByRow = false; + rParam.nRow2 = nRow1; + rEntry.nField = nCol1; + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false); + // Advance Entry.nField in Iterator if column changed + aCellIter.SetAdvanceQueryParamEntryField( sal_True ); + if (fTyp == 0.0) + { // EQUAL + if ( aCellIter.GetFirst() ) + nC = aCellIter.GetCol(); + else + { + PushNA(); + return; + } + } + else + { // <= or >= + SCROW nR; + if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) ) + { + PushNA(); + return; + } + } + nDelta = nC - nCol1; + } + PushDouble((double) (nDelta + 1)); + } + else + PushIllegalParameter(); + } +} + + +void ScInterpreter::ScCountEmptyCells() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCountEmptyCells" ); + if ( MustHaveParamCount( GetByte(), 1 ) ) + { + sal_uLong nMaxCount = 0, nCount = 0; + CellType eCellType; + switch (GetStackType()) + { + case svSingleRef : + { + nMaxCount = 1; + ScAddress aAdr; + PopSingleRef( aAdr ); + eCellType = GetCellType( GetCell( aAdr ) ); + if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE) + nCount = 1; + } + break; + case svDoubleRef : + case svRefList : + { + ScRange aRange; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + PopDoubleRef( aRange, nParam, nRefInList); + nMaxCount += + static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) * + static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) * + static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1); + ScBaseCell* pCell; + ScCellIterator aDocIter( pDok, aRange, glSubTotal); + if ( (pCell = aDocIter.GetFirst()) != NULL ) + { + do + { + if ((eCellType = pCell->GetCellType()) != CELLTYPE_NONE + && eCellType != CELLTYPE_NOTE) + nCount++; + } while ( (pCell = aDocIter.GetNext()) != NULL ); + } + } + } + break; + default : SetError(errIllegalParameter); break; + } + PushDouble(nMaxCount - nCount); + } +} + + +void ScInterpreter::ScCountIf() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCountIf" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String rString; + double fVal = 0.0; + sal_Bool bIsString = sal_True; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + bIsString = false; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + bIsString = false; + } + else + GetCellString(rString, pCell); + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + GetCellString(rString, pCell); + break; + default: + fVal = 0.0; + bIsString = false; + } + } + break; + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, + rString); + bIsString = ScMatrix::IsNonValueType( nType); + } + break; + case svString: + rString = GetString(); + break; + default: + { + fVal = GetDouble(); + bIsString = false; + } + } + double fSum = 0.0; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + ScMatrixRef pQueryMatrix; + switch ( GetStackType() ) + { + case svDoubleRef : + case svRefList : + { + ScRange aRange; + PopDoubleRef( aRange, nParam, nRefInList); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + break; + case svSingleRef : + PopSingleRef( nCol1, nRow1, nTab1 ); + nCol2 = nCol1; + nRow2 = nRow1; + nTab2 = nTab1; + break; + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + pQueryMatrix = GetMatrix(); + if (!pQueryMatrix) + { + PushIllegalParameter(); + return; + } + nCol1 = 0; + nRow1 = 0; + nTab1 = 0; + SCSIZE nC, nR; + pQueryMatrix->GetDimensions( nC, nR); + nCol2 = static_cast<SCCOL>(nC - 1); + nRow2 = static_cast<SCROW>(nR - 1); + nTab2 = 0; + } + break; + default: + PushIllegalParameter(); + return ; + } + if ( nTab1 != nTab2 ) + { + PushIllegalParameter(); + return; + } + if (nCol1 > nCol2) + { + PushIllegalParameter(); + return; + } + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nRow1 = nRow1; + rParam.nRow2 = nRow2; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = sal_True; + if (!bIsString) + { + rEntry.bQueryByString = false; + rEntry.nVal = fVal; + rEntry.eOp = SC_EQUAL; + } + else + { + rParam.FillInExcelSyntax(rString, 0); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal)); + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + rParam.nCol1 = nCol1; + rParam.nCol2 = nCol2; + rEntry.nField = nCol1; + if (pQueryMatrix) + { + // Never case-sensitive. + ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp); + ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); + if (nGlobalError || !pResultMatrix) + { + PushIllegalParameter(); + return; + } + + SCSIZE nSize = pResultMatrix->GetElementCount(); + for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex) + { + if (pResultMatrix->IsValue( nIndex) && + pResultMatrix->GetDouble( nIndex)) + ++fSum; + } + } + else + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false); + // Keep Entry.nField in iterator on column change + aCellIter.SetAdvanceQueryParamEntryField( true ); + if ( aCellIter.GetFirst() ) + { + do + { + fSum++; + } while ( aCellIter.GetNext() ); + } + } + } + else + { + PushIllegalParameter(); + return; + } + } + PushDouble(fSum); + } +} + + +void ScInterpreter::ScSumIf() +{ + sal_uInt8 nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return; + + SCCOL nCol3 = 0; + SCROW nRow3 = 0; + SCTAB nTab3 = 0; + + ScMatrixRef pSumExtraMatrix; + bool bSumExtraRange = (nParamCount == 3); + if (bSumExtraRange) + { + // Save only the upperleft cell in case of cell range. The geometry + // of the 3rd parameter is taken from the 1st parameter. + + switch ( GetStackType() ) + { + case svDoubleRef : + { + SCCOL nColJunk = 0; + SCROW nRowJunk = 0; + SCTAB nTabJunk = 0; + PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk ); + if ( nTabJunk != nTab3 ) + { + PushIllegalParameter(); + return; + } + } + break; + case svSingleRef : + PopSingleRef( nCol3, nRow3, nTab3 ); + break; + case svMatrix: + pSumExtraMatrix = PopMatrix(); + //! nCol3, nRow3, nTab3 remain 0 + break; + case svExternalSingleRef: + { + pSumExtraMatrix = new ScMatrix(1, 1); + ScExternalRefCache::TokenRef pToken; + PopExternalSingleRef(pToken); + if (!pToken) + { + PushIllegalParameter(); + return; + } + + if (pToken->GetType() == svDouble) + pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0); + else + pSumExtraMatrix->PutString(pToken->GetString(), 0, 0); + } + break; + case svExternalDoubleRef: + PopExternalDoubleRef(pSumExtraMatrix); + break; + default: + PushIllegalParameter(); + return ; + } + } + + String aString; + double fVal = 0.0; + bool bIsString = true; + switch ( GetStackType() ) + { + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return ; + } + ScBaseCell* pCell = GetCell( aAdr ); + switch ( GetCellType( pCell ) ) + { + case CELLTYPE_VALUE : + fVal = GetCellValue( aAdr, pCell ); + bIsString = false; + break; + case CELLTYPE_FORMULA : + if( ((ScFormulaCell*)pCell)->IsValue() ) + { + fVal = GetCellValue( aAdr, pCell ); + bIsString = false; + } + else + GetCellString(aString, pCell); + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + GetCellString(aString, pCell); + break; + default: + fVal = 0.0; + bIsString = false; + } + } + break; + case svString: + aString = GetString(); + break; + case svMatrix : + case svExternalDoubleRef: + { + ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString); + bIsString = ScMatrix::IsNonValueType( nType); + } + break; + case svExternalSingleRef: + { + ScExternalRefCache::TokenRef pToken; + PopExternalSingleRef(pToken); + if (pToken) + { + if (pToken->GetType() == svDouble) + { + fVal = pToken->GetDouble(); + bIsString = false; + } + else + aString = pToken->GetString(); + } + } + break; + default: + { + fVal = GetDouble(); + bIsString = false; + } + } + + double fSum = 0.0; + double fMem = 0.0; + sal_Bool bNull = true; + short nParam = 1; + size_t nRefInList = 0; + while (nParam-- > 0) + { + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + ScMatrixRef pQueryMatrix; + switch ( GetStackType() ) + { + case svRefList : + if (bSumExtraRange) + { + PushIllegalParameter(); + return; + } + else + { + ScRange aRange; + PopDoubleRef( aRange, nParam, nRefInList); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + break; + case svDoubleRef : + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + break; + case svSingleRef : + PopSingleRef( nCol1, nRow1, nTab1 ); + nCol2 = nCol1; + nRow2 = nRow1; + nTab2 = nTab1; + break; + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + pQueryMatrix = GetMatrix(); + if (!pQueryMatrix) + { + PushIllegalParameter(); + return; + } + nCol1 = 0; + nRow1 = 0; + nTab1 = 0; + SCSIZE nC, nR; + pQueryMatrix->GetDimensions( nC, nR); + nCol2 = static_cast<SCCOL>(nC - 1); + nRow2 = static_cast<SCROW>(nR - 1); + nTab2 = 0; + } + break; + default: + PushIllegalParameter(); + return ; + } + if ( nTab1 != nTab2 ) + { + PushIllegalArgument(); + return; + } + + if (bSumExtraRange) + { + // Take the range geometry of the 1st parameter and apply it to + // the 3rd. If parts of the resulting range would point outside + // the sheet, don't complain but silently ignore and simply cut + // them away, this is what Xcl does :-/ + + // For the cut-away part we also don't need to determine the + // criteria match, so shrink the source range accordingly, + // instead of the result range. + SCCOL nColDelta = nCol2 - nCol1; + SCROW nRowDelta = nRow2 - nRow1; + SCCOL nMaxCol; + SCROW nMaxRow; + if (pSumExtraMatrix) + { + SCSIZE nC, nR; + pSumExtraMatrix->GetDimensions( nC, nR); + nMaxCol = static_cast<SCCOL>(nC - 1); + nMaxRow = static_cast<SCROW>(nR - 1); + } + else + { + nMaxCol = MAXCOL; + nMaxRow = MAXROW; + } + if (nCol3 + nColDelta > nMaxCol) + { + SCCOL nNewDelta = nMaxCol - nCol3; + nCol2 = nCol1 + nNewDelta; + } + + if (nRow3 + nRowDelta > nMaxRow) + { + SCROW nNewDelta = nMaxRow - nRow3; + nRow2 = nRow1 + nNewDelta; + } + } + else + { + nCol3 = nCol1; + nRow3 = nRow1; + nTab3 = nTab1; + } + + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nRow1 = nRow1; + rParam.nRow2 = nRow2; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = true; + if (!bIsString) + { + rEntry.bQueryByString = false; + rEntry.nVal = fVal; + rEntry.eOp = SC_EQUAL; + } + else + { + rParam.FillInExcelSyntax(aString, 0); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal)); + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + ScAddress aAdr; + aAdr.SetTab( nTab3 ); + rParam.nCol1 = nCol1; + rParam.nCol2 = nCol2; + rEntry.nField = nCol1; + SCsCOL nColDiff = nCol3 - nCol1; + SCsROW nRowDiff = nRow3 - nRow1; + if (pQueryMatrix) + { + // Never case-sensitive. + ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp); + ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); + if (nGlobalError || !pResultMatrix) + { + PushIllegalParameter(); + return; + } + + if (pSumExtraMatrix) + { + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + if (pResultMatrix->IsValue( nCol, nRow) && + pResultMatrix->GetDouble( nCol, nRow)) + { + SCSIZE nC = nCol + nColDiff; + SCSIZE nR = nRow + nRowDiff; + if (pSumExtraMatrix->IsValue( nC, nR)) + { + fVal = pSumExtraMatrix->GetDouble( nC, nR); + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fSum += fVal; + } + } + } + } + } + else + { + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) + { + if (pResultMatrix->GetDouble( nCol, nRow)) + { + aAdr.SetCol( nCol + nColDiff); + aAdr.SetRow( nRow + nRowDiff); + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData(pCell) ) + { + fVal = GetCellValue( aAdr, pCell ); + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fSum += fVal; + } + } + } + } + } + } + else + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false); + // Increment Entry.nField in iterator when switching to next column. + aCellIter.SetAdvanceQueryParamEntryField( true ); + if ( aCellIter.GetFirst() ) + { + if (pSumExtraMatrix) + { + do + { + SCSIZE nC = aCellIter.GetCol() + nColDiff; + SCSIZE nR = aCellIter.GetRow() + nRowDiff; + if (pSumExtraMatrix->IsValue( nC, nR)) + { + fVal = pSumExtraMatrix->GetDouble( nC, nR); + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fSum += fVal; + } + } while ( aCellIter.GetNext() ); + } + else + { + do + { + aAdr.SetCol( aCellIter.GetCol() + nColDiff); + aAdr.SetRow( aCellIter.GetRow() + nRowDiff); + ScBaseCell* pCell = GetCell( aAdr ); + if ( HasCellValueData(pCell) ) + { + fVal = GetCellValue( aAdr, pCell ); + if ( bNull && fVal != 0.0 ) + { + bNull = false; + fMem = fVal; + } + else + fSum += fVal; + } + } while ( aCellIter.GetNext() ); + } + } + } + } + else + { + PushIllegalParameter(); + return; + } + } + PushDouble( ::rtl::math::approxAdd( fSum, fMem ) ); +} + + +void ScInterpreter::ScLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLookup" ); + sal_uInt8 nParamCount = GetByte(); + if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) + return ; + + ScMatrixRef pDataMat = NULL, pResMat = NULL; + SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0; + SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0; + SCTAB nTab1 = 0, nResTab = 0; + SCSIZE nLenMajor = 0; // length of major direction + bool bVertical = true; // whether to lookup vertically or horizontally + + // The third parameter, result array, for double, string and single reference. + double fResVal = 0.0; + String aResStr; + ScAddress aResAdr; + StackVar eResArrayType = svUnknown; + + if (nParamCount == 3) + { + eResArrayType = GetStackType(); + switch (eResArrayType) + { + case svDoubleRef: + { + SCTAB nTabJunk; + PopDoubleRef(nResCol1, nResRow1, nResTab, + nResCol2, nResRow2, nTabJunk); + if (nResTab != nTabJunk || + ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0)) + { + // The result array must be a vector. + PushIllegalParameter(); + return; + } + } + break; + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + pResMat = GetMatrix(); + if (!pResMat) + { + PushIllegalParameter(); + return; + } + SCSIZE nC, nR; + pResMat->GetDimensions(nC, nR); + if (nC != 1 && nR != 1) + { + // Result matrix must be a vector. + PushIllegalParameter(); + return; + } + } + break; + case svDouble: + fResVal = GetDouble(); + break; + case svString: + aResStr = GetString(); + break; + case svSingleRef: + PopSingleRef( aResAdr ); + break; + default: + PushIllegalParameter(); + return; + } + } + + // For double, string and single reference. + double fDataVal = 0.0; + String aDataStr; + ScAddress aDataAdr; + bool bValueData = false; + + // Get the data-result range and also determine whether this is vertical + // lookup or horizontal lookup. + + StackVar eDataArrayType = GetStackType(); + switch (eDataArrayType) + { + case svDoubleRef: + { + SCTAB nTabJunk; + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk); + if (nTab1 != nTabJunk) + { + PushIllegalParameter(); + return; + } + bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1); + nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1; + } + break; + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + pDataMat = GetMatrix(); + if (!pDataMat) + { + PushIllegalParameter(); + return; + } + + SCSIZE nC, nR; + pDataMat->GetDimensions(nC, nR); + bVertical = (nR >= nC); + nLenMajor = bVertical ? nR : nC; + } + break; + case svDouble: + { + fDataVal = GetDouble(); + bValueData = true; + } + break; + case svString: + { + aDataStr = GetString(); + } + break; + case svSingleRef: + { + PopSingleRef( aDataAdr ); + const ScBaseCell* pDataCell = GetCell( aDataAdr ); + if (HasCellEmptyData( pDataCell)) + { + // Empty cells aren't found anywhere, bail out early. + SetError( NOTAVAILABLE); + } + else if (HasCellValueData( pDataCell)) + { + fDataVal = GetCellValue( aDataAdr, pDataCell ); + bValueData = true; + } + else + GetCellString( aDataStr, pDataCell ); + } + break; + default: + SetError( errIllegalParameter); + } + + + if (nGlobalError) + { + PushError( nGlobalError); + return; + } + + // Get the lookup value. + + ScQueryParam aParam; + ScQueryEntry& rEntry = aParam.GetEntry(0); + if ( !FillEntry(rEntry) ) + return; + + if ( eDataArrayType == svDouble || eDataArrayType == svString || + eDataArrayType == svSingleRef ) + { + // Delta position for a single value is always 0. + + // Found if data <= query, but not if query is string and found data is + // numeric or vice versa. This is how Excel does it but doesn't + // document it. + + bool bFound = false; + if ( bValueData ) + { + if ( rEntry.bQueryByString ) + bFound = false; + else + bFound = (fDataVal <= rEntry.nVal); + } + else + { + if ( !rEntry.bQueryByString ) + bFound = false; + else + bFound = (ScGlobal::GetCollator()->compareString( aDataStr, *rEntry.pStr) <= 0); + } + + if (!bFound) + { + PushNA(); + return; + } + + if (pResMat) + { + if (pResMat->IsValue( 0, 0 )) + PushDouble(pResMat->GetDouble( 0, 0 )); + else + PushString(pResMat->GetString( 0, 0 )); + } + else if (nParamCount == 3) + { + switch (eResArrayType) + { + case svDouble: + PushDouble( fResVal ); + break; + case svString: + PushString( aResStr ); + break; + case svDoubleRef: + aResAdr.Set( nResCol1, nResRow1, nResTab); + // fallthru + case svSingleRef: + PushCellResultToken( true, aResAdr, NULL, NULL); + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data"); + } + } + else + { + switch (eDataArrayType) + { + case svDouble: + PushDouble( fDataVal ); + break; + case svString: + PushString( aDataStr ); + break; + case svSingleRef: + PushCellResultToken( true, aDataAdr, NULL, NULL); + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data"); + } + } + return; + } + + // Now, perform the search to compute the delta position (nDelta). + + if (pDataMat) + { + // Data array is given as a matrix. + rEntry.bDoQuery = true; + rEntry.eOp = SC_LESS_EQUAL; + bool bFound = false; + + SCSIZE nC, nR; + pDataMat->GetDimensions(nC, nR); + + // In case of non-vector matrix, only search the first row or column. + ScMatrixRef pDataMat2; + if (bVertical) + { + ScMatrixRef pTempMat(new ScMatrix(1, nR)); + for (SCSIZE i = 0; i < nR; ++i) + if (pDataMat->IsValue(0, i)) + pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i); + else + pTempMat->PutString(pDataMat->GetString(0, i), 0, i); + pDataMat2 = pTempMat; + } + else + { + ScMatrixRef pTempMat(new ScMatrix(nC, 1)); + for (SCSIZE i = 0; i < nC; ++i) + if (pDataMat->IsValue(i, 0)) + pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0); + else + pTempMat->PutString(pDataMat->GetString(i, 0), i, 0); + pDataMat2 = pTempMat; + } + + VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical); + + // binary search for non-equality mode (the source data is + // assumed to be sorted in ascending order). + + SCCOLROW nDelta = -1; + + SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0; + for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) + { + SCSIZE nMid = nFirst + nLen/2; + sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry); + if (nCmp == 0) + { + // exact match. find the last item with the same value. + lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor, false); + nDelta = nMid; + bFound = true; + break; + } + + if (nLen == 1) // first and last items are next to each other. + { + nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1; + // If already the 1st item is greater there's nothing found. + bFound = (nDelta >= 0); + break; + } + + if (nCmp < 0) + nFirst = nMid; + else + nLast = nMid; + } + + if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item + { + sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry); + if (nCmp <= 0) + { + // either the last item is an exact match or the real + // hit is beyond the last item. + nDelta += 1; + bFound = true; + } + } + else if (nDelta > 0) // valid hit must be 2nd item or higher + { + // non-exact match + bFound = true; + } + + // With 0-9 < A-Z, if query is numeric and data found is string, or + // vice versa, the (yet another undocumented) Excel behavior is to + // return #N/A instead. + + if (bFound) + { + VectorMatrixAccessor aMatAcc(*pDataMat, bVertical); + SCCOLROW i = nDelta; + SCSIZE n = aMatAcc.GetElementCount(); + if (static_cast<SCSIZE>(i) >= n) + i = static_cast<SCCOLROW>(n); + if (static_cast<bool>(rEntry.bQueryByString) == aMatAcc.IsValue(i)) + bFound = false; + } + + if (!bFound) + { + PushNA(); + return; + } + + // Now that we've found the delta, push the result back to the cell. + + if (pResMat) + { + VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); + // result array is matrix. + if (static_cast<SCSIZE>(nDelta) >= aResMatAcc.GetElementCount()) + { + PushNA(); + return; + } + if (aResMatAcc.IsValue(nDelta)) + PushDouble(aResMatAcc.GetDouble(nDelta)); + else + PushString(aResMatAcc.GetString(nDelta)); + } + else if (nParamCount == 3) + { + // result array is cell range. + ScAddress aAdr; + aAdr.SetTab(nResTab); + bool bResVertical = (nResRow2 - nResRow1) > 0; + if (bResVertical) + { + SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nResCol1); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nResRow1); + } + PushCellResultToken(true, aAdr, NULL, NULL); + } + else + { + // no result array. Use the data array to get the final value from. + if (bVertical) + { + if (pDataMat->IsValue(nC-1, nDelta)) + PushDouble(pDataMat->GetDouble(nC-1, nDelta)); + else + PushString(pDataMat->GetString(nC-1, nDelta)); + } + else + { + if (pDataMat->IsValue(nDelta, nR-1)) + PushDouble(pDataMat->GetDouble(nDelta, nR-1)); + else + PushString(pDataMat->GetString(nDelta, nR-1)); + } + } + + return; + } + + // Perform cell range search. + + aParam.nCol1 = nCol1; + aParam.nRow1 = nRow1; + aParam.nCol2 = bVertical ? nCol1 : nCol2; + aParam.nRow2 = bVertical ? nRow2 : nRow1; + aParam.bByRow = bVertical; + aParam.bMixedComparison = true; + + rEntry.bDoQuery = sal_True; + rEntry.eOp = SC_LESS_EQUAL; + rEntry.nField = nCol1; + if ( rEntry.bQueryByString ) + aParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + + ScQueryCellIterator aCellIter(pDok, nTab1, aParam, false); + SCCOL nC; + SCROW nR; + // Advance Entry.nField in iterator upon switching columns if + // lookup in row. + aCellIter.SetAdvanceQueryParamEntryField(!bVertical); + if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) ) + { + PushNA(); + return; + } + + SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1); + + if (pResMat) + { + VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); + // Use the matrix result array. + if (aResMatAcc.IsValue(nDelta)) + PushDouble(aResMatAcc.GetDouble(nDelta)); + else + PushString(aResMatAcc.GetString(nDelta)); + } + else if (nParamCount == 3) + { + switch (eResArrayType) + { + case svDoubleRef: + { + // Use the result array vector. Note that the result array is assumed + // to be a vector (i.e. 1-dimensinoal array). + + ScAddress aAdr; + aAdr.SetTab(nResTab); + bool bResVertical = (nResRow2 - nResRow1) > 0; + if (bResVertical) + { + SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nResCol1); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nResRow1); + } + PushCellResultToken( true, aAdr, NULL, NULL); + } + break; + case svDouble: + case svString: + case svSingleRef: + { + if (nDelta != 0) + PushNA(); + else + { + switch (eResArrayType) + { + case svDouble: + PushDouble( fResVal ); + break; + case svString: + PushString( aResStr ); + break; + case svSingleRef: + PushCellResultToken( true, aResAdr, NULL, NULL); + break; + default: + ; // nothing + } + } + } + break; + default: + DBG_ERRORFILE( "ScInterpreter::ScLookup: unhandled eResArrayType, range search"); + } + } + else + { + // Regardless of whether or not the result array exists, the last + // array is always used as the "result" array. + + ScAddress aAdr; + aAdr.SetTab(nTab1); + if (bVertical) + { + SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta); + if (nTempRow > MAXROW) + { + PushDouble(0); + return; + } + aAdr.SetCol(nCol2); + aAdr.SetRow(nTempRow); + } + else + { + SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta); + if (nTempCol > MAXCOL) + { + PushDouble(0); + return; + } + aAdr.SetCol(nTempCol); + aAdr.SetRow(nRow2); + } + PushCellResultToken(true, aAdr, NULL, NULL); + } +} + + +void ScInterpreter::ScHLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScHLookup" ); + CalculateLookup(sal_True); +} +void ScInterpreter::CalculateLookup(sal_Bool HLookup) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::CalculateLookup" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + sal_Bool bSorted; + if (nParamCount == 4) + bSorted = GetBool(); + else + bSorted = sal_True; + double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0; + ScMatrixRef pMat = NULL; + SCSIZE nC = 0, nR = 0; + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2; + StackVar eType = GetStackType(); + if (eType == svDoubleRef) + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nTab1 != nTab2) + { + PushIllegalParameter(); + return; + } + } + else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef) + { + pMat = GetMatrix(); + + if (pMat) + pMat->GetDimensions(nC, nR); + else + { + PushIllegalParameter(); + return; + } + } + else + { + PushIllegalParameter(); + return; + } + if ( fIndex < 0.0 || (HLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) ) + { + PushIllegalArgument(); + return; + } + SCROW nZIndex = static_cast<SCROW>(fIndex); + SCCOL nSpIndex = static_cast<SCCOL>(fIndex); + + if (!pMat) + { + nZIndex += nRow1; // Wertzeile + nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column + } + + if (nGlobalError == 0) + { + ScQueryParam rParam; + rParam.nCol1 = nCol1; + rParam.nRow1 = nRow1; + if ( HLookup ) + { + rParam.nCol2 = nCol2; + rParam.nRow2 = nRow1; // nur in der ersten Zeile suchen + rParam.bByRow = false; + } // if ( HLookup ) + else + { + rParam.nCol2 = nCol1; // nur in der ersten Spalte suchen + rParam.nRow2 = nRow2; + rParam.nTab = nTab1; + } + rParam.bMixedComparison = sal_True; + + ScQueryEntry& rEntry = rParam.GetEntry(0); + rEntry.bDoQuery = sal_True; + if ( bSorted ) + rEntry.eOp = SC_LESS_EQUAL; + if ( !FillEntry(rEntry) ) + return; + if ( rEntry.bQueryByString ) + rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + if (pMat) + { + SCSIZE nMatCount = HLookup ? nC : nR; + SCSIZE nDelta = SCSIZE_MAX; + if (rEntry.bQueryByString) + { + //!!!!!!! + //! TODO: enable regex on matrix strings + //!!!!!!! + String aParamStr = *rEntry.pStr; + if ( bSorted ) + { + static CollatorWrapper* pCollator = ScGlobal::GetCollator(); + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)) + { + sal_Int32 nRes = + pCollator->compareString( HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr); + if (nRes <= 0) + nDelta = i; + else if (i>0) // #i2168# ignore first mismatch + i = nMatCount+1; + } + else + nDelta = i; + } + } + else + { + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)) + { + if ( ScGlobal::GetpTransliteration()->isEqual( + HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr ) ) + { + nDelta = i; + i = nMatCount + 1; + } + } + } + } + } + else + { + if ( bSorted ) + { + // #i2168# ignore strings + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))) + { + if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rEntry.nVal) + nDelta = i; + else + i = nMatCount+1; + } + } + } + else + { + for (SCSIZE i = 0; i < nMatCount; i++) + { + if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))) + { + if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) == rEntry.nVal) + { + nDelta = i; + i = nMatCount + 1; + } + } + } + } + } + if ( nDelta != SCSIZE_MAX ) + { + SCSIZE nX = static_cast<SCSIZE>(nSpIndex); + SCSIZE nY = nDelta; + if ( HLookup ) + { + nX = nDelta; + nY = static_cast<SCSIZE>(nZIndex); + } + if ( pMat->IsString( nX, nY) ) + PushString(pMat->GetString( nX,nY)); + else + PushDouble(pMat->GetDouble( nX,nY)); + } + else + PushNA(); + } + else + { + rEntry.nField = nCol1; + sal_Bool bFound = false; + SCCOL nCol = 0; + SCROW nRow = 0; + if ( bSorted ) + rEntry.eOp = SC_LESS_EQUAL; + if ( HLookup ) + { + ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false); + // advance Entry.nField in Iterator upon switching columns + aCellIter.SetAdvanceQueryParamEntryField( sal_True ); + if ( bSorted ) + { + SCROW nRow1_temp; + bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp ); + } + else if ( aCellIter.GetFirst() ) + { + bFound = sal_True; + nCol = aCellIter.GetCol(); + } + nRow = nZIndex; + } // if ( HLookup ) + else + { + ScAddress aResultPos( nCol1, nRow1, nTab1); + bFound = LookupQueryWithCache( aResultPos, rParam); + nRow = aResultPos.Row(); + nCol = nSpIndex; + } + if ( bFound ) + { + ScAddress aAdr( nCol, nRow, nTab1 ); + PushCellResultToken( true, aAdr, NULL, NULL); + } + else + PushNA(); + } + } + else + PushIllegalParameter(); + } +} + +bool ScInterpreter::FillEntry(ScQueryEntry& rEntry) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::FillEntry" ); + switch ( GetStackType() ) + { + case svDouble: + { + rEntry.bQueryByString = false; + rEntry.nVal = GetDouble(); + } + break; + case svString: + { + const String sStr = GetString(); + rEntry.bQueryByString = sal_True; + *rEntry.pStr = sStr; + } + break; + case svDoubleRef : + case svSingleRef : + { + ScAddress aAdr; + if ( !PopDoubleRefOrSingleRef( aAdr ) ) + { + PushInt(0); + return false; + } + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + { + rEntry.bQueryByString = false; + rEntry.nVal = GetCellValue( aAdr, pCell ); + } + else + { + if ( GetCellType( pCell ) == CELLTYPE_NOTE ) + { + rEntry.bQueryByString = false; + rEntry.nVal = 0.0; + } + else + { + String sStr; + GetCellString(sStr, pCell); + rEntry.bQueryByString = sal_True; + *rEntry.pStr = sStr; + } + } + } + break; + case svMatrix : + { + const ScMatValType nType = GetDoubleOrStringFromMatrix(rEntry.nVal, *rEntry.pStr); + rEntry.bQueryByString = ScMatrix::IsNonValueType( nType); + } + break; + default: + { + PushIllegalParameter(); + return false; + } + } // switch ( GetStackType() ) + return true; +} +void ScInterpreter::ScVLookup() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScVLookup" ); + CalculateLookup(false); +} + +void ScInterpreter::ScSubTotal() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSubTotal" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCountMin( nParamCount, 2 ) ) + { + // We must fish the 1st parameter deep from the stack! And push it on top. + const FormulaToken* p = pStack[ sp - nParamCount ]; + PushTempToken( *p ); + int nFunc = (int) ::rtl::math::approxFloor( GetDouble() ); + if( nFunc < 1 || nFunc > 11 ) + PushIllegalArgument(); // simulate return on stack, not SetError(...) + else + { + cPar = nParamCount - 1; + glSubTotal = sal_True; + switch( nFunc ) + { + case SUBTOTAL_FUNC_AVE : ScAverage(); break; + case SUBTOTAL_FUNC_CNT : ScCount(); break; + case SUBTOTAL_FUNC_CNT2 : ScCount2(); break; + case SUBTOTAL_FUNC_MAX : ScMax(); break; + case SUBTOTAL_FUNC_MIN : ScMin(); break; + case SUBTOTAL_FUNC_PROD : ScProduct(); break; + case SUBTOTAL_FUNC_STD : ScStDev(); break; + case SUBTOTAL_FUNC_STDP : ScStDevP(); break; + case SUBTOTAL_FUNC_SUM : ScSum(); break; + case SUBTOTAL_FUNC_VAR : ScVar(); break; + case SUBTOTAL_FUNC_VARP : ScVarP(); break; + default : PushIllegalArgument(); break; + } + glSubTotal = false; + } + // Get rid of the 1st (fished) parameter. + double nVal = GetDouble(); + Pop(); + PushDouble( nVal ); + } +} + +ScDBQueryParamBase* ScInterpreter::GetDBParams( sal_Bool& rMissingField ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDBParams" ); + sal_Bool bAllowMissingField = false; + if ( rMissingField ) + { + bAllowMissingField = sal_True; + rMissingField = false; + } + if ( GetByte() == 3 ) + { + // First, get the query criteria range. + ::std::auto_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() ); + if (!pQueryRef.get()) + return NULL; + + sal_Bool bByVal = sal_True; + double nVal = 0.0; + String aStr; + ScRange aMissingRange; + sal_Bool bRangeFake = false; + switch (GetStackType()) + { + case svDouble : + nVal = ::rtl::math::approxFloor( GetDouble() ); + if ( bAllowMissingField && nVal == 0.0 ) + rMissingField = sal_True; // fake missing parameter + break; + case svString : + bByVal = false; + aStr = GetString(); + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + ScBaseCell* pCell = GetCell( aAdr ); + if (HasCellValueData(pCell)) + nVal = GetCellValue( aAdr, pCell ); + else + { + bByVal = false; + GetCellString(aStr, pCell); + } + } + break; + case svDoubleRef : + if ( bAllowMissingField ) + { // fake missing parameter for old SO compatibility + bRangeFake = sal_True; + PopDoubleRef( aMissingRange ); + } + else + { + PopError(); + SetError( errIllegalParameter ); + } + break; + case svMissing : + PopError(); + if ( bAllowMissingField ) + rMissingField = sal_True; + else + SetError( errIllegalParameter ); + break; + default: + PopError(); + SetError( errIllegalParameter ); + } + + auto_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() ); + + if (nGlobalError || !pDBRef.get()) + return NULL; + + if ( bRangeFake ) + { + // range parameter must match entire database range + if (pDBRef->isRangeEqual(aMissingRange)) + rMissingField = sal_True; + else + SetError( errIllegalParameter ); + } + + if (nGlobalError) + return NULL; + + SCCOL nField = pDBRef->getFirstFieldColumn(); + if (rMissingField) + ; // special case + else if (bByVal) + nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal)); + else + { + sal_uInt16 nErr = 0; + nField = pDBRef->findFieldColumn(aStr, &nErr); + SetError(nErr); + } + + if (!ValidCol(nField)) + return NULL; + + auto_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) ); + + if (pParam.get()) + { + // An allowed missing field parameter sets the result field + // to any of the query fields, just to be able to return + // some cell from the iterator. + if ( rMissingField ) + nField = static_cast<SCCOL>(pParam->GetEntry(0).nField); + pParam->mnField = nField; + + SCSIZE nCount = pParam->GetEntryCount(); + for ( SCSIZE i=0; i < nCount; i++ ) + { + ScQueryEntry& rEntry = pParam->GetEntry(i); + if ( rEntry.bDoQuery ) + { + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = !pFormatter->IsNumberFormat( + *rEntry.pStr, nIndex, rEntry.nVal ); + if ( rEntry.bQueryByString && !pParam->bRegExp ) + pParam->bRegExp = MayBeRegExp( *rEntry.pStr, pDok ); + } + else + break; // for + } + return pParam.release(); + } + } + return false; +} + + +void ScInterpreter::DBIterator( ScIterFunc eFunc ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::DBIterator" ); + double nErg = 0.0; + double fMem = 0.0; + sal_Bool bNull = sal_True; + sal_uLong nCount = 0; + sal_Bool bMissingField = false; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + if (!pQueryParam->IsValidFieldIndex()) + { + SetError(errNoValue); + return; + } + ScDBQueryDataIterator aValIter(pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + switch( eFunc ) + { + case ifPRODUCT: nErg = 1; break; + case ifMAX: nErg = -MAXDOUBLE; break; + case ifMIN: nErg = MAXDOUBLE; break; + default: ; // nothing + } + do + { + nCount++; + switch( eFunc ) + { + case ifAVERAGE: + case ifSUM: + if ( bNull && aValue.mfValue != 0.0 ) + { + bNull = false; + fMem = aValue.mfValue; + } + else + nErg += aValue.mfValue; + break; + case ifSUMSQ: nErg += aValue.mfValue * aValue.mfValue; break; + case ifPRODUCT: nErg *= aValue.mfValue; break; + case ifMAX: if( aValue.mfValue > nErg ) nErg = aValue.mfValue; break; + case ifMIN: if( aValue.mfValue < nErg ) nErg = aValue.mfValue; break; + default: ; // nothing + } + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + } + else + SetError( errIllegalParameter); + switch( eFunc ) + { + case ifCOUNT: nErg = nCount; break; + case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break; + case ifAVERAGE: nErg = ::rtl::math::approxAdd( nErg, fMem ) / nCount; break; + default: ; // nothing + } + PushDouble( nErg ); +} + + +void ScInterpreter::ScDBSum() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBSum" ); + DBIterator( ifSUM ); +} + + +void ScInterpreter::ScDBCount() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBCount" ); + sal_Bool bMissingField = sal_True; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + sal_uLong nCount = 0; + if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL ) + { // count all matching records + // TODO: currently the QueryIterators only return cell pointers of + // existing cells, so if a query matches an empty cell there's + // nothing returned, and therefor not counted! + // Since this has ever been the case and this code here only came + // into existance to fix #i6899 and it never worked before we'll + // have to live with it until we reimplement the iterators to also + // return empty cells, which would mean to adapt all callers of + // iterators. + ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get()); + p->nCol2 = p->nCol1; // Don't forget to select only one column. + SCTAB nTab = p->nTab; + // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField, + // so the source range has to be restricted, like before the introduction + // of ScDBQueryParamBase. + p->nCol1 = p->nCol2 = p->mnField; + ScQueryCellIterator aCellIter( pDok, nTab, *p); + if ( aCellIter.GetFirst() ) + { + do + { + nCount++; + } while ( aCellIter.GetNext() ); + } + } + else + { // count only matching records with a value in the "result" field + if (!pQueryParam->IsValidFieldIndex()) + { + SetError(errNoValue); + return; + } + ScDBQueryDataIterator aValIter( pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + do + { + nCount++; + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + } + PushDouble( nCount ); + } + else + PushIllegalParameter(); +} + + +void ScInterpreter::ScDBCount2() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBCount2" ); + sal_Bool bMissingField = sal_True; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + if (!pQueryParam->IsValidFieldIndex()) + { + SetError(errNoValue); + return; + } + sal_uLong nCount = 0; + pQueryParam->mbSkipString = false; + ScDBQueryDataIterator aValIter( pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if ( aValIter.GetFirst(aValue) && !aValue.mnError ) + { + do + { + nCount++; + } + while ( aValIter.GetNext(aValue) && !aValue.mnError ); + } + SetError(aValue.mnError); + PushDouble( nCount ); + } + else + PushIllegalParameter(); +} + + +void ScInterpreter::ScDBAverage() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBAverage" ); + DBIterator( ifAVERAGE ); +} + + +void ScInterpreter::ScDBMax() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBMax" ); + DBIterator( ifMAX ); +} + + +void ScInterpreter::ScDBMin() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBMin" ); + DBIterator( ifMIN ); +} + + +void ScInterpreter::ScDBProduct() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBProduct" ); + DBIterator( ifPRODUCT ); +} + + +void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::GetDBStVarParams" ); + std::vector<double> values; + double vSum = 0.0; + double vMean = 0.0; + + rValCount = 0.0; + double fSum = 0.0; + sal_Bool bMissingField = false; + auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); + if (pQueryParam.get()) + { + if (!pQueryParam->IsValidFieldIndex()) + { + SetError(errNoValue); + return; + } + ScDBQueryDataIterator aValIter(pDok, pQueryParam.release()); + ScDBQueryDataIterator::Value aValue; + if (aValIter.GetFirst(aValue) && !aValue.mnError) + { + do + { + rValCount++; + values.push_back(aValue.mfValue); + fSum += aValue.mfValue; + } + while ((aValue.mnError == 0) && aValIter.GetNext(aValue)); + } + SetError(aValue.mnError); + } + else + SetError( errIllegalParameter); + + vMean = fSum / values.size(); + + for (size_t i = 0; i < values.size(); i++) + vSum += (values[i] - vMean) * (values[i] - vMean); + + rVal = vSum; +} + + +void ScInterpreter::ScDBStdDev() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBStdDev" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble( sqrt(fVal/(fCount-1))); +} + + +void ScInterpreter::ScDBStdDevP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBStdDevP" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble( sqrt(fVal/fCount)); +} + + +void ScInterpreter::ScDBVar() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBVar" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble(fVal/(fCount-1)); +} + + +void ScInterpreter::ScDBVarP() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScDBVarP" ); + double fVal, fCount; + GetDBStVarParams( fVal, fCount ); + PushDouble(fVal/fCount); +} + + +ScTokenArray* lcl_CreateExternalRefTokenArray( const ScAddress& rPos, ScDocument* pDoc, + const ScAddress::ExternalInfo& rExtInfo, const ScRefAddress& rRefAd1, + const ScRefAddress* pRefAd2 ) +{ + ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); + size_t nSheets = 1; + const String* pRealTab = pRefMgr->getRealTableName( rExtInfo.mnFileId, rExtInfo.maTabName); + ScTokenArray* pTokenArray = new ScTokenArray; + if (pRefAd2) + { + ScComplexRefData aRef; + aRef.InitRangeRel( ScRange( rRefAd1.GetAddress(), pRefAd2->GetAddress()), rPos); + aRef.Ref1.SetColRel( rRefAd1.IsRelCol()); + aRef.Ref1.SetRowRel( rRefAd1.IsRelRow()); + aRef.Ref1.SetTabRel( rRefAd1.IsRelTab()); + aRef.Ref1.SetFlag3D( true); + aRef.Ref2.SetColRel( pRefAd2->IsRelCol()); + aRef.Ref2.SetRowRel( pRefAd2->IsRelRow()); + aRef.Ref2.SetTabRel( pRefAd2->IsRelTab()); + nSheets = aRef.Ref2.nTab - aRef.Ref1.nTab + 1; + aRef.Ref2.SetFlag3D( nSheets > 1 ); + pTokenArray->AddExternalDoubleReference( rExtInfo.mnFileId, + (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef); + } + else + { + ScSingleRefData aRef; + aRef.InitAddressRel( rRefAd1.GetAddress(), rPos); + aRef.SetColRel( rRefAd1.IsRelCol()); + aRef.SetRowRel( rRefAd1.IsRelRow()); + aRef.SetTabRel( rRefAd1.IsRelTab()); + aRef.SetFlag3D( true); + pTokenArray->AddExternalSingleReference( rExtInfo.mnFileId, + (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef); + } + // The indirect usage of the external table can't be detected during the + // store-to-file cycle, mark it as permanently referenced so it gets stored + // even if not directly referenced anywhere. + pRefMgr->setCacheTableReferencedPermanently( rExtInfo.mnFileId, + rExtInfo.maTabName, nSheets); + ScCompiler aComp( pDoc, rPos, *pTokenArray); + aComp.CompileTokenArray(); + return pTokenArray; +} + + +void ScInterpreter::ScIndirect() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIndirect" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + bool bTryXlA1 = true; // whether to try XL_A1 style as well. + FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; + if (nParamCount == 2 && 0.0 == ::rtl::math::approxFloor( GetDouble())) + { + eConv = FormulaGrammar::CONV_XL_R1C1; + bTryXlA1 = false; + } + const ScAddress::Details aDetails( eConv, aPos ); + const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos ); + SCTAB nTab = aPos.Tab(); + String sRefStr( GetString() ); + ScRefAddress aRefAd, aRefAd2; + ScAddress::ExternalInfo aExtInfo; + if ( ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) || + (bTryXlA1 && ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd, + aRefAd2, aDetailsXlA1, &aExtInfo))) + { + if (aExtInfo.mbExternal) + { + PushExternalDoubleRef( + aExtInfo.mnFileId, aExtInfo.maTabName, + aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(), + aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab()); + } + else + PushDoubleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(), + aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab() ); + } + else if ( ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) || + (bTryXlA1 && ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd, + aDetailsXlA1, &aExtInfo))) + { + if (aExtInfo.mbExternal) + { + PushExternalSingleRef( + aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab()); + } + else + PushSingleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab() ); + } + else + { + do + { + ScRangeName* pNames = pDok->GetRangeName(); + if (!pNames) + break; + + ScRangeData* pData = pNames->findByName(sRefStr); + if (!pData) + break; + + // We need this in order to obtain a good range. + pData->ValidateTabRefs(); + + ScRange aRange; + + // This is the usual way to treat named ranges containing + // relative references. + if (!pData->IsReference( aRange, aPos)) + break; + + if (aRange.aStart == aRange.aEnd) + PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aStart.Tab()); + else + PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aStart.Tab(), aRange.aEnd.Col(), + aRange.aEnd.Row(), aRange.aEnd.Tab()); + + // success! + return; + } + while (false); + + PushIllegalArgument(); + } + } +} + + +void ScInterpreter::ScAddressFunc() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAddressFunc" ); + String sTabStr; + + sal_uInt8 nParamCount = GetByte(); + if( !MustHaveParamCount( nParamCount, 2, 5 ) ) + return; + + if( nParamCount >= 5 ) + sTabStr = GetString(); + + FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default + if( nParamCount >= 4 && 0.0 == ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0))) + eConv = FormulaGrammar::CONV_XL_R1C1; + + sal_uInt16 nFlags = SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; // default + if( nParamCount >= 3 ) + { + sal_uInt16 n = (sal_uInt16) ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0)); + switch ( n ) + { + default : + PushNoValue(); + return; + + case 5: + case 1 : break; // default + case 6: + case 2 : nFlags = SCA_ROW_ABSOLUTE; break; + case 7: + case 3 : nFlags = SCA_COL_ABSOLUTE; break; + case 8: + case 4 : nFlags = 0; break; // both relative + } + } + nFlags |= SCA_VALID | SCA_VALID_ROW | SCA_VALID_COL; + + SCCOL nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble()); + SCROW nRow = (SCROW) ::rtl::math::approxFloor(GetDouble()); + if( eConv == FormulaGrammar::CONV_XL_R1C1 ) + { + // YUCK! The XL interface actually treats rel R1C1 refs differently + // than A1 + if( !(nFlags & SCA_COL_ABSOLUTE) ) + nCol += aPos.Col() + 1; + if( !(nFlags & SCA_ROW_ABSOLUTE) ) + nRow += aPos.Row() + 1; + } + + --nCol; + --nRow; + if(!ValidCol( nCol) || !ValidRow( nRow)) + { + PushIllegalArgument(); + return; + } + + String aRefStr; + const ScAddress::Details aDetails( eConv, aPos ); + const ScAddress aAdr( nCol, nRow, 0); + aAdr.Format( aRefStr, nFlags, pDok, aDetails ); + + if( nParamCount >= 5 ) + { + ScCompiler::CheckTabQuotes( sTabStr, eConv); + sTabStr += static_cast<sal_Unicode>(eConv == FormulaGrammar::CONV_XL_R1C1 ? '!' : '.'); + sTabStr += aRefStr; + PushString( sTabStr ); + } + else + PushString( aRefStr ); +} + + +void ScInterpreter::ScOffset() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScOffset" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 5 ) ) + { + long nColNew = -1, nRowNew = -1, nColPlus, nRowPlus; + if (nParamCount == 5) + nColNew = (long) ::rtl::math::approxFloor(GetDouble()); + if (nParamCount >= 4) + nRowNew = (long) ::rtl::math::approxFloor(GetDoubleWithDefault( -1.0 )); + nColPlus = (long) ::rtl::math::approxFloor(GetDouble()); + nRowPlus = (long) ::rtl::math::approxFloor(GetDouble()); + SCCOL nCol1(0); + SCROW nRow1(0); + SCTAB nTab1(0); + SCCOL nCol2(0); + SCROW nRow2(0); + SCTAB nTab2(0); + if (nColNew == 0 || nRowNew == 0) + { + PushIllegalArgument(); + return; + } + if (GetStackType() == svSingleRef) + { + PopSingleRef(nCol1, nRow1, nTab1); + if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0)) + { + nCol1 = (SCCOL)((long) nCol1 + nColPlus); + nRow1 = (SCROW)((long) nRow1 + nRowPlus); + if (!ValidCol(nCol1) || !ValidRow(nRow1)) + PushIllegalArgument(); + else + PushSingleRef(nCol1, nRow1, nTab1); + } + else + { + if (nColNew < 0) + nColNew = 1; + if (nRowNew < 0) + nRowNew = 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); // ! nCol1 wird veraendert! + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nRow2 = (SCROW)((long)nRow1+nRowNew-1); + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2)) + PushIllegalArgument(); + else + PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); + } + } + else if (GetStackType() == svExternalSingleRef) + { + sal_uInt16 nFileId; + String aTabName; + ScSingleRefData aRef; + PopExternalSingleRef(nFileId, aTabName, aRef); + aRef.CalcAbsIfRel(aPos); + nCol1 = aRef.nCol; + nRow1 = aRef.nRow; + nTab1 = aRef.nTab; + + if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0)) + { + nCol1 = (SCCOL)((long) nCol1 + nColPlus); + nRow1 = (SCROW)((long) nRow1 + nRowPlus); + if (!ValidCol(nCol1) || !ValidRow(nRow1)) + PushIllegalArgument(); + else + PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1); + } + else + { + if (nColNew < 0) + nColNew = 1; + if (nRowNew < 0) + nRowNew = 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); // ! nCol1 wird veraendert! + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nTab2 = nTab1; + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2)) + PushIllegalArgument(); + else + PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + } + else if (GetStackType() == svDoubleRef) + { + PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if (nColNew < 0) + nColNew = nCol2 - nCol1 + 1; + if (nRowNew < 0) + nRowNew = nRow2 - nRow1 + 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nRow2 = (SCROW)((long)nRow1+nRowNew-1); + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2) + PushIllegalArgument(); + else + PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); + } + else if (GetStackType() == svExternalDoubleRef) + { + sal_uInt16 nFileId; + String aTabName; + ScComplexRefData aRef; + PopExternalDoubleRef(nFileId, aTabName, aRef); + aRef.CalcAbsIfRel(aPos); + nCol1 = aRef.Ref1.nCol; + nRow1 = aRef.Ref1.nRow; + nTab1 = aRef.Ref1.nTab; + nCol2 = aRef.Ref2.nCol; + nRow2 = aRef.Ref2.nRow; + nTab2 = aRef.Ref2.nTab; + if (nColNew < 0) + nColNew = nCol2 - nCol1 + 1; + if (nRowNew < 0) + nRowNew = nRow2 - nRow1 + 1; + nCol1 = (SCCOL)((long)nCol1+nColPlus); + nRow1 = (SCROW)((long)nRow1+nRowPlus); + nCol2 = (SCCOL)((long)nCol1+nColNew-1); + nRow2 = (SCROW)((long)nRow1+nRowNew-1); + if (!ValidCol(nCol1) || !ValidRow(nRow1) || + !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2) + PushIllegalArgument(); + else + PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + } + else + PushIllegalParameter(); + } +} + + +void ScInterpreter::ScIndex() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScIndex" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 4 ) ) + { + long nArea; + size_t nAreaCount; + SCCOL nCol; + SCROW nRow; + if (nParamCount == 4) + nArea = (long) ::rtl::math::approxFloor(GetDouble()); + else + nArea = 1; + if (nParamCount >= 3) + nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble()); + else + nCol = 0; + if (nParamCount >= 2) + nRow = (SCROW) ::rtl::math::approxFloor(GetDouble()); + else + nRow = 0; + if (GetStackType() == svRefList) + nAreaCount = (sp ? static_cast<ScToken*>(pStack[sp-1])->GetRefList()->size() : 0); + else + nAreaCount = 1; // one reference or array or whatever + if (nAreaCount == 0 || (size_t)nArea > nAreaCount) + { + PushError( errNoRef); + return; + } + else if (nArea < 1 || nCol < 0 || nRow < 0) + { + PushIllegalArgument(); + return; + } + switch (GetStackType()) + { + case svMatrix: + case svExternalSingleRef: + case svExternalDoubleRef: + { + if (nArea != 1) + SetError(errIllegalArgument); + sal_uInt16 nOldSp = sp; + ScMatrixRef pMat = GetMatrix(); + if (pMat) + { + SCSIZE nC, nR; + pMat->GetDimensions(nC, nR); + // Access one element of a vector independent of col/row + // orientation? + bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1)); + SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol), + static_cast<SCSIZE>(nRow)); + if (nC == 0 || nR == 0 || + (!bVector && (static_cast<SCSIZE>(nCol) > nC || + static_cast<SCSIZE>(nRow) > nR)) || + (bVector && nElement > nC * nR)) + PushIllegalArgument(); + else if (nCol == 0 && nRow == 0) + sp = nOldSp; + else if (bVector) + { + --nElement; + if (pMat->IsString( nElement)) + PushString( pMat->GetString( nElement)); + else + PushDouble( pMat->GetDouble( nElement)); + } + else if (nCol == 0) + { + ScMatrixRef pResMat = GetNewMat(nC, 1); + if (pResMat) + { + SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1); + for (SCSIZE i = 0; i < nC; i++) + if (!pMat->IsString(i, nRowMinus1)) + pResMat->PutDouble(pMat->GetDouble(i, + nRowMinus1), i, 0); + else + pResMat->PutString(pMat->GetString(i, + nRowMinus1), i, 0); + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else if (nRow == 0) + { + ScMatrixRef pResMat = GetNewMat(1, nR); + if (pResMat) + { + SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1); + for (SCSIZE i = 0; i < nR; i++) + if (!pMat->IsString(nColMinus1, i)) + pResMat->PutDouble(pMat->GetDouble(nColMinus1, + i), i); + else + pResMat->PutString(pMat->GetString(nColMinus1, + i), i); + PushMatrix(pResMat); + } + else + PushIllegalArgument(); + } + else + { + if (!pMat->IsString( static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))) + PushDouble( pMat->GetDouble( + static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))); + else + PushString( pMat->GetString( + static_cast<SCSIZE>(nCol-1), + static_cast<SCSIZE>(nRow-1))); + } + } + } + break; + case svSingleRef: + { + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + PopSingleRef( nCol1, nRow1, nTab1); + if (nCol > 1 || nRow > 1) + PushIllegalArgument(); + else + PushSingleRef( nCol1, nRow1, nTab1); + } + break; + case svDoubleRef: + case svRefList: + { + SCCOL nCol1 = 0; + SCROW nRow1 = 0; + SCTAB nTab1 = 0; + SCCOL nCol2 = 0; + SCROW nRow2 = 0; + SCTAB nTab2 = 0; + sal_Bool bRowArray = false; + if (GetStackType() == svRefList) + { + FormulaTokenRef xRef = PopToken(); + if (nGlobalError || !xRef) + { + PushIllegalParameter(); + return; + } + ScRange aRange( ScAddress::UNINITIALIZED); + DoubleRefToRange( (*(static_cast<ScToken*>(xRef.get())->GetRefList()))[nArea-1], aRange); + aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if ( nParamCount == 2 && nRow1 == nRow2 ) + bRowArray = sal_True; + } + else + { + PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + if ( nParamCount == 2 && nRow1 == nRow2 ) + bRowArray = sal_True; + } + if ( nTab1 != nTab2 || + (nCol > 0 && nCol1+nCol-1 > nCol2) || + (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) || + ( nRow > nCol2 - nCol1 + 1 && bRowArray )) + PushIllegalArgument(); + else if (nCol == 0 && nRow == 0) + { + if ( nCol1 == nCol2 && nRow1 == nRow2 ) + PushSingleRef( nCol1, nRow1, nTab1 ); + else + PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 ); + } + else if (nRow == 0) + { + if ( nRow1 == nRow2 ) + PushSingleRef( nCol1+nCol-1, nRow1, nTab1 ); + else + PushDoubleRef( nCol1+nCol-1, nRow1, nTab1, + nCol1+nCol-1, nRow2, nTab1 ); + } + else if (nCol == 0) + { + if ( nCol1 == nCol2 ) + PushSingleRef( nCol1, nRow1+nRow-1, nTab1 ); + else if ( bRowArray ) + { + nCol =(SCCOL) nRow; + nRow = 1; + PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); + } + else + PushDoubleRef( nCol1, nRow1+nRow-1, nTab1, + nCol2, nRow1+nRow-1, nTab1); + } + else + PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); + } + break; + default: + PushIllegalParameter(); + } + } +} + + +void ScInterpreter::ScMultiArea() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMultiArea" ); + // Legacy support, convert to RefList + sal_uInt8 nParamCount = GetByte(); + if (MustHaveParamCountMin( nParamCount, 1)) + { + while (!nGlobalError && nParamCount-- > 1) + { + ScUnionFunc(); + } + } +} + + +void ScInterpreter::ScAreas() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScAreas" ); + sal_uInt8 nParamCount = GetByte(); + if (MustHaveParamCount( nParamCount, 1)) + { + size_t nCount = 0; + switch (GetStackType()) + { + case svSingleRef: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( static_cast<ScToken*>(xT.get())->GetSingleRef()); + ++nCount; + } + break; + case svDoubleRef: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( static_cast<ScToken*>(xT.get())->GetDoubleRef()); + ++nCount; + } + break; + case svRefList: + { + FormulaTokenRef xT = PopToken(); + ValidateRef( *(static_cast<ScToken*>(xT.get())->GetRefList())); + nCount += static_cast<ScToken*>(xT.get())->GetRefList()->size(); + } + break; + default: + SetError( errIllegalParameter); + } + PushDouble( double(nCount)); + } +} + + +void ScInterpreter::ScCurrency() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScCurrency" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + String aStr; + double fDec; + if (nParamCount == 2) + { + fDec = ::rtl::math::approxFloor(GetDouble()); + if (fDec < -15.0 || fDec > 15.0) + { + PushIllegalArgument(); + return; + } + } + else + fDec = 2.0; + double fVal = GetDouble(); + double fFac; + if ( fDec != 0.0 ) + fFac = pow( (double)10, fDec ); + else + fFac = 1.0; + if (fVal < 0.0) + fVal = ceil(fVal*fFac-0.5)/fFac; + else + fVal = floor(fVal*fFac+0.5)/fFac; + Color* pColor = NULL; + if ( fDec < 0.0 ) + fDec = 0.0; + sal_uLong nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_CURRENCY, + ScGlobal::eLnge); + if ( (sal_uInt16) fDec != pFormatter->GetFormatPrecision( nIndex ) ) + { + String sFormatString; + pFormatter->GenerateFormat(sFormatString, + nIndex, + ScGlobal::eLnge, + sal_True, // mit Tausenderpunkt + false, // nicht rot + (sal_uInt16) fDec,// Nachkommastellen + 1); // 1 Vorkommanull + if (!pFormatter->GetPreviewString(sFormatString, + fVal, + aStr, + &pColor, + ScGlobal::eLnge)) + SetError(errIllegalArgument); + } + else + { + pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor); + } + PushString(aStr); + } +} + + +void ScInterpreter::ScReplace() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScReplace" ); + if ( MustHaveParamCount( GetByte(), 4 ) ) + { + String aNewStr( GetString() ); + double fCount = ::rtl::math::approxFloor( GetDouble()); + double fPos = ::rtl::math::approxFloor( GetDouble()); + String aOldStr( GetString() ); + if (fPos < 1.0 || fPos > static_cast<double>(STRING_MAXLEN) + || fCount < 0.0 || fCount > static_cast<double>(STRING_MAXLEN)) + PushIllegalArgument(); + else + { + xub_StrLen nCount = static_cast<xub_StrLen>(fCount); + xub_StrLen nPos = static_cast<xub_StrLen>(fPos); + xub_StrLen nLen = aOldStr.Len(); + if (nPos > nLen + 1) + nPos = nLen + 1; + if (nCount > nLen - nPos + 1) + nCount = nLen - nPos + 1; + aOldStr.Erase( nPos-1, nCount ); + if ( CheckStringResultLen( aOldStr, aNewStr ) ) + aOldStr.Insert( aNewStr, nPos-1 ); + PushString( aOldStr ); + } + } +} + + +void ScInterpreter::ScFixed() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFixed" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 3 ) ) + { + String aStr; + double fDec; + sal_Bool bThousand; + if (nParamCount == 3) + bThousand = !GetBool(); // Param TRUE: keine Tausenderpunkte + else + bThousand = sal_True; + if (nParamCount >= 2) + { + fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 )); + if (fDec < -15.0 || fDec > 15.0) + { + PushIllegalArgument(); + return; + } + } + else + fDec = 2.0; + double fVal = GetDouble(); + double fFac; + if ( fDec != 0.0 ) + fFac = pow( (double)10, fDec ); + else + fFac = 1.0; + if (fVal < 0.0) + fVal = ceil(fVal*fFac-0.5)/fFac; + else + fVal = floor(fVal*fFac+0.5)/fFac; + Color* pColor = NULL; + String sFormatString; + if (fDec < 0.0) + fDec = 0.0; + sal_uLong nIndex = pFormatter->GetStandardFormat( + NUMBERFORMAT_NUMBER, + ScGlobal::eLnge); + pFormatter->GenerateFormat(sFormatString, + nIndex, + ScGlobal::eLnge, + bThousand, // mit Tausenderpunkt + false, // nicht rot + (sal_uInt16) fDec,// Nachkommastellen + 1); // 1 Vorkommanull + if (!pFormatter->GetPreviewString(sFormatString, + fVal, + aStr, + &pColor, + ScGlobal::eLnge)) + PushIllegalArgument(); + else + PushString(aStr); + } +} + + +void ScInterpreter::ScFind() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScFind" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + double fAnz; + if (nParamCount == 3) + fAnz = GetDouble(); + else + fAnz = 1.0; + String sStr = GetString(); + if( fAnz < 1.0 || fAnz > (double) sStr.Len() ) + PushNoValue(); + else + { + xub_StrLen nPos = sStr.Search( GetString(), (xub_StrLen) fAnz - 1 ); + if (nPos == STRING_NOTFOUND) + PushNoValue(); + else + PushDouble((double)(nPos + 1)); + } + } +} + + +void ScInterpreter::ScExact() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScExact" ); + nFuncFmtType = NUMBERFORMAT_LOGICAL; + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String s1( GetString() ); + String s2( GetString() ); + PushInt( s1 == s2 ); + } +} + + +void ScInterpreter::ScLeft() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScLeft" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + xub_StrLen n; + if (nParamCount == 2) + { + double nVal = ::rtl::math::approxFloor(GetDouble()); + if ( nVal < 0.0 || nVal > STRING_MAXLEN ) + { + PushIllegalArgument(); + return ; + } + else + n = (xub_StrLen) nVal; + } + else + n = 1; + String aStr( GetString() ); + aStr.Erase( n ); + PushString( aStr ); + } +} + + +void ScInterpreter::ScRight() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRight" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 1, 2 ) ) + { + xub_StrLen n; + if (nParamCount == 2) + { + double nVal = ::rtl::math::approxFloor(GetDouble()); + if ( nVal < 0.0 || nVal > STRING_MAXLEN ) + { + PushIllegalArgument(); + return ; + } + else + n = (xub_StrLen) nVal; + } + else + n = 1; + String aStr( GetString() ); + if( n < aStr.Len() ) + aStr.Erase( 0, aStr.Len() - n ); + PushString( aStr ); + } +} + + +void ScInterpreter::ScSearch() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSearch" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 2, 3 ) ) + { + double fAnz; + if (nParamCount == 3) + { + fAnz = ::rtl::math::approxFloor(GetDouble()); + if (fAnz > double(STRING_MAXLEN)) + { + PushIllegalArgument(); + return; + } + } + else + fAnz = 1.0; + String sStr = GetString(); + String SearchStr = GetString(); + xub_StrLen nPos = (xub_StrLen) fAnz - 1; + xub_StrLen nEndPos = sStr.Len(); + if( nPos >= nEndPos ) + PushNoValue(); + else + { + utl::SearchParam::SearchType eSearchType = + (MayBeRegExp( SearchStr, pDok ) ? + utl::SearchParam::SRCH_REGEXP : utl::SearchParam::SRCH_NORMAL); + utl::SearchParam sPar(SearchStr, eSearchType, false, false, false); + utl::TextSearch sT( sPar, *ScGlobal::pCharClass ); + int nBool = sT.SearchFrwrd(sStr, &nPos, &nEndPos); + if (!nBool) + PushNoValue(); + else + PushDouble((double)(nPos) + 1); + } + } +} + + +void ScInterpreter::ScMid() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScMid" ); + if ( MustHaveParamCount( GetByte(), 3 ) ) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + double fAnfang = ::rtl::math::approxFloor(GetDouble()); + const String& rStr = GetString(); + if (fAnfang < 1.0 || fAnz < 0.0 || fAnfang > double(STRING_MAXLEN) || fAnz > double(STRING_MAXLEN)) + PushIllegalArgument(); + else + PushString(rStr.Copy( (xub_StrLen) fAnfang - 1, (xub_StrLen) fAnz )); + } +} + + +void ScInterpreter::ScText() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScText" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + String sFormatString = GetString(); + String aStr; + bool bString = false; + double fVal = 0.0; + switch (GetStackType()) + { + case svError: + PopError(); + break; + case svDouble: + fVal = PopDouble(); + break; + default: + { + FormulaTokenRef xTok( PopToken()); + if (!nGlobalError) + { + PushTempToken( xTok.get()); + // Temporarily override the ConvertStringToValue() + // error for GetCellValue() / GetCellValueOrZero() + sal_uInt16 nSErr = mnStringNoValueError; + mnStringNoValueError = errNotNumericString; + fVal = GetDouble(); + mnStringNoValueError = nSErr; + if (nGlobalError == errNotNumericString) + { + // Not numeric. + nGlobalError = 0; + PushTempToken( xTok.get()); + aStr = GetString(); + bString = true; + } + } + } + } + if (nGlobalError) + PushError( nGlobalError); + else + { + String aResult; + Color* pColor = NULL; + LanguageType eCellLang; + const ScPatternAttr* pPattern = pDok->GetPattern( + aPos.Col(), aPos.Row(), aPos.Tab() ); + if ( pPattern ) + eCellLang = ((const SvxLanguageItem&) + pPattern->GetItem( ATTR_LANGUAGE_FORMAT )).GetValue(); + else + eCellLang = ScGlobal::eLnge; + if (bString) + { + if (!pFormatter->GetPreviewString( sFormatString, aStr, + aResult, &pColor, eCellLang)) + PushIllegalArgument(); + else + PushString( aResult); + } + else + { + if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal, + aResult, &pColor, eCellLang)) + PushIllegalArgument(); + else + PushString( aResult); + } + } + } +} + + +void ScInterpreter::ScSubstitute() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSubstitute" ); + sal_uInt8 nParamCount = GetByte(); + if ( MustHaveParamCount( nParamCount, 3, 4 ) ) + { + xub_StrLen nAnz; + if (nParamCount == 4) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + if( fAnz < 1 || fAnz > STRING_MAXLEN ) + { + PushIllegalArgument(); + return; + } + else + nAnz = (xub_StrLen) fAnz; + } + else + nAnz = 0; + String sNewStr = GetString(); + String sOldStr = GetString(); + String sStr = GetString(); + xub_StrLen nPos = 0; + xub_StrLen nCount = 0; + xub_StrLen nNewLen = sNewStr.Len(); + xub_StrLen nOldLen = sOldStr.Len(); + while( sal_True ) + { + nPos = sStr.Search( sOldStr, nPos ); + if (nPos != STRING_NOTFOUND) + { + nCount++; + if( !nAnz || nCount == nAnz ) + { + sStr.Erase(nPos,nOldLen); + if ( CheckStringResultLen( sStr, sNewStr ) ) + { + sStr.Insert(sNewStr,nPos); + nPos = sal::static_int_cast<xub_StrLen>( nPos + nNewLen ); + } + else + break; + } + else + nPos++; + } + else + break; + } + PushString( sStr ); + } +} + + +void ScInterpreter::ScRept() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScRept" ); + if ( MustHaveParamCount( GetByte(), 2 ) ) + { + double fAnz = ::rtl::math::approxFloor(GetDouble()); + String aStr( GetString() ); + if ( fAnz < 0.0 ) + PushIllegalArgument(); + else if ( fAnz * aStr.Len() > STRING_MAXLEN ) + { + PushError( errStringOverflow ); + } + else if ( fAnz == 0.0 ) + PushString( EMPTY_STRING ); + else + { + xub_StrLen n = (xub_StrLen) fAnz; + const xub_StrLen nLen = aStr.Len(); + String aRes; + const sal_Unicode* const pSrc = aStr.GetBuffer(); + sal_Unicode* pDst = aRes.AllocBuffer( n * nLen ); + while( n-- ) + { + memcpy( pDst, pSrc, nLen * sizeof(sal_Unicode) ); + pDst += nLen; + } + PushString( aRes ); + } + } +} + + +void ScInterpreter::ScConcat() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScConcat" ); + sal_uInt8 nParamCount = GetByte(); + String aRes; + while( nParamCount-- > 0) + { + const String& rStr = GetString(); + aRes.Insert( rStr, 0 ); + } + PushString( aRes ); +} + + +void ScInterpreter::ScErrorType() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScErrorType" ); + sal_uInt16 nErr; + sal_uInt16 nOldError = nGlobalError; + nGlobalError = 0; + switch ( GetStackType() ) + { + case svRefList : + { + FormulaTokenRef x = PopToken(); + if (nGlobalError) + nErr = nGlobalError; + else + { + const ScRefList* pRefList = static_cast<ScToken*>(x.get())->GetRefList(); + size_t n = pRefList->size(); + if (!n) + nErr = errNoRef; + else if (n > 1) + nErr = errNoValue; + else + { + ScRange aRange; + DoubleRefToRange( (*pRefList)[0], aRange); + if (nGlobalError) + nErr = nGlobalError; + else + { + ScAddress aAdr; + if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) + nErr = pDok->GetErrCode( aAdr ); + else + nErr = nGlobalError; + } + } + } + } + break; + case svDoubleRef : + { + ScRange aRange; + PopDoubleRef( aRange ); + if ( nGlobalError ) + nErr = nGlobalError; + else + { + ScAddress aAdr; + if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) + nErr = pDok->GetErrCode( aAdr ); + else + nErr = nGlobalError; + } + } + break; + case svSingleRef : + { + ScAddress aAdr; + PopSingleRef( aAdr ); + if ( nGlobalError ) + nErr = nGlobalError; + else + nErr = pDok->GetErrCode( aAdr ); + } + break; + default: + PopError(); + nErr = nGlobalError; + } + if ( nErr ) + { + nGlobalError = 0; + PushDouble( nErr ); + } + else + { + nGlobalError = nOldError; + PushNA(); + } +} + + +sal_Bool ScInterpreter::MayBeRegExp( const String& rStr, const ScDocument* pDoc ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::MayBeRegExp" ); + if ( pDoc && !pDoc->GetDocOptions().IsFormulaRegexEnabled() ) + return false; + if ( !rStr.Len() || (rStr.Len() == 1 && rStr.GetChar(0) != '.') ) + return false; // einzelnes Metazeichen kann keine RegExp sein + static const sal_Unicode cre[] = { '.','*','+','?','[',']','^','$','\\','<','>','(',')','|', 0 }; + const sal_Unicode* p1 = rStr.GetBuffer(); + sal_Unicode c1; + while ( ( c1 = *p1++ ) != 0 ) + { + const sal_Unicode* p2 = cre; + while ( *p2 ) + { + if ( c1 == *p2++ ) + return sal_True; + } + } + return false; +} + +static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc, + const ScQueryParam & rParam, const ScQueryEntry & rEntry ) +{ + bool bFound = false; + ScQueryCellIterator aCellIter( pDoc, rParam.nTab, rParam, false); + if (rEntry.eOp != SC_EQUAL) + { + // range lookup <= or >= + SCCOL nCol; + SCROW nRow; + bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow); + if (bFound) + { + o_rResultPos.SetCol( nCol); + o_rResultPos.SetRow( nRow); + } + } + else if (aCellIter.GetFirst()) + { + // EQUAL + bFound = true; + o_rResultPos.SetCol( aCellIter.GetCol()); + o_rResultPos.SetRow( aCellIter.GetRow()); + } + return bFound; +} + +bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, + const ScQueryParam & rParam ) const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::LookupQueryWithCache" ); + bool bFound = false; + const ScQueryEntry& rEntry = rParam.GetEntry(0); + bool bColumnsMatch = (rParam.nCol1 == rEntry.nField); + DBG_ASSERT( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match"); + if (!bColumnsMatch) + bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry); + else + { + ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab, + rParam.nCol2, rParam.nRow2, rParam.nTab); + ScLookupCache& rCache = pDok->GetLookupCache( aLookupRange); + ScLookupCache::QueryCriteria aCriteria( rEntry); + ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos, + aCriteria, aPos); + switch (eCacheResult) + { + case ScLookupCache::NOT_CACHED : + case ScLookupCache::CRITERIA_DIFFERENT : + bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry); + if (eCacheResult == ScLookupCache::NOT_CACHED) + rCache.insert( o_rResultPos, aCriteria, aPos, bFound); + break; + case ScLookupCache::FOUND : + bFound = true; + break; + case ScLookupCache::NOT_AVAILABLE : + ; // nothing, bFound remains FALSE + break; + } + } + return bFound; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |