blob: 4f45949ba0cb5f91267dc9340cfc484b7bd39420 [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.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sc.hxx"
// INCLUDE ---------------------------------------------------------------
#include <sfx2/app.hxx>
#include <sfx2/objsh.hxx>
#include <basic/sbmeth.hxx>
#include <basic/sbstar.hxx>
#include <svl/zforlist.hxx>
#include <tools/rcid.h>
#include <tools/rc.hxx>
#include <tools/solar.h>
#include <unotools/charclass.hxx>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
#include <com/sun/star/sheet/FormulaLanguage.hpp>
#include <com/sun/star/sheet/FormulaMapGroup.hpp>
#include <comphelper/processfactory.hxx>
#include <unotools/transliterationwrapper.hxx>
#include <tools/urlobj.hxx>
#include <rtl/math.hxx>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "compiler.hxx"
#include "rangenam.hxx"
#include "dbcolect.hxx"
#include "document.hxx"
#include "callform.hxx"
#include "addincol.hxx"
#include "refupdat.hxx"
#include "scresid.hxx"
#include "sc.hrc"
#include "globstr.hrc"
#include "cell.hxx"
#include "dociter.hxx"
#include "docoptio.hxx"
#include <formula/errorcodes.hxx>
#include "parclass.hxx"
#include "autonamecache.hxx"
#include "externalrefmgr.hxx"
#include "rangeutl.hxx"
#include "convuno.hxx"
#include "tokenuno.hxx"
#include "formulaparserpool.hxx"
using namespace formula;
using namespace ::com::sun::star;
using rtl::OUString;
using ::std::vector;
#if OSL_DEBUG_LEVEL > 1
// For some unknown reason the identical dbg_dump utilities in
// tools/source/string/debugprint.cxx tend to crash when called from within
// gdb. Having them here also comes handy as libtl*.so doesn't have to be
// replaced.
const char* dbg_sc_dump( const ByteString & rStr )
{
static ByteString aStr;
aStr = rStr;
aStr.Append(static_cast<char>(0));
return aStr.GetBuffer();
}
const char* dbg_sc_dump( const UniString & rStr )
{
return dbg_sc_dump(ByteString(rStr, RTL_TEXTENCODING_UTF8));
}
const char* dbg_sc_dump( const sal_Unicode * pBuf )
{
return dbg_sc_dump( UniString( pBuf));
}
const char* dbg_sc_dump( const sal_Unicode c )
{
return dbg_sc_dump( UniString( c));
}
#endif
CharClass* ScCompiler::pCharClassEnglish = NULL;
const ScCompiler::Convention* ScCompiler::pConventions[ ] = { NULL, NULL, NULL, NULL, NULL, NULL };
enum ScanState
{
ssGetChar,
ssGetBool,
ssGetValue,
ssGetString,
ssSkipString,
ssGetIdent,
ssGetReference,
ssSkipReference,
ssStop
};
static const sal_Char* pInternal[ 1 ] = { "TTT" };
using namespace ::com::sun::star::i18n;
/////////////////////////////////////////////////////////////////////////
class ScCompilerRecursionGuard
{
private:
short& rRecursion;
public:
ScCompilerRecursionGuard( short& rRec )
: rRecursion( rRec ) { ++rRecursion; }
~ScCompilerRecursionGuard() { --rRecursion; }
};
void ScCompiler::fillFromAddInMap( NonConstOpCodeMapPtr xMap,FormulaGrammar::Grammar _eGrammar ) const
{
size_t nSymbolOffset;
switch( _eGrammar )
{
case FormulaGrammar::GRAM_PODF:
nSymbolOffset = offsetof( AddInMap, pUpper);
break;
default:
case FormulaGrammar::GRAM_ODFF:
nSymbolOffset = offsetof( AddInMap, pODFF);
break;
case FormulaGrammar::GRAM_ENGLISH:
nSymbolOffset = offsetof( AddInMap, pEnglish);
break;
}
const AddInMap* pMap = GetAddInMap();
const AddInMap* const pStop = pMap + GetAddInMapCount();
for ( ; pMap < pStop; ++pMap)
{
char const * const * ppSymbol =
reinterpret_cast< char const * const * >(
reinterpret_cast< char const * >(pMap) + nSymbolOffset);
xMap->putExternal( String::CreateFromAscii( *ppSymbol),
String::CreateFromAscii( pMap->pOriginal));
}
}
void ScCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr xMap ) const
{
ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
long nCount = pColl->GetFuncCount();
for (long i=0; i < nCount; ++i)
{
const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
if (pFuncData)
xMap->putExternalSoftly( pFuncData->GetUpperName(),
pFuncData->GetOriginalName());
}
}
void ScCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr xMap ) const
{
ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
long nCount = pColl->GetFuncCount();
for (long i=0; i < nCount; ++i)
{
const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
if (pFuncData)
{
String aName;
if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
else
xMap->putExternalSoftly( pFuncData->GetUpperName(),
pFuncData->GetOriginalName());
}
}
}
#ifdef erGENERATEMAPPING
// Run in en-US UI by calling from within gdb, edit pODFF entries afterwards.
void dbg_call_generateMappingODFF()
{
// static ScCompiler members
fprintf( stdout, "%s", "static struct AddInMap\n{\n const char* pODFF;\n const char* pEnglish;\n bool bMapDupToInternal;\n const char* pOriginal;\n const char* pUpper;\n} maAddInMap[];\n");
fprintf( stdout, "%s", "static const AddInMap* GetAddInMap();\n");
fprintf( stdout, "%s", "static size_t GetAddInMapCount();\n");
fprintf( stdout, "addinfuncdata___:%s", "ScCompiler::AddInMap ScCompiler::maAddInMap[] =\n{\n");
ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
long nCount = pColl->GetFuncCount();
for (long i=0; i < nCount; ++i)
{
const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
if (pFuncData)
{
#define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer())
String aL = pFuncData->GetUpperLocal();
String aP = pFuncData->GetOriginalName();
String aU = pFuncData->GetUpperName();
fprintf( stdout, "addinfuncdata%3ld: { \"%s\", \"%s\", false, \"%s\", \"%s\" },\n",
i, out(aL), out(aL), out(aP), out(aU));
#undef out
}
}
fprintf( stdout, "addinfuncdata___:%s", "};\n");
fprintf( stdout, "%s", "\n// static\nconst ScCompiler::AddInMap* ScCompiler::GetAddInMap()\n{\n return maAddInMap;\n}\n");
fprintf( stdout, "%s", "\n// static\nsize_t ScCompiler::GetAddInMapCount()\n{\n return sizeof(maAddInMap)/sizeof(maAddInMap[0]);\n}\n");
fflush( stdout);
}
#endif // erGENERATEMAPPING
#ifdef erGENERATEMAPPINGDIFF
// Run in en-US UI by calling from within gdb.
void dbg_call_generateMappingDiff()
{
using namespace ::com::sun::star::sheet;
ScCompiler::OpCodeMapPtr xPODF = ScCompiler::GetOpCodeMap(
FormulaLanguage::ODF_11);
ScCompiler::OpCodeMapPtr xODFF = ScCompiler::GetOpCodeMap(
FormulaLanguage::ODFF);
ScCompiler::OpCodeMapPtr xENUS = ScCompiler::GetOpCodeMap(
FormulaLanguage::ENGLISH);
sal_uInt16 nPODF = xPODF->getSymbolCount();
sal_uInt16 nODFF = xODFF->getSymbolCount();
sal_uInt16 nENUS = xENUS->getSymbolCount();
printf( "%s\n", "This is a semicolon separated file, you may import it as such to Calc.");
printf( "%s\n", "Spreadsheet functions name differences between PODF (ODF < 1.2) and ODFF (ODF >= 1.2), plus English UI names.");
printf( "\nInternal OpCodes; PODF: %d; ODFF: %d; ENUS: %d\n",
(int)nPODF, (int)nODFF, (int)nENUS);
sal_uInt16 nMax = ::std::max( ::std::max( nPODF, nODFF), nENUS);
#define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer())
for (sal_uInt16 i=0; i < nMax; ++i)
{
const String& rPODF = xPODF->getSymbol(static_cast<OpCode>(i));
const String& rODFF = xODFF->getSymbol(static_cast<OpCode>(i));
const String& rENUS = xENUS->getSymbol(static_cast<OpCode>(i));
if (rPODF != rODFF)
printf( "%d;%s;%s;%s\n", (int)i, out(rPODF), out(rODFF), out(rENUS));
}
// Actually they should all differ, so we could simply list them all, but
// this is correct and we would find odd things, if any.
const ExternalHashMap* pPODF = xPODF->getReverseExternalHashMap();
const ExternalHashMap* pODFF = xODFF->getReverseExternalHashMap();
const ExternalHashMap* pENUS = xENUS->getReverseExternalHashMap();
printf( "\n%s\n", "Add-In mapping");
for (ExternalHashMap::const_iterator it = pPODF->begin(); it != pPODF->end(); ++it)
{
ExternalHashMap::const_iterator iLookODFF = pODFF->find( (*it).first);
ExternalHashMap::const_iterator iLookENUS = pENUS->find( (*it).first);
String aNative( iLookENUS == pENUS->end() ?
String::CreateFromAscii( "ENGLISH_SYMBOL_NOT_FOUND") :
(*iLookENUS).second);
if (iLookODFF == pODFF->end())
printf( "NOT FOUND;%s;;%s\n", out((*it).first), out(aNative));
else if((*it).second == (*iLookODFF).second) // upper equal
printf( "EQUAL;%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative));
else
printf( ";%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative));
}
#undef out
fflush( stdout);
}
#endif // erGENERATEMAPPINGDIFF
// static
void ScCompiler::DeInit()
{
if (pCharClassEnglish)
{
delete pCharClassEnglish;
pCharClassEnglish = NULL;
}
}
bool ScCompiler::IsEnglishSymbol( const String& rName )
{
// function names are always case-insensitive
String aUpper( ScGlobal::pCharClass->upper( rName ) );
// 1. built-in function name
OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper );
if ( eOp != ocNone )
{
return true;
}
// 2. old add in functions
sal_uInt16 nIndex;
if ( ScGlobal::GetFuncCollection()->SearchFunc( aUpper, nIndex ) )
{
return true;
}
// 3. new (uno) add in functions
String aIntName(ScGlobal::GetAddInCollection()->FindFunction( aUpper, sal_False ));
if (aIntName.Len())
{
return true;
}
return false; // no valid function name
}
// static
void ScCompiler::InitCharClassEnglish()
{
::com::sun::star::lang::Locale aLocale(
OUString( RTL_CONSTASCII_USTRINGPARAM( "en")),
OUString( RTL_CONSTASCII_USTRINGPARAM( "US")),
OUString());
pCharClassEnglish = new CharClass(
::comphelper::getProcessServiceFactory(), aLocale);
}
void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
{
DBG_ASSERT( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
if (eGrammar == GetGrammar())
return; // nothing to be done
if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
{
meGrammar = eGrammar;
mxSymbols = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
}
else
{
FormulaGrammar::Grammar eMyGrammar = eGrammar;
const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
DBG_ASSERT( xMap, "ScCompiler::SetGrammar: unknown formula language");
if (!xMap)
{
xMap = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
eMyGrammar = xMap->getGrammar();
}
// Save old grammar for call to SetGrammarAndRefConvention().
FormulaGrammar::Grammar eOldGrammar = GetGrammar();
// This also sets the grammar associated with the map!
SetFormulaLanguage( xMap);
// Override if necessary.
if (eMyGrammar != GetGrammar())
SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
}
}
void ScCompiler::SetEncodeUrlMode( EncodeUrlMode eMode )
{
meEncodeUrlMode = eMode;
}
ScCompiler::EncodeUrlMode ScCompiler::GetEncodeUrlMode() const
{
return meEncodeUrlMode;
}
void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
{
if (xMap.get())
{
mxSymbols = xMap;
if (mxSymbols->isEnglish())
{
if (!pCharClassEnglish)
InitCharClassEnglish();
pCharClass = pCharClassEnglish;
}
else
pCharClass = ScGlobal::pCharClass;
SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
}
}
void ScCompiler::SetGrammarAndRefConvention(
const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
{
meGrammar = eNewGrammar; //! SetRefConvention needs the new grammar set!
FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
{
if (pDoc)
SetRefConvention( pDoc->GetAddressConvention());
else
SetRefConvention( pConvOOO_A1);
}
else
SetRefConvention( eConv );
}
String ScCompiler::FindAddInFunction( const String& rUpperName, sal_Bool bLocalFirst ) const
{
return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=sal_False for english
}
#ifdef erDEBUG
void dbg_call_testcreatemapping()
{
using namespace ::com::sun::star::sheet;
ScCompiler::OpCodeMapPtr xMap = ScCompiler::GetOpCodeMap( FormulaLanguage::ODFF);
xMap->createSequenceOfAvailableMappings( FormulaMapGroup::FUNCTIONS);
}
#endif
//-----------------------------------------------------------------------------
ScCompiler::Convention::~Convention()
{
delete [] mpCharTable;
mpCharTable = NULL;
}
ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
:
meConv( eConv )
{
int i;
sal_uLong *t= new sal_uLong [128];
ScCompiler::pConventions[ meConv ] = this;
mpCharTable = t;
for (i = 0; i < 128; i++)
t[i] = SC_COMPILER_C_ILLEGAL;
/* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
if (FormulaGrammar::CONV_ODF == meConv)
/* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
/* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
/* # */ t[35] = SC_COMPILER_C_WORD_SEP;
/* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
if (FormulaGrammar::CONV_ODF == meConv)
/* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
/* % */ t[37] = SC_COMPILER_C_VALUE;
/* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* ' */ t[39] = SC_COMPILER_C_NAME_SEP;
/* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
/* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE;
/* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
/* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
/* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
for (i = 48; i < 58; i++)
/* 0-9 */ t[i] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_WORD | SC_COMPILER_C_VALUE | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
/* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD;
/* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_NAME;
/* @ */ // FREE
for (i = 65; i < 91; i++)
/* A-Z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
if (FormulaGrammar::CONV_ODF == meConv)
{
/* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET;
/* \ */ // FREE
/* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET;
}
else
{
/* [ */ // FREE
/* \ */ // FREE
/* ] */ // FREE
}
/* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
/* _ */ t[95] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
/* ` */ // FREE
for (i = 97; i < 123; i++)
/* a-z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
/* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open
/* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific)
/* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close
/* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific
/* 127 */ // FREE
if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
{
/* */ t[32] |= SC_COMPILER_C_WORD;
/* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD;
/* " */ t[34] |= SC_COMPILER_C_WORD;
/* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP);
/* # */ t[35] |= SC_COMPILER_C_WORD;
/* % */ t[37] |= SC_COMPILER_C_WORD;
/* ' */ t[39] |= SC_COMPILER_C_WORD;
/* % */ t[37] |= SC_COMPILER_C_WORD;
/* & */ t[38] |= SC_COMPILER_C_WORD;
/* ' */ t[39] |= SC_COMPILER_C_WORD;
/* ( */ t[40] |= SC_COMPILER_C_WORD;
/* ) */ t[41] |= SC_COMPILER_C_WORD;
/* * */ t[42] |= SC_COMPILER_C_WORD;
/* + */ t[43] |= SC_COMPILER_C_WORD;
#if 0 /* this really needs to be locale specific. */
/* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
#else
/* , */ t[44] |= SC_COMPILER_C_WORD;
#endif
/* - */ t[45] |= SC_COMPILER_C_WORD;
/* ; */ t[59] |= SC_COMPILER_C_WORD;
/* < */ t[60] |= SC_COMPILER_C_WORD;
/* = */ t[61] |= SC_COMPILER_C_WORD;
/* > */ t[62] |= SC_COMPILER_C_WORD;
/* ? */ // question really is not permitted in sheet name
/* @ */ t[64] |= SC_COMPILER_C_WORD;
/* [ */ t[91] |= SC_COMPILER_C_WORD;
/* ] */ t[93] |= SC_COMPILER_C_WORD;
/* { */ t[123]|= SC_COMPILER_C_WORD;
/* | */ t[124]|= SC_COMPILER_C_WORD;
/* } */ t[125]|= SC_COMPILER_C_WORD;
/* ~ */ t[126]|= SC_COMPILER_C_WORD;
if( FormulaGrammar::CONV_XL_R1C1 == meConv )
{
/* - */ t[45] |= SC_COMPILER_C_IDENT;
/* [ */ t[91] |= SC_COMPILER_C_IDENT;
/* ] */ t[93] |= SC_COMPILER_C_IDENT;
}
if( FormulaGrammar::CONV_XL_OOX == meConv )
{
/* [ */ t[91] |= SC_COMPILER_C_CHAR_IDENT;
/* ] */ t[93] |= SC_COMPILER_C_IDENT;
}
}
}
//-----------------------------------------------------------------------------
static bool lcl_isValidQuotedText( const String& rFormula, xub_StrLen nSrcPos, ParseResult& rRes )
{
// Tokens that start at ' can have anything in them until a final '
// but '' marks an escaped '
// We've earlier guaranteed that a string containing '' will be
// surrounded by '
if (rFormula.GetChar(nSrcPos) == '\'')
{
xub_StrLen nPos = nSrcPos+1;
while (nPos < rFormula.Len())
{
if (rFormula.GetChar(nPos) == '\'')
{
if ( (nPos+1 == rFormula.Len()) || (rFormula.GetChar(nPos+1) != '\'') )
{
rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
rRes.EndPos = nPos+1;
return true;
}
++nPos;
}
++nPos;
}
}
return false;
}
static bool lcl_parseExternalName(
const String& rSymbol,
String& rFile,
String& rName,
const sal_Unicode cSep,
const ScDocument* pDoc = NULL,
const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
{
/* TODO: future versions will have to support sheet-local names too, thus
* return a possible sheet name as well. */
const sal_Unicode* const pStart = rSymbol.GetBuffer();
const sal_Unicode* p = pStart;
xub_StrLen nLen = rSymbol.Len();
sal_Unicode cPrev = 0;
String aTmpFile, aTmpName;
xub_StrLen i = 0;
bool bInName = false;
if (cSep == '!')
{
// For XL use existing parser that resolves bracketed and quoted and
// indexed external document names.
ScRange aRange;
String aStartTabName, aEndTabName;
sal_uInt16 nFlags = 0;
p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
aEndTabName, nFlags, true, pExternalLinks );
if (!p || p == pStart)
return false;
i = xub_StrLen(p - pStart);
cPrev = *(p-1);
}
for ( ; i < nLen; ++i, ++p)
{
sal_Unicode c = *p;
if (i == 0)
{
if (c == '.' || c == cSep)
return false;
if (c == '\'')
{
// Move to the next chart and loop until the second single
// quote.
cPrev = c;
++i; ++p;
for (xub_StrLen j = i; j < nLen; ++j, ++p)
{
c = *p;
if (c == '\'')
{
if (j == i)
{
// empty quote e.g. (=''!Name)
return false;
}
if (cPrev == '\'')
{
// two consecutive quotes equals a single
// quote in the file name.
aTmpFile.Append(c);
cPrev = 'a';
}
else
cPrev = c;
continue;
}
if (cPrev == '\'' && j != i)
{
// this is not a quote but the previous one
// is. This ends the parsing of the quoted
// segment.
i = j;
bInName = true;
break;
}
aTmpFile.Append(c);
cPrev = c;
}
if (!bInName)
{
// premature ending of the quoted segment.
return false;
}
if (c != cSep)
{
// only the separator is allowed after the closing quote.
return false;
}
cPrev = c;
continue;
}
}
if (bInName)
{
if (c == cSep)
{
// A second separator ? Not a valid external name.
return false;
}
aTmpName.Append(c);
}
else
{
if (c == cSep)
{
bInName = true;
}
else
{
do
{
if (CharClass::isAsciiAlphaNumeric(c))
// allowed.
break;
if (c > 128)
// non-ASCII character is allowed.
break;
bool bValid = false;
switch (c)
{
case '_':
case '-':
case '.':
// these special characters are allowed.
bValid = true;
break;
}
if (bValid)
break;
return false;
}
while (false);
aTmpFile.Append(c);
}
}
cPrev = c;
}
if (!bInName)
{
// No name found - most likely the symbol has no '!'s.
return false;
}
rFile = aTmpFile;
rName = aTmpName;
return true;
}
static String lcl_makeExternalNameStr( const String& rFile, const String& rName,
const sal_Unicode cSep, bool bODF )
{
String aFile( rFile), aName( rName), aEscQuote( RTL_CONSTASCII_USTRINGPARAM("''"));
aFile.SearchAndReplaceAllAscii( "'", aEscQuote);
if (bODF)
aName.SearchAndReplaceAllAscii( "'", aEscQuote);
rtl::OUStringBuffer aBuf( aFile.Len() + aName.Len() + 9);
if (bODF)
aBuf.append( sal_Unicode( '['));
aBuf.append( sal_Unicode( '\''));
aBuf.append( aFile);
aBuf.append( sal_Unicode( '\''));
aBuf.append( cSep);
if (bODF)
aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "$$'"));
aBuf.append( aName);
if (bODF)
aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "']"));
return String( aBuf.makeStringAndClear());
}
static bool lcl_getLastTabName( String& rTabName2, const String& rTabName1,
const vector<String>& rTabNames, const ScComplexRefData& rRef )
{
SCsTAB nTabSpan = rRef.Ref2.nTab - rRef.Ref1.nTab;
if (nTabSpan > 0)
{
size_t nCount = rTabNames.size();
vector<String>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
vector<String>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
if (itr == rTabNames.end())
{
rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
return false;
}
size_t nDist = ::std::distance(itrBeg, itr);
if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
{
rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
return false;
}
rTabName2 = rTabNames[nDist+nTabSpan];
}
else
rTabName2 = rTabName1;
return true;
}
struct Convention_A1 : public ScCompiler::Convention
{
Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
static void MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol );
static void MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow );
ParseResult parseAnyToken( const String& rFormula,
xub_StrLen nSrcPos,
const CharClass* pCharClass) const
{
ParseResult aRet;
if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
return aRet;
static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
// '?' allowed in range names because of Xcl :-/
static const String aAddAllowed(String::CreateFromAscii("?#"));
return pCharClass->parseAnyToken( rFormula,
nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
}
};
void Convention_A1::MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol )
{
if ( !ValidCol( nCol) )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
::ScColToAlpha( rBuffer, nCol);
}
void Convention_A1::MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow )
{
if ( !ValidRow(nRow) )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
rBuffer.append(sal_Int32(nRow + 1));
}
//-----------------------------------------------------------------------------
struct ConventionOOO_A1 : public Convention_A1
{
ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
static String MakeTabStr( const ScCompiler& rComp, SCTAB nTab, String& aDoc )
{
String aString;
if (!rComp.GetDoc()->GetName( nTab, aString ))
aString = ScGlobal::GetRscString(STR_NO_REF_TABLE);
else
{
// "'Doc'#Tab"
xub_StrLen nPos = ScCompiler::GetDocTabPos( aString);
if (nPos != STRING_NOTFOUND)
{
aDoc = aString.Copy( 0, nPos + 1 );
aString.Erase( 0, nPos + 1 );
aDoc = INetURLObject::decode( aDoc, INET_HEX_ESCAPE,
INetURLObject::DECODE_UNAMBIGUOUS );
}
else
aDoc.Erase();
ScCompiler::CheckTabQuotes( aString, FormulaGrammar::CONV_OOO );
}
aString += '.';
return aString;
}
void MakeRefStrImpl( rtl::OUStringBuffer& rBuffer,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
bool bSingleRef,
bool bODF ) const
{
if (bODF)
rBuffer.append(sal_Unicode('['));
ScComplexRefData aRef( rRef );
// In case absolute/relative positions weren't separately available:
// transform relative to absolute!
// AdjustReference( aRef.Ref1 );
// if( !bSingleRef )
// AdjustReference( aRef.Ref2 );
aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
if( !bSingleRef )
aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
if( aRef.Ref1.IsFlag3D() )
{
if (aRef.Ref1.IsTabDeleted())
{
if (!aRef.Ref1.IsTabRel())
rBuffer.append(sal_Unicode('$'));
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
rBuffer.append(sal_Unicode('.'));
}
else
{
String aDoc;
String aRefStr( MakeTabStr( rComp, aRef.Ref1.nTab, aDoc ) );
rBuffer.append(aDoc);
if (!aRef.Ref1.IsTabRel()) rBuffer.append(sal_Unicode('$'));
rBuffer.append(aRefStr);
}
}
else if (bODF)
rBuffer.append(sal_Unicode('.'));
if (!aRef.Ref1.IsColRel())
rBuffer.append(sal_Unicode('$'));
if ( aRef.Ref1.IsColDeleted() )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
MakeColStr(rBuffer, aRef.Ref1.nCol );
if (!aRef.Ref1.IsRowRel())
rBuffer.append(sal_Unicode('$'));
if ( aRef.Ref1.IsRowDeleted() )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
MakeRowStr( rBuffer, aRef.Ref1.nRow );
if (!bSingleRef)
{
rBuffer.append(sal_Unicode(':'));
if (aRef.Ref2.IsFlag3D() || aRef.Ref2.nTab != aRef.Ref1.nTab)
{
if (aRef.Ref2.IsTabDeleted())
{
if (!aRef.Ref2.IsTabRel())
rBuffer.append(sal_Unicode('$'));
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
rBuffer.append(sal_Unicode('.'));
}
else
{
String aDoc;
String aRefStr( MakeTabStr( rComp, aRef.Ref2.nTab, aDoc ) );
rBuffer.append(aDoc);
if (!aRef.Ref2.IsTabRel()) rBuffer.append(sal_Unicode('$'));
rBuffer.append(aRefStr);
}
}
else if (bODF)
rBuffer.append(sal_Unicode('.'));
if (!aRef.Ref2.IsColRel())
rBuffer.append(sal_Unicode('$'));
if ( aRef.Ref2.IsColDeleted() )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
MakeColStr( rBuffer, aRef.Ref2.nCol );
if (!aRef.Ref2.IsRowRel())
rBuffer.append(sal_Unicode('$'));
if ( aRef.Ref2.IsRowDeleted() )
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
else
MakeRowStr( rBuffer, aRef.Ref2.nRow );
}
if (bODF)
rBuffer.append(sal_Unicode(']'));
}
void MakeRefStr( rtl::OUStringBuffer& rBuffer,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
sal_Bool bSingleRef ) const
{
MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, false);
}
virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
{
switch (eSymType)
{
case ScCompiler::Convention::ABS_SHEET_PREFIX:
return '$';
case ScCompiler::Convention::SHEET_SEPARATOR:
return '.';
}
return sal_Unicode(0);
}
virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
const ScDocument* pDoc,
const ::com::sun::star::uno::Sequence<
const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
{
return lcl_parseExternalName(rSymbol, rFile, rName, sal_Unicode('#'), pDoc, pExternalLinks);
}
virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
{
return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), false);
}
bool makeExternalSingleRefStr( ::rtl::OUStringBuffer& rBuffer, sal_uInt16 nFileId,
const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr, bool bDisplayTabName, bool bEncodeUrl ) const
{
if (bDisplayTabName)
{
String aFile;
const String* p = pRefMgr->getExternalFileName(nFileId);
if (p)
{
if (bEncodeUrl)
aFile = *p;
else
aFile = INetURLObject::decode(*p, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
}
aFile.SearchAndReplaceAllAscii("'", String::CreateFromAscii("''"));
rBuffer.append(sal_Unicode('\''));
rBuffer.append(aFile);
rBuffer.append(sal_Unicode('\''));
rBuffer.append(sal_Unicode('#'));
if (!rRef.IsTabRel())
rBuffer.append(sal_Unicode('$'));
ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
rBuffer.append(sal_Unicode('.'));
}
if (!rRef.IsColRel())
rBuffer.append(sal_Unicode('$'));
MakeColStr( rBuffer, rRef.nCol);
if (!rRef.IsRowRel())
rBuffer.append(sal_Unicode('$'));
MakeRowStr( rBuffer, rRef.nRow);
return true;
}
void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr, bool bODF ) const
{
ScSingleRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
if (bODF)
rBuffer.append( sal_Unicode('['));
bool bEncodeUrl = true;
switch (rCompiler.GetEncodeUrlMode())
{
case ScCompiler::ENCODE_BY_GRAMMAR:
bEncodeUrl = bODF;
break;
case ScCompiler::ENCODE_ALWAYS:
bEncodeUrl = true;
break;
case ScCompiler::ENCODE_NEVER:
bEncodeUrl = false;
break;
default:
;
}
makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef, pRefMgr, true, bEncodeUrl);
if (bODF)
rBuffer.append( sal_Unicode(']'));
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
}
void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
ScExternalRefManager* pRefMgr, bool bODF ) const
{
ScComplexRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
if (bODF)
rBuffer.append( sal_Unicode('['));
// Ensure that there's always a closing bracket, no premature returns.
bool bEncodeUrl = true;
switch (rCompiler.GetEncodeUrlMode())
{
case ScCompiler::ENCODE_BY_GRAMMAR:
bEncodeUrl = bODF;
break;
case ScCompiler::ENCODE_ALWAYS:
bEncodeUrl = true;
break;
case ScCompiler::ENCODE_NEVER:
bEncodeUrl = false;
break;
default:
;
}
do
{
if (!makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef.Ref1, pRefMgr, true, bEncodeUrl))
break;
rBuffer.append(sal_Unicode(':'));
String aLastTabName;
bool bDisplayTabName = (aRef.Ref1.nTab != aRef.Ref2.nTab);
if (bDisplayTabName)
{
// Get the name of the last table.
vector<String> aTabNames;
pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
if (aTabNames.empty())
{
DBG_ERROR1( "ConventionOOO_A1::makeExternalRefStrImpl: no sheet names for document ID %s", nFileId);
}
if (!lcl_getLastTabName(aLastTabName, rTabName, aTabNames, aRef))
{
DBG_ERROR( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
// aLastTabName contains #REF!, proceed.
}
}
else if (bODF)
rBuffer.append( sal_Unicode('.')); // need at least the sheet separator in ODF
makeExternalSingleRefStr( rBuffer, nFileId, aLastTabName,
aRef.Ref2, pRefMgr, bDisplayTabName, bEncodeUrl);
} while (0);
if (bODF)
rBuffer.append( sal_Unicode(']'));
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
}
};
static const ConventionOOO_A1 ConvOOO_A1;
const ScCompiler::Convention * const ScCompiler::pConvOOO_A1 = &ConvOOO_A1;
//-----------------------------------------------------------------------------
struct ConventionOOO_A1_ODF : public ConventionOOO_A1
{
ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
void MakeRefStr( rtl::OUStringBuffer& rBuffer,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
sal_Bool bSingleRef ) const
{
MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, true);
}
virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
{
return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), true);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
}
};
static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
const ScCompiler::Convention * const ScCompiler::pConvOOO_A1_ODF = &ConvOOO_A1_ODF;
//-----------------------------------------------------------------------------
struct ConventionXL
{
static bool GetDocAndTab( const ScCompiler& rComp,
const ScSingleRefData& rRef,
String& rDocName,
String& rTabName )
{
bool bHasDoc = false;
rDocName.Erase();
if (rRef.IsTabDeleted() ||
!rComp.GetDoc()->GetName( rRef.nTab, rTabName ))
{
rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE );
return false;
}
// Cheesy hack to unparse the OOO style "'Doc'#Tab"
xub_StrLen nPos = ScCompiler::GetDocTabPos( rTabName);
if (nPos != STRING_NOTFOUND)
{
rDocName = rTabName.Copy( 0, nPos );
// TODO : More research into how XL escapes the doc path
rDocName = INetURLObject::decode( rDocName, INET_HEX_ESCAPE,
INetURLObject::DECODE_UNAMBIGUOUS );
rTabName.Erase( 0, nPos + 1 );
bHasDoc = true;
}
// XL uses the same sheet name quoting conventions in both modes
// it is safe to use A1 here
ScCompiler::CheckTabQuotes( rTabName, FormulaGrammar::CONV_XL_A1 );
return bHasDoc;
}
static void MakeDocStr( rtl::OUStringBuffer& rBuf,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
bool bSingleRef )
{
if( rRef.Ref1.IsFlag3D() )
{
String aStartTabName, aStartDocName, aEndTabName, aEndDocName;
bool bStartHasDoc = false, bEndHasDoc = false;
bStartHasDoc = GetDocAndTab( rComp, rRef.Ref1,
aStartDocName, aStartTabName);
if( !bSingleRef && rRef.Ref2.IsFlag3D() )
{
bEndHasDoc = GetDocAndTab( rComp, rRef.Ref2,
aEndDocName, aEndTabName);
}
else
bEndHasDoc = bStartHasDoc;
if( bStartHasDoc )
{
// A ref across multipled workbooks ?
if( !bEndHasDoc )
return;
rBuf.append( sal_Unicode( '[' ) );
rBuf.append( aStartDocName );
rBuf.append( sal_Unicode( ']' ) );
}
rBuf.append( aStartTabName );
if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
{
rBuf.append( sal_Unicode( ':' ) );
rBuf.append( aEndTabName );
}
rBuf.append( sal_Unicode( '!' ) );
}
}
static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
{
switch (eSymType)
{
case ScCompiler::Convention::ABS_SHEET_PREFIX:
return sal_Unicode(0);
case ScCompiler::Convention::SHEET_SEPARATOR:
return '!';
}
return sal_Unicode(0);
}
static bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
const ScDocument* pDoc,
const ::com::sun::star::uno::Sequence<
const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks )
{
return lcl_parseExternalName( rSymbol, rFile, rName, sal_Unicode('!'), pDoc, pExternalLinks);
}
static String makeExternalNameStr( const String& rFile, const String& rName )
{
return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('!'), false);
}
static void makeExternalDocStr( ::rtl::OUStringBuffer& rBuffer, const String& rFullName, bool bEncodeUrl )
{
// Format that is easier to deal with inside OOo, because we use file
// URL, and all characetrs are allowed. Check if it makes sense to do
// it the way Gnumeric does it. Gnumeric doesn't use the URL form
// and allows relative file path.
//
// ['file:///path/to/source/filename.xls']
rBuffer.append(sal_Unicode('['));
rBuffer.append(sal_Unicode('\''));
String aFullName;
if (bEncodeUrl)
aFullName = rFullName;
else
aFullName = INetURLObject::decode(rFullName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
const sal_Unicode* pBuf = aFullName.GetBuffer();
xub_StrLen nLen = aFullName.Len();
for (xub_StrLen i = 0; i < nLen; ++i)
{
const sal_Unicode c = pBuf[i];
if (c == sal_Unicode('\''))
rBuffer.append(c);
rBuffer.append(c);
}
rBuffer.append(sal_Unicode('\''));
rBuffer.append(sal_Unicode(']'));
}
static void makeExternalTabNameRange( ::rtl::OUStringBuffer& rBuf, const String& rTabName,
const vector<String>& rTabNames,
const ScComplexRefData& rRef )
{
String aLastTabName;
if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
{
ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
return;
}
ScRangeStringConverter::AppendTableName(rBuf, rTabName);
if (rTabName != aLastTabName)
{
rBuf.append(sal_Unicode(':'));
ScRangeStringConverter::AppendTableName(rBuf, rTabName);
}
}
static void parseExternalDocName( const String& rFormula, xub_StrLen& rSrcPos )
{
xub_StrLen nLen = rFormula.Len();
const sal_Unicode* p = rFormula.GetBuffer();
sal_Unicode cPrev = 0;
for (xub_StrLen i = rSrcPos; i < nLen; ++i)
{
sal_Unicode c = p[i];
if (i == rSrcPos)
{
// first character must be '['.
if (c != '[')
return;
}
else if (i == rSrcPos + 1)
{
// second character must be a single quote.
if (c != '\'')
return;
}
else if (c == '\'')
{
if (cPrev == '\'')
// two successive single quote is treated as a single
// valid character.
c = 'a';
}
else if (c == ']')
{
if (cPrev == '\'')
{
// valid source document path found. Increment the
// current position to skip the source path.
rSrcPos = i + 1;
if (rSrcPos >= nLen)
rSrcPos = nLen - 1;
return;
}
else
return;
}
else
{
// any other character
if (i > rSrcPos + 2 && cPrev == '\'')
// unless it's the 3rd character, a normal character
// following immediately a single quote is invalid.
return;
}
cPrev = c;
}
}
};
struct ConventionXL_A1 : public Convention_A1, public ConventionXL
{
ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
void makeSingleCellStr( ::rtl::OUStringBuffer& rBuf, const ScSingleRefData& rRef ) const
{
if (!rRef.IsColRel())
rBuf.append(sal_Unicode('$'));
MakeColStr(rBuf, rRef.nCol);
if (!rRef.IsRowRel())
rBuf.append(sal_Unicode('$'));
MakeRowStr(rBuf, rRef.nRow);
}
void MakeRefStr( rtl::OUStringBuffer& rBuf,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
sal_Bool bSingleRef ) const
{
ScComplexRefData aRef( rRef );
// Play fast and loose with invalid refs. There is not much point in producing
// Foo!A1:#REF! versus #REF! at this point
aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
MakeDocStr( rBuf, rComp, aRef, bSingleRef );
if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
{
rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
return;
}
if( !bSingleRef )
{
aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
{
rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
return;
}
if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
{
if (!aRef.Ref1.IsRowRel())
rBuf.append(sal_Unicode( '$' ));
MakeRowStr( rBuf, aRef.Ref1.nRow );
rBuf.append(sal_Unicode( ':' ));
if (!aRef.Ref2.IsRowRel())
rBuf.append(sal_Unicode( '$' ));
MakeRowStr( rBuf, aRef.Ref2.nRow );
return;
}
if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
{
if (!aRef.Ref1.IsColRel())
rBuf.append(sal_Unicode( '$' ));
MakeColStr(rBuf, aRef.Ref1.nCol );
rBuf.append(sal_Unicode( ':' ));
if (!aRef.Ref2.IsColRel())
rBuf.append(sal_Unicode( '$' ));
MakeColStr(rBuf, aRef.Ref2.nCol );
return;
}
}
makeSingleCellStr(rBuf, aRef.Ref1);
if (!bSingleRef)
{
rBuf.append(sal_Unicode( ':' ));
makeSingleCellStr(rBuf, aRef.Ref2);
}
}
virtual ParseResult parseAnyToken( const String& rFormula,
xub_StrLen nSrcPos,
const CharClass* pCharClass) const
{
ParseResult aRet;
if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
return aRet;
static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
// '?' allowed in range names
static const String aAddAllowed = String::CreateFromAscii("?!");
return pCharClass->parseAnyToken( rFormula,
nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
}
virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
{
return ConventionXL::getSpecialSymbol(eSymType);
}
virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
const ScDocument* pDoc,
const ::com::sun::star::uno::Sequence<
const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
{
return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
}
virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
{
return ConventionXL::makeExternalNameStr(rFile, rName);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
// ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
// This is a little different from the format Excel uses, as Excel
// puts [] only around the file name. But we need to enclose the
// whole file path with [] because the file name can contain any
// characters.
const String* pFullName = pRefMgr->getExternalFileName(nFileId);
if (!pFullName)
return;
ScSingleRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
ConventionXL::makeExternalDocStr(
rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
rBuffer.append(sal_Unicode('!'));
makeSingleCellStr(rBuffer, aRef);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
const String* pFullName = pRefMgr->getExternalFileName(nFileId);
if (!pFullName)
return;
vector<String> aTabNames;
pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
if (aTabNames.empty())
return;
ScComplexRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
ConventionXL::makeExternalDocStr(
rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
rBuffer.append(sal_Unicode('!'));
makeSingleCellStr(rBuffer, aRef.Ref1);
if (aRef.Ref1 != aRef.Ref2)
{
rBuffer.append(sal_Unicode(':'));
makeSingleCellStr(rBuffer, aRef.Ref2);
}
}
};
static const ConventionXL_A1 ConvXL_A1;
const ScCompiler::Convention * const ScCompiler::pConvXL_A1 = &ConvXL_A1;
struct ConventionXL_OOX : public ConventionXL_A1
{
ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
};
static const ConventionXL_OOX ConvXL_OOX;
const ScCompiler::Convention * const ScCompiler::pConvXL_OOX = &ConvXL_OOX;
//-----------------------------------------------------------------------------
static void
r1c1_add_col( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
{
rBuf.append( sal_Unicode( 'C' ) );
if( rRef.IsColRel() )
{
if (rRef.nRelCol != 0)
{
rBuf.append( sal_Unicode( '[' ) );
rBuf.append( String::CreateFromInt32( rRef.nRelCol ) );
rBuf.append( sal_Unicode( ']' ) );
}
}
else
rBuf.append( String::CreateFromInt32( rRef.nCol + 1 ) );
}
static void
r1c1_add_row( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
{
rBuf.append( sal_Unicode( 'R' ) );
if( rRef.IsRowRel() )
{
if (rRef.nRelRow != 0)
{
rBuf.append( sal_Unicode( '[' ) );
rBuf.append( String::CreateFromInt32( rRef.nRelRow ) );
rBuf.append( sal_Unicode( ']' ) );
}
}
else
rBuf.append( String::CreateFromInt32( rRef.nRow + 1 ) );
}
struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
{
ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
void MakeRefStr( rtl::OUStringBuffer& rBuf,
const ScCompiler& rComp,
const ScComplexRefData& rRef,
sal_Bool bSingleRef ) const
{
ScComplexRefData aRef( rRef );
MakeDocStr( rBuf, rComp, aRef, bSingleRef );
// Play fast and loose with invalid refs. There is not much point in producing
// Foo!A1:#REF! versus #REF! at this point
aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
{
rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
return;
}
if( !bSingleRef )
{
aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
{
rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
return;
}
if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
{
r1c1_add_row( rBuf, rRef.Ref1 );
if( rRef.Ref1.nRow != rRef.Ref2.nRow ||
rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ) {
rBuf.append (sal_Unicode ( ':' ) );
r1c1_add_row( rBuf, rRef.Ref2 );
}
return;
}
if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
{
r1c1_add_col( rBuf, rRef.Ref1 );
if( rRef.Ref1.nCol != rRef.Ref2.nCol ||
rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel() )
{
rBuf.append (sal_Unicode ( ':' ) );
r1c1_add_col( rBuf, rRef.Ref2 );
}
return;
}
}
r1c1_add_row( rBuf, rRef.Ref1 );
r1c1_add_col( rBuf, rRef.Ref1 );
if (!bSingleRef)
{
rBuf.append (sal_Unicode ( ':' ) );
r1c1_add_row( rBuf, rRef.Ref2 );
r1c1_add_col( rBuf, rRef.Ref2 );
}
}
ParseResult parseAnyToken( const String& rFormula,
xub_StrLen nSrcPos,
const CharClass* pCharClass) const
{
ConventionXL::parseExternalDocName(rFormula, nSrcPos);
ParseResult aRet;
if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
return aRet;
static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
KParseTokens::ASC_UNDERSCORE ;
static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
// '?' allowed in range names
static const String aAddAllowed = String::CreateFromAscii( "?-[]!" );
return pCharClass->parseAnyToken( rFormula,
nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
}
virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
{
return ConventionXL::getSpecialSymbol(eSymType);
}
virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
const ScDocument* pDoc,
const ::com::sun::star::uno::Sequence<
const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
{
return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
}
virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
{
return ConventionXL::makeExternalNameStr(rFile, rName);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
// ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
// This is a little different from the format Excel uses, as Excel
// puts [] only around the file name. But we need to enclose the
// whole file path with [] because the file name can contain any
// characters.
const String* pFullName = pRefMgr->getExternalFileName(nFileId);
if (!pFullName)
return;
ScSingleRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
ConventionXL::makeExternalDocStr(
rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
rBuffer.append(sal_Unicode('!'));
r1c1_add_row(rBuffer, aRef);
r1c1_add_col(rBuffer, aRef);
}
virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
ScExternalRefManager* pRefMgr ) const
{
const String* pFullName = pRefMgr->getExternalFileName(nFileId);
if (!pFullName)
return;
vector<String> aTabNames;
pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
if (aTabNames.empty())
return;
ScComplexRefData aRef(rRef);
aRef.CalcAbsIfRel(rCompiler.GetPos());
ConventionXL::makeExternalDocStr(
rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
rBuffer.append(sal_Unicode('!'));
if (aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted())
{
rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
return;
}
if (aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL)
{
r1c1_add_row(rBuffer, rRef.Ref1);
if (rRef.Ref1.nRow != rRef.Ref2.nRow || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
{
rBuffer.append (sal_Unicode(':'));
r1c1_add_row(rBuffer, rRef.Ref2);
}
return;
}
if (aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW)
{
r1c1_add_col(rBuffer, aRef.Ref1);
if (aRef.Ref1.nCol != aRef.Ref2.nCol || aRef.Ref1.IsColRel() != aRef.Ref2.IsColRel())
{
rBuffer.append (sal_Unicode(':'));
r1c1_add_col(rBuffer, aRef.Ref2);
}
return;
}
r1c1_add_row(rBuffer, aRef.Ref1);
r1c1_add_col(rBuffer, aRef.Ref1);
rBuffer.append (sal_Unicode (':'));
r1c1_add_row(rBuffer, aRef.Ref2);
r1c1_add_col(rBuffer, aRef.Ref2);
}
};
static const ConventionXL_R1C1 ConvXL_R1C1;
const ScCompiler::Convention * const ScCompiler::pConvXL_R1C1 = &ConvXL_R1C1;
//-----------------------------------------------------------------------------
ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr)
: FormulaCompiler(rArr),
pDoc( pDocument ),
aPos( rPos ),
pCharClass( ScGlobal::pCharClass ),
mnPredetectedReference(0),
mnRangeOpPosInSymbol(-1),
pConv( pConvOOO_A1 ),
meEncodeUrlMode( ENCODE_BY_GRAMMAR ),
mbCloseBrackets( true ),
mbExtendedErrorDetection( false ),
mbRewind( false )
{
nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
}
ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos)
:
pDoc( pDocument ),
aPos( rPos ),
pCharClass( ScGlobal::pCharClass ),
mnPredetectedReference(0),
mnRangeOpPosInSymbol(-1),
pConv( pConvOOO_A1 ),
meEncodeUrlMode( ENCODE_BY_GRAMMAR ),
mbCloseBrackets( true ),
mbExtendedErrorDetection( false ),
mbRewind( false )
{
nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
}
void ScCompiler::CheckTabQuotes( String& rString,
const FormulaGrammar::AddressConvention eConv )
{
using namespace ::com::sun::star::i18n;
sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
sal_Int32 nContFlags = nStartFlags;
ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_STRING, nContFlags, EMPTY_STRING);
bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.Len());
switch ( eConv )
{
default :
case FormulaGrammar::CONV_UNSPECIFIED :
break;
case FormulaGrammar::CONV_OOO :
case FormulaGrammar::CONV_XL_A1 :
case FormulaGrammar::CONV_XL_R1C1 :
case FormulaGrammar::CONV_XL_OOX :
if( bNeedsQuote )
{
static const String one_quote = static_cast<sal_Unicode>( '\'' );
static const String two_quote = String::CreateFromAscii( "''" );
// escape embedded quotes
rString.SearchAndReplaceAll( one_quote, two_quote );
}
break;
}
if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
{
// Prevent any possible confusion resulting from pure numeric sheet names.
bNeedsQuote = true;
}
if( bNeedsQuote )
{
rString.Insert( '\'', 0 );
rString += '\'';
}
}
xub_StrLen ScCompiler::GetDocTabPos( const String& rString )
{
if (rString.GetChar(0) != '\'')
return STRING_NOTFOUND;
xub_StrLen nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
// it must be 'Doc'#
if (nPos != STRING_NOTFOUND && rString.GetChar(nPos-1) != '\'')
nPos = STRING_NOTFOUND;
return nPos;
}
//---------------------------------------------------------------------------
void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
{
switch ( eConv ) {
case FormulaGrammar::CONV_UNSPECIFIED :
break;
default :
case FormulaGrammar::CONV_OOO : SetRefConvention( pConvOOO_A1 ); break;
case FormulaGrammar::CONV_ODF : SetRefConvention( pConvOOO_A1_ODF ); break;
case FormulaGrammar::CONV_XL_A1 : SetRefConvention( pConvXL_A1 ); break;
case FormulaGrammar::CONV_XL_R1C1 : SetRefConvention( pConvXL_R1C1 ); break;
case FormulaGrammar::CONV_XL_OOX : SetRefConvention( pConvXL_OOX ); break;
}
}
void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
{
pConv = pConvP;
meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
DBG_ASSERT( FormulaGrammar::isSupported( meGrammar),
"ScCompiler::SetRefConvention: unsupported grammar resulting");
}
void ScCompiler::SetError(sal_uInt16 nError)
{
if( !pArr->GetCodeError() )
pArr->SetCodeError( nError);
}
sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, xub_StrLen nMax )
{
const sal_Unicode* const pStop = pDst + nMax;
while ( *pSrc && pDst < pStop )
{
*pDst++ = *pSrc++;
}
*pDst = 0;
return pDst;
}
//---------------------------------------------------------------------------
// NextSymbol
//---------------------------------------------------------------------------
// Zerlegt die Formel in einzelne Symbole fuer die weitere
// Verarbeitung (Turing-Maschine).
//---------------------------------------------------------------------------
// Ausgangs Zustand = GetChar
//---------------+-------------------+-----------------------+---------------
// Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
//---------------+-------------------+-----------------------+---------------
// GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop
// | <> | Symbol=Zeichen | GetBool
// | $ Buchstabe | Symbol=Zeichen | GetWord
// | Ziffer | Symbol=Zeichen | GetValue
// | " | Keine | GetString
// | Sonst | Keine | GetChar
//---------------+-------------------+-----------------------+---------------
// GetBool | => | Symbol=Symbol+Zeichen | Stop
// | Sonst | Dec(CharPos) | Stop
//---------------+-------------------+-----------------------+---------------
// GetWord | SepSymbol | Dec(CharPos) | Stop
// | ()+-*/^=<>&~ | |
// | Leerzeichen | Dec(CharPos) | Stop
// | $_:. | |
// | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord
// | Sonst | Fehler | Stop
//---------------|-------------------+-----------------------+---------------
// GetValue | ;()*/^=<>& | |
// | Leerzeichen | Dec(CharPos) | Stop
// | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue
// | Sonst | Fehler | Stop
//---------------+-------------------+-----------------------+---------------
// GetString | " | Keine | Stop
// | Sonst | Symbol=Symbol+Zeichen | GetString
//---------------+-------------------+-----------------------+---------------
xub_StrLen ScCompiler::NextSymbol(bool bInArray)
{
cSymbol[MAXSTRLEN-1] = 0; // Stopper
sal_Unicode* pSym = cSymbol;
const sal_Unicode* const pStart = aFormula.GetBuffer();
const sal_Unicode* pSrc = pStart + nSrcPos;
bool bi18n = false;
sal_Unicode c = *pSrc;
sal_Unicode cLast = 0;
bool bQuote = false;
mnRangeOpPosInSymbol = -1;
ScanState eState = ssGetChar;
xub_StrLen nSpaces = 0;
sal_Unicode cSep = mxSymbols->getSymbol( ocSep).GetChar(0);
sal_Unicode cArrayColSep = mxSymbols->getSymbol( ocArrayColSep).GetChar(0);
sal_Unicode cArrayRowSep = mxSymbols->getSymbol( ocArrayRowSep).GetChar(0);
sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' :
ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0));
// special symbols specific to address convention used
sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
int nDecSeps = 0;
bool bAutoIntersection = false;
int nRefInName = 0;
mnPredetectedReference = 0;
// try to parse simple tokens before calling i18n parser
while ((c != 0) && (eState != ssStop) )
{
pSrc++;
sal_uLong nMask = GetCharTableFlags( c );
// The parameter separator and the array column and row separators end
// things unconditionally if not in string or reference.
if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
{
switch (eState)
{
// these are to be continued
case ssGetString:
case ssSkipString:
case ssGetReference:
case ssSkipReference:
break;
default:
if (eState == ssGetChar)
*pSym++ = c;
else
pSrc--;
eState = ssStop;
}
}
Label_MaskStateMachine:
switch (eState)
{
case ssGetChar :
{
// Order is important!
if( nMask & SC_COMPILER_C_ODF_LABEL_OP )
{
// '!!' automatic intersection
if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_LABEL_OP)
{
/* TODO: For now the UI "space operator" is used, this
* could be enhanced using a specialized OpCode to get
* rid of the space ambiguity, which would need some
* places to be adapted though. And we would still need
* to support the ambiguous space operator for UI
* purposes anyway. However, we then could check for
* invalid usage of '!!', which currently isn't
* possible. */
if (!bAutoIntersection)
{
++pSrc;
nSpaces += 2; // must match the character count
bAutoIntersection = true;
}
else
{
pSrc--;
eState = ssStop;
}
}
else
{
nMask &= ~SC_COMPILER_C_ODF_LABEL_OP;
goto Label_MaskStateMachine;
}
}
else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER )
{
// '$$' defined name marker
if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_NAME_MARKER)
{
// both eaten, not added to pSym
++pSrc;
}
else
{
nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER;
goto Label_MaskStateMachine;
}
}
else if( nMask & SC_COMPILER_C_CHAR )
{
*pSym++ = c;
eState = ssStop;
}
else if( nMask & SC_COMPILER_C_ODF_LBRACKET )
{
// eaten, not added to pSym
eState = ssGetReference;
mnPredetectedReference = 1;
}
else if( nMask & SC_COMPILER_C_CHAR_BOOL )
{
*pSym++ = c;
eState = ssGetBool;
}
else if( nMask & SC_COMPILER_C_CHAR_VALUE )
{
*pSym++ = c;
eState = ssGetValue;
}
else if( nMask & SC_COMPILER_C_CHAR_STRING )
{
*pSym++ = c;
eState = ssGetString;
}
else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
{
nSpaces++;
}
else if( nMask & SC_COMPILER_C_CHAR_IDENT )
{ // try to get a simple ASCII identifier before calling
// i18n, to gain performance during import
*pSym++ = c;
eState = ssGetIdent;
}
else
{
bi18n = true;
eState = ssStop;
}
}
break;
case ssGetIdent:
{
if ( nMask & SC_COMPILER_C_IDENT )
{ // This catches also $Sheet1.A$1, for example.
if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
{
SetError(errStringOverflow);
eState = ssStop;
}
else
*pSym++ = c;
}
else if (c == ':' && mnRangeOpPosInSymbol < 0)
{
// One range operator may form Sheet1.A:A, which we need to
// pass as one entity to IsReference().
mnRangeOpPosInSymbol = pSym - &cSymbol[0];
if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
{
SetError(errStringOverflow);
eState = ssStop;
}
else
*pSym++ = c;
}
else if ( 128 <= c || '\'' == c )
{ // High values need reparsing with i18n,
// single quoted $'sheet' names too (otherwise we'd had to
// implement everything twice).
bi18n = true;
eState = ssStop;
}
else
{
pSrc--;
eState = ssStop;
}
}
break;
case ssGetBool :
{
if( nMask & SC_COMPILER_C_BOOL )
{
*pSym++ = c;
eState = ssStop;
}
else
{
pSrc--;
eState = ssStop;
}
}
break;
case ssGetValue :
{
if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
{
SetError(errStringOverflow);
eState = ssStop;
}
else if (c == cDecSep)
{
if (++nDecSeps > 1)
{
// reparse with i18n, may be numeric sheet name as well
bi18n = true;
eState = ssStop;
}
else
*pSym++ = c;
}
else if( nMask & SC_COMPILER_C_VALUE )
*pSym++ = c;
else if( nMask & SC_COMPILER_C_VALUE_SEP )
{
pSrc--;
eState = ssStop;
}
else if (c == 'E' || c == 'e')
{
if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_EXP)
*pSym++ = c;
else
{
// reparse with i18n
bi18n = true;
eState = ssStop;
}
}
else if( nMask & SC_COMPILER_C_VALUE_SIGN )
{
if (((cLast == 'E') || (cLast == 'e')) &&
(GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_VALUE))
{
*pSym++ = c;
}
else
{
pSrc--;
eState = ssStop;
}
}
else
{
// reparse with i18n
bi18n = true;
eState = ssStop;
}
}
break;
case ssGetString :
{
if( nMask & SC_COMPILER_C_STRING_SEP )
{
if ( !bQuote )
{
if ( *pSrc == '"' )
bQuote = true; // "" => literal "
else
eState = ssStop;
}
else
bQuote = false;
}
if ( !bQuote )
{
if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
{
SetError(errStringOverflow);
eState = ssSkipString;
}
else
*pSym++ = c;
}
}
break;
case ssSkipString:
if( nMask & SC_COMPILER_C_STRING_SEP )
eState = ssStop;
break;
case ssGetReference:
if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
{
SetError( errStringOverflow);
eState = ssSkipReference;
}
// fall through and follow logic
case ssSkipReference:
// ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
// mandatory also if no sheet name. 'External'# is optional,
// sheet name is optional, quotes around sheet name are
// optional if no quote contained.
// 2nd usage: ['Sheet'.$$'DefinedName']
// 3rd usage: ['External'#$$'DefinedName']
// 4th usage: ['External'#$'Sheet'.$$'DefinedName']
// Also for all these names quotes are optional if no quote
// contained.
{
// nRefInName: 0 := not in sheet name yet. 'External'
// is parsed as if it was a sheet name and nRefInName
// is reset when # is encountered immediately after closing
// quote. Same with 'DefinedName', nRefInName is cleared
// when : is encountered.
// Encountered leading $ before sheet name.
static const int kDollar = (1 << 1);
// Encountered ' opening quote, which may be after $ or
// not.
static const int kOpen = (1 << 2);
// Somewhere in name.
static const int kName = (1 << 3);
// Encountered ' in name, will be cleared if double or
// transformed to kClose if not, in which case kOpen is
// cleared.
static const int kQuote = (1 << 4);
// Past ' closing quote.
static const int kClose = (1 << 5);
// Encountered # file/sheet separator.
static const int kFileSep = (1 << 6);
// Past . sheet name separator.
static const int kPast = (1 << 7);
// Marked name $$ follows sheet name separator, detected
// while we're still on the separator. Will be cleared when
// entering the name.
static const int kMarkAhead = (1 << 8);
// In marked defined name.
static const int kDefName = (1 << 9);
bool bAddToSymbol = true;
if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
{
DBG_ASSERT( nRefInName & (kPast | kDefName),
"ScCompiler::NextSymbol: reference: "
"closing bracket ']' without prior sheet name separator '.' violates ODF spec");
// eaten, not added to pSym
bAddToSymbol = false;
eState = ssStop;
}
else if (cSheetSep == c && nRefInName == 0)
{
// eat it, no sheet name [.A1]
bAddToSymbol = false;
nRefInName |= kPast;
if ('$' == pSrc[0] && '$' == pSrc[1])
nRefInName |= kMarkAhead;
}
else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
{
// Not in col/row yet.
if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
nRefInName = 0;
else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
{
nRefInName &= ~kMarkAhead;
if (!(nRefInName & kDefName))
{
// eaten, not added to pSym (2 chars)
bAddToSymbol = false;
++pSrc;
nRefInName &= kPast;
nRefInName |= kDefName;
}
else
{
// ScAddress::Parse() will recognize this as
// invalid later.
if (eState != ssSkipReference)
{
*pSym++ = c;
*pSym++ = *pSrc++;
}
bAddToSymbol = false;
}
}
else if (cSheetPrefix == c && nRefInName == 0)
nRefInName |= kDollar;
else if ('\'' == c)
{
// TODO: The conventions' parseExternalName()
// should handle quoted names, but as long as they
// don't remove non-embedded quotes here.
if (!(nRefInName & kName))
{
nRefInName |= (kOpen | kName);
bAddToSymbol = !(nRefInName & kDefName);
}
else if (!(nRefInName & kOpen))
{
DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
"a ''' without the name being enclosed in '...' violates ODF spec");
}
else if (nRefInName & kQuote)
{
// escaped embedded quote
nRefInName &= ~kQuote;
}
else
{
switch (pSrc[0])
{
case '\'':
// escapes embedded quote
nRefInName |= kQuote;
break;
case SC_COMPILER_FILE_TAB_SEP:
// sheet name should follow
nRefInName |= kFileSep;
// fallthru
default:
// quote not followed by quote => close
nRefInName |= kClose;
nRefInName &= ~kOpen;
}
bAddToSymbol = !(nRefInName & kDefName);
}
}
else if (cSheetSep == c && !(nRefInName & kOpen))
{
// unquoted sheet name separator
nRefInName |= kPast;
if ('$' == pSrc[0] && '$' == pSrc[1])
nRefInName |= kMarkAhead;
}
else if (':' == c && !(nRefInName & kOpen))
{
DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
"range operator ':' without prior sheet name separator '.' violates ODF spec");
nRefInName = 0;
++mnPredetectedReference;
}
else if (!(nRefInName & kName))
{
// start unquoted name
nRefInName |= kName;
}
}
else if (':' == c)
{
// range operator
nRefInName = 0;
++mnPredetectedReference;
}
if (bAddToSymbol && eState != ssSkipReference)
*pSym++ = c; // everything is part of reference
}
break;
case ssStop:
; // nothing, prevent warning
break;
}
cLast = c;
c = *pSrc;
}
if ( bi18n )
{
nSrcPos = sal::static_int_cast<xub_StrLen>( nSrcPos + nSpaces );
String aSymbol;
mnRangeOpPosInSymbol = -1;
sal_uInt16 nErr = 0;
do
{
bi18n = false;
// special case (e.g. $'sheetname' in OOO A1)
if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
aSymbol += pStart[nSrcPos++];
ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass );
if ( !aRes.TokenType )
SetError( nErr = errIllegalChar ); // parsed chars as string
if ( aRes.EndPos <= nSrcPos )
{ // ?!?
SetError( nErr = errIllegalChar );
nSrcPos = aFormula.Len();
aSymbol.Erase();
}
else
{
aSymbol.Append( pStart + nSrcPos, (xub_StrLen)aRes.EndPos - nSrcPos );
nSrcPos = (xub_StrLen) aRes.EndPos;
c = pStart[nSrcPos];
if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
{ // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
}
// One range operator restarts parsing for second reference.
if (c == ':' && mnRangeOpPosInSymbol < 0)
{
mnRangeOpPosInSymbol = aSymbol.Len();
bi18n = true;
}
if ( bi18n )
aSymbol += pStart[nSrcPos++];
}
} while ( bi18n && !nErr );
xub_StrLen nLen = aSymbol.Len();
if ( nLen >= MAXSTRLEN )
{
SetError( errStringOverflow );
nLen = MAXSTRLEN-1;
}
lcl_UnicodeStrNCpy( cSymbol, aSymbol.GetBuffer(), nLen );
}
else
{
nSrcPos = sal::static_int_cast<xub_StrLen>( pSrc - pStart );
*pSym = 0;
}
if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
{
// This is a trailing range operator, which is nonsense. Will be caught
// in next round.
mnRangeOpPosInSymbol = -1;
*--pSym = 0;
--nSrcPos;
}
if ( bAutoCorrect )
aCorrectedSymbol = cSymbol;
if (bAutoIntersection && nSpaces > 1)
--nSpaces; // replace '!!' with only one space
return nSpaces;
}
//---------------------------------------------------------------------------
// Convert symbol to token
//---------------------------------------------------------------------------
sal_Bool ScCompiler::IsOpCode( const String& rName, bool bInArray )
{
OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
sal_Bool bFound = (iLook != mxSymbols->getHashMap()->end());
if (bFound)
{
ScRawToken aToken;
OpCode eOp = iLook->second;
if (bInArray)
{
if (rName.Equals(mxSymbols->getSymbol(ocArrayColSep)))
eOp = ocArrayColSep;
else if (rName.Equals(mxSymbols->getSymbol(ocArrayRowSep)))
eOp = ocArrayRowSep;
}
aToken.SetOpCode(eOp);
pRawToken = aToken.Clone();
}
else if (mxSymbols->isODFF())
{
// ODFF names that are not written in the current mapping but to be
// recognized. New names will be written in a future relase, then
// exchange (!) with the names in
// formula/source/core/resource/core_resource.src to be able to still
// read the old names as well.
struct FunctionName
{
const sal_Char* pName;
OpCode eOp;
};
static const FunctionName aOdffAliases[] = {
// Renamed old names:
// XXX none yet.
// Renamed new names:
{ "BINOM.DIST.RANGE", ocB }, // B -> BINOM.DIST.RANGE
{ "LEGACY.TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
{ "ORG.OPENOFFICE.EASTERSUNDAY", ocEasterSunday } // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
};
static const size_t nOdffAliases = sizeof(aOdffAliases) / sizeof(aOdffAliases[0]);
for (size_t i=0; i<nOdffAliases; ++i)
{
if (rName.EqualsIgnoreCaseAscii( aOdffAliases[i].pName))
{
ScRawToken aToken;
aToken.SetOpCode( aOdffAliases[i].eOp);
pRawToken = aToken.Clone();
bFound = sal_True;
break; // for
}
}
}
if (!bFound)
{
String aIntName;
if (mxSymbols->hasExternals())
{
// If symbols are set by filters get mapping to exact name.
ExternalHashMap::const_iterator iExt(
mxSymbols->getExternalHashMap()->find( rName));
if (iExt != mxSymbols->getExternalHashMap()->end())
{
if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
aIntName = (*iExt).second;
}
if (!aIntName.Len())
{
// If that isn't found we might continue with rName lookup as a
// last resort by just falling through to FindFunction(), but
// it shouldn't happen if the map was setup correctly. Don't
// waste time and bail out.
return sal_False;
}
}
if (!aIntName.Len())
{
// Old (deprecated) addins first for legacy.
sal_uInt16 nIndex;
bFound = ScGlobal::GetFuncCollection()->SearchFunc( cSymbol, nIndex);
if (bFound)
{
ScRawToken aToken;
aToken.SetExternal( cSymbol );
pRawToken = aToken.Clone();
}
else
// bLocalFirst=sal_False for (English) upper full original name
// (service.function)
aIntName = ScGlobal::GetAddInCollection()->FindFunction(
rName, !mxSymbols->isEnglish());
}
if (aIntName.Len())
{
ScRawToken aToken;
aToken.SetExternal( aIntName.GetBuffer() ); // international name
pRawToken = aToken.Clone();
bFound = sal_True;
}
}
OpCode eOp;
if (bFound && ((eOp = pRawToken->GetOpCode()) == ocSub || eOp == ocNegSub))
{
bool bShouldBeNegSub =
(eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
(SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
eLastOp == ocArrayOpen ||
eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
if (bShouldBeNegSub && eOp == ocSub)
pRawToken->NewOpCode( ocNegSub );
//! if ocNegSub had ForceArray we'd have to set it here
else if (!bShouldBeNegSub && eOp == ocNegSub)
pRawToken->NewOpCode( ocSub );
}
return bFound;
}
sal_Bool ScCompiler::IsOpCode2( const String& rName )
{
sal_Bool bFound = sal_False;
sal_uInt16 i;
for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
bFound = rName.EqualsAscii( pInternal[ i-ocInternalBegin ] );
if (bFound)
{
ScRawToken aToken;
aToken.SetOpCode( (OpCode) --i );
pRawToken = aToken.Clone();
}
return bFound;
}
sal_Bool ScCompiler::IsValue( const String& rSym )
{
double fVal;
sal_uInt32 nIndex = ( mxSymbols->isEnglish() ?
pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ) : 0 );
// sal_uLong nIndex = 0;
//// sal_uLong nIndex = pDoc->GetFormatTable()->GetStandardIndex(ScGlobal::eLnge);
if (pDoc->GetFormatTable()->IsNumberFormat( rSym, nIndex, fVal ) )
{
sal_uInt16 nType = pDoc->GetFormatTable()->GetType(nIndex);
// Don't accept 3:3 as time, it is a reference to entire row 3 instead.
// Dates should never be entered directly and automatically converted
// to serial, because the serial would be wrong if null-date changed.
// Usually it wouldn't be accepted anyway because the date separator
// clashed with other separators or operators.
if (nType & (NUMBERFORMAT_TIME | NUMBERFORMAT_DATE))
return sal_False;
if (nType == NUMBERFORMAT_LOGICAL)
{
const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
while( *p == ' ' )
p++;
if (*p == '(')
return sal_False; // Boolean function instead.
}
if( aFormula.GetChar(nSrcPos) == '.' )
// numerical sheet name?
return sal_False;
if( nType == NUMBERFORMAT_TEXT )
// HACK: number too big!
SetError( errIllegalArgument );
ScRawToken aToken;
aToken.SetDouble( fVal );
pRawToken = aToken.Clone();
return sal_True;
}
else
return sal_False;
}
sal_Bool ScCompiler::IsString()
{
register const sal_Unicode* p = cSymbol;
while ( *p )
p++;
xub_StrLen nLen = sal::static_int_cast<xub_StrLen>( p - cSymbol - 1 );
sal_Bool bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"'));
if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1)
{
SetError(errStringOverflow);
return sal_False;
}
if ( bQuote )
{
cSymbol[nLen] = '\0';
ScRawToken aToken;
aToken.SetString( cSymbol+1 );
pRawToken = aToken.Clone();
return sal_True;
}
return sal_False;
}
sal_Bool ScCompiler::IsPredetectedReference( const String& rName )
{
// Speedup documents with lots of broken references, e.g. sheet deleted.
xub_StrLen nPos = rName.SearchAscii( "#REF!");
if (nPos != STRING_NOTFOUND)
{
/* TODO: this may be enhanced by reusing scan information from
* NextSymbol(), the positions of quotes and special characters found
* there for $'sheet'.A1:... could be stored in a vector. We don't
* fully rescan here whether found positions are within single quotes
* for performance reasons. This code does not check for possible
* occurrences of insane "valid" sheet names like
* 'haha.#REF!1fooledyou' and will generate an error on such. */
if (nPos == 0)
return false; // #REF!.AB42 or #REF!42 or #REF!#REF!
sal_Unicode c = rName.GetChar(nPos-1); // before #REF!
if ('$' == c)
{
if (nPos == 1)
return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
c = rName.GetChar(nPos-2); // before $#REF!
}
sal_Unicode c2 = rName.GetChar(nPos+5); // after #REF!
switch (c)
{
case '.':
if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
return false; // sheet.#REF!42 or sheet.#REF!#REF!
break;
case ':':
if (mnPredetectedReference > 1 &&
('.' == c2 || '$' == c2 || '#' == c2 ||
('0' <= c2 && c2 <= '9')))
return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
break;
default:
if ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) &&
((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
return false; // AB#REF!: or AB#REF!
}
}
switch (mnPredetectedReference)
{
case 1:
return IsSingleReference( rName);
case 2:
return IsDoubleReference( rName);
}
return false;
}
sal_Bool ScCompiler::IsDoubleReference( const String& rName )
{
ScRange aRange( aPos, aPos );
const ScAddress::Details aDetails( pConv->meConv, aPos );
ScAddress::ExternalInfo aExtInfo;
sal_uInt16 nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
if( nFlags & SCA_VALID )
{
ScRawToken aToken;
ScComplexRefData aRef;
aRef.InitRange( aRange );
aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
if ( !(nFlags & SCA_VALID_TAB) )
aRef.Ref1.SetTabDeleted( sal_True ); // #REF!
aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 );
aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 );
aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 );
if ( !(nFlags & SCA_VALID_TAB2) )
aRef.Ref2.SetTabDeleted( sal_True ); // #REF!
aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 );
aRef.CalcRelFromAbs( aPos );
if (aExtInfo.mbExternal)
{
ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
aToken.SetExternalDoubleRef(
aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
}
else
{
aToken.SetDoubleReference(aRef);
}
pRawToken = aToken.Clone();
}
return ( nFlags & SCA_VALID ) != 0;
}
sal_Bool ScCompiler::IsSingleReference( const String& rName )
{
ScAddress aAddr( aPos );
const ScAddress::Details aDetails( pConv->meConv, aPos );
ScAddress::ExternalInfo aExtInfo;
sal_uInt16 nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
// Something must be valid in order to recognize Sheet1.blah or blah.a1
// as a (wrong) reference.
if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) )
{
ScRawToken aToken;
ScSingleRefData aRef;
aRef.InitAddress( aAddr );
aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
// the reference is really invalid
if( !( nFlags & SCA_VALID ) )
{
if( !( nFlags & SCA_VALID_COL ) )
aRef.nCol = MAXCOL+1;
if( !( nFlags & SCA_VALID_ROW ) )
aRef.nRow = MAXROW+1;
if( !( nFlags & SCA_VALID_TAB ) )
aRef.nTab = MAXTAB+3;
nFlags |= SCA_VALID;
}
aRef.CalcRelFromAbs( aPos );
if (aExtInfo.mbExternal)
{
ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
aToken.SetExternalSingleRef(
aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
}
else
aToken.SetSingleReference(aRef);
pRawToken = aToken.Clone();
}
return ( nFlags & SCA_VALID ) != 0;
}
sal_Bool ScCompiler::IsReference( const String& rName )
{
// Has to be called before IsValue
sal_Unicode ch1 = rName.GetChar(0);
sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' :
ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0) );
if ( ch1 == cDecSep )
return sal_False;
// Who was that imbecile introducing '.' as the sheet name separator!?!
if ( CharClass::isAsciiNumeric( ch1 ) )
{
// Numerical sheet name is valid.
// But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
// Don't create a #REF! of values. But also do not bail out on
// something like 3:3, meaning entire row 3.
do
{
const xub_StrLen nPos = ScGlobal::FindUnquoted( rName, '.');
if ( nPos == STRING_NOTFOUND )
{
if (ScGlobal::FindUnquoted( rName, ':') != STRING_NOTFOUND)
break; // may be 3:3, continue as usual
return sal_False;
}
sal_Unicode const * const pTabSep = rName.GetBuffer() + nPos;
sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
if ( !(ch2 == '$' || CharClass::isAsciiAlpha( ch2 )) )
return sal_False;
if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
&& (GetCharTableFlags( pTabSep[2] ) & SC_COMPILER_C_VALUE_EXP) )
{ // #91053#
// If it is an 1.E2 expression check if "1" is an existent sheet
// name. If so, a desired value 1.E2 would have to be entered as
// 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
// require numerical sheet names always being entered quoted, which
// is not desirable (too many 1999, 2000, 2001 sheets in use).
// Furthermore, XML files created with versions prior to SRC640e
// wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
// and would produce wrong formulas if the conditions here are met.
// If you can live with these restrictions you may remove the
// check and return an unconditional FALSE.
String aTabName( rName.Copy( 0, nPos ) );
SCTAB nTab;
if ( !pDoc->GetTable( aTabName, nTab ) )
return sal_False;
// If sheet "1" exists and the expression is 1.E+2 continue as
// usual, the ScRange/ScAddress parser will take care of it.
}
} while(0);
}
if (IsSingleReference( rName))
return true;
// Though the range operator is handled explicitly, when encountering
// something like Sheet1.A:A we will have to treat it as one entity if it
// doesn't pass as single cell reference.
if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
{
if (IsDoubleReference( rName))
return true;
// Now try with a symbol up to the range operator, rewind source
// position.
sal_Int32 nLen = mnRangeOpPosInSymbol;
while (cSymbol[++nLen])
;
cSymbol[mnRangeOpPosInSymbol] = 0;
nSrcPos -= static_cast<xub_StrLen>(nLen - mnRangeOpPosInSymbol);
mnRangeOpPosInSymbol = -1;
mbRewind = true;
return true; // end all checks
}
else
{
// Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel sickness,
// mnRangeOpPosInSymbol did not catch the range operator as it is
// within a quoted name.
switch (pConv->meConv)
{
case FormulaGrammar::CONV_XL_A1:
case FormulaGrammar::CONV_XL_R1C1:
case FormulaGrammar::CONV_XL_OOX:
if (rName.GetChar(0) == '\'' && IsDoubleReference( rName))
return true;
break;
default:
; // nothing
}
}
return false;
}
sal_Bool ScCompiler::IsMacro( const String& rName )
{
String aName( rName);
StarBASIC* pObj = 0;
SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
SfxApplication* pSfxApp = SFX_APP();
if( pDocSh )//XXX
pObj = pDocSh->GetBasic();
else
pObj = pSfxApp->GetBasic();
// ODFF recommends to store user-defined functions prefixed with "USER.",
// use only unprefixed name if encountered. BASIC doesn't allow '.' in a
// function name so a function "USER.FOO" could not exist, and macro check
// is assigned the lowest priority in function name check.
if (FormulaGrammar::isODFF( GetGrammar()) && aName.EqualsIgnoreCaseAscii( "USER.", 0, 5))
aName.Erase( 0, 5);
SbxMethod* pMeth = (SbxMethod*) pObj->Find( aName, SbxCLASS_METHOD );
if( !pMeth )
{
return sal_False;
}
// It really should be a BASIC function!
if( pMeth->GetType() == SbxVOID
|| ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
|| !pMeth->ISA(SbMethod) )
{
return sal_False;
}
ScRawToken aToken;
aToken.SetExternal( aName.GetBuffer() );
aToken.eOp = ocMacro;
pRawToken = aToken.Clone();
return sal_True;
}
sal_Bool ScCompiler::IsNamedRange( const String& rUpperName )
{
// IsNamedRange is called only from NextNewToken, with an upper-case string
sal_uInt16 n;
ScRangeName* pRangeName = pDoc->GetRangeName();
if (pRangeName->SearchNameUpper( rUpperName, n, aPos.Tab() ) )
{
ScRangeData* pData = (*pRangeName)[n];
ScRawToken aToken;
aToken.SetName( pData->GetIndex() );
pRawToken = aToken.Clone();
return sal_True;
}
if (pRangeName->SearchNameUpper( rUpperName, n ) )
{
ScRangeData* pData = (*pRangeName)[n];
ScRawToken aToken;
aToken.SetName( pData->GetIndex() );
pRawToken = aToken.Clone();
return sal_True;
}
else
return sal_False;
}
bool ScCompiler::IsExternalNamedRange( const String& rSymbol )
{
/* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
* correctly parses external named references in OOo, as required per RFE
* #i3740#, just that we can't store them in ODF yet. We will need an OASIS
* spec first. Until then don't pretend to support external names that
* wouldn't survive a save and reload cycle, return false instead. */
#if 0
if (!pConv)
return false;
String aFile, aName;
if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
return false;
ScRawToken aToken;
if (aFile.Len() > MAXSTRLEN || aName.Len() > MAXSTRLEN)
return false;
ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
pRefMgr->convertToAbsName(aFile);
sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
if (!pRefMgr->getRangeNameTokens(nFileId, aName).get())
// range name doesn't exist in the source document.
return false;
const String* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
aToken.SetExternalName(nFileId, pRealName ? *pRealName : aName);
pRawToken = aToken.Clone();
return true;
#else
(void)rSymbol;
return false;
#endif
}
sal_Bool ScCompiler::IsDBRange( const String& rName )
{
sal_uInt16 n;
ScDBCollection* pDBColl = pDoc->GetDBCollection();
if (pDBColl->SearchName( rName, n ) )
{
ScDBData* pData = (*pDBColl)[n];
ScRawToken aToken;
aToken.SetName( pData->GetIndex() );
aToken.eOp = ocDBArea;
pRawToken = aToken.Clone();
return sal_True;
}
else
return sal_False;
}
sal_Bool ScCompiler::IsColRowName( const String& rName )
{
sal_Bool bInList = sal_False;
sal_Bool bFound = sal_False;
ScSingleRefData aRef;
String aName( rName );
DeQuote( aName );
SCTAB nThisTab = aPos.Tab();
for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
{ // #50300# first check ranges on this sheet, in case of duplicated names
for ( short jRow=0; jRow<2 && !bInList; jRow++ )
{
ScRangePairList* pRL;
if ( !jRow )
pRL = pDoc->GetColNameRanges();
else
pRL = pDoc->GetRowNameRanges();
for ( ScRangePair* pR = pRL->First(); pR && !bInList; pR = pRL->Next() )
{
const ScRange& rNameRange = pR->GetRange(0);
if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
nThisTab <= rNameRange.aEnd.Tab()) )
continue; // for
ScCellIterator aIter( pDoc, rNameRange );
for ( ScBaseCell* pCell = aIter.GetFirst(); pCell && !bInList;
pCell = aIter.GetNext() )
{
// Don't crash if cell (via CompileNameFormula) encounters
// a formula cell without code and
// HasStringData/Interpret/Compile is executed and all that
// recursive..
// Furthermore, *this* cell won't be touched, since no RPN exists yet.
CellType eType = pCell->GetCellType();
sal_Bool bOk = sal::static_int_cast<sal_Bool>( (eType == CELLTYPE_FORMULA ?
((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
&& ((ScFormulaCell*)pCell)->aPos != aPos // noIter
: sal_True ) );
if ( bOk && pCell->HasStringData() )
{
String aStr;
switch ( eType )
{
case CELLTYPE_STRING:
((ScStringCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_FORMULA:
((ScFormulaCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_EDIT:
((ScEditCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_NONE:
case CELLTYPE_VALUE:
case CELLTYPE_NOTE:
case CELLTYPE_SYMBOLS:
#if DBG_UTIL
case CELLTYPE_DESTROYED:
#endif
; // nothing, prevent compiler warning
break;
}
if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
{
aRef.InitFlags();
aRef.nCol = aIter.GetCol();
aRef.nRow = aIter.GetRow();
aRef.nTab = aIter.GetTab();
if ( !jRow )
aRef.SetColRel( sal_True ); // ColName
else
aRef.SetRowRel( sal_True ); // RowName
aRef.CalcRelFromAbs( aPos );
bInList = bFound = sal_True;
}
}
}
}
}
}
if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
{ // search in current sheet
long nDistance = 0, nMax = 0;
long nMyCol = (long) aPos.Col();
long nMyRow = (long) aPos.Row();
sal_Bool bTwo = sal_False;
ScAddress aOne( 0, 0, aPos.Tab() );
ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
if ( pNameCache )
{
// #b6355215# use GetNameOccurences to collect all positions of aName on the sheet
// (only once), similar to the outer part of the loop in the "else" branch.
const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurences( aName, aPos.Tab() );
// Loop through the found positions, similar to the inner part of the loop in the "else" branch.
// The order of addresses in the vector is the same as from ScCellIterator.
ScAutoNameAddresses::const_iterator aEnd(rAddresses.end());
for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter )
{
ScAddress aAddress( *aAdrIter ); // cell address with an equal string
if ( bFound )
{ // stop if everything else is further away
if ( nMax < (long)aAddress.Col() )
break; // aIter
}
if ( aAddress != aPos )
{
// same treatment as in isEqual case below
SCCOL nCol = aAddress.Col();
SCROW nRow = aAddress.Row();
long nC = nMyCol - nCol;
long nR = nMyRow - nRow;
if ( bFound )
{
long nD = nC * nC + nR * nR;
if ( nD < nDistance )
{
if ( nC < 0 || nR < 0 )
{ // right or below
bTwo = sal_True;
aTwo.Set( nCol, nRow, aAddress.Tab() );
nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
nDistance = nD;
}
else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
{
// upper left, only if not further up than the
// current entry and nMyRow is below (CellIter
// runs column-wise)
bTwo = sal_False;
aOne.Set( nCol, nRow, aAddress.Tab() );
nMax = Max( nMyCol + nC, nMyRow + nR );
nDistance = nD;
}
}
}
else
{
aOne.Set( nCol, nRow, aAddress.Tab() );
nDistance = nC * nC + nR * nR;
nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
}
bFound = sal_True;
}
}
}
else
{
ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() )
{
if ( bFound )
{ // stop if everything else is further away
if ( nMax < (long)aIter.GetCol() )
break; // aIter
}
CellType eType = pCell->GetCellType();
sal_Bool bOk = sal::static_int_cast<sal_Bool>( (eType == CELLTYPE_FORMULA ?
((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
&& ((ScFormulaCell*)pCell)->aPos != aPos // noIter
: sal_True ) );
if ( bOk && pCell->HasStringData() )
{
String aStr;
switch ( eType )
{
case CELLTYPE_STRING:
((ScStringCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_FORMULA:
((ScFormulaCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_EDIT:
((ScEditCell*)pCell)->GetString( aStr );
break;
case CELLTYPE_NONE:
case CELLTYPE_VALUE:
case CELLTYPE_NOTE:
case CELLTYPE_SYMBOLS:
#if DBG_UTIL
case CELLTYPE_DESTROYED:
#endif
; // nothing, prevent compiler warning
break;
}
if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
{
SCCOL nCol = aIter.GetCol();
SCROW nRow = aIter.GetRow();
long nC = nMyCol - nCol;
long nR = nMyRow - nRow;
if ( bFound )
{
long nD = nC * nC + nR * nR;
if ( nD < nDistance )
{
if ( nC < 0 || nR < 0 )
{ // right or below
bTwo = sal_True;
aTwo.Set( nCol, nRow, aIter.GetTab() );
nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
nDistance = nD;
}
else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
{
// upper left, only if not further up than the
// current entry and nMyRow is below (CellIter
// runs column-wise)
bTwo = sal_False;
aOne.Set( nCol, nRow, aIter.GetTab() );
nMax = Max( nMyCol + nC, nMyRow + nR );
nDistance = nD;
}
}
}
else
{
aOne.Set( nCol, nRow, aIter.GetTab() );
nDistance = nC * nC + nR * nR;
nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
}
bFound = sal_True;
}
}
}
}
if ( bFound )
{
ScAddress aAdr;
if ( bTwo )
{
if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() )
aAdr = aOne; // upper left takes precedence
else
{
if ( nMyCol < (long)aOne.Col() )
{ // two to the right
if ( nMyRow >= (long)aTwo.Row() )
aAdr = aTwo; // directly right
else
aAdr = aOne;
}
else
{ // two below or below and right, take the nearest
long nC1 = nMyCol - aOne.Col();
long nR1 = nMyRow - aOne.Row();
long nC2 = nMyCol - aTwo.Col();
long nR2 = nMyRow - aTwo.Row();
if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
aAdr = aOne;
else
aAdr = aTwo;
}
}
}
else
aAdr = aOne;
aRef.InitAddress( aAdr );
if ( (aRef.nRow != MAXROW && pDoc->HasStringData(
aRef.nCol, aRef.nRow + 1, aRef.nTab ))
|| (aRef.nRow != 0 && pDoc->HasStringData(
aRef.nCol, aRef.nRow - 1, aRef.nTab )) )
aRef.SetRowRel( sal_True ); // RowName
else
aRef.SetColRel( sal_True ); // ColName
aRef.CalcRelFromAbs( aPos );
}
}
if ( bFound )
{
ScRawToken aToken;
aToken.SetSingleReference( aRef );
aToken.eOp = ocColRowName;
pRawToken = aToken.Clone();
return sal_True;
}
else
return sal_False;
}
sal_Bool ScCompiler::IsBoolean( const String& rName )
{
OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) );
if( iLook != mxSymbols->getHashMap()->end() &&
((*iLook).second == ocTrue ||
(*iLook).second == ocFalse) )
{
ScRawToken aToken;
aToken.SetOpCode( (*iLook).second );
pRawToken = aToken.Clone();
return sal_True;
}
else
return sal_False;
}
//---------------------------------------------------------------------------
void ScCompiler::AutoCorrectParsedSymbol()
{
xub_StrLen nPos = aCorrectedSymbol.Len();
if ( nPos )
{
nPos--;
const sal_Unicode cQuote = '\"';
const sal_Unicode cx = 'x';
const sal_Unicode cX = 'X';
sal_Unicode c1 = aCorrectedSymbol.GetChar( 0 );
sal_Unicode c2 = aCorrectedSymbol.GetChar( nPos );
if ( c1 == cQuote && c2 != cQuote )
{ // "...
// What's not a word doesn't belong to it.
// Don't be pedantic: c < 128 should be sufficient here.
while ( nPos && ((aCorrectedSymbol.GetChar(nPos) < 128) &&
((GetCharTableFlags( aCorrectedSymbol.GetChar(nPos) ) &
(SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) )
nPos--;
if ( nPos == MAXSTRLEN - 2 )
aCorrectedSymbol.SetChar( nPos, cQuote ); // '"' the 255th character
else
aCorrectedSymbol.Insert( cQuote, nPos + 1 );
bCorrected = sal_True;
}
else if ( c1 != cQuote && c2 == cQuote )
{ // ..."
aCorrectedSymbol.Insert( cQuote, 0 );
bCorrected = sal_True;
}
else if ( nPos == 0 && (c1 == cx || c1 == cX) )
{ // x => *
aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
bCorrected = sal_True;
}
else if ( (GetCharTableFlags( c1 ) & SC_COMPILER_C_CHAR_VALUE)
&& (GetCharTableFlags( c2 ) & SC_COMPILER_C_CHAR_VALUE) )
{
xub_StrLen nXcount;
if ( (nXcount = aCorrectedSymbol.GetTokenCount( cx )) > 1 )
{ // x => *
xub_StrLen nIndex = 0;
sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
cx, c, nIndex )) != STRING_NOTFOUND )
nIndex++;
bCorrected = sal_True;
}
if ( (nXcount = aCorrectedSymbol.GetTokenCount( cX )) > 1 )
{ // X => *
xub_StrLen nIndex = 0;
sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
cX, c, nIndex )) != STRING_NOTFOUND )
nIndex++;
bCorrected = sal_True;
}
}
else
{
String aSymbol( aCorrectedSymbol );
String aDoc;
xub_StrLen nPosition;
if ( aSymbol.GetChar(0) == '\''
&& ((nPosition = aSymbol.SearchAscii( "'#" )) != STRING_NOTFOUND) )
{ // Split off 'Doc'#, may be d:\... or whatever
aDoc = aSymbol.Copy( 0, nPosition + 2 );
aSymbol.Erase( 0, nPosition + 2 );
}
xub_StrLen nRefs = aSymbol.GetTokenCount( ':' );
sal_Bool bColons;
if ( nRefs > 2 )
{ // duplicated or too many ':'? B:2::C10 => B2:C10
bColons = sal_True;
xub_StrLen nIndex = 0;
String aTmp1( aSymbol.GetToken( 0, ':', nIndex ) );
xub_StrLen nLen1 = aTmp1.Len();
String aSym, aTmp2;
sal_Bool bLastAlp, bNextNum;
bLastAlp = bNextNum = sal_True;
xub_StrLen nStrip = 0;
xub_StrLen nCount = nRefs;
for ( xub_StrLen j=1; j<nCount; j++ )
{
aTmp2 = aSymbol.GetToken( 0, ':', nIndex );
xub_StrLen nLen2 = aTmp2.Len();
if ( nLen1 || nLen2 )
{
if ( nLen1 )
{
aSym += aTmp1;
bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
}
if ( nLen2 )
{
bNextNum = CharClass::isAsciiNumeric( aTmp2 );
if ( bLastAlp == bNextNum && nStrip < 1 )
{
// Must be alternating number/string, only
// strip within a reference.
nRefs--;
nStrip++;
}
else
{
xub_StrLen nSymLen = aSym.Len();
if ( nSymLen
&& (aSym.GetChar( nSymLen - 1 ) != ':') )
aSym += ':';
nStrip = 0;
}
bLastAlp = !bNextNum;
}
else
{ // ::
nRefs--;
if ( nLen1 )
{ // B10::C10 ? append ':' on next round
if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
nStrip++;
}
bNextNum = !bLastAlp;
}
aTmp1 = aTmp2;
nLen1 = nLen2;
}
else
nRefs--;
}
aSymbol = aSym;
aSymbol += aTmp1;
}
else
bColons = sal_False;
if ( nRefs && nRefs <= 2 )
{ // reference twisted? 4A => A4 etc.
String aTab[2], aRef[2];
const ScAddress::Details aDetails( pConv->meConv, aPos );
if ( nRefs == 2 )
{
aRef[0] = aSymbol.GetToken( 0, ':' );
aRef[1] = aSymbol.GetToken( 1, ':' );
}
else
aRef[0] = aSymbol;
sal_Bool bChanged = sal_False;
sal_Bool bOk = sal_True;
sal_uInt16 nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW;
for ( int j=0; j<nRefs; j++ )
{
xub_StrLen nTmp = 0;
xub_StrLen nDotPos = STRING_NOTFOUND;
while ( (nTmp = aRef[j].Search( '.', nTmp )) != STRING_NOTFOUND )
nDotPos = nTmp++; // the last one counts
if ( nDotPos != STRING_NOTFOUND )
{
aTab[j] = aRef[j].Copy( 0, nDotPos + 1 ); // with '.'
aRef[j].Erase( 0, nDotPos + 1 );
}
String aOld( aRef[j] );
String aStr2;
const sal_Unicode* p = aRef[j].GetBuffer();
while ( *p && CharClass::isAsciiNumeric( *p ) )
aStr2 += *p++;
aRef[j] = String( p );
aRef[j] += aStr2;
if ( bColons || aRef[j] != aOld )
{
bChanged = sal_True;
ScAddress aAdr;
bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
}
}
if ( bChanged && bOk )
{
aCorrectedSymbol = aDoc;
aCorrectedSymbol += aTab[0];
aCorrectedSymbol += aRef[0];
if ( nRefs == 2 )
{
aCorrectedSymbol += ':';
aCorrectedSymbol += aTab[1];
aCorrectedSymbol += aRef[1];
}
bCorrected = sal_True;
}
}
}
}
}
inline bool lcl_UpperAsciiOrI18n( String& rUpper, const String& rOrg, FormulaGrammar::Grammar eGrammar )
{
if (FormulaGrammar::isODFF( eGrammar ))
{
// ODFF has a defined set of English function names, avoid i18n
// overhead.
rUpper = rOrg;
rUpper.ToUpperAscii();
return true;
}
else
{
rUpper = ScGlobal::pCharClass->upper( rOrg );
return false;
}
}
sal_Bool ScCompiler::NextNewToken( bool bInArray )
{
bool bAllowBooleans = bInArray;
xub_StrLen nSpaces = NextSymbol(bInArray);
#if 0
fprintf( stderr, "NextNewToken '%s' (spaces = %d)\n",
rtl::OUStringToOString( cSymbol, RTL_TEXTENCODING_UTF8 ).getStr(), nSpaces );
#endif
if (!cSymbol[0])
return false;
if( nSpaces )
{
ScRawToken aToken;
aToken.SetOpCode( ocSpaces );
aToken.sbyte.cByte = (sal_uInt8) ( nSpaces > 255 ? 255 : nSpaces );
if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
{
SetError(errCodeOverflow);
return false;
}
}
// Short cut for references when reading ODF to speedup things.
if (mnPredetectedReference)
{
String aStr( cSymbol);
if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
{
/* TODO: it would be nice to generate a #REF! error here, which
* would need an ocBad token with additional error value.
* FormulaErrorToken wouldn't do because we want to preserve the
* original string containing partial valid address
* information. */
ScRawToken aToken;
aToken.SetString( aStr.GetBuffer() );
aToken.NewOpCode( ocBad );
pRawToken = aToken.Clone();
}
return true;
}
if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
!bAutoCorrect )
{ // #101100# special case to speed up broken [$]#REF documents
/* FIXME: ISERROR(#REF!) would be valid and sal_True and the formula to
* be processed as usual. That would need some special treatment,
* also in NextSymbol() because of possible combinations of
* #REF!.#REF!#REF! parts. In case of reading ODF that is all
* handled by IsPredetectedReference(), this case here remains for
* manual/API input. */
String aBad( aFormula.Copy( nSrcPos-1 ) );
eLastOp = pArr->AddBad( aBad )->GetOpCode();
return false;
}
if( IsString() )
return true;
bool bMayBeFuncName;
bool bAsciiNonAlnum; // operators, separators, ...
if ( cSymbol[0] < 128 )
{
bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] );
bAsciiNonAlnum = !bMayBeFuncName && !CharClass::isAsciiDigit( cSymbol[0] );
}
else
{
String aTmpStr( cSymbol[0] );
bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
bAsciiNonAlnum = false;
}
if ( bMayBeFuncName )
{
// a function name must be followed by a parenthesis
const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
while( *p == ' ' )
p++;
bMayBeFuncName = ( *p == '(' );
}
#if 0
fprintf( stderr, "Token '%s'\n",
rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
// #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
// IsReference().
String aUpper;
do
{
mbRewind = false;
const String aOrg( cSymbol );
if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray ))
return true;
aUpper.Erase();
bool bAsciiUpper = false;
if (bMayBeFuncName)
{
bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
if (IsOpCode( aUpper, bInArray ))
return true;
}
// Column 'DM' ("Deutsche Mark", German currency) couldn't be
// referred => IsReference() before IsValue().
// Preserve case of file names in external references.
if (IsReference( aOrg ))
{
if (mbRewind) // Range operator, but no direct reference.
continue; // do; up to range operator.
return true;
}
if (!aUpper.Len())
bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
// IsBoolean() before IsValue() to catch inline bools without the kludge
// for inline arrays.
if (bAllowBooleans && IsBoolean( aUpper ))
return true;
if (IsValue( aUpper ))
return true;
// User defined names and such do need i18n upper also in ODF.
if (bAsciiUpper)
aUpper = ScGlobal::pCharClass->upper( aOrg );
if (IsNamedRange( aUpper ))
return true;
// Preserve case of file names in external references.
if (IsExternalNamedRange( aOrg ))
return true;
if (IsDBRange( aUpper ))
return true;
if (IsColRowName( aUpper ))
return true;
if (bMayBeFuncName && IsMacro( aUpper ))
return true;
if (bMayBeFuncName && IsOpCode2( aUpper ))
return true;
} while (mbRewind);
if ( mbExtendedErrorDetection )
{
// set an error and end compilation
SetError( errNoName );
return false;
}
// Provide single token information and continue. Do not set an error, that
// would prematurely end compilation. Simple unknown names are handled by
// the interpreter.
ScGlobal::pCharClass->toLower( aUpper );
ScRawToken aToken;
aToken.SetString( aUpper.GetBuffer() );
aToken.NewOpCode( ocBad );
pRawToken = aToken.Clone();
if ( bAutoCorrect )
AutoCorrectParsedSymbol();
return true;
}
void ScCompiler::CreateStringFromXMLTokenArray( String& rFormula, String& rFormulaNmsp )
{
bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
DBG_ASSERT( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
if( pArr->GetLen() == nExpectedCount )
{
FormulaToken** ppTokens = pArr->GetArray();
// string tokens expected, GetString() will assert if token type is wrong
rFormula = ppTokens[ 0 ]->GetString();
if( bExternal )
rFormulaNmsp = ppTokens[ 1 ]->GetString();
}
}
ScTokenArray* ScCompiler::CompileString( const String& rFormula )
{
#if 0
fprintf( stderr, "CompileString '%s'\n",
rtl::OUStringToOString( rFormula, RTL_TEXTENCODING_UTF8 ).getStr() );
#endif
OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
SetGrammar( FormulaGrammar::GRAM_PODF );
ScTokenArray aArr;
pArr = &aArr;
aFormula = rFormula;
aFormula.EraseLeadingChars();
aFormula.EraseTrailingChars();
nSrcPos = 0;
bCorrected = sal_False;
if ( bAutoCorrect )
{
aCorrectedFormula.Erase();
aCorrectedSymbol.Erase();
}
sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
if( aFormula.GetChar(nSrcPos) == '=' )
{
nSrcPos++;
nForced++;
if ( bAutoCorrect )
aCorrectedFormula += '=';
}
if( aFormula.GetChar(nSrcPos) == '=' )
{
nSrcPos++;
nForced++;
if ( bAutoCorrect )
aCorrectedFormula += '=';
}
struct FunctionStack
{
OpCode eOp;
short nPar;
};
// FunctionStack only used if PODF!
bool bPODF = FormulaGrammar::isPODF( meGrammar);
const size_t nAlloc = 512;
FunctionStack aFuncs[ nAlloc ];
FunctionStack* pFunctionStack = (bPODF && rFormula.Len() > nAlloc ?
new FunctionStack[ rFormula.Len() ] : &aFuncs[0]);
pFunctionStack[0].eOp = ocNone;
pFunctionStack[0].nPar = 0;
size_t nFunction = 0;
short nBrackets = 0;
bool bInArray = false;
eLastOp = ocOpen;
while( NextNewToken( bInArray ) )
{
const OpCode eOp = pRawToken->GetOpCode();
switch (eOp)
{
case ocOpen:
{
++nBrackets;
if (bPODF)
{
++nFunction;
pFunctionStack[ nFunction ].eOp = eLastOp;
pFunctionStack[ nFunction ].nPar = 0;
}
}
break;
case ocClose:
{
if( !nBrackets )
{
SetError( errPairExpected );
if ( bAutoCorrect )
{
bCorrected = sal_True;
aCorrectedSymbol.Erase();
}
}
else
nBrackets--;
if (bPODF && nFunction)
--nFunction;
}
break;
case ocSep:
{
if (bPODF)
++pFunctionStack[ nFunction ].nPar;
}
break;
case ocArrayOpen:
{
if( bInArray )
SetError( errNestedArray );
else
bInArray = true;
// Don't count following column separator as parameter separator.
if (bPODF)
{
++nFunction;
pFunctionStack[ nFunction ].eOp = eOp;
pFunctionStack[ nFunction ].nPar = 0;
}
}
break;
case ocArrayClose:
{
if( bInArray )
{
bInArray = false;
}
else
{
SetError( errPairExpected );
if ( bAutoCorrect )
{
bCorrected = sal_True;
aCorrectedSymbol.Erase();
}
}
if (bPODF && nFunction)
--nFunction;
}
default:
break;
}
if( (eLastOp == ocSep ||
eLastOp == ocArrayRowSep ||
eLastOp == ocArrayColSep ||
eLastOp == ocArrayOpen) &&
(eOp == ocSep ||
eOp == ocArrayRowSep ||
eOp == ocArrayColSep ||
eOp == ocArrayClose) )
{
// FIXME: should we check for known functions with optional empty
// args so the correction dialog can do better?
if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
{
SetError(errCodeOverflow); break;
}
}
if (bPODF)
{
/* TODO: for now this is the only PODF adapter. If there were more,
* factor this out. */
// Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
if (eOp == ocSep &&
pFunctionStack[ nFunction ].eOp == ocAddress &&
pFunctionStack[ nFunction ].nPar == 3)
{
if (!static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep,ocSep)) ||
!static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
{
SetError(errCodeOverflow); break;
}
++pFunctionStack[ nFunction ].nPar;
}
}
FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( pRawToken->CreateToken());
if (!pNewToken)
{
SetError(errCodeOverflow); break;
}
else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush &&
pNewToken->GetType() == svSingleRef)
static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
eLastOp = pRawToken->GetOpCode();
if ( bAutoCorrect )
aCorrectedFormula += aCorrectedSymbol;
}
if ( mbCloseBrackets )
{
if( bInArray )
{
FormulaByteToken aToken( ocArrayClose );
if( !pArr->AddToken( aToken ) )
{
SetError(errCodeOverflow);
}
else if ( bAutoCorrect )
aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
}
FormulaByteToken aToken( ocClose );
while( nBrackets-- )
{
if( !pArr->AddToken( aToken ) )
{
SetError(errCodeOverflow); break;
}
if ( bAutoCorrect )
aCorrectedFormula += mxSymbols->getSymbol(ocClose);
}
}
if ( nForced >= 2 )
pArr->SetRecalcModeForced();
if (pFunctionStack != &aFuncs[0])
delete [] pFunctionStack;
// remember pArr, in case a subsequent CompileTokenArray() is executed.
ScTokenArray* pNew = new ScTokenArray( aArr );
pArr = pNew;
return pNew;
}
ScTokenArray* ScCompiler::CompileString( const String& rFormula, const String& rFormulaNmsp )
{
DBG_ASSERT( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || (rFormulaNmsp.Len() == 0),
"ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
{
ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
table::CellAddress aReferencePos;
ScUnoConversion::FillApiAddress( aReferencePos, aPos );
uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
ScTokenArray aTokenArray;
if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
{
// remember pArr, in case a subsequent CompileTokenArray() is executed.
ScTokenArray* pNew = new ScTokenArray( aTokenArray );
pArr = pNew;
return pNew;
}
}
catch( uno::Exception& )
{
}
// no success - fallback to some internal grammar and hope the best
return CompileString( rFormula );
}
sal_Bool ScCompiler::HandleRange()
{
ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( pToken->GetIndex() );
if (pRangeData)
{
sal_uInt16 nErr = pRangeData->GetErrCode();
if( nErr )
SetError( errNoName );
else if ( !bCompileForFAP )
{
ScTokenArray* pNew;
// #35168# put named formula into parentheses.
// #37680# But only if there aren't any yet, parenthetical
// ocSep doesn't work, e.g. SUM((...;...))
// or if not directly between ocSep/parenthesis,
// e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
// in short: if it isn't a self-contained expression.
FormulaToken* p1 = pArr->PeekPrevNoSpaces();
FormulaToken* p2 = pArr->PeekNextNoSpaces();
OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) );
OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) );
sal_Bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
sal_Bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
sal_Bool bAddPair = !(bBorder1 && bBorder2);
if ( bAddPair )
{
pNew = new ScTokenArray();
pNew->AddOpCode( ocClose );
PushTokenArray( pNew, sal_True );
pNew->Reset();
}
pNew = pRangeData->GetCode()->Clone();
PushTokenArray( pNew, sal_True );
if( pRangeData->HasReferences() )
{
SetRelNameReference();
MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
}
pNew->Reset();
if ( bAddPair )
{
pNew = new ScTokenArray();
pNew->AddOpCode( ocOpen );
PushTokenArray( pNew, sal_True );
pNew->Reset();
}
return GetToken();
}
}
else
SetError(errNoName);
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
{
// Handle external range names.
switch (_aToken.GetType())
{
case svExternalSingleRef:
case svExternalDoubleRef:
pArr->IncrementRefs();
break;
case svExternalName:
{
ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
const String* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
if (!pFile)
{
SetError(errNoName);
return true;
}
const String& rName = _aToken.GetString();
ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
_aToken.GetIndex(), rName, &aPos);
if (!xNew)
{
SetError(errNoName);
return true;
}
ScTokenArray* pNew = xNew->Clone();
PushTokenArray( pNew, true);
if (pNew->GetNextReference() != NULL)
{
SetRelNameReference();
MoveRelWrap(MAXCOL, MAXROW);
}
pNew->Reset();
return GetToken();
}
default:
DBG_ERROR("Wrong type for external reference!");
return sal_False;
}
return sal_True;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Append token to RPN code
//---------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//---------------------------------------------------------------------------
// RPN creation by recursion
//---------------------------------------------------------------------------
//-----------------------------------------------------------------------------
sal_Bool ScCompiler::HasModifiedRange()
{
pArr->Reset();
for ( FormulaToken* t = pArr->Next(); t; t = pArr->Next() )
{
OpCode eOpCode = t->GetOpCode();
if ( eOpCode == ocName )
{
ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex(t->GetIndex());
if (pRangeData && pRangeData->IsModified())
return sal_True;
}
else if ( eOpCode == ocDBArea )
{
ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(t->GetIndex());
if (pDBData && pDBData->IsModified())
return sal_True;
}
}
return sal_False;
}
//---------------------------------------------------------------------------
template< typename T, typename S >
S lcl_adjval( S& n, T pos, T max, sal_Bool bRel )
{
max++;
if( bRel )
n = sal::static_int_cast<S>( n + pos );
if( n < 0 )
n = sal::static_int_cast<S>( n + max );
else if( n >= max )
n = sal::static_int_cast<S>( n - max );
if( bRel )
n = sal::static_int_cast<S>( n - pos );
return n;
}
// reference of named range with relative references
void ScCompiler::SetRelNameReference()
{
pArr->Reset();
for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
t = static_cast<ScToken*>(pArr->GetNextReference()) )
{
ScSingleRefData& rRef1 = t->GetSingleRef();
if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
rRef1.SetRelName( sal_True );
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
rRef2.SetRelName( sal_True );
}
}
}
// Wrap-adjust relative references of a RangeName to current position,
// don't call for other token arrays!
void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow )
{
pArr->Reset();
for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
t = static_cast<ScToken*>(pArr->GetNextReference()) )
{
if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
else
ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
}
}
// static
// Wrap-adjust relative references of a RangeName to current position,
// don't call for other token arrays!
void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos,
SCCOL nMaxCol, SCROW nMaxRow )
{
rArr.Reset();
for( ScToken* t = static_cast<ScToken*>(rArr.GetNextReference()); t;
t = static_cast<ScToken*>(rArr.GetNextReference()) )
{
if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
else
ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
}
}
ScRangeData* ScCompiler::UpdateReference(UpdateRefMode eUpdateRefMode,
const ScAddress& rOldPos, const ScRange& r,
SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
sal_Bool& rChanged, sal_Bool& rRefSizeChanged )
{
rChanged = rRefSizeChanged = sal_False;
if ( eUpdateRefMode == URM_COPY )
{ // Normally nothing has to be done here since RelRefs are used, also
// SharedFormulas don't need any special handling, except if they
// wrapped around sheet borders.
// #67383# But ColRowName tokens pointing to a ColRow header which was
// copied along with this formula need to be updated to point to the
// copied header instead of the old position's new intersection.
ScToken* t;
pArr->Reset();
while( (t = static_cast<ScToken*>(pArr->GetNextColRowName())) != NULL )
{
ScSingleRefData& rRef = t->GetSingleRef();
rRef.CalcAbsIfRel( rOldPos );
ScAddress aNewRef( rRef.nCol + nDx, rRef.nRow + nDy, rRef.nTab + nDz );
if ( r.In( aNewRef ) )
{ // yes, this is URM_MOVE
if ( ScRefUpdate::Update( pDoc, URM_MOVE, aPos,
r, nDx, nDy, nDz,
SingleDoubleRefModifier( rRef ).Ref() )
!= UR_NOTHING
)
rChanged = sal_True;
}
}
// Check for SharedFormulas.
ScRangeData* pRangeData = NULL;
pArr->Reset();
for( FormulaToken* j = pArr->GetNextName(); j && !pRangeData;
j = pArr->GetNextName() )
{
if( j->GetOpCode() == ocName )
{
ScRangeData* pName = pDoc->GetRangeName()->FindIndex( j->GetIndex() );
if (pName && pName->HasType(RT_SHARED))
pRangeData = pName;
}
}
// Check SharedFormulas for wraps.
if (pRangeData)
{
ScRangeData* pName = pRangeData;
pRangeData = NULL;
pArr->Reset();
for( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()); t && !pRangeData;
t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) )
{
sal_Bool bRelName = (t->GetType() == svSingleRef ?
t->GetSingleRef().IsRelName() :
(t->GetDoubleRef().Ref1.IsRelName() ||
t->GetDoubleRef().Ref2.IsRelName()));
if (bRelName)
{
t->CalcAbsIfRel( rOldPos);
sal_Bool bValid = (t->GetType() == svSingleRef ?
t->GetSingleRef().Valid() :
t->GetDoubleRef().Valid());
// If the reference isn't valid, copying the formula
// wrapped it. Replace SharedFormula.
if (!bValid)
{
pRangeData = pName;
rChanged = sal_True;
}
}
}
}
return pRangeData;
}
else
{
/*
* Set SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE to 1 if we wanted to preserve as
* many shared formulas as possible instead of replacing them with direct code.
* Note that this may produce shared formula usage Excel doesn't understand,
* which would have to be adapted for in the export filter. Advisable as a long
* term goal, since it could decrease memory footprint.
*/
#define SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE 0
ScRangeData* pRangeData = NULL;
ScToken* t;
pArr->Reset();
while( (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL )
{
if( t->GetOpCode() == ocName )
{
ScRangeData* pName = pDoc->GetRangeName()->FindIndex( t->GetIndex() );
if (pName && pName->HasType(RT_SHAREDMOD))
{
pRangeData = pName; // maybe need a replacement of shared with own code
#if ! SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
rChanged = sal_True;
#endif
}
}
else if( t->GetType() != svIndex ) // it may be a DB area!!!
{
t->CalcAbsIfRel( rOldPos );
switch (t->GetType())
{
case svExternalSingleRef:
case svExternalDoubleRef:
// External references never change their positioning
// nor point to parts that will be removed or expanded.
// In fact, calling ScRefUpdate::Update() for URM_MOVE
// may have negative side effects. Simply adapt
// relative references to the new position.
t->CalcRelFromAbs( aPos);
break;
case svSingleRef:
{
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
aPos, r, nDx, nDy, nDz,
SingleDoubleRefModifier(
t->GetSingleRef()).Ref())
!= UR_NOTHING)
rChanged = sal_True;
}
break;
default:
{
ScComplexRefData& rRef = t->GetDoubleRef();
SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
aPos, r, nDx, nDy, nDz,
t->GetDoubleRef()) != UR_NOTHING)
{
rChanged = sal_True;
if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
rRefSizeChanged = sal_True;
}
}
}
}
}
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
sal_Bool bEasyShared, bPosInRange;
if ( !pRangeData )
bEasyShared = bPosInRange = sal_False;
else
{
bEasyShared = sal_True;
bPosInRange = r.In( eUpdateRefMode == URM_MOVE ? aPos : rOldPos );
}
#endif
pArr->Reset();
while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
{
if ( t->GetRef() != 1 )
{
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
bEasyShared = sal_False;
#endif
}
else
{ // if nRefCnt>1 it's already updated in token code
if ( t->GetType() == svSingleRef )
{
ScSingleRefData& rRef = t->GetSingleRef();
SingleDoubleRefModifier aMod( rRef );
if ( rRef.IsRelName() )
{
ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, aMod.Ref() );
rChanged = sal_True;
}
else
{
aMod.Ref().CalcAbsIfRel( rOldPos );
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
r, nDx, nDy, nDz, aMod.Ref() )
!= UR_NOTHING
)
rChanged = sal_True;
}
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
if ( bEasyShared )
{
const ScSingleRefData& rSRD = aMod.Ref().Ref1;
ScAddress aRef( rSRD.nCol, rSRD.nRow, rSRD.nTab );
if ( r.In( aRef ) != bPosInRange )
bEasyShared = sal_False;
}
#endif
}
else
{
ScComplexRefData& rRef = t->GetDoubleRef();
SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
if ( rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName() )
{
ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, rRef );
rChanged = sal_True;
}
else
{
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
r, nDx, nDy, nDz, rRef )
!= UR_NOTHING
)
{
rChanged = sal_True;
if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
{
rRefSizeChanged = sal_True;
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
bEasyShared = sal_False;
#endif
}
}
}
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
if ( bEasyShared )
{
ScRange aRef( rRef.Ref1.nCol, rRef.Ref1.nRow,
rRef.Ref1.nTab, rRef.Ref2.nCol, rRef.Ref2.nRow,
rRef.Ref2.nTab );
if ( r.In( aRef ) != bPosInRange )
bEasyShared = sal_False;
}
#endif
}
}
}
#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
if ( pRangeData )
{
if ( bEasyShared )
pRangeData = 0;
else
rChanged = sal_True;
}
#endif
#undef SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
return pRangeData;
}
}
sal_Bool ScCompiler::UpdateNameReference(UpdateRefMode eUpdateRefMode,
const ScRange& r,
SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
sal_Bool& rChanged, sal_Bool bSharedFormula)
{
sal_Bool bRelRef = sal_False; // set if relative reference
rChanged = sal_False;
pArr->Reset();
ScToken* t;
while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
{
SingleDoubleRefModifier aMod( *t );
ScComplexRefData& rRef = aMod.Ref();
bRelRef = rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() ||
rRef.Ref1.IsTabRel();
if (!bRelRef && t->GetType() == svDoubleRef)
bRelRef = rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() ||
rRef.Ref2.IsTabRel();
bool bUpdate = !rRef.Ref1.IsColRel() || !rRef.Ref1.IsRowRel() ||
!rRef.Ref1.IsTabRel();
if (!bUpdate && t->GetType() == svDoubleRef)
bUpdate = !rRef.Ref2.IsColRel() || !rRef.Ref2.IsRowRel() ||
!rRef.Ref2.IsTabRel();
if (!bSharedFormula)
{
// We cannot update names with sheet-relative references, they may
// be used on other sheets as well and the resulting reference
// would be wrong. This is a dilemma if col/row would need to be
// updated for the current usage.
// TODO: seems the only way out of this would be to not allow
// relative sheet references and have sheet-local names that can be
// copied along with sheets.
bUpdate = bUpdate && !rRef.Ref1.IsTabRel() && !rRef.Ref2.IsTabRel();
}
if (bUpdate)
{
rRef.CalcAbsIfRel( aPos);
if (ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, r,
nDx, nDy, nDz, rRef, ScRefUpdate::ABSOLUTE)
!= UR_NOTHING )
rChanged = sal_True;
}
}
return bRelRef;
}
void ScCompiler::UpdateSharedFormulaReference( UpdateRefMode eUpdateRefMode,
const ScAddress& rOldPos, const ScRange& r,
SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
if ( eUpdateRefMode == URM_COPY )
return ;
else
{
ScToken* t;
pArr->Reset();
while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
{
if( t->GetType() != svIndex ) // it may be a DB area!!!
{
t->CalcAbsIfRel( rOldPos );
// Absolute references have been already adjusted in the named
// shared formula itself prior to breaking the shared formula
// and calling this function. Don't readjust them again.
SingleDoubleRefModifier aMod( *t );
ScComplexRefData& rRef = aMod.Ref();
ScComplexRefData aBkp = rRef;
ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
r, nDx, nDy, nDz, rRef );
// restore absolute parts
if ( !aBkp.Ref1.IsColRel() )
{
rRef.Ref1.nCol = aBkp.Ref1.nCol;
rRef.Ref1.nRelCol = aBkp.Ref1.nRelCol;
rRef.Ref1.SetColDeleted( aBkp.Ref1.IsColDeleted() );
}
if ( !aBkp.Ref1.IsRowRel() )
{
rRef.Ref1.nRow = aBkp.Ref1.nRow;
rRef.Ref1.nRelRow = aBkp.Ref1.nRelRow;
rRef.Ref1.SetRowDeleted( aBkp.Ref1.IsRowDeleted() );
}
if ( !aBkp.Ref1.IsTabRel() )
{
rRef.Ref1.nTab = aBkp.Ref1.nTab;
rRef.Ref1.nRelTab = aBkp.Ref1.nRelTab;
rRef.Ref1.SetTabDeleted( aBkp.Ref1.IsTabDeleted() );
}
if ( t->GetType() == svDoubleRef )
{
if ( !aBkp.Ref2.IsColRel() )
{
rRef.Ref2.nCol = aBkp.Ref2.nCol;
rRef.Ref2.nRelCol = aBkp.Ref2.nRelCol;
rRef.Ref2.SetColDeleted( aBkp.Ref2.IsColDeleted() );
}
if ( !aBkp.Ref2.IsRowRel() )
{
rRef.Ref2.nRow = aBkp.Ref2.nRow;
rRef.Ref2.nRelRow = aBkp.Ref2.nRelRow;
rRef.Ref2.SetRowDeleted( aBkp.Ref2.IsRowDeleted() );
}
if ( !aBkp.Ref2.IsTabRel() )
{
rRef.Ref2.nTab = aBkp.Ref2.nTab;
rRef.Ref2.nRelTab = aBkp.Ref2.nRelTab;
rRef.Ref2.SetTabDeleted( aBkp.Ref2.IsTabDeleted() );
}
}
}
}
}
}
ScRangeData* ScCompiler::UpdateInsertTab( SCTAB nTable, sal_Bool bIsName )
{
ScRangeData* pRangeData = NULL;
SCTAB nPosTab = aPos.Tab(); // _after_ incremented!
SCTAB nOldPosTab = ((nPosTab > nTable) ? (nPosTab - 1) : nPosTab);
sal_Bool bIsRel = sal_False;
ScToken* t;
pArr->Reset();
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
while( t )
{
if( t->GetOpCode() == ocName )
{
if (!bIsName)
{
ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
if (pName && pName->HasType(RT_SHAREDMOD))
pRangeData = pName;
}
}
else if( t->GetType() != svIndex ) // it may be a DB area!!!
{
if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
{ // of names only adjust absolute references
ScSingleRefData& rRef = t->GetSingleRef();
if ( rRef.IsTabRel() )
{
rRef.nTab = rRef.nRelTab + nOldPosTab;
if ( rRef.nTab < 0 )
rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
}
if (nTable <= rRef.nTab)
++rRef.nTab;
rRef.nRelTab = rRef.nTab - nPosTab;
}
else
bIsRel = sal_True;
if ( t->GetType() == svDoubleRef )
{
if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
{ // of names only adjust absolute references
ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
if ( rRef.IsTabRel() )
{
rRef.nTab = rRef.nRelTab + nOldPosTab;
if ( rRef.nTab < 0 )
rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
}
if (nTable <= rRef.nTab)
++rRef.nTab;
rRef.nRelTab = rRef.nTab - nPosTab;
}
else
bIsRel = sal_True;
}
if ( bIsName && bIsRel )
pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
}
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
}
if ( !bIsName )
{
pArr->Reset();
while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
{
if ( t->GetRef() == 1 )
{
ScSingleRefData& rRef1 = t->GetSingleRef();
if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef1.IsTabRel() )
{
rRef1.nTab = rRef1.nRelTab + nOldPosTab;
if ( rRef1.nTab < 0 )
rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + pDoc->GetTableCount() ); // was a wrap
}
if (nTable <= rRef1.nTab)
++rRef1.nTab;
rRef1.nRelTab = rRef1.nTab - nPosTab;
}
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef2.IsTabRel() )
{
rRef2.nTab = rRef2.nRelTab + nOldPosTab;
if ( rRef2.nTab < 0 )
rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + pDoc->GetTableCount() ); // was a wrap
}
if (nTable <= rRef2.nTab)
++rRef2.nTab;
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
}
}
}
}
return pRangeData;
}
ScRangeData* ScCompiler::UpdateDeleteTab(SCTAB nTable, sal_Bool /* bIsMove */, sal_Bool bIsName,
sal_Bool& rChanged)
{
ScRangeData* pRangeData = NULL;
SCTAB nTab, nTab2;
SCTAB nPosTab = aPos.Tab(); // _after_ decremented!
SCTAB nOldPosTab = ((nPosTab >= nTable) ? (nPosTab + 1) : nPosTab);
rChanged = sal_False;
sal_Bool bIsRel = sal_False;
ScToken* t;
pArr->Reset();
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
while( t )
{
if( t->GetOpCode() == ocName )
{
if (!bIsName)
{
ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
if (pName && pName->HasType(RT_SHAREDMOD))
pRangeData = pName;
}
rChanged = sal_True;
}
else if( t->GetType() != svIndex ) // it may be a DB area!!!
{
if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
{ // of names only adjust absolute references
ScSingleRefData& rRef = t->GetSingleRef();
if ( rRef.IsTabRel() )
nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
else
nTab = rRef.nTab;
if ( nTable < nTab )
{
rRef.nTab = nTab - 1;
rChanged = sal_True;
}
else if ( nTable == nTab )
{
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( rRef2.IsTabRel() )
nTab2 = rRef2.nRelTab + nOldPosTab;
else
nTab2 = rRef2.nTab;
if ( nTab == nTab2
|| (nTab+1) >= pDoc->GetTableCount() )
{
rRef.nTab = MAXTAB+1;
rRef.SetTabDeleted( sal_True );
}
// else: nTab later points to what's nTable+1 now
// => area shrunk
}
else
{
rRef.nTab = MAXTAB+1;
rRef.SetTabDeleted( sal_True );
}
rChanged = sal_True;
}
rRef.nRelTab = rRef.nTab - nPosTab;
}
else
bIsRel = sal_True;
if ( t->GetType() == svDoubleRef )
{
if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
{ // of names only adjust absolute references
ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
if ( rRef.IsTabRel() )
nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
else
nTab = rRef.nTab;
if ( nTable < nTab )
{
rRef.nTab = nTab - 1;
rChanged = sal_True;
}
else if ( nTable == nTab )
{
if ( !t->GetDoubleRef().Ref1.IsTabDeleted() )
rRef.nTab = nTab - 1; // shrink area
else
{
rRef.nTab = MAXTAB+1;
rRef.SetTabDeleted( sal_True );
}
rChanged = sal_True;
}
rRef.nRelTab = rRef.nTab - nPosTab;
}
else
bIsRel = sal_True;
}
if ( bIsName && bIsRel )
pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
}
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
}
if ( !bIsName )
{
pArr->Reset();
while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
{
if ( t->GetRef() == 1 )
{
ScSingleRefData& rRef1 = t->GetSingleRef();
if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef1.IsTabRel() )
nTab = rRef1.nTab = rRef1.nRelTab + nOldPosTab;
else
nTab = rRef1.nTab;
if ( nTable < nTab )
{
rRef1.nTab = nTab - 1;
rChanged = sal_True;
}
else if ( nTable == nTab )
{
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( rRef2.IsTabRel() )
nTab2 = rRef2.nRelTab + nOldPosTab;
else
nTab2 = rRef2.nTab;
if ( nTab == nTab2
|| (nTab+1) >= pDoc->GetTableCount() )
{
rRef1.nTab = MAXTAB+1;
rRef1.SetTabDeleted( sal_True );
}
// else: nTab later points to what's nTable+1 now
// => area shrunk
}
else
{
rRef1.nTab = MAXTAB+1;
rRef1.SetTabDeleted( sal_True );
}
rChanged = sal_True;
}
rRef1.nRelTab = rRef1.nTab - nPosTab;
}
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef2.IsTabRel() )
nTab = rRef2.nTab = rRef2.nRelTab + nOldPosTab;
else
nTab = rRef2.nTab;
if ( nTable < nTab )
{
rRef2.nTab = nTab - 1;
rChanged = sal_True;
}
else if ( nTable == nTab )
{
if ( !rRef1.IsTabDeleted() )
rRef2.nTab = nTab - 1; // shrink area
else
{
rRef2.nTab = MAXTAB+1;
rRef2.SetTabDeleted( sal_True );
}
rChanged = sal_True;
}
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
}
}
}
}
return pRangeData;
}
// aPos.Tab() must be already adjusted!
ScRangeData* ScCompiler::UpdateMoveTab( SCTAB nOldTab, SCTAB nNewTab,
bool bIsName, bool bOnlyUpdateOwnTab /*= FALSE*/)
{
ScRangeData* pRangeData = NULL;
SCsTAB nTab;
SCTAB nStart, nEnd;
short nDir; // direction in which others move
if ( nOldTab < nNewTab )
{
nDir = -1;
nStart = nOldTab;
nEnd = nNewTab;
}
else
{
nDir = 1;
nStart = nNewTab;
nEnd = nOldTab;
}
SCTAB nPosTab = aPos.Tab(); // current sheet
SCTAB nOldPosTab; // previously it was this one
if ( nPosTab == nNewTab )
nOldPosTab = nOldTab; // look, it's me!
else if ( nPosTab < nStart || nEnd < nPosTab )
nOldPosTab = nPosTab; // wasn't moved
else
nOldPosTab = nPosTab - nDir; // moved by one
sal_Bool bIsRel = sal_False;
ScToken* t;
pArr->Reset();
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
while( t )
{
if( t->GetOpCode() == ocName )
{
if (!bIsName)
{
ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
if (pName && pName->HasType(RT_SHAREDMOD))
pRangeData = pName;
}
}
else if( t->GetType() != svIndex ) // it may be a DB area!!!
{
ScSingleRefData& rRef1 = t->GetSingleRef();
if ( !(bIsName && rRef1.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef1.IsTabRel() )
nTab = rRef1.nRelTab + nOldPosTab;
else
nTab = rRef1.nTab;
if ( nTab == nOldTab )
rRef1.nTab = nNewTab;
else if ( nStart <= nTab && nTab <= nEnd && !bOnlyUpdateOwnTab)
rRef1.nTab = nTab + nDir;
rRef1.nRelTab = rRef1.nTab - nPosTab;
}
else
bIsRel = sal_True;
if ( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( !(bIsName && rRef2.IsTabRel()) )
{ // of names only adjust absolute references
if ( rRef2.IsTabRel() )
nTab = rRef2.nRelTab + nOldPosTab;
else
nTab = rRef2.nTab;
if ( nTab == nOldTab )
rRef2.nTab = nNewTab;
else if ( nStart <= nTab && nTab <= nEnd && !bOnlyUpdateOwnTab)
rRef2.nTab = nTab + nDir;
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
else
bIsRel = sal_True;
SCsTAB nTab1, nTab2;
if ( rRef1.IsTabRel() )
nTab1 = rRef1.nRelTab + nPosTab;
else
nTab1 = rRef1.nTab;
if ( rRef2.IsTabRel() )
nTab2 = rRef2.nRelTab + nPosTab;
else
nTab2 = rRef1.nTab;
if ( nTab2 < nTab1 )
{ // PutInOrder
rRef1.nTab = nTab2;
rRef2.nTab = nTab1;
rRef1.nRelTab = rRef1.nTab - nPosTab;
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
}
if ( bIsName && bIsRel )
pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
}
if (bIsName)
t = static_cast<ScToken*>(pArr->GetNextReference());
else
t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
}
if ( !bIsName )
{
SCsTAB nMaxTabMod = (SCsTAB) pDoc->GetTableCount();
pArr->Reset();
while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
{
if ( t->GetRef() == 1 )
{
ScSingleRefData& rRef1 = t->GetSingleRef();
if ( rRef1.IsRelName() && rRef1.IsTabRel() )
{ // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
nTab = rRef1.nRelTab + nPosTab;
if ( nTab < 0 )
nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
else if ( nTab > nMaxTab )
nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
rRef1.nRelTab = nTab - nPosTab;
}
else
{
if ( rRef1.IsTabRel() )
nTab = rRef1.nRelTab + nOldPosTab;
else
nTab = rRef1.nTab;
if ( nTab == nOldTab )
rRef1.nTab = nNewTab;
else if ( nStart <= nTab && nTab <= nEnd )
rRef1.nTab = nTab + nDir;
rRef1.nRelTab = rRef1.nTab - nPosTab;
}
if( t->GetType() == svDoubleRef )
{
ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
if ( rRef2.IsRelName() && rRef2.IsTabRel() )
{ // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
nTab = rRef2.nRelTab + nPosTab;
if ( nTab < 0 )
nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
else if ( nTab > nMaxTab )
nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
rRef2.nRelTab = nTab - nPosTab;
}
else
{
if ( rRef2.IsTabRel() )
nTab = rRef2.nRelTab + nOldPosTab;
else
nTab = rRef2.nTab;
if ( nTab == nOldTab )
rRef2.nTab = nNewTab;
else if ( nStart <= nTab && nTab <= nEnd )
rRef2.nTab = nTab + nDir;
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
SCsTAB nTab1, nTab2;
if ( rRef1.IsTabRel() )
nTab1 = rRef1.nRelTab + nPosTab;
else
nTab1 = rRef1.nTab;
if ( rRef2.IsTabRel() )
nTab2 = rRef2.nRelTab + nPosTab;
else
nTab2 = rRef1.nTab;
if ( nTab2 < nTab1 )
{ // PutInOrder
rRef1.nTab = nTab2;
rRef2.nTab = nTab1;
rRef1.nRelTab = rRef1.nTab - nPosTab;
rRef2.nRelTab = rRef2.nTab - nPosTab;
}
}
}
}
}
return pRangeData;
}
void ScCompiler::CreateStringFromExternal(rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP)
{
FormulaToken* t = pTokenP;
ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
switch (t->GetType())
{
case svExternalName:
{
const String *pStr = pRefMgr->getExternalFileName(t->GetIndex());
String aFileName = pStr ? *pStr : ScGlobal::GetRscString(STR_NO_NAME_REF);
rBuffer.append(pConv->makeExternalNameStr( aFileName, t->GetString()));
}
break;
case svExternalSingleRef:
pConv->makeExternalRefStr(
rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetSingleRef(), pRefMgr);
break;
case svExternalDoubleRef:
pConv->makeExternalRefStr(
rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetDoubleRef(), pRefMgr);
break;
default:
// warning, not error, otherwise we may end up with a never
// ending message box loop if this was the cursor cell to be redrawn.
DBG_WARNING("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
}
}
void ScCompiler::CreateStringFromMatrix( rtl::OUStringBuffer& rBuffer,
FormulaToken* pTokenP)
{
const ScMatrix* pMatrix = static_cast<ScToken*>(pTokenP)->GetMatrix();
SCSIZE nC, nMaxC, nR, nMaxR;
pMatrix->GetDimensions( nMaxC, nMaxR);
rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
for( nR = 0 ; nR < nMaxR ; nR++)
{
if( nR > 0)
{
rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
}
for( nC = 0 ; nC < nMaxC ; nC++)
{
if( nC > 0)
{
rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
}
if( pMatrix->IsValue( nC, nR ) )
{
ScMatValType nType;
const ScMatrixValue* pVal = pMatrix->Get( nC, nR, nType);
if( nType == SC_MATVAL_BOOLEAN )
AppendBoolean( rBuffer, pVal->GetBoolean() );
else
{
sal_uInt16 nErr = pVal->GetError();
if( nErr )
rBuffer.append( ScGlobal::GetErrorString( nErr ) );
else
AppendDouble( rBuffer, pVal->fVal );
}
}
else if( pMatrix->IsEmpty( nC, nR ) )
;
else if( pMatrix->IsString( nC, nR ) )
AppendString( rBuffer, pMatrix->GetString( nC, nR ) );
}
}
rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
}
void ScCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
{
const OpCode eOp = _pTokenP->GetOpCode();
ScSingleRefData& rRef = static_cast<ScToken*>(_pTokenP)->GetSingleRef();
ScComplexRefData aRef;
aRef.Ref1 = aRef.Ref2 = rRef;
if ( eOp == ocColRowName )
{
rRef.CalcAbsIfRel( aPos );
if ( pDoc->HasStringData( rRef.nCol, rRef.nRow, rRef.nTab ) )
{
String aStr;
pDoc->GetString( rRef.nCol, rRef.nRow, rRef.nTab, aStr );
EnQuote( aStr );
rBuffer.append(aStr);
}
else
{
rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
pConv->MakeRefStr (rBuffer, *this, aRef, sal_True );
}
}
else
pConv->MakeRefStr( rBuffer, *this, aRef, sal_True );
}
// -----------------------------------------------------------------------------
void ScCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
{
pConv->MakeRefStr( rBuffer, *this, static_cast<ScToken*>(_pTokenP)->GetDoubleRef(), sal_False );
}
// -----------------------------------------------------------------------------
void ScCompiler::CreateStringFromIndex(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
{
const OpCode eOp = _pTokenP->GetOpCode();
rtl::OUStringBuffer aBuffer;
switch ( eOp )
{
case ocName:
{
ScRangeData* pData = pDoc->GetRangeName()->FindIndex(_pTokenP->GetIndex());
if (pData)
{
if (pData->HasType(RT_SHARED))
pData->UpdateSymbol( aBuffer, aPos, GetGrammar());
else
aBuffer.append(pData->GetName());
}
}
break;
case ocDBArea:
{
ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(_pTokenP->GetIndex());
if (pDBData)
aBuffer.append(pDBData->GetName());
}
break;
default:
; // nothing
}
if ( aBuffer.getLength() )
rBuffer.append(aBuffer);
else
rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
}
// -----------------------------------------------------------------------------
void ScCompiler::LocalizeString( String& rName )
{
ScGlobal::GetAddInCollection()->LocalizeString( rName );
}
// -----------------------------------------------------------------------------
sal_Bool ScCompiler::IsImportingXML() const
{
return pDoc->IsImportingXML();
}
// Put quotes around string if non-alphanumeric characters are contained,
// quote characters contained within are escaped by '\\'.
sal_Bool ScCompiler::EnQuote( String& rStr )
{
sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.Len() );
if ( !CharClass::isNumericType( nType )
&& CharClass::isAlphaNumericType( nType ) )
return sal_False;
xub_StrLen nPos = 0;
while ( (nPos = rStr.Search( '\'', nPos)) != STRING_NOTFOUND )
{
rStr.Insert( '\\', nPos );
nPos += 2;
}
rStr.Insert( '\'', 0 );
rStr += '\'';
return sal_True;
}
sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
{
return pConv->getSpecialSymbol(eType);
}
void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
{
// All known AddIn functions.
sheet::FormulaOpCodeMapEntry aEntry;
aEntry.Token.OpCode = ocExternal;
ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
const long nCount = pColl->GetFuncCount();
for (long i=0; i < nCount; ++i)
{
const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
if (pFuncData)
{
if ( _bIsEnglish )
{
String aName;
if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
aEntry.Name = aName;
else
aEntry.Name = pFuncData->GetUpperName();
}
else
aEntry.Name = pFuncData->GetUpperLocal();
aEntry.Token.Data <<= ::rtl::OUString( pFuncData->GetOriginalName());
_rVec.push_back( aEntry);
}
}
// FIXME: what about those old non-UNO AddIns?
}
// -----------------------------------------------------------------------------
sal_Bool ScCompiler::HandleSingleRef()
{
ScSingleRefData& rRef = static_cast<ScToken*>((FormulaToken*)pToken)->GetSingleRef();
rRef.CalcAbsIfRel( aPos );
if ( !rRef.Valid() )
{
SetError( errNoRef );
return sal_True;
}
SCCOL nCol = rRef.nCol;
SCROW nRow = rRef.nRow;
SCTAB nTab = rRef.nTab;
ScAddress aLook( nCol, nRow, nTab );
sal_Bool bColName = rRef.IsColRel();
SCCOL nMyCol = aPos.Col();
SCROW nMyRow = aPos.Row();
sal_Bool bInList = sal_False;
sal_Bool bValidName = sal_False;
ScRangePairList* pRL = (bColName ?
pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
ScRange aRange;
for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
{
if ( pR->GetRange(0).In( aLook ) )
{
bInList = bValidName = sal_True;
aRange = pR->GetRange(1);
if ( bColName )
{
aRange.aStart.SetCol( nCol );
aRange.aEnd.SetCol( nCol );
}
else
{
aRange.aStart.SetRow( nRow );
aRange.aEnd.SetRow( nRow );
}
break; // for
}
}
if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
{ // automagically or created by copying and NamePos isn't in list
sal_Bool bString = pDoc->HasStringData( nCol, nRow, nTab );
if ( !bString && !pDoc->GetCell( aLook ) )
bString = sal_True; // empty cell is ok
if ( bString )
{ //! coresponds with ScInterpreter::ScColRowNameAuto()
bValidName = sal_True;
if ( bColName )
{ // ColName
SCROW nStartRow = nRow + 1;
if ( nStartRow > MAXROW )
nStartRow = MAXROW;
SCROW nMaxRow = MAXROW;
if ( nMyCol == nCol )
{ // formula cell in same column
if ( nMyRow == nStartRow )
{ // take remainder under name cell
nStartRow++;
if ( nStartRow > MAXROW )
nStartRow = MAXROW;
}
else if ( nMyRow > nStartRow )
{ // from name cell down to formula cell
nMaxRow = nMyRow - 1;
}
}
for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
{ // next defined ColNameRange below limits row
const ScRange& rRange = pR->GetRange(1);
if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
{ // identical column range
SCROW nTmp = rRange.aStart.Row();
if ( nStartRow < nTmp && nTmp <= nMaxRow )
nMaxRow = nTmp - 1;
}
}
aRange.aStart.Set( nCol, nStartRow, nTab );
aRange.aEnd.Set( nCol, nMaxRow, nTab );
}
else
{ // RowName
SCCOL nStartCol = nCol + 1;
if ( nStartCol > MAXCOL )
nStartCol = MAXCOL;
SCCOL nMaxCol = MAXCOL;
if ( nMyRow == nRow )
{ // formula cell in same row
if ( nMyCol == nStartCol )
{ // take remainder right from name cell
nStartCol++;
if ( nStartCol > MAXCOL )
nStartCol = MAXCOL;
}
else if ( nMyCol > nStartCol )
{ // from name cell right to formula cell
nMaxCol = nMyCol - 1;
}
}
for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
{ // next defined RowNameRange to the right limits column
const ScRange& rRange = pR->GetRange(1);
if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
{ // identical row range
SCCOL nTmp = rRange.aStart.Col();
if ( nStartCol < nTmp && nTmp <= nMaxCol )
nMaxCol = nTmp - 1;
}
}
aRange.aStart.Set( nStartCol, nRow, nTab );
aRange.aEnd.Set( nMaxCol, nRow, nTab );
}
}
}
if ( bValidName )
{
// And now the magic to distinguish between a range and a single
// cell thereof, which is picked position-dependent of the formula
// cell. If a direct neighbor is a binary operator (ocAdd, ...) a
// SingleRef matching the column/row of the formula cell is
// generated. A ocColRowName or ocIntersect as a neighbor results
// in a range. Special case: if label is valid for a single cell, a
// position independent SingleRef is generated.
sal_Bool bSingle = (aRange.aStart == aRange.aEnd);
sal_Bool bFound;
if ( bSingle )
bFound = sal_True;
else
{
FormulaToken* p1 = pArr->PeekPrevNoSpaces();
FormulaToken* p2 = pArr->PeekNextNoSpaces();
// begin/end of a formula => single
OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd );
OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd );
if ( eOp1 != ocColRowName && eOp1 != ocIntersect
&& eOp2 != ocColRowName && eOp2 != ocIntersect )
{
if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
(SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
bSingle = sal_True;
}
if ( bSingle )
{ // column and/or row must match range
if ( bColName )
{
bFound = (aRange.aStart.Row() <= nMyRow
&& nMyRow <= aRange.aEnd.Row());
if ( bFound )
aRange.aStart.SetRow( nMyRow );
}
else
{
bFound = (aRange.aStart.Col() <= nMyCol
&& nMyCol <= aRange.aEnd.Col());
if ( bFound )
aRange.aStart.SetCol( nMyCol );
}
}
else
bFound = sal_True;
}
if ( !bFound )
SetError(errNoRef);
else if ( !bCompileForFAP )
{
ScTokenArray* pNew = new ScTokenArray();
if ( bSingle )
{
ScSingleRefData aRefData;
aRefData.InitAddress( aRange.aStart );
if ( bColName )
aRefData.SetColRel( sal_True );
else
aRefData.SetRowRel( sal_True );
aRefData.CalcRelFromAbs( aPos );
pNew->AddSingleReference( aRefData );
}
else
{
ScComplexRefData aRefData;
aRefData.InitRange( aRange );
if ( bColName )
{
aRefData.Ref1.SetColRel( sal_True );
aRefData.Ref2.SetColRel( sal_True );
}
else
{
aRefData.Ref1.SetRowRel( sal_True );
aRefData.Ref2.SetRowRel( sal_True );
}
aRefData.CalcRelFromAbs( aPos );
if ( bInList )
pNew->AddDoubleReference( aRefData );
else
{ // automagically
pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) );
}
}
PushTokenArray( pNew, sal_True );
pNew->Reset();
return GetToken();
}
}
else
SetError(errNoName);
return sal_True;
}
// -----------------------------------------------------------------------------
sal_Bool ScCompiler::HandleDbData()
{
ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex( pToken->GetIndex() );
if ( !pDBData )
SetError(errNoName);
else if ( !bCompileForFAP )
{
ScComplexRefData aRefData;
aRefData.InitFlags();
pDBData->GetArea( (SCTAB&) aRefData.Ref1.nTab,
(SCCOL&) aRefData.Ref1.nCol,
(SCROW&) aRefData.Ref1.nRow,
(SCCOL&) aRefData.Ref2.nCol,
(SCROW&) aRefData.Ref2.nRow);
aRefData.Ref2.nTab = aRefData.Ref1.nTab;
aRefData.CalcRelFromAbs( aPos );
ScTokenArray* pNew = new ScTokenArray();
pNew->AddDoubleReference( aRefData );
PushTokenArray( pNew, sal_True );
pNew->Reset();
return GetToken();
}
return sal_True;
}
String GetScCompilerNativeSymbol( OpCode eOp )
{
return ScCompiler::GetNativeSymbol( eOp );
}
// -----------------------------------------------------------------------------
FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
{
return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );
}