/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" // INCLUDE --------------------------------------------------------------- #include "scitems.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #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" #ifndef _COMPHELPER_PROCESSFACTORY_HXX_ #include #endif #include #include #include #include #include #include "cellkeytranslator.hxx" #include "lookupcache.hxx" #include "rangenam.hxx" #include "compiler.hxx" #include "externalrefmgr.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 = sal_False; using namespace formula; using ::std::auto_ptr; //----------------------------------------------------------------------------- // Funktionen //----------------------------------------------------------------------------- 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; ScMatValType nType = 0; const ScMatrixValue* pMatVal = pMat->Get( nC, nR, nType); bool bIsValue = ScMatrix::IsValueType( nType); if ( bIsValue ) { fVal = pMatVal->fVal; 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 = !ScMatrix::IsRealStringType( nType); 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); // 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; ScMatValType nType; const ScMatrixValue* pMatVal = pMat->Get( nC, nR, nType); bool bIsValue = ScMatrix::IsValueType( nType); if ( bIsValue ) { fVal = pMatVal->fVal; bIsValue = ::rtl::math::isFinite( fVal ); if ( bIsValue ) { fVal = ::rtl::math::approxFloor( fVal); if ( (fVal < 1) || (fVal >= nJumpCount)) { bIsValue = sal_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); // 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(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(aRange.aEnd.Col()) && aRange.aEnd.Col() != aRange.aStart.Col()) || (nRow > static_cast(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(nCol) ); aAdr.SetRow( static_cast(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]; size_t nStringQuery = 0; // 0:=no, 1:=0, 2:=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 nStringQuery = 2; // 1+1 } } else if( rComp.bVal[ 1 ] ) { fRes = 1; // string is greater than number nStringQuery = 1; // 0+1 } 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 ] ); } if (nStringQuery && pOptions) { const ScQueryEntry& rEntry = pOptions->aQueryEntry; if (!rEntry.bQueryByString && rEntry.pStr->Len() && (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)) { // As in ScTable::ValidQuery() match a numeric string for a // number query that originated from a string, e.g. in SUMIF // and COUNTIF. Transliteration is not needed here. bool bEqual = rComp.pVal[nStringQuery-1]->Equals( *rEntry.pStr); // match => fRes=0, else fRes=1 fRes = (rEntry.eOp == SC_NOT_EQUAL) ? bEqual : !bEqual; } } 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 ] = sal_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 ] = sal_False; } else { aComp.nVal[ i ] = GetCellValue( aAdr, pCell ); aComp.bVal[ i ] = sal_True; } } break; 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 ] = sal_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 ] = sal_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; jValidColRowOrReplicated( nCol, nRow ) && pMat[1]->ValidColRowOrReplicated( nCol, nRow )) { for ( short i=1; i>=0; i-- ) { if ( pMat[i]->IsString(j,k) ) { aComp.bVal[i] = sal_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] = sal_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; SCSIZE n = nC * nR; for ( SCSIZE j=0; jIsValue(j) ) { aComp.bVal[i] = sal_True; aComp.nVal[i] = pMat[i]->GetDouble(j); aComp.bEmpty[i] = sal_False; } else { aComp.bVal[i] = sal_False; *aComp.pVal[i] = pMat[i]->GetString(j); aComp.bEmpty[i] = pMat[i]->IsEmpty(j); } pResMat->PutDouble( CompareFunc( aComp, pOptions ), j ); } } } nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL; return pResMat; } ScMatrixRef ScInterpreter::QueryMat( ScMatrix* 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); DBG_ERROR1( "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 = sal_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 setzt hier keinen Fehler } } 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 = sal_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 = sal_False; short nRes = sal_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 setzt hier keinen Fehler } } 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 = sal_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::ScXor() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "makkica", "ScInterpreter::ScXor" ); nFuncFmtType = NUMBERFORMAT_LOGICAL; short nParamCount = GetByte(); if ( MustHaveParamCountMin( nParamCount, 1 ) ) { bool bHaveValue = false; short nRes = 0; size_t nRefInList = 0; while( nParamCount-- > 0) { if ( !nGlobalError ) { switch ( GetStackType() ) { case svDouble : bHaveValue = 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 = true; nRes ^= ( GetCellValue( aAdr, pCell ) != 0.0 ); } /* TODO: set error? Excel doesn't have XOR, but * doesn't set an error in this case for AND and * OR. */ } } 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 = true; do { nRes ^= ( fVal != 0.0 ); } while ( (nErr == 0) && aValIter.GetNext( fVal, nErr ) ); } SetError( nErr ); } } break; case svMatrix: { bHaveValue = true; ScMatrixRef pMat = GetMatrix(); if ( pMat ) { bHaveValue = true; double fVal = pMat->Xor(); sal_uInt16 nErr = GetDoubleErrorValue( fVal ); if ( nErr ) { SetError( nErr ); nRes = 0; } 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 { SCSIZE nCount = nC * nR; for ( SCSIZE j=0; jIsValueOrEmpty(j) ) pResMat->PutDouble( -pMat->GetDouble(j), j ); else pResMat->PutString( ScGlobal::GetRscString( STR_NO_VALUE ), 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 { SCSIZE nCount = nC * nR; for ( SCSIZE j=0; jIsValueOrEmpty(j) ) pResMat->PutDouble( (pMat->GetDouble(j) == 0.0), j ); else pResMat->PutString( ScGlobal::GetRscString( STR_NO_VALUE ), 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::ScCosecant() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "regina", "ScInterpreter::ScCosecant" ); PushDouble(1.0 / ::rtl::math::sin(GetDouble())); } void ScInterpreter::ScSecant() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "regina", "ScInterpreter::ScSecant" ); PushDouble(1.0 / ::rtl::math::cos(GetDouble())); } void ScInterpreter::ScCosecantHyp() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "regina", "ScInterpreter::ScCosecantHyp" ); PushDouble(1.0 / sinh(GetDouble())); } void ScInterpreter::ScSecantHyp() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "regina", "ScInterpreter::ScSecantHyp" ); PushDouble(1.0 / cosh(GetDouble())); } 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(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 ); 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) && !pMat->IsEmpty(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 = sal_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(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 = sal_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(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) nRes = pMat->IsValue( 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 ) == 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 ); 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); 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 ); if ( nRes ) fVal = pMat->GetDouble( 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() { // trimmt nicht nur sondern schnibbelt auch doppelte raus! 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] != ' ' ) // erster kann kein ' ' sein, -1 ist also ok 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 = sal_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 { // wie 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 * 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::createFromAscii( "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::createFromAscii( "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( 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::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 : { ScMatrixRef pMat = PopMatrix(); 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::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 : { ScMatrixRef pMat = PopMatrix(); 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); } 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; sal_Bool bNull = sal_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 = sal_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 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 = sal_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 : { sal_uInt16 nErr = 0; 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 ); if (aValIter.GetFirst(fVal, nErr)) { // Schleife aus Performance-Gruenden nach innen verlegt: aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex ); switch( eFunc ) { case ifAVERAGE: case ifSUM: do { SetError(nErr); if ( bNull && fVal != 0.0 ) { bNull = sal_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 svMatrix : { ScMatrixRef pMat = PopMatrix(); if (pMat) { SCSIZE nC, nR; nFuncFmtType = NUMBERFORMAT_NUMBER; pMat->GetDimensions(nC, nR); if( eFunc == ifCOUNT2 ) nCount += (sal_uLong) nC * nR; else { for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) { for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) { if (!pMat->IsString(nMatCol,nMatRow)) { nCount++; fVal = pMat->GetDouble(nMatCol,nMatRow); switch( eFunc ) { case ifAVERAGE: case ifSUM: if ( bNull && fVal != 0.0 ) { bNull = sal_False; fMem = fVal; } else fRes += fVal; break; case ifSUMSQ: fRes += fVal * fVal; break; case ifPRODUCT: fRes *= fVal; break; default: ; // nothing } } else if ( bTextAsZero ) { nCount++; if ( eFunc == ifPRODUCT ) fRes = 0.0; } } } } } } 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 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::size_type n = values.size(); vMean = fSum / n; for (::std::vector::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(nTab2 - nTab1 + 1) * static_cast(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(nTab2 - nTab1 + 1) * static_cast(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(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(nCols), 1); if (pResMat) { for (SCCOL i=0; i < nCols; ++i) pResMat->PutDouble( nVal + i, static_cast(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(nCol2-nCol1+1), 1); if (pResMat) { for (SCCOL i = nCol1; i <= nCol2; i++) pResMat->PutDouble((double)(i+1), static_cast(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(nRows)); if (pResMat) { for (SCROW i=0; i < nRows; i++) pResMat->PutDouble( nVal + i, 0, static_cast(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(nRow2-nRow1+1)); if (pResMat) { for (SCROW i = nRow1; i <= nRow2; i++) pResMat->PutDouble((double)(i+1), 0, static_cast(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 ); } } /** 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 ScMatrix& 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 ScMatrix& 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" ); ScMatrixRef pMatSrc = NULL; 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; if (GetStackType() == svDoubleRef) { PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2)) { PushIllegalParameter(); return; } } else if (GetStackType() == svMatrix) { pMatSrc = PopMatrix(); if (!pMatSrc) { PushIllegalParameter(); return; } } else { 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 = sal_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 = sal_False; rEntry.nVal = fVal; } else { GetCellString(sStr, pCell); rEntry.bQueryByString = sal_True; *rEntry.pStr = sStr; } } break; case svMatrix : { ScMatValType nType = GetDoubleOrStringFromMatrix( rEntry.nVal, *rEntry.pStr); rEntry.bQueryByString = ScMatrix::IsNonValueType( nType); } break; default: { PushIllegalParameter(); return; } } if ( rEntry.bQueryByString ) 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; // 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, *pMatSrc, 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, *pMatSrc, rEntry); if (nCmp == 0) { // exact match. find the last item with the same value. lcl_GetLastMatch( nMid, *pMatSrc, 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, *pMatSrc, 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 = sal_False; rParam.nRow2 = nRow1; rEntry.nField = nCol1; ScQueryCellIterator aCellIter(pDok, nTab1, rParam, sal_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(aRange.aEnd.Row() - aRange.aStart.Row() + 1) * static_cast(aRange.aEnd.Col() - aRange.aStart.Col() + 1) * static_cast(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); } } double ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc ) { sal_uInt8 nParamCount = GetByte(); if ( MustHaveParamCount( nParamCount, 2, 3 ) ) { 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 ) { SetError( errIllegalParameter); } } break; case svSingleRef : PopSingleRef( nCol3, nRow3, nTab3 ); break; case svMatrix: pSumExtraMatrix = PopMatrix(); //! nCol3, nRow3, nTab3 remain 0 break; default: SetError( errIllegalParameter); } } String rString; double fVal = 0.0; bool bIsString = true; switch ( GetStackType() ) { case svDoubleRef : case svSingleRef : { ScAddress aAdr; if ( !PopDoubleRefOrSingleRef( aAdr ) ) return 0; 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 svString: rString = GetString(); break; case svMatrix : { ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, rString); bIsString = ScMatrix::IsNonValueType( nType); } break; default: { fVal = GetDouble(); bIsString = false; } } double fSum = 0.0; double fMem = 0.0; double fRes = 0.0; double fCount = 0.0; 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) { SetError( errIllegalParameter); } 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: { pQueryMatrix = PopMatrix(); if (!pQueryMatrix) { SetError( errIllegalParameter); } nCol1 = 0; nRow1 = 0; nTab1 = 0; SCSIZE nC, nR; pQueryMatrix->GetDimensions( nC, nR); nCol2 = static_cast(nC - 1); nRow2 = static_cast(nR - 1); nTab2 = 0; } break; default: SetError( errIllegalParameter); } if ( nTab1 != nTab2 ) { SetError( errIllegalParameter); } 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(nC - 1); nMaxRow = static_cast(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(rString, 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) { SetError( errIllegalParameter); } 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); ++fCount; 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 ); ++fCount; 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); ++fCount; 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 ); ++fCount; if ( bNull && fVal != 0.0 ) { bNull = false; fMem = fVal; } else fSum += fVal; } } while ( aCellIter.GetNext() ); } } } } else { SetError( errIllegalParameter); } } switch( eFunc ) { case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break; case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break; } return fRes; } return 0; } void ScInterpreter::ScSumIf() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "er", "ScInterpreter::ScSumIf" ); PushDouble( IterateParametersIf( ifSUMIF)); } void ScInterpreter::ScAverageIf() { RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "makkica", "ScInterpreter::ScAverageIf" ); PushDouble( IterateParametersIf( ifAVERAGEIF)); } 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 = sal_False; break; case CELLTYPE_FORMULA : if( ((ScFormulaCell*)pCell)->IsValue() ) { fVal = GetCellValue( aAdr, pCell ); bIsString = sal_False; } else GetCellString(rString, pCell); break; case CELLTYPE_STRING : case CELLTYPE_EDIT : GetCellString(rString, pCell); break; default: fVal = 0.0; bIsString = sal_False; } } break; case svMatrix : { ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, rString); bIsString = ScMatrix::IsNonValueType( nType); } break; case svString: rString = GetString(); break; default: { fVal = GetDouble(); bIsString = sal_False; } } double fCount = 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: { pQueryMatrix = PopMatrix(); if (!pQueryMatrix) { PushIllegalParameter(); return; } nCol1 = 0; nRow1 = 0; nTab1 = 0; SCSIZE nC, nR; pQueryMatrix->GetDimensions( nC, nR); nCol2 = static_cast(nC - 1); nRow2 = static_cast(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 = sal_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)) ++fCount; } } else { ScQueryCellIterator aCellIter(pDok, nTab1, rParam, sal_False); // Entry.nField im Iterator bei Spaltenwechsel weiterschalten aCellIter.SetAdvanceQueryParamEntryField( sal_True ); if ( aCellIter.GetFirst() ) { do { fCount++; } while ( aCellIter.GetNext() ); } } } else { PushIllegalParameter(); return; } } PushDouble(fCount); } } 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: { pResMat = PopMatrix(); 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: { pDataMat = PopMatrix(); 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 )) PushDouble(pResMat->GetDouble( 0 )); else PushString(pResMat->GetString( 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; } // 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, *pDataMat2, rEntry); if (nCmp == 0) { // exact match. find the last item with the same value. lcl_GetLastMatch( nMid, *pDataMat2, 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(nLenMajor-2)) // last item { sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, *pDataMat2, 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) { SCCOLROW i = nDelta; SCSIZE n = pDataMat->GetElementCount(); if (static_cast(i) >= n) i = static_cast(n); if (bool(rEntry.bQueryByString) == bool(pDataMat->IsValue(i))) bFound = false; } if (!bFound) { PushNA(); return; } // Now that we've found the delta, push the result back to the cell. if (pResMat) { // result array is matrix. if (static_cast(nDelta) >= pResMat->GetElementCount()) { PushNA(); return; } if (pResMat->IsValue(nDelta)) PushDouble(pResMat->GetDouble(nDelta)); else PushString(pResMat->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(nResRow1 + nDelta); if (nTempRow > MAXROW) { PushDouble(0); return; } aAdr.SetCol(nResCol1); aAdr.SetRow(nTempRow); } else { SCCOL nTempCol = static_cast(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, sal_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(nR-nRow1) : static_cast(nC-nCol1); if (pResMat) { // Use the matrix result array. if (pResMat->IsValue(nDelta)) PushDouble(pResMat->GetDouble(nDelta)); else PushString(pResMat->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(nResRow1 + nDelta); if (nTempRow > MAXROW) { PushDouble(0); return; } aAdr.SetCol(nResCol1); aAdr.SetRow(nTempRow); } else { SCCOL nTempCol = static_cast(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(nRow1 + nDelta); if (nTempRow > MAXROW) { PushDouble(0); return; } aAdr.SetCol(nCol2); aAdr.SetRow(nTempRow); } else { SCCOL nTempCol = static_cast(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; if (GetStackType() == svDoubleRef) { PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); if (nTab1 != nTab2) { PushIllegalParameter(); return; } } else if (GetStackType() == svMatrix) { pMat = PopMatrix(); 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(fIndex); SCCOL nSpIndex = static_cast(fIndex); if (!pMat) { nZIndex += nRow1; // Wertzeile nSpIndex = sal::static_int_cast( 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 = sal_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(nSpIndex); SCSIZE nY = nDelta; if ( HLookup ) { nX = nDelta; nY = static_cast(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 = sal_False; SCCOL nCol = 0; SCROW nRow = 0; if ( bSorted ) rEntry.eOp = SC_LESS_EQUAL; if ( HLookup ) { ScQueryCellIterator aCellIter(pDok, nTab1, rParam, sal_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 = sal_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 = sal_False; rEntry.nVal = GetCellValue( aAdr, pCell ); } else { if ( GetCellType( pCell ) == CELLTYPE_NOTE ) { rEntry.bQueryByString = sal_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(sal_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 = sal_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 = sal_False; if ( rMissingField ) { bAllowMissingField = sal_True; rMissingField = sal_False; } if ( GetByte() == 3 ) { // First, get the query criteria range. ::std::auto_ptr pQueryRef( PopDoubleRef() ); if (!pQueryRef.get()) return NULL; sal_Bool bByVal = sal_True; double nVal = 0.0; String aStr; ScRange aMissingRange; sal_Bool bRangeFake = sal_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 = sal_False; aStr = GetString(); break; case svSingleRef : { ScAddress aAdr; PopSingleRef( aAdr ); ScBaseCell* pCell = GetCell( aAdr ); if (HasCellValueData(pCell)) nVal = GetCellValue( aAdr, pCell ); else { bByVal = sal_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 pDBRef( PopDoubleRef() ); 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(nVal)); else { sal_uInt16 nErr = 0; nField = pDBRef->findFieldColumn(aStr, &nErr); SetError(nErr); } if (!ValidCol(nField)) return NULL; auto_ptr 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(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 = sal_False; auto_ptr pQueryParam( GetDBParams(bMissingField) ); if (pQueryParam.get()) { 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 = sal_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 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(pQueryParam.get()); 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 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 pQueryParam( GetDBParams(bMissingField) ); if (pQueryParam.get()) { 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 values; double vSum = 0.0; double vMean = 0.0; rValCount = 0.0; double fSum = 0.0; sal_Bool bMissingField = sal_False; auto_ptr pQueryParam( GetDBParams(bMissingField) ); if (pQueryParam.get()) { 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); } FormulaSubroutineToken* lcl_CreateExternalRefSubroutine( 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 new FormulaSubroutineToken( 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) { // Push a subroutine that resolves the external reference as // the next instruction. PushTempToken( lcl_CreateExternalRefSubroutine( aPos, pDok, aExtInfo, aRefAd, &aRefAd2)); } 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) { // Push a subroutine that resolves the external reference as // the next instruction. PushTempToken( lcl_CreateExternalRefSubroutine( aPos, pDok, aExtInfo, aRefAd, NULL)); } else PushSingleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab() ); } else { do { ScRangeName* pNames = pDok->GetRangeName(); if (!pNames) break; sal_uInt16 nPos = 0; if (!pNames->SearchName( sRefStr, nPos)) break; ScRangeData* rData = (*pNames)[nPos]; if (!rData) break; // We need this in order to obtain a good range. rData->ValidateTabRefs(); ScRange aRange; #if 0 // This is some really odd Excel behavior and renders named // ranges containing relative references totally useless. if (!rData->IsReference(aRange, ScAddress( aPos.Tab(), 0, 0))) break; #else // This is the usual way to treat named ranges containing // relative references. if (!rData->IsReference( aRange, aPos)) break; #endif 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 && sTabStr.Len() ) { String aDoc; if (eConv == FormulaGrammar::CONV_OOO) { // Isolate Tab from 'Doc'#Tab xub_StrLen nPos = ScCompiler::GetDocTabPos( sTabStr); if (nPos != STRING_NOTFOUND) { if (sTabStr.GetChar(nPos+1) == '$') ++nPos; // also split 'Doc'#$Tab aDoc = sTabStr.Copy( 0, nPos+1); sTabStr.Erase( 0, nPos+1); } } /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may * need some extra handling to isolate Tab from Doc. */ if (sTabStr.GetChar(0) != '\'' || sTabStr.GetChar(sTabStr.Len()-1) != '\'') ScCompiler::CheckTabQuotes( sTabStr, eConv); if (aDoc.Len()) sTabStr.Insert( aDoc, 0); sTabStr += static_cast(eConv == FormulaGrammar::CONV_XL_R1C1 ? '!' : '.'); sTabStr += aRefStr; PushString( sTabStr ); } else PushString( aRefStr ); } FormulaSubroutineToken* lcl_CreateExternalRefSubroutine( const ScAddress& rPos, ScDocument* pDoc, const FormulaTokenRef& xExtRef ) { // The exact usage (which cell range) 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. ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); pRefMgr->setCacheTableReferencedPermanently( static_cast(xExtRef.get())->GetIndex(), static_cast(xExtRef.get())->GetString(), 1); ScTokenArray* pTokenArray = new ScTokenArray; pTokenArray->AddToken( *xExtRef); ScCompiler aComp( pDoc, rPos, *pTokenArray); aComp.CompileTokenArray(); return new FormulaSubroutineToken( pTokenArray); } 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; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; if (nColNew == 0 || nRowNew == 0) { PushIllegalArgument(); return; } FormulaTokenRef xExtRef; switch (GetStackType()) { case svExternalSingleRef: xExtRef = PopToken()->Clone(); // fallthru case svSingleRef: { if (xExtRef) { ScSingleRefData& rData = static_cast(xExtRef.get())->GetSingleRef(); rData.CalcAbsIfRel( aPos); nCol1 = rData.nCol; nRow1 = rData.nRow; nTab1 = rData.nTab; } else 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 if (xExtRef) { ScSingleRefData& rData = static_cast(xExtRef.get())->GetSingleRef(); rData.nCol = nCol1; rData.nRow = nRow1; rData.nTab = nTab1; rData.CalcRelFromAbs( aPos); // Push a subroutine that resolves the external // reference as the next instruction. PushTempToken( lcl_CreateExternalRefSubroutine( aPos, pDok, xExtRef)); } else PushSingleRef(nCol1, nRow1, nTab1); } else { if (nColNew < 0) nColNew = 1; if (nRowNew < 0) nRowNew = 1; nCol1 = (SCCOL)((long)nCol1+nColPlus); // ! nCol1 is modified 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 if (xExtRef) { // Convert SingleRef to DoubleRef. xExtRef = new ScExternalDoubleRefToken( *static_cast(xExtRef.get())); ScComplexRefData& rData = static_cast(xExtRef.get())->GetDoubleRef(); rData.Ref1.nCol = nCol1; rData.Ref1.nRow = nRow1; rData.Ref1.nTab = nTab1; rData.Ref2.nCol = nCol2; rData.Ref2.nRow = nRow2; rData.Ref2.nTab = nTab1; rData.CalcRelFromAbs( aPos); // Push a subroutine that resolves the external // reference as the next instruction. PushTempToken( lcl_CreateExternalRefSubroutine( aPos, pDok, xExtRef)); } else PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); } } break; case svExternalDoubleRef: xExtRef = PopToken()->Clone(); // fallthru case svDoubleRef: { if (xExtRef) { ScComplexRefData& rData = static_cast(xExtRef.get())->GetDoubleRef(); rData.CalcAbsIfRel( aPos); nCol1 = rData.Ref1.nCol; nRow1 = rData.Ref1.nRow; nTab1 = rData.Ref1.nTab; nCol2 = rData.Ref2.nCol; nRow2 = rData.Ref2.nRow; nTab2 = rData.Ref2.nTab; } else 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 if (xExtRef) { ScComplexRefData& rData = static_cast(xExtRef.get())->GetDoubleRef(); rData.Ref1.nCol = nCol1; rData.Ref1.nRow = nRow1; rData.Ref1.nTab = nTab1; rData.Ref2.nCol = nCol2; rData.Ref2.nRow = nRow2; rData.Ref2.nTab = nTab1; rData.CalcRelFromAbs( aPos); // Push a subroutine that resolves the external // reference as the next instruction. PushTempToken( lcl_CreateExternalRefSubroutine( aPos, pDok, xExtRef)); } else PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); } break; default: 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(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: { 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(nCol), static_cast(nRow)); if (nC == 0 || nR == 0 || (!bVector && (static_cast(nCol) > nC || static_cast(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(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(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(nCol-1), static_cast(nRow-1))) PushDouble( pMat->GetDouble( static_cast(nCol-1), static_cast(nRow-1))); else PushString( pMat->GetString( static_cast(nCol-1), static_cast(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 = sal_False; if (GetStackType() == svRefList) { FormulaTokenRef xRef = PopToken(); if (nGlobalError || !xRef) { PushIllegalParameter(); return; } ScRange aRange( ScAddress::UNINITIALIZED); DoubleRefToRange( (*(static_cast(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(xT.get())->GetSingleRef()); ++nCount; } break; case svDoubleRef: { FormulaTokenRef xT = PopToken(); ValidateRef( static_cast(xT.get())->GetDoubleRef()); ++nCount; } break; case svRefList: { FormulaTokenRef xT = PopToken(); ValidateRef( *(static_cast(xT.get())->GetRefList())); nCount += static_cast(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 sal_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(STRING_MAXLEN) || fCount < 0.0 || fCount > static_cast(STRING_MAXLEN)) PushIllegalArgument(); else { xub_StrLen nCount = static_cast(fCount); xub_StrLen nPos = static_cast(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 sal_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" ); double fAnz; sal_uInt8 nParamCount = GetByte(); if ( MustHaveParamCount( nParamCount, 2, 3 ) ) { 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, sal_False, sal_False, sal_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); // 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); 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( 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(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 sal_False; if ( !rStr.Len() || (rStr.Len() == 1 && rStr.GetChar(0) != '.') ) return sal_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 sal_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, sal_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; } #define erDEBUG_LOOKUPCACHE 0 #if erDEBUG_LOOKUPCACHE #include using ::std::fprintf; using ::std::fflush; static struct LookupCacheDebugCounter { unsigned long nMiss; unsigned long nHit; LookupCacheDebugCounter() : nMiss(0), nHit(0) {} ~LookupCacheDebugCounter() { fprintf( stderr, "\nmiss: %lu, hit: %lu, total: %lu, hit/miss: %lu, hit/total %lu%\n", nMiss, nHit, nHit+nMiss, (nMiss>0 ? nHit/nMiss : 0), ((nHit+nMiss)>0 ? (100*nHit)/(nHit+nMiss) : 0)); fflush( stderr); } } aLookupCacheDebugCounter; #endif 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 : #if erDEBUG_LOOKUPCACHE ++aLookupCacheDebugCounter.nMiss; #if erDEBUG_LOOKUPCACHE > 1 fprintf( stderr, "miss %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab()); #endif #endif bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry); if (eCacheResult == ScLookupCache::NOT_CACHED) rCache.insert( o_rResultPos, aCriteria, aPos, bFound); break; case ScLookupCache::FOUND : #if erDEBUG_LOOKUPCACHE ++aLookupCacheDebugCounter.nHit; #if erDEBUG_LOOKUPCACHE > 1 fprintf( stderr, "hit %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab()); #endif #endif bFound = true; break; case ScLookupCache::NOT_AVAILABLE : ; // nothing, bFound remains FALSE break; } } return bFound; }