summaryrefslogtreecommitdiff
path: root/sc/source/core/tool/compiler.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/tool/compiler.cxx')
-rw-r--r--sc/source/core/tool/compiler.cxx5426
1 files changed, 5426 insertions, 0 deletions
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
new file mode 100644
index 000000000000..e4eab3fa510f
--- /dev/null
+++ b/sc/source/core/tool/compiler.cxx
@@ -0,0 +1,5426 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_sc.hxx"
+
+// INCLUDE ---------------------------------------------------------------
+
+#include <sfx2/app.hxx>
+#include <sfx2/objsh.hxx>
+#include <basic/sbmeth.hxx>
+#include <basic/sbstar.hxx>
+#include <svl/zforlist.hxx>
+#include <sal/macros.h>
+#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());
+ }
+ }
+}
+
+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, false ));
+ if (aIntName.Len())
+ {
+ return true;
+ }
+ return false; // no valid function name
+}
+
+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);
+ }
+}
+
+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
+}
+
+
+//-----------------------------------------------------------------------------
+
+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[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 char 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 equal 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. At this
+ // point, the current char must equal the separator
+ // char.
+
+ i = j;
+ bInName = true;
+ aTmpName.Append(c); // Keep the separator as part of the name.
+ 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;
+ aTmpName.Append(c); // Keep the separator as part of the name.
+ }
+ 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;
+ }
+
+ xub_StrLen nNameLen = aTmpName.Len();
+ if (nNameLen < 2)
+ {
+ // Name must be at least 2-char long (separator plus name).
+ return false;
+ }
+
+ if (aTmpName.GetChar(0) != cSep)
+ {
+ // 1st char of the name must equal the separator.
+ return false;
+ }
+
+ sal_Unicode cLast = aTmpName.GetChar(nNameLen-1);
+ if (cLast == sal_Unicode('!'))
+ {
+ // Check against #REF!.
+ if (aTmpName.EqualsAscii("#REF!"))
+ return false;
+ }
+
+ rFile = aTmpFile;
+ rName = aTmpName.Copy(1); // Skip the first char as it is always the separator.
+ 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( OUString& rTabName2, const OUString& rTabName1,
+ const vector<OUString>& rTabNames, const ScComplexRefData& rRef )
+{
+ SCsTAB nTabSpan = rRef.Ref2.nTab - rRef.Ref1.nTab;
+ if (nTabSpan > 0)
+ {
+ size_t nCount = rTabNames.size();
+ vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
+ vector<OUString>::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 );
+ }
+
+ virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const
+ {
+ return mpCharTable[static_cast<sal_uInt8>(c)];
+ }
+};
+
+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
+ {
+ if ( aString.GetChar(0) == '\'' )
+ { // "'Doc'#Tab"
+ xub_StrLen nPos = ScGlobal::FindUnquoted( aString, SC_COMPILER_FILE_TAB_SEP);
+ if (nPos != STRING_NOTFOUND && nPos > 0 && aString.GetChar(nPos-1) == '\'')
+ {
+ aDoc = aString.Copy( 0, nPos + 1 );
+ aString.Erase( 0, nPos + 1 );
+ aDoc = INetURLObject::decode( aDoc, INET_HEX_ESCAPE,
+ INetURLObject::DECODE_UNAMBIGUOUS );
+ }
+ else
+ aDoc.Erase();
+ }
+ 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!
+ 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(':'));
+
+ OUString aLastTabName;
+ bool bDisplayTabName = (aRef.Ref1.nTab != aRef.Ref2.nTab);
+ if (bDisplayTabName)
+ {
+ // Get the name of the last table.
+ vector<OUString> aTabNames;
+ pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
+ if (aTabNames.empty())
+ {
+ OSL_TRACE( "ConventionOOO_A1::makeExternalRefStrImpl: no sheet names for document ID %s", nFileId);
+ }
+
+ if (!lcl_getLastTabName(aLastTabName, rTabName, aTabNames, aRef))
+ {
+ OSL_FAIL( "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"
+ if ( rTabName.GetChar(0) == '\'' )
+ {
+ xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
+ if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
+ {
+ 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 OUString& rTabName,
+ const vector<OUString>& rTabNames,
+ const ScComplexRefData& rRef )
+ {
+ OUString 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<OUString> 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<OUString> 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);
+ }
+
+ virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const
+ {
+ sal_uLong nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
+ if (c == '-' && cLast == '[')
+ // '-' can occur within a reference string only after '[' e.g. R[-1]C.
+ nFlags |= SC_COMPILER_C_IDENT;
+ return nFlags;
+ }
+};
+
+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 += '\'';
+ }
+}
+
+//---------------------------------------------------------------------------
+
+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, cLast );
+
+ // 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], 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], 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], 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], 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 );
+ pSym = &cSymbol[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 = SAL_N_ELEMENTS(aOdffAliases);
+ 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 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 = 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 );
+
+ 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 false;
+
+ if (nType == NUMBERFORMAT_LOGICAL)
+ {
+ const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
+ while( *p == ' ' )
+ p++;
+ if (*p == '(')
+ return false; // Boolean function instead.
+ }
+
+ if( nType == NUMBERFORMAT_TEXT )
+ // HACK: number too big!
+ SetError( errIllegalArgument );
+ ScRawToken aToken;
+ aToken.SetDouble( fVal );
+ pRawToken = aToken.Clone();
+ return sal_True;
+ }
+ else
+ return 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 false;
+ }
+ if ( bQuote )
+ {
+ cSymbol[nLen] = '\0';
+ ScRawToken aToken;
+ aToken.SetString( cSymbol+1 );
+ pRawToken = aToken.Clone();
+ return sal_True;
+ }
+ return 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 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 false;
+ }
+ sal_Unicode const * const pTabSep = rName.GetBuffer() + nPos;
+ sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
+ if ( !(ch2 == '$' || CharClass::isAsciiAlpha( ch2 )) )
+ return false;
+ if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
+ && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & 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 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 false;
+ }
+ // It really should be a BASIC function!
+ if( pMeth->GetType() == SbxVOID
+ || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
+ || !pMeth->ISA(SbMethod) )
+ {
+ return 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
+
+ // try local names first
+ bool bGlobal = false;
+ ScRangeName* pRangeName = pDoc->GetRangeName(aPos.Tab());
+ const ScRangeData* pData = pRangeName->findByUpperName(rUpperName);
+ if (!pData)
+ {
+ pRangeName = pDoc->GetRangeName();
+ if (pRangeName)
+ pData = pRangeName->findByUpperName(rUpperName);
+ if (pData)
+ bGlobal = true;
+ }
+
+ if (pData)
+ {
+ ScRawToken aToken;
+ aToken.SetName(bGlobal, pData->GetIndex());
+ pRawToken = aToken.Clone();
+ return true;
+ }
+ else
+ return 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 (!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;
+}
+
+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(true, pData->GetIndex()); // DB range is always global.
+ aToken.eOp = ocDBArea;
+ pRawToken = aToken.Clone();
+ return sal_True;
+ }
+ else
+ return false;
+}
+
+sal_Bool ScCompiler::IsColRowName( const String& rName )
+{
+ sal_Bool bInList = false;
+ sal_Bool bFound = false;
+ ScSingleRefData aRef;
+ String aName( rName );
+ DeQuote( aName );
+ SCTAB nThisTab = aPos.Tab();
+ for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
+ { // 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 ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
+ {
+ ScRangePair* pR = (*pRL)[iPair];
+ 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 = false;
+ ScAddress aOne( 0, 0, aPos.Tab() );
+ ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
+
+ ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
+ if ( pNameCache )
+ {
+ // use GetNameOccurrences 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->GetNameOccurrences( 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 = 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 = 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 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 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 );
+ sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol.GetChar( nPos-1 ) : 0;
+ 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), aCorrectedSymbol.GetChar(nPos-1)) &
+ (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, 0 ) & SC_COMPILER_C_CHAR_VALUE)
+ && (GetCharTableFlags( c2, c2p ) & 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 = 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 = 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 (!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 )
+ { // special case to speed up broken [$]#REF documents
+ /* FIXME: ISERROR(#REF!) would be valid and 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 == '(' );
+ }
+
+ // 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 )
+{
+ 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 = 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 == ocClose ||
+ 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 = NULL;
+
+ bool bGlobal = pToken->GetByte();
+ if (bGlobal)
+ // global named range.
+ pRangeData = pDoc->GetRangeName()->findByIndex( pToken->GetIndex() );
+ else
+ {
+ // sheet local named range.
+ ScRangeName* pRN = pDoc->GetRangeName(aPos.Tab());
+ if (pRN)
+ pRangeData = pRN->findByIndex( pToken->GetIndex() );
+ }
+
+ if (pRangeData)
+ {
+ sal_uInt16 nErr = pRangeData->GetErrCode();
+ if( nErr )
+ SetError( errNoName );
+ else if ( !bCompileForFAP )
+ {
+ ScTokenArray* pNew;
+ // put named formula into parentheses.
+ // 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:
+ OSL_FAIL("Wrong type for external reference!");
+ return 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()->findByIndex(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 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() );
+ }
+}
+
+// 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,
+ bool& rChanged, bool& rRefSizeChanged )
+{
+ rChanged = rRefSizeChanged = 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.
+ // 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 = 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()->findByIndex( 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 = 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()->findByIndex( 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 = 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 = 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 = true;
+ if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
+ rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
+ rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
+ rRefSizeChanged = true;
+ }
+ }
+ }
+ }
+ }
+#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
+ sal_Bool bEasyShared, bPosInRange;
+ if ( !pRangeData )
+ bEasyShared = bPosInRange = 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 = 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 = true;
+ }
+ else
+ {
+ aMod.Ref().CalcAbsIfRel( rOldPos );
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
+ r, nDx, nDy, nDz, aMod.Ref() )
+ != UR_NOTHING
+ )
+ rChanged = 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 = 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 = true;
+ }
+ else
+ {
+ if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
+ r, nDx, nDy, nDz, rRef )
+ != UR_NOTHING
+ )
+ {
+ rChanged = true;
+ if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
+ rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
+ rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
+ {
+ rRefSizeChanged = true;
+#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
+ bEasyShared = 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 = false;
+ }
+#endif
+ }
+ }
+ }
+#if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
+ if ( pRangeData )
+ {
+ if ( bEasyShared )
+ pRangeData = 0;
+ else
+ rChanged = 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 = false; // set if relative reference
+ rChanged = 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 = 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()->findByIndex(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 = false;
+ sal_Bool bIsRel = 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()->findByIndex(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,
+ sal_Bool bIsName )
+{
+ 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 = 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()->findByIndex(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 )
+ 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 )
+ 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 ) )
+ {
+ if (pMatrix->IsBoolean(nC, nR))
+ AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
+ else
+ {
+ sal_uInt16 nErr = pMatrix->GetError(nC, nR);
+ if (nErr)
+ rBuffer.append(ScGlobal::GetErrorString(nErr));
+ else
+ AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
+ }
+ }
+ 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(), false );
+}
+// -----------------------------------------------------------------------------
+void ScCompiler::CreateStringFromIndex(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
+{
+ const OpCode eOp = _pTokenP->GetOpCode();
+ rtl::OUStringBuffer aBuffer;
+ switch ( eOp )
+ {
+ case ocName:
+ {
+ bool bGlobal = _pTokenP->GetByte();
+ ScRangeData* pData = NULL;
+ if (bGlobal)
+ // global named range.
+ pData = pDoc->GetRangeName()->findByIndex(_pTokenP->GetIndex());
+ else
+ {
+ // sheet local named range.
+ ScRangeName* pRN = pDoc->GetRangeName(aPos.Tab());
+ if (pRN)
+ pData = pRN->findByIndex(_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 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*>(pToken.get())->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 = false;
+ sal_Bool bValidName = false;
+ ScRangePairList* pRL = (bColName ?
+ pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
+ ScRange aRange;
+ for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ {
+ ScRangePair* pR = (*pRL)[i];
+ 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 ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ { // next defined ColNameRange below limits row
+ ScRangePair* pR = (*pRL)[i];
+ 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 ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
+ { // next defined RowNameRange to the right limits column
+ ScRangePair* pR = (*pRL)[i];
+ 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;
+}
+
+// -----------------------------------------------------------------------------
+FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
+{
+ return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */