blob: d5554e30c01e465bc350f55e53e21a20dd4616ca [file] [log] [blame]
/**************************************************************
*
* 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.
*
*************************************************************/
#include "precompiled_formula.hxx"
#include "formula/FormulaCompiler.hxx"
#include "formula/errorcodes.hxx"
#include "formula/token.hxx"
#include "formula/tokenarray.hxx"
#include "core_resource.hxx"
#include "core_resource.hrc"
#include <svl/zforlist.hxx>
#include <tools/rc.hxx>
#include <tools/rcid.h>
#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
#include <com/sun/star/sheet/FormulaMapGroup.hpp>
#include <com/sun/star/sheet/FormulaMapGroupSpecialOffset.hpp>
#include <stdio.h>
// =============================================================================
namespace formula
{
// =============================================================================
using namespace ::com::sun::star;
static const sal_Char* pInternal[ 1 ] = { "TTT" };
// =============================================================================
namespace
{
// =============================================================================
class FormulaCompilerRecursionGuard
{
private:
short& rRecursion;
public:
FormulaCompilerRecursionGuard( short& rRec )
: rRecursion( rRec ) { ++rRecursion; }
~FormulaCompilerRecursionGuard() { --rRecursion; }
};
short lcl_GetRetFormat( OpCode eOpCode )
{
switch (eOpCode)
{
case ocEqual:
case ocNotEqual:
case ocLess:
case ocGreater:
case ocLessEqual:
case ocGreaterEqual:
case ocAnd:
case ocOr:
case ocNot:
case ocTrue:
case ocFalse:
case ocIsEmpty:
case ocIsString:
case ocIsNonString:
case ocIsLogical:
case ocIsRef:
case ocIsValue:
case ocIsFormula:
case ocIsNA:
case ocIsErr:
case ocIsError:
case ocIsEven:
case ocIsOdd:
case ocExact:
return NUMBERFORMAT_LOGICAL;
case ocGetActDate:
case ocGetDate:
case ocEasterSunday :
return NUMBERFORMAT_DATE;
case ocGetActTime:
return NUMBERFORMAT_DATETIME;
case ocGetTime:
return NUMBERFORMAT_TIME;
case ocNPV:
case ocBW:
case ocDIA:
case ocGDA:
case ocGDA2:
case ocVBD:
case ocLIA:
case ocRMZ:
case ocZW:
case ocZinsZ:
case ocKapz:
case ocKumZinsZ:
case ocKumKapZ:
return NUMBERFORMAT_CURRENCY;
case ocZins:
case ocIRR:
case ocMIRR:
case ocZGZ:
case ocEffektiv:
case ocNominal:
case ocPercentSign:
return NUMBERFORMAT_PERCENT;
// case ocSum:
// case ocSumSQ:
// case ocProduct:
// case ocAverage:
// return -1;
default:
return NUMBERFORMAT_NUMBER;
}
return NUMBERFORMAT_NUMBER;
}
inline void lclPushOpCodeMapEntry( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCode )
{
sheet::FormulaOpCodeMapEntry aEntry;
aEntry.Token.OpCode = nOpCode;
aEntry.Name = pTable[nOpCode];
rVec.push_back( aEntry);
}
void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, sal_uInt16 nOpCodeBeg, sal_uInt16 nOpCodeEnd )
{
for (sal_uInt16 nOpCode = nOpCodeBeg; nOpCode < nOpCodeEnd; ++nOpCode)
lclPushOpCodeMapEntry( rVec, pTable, nOpCode );
}
void lclPushOpCodeMapEntries( ::std::vector< sheet::FormulaOpCodeMapEntry >& rVec, const String* pTable, const sal_uInt16* pnOpCodes, size_t nCount )
{
for (const sal_uInt16* pnEnd = pnOpCodes + nCount; pnOpCodes < pnEnd; ++pnOpCodes)
lclPushOpCodeMapEntry( rVec, pTable, *pnOpCodes );
}
class OpCodeList : public Resource // temp object for resource
{
public:
OpCodeList( sal_uInt16, FormulaCompiler::NonConstOpCodeMapPtr );
private:
bool getOpCodeString( String& rStr, sal_uInt16 nOp );
void putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp );
private:
enum SeparatorType
{
SEMICOLON_BASE,
COMMA_BASE
};
SeparatorType meSepType;
};
OpCodeList::OpCodeList( sal_uInt16 nRID, FormulaCompiler::NonConstOpCodeMapPtr xMap ) :
Resource( ResId(nRID,*ResourceManager::getResManager()) )
,meSepType(SEMICOLON_BASE)
{
for (sal_uInt16 i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; ++i)
{
String aOpStr;
if ( getOpCodeString(aOpStr, i) )
xMap->putOpCode(aOpStr, OpCode(i));
else
putDefaultOpCode(xMap, i);
}
FreeResource();
}
bool OpCodeList::getOpCodeString( String& rStr, sal_uInt16 nOp )
{
switch (nOp)
{
case SC_OPCODE_SEP:
{
if (meSepType == COMMA_BASE)
{
rStr = String::CreateFromAscii(",");
return true;
}
else if (meSepType == SEMICOLON_BASE)
{
rStr = String::CreateFromAscii(";");
return true;
}
}
break;
case SC_OPCODE_ARRAY_COL_SEP:
{
if (meSepType == COMMA_BASE)
{
rStr = String::CreateFromAscii(",");
return true;
}
else if (meSepType == SEMICOLON_BASE)
{
rStr = String::CreateFromAscii(";");
return true;
}
}
break;
case SC_OPCODE_ARRAY_ROW_SEP:
{
if (meSepType == COMMA_BASE)
{
rStr = String::CreateFromAscii(";");
return true;
}
else if (meSepType == SEMICOLON_BASE)
{
rStr = String::CreateFromAscii("|");
return true;
}
}
break;
}
return false;
}
void OpCodeList::putDefaultOpCode( FormulaCompiler::NonConstOpCodeMapPtr xMap, sal_uInt16 nOp )
{
ResId aRes(nOp,*ResourceManager::getResManager());
aRes.SetRT(RSC_STRING);
if (IsAvailableRes(aRes))
xMap->putOpCode(aRes, OpCode(nOp));
}
// -----------------------------------------------------------------------------
// static
const sal_Unicode* lcl_UnicodeStrChr( const sal_Unicode* pStr,sal_Unicode c )
{
if ( !pStr )
return NULL;
while ( *pStr )
{
if ( *pStr == c )
return pStr;
pStr++;
}
return NULL;
}
// =============================================================================
} // empty
// =============================================================================
void FormulaCompiler::OpCodeMap::putExternal( const String & rSymbol, const String & rAddIn )
{
bool bOk = mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second;
if (bOk)
bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
DBG_ASSERT( bOk, "OpCodeMap::putExternal: symbol not inserted");
}
void FormulaCompiler::OpCodeMap::putExternalSoftly( const String & rSymbol, const String & rAddIn )
{
bool bOk = mpReverseExternalHashMap->insert( ExternalHashMap::value_type( rAddIn, rSymbol)).second;
if (bOk)
mpExternalHashMap->insert( ExternalHashMap::value_type( rSymbol, rAddIn)).second;
}
uno::Sequence< sheet::FormulaToken > FormulaCompiler::OpCodeMap::createSequenceOfFormulaTokens(const FormulaCompiler& _rCompiler,const uno::Sequence< ::rtl::OUString >& rNames ) const
{
const sal_Int32 nLen = rNames.getLength();
uno::Sequence< sheet::FormulaToken > aTokens( nLen);
sheet::FormulaToken* pToken = aTokens.getArray();
::rtl::OUString const * pName = rNames.getConstArray();
::rtl::OUString const * const pStop = pName + nLen;
for ( ; pName < pStop; ++pName, ++pToken)
{
OpCodeHashMap::const_iterator iLook( mpHashMap->find( *pName));
if (iLook != mpHashMap->end())
pToken->OpCode = (*iLook).second;
else
{
::rtl::OUString aIntName;
if (hasExternals())
{
ExternalHashMap::const_iterator iExt( mpExternalHashMap->find( *pName));
if (iExt != mpExternalHashMap->end())
aIntName = (*iExt).second;
// Check for existence not needed here, only name-mapping is of
// interest.
}
if (!aIntName.getLength())
aIntName = _rCompiler.FindAddInFunction(*pName, !isEnglish()); // bLocalFirst=sal_False for english
if (!aIntName.getLength())
pToken->OpCode = getOpCodeUnknown();
else
{
pToken->OpCode = ocExternal;
pToken->Data <<= aIntName;
}
}
}
return aTokens;
}
uno::Sequence< sheet::FormulaOpCodeMapEntry > FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings(const FormulaCompiler& _rCompiler,const sal_Int32 nGroups ) const
{
using namespace sheet;
// Unfortunately uno::Sequence can't grow without cumbersome reallocs. As
// we don't know in advance how many elements it will have we use a
// temporary vector to add elements and then copy to Sequence :-(
::std::vector< FormulaOpCodeMapEntry > aVec;
if (nGroups == FormulaMapGroup::SPECIAL)
{
// Use specific order, keep in sync with
// offapi/com/sun/star/sheet/FormulaMapGroupSpecialOffset.idl
static const struct
{
sal_Int32 nOff;
OpCode eOp;
} aMap[] = {
{ FormulaMapGroupSpecialOffset::PUSH , ocPush } ,
{ FormulaMapGroupSpecialOffset::CALL , ocCall } ,
{ FormulaMapGroupSpecialOffset::STOP , ocStop } ,
{ FormulaMapGroupSpecialOffset::EXTERNAL , ocExternal } ,
{ FormulaMapGroupSpecialOffset::NAME , ocName } ,
{ FormulaMapGroupSpecialOffset::NO_NAME , ocNoName } ,
{ FormulaMapGroupSpecialOffset::MISSING , ocMissing } ,
{ FormulaMapGroupSpecialOffset::BAD , ocBad } ,
{ FormulaMapGroupSpecialOffset::SPACES , ocSpaces } ,
{ FormulaMapGroupSpecialOffset::MAT_REF , ocMatRef } ,
{ FormulaMapGroupSpecialOffset::DB_AREA , ocDBArea } ,
{ FormulaMapGroupSpecialOffset::MACRO , ocMacro } ,
{ FormulaMapGroupSpecialOffset::COL_ROW_NAME , ocColRowName }
};
const size_t nCount = sizeof(aMap)/sizeof(aMap[0]);
// Preallocate vector elements.
if (aVec.size() < nCount)
{
FormulaOpCodeMapEntry aEntry;
aEntry.Token.OpCode = getOpCodeUnknown();
aVec.resize( nCount, aEntry);
} // if (aVec.size() < nCount)
FormulaOpCodeMapEntry aEntry;
for (size_t i=0; i < nCount; ++i)
{
size_t nIndex = static_cast< size_t >( aMap[i].nOff );
if (aVec.size() <= nIndex)
{
// The offsets really should be aligned with the size, so if
// the vector was preallocated above this code to resize it is
// just a measure in case the table isn't in sync with the API,
// usually it isn't executed.
aEntry.Token.OpCode = getOpCodeUnknown();
aVec.resize( nIndex + 1, aEntry );
}
aEntry.Token.OpCode = aMap[i].eOp;
aVec[nIndex] = aEntry;
}
}
else
{
/* FIXME: Once we support error constants in formulas we'll need a map
* group for that, e.g. FormulaMapGroup::ERROR_CONSTANTS, and fill
* SC_OPCODE_START_ERRORS to SC_OPCODE_STOP_ERRORS. */
// Anything else but SPECIAL.
if ((nGroups & FormulaMapGroup::SEPARATORS) != 0)
{
static const sal_uInt16 aOpCodes[] = {
SC_OPCODE_OPEN,
SC_OPCODE_CLOSE,
SC_OPCODE_SEP,
};
lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
}
if ((nGroups & FormulaMapGroup::ARRAY_SEPARATORS) != 0)
{
static const sal_uInt16 aOpCodes[] = {
SC_OPCODE_ARRAY_OPEN,
SC_OPCODE_ARRAY_CLOSE,
SC_OPCODE_ARRAY_ROW_SEP,
SC_OPCODE_ARRAY_COL_SEP
};
lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
}
if ((nGroups & FormulaMapGroup::UNARY_OPERATORS) != 0)
{
// Due to the nature of the percent operator following its operand
// it isn't sorted into unary operators for compiler interna.
lclPushOpCodeMapEntry( aVec, mpTable, ocPercentSign );
// "+" can be used as unary operator too, push only if binary group is not set
if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) == 0)
lclPushOpCodeMapEntry( aVec, mpTable, ocAdd );
// regular unary operators
for (sal_uInt16 nOp = SC_OPCODE_START_UN_OP; nOp < SC_OPCODE_STOP_UN_OP && nOp < mnSymbols; ++nOp)
{
switch (nOp)
{
// NOT and NEG in fact are functions but for legacy reasons
// are sorted into unary operators for compiler interna.
case SC_OPCODE_NOT :
case SC_OPCODE_NEG :
break; // nothing,
default:
lclPushOpCodeMapEntry( aVec, mpTable, nOp );
}
}
}
if ((nGroups & FormulaMapGroup::BINARY_OPERATORS) != 0)
{
for (sal_uInt16 nOp = SC_OPCODE_START_BIN_OP; nOp < SC_OPCODE_STOP_BIN_OP && nOp < mnSymbols; ++nOp)
{
switch (nOp)
{
// AND and OR in fact are functions but for legacy reasons
// are sorted into binary operators for compiler interna.
case SC_OPCODE_AND :
case SC_OPCODE_OR :
break; // nothing,
default:
lclPushOpCodeMapEntry( aVec, mpTable, nOp );
}
}
}
if ((nGroups & FormulaMapGroup::FUNCTIONS) != 0)
{
// Function names are not consecutive, skip the gaps between
// functions with no parameter, functions with 1 parameter
lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_NO_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_NO_PAR, mnSymbols ) );
lclPushOpCodeMapEntries( aVec, mpTable, SC_OPCODE_START_1_PAR, ::std::min< sal_uInt16 >( SC_OPCODE_STOP_1_PAR, mnSymbols ) );
// Additional functions not within range of functions.
static const sal_uInt16 aOpCodes[] = {
SC_OPCODE_IF,
SC_OPCODE_CHOSE,
SC_OPCODE_AND,
SC_OPCODE_OR,
SC_OPCODE_NOT,
SC_OPCODE_NEG
};
lclPushOpCodeMapEntries( aVec, mpTable, aOpCodes, sizeof(aOpCodes)/sizeof(aOpCodes[0]) );
// functions with 2 or more parameters.
for (sal_uInt16 nOp = SC_OPCODE_START_2_PAR; nOp < SC_OPCODE_STOP_2_PAR && nOp < mnSymbols; ++nOp)
{
switch (nOp)
{
// NO_NAME is in SPECIAL.
case SC_OPCODE_NO_NAME :
break; // nothing,
default:
lclPushOpCodeMapEntry( aVec, mpTable, nOp );
}
}
// If AddIn functions are present in this mapping, use them, and only those.
if (hasExternals())
{
for (ExternalHashMap::const_iterator it( mpExternalHashMap->begin());it != mpExternalHashMap->end(); ++it)
{
FormulaOpCodeMapEntry aEntry;
aEntry.Name = (*it).first;
aEntry.Token.Data <<= ::rtl::OUString( (*it).second);
aEntry.Token.OpCode = ocExternal;
aVec.push_back( aEntry);
}
}
else
{
//DBG_ASSERT( isCore(), "FormulaCompiler::OpCodeMap::createSequenceOfAvailableMappings: AddIn mapping from collection only implemented for core languages");
_rCompiler.fillAddInToken(aVec,isEnglish());
}
}
}
const FormulaOpCodeMapEntry* pRet = aVec.empty() ? 0 : &aVec[0];
return uno::Sequence< FormulaOpCodeMapEntry >(pRet, aVec.size());
}
//-----------------------------------------------------------------------------
void FormulaCompiler::OpCodeMap::putOpCode( const String & rStr, const OpCode eOp )
{
DBG_ASSERT( 0 < eOp && sal_uInt16(eOp) < mnSymbols, "OpCodeMap::putOpCode: OpCode out of range");
if (0 < eOp && sal_uInt16(eOp) < mnSymbols)
{
DBG_ASSERT( (mpTable[eOp].Len() == 0) || (mpTable[eOp] == rStr) || (eOp == ocCurrency),
ByteString( "OpCodeMap::putOpCode: reusing OpCode ").
Append( ByteString::CreateFromInt32( sal_Int32( eOp))).Append( " (").
Append( ByteString( rStr, RTL_TEXTENCODING_ASCII_US)).Append( ')').GetBuffer());
mpTable[eOp] = rStr;
mpHashMap->insert( OpCodeHashMap::value_type( rStr, eOp));
}
}
// -----------------------------------------------------------------------------
// class FormulaCompiler
// -----------------------------------------------------------------------------
DBG_NAME(FormulaCompiler)
FormulaCompiler::FormulaCompiler(FormulaTokenArray& _rArr)
:
pArr( &_rArr ),
pExternalRef(NULL),
pStack( NULL ),
nRecursion(0),
nNumFmt( NUMBERFORMAT_UNDEFINED ),
meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
bAutoCorrect( sal_False ),
bCorrected( sal_False ),
bCompileForFAP( sal_False ),
bIgnoreErrors( sal_False )
{
DBG_CTOR(FormulaCompiler,NULL);
}
FormulaCompiler::FormulaCompiler()
:
pArr( NULL ),
pExternalRef(NULL),
pStack( NULL ),
nRecursion(0),
nNumFmt( NUMBERFORMAT_UNDEFINED ),
meGrammar( formula::FormulaGrammar::GRAM_UNSPECIFIED ),
bAutoCorrect( sal_False ),
bCorrected( sal_False ),
bCompileForFAP( sal_False ),
bIgnoreErrors( sal_False )
{
DBG_CTOR(FormulaCompiler,NULL);
}
FormulaCompiler::~FormulaCompiler()
{
DBG_DTOR(FormulaCompiler,NULL);
}
FormulaCompiler::OpCodeMapPtr FormulaCompiler::GetOpCodeMap( const sal_Int32 nLanguage ) const
{
FormulaCompiler::OpCodeMapPtr xMap;
using namespace sheet;
switch (nLanguage)
{
case FormulaLanguage::ODFF :
if (!mxSymbolsODFF)
InitSymbolsODFF();
xMap = mxSymbolsODFF;
break;
case FormulaLanguage::ODF_11 :
if (!mxSymbolsPODF)
InitSymbolsPODF();
xMap = mxSymbolsPODF;
break;
case FormulaLanguage::ENGLISH :
if (!mxSymbolsEnglish)
InitSymbolsEnglish();
xMap = mxSymbolsEnglish;
break;
case FormulaLanguage::NATIVE :
if (!mxSymbolsNative)
InitSymbolsNative();
xMap = mxSymbolsNative;
break;
default:
; // nothing, NULL map returned
}
return xMap;
}
// -----------------------------------------------------------------------------
String FormulaCompiler::FindAddInFunction( const String& /*rUpperName*/, sal_Bool /*bLocalFirst*/ ) const
{
return String();
}
// -----------------------------------------------------------------------------
FormulaCompiler::OpCodeMapPtr FormulaCompiler::CreateOpCodeMap(
const uno::Sequence<
const sheet::FormulaOpCodeMapEntry > & rMapping,
bool bEnglish )
{
using sheet::FormulaOpCodeMapEntry;
// Filter / API maps are never Core
NonConstOpCodeMapPtr xMap( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1,false, FormulaGrammar::mergeToGrammar( FormulaGrammar::setEnglishBit(FormulaGrammar::GRAM_EXTERNAL, bEnglish),FormulaGrammar::CONV_UNSPECIFIED)));
FormulaOpCodeMapEntry const * pArr2 = rMapping.getConstArray();
FormulaOpCodeMapEntry const * const pStop = pArr2 + rMapping.getLength();
for ( ; pArr2 < pStop; ++pArr2)
{
OpCode eOp = OpCode(pArr2->Token.OpCode);
if (eOp != ocExternal)
xMap->putOpCode( pArr2->Name, eOp);
else
{
::rtl::OUString aExternalName;
if (pArr2->Token.Data >>= aExternalName)
xMap->putExternal( pArr2->Name, aExternalName);
else
{
DBG_ERRORFILE( "FormulaCompiler::CreateOpCodeMap: no Token.Data external name");
}
}
}
return xMap;
}
// -----------------------------------------------------------------------------
void lcl_fillNativeSymbols(FormulaCompiler::NonConstOpCodeMapPtr& _xMap,bool _destroy = false)
{
static FormulaCompiler::NonConstOpCodeMapPtr s_SymbolMap;
if ( _destroy )
{
s_SymbolMap.reset();
} // if ( _destroy )
else if ( !s_SymbolMap.get() )
{
// Core
s_SymbolMap.reset( new FormulaCompiler::OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, true, FormulaGrammar::GRAM_NATIVE_UI));
OModuleClient aModuleClient;
OpCodeList aOpCodeListNative( RID_STRLIST_FUNCTION_NAMES, s_SymbolMap );
// No AddInMap for native core mapping.
} // if ( !s_SymbolMap.get() )
_xMap = s_SymbolMap;
}
// -----------------------------------------------------------------------------
const String& FormulaCompiler::GetNativeSymbol( OpCode eOp )
{
NonConstOpCodeMapPtr xSymbolsNative;
lcl_fillNativeSymbols(xSymbolsNative);
return xSymbolsNative->getSymbol( eOp );
}
// -----------------------------------------------------------------------------
void FormulaCompiler::InitSymbolsNative() const
{
if (mxSymbolsNative.get())
return;
//! Experimental!
// Use English function names and separators instead of native in UI.
static const sal_Char aEnvVarName[] = "OOO_CALC_USE_ENGLISH_FORMULAS";
const char* pEnv = getenv( aEnvVarName);
if (pEnv && (*pEnv == 'Y' || *pEnv == 'y' || *pEnv == '1') )
{
fprintf( stderr, "%s=%s => UI uses English function names and separators in formulas.\n",
aEnvVarName, pEnv);
InitSymbolsEnglish();
mxSymbolsNative = mxSymbolsEnglish;
return;
}
static NonConstOpCodeMapPtr s_sSymbol;
if ( !s_sSymbol.get() )
lcl_fillNativeSymbols(s_sSymbol);
mxSymbolsNative = s_sSymbol;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::InitSymbolsEnglish() const
{
static NonConstOpCodeMapPtr s_sSymbol;
if ( !s_sSymbol.get() )
loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_ENGLISH,s_sSymbol);
mxSymbolsEnglish = s_sSymbol;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::InitSymbolsPODF() const
{
static NonConstOpCodeMapPtr s_sSymbol;
if ( !s_sSymbol.get() )
loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH,FormulaGrammar::GRAM_PODF,s_sSymbol);
mxSymbolsPODF = s_sSymbol;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::InitSymbolsODFF() const
{
static NonConstOpCodeMapPtr s_sSymbol;
if ( !s_sSymbol.get() )
loadSymbols(RID_STRLIST_FUNCTION_NAMES_ENGLISH_ODFF,FormulaGrammar::GRAM_ODFF,s_sSymbol);
mxSymbolsODFF = s_sSymbol;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::loadSymbols(sal_uInt16 _nSymbols,FormulaGrammar::Grammar _eGrammar,NonConstOpCodeMapPtr& _xMap) const
{
if ( !_xMap.get() )
{
// not Core
_xMap.reset( new OpCodeMap( SC_OPCODE_LAST_OPCODE_ID + 1, _eGrammar != FormulaGrammar::GRAM_ODFF, _eGrammar ));
OModuleClient aModuleClient;
OpCodeList aOpCodeList( _nSymbols, _xMap );
fillFromAddInMap( _xMap, _eGrammar);
// Fill from collection for AddIns not already present.
if ( FormulaGrammar::GRAM_ENGLISH != _eGrammar )
fillFromAddInCollectionUpperName( _xMap);
else
fillFromAddInCollectionEnglishName( _xMap);
}
}
// -----------------------------------------------------------------------------
void FormulaCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr /*xMap */) const
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr /*xMap */) const
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::fillFromAddInMap( NonConstOpCodeMapPtr /*xMap*/, FormulaGrammar::Grammar /*_eGrammar */) const
{
}
// -----------------------------------------------------------------------------
OpCode FormulaCompiler::GetEnglishOpCode( const String& rName ) const
{
FormulaCompiler::OpCodeMapPtr xMap = GetOpCodeMap(sheet::FormulaLanguage::ENGLISH);
formula::OpCodeHashMap::const_iterator iLook( xMap->getHashMap()->find( rName ) );
bool bFound = (iLook != xMap->getHashMap()->end());
return bFound ? (*iLook).second : OpCode(ocNone);
}
// Remove quotes, escaped quotes are unescaped.
sal_Bool FormulaCompiler::DeQuote( String& rStr )
{
xub_StrLen nLen = rStr.Len();
if ( nLen > 1 && rStr.GetChar(0) == '\'' && rStr.GetChar( nLen-1 ) == '\'' )
{
rStr.Erase( nLen-1, 1 );
rStr.Erase( 0, 1 );
xub_StrLen nPos = 0;
while ( (nPos = rStr.SearchAscii( "\\\'", nPos)) != STRING_NOTFOUND )
{
rStr.Erase( nPos, 1 );
++nPos;
}
return sal_True;
}
return sal_False;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::fillAddInToken(::std::vector< sheet::FormulaOpCodeMapEntry >& /*_rVec*/,bool /*_bIsEnglish*/) const
{
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::IsMatrixFunction(OpCode _eOpCode)
{
switch ( _eOpCode )
{
case ocDde :
case ocGrowth :
case ocTrend :
case ocRKP :
case ocRGP :
case ocFrequency :
case ocMatTrans :
case ocMatMult :
case ocMatInv :
case ocMatrixUnit :
return sal_True;
default:
{
// added to avoid warnings
}
}
return sal_False;
}
// -----------------------------------------------------------------------------
FormulaCompiler::OpCodeMap::~OpCodeMap()
{
delete mpReverseExternalHashMap;
delete mpExternalHashMap;
delete [] mpTable;
delete mpHashMap;
}
// -----------------------------------------------------------------------------
sal_Int32 FormulaCompiler::OpCodeMap::getOpCodeUnknown()
{
static const sal_Int32 kOpCodeUnknown = -1;
return kOpCodeUnknown;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::GetToken()
{
static const short nRecursionMax = 42;
FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
if ( nRecursion > nRecursionMax )
{
SetError( errStackOverflow );
pToken = new FormulaByteToken( ocStop );
return sal_False;
}
if ( bAutoCorrect && !pStack )
{ // #61426# don't merge stacked subroutine code into entered formula
aCorrectedFormula += aCorrectedSymbol;
aCorrectedSymbol.Erase();
}
sal_Bool bStop = sal_False;
if( pArr->GetCodeError() && !bIgnoreErrors )
bStop = sal_True;
else
{
short nWasColRowName;
if ( pArr->nIndex
&& pArr->pCode[ pArr->nIndex-1 ]->GetOpCode() == ocColRowName )
nWasColRowName = 1;
else
nWasColRowName = 0;
pToken = pArr->Next();
while( pToken && pToken->GetOpCode() == ocSpaces )
{
if ( nWasColRowName )
nWasColRowName++;
if ( bAutoCorrect && !pStack )
CreateStringFromToken( aCorrectedFormula, pToken, sal_False );
pToken = pArr->Next();
}
if ( bAutoCorrect && !pStack && pToken )
CreateStringFromToken( aCorrectedSymbol, pToken, sal_False );
if( !pToken )
{
if( pStack )
{
PopTokenArray();
return GetToken();
}
else
bStop = sal_True;
}
else
{
if ( nWasColRowName >= 2 && pToken->GetOpCode() == ocColRowName )
{ // convert an ocSpaces to ocIntersect in RPN
pToken = new FormulaByteToken( ocIntersect );
pArr->nIndex--; // we advanced to the second ocColRowName, step back
}
}
}
if( bStop )
{
pToken = new FormulaByteToken( ocStop );
return sal_False;
}
if( pToken->GetOpCode() == ocSubTotal )
glSubTotal = sal_True;
else if ( pToken->GetOpCode() == ocExternalRef )
{
return HandleExternalReference(*pToken);
}
else if( pToken->GetOpCode() == ocName )
{
return HandleRange();
}
else if( pToken->GetOpCode() == ocColRowName )
{
return HandleSingleRef();
}
else if( pToken->GetOpCode() == ocDBArea )
{
return HandleDbData();
}
else if( pToken->GetType() == svSingleRef )
{
pArr->nRefs++;
}
else if( pToken->GetType() == svDoubleRef )
{
pArr->nRefs++;
}
return sal_True;
}
//---------------------------------------------------------------------------
// RPN creation by recursion
//---------------------------------------------------------------------------
void FormulaCompiler::Factor()
{
if ( pArr->GetCodeError() && !bIgnoreErrors )
return;
CurrentFactor pFacToken( this );
OpCode eOp = pToken->GetOpCode();
if( eOp == ocPush || eOp == ocColRowNameAuto || eOp == ocMatRef ||
eOp == ocDBArea
|| (bCompileForFAP && ((eOp == ocName) || (eOp == ocDBArea)
|| (eOp == ocColRowName) || (eOp == ocBad)))
)
{
PutCode( pToken );
eOp = NextToken();
if( eOp == ocOpen )
{
// PUSH( is an error that may be caused by an unknown function.
SetError(
( pToken->GetType() == svString
|| pToken->GetType() == svSingleRef )
? errNoName : errOperatorExpected );
if ( bAutoCorrect && !pStack )
{ // assume multiplication
aCorrectedFormula += mxSymbols->getSymbol(ocMul);
bCorrected = sal_True;
NextToken();
eOp = Expression();
if( eOp != ocClose )
SetError(errPairExpected);
else
eOp = NextToken();
}
}
}
else if( eOp == ocOpen )
{
NextToken();
eOp = Expression();
while ((eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors))
{ // range list (A1;A2) converted to (A1~A2)
pFacToken = pToken;
NextToken();
eOp = Expression();
// Do not ignore error here, regardless of bIgnoreErrors, otherwise
// errors like =(1;) would also result in display of =(1~)
if (!pArr->GetCodeError())
{
pFacToken->NewOpCode( ocUnion,FormulaToken::PrivateAccess());
PutCode( pFacToken);
}
}
if (eOp != ocClose)
SetError(errPairExpected);
else
eOp = NextToken();
}
else
{
if( nNumFmt == NUMBERFORMAT_UNDEFINED )
nNumFmt = lcl_GetRetFormat( eOp );
// Functions that have to be always recalculated
switch( eOp )
{
// no parameters:
case ocRandom:
case ocGetActDate:
case ocGetActTime:
// one parameter:
case ocFormula:
case ocInfo:
// more than one parameters:
// ocIndirect/ocIndirectXL otherwise would have to do
// StopListening and StartListening on a reference for every
// interpreted value.
case ocIndirect:
case ocIndirectXL:
// ocOffset results in indirect references.
case ocOffset:
pArr->SetRecalcModeAlways();
break;
// Functions recalculated on every document load.
// Don't use SetRecalcModeOnLoad() which would override
// ModeAlways.
case ocConvert :
pArr->AddRecalcMode( RECALCMODE_ONLOAD );
break;
// If the referred cell is moved the value changes.
case ocColumn :
case ocRow :
// ocCell needs recalc on move for some possible type values.
case ocCell :
pArr->SetRecalcModeOnRefMove();
break;
case ocHyperLink :
pArr->SetHyperLink(sal_True);
break;
default:
; // nothing
}
if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR)
{
pFacToken = pToken;
eOp = NextToken();
if (eOp != ocOpen)
{
SetError(errPairExpected);
PutCode( pFacToken );
}
else
{
eOp = NextToken();
if (eOp != ocClose)
SetError(errPairExpected);
PutCode(pFacToken);
eOp = NextToken();
}
}
// special cases NOT() and NEG()
else if( eOp == ocNot || eOp == ocNeg
|| (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) )
{
pFacToken = pToken;
eOp = NextToken();
if( nNumFmt == NUMBERFORMAT_UNDEFINED && eOp == ocNot )
nNumFmt = NUMBERFORMAT_LOGICAL;
if (eOp == ocOpen)
{
NextToken();
eOp = Expression();
}
else
SetError(errPairExpected);
if (eOp != ocClose)
SetError(errPairExpected);
else if ( !pArr->GetCodeError() )
pFacToken->SetByte( 1 );
PutCode( pFacToken );
eOp = NextToken();
}
else if ((SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR)
|| eOp == ocExternal
|| eOp == ocMacro
|| eOp == ocAnd
|| eOp == ocOr
|| eOp == ocBad
|| ( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
|| (bCompileForFAP && ((eOp == ocIf) || (eOp == ocChose)))
)
{
pFacToken = pToken;
OpCode eMyLastOp = eOp;
eOp = NextToken();
bool bNoParam = false;
bool bBadName = false;
if (eOp == ocOpen)
{
eOp = NextToken();
if (eOp == ocClose)
bNoParam = true;
else
eOp = Expression();
}
else if (eMyLastOp == ocBad)
{
// Just a bad name, not an unknown function, no parameters, no
// closing expected.
bBadName = true;
bNoParam = true;
}
else
SetError(errPairExpected);
sal_uInt8 nSepCount = 0;
if( !bNoParam )
{
nSepCount++;
while ( (eOp == ocSep) && (!pArr->GetCodeError() || bIgnoreErrors) )
{
nSepCount++;
NextToken();
eOp = Expression();
}
}
if (bBadName)
; // nothing, keep current token for return
else if (eOp != ocClose)
SetError(errPairExpected);
else
eOp = NextToken();
// Jumps are just normal functions for the FunctionAutoPilot tree view
if ( bCompileForFAP && pFacToken->GetType() == svJump )
pFacToken = new FormulaFAPToken( pFacToken->GetOpCode(), nSepCount, pFacToken );
else
pFacToken->SetByte( nSepCount );
PutCode( pFacToken );
}
else if (eOp == ocIf || eOp == ocChose)
{
// the PC counters are -1
pFacToken = pToken;
if ( eOp == ocIf )
pFacToken->GetJump()[ 0 ] = 3; // if, else, behind
else
pFacToken->GetJump()[ 0 ] = MAXJUMPCOUNT+1;
eOp = NextToken();
if (eOp == ocOpen)
{
NextToken();
eOp = Expression();
}
else
SetError(errPairExpected);
short nJumpCount = 0;
PutCode( pFacToken );
// #36253# during AutoCorrect (since pArr->GetCodeError() is
// ignored) an unlimited ocIf would crash because
// ScRawToken::Clone() allocates the JumpBuffer according to
// nJump[0]*2+2, which is 3*2+2 on ocIf.
const short nJumpMax =
(pFacToken->GetOpCode() == ocIf ? 3 : MAXJUMPCOUNT);
while ( (nJumpCount < (MAXJUMPCOUNT - 1)) && (eOp == ocSep)
&& (!pArr->GetCodeError() || bIgnoreErrors) )
{
if ( ++nJumpCount <= nJumpMax )
pFacToken->GetJump()[nJumpCount] = pc-1;
NextToken();
eOp = Expression();
// ocSep or ocClose terminate the subexpression
PutCode( pToken );
}
if (eOp != ocClose)
SetError(errPairExpected);
else
{
eOp = NextToken();
// always limit to nJumpMax, no arbitrary overwrites
if ( ++nJumpCount <= nJumpMax )
pFacToken->GetJump()[ nJumpCount ] = pc-1;
if ((pFacToken->GetOpCode() == ocIf && (nJumpCount > 3)) ||
(nJumpCount >= MAXJUMPCOUNT))
SetError(errIllegalParameter);
else
pFacToken->GetJump()[ 0 ] = nJumpCount;
}
}
else if ( eOp == ocMissing )
{
PutCode( pToken );
eOp = NextToken();
}
else if ( eOp == ocClose )
{
SetError( errParameterExpected );
}
else if ( eOp == ocSep )
{ // Subsequent ocSep
SetError( errParameterExpected );
if ( bAutoCorrect && !pStack )
{
aCorrectedSymbol.Erase();
bCorrected = sal_True;
}
}
else if ( eOp == ocExternalRef )
{
PutCode(pToken);
eOp = NextToken();
}
else
{
SetError( errUnknownToken );
if ( bAutoCorrect && !pStack )
{
if ( eOp == ocStop )
{ // trailing operator w/o operand
xub_StrLen nLen = aCorrectedFormula.Len();
if ( nLen )
aCorrectedFormula.Erase( nLen - 1 );
aCorrectedSymbol.Erase();
bCorrected = sal_True;
}
}
}
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::RangeLine()
{
Factor();
while (pToken->GetOpCode() == ocRange)
{
FormulaToken** pCode1 = pCode - 1;
FormulaTokenRef p = pToken;
NextToken();
Factor();
FormulaToken** pCode2 = pCode - 1;
if (!MergeRangeReference( pCode1, pCode2))
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::IntersectionLine()
{
RangeLine();
while (pToken->GetOpCode() == ocIntersect)
{
FormulaTokenRef p = pToken;
NextToken();
RangeLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::UnionLine()
{
IntersectionLine();
while (pToken->GetOpCode() == ocUnion)
{
FormulaTokenRef p = pToken;
NextToken();
IntersectionLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::UnaryLine()
{
if( pToken->GetOpCode() == ocAdd )
GetToken();
else if (SC_OPCODE_START_UN_OP <= pToken->GetOpCode() &&
pToken->GetOpCode() < SC_OPCODE_STOP_UN_OP)
{
FormulaTokenRef p = pToken;
NextToken();
UnaryLine();
PutCode( p );
}
else
UnionLine();
}
//---------------------------------------------------------------------------
void FormulaCompiler::PostOpLine()
{
UnaryLine();
while ( pToken->GetOpCode() == ocPercentSign )
{ // this operator _follows_ its operand
PutCode( pToken );
NextToken();
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::PowLine()
{
PostOpLine();
while (pToken->GetOpCode() == ocPow)
{
FormulaTokenRef p = pToken;
NextToken();
PostOpLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::MulDivLine()
{
PowLine();
while (pToken->GetOpCode() == ocMul || pToken->GetOpCode() == ocDiv)
{
FormulaTokenRef p = pToken;
NextToken();
PowLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::AddSubLine()
{
MulDivLine();
while (pToken->GetOpCode() == ocAdd || pToken->GetOpCode() == ocSub)
{
FormulaTokenRef p = pToken;
NextToken();
MulDivLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::ConcatLine()
{
AddSubLine();
while (pToken->GetOpCode() == ocAmpersand)
{
FormulaTokenRef p = pToken;
NextToken();
AddSubLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::CompareLine()
{
ConcatLine();
while (pToken->GetOpCode() >= ocEqual && pToken->GetOpCode() <= ocGreaterEqual)
{
FormulaTokenRef p = pToken;
NextToken();
ConcatLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
void FormulaCompiler::NotLine()
{
CompareLine();
while (pToken->GetOpCode() == ocNot)
{
FormulaTokenRef p = pToken;
NextToken();
CompareLine();
PutCode(p);
}
}
//---------------------------------------------------------------------------
OpCode FormulaCompiler::Expression()
{
static const short nRecursionMax = 42;
FormulaCompilerRecursionGuard aRecursionGuard( nRecursion );
if ( nRecursion > nRecursionMax )
{
SetError( errStackOverflow );
return ocStop; //! generate token instead?
}
NotLine();
while (pToken->GetOpCode() == ocAnd || pToken->GetOpCode() == ocOr)
{
FormulaTokenRef p = pToken;
pToken->SetByte( 2 ); // 2 parameters!
NextToken();
NotLine();
PutCode(p);
}
return pToken->GetOpCode();
}
// -----------------------------------------------------------------------------
void FormulaCompiler::SetError(sal_uInt16 /*nError*/)
{
}
// -----------------------------------------------------------------------------
FormulaTokenRef FormulaCompiler::ExtendRangeReference( FormulaToken & /*rTok1*/, FormulaToken & /*rTok2*/, bool /*bReuseDoubleRef*/ )
{
return FormulaTokenRef();
}
// -----------------------------------------------------------------------------
bool FormulaCompiler::MergeRangeReference(FormulaToken * * const pCode1, FormulaToken * const * const pCode2 )
{
FormulaToken *p1, *p2;
if (pc < 2 || !pCode1 || !pCode2 ||
(pCode2 - pCode1 != 1) || (pCode - pCode2 != 1) ||
((p1 = *pCode1) == 0) || ((p2 = *pCode2) == 0) )
return false;
FormulaTokenRef p = ExtendRangeReference( *p1, *p2, true);
if (!p)
return false;
p->IncRef();
p1->DecRef();
p2->DecRef();
*pCode1 = p;
--pCode, --pc;
pArr->nRefs--;
return true;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::CompileTokenArray()
{
glSubTotal = sal_False;
bCorrected = sal_False;
if( !pArr->GetCodeError() || bIgnoreErrors )
{
if ( bAutoCorrect )
{
aCorrectedFormula.Erase();
aCorrectedSymbol.Erase();
}
pArr->nRefs = 0; // count from start
pArr->DelRPN();
pStack = NULL;
FormulaToken* pData[ MAXCODE ];
pCode = pData;
sal_Bool bWasForced = pArr->IsRecalcModeForced();
if ( bWasForced )
{
if ( bAutoCorrect )
aCorrectedFormula = '=';
}
pArr->ClearRecalcMode();
pArr->Reset();
eLastOp = ocOpen;
pc = 0;
NextToken();
OpCode eOp = Expression();
// Some trailing garbage that doesn't form an expression?
if (eOp != ocStop)
SetError( errOperatorExpected);
sal_uInt16 nErrorBeforePop = pArr->GetCodeError();
while( pStack )
PopTokenArray();
if( pc )
{
pArr->pRPN = new FormulaToken*[ pc ];
pArr->nRPN = pc;
memcpy( pArr->pRPN, pData, pc * sizeof( FormulaToken* ) );
}
// once an error, always an error
if( !pArr->GetCodeError() && nErrorBeforePop )
pArr->SetCodeError( nErrorBeforePop);
if( pArr->GetCodeError() && !bIgnoreErrors )
{
pArr->DelRPN();
pArr->SetHyperLink(sal_False);
}
if ( bWasForced )
pArr->SetRecalcModeForced();
}
if( nNumFmt == NUMBERFORMAT_UNDEFINED )
nNumFmt = NUMBERFORMAT_NUMBER;
return glSubTotal;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::PopTokenArray()
{
if( pStack )
{
FormulaArrayStack* p = pStack;
pStack = p->pNext;
p->pArr->nRefs = sal::static_int_cast<short>( p->pArr->nRefs + pArr->nRefs );
// obtain special RecalcMode from SharedFormula
if ( pArr->IsRecalcModeAlways() )
p->pArr->SetRecalcModeAlways();
else if ( !pArr->IsRecalcModeNormal() && p->pArr->IsRecalcModeNormal() )
p->pArr->SetMaskedRecalcMode( pArr->GetRecalcMode() );
p->pArr->SetCombinedBitsRecalcMode( pArr->GetRecalcMode() );
if( p->bTemp )
delete pArr;
pArr = p->pArr;
delete p;
}
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromTokenArray( String& rFormula )
{
rtl::OUStringBuffer aBuffer( pArr->GetLen() * 5 );
CreateStringFromTokenArray( aBuffer );
rFormula = aBuffer;
}
void FormulaCompiler::CreateStringFromTokenArray( rtl::OUStringBuffer& rBuffer )
{
rBuffer.setLength(0);
if( !pArr->GetLen() )
return;
FormulaTokenArray* pSaveArr = pArr;
bool bODFF = FormulaGrammar::isODFF( meGrammar);
if (bODFF || FormulaGrammar::isPODF( meGrammar) )
{
// Scan token array for missing args and re-write if present.
MissingConvention aConv( bODFF);
if (pArr->NeedsPofRewrite( aConv))
pArr = pArr->RewriteMissingToPof( aConv);
}
// At least one character per token, plus some are references, some are
// function names, some are numbers, ...
rBuffer.ensureCapacity( pArr->GetLen() * 5 );
if ( pArr->IsRecalcModeForced() )
rBuffer.append(sal_Unicode('='));
FormulaToken* t = pArr->First();
while( t )
t = CreateStringFromToken( rBuffer, t, sal_True );
if (pSaveArr != pArr)
{
delete pArr;
pArr = pSaveArr;
}
}
// -----------------------------------------------------------------------------
FormulaToken* FormulaCompiler::CreateStringFromToken( String& rFormula, FormulaToken* pTokenP,sal_Bool bAllowArrAdvance )
{
rtl::OUStringBuffer aBuffer;
FormulaToken* p = CreateStringFromToken( aBuffer, pTokenP, bAllowArrAdvance );
rFormula += aBuffer;
return p;
}
FormulaToken* FormulaCompiler::CreateStringFromToken( rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP,sal_Bool bAllowArrAdvance )
{
sal_Bool bNext = sal_True;
sal_Bool bSpaces = sal_False;
FormulaToken* t = pTokenP;
OpCode eOp = t->GetOpCode();
if( eOp >= ocAnd && eOp <= ocOr )
{
// AND, OR infix?
if ( bAllowArrAdvance )
t = pArr->Next();
else
t = pArr->PeekNext();
bNext = sal_False;
bSpaces = ( !t || t->GetOpCode() != ocOpen );
}
if( bSpaces )
rBuffer.append(sal_Unicode(' '));
if( eOp == ocSpaces )
{
bool bIntersectionOp = mxSymbols->isODFF();
if (bIntersectionOp)
{
const FormulaToken* p = pArr->PeekPrevNoSpaces();
bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
if (bIntersectionOp)
{
p = pArr->PeekNextNoSpaces();
bIntersectionOp = (p && p->GetOpCode() == ocColRowName);
}
}
if (bIntersectionOp)
rBuffer.appendAscii( "!!");
else
{
// most times it's just one blank
sal_uInt8 n = t->GetByte();
for ( sal_uInt8 j=0; j<n; ++j )
{
rBuffer.append(sal_Unicode(' '));
}
}
}
else if( eOp >= ocInternalBegin && eOp <= ocInternalEnd )
rBuffer.appendAscii( pInternal[ eOp - ocInternalBegin ] );
else if( (sal_uInt16) eOp < mxSymbols->getSymbolCount()) // Keyword:
rBuffer.append(mxSymbols->getSymbol(eOp));
else
{
DBG_ERRORFILE("unknown OpCode");
rBuffer.append(GetNativeSymbol( ocErrName ));
}
if( bNext )
{
if (eOp == ocExternalRef)
{
CreateStringFromExternal(rBuffer, pTokenP);
}
else
{
switch( t->GetType() )
{
case svDouble:
AppendDouble( rBuffer, t->GetDouble() );
break;
case svString:
if( eOp == ocBad )
rBuffer.append(t->GetString());
else
AppendString( rBuffer, t->GetString() );
break;
case svSingleRef:
CreateStringFromSingleRef(rBuffer,t);
break;
case svDoubleRef:
CreateStringFromDoubleRef(rBuffer,t);
break;
case svMatrix:
CreateStringFromMatrix( rBuffer, t );
break;
case svIndex:
CreateStringFromIndex( rBuffer, t );
break;
case svExternal:
{
// mapped or translated name of AddIns
String aAddIn( t->GetExternal() );
bool bMapped = mxSymbols->isPODF(); // ODF 1.1 directly uses programmatical name
if (!bMapped && mxSymbols->hasExternals())
{
ExternalHashMap::const_iterator iLook = mxSymbols->getReverseExternalHashMap()->find( aAddIn);
if (iLook != mxSymbols->getReverseExternalHashMap()->end())
{
aAddIn = (*iLook).second;
bMapped = true;
}
}
if (!bMapped && !mxSymbols->isEnglish())
LocalizeString( aAddIn );
rBuffer.append(aAddIn);
}
break;
case svByte:
case svJump:
case svFAP:
case svMissing:
case svSep:
break; // Opcodes
default:
DBG_ERROR("FormulaCompiler:: GetStringFromToken errUnknownVariable");
} // of switch
}
}
if( bSpaces )
rBuffer.append(sal_Unicode(' '));
if ( bAllowArrAdvance )
{
if( bNext )
t = pArr->Next();
return t;
}
return pTokenP;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::AppendDouble( rtl::OUStringBuffer& rBuffer, double fVal )
{
if ( mxSymbols->isEnglish() )
{
::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max, '.', sal_True );
}
else
{
SvtSysLocale aSysLocale;
::rtl::math::doubleToUStringBuffer( rBuffer, fVal,
rtl_math_StringFormat_Automatic,
rtl_math_DecimalPlaces_Max,
aSysLocale.GetLocaleDataPtr()->getNumDecimalSep().GetChar(0),
sal_True );
}
}
// -----------------------------------------------------------------------------
void FormulaCompiler::AppendBoolean( rtl::OUStringBuffer& rBuffer, bool bVal )
{
rBuffer.append( mxSymbols->getSymbol(static_cast<OpCode>(bVal ? ocTrue : ocFalse)) );
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::IsImportingXML() const
{
return sal_False;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::AppendString( rtl::OUStringBuffer& rBuffer, const String & rStr )
{
if (IsImportingXML())
rBuffer.append( rStr );
else
{
rBuffer.append(sal_Unicode('"'));
if ( lcl_UnicodeStrChr( rStr.GetBuffer(), '"' ) == NULL )
rBuffer.append( rStr );
else
{
String aStr( rStr );
aStr.SearchAndReplaceAll( '"', String( RTL_CONSTASCII_USTRINGPARAM( "\"\"")));
rBuffer.append(aStr);
}
rBuffer.append(sal_Unicode('"'));
}
}
// -----------------------------------------------------------------------------
OpCode FormulaCompiler::NextToken()
{
if( !GetToken() )
return ocStop;
OpCode eOp = pToken->GetOpCode();
// There must be an operator before a push
if ( (eOp == ocPush || eOp == ocColRowNameAuto) &&
!( (eLastOp == ocOpen) || (eLastOp == ocSep) ||
(SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)) )
SetError(errOperatorExpected);
// Operator and Plus => operator
if (eOp == ocAdd && (eLastOp == ocOpen || eLastOp == ocSep ||
(SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
eOp = NextToken();
else
{
// Before an operator there must not be another operator, with the
// exception of AND and OR.
if ( eOp != ocAnd && eOp != ocOr &&
(SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP )
&& (eLastOp == ocOpen || eLastOp == ocSep ||
(SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_UN_OP)))
{
SetError(errVariableExpected);
if ( bAutoCorrect && !pStack )
{
if ( eOp == eLastOp || eLastOp == ocOpen )
{ // throw away duplicated operator
aCorrectedSymbol.Erase();
bCorrected = sal_True;
}
else
{
xub_StrLen nPos = aCorrectedFormula.Len();
if ( nPos )
{
nPos--;
sal_Unicode c = aCorrectedFormula.GetChar( nPos );
switch ( eOp )
{ // swap operators
case ocGreater:
if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) )
{ // >= instead of =>
aCorrectedFormula.SetChar( nPos,
mxSymbols->getSymbol(ocGreater).GetChar(0) );
aCorrectedSymbol = c;
bCorrected = sal_True;
}
break;
case ocLess:
if ( c == mxSymbols->getSymbol(ocEqual).GetChar(0) )
{ // <= instead of =<
aCorrectedFormula.SetChar( nPos,
mxSymbols->getSymbol(ocLess).GetChar(0) );
aCorrectedSymbol = c;
bCorrected = sal_True;
}
else if ( c == mxSymbols->getSymbol(ocGreater).GetChar(0) )
{ // <> instead of ><
aCorrectedFormula.SetChar( nPos,
mxSymbols->getSymbol(ocLess).GetChar(0) );
aCorrectedSymbol = c;
bCorrected = sal_True;
}
break;
case ocMul:
if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) )
{ // *- instead of -*
aCorrectedFormula.SetChar( nPos,
mxSymbols->getSymbol(ocMul).GetChar(0) );
aCorrectedSymbol = c;
bCorrected = sal_True;
}
break;
case ocDiv:
if ( c == mxSymbols->getSymbol(ocSub).GetChar(0) )
{ // /- instead of -/
aCorrectedFormula.SetChar( nPos,
mxSymbols->getSymbol(ocDiv).GetChar(0) );
aCorrectedSymbol = c;
bCorrected = sal_True;
}
break;
default:
; // nothing
}
}
}
}
}
eLastOp = eOp;
}
return eOp;
}
void FormulaCompiler::PutCode( FormulaTokenRef& p )
{
if( pc >= MAXCODE-1 )
{
if ( pc == MAXCODE-1 )
{
p = new FormulaByteToken( ocStop );
p->IncRef();
*pCode++ = p;
++pc;
}
SetError(errCodeOverflow);
return;
}
if( pArr->GetCodeError() && !bCompileForFAP )
return;
ForceArrayOperator( p, pCurrentFactorToken);
p->IncRef();
*pCode++ = p;
pc++;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::HandleExternalReference(const FormulaToken& /*_aToken*/)
{
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::HandleRange()
{
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::HandleSingleRef()
{
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool FormulaCompiler::HandleDbData()
{
return sal_True;
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromIndex(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromMatrix(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::CreateStringFromExternal(rtl::OUStringBuffer& /*rBuffer*/,FormulaToken* /*pTokenP*/)
{
}
// -----------------------------------------------------------------------------
void FormulaCompiler::LocalizeString( String& /*rName*/ )
{
}
void FormulaCompiler::PushTokenArray( FormulaTokenArray* pa, sal_Bool bTemp )
{
if ( bAutoCorrect && !pStack )
{ // #61426# don't merge stacked subroutine code into entered formula
aCorrectedFormula += aCorrectedSymbol;
aCorrectedSymbol.Erase();
}
FormulaArrayStack* p = new FormulaArrayStack;
p->pNext = pStack;
p->pArr = pArr;
p->bTemp = bTemp;
pStack = p;
pArr = pa;
}
// =============================================================================
} // formula
// =============================================================================