merge the formfield patch from ooo-build
[ooovba.git] / sc / source / core / tool / compiler.cxx
blob1c85ea56bd7b2b40e5f0e0299717482a8ae1e817
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: compiler.cxx,v $
10 * $Revision: 1.82.18.2 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 // INCLUDE ---------------------------------------------------------------
36 #include <sfx2/app.hxx>
37 #include <sfx2/objsh.hxx>
38 #include <basic/sbmeth.hxx>
39 #include <basic/sbstar.hxx>
40 #include <svtools/zforlist.hxx>
41 #include <tools/rcid.h>
42 #include <tools/rc.hxx>
43 #include <tools/solar.h>
44 #include <unotools/charclass.hxx>
45 #include <com/sun/star/lang/Locale.hpp>
46 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
47 #include <com/sun/star/sheet/FormulaLanguage.hpp>
48 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
49 #include <comphelper/processfactory.hxx>
50 #include <unotools/transliterationwrapper.hxx>
51 #include <tools/urlobj.hxx>
52 #include <rtl/math.hxx>
53 #include <ctype.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <math.h>
58 #include "compiler.hxx"
59 #include "rangenam.hxx"
60 #include "dbcolect.hxx"
61 #include "document.hxx"
62 #include "callform.hxx"
63 #include "addincol.hxx"
64 #include "refupdat.hxx"
65 #include "scresid.hxx"
66 #include "sc.hrc"
67 #include "globstr.hrc"
68 #include "cell.hxx"
69 #include "dociter.hxx"
70 #include "docoptio.hxx"
71 #include <formula/errorcodes.hxx>
72 #include "parclass.hxx"
73 #include "autonamecache.hxx"
74 #include "externalrefmgr.hxx"
75 #include "rangeutl.hxx"
76 #include "convuno.hxx"
77 #include "tokenuno.hxx"
78 #include "formulaparserpool.hxx"
80 using namespace formula;
81 using namespace ::com::sun::star;
82 using rtl::OUString;
83 using ::std::vector;
85 #if OSL_DEBUG_LEVEL > 1
86 // For some unknown reason the identical dbg_dump utilities in
87 // tools/source/string/debugprint.cxx tend to crash when called from within
88 // gdb. Having them here also comes handy as libtl*.so doesn't have to be
89 // replaced.
90 const char* dbg_sc_dump( const ByteString & rStr )
92 static ByteString aStr;
93 aStr = rStr;
94 aStr.Append(static_cast<char>(0));
95 return aStr.GetBuffer();
97 const char* dbg_sc_dump( const UniString & rStr )
99 return dbg_sc_dump(ByteString(rStr, RTL_TEXTENCODING_UTF8));
101 const char* dbg_sc_dump( const sal_Unicode * pBuf )
103 return dbg_sc_dump( UniString( pBuf));
105 const char* dbg_sc_dump( const sal_Unicode c )
107 return dbg_sc_dump( UniString( c));
109 #endif
111 CharClass* ScCompiler::pCharClassEnglish = NULL;
112 const ScCompiler::Convention* ScCompiler::pConventions[ ] = { NULL, NULL, NULL, NULL, NULL, NULL };
114 enum ScanState
116 ssGetChar,
117 ssGetBool,
118 ssGetValue,
119 ssGetString,
120 ssSkipString,
121 ssGetIdent,
122 ssGetReference,
123 ssSkipReference,
124 ssStop
127 static const sal_Char* pInternal[ 5 ] = { "GAME", "SPEW", "TTT", "STARCALCTEAM", "ANTWORT" };
129 using namespace ::com::sun::star::i18n;
131 /////////////////////////////////////////////////////////////////////////
135 class ScCompilerRecursionGuard
137 private:
138 short& rRecursion;
139 public:
140 ScCompilerRecursionGuard( short& rRec )
141 : rRecursion( rRec ) { ++rRecursion; }
142 ~ScCompilerRecursionGuard() { --rRecursion; }
146 void ScCompiler::fillFromAddInMap( NonConstOpCodeMapPtr xMap,FormulaGrammar::Grammar _eGrammar ) const
148 size_t nSymbolOffset;
149 switch( _eGrammar )
151 case FormulaGrammar::GRAM_PODF:
152 nSymbolOffset = offsetof( AddInMap, pUpper);
153 break;
154 default:
155 case FormulaGrammar::GRAM_ODFF:
156 nSymbolOffset = offsetof( AddInMap, pODFF);
157 break;
158 case FormulaGrammar::GRAM_ENGLISH:
159 nSymbolOffset = offsetof( AddInMap, pEnglish);
160 break;
162 const AddInMap* pMap = GetAddInMap();
163 const AddInMap* const pStop = pMap + GetAddInMapCount();
164 for ( ; pMap < pStop; ++pMap)
166 char const * const * ppSymbol =
167 reinterpret_cast< char const * const * >(
168 reinterpret_cast< char const * >(pMap) + nSymbolOffset);
169 xMap->putExternal( String::CreateFromAscii( *ppSymbol),
170 String::CreateFromAscii( pMap->pOriginal));
174 void ScCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr xMap ) const
176 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
177 long nCount = pColl->GetFuncCount();
178 for (long i=0; i < nCount; ++i)
180 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
181 if (pFuncData)
182 xMap->putExternalSoftly( pFuncData->GetUpperName(),
183 pFuncData->GetOriginalName());
187 void ScCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr xMap ) const
189 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
190 long nCount = pColl->GetFuncCount();
191 for (long i=0; i < nCount; ++i)
193 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
194 if (pFuncData)
196 String aName;
197 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
198 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
199 else
200 xMap->putExternalSoftly( pFuncData->GetUpperName(),
201 pFuncData->GetOriginalName());
207 #ifdef erGENERATEMAPPING
208 // Run in en-US UI by calling from within gdb, edit pODFF entries afterwards.
209 void dbg_call_generateMappingODFF()
211 // static ScCompiler members
212 fprintf( stdout, "%s", "static struct AddInMap\n{\n const char* pODFF;\n const char* pEnglish;\n bool bMapDupToInternal;\n const char* pOriginal;\n const char* pUpper;\n} maAddInMap[];\n");
213 fprintf( stdout, "%s", "static const AddInMap* GetAddInMap();\n");
214 fprintf( stdout, "%s", "static size_t GetAddInMapCount();\n");
215 fprintf( stdout, "addinfuncdata___:%s", "ScCompiler::AddInMap ScCompiler::maAddInMap[] =\n{\n");
216 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
217 long nCount = pColl->GetFuncCount();
218 for (long i=0; i < nCount; ++i)
220 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
221 if (pFuncData)
223 #define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer())
224 String aL = pFuncData->GetUpperLocal();
225 String aP = pFuncData->GetOriginalName();
226 String aU = pFuncData->GetUpperName();
227 fprintf( stdout, "addinfuncdata%3ld: { \"%s\", \"%s\", false, \"%s\", \"%s\" },\n",
228 i, out(aL), out(aL), out(aP), out(aU));
229 #undef out
232 fprintf( stdout, "addinfuncdata___:%s", "};\n");
233 fprintf( stdout, "%s", "\n// static\nconst ScCompiler::AddInMap* ScCompiler::GetAddInMap()\n{\n return maAddInMap;\n}\n");
234 fprintf( stdout, "%s", "\n// static\nsize_t ScCompiler::GetAddInMapCount()\n{\n return sizeof(maAddInMap)/sizeof(maAddInMap[0]);\n}\n");
235 fflush( stdout);
237 #endif // erGENERATEMAPPING
239 #ifdef erGENERATEMAPPINGDIFF
240 // Run in en-US UI by calling from within gdb.
241 void dbg_call_generateMappingDiff()
243 using namespace ::com::sun::star::sheet;
244 ScCompiler::OpCodeMapPtr xPODF = ScCompiler::GetOpCodeMap(
245 FormulaLanguage::ODF_11);
246 ScCompiler::OpCodeMapPtr xODFF = ScCompiler::GetOpCodeMap(
247 FormulaLanguage::ODFF);
248 ScCompiler::OpCodeMapPtr xENUS = ScCompiler::GetOpCodeMap(
249 FormulaLanguage::ENGLISH);
250 USHORT nPODF = xPODF->getSymbolCount();
251 USHORT nODFF = xODFF->getSymbolCount();
252 USHORT nENUS = xENUS->getSymbolCount();
253 printf( "%s\n", "This is a semicolon separated file, you may import it as such to Calc.");
254 printf( "%s\n", "Spreadsheet functions name differences between PODF (ODF < 1.2) and ODFF (ODF >= 1.2), plus English UI names.");
255 printf( "\nInternal OpCodes; PODF: %d; ODFF: %d; ENUS: %d\n",
256 (int)nPODF, (int)nODFF, (int)nENUS);
257 USHORT nMax = ::std::max( ::std::max( nPODF, nODFF), nENUS);
258 #define out(rStr) (ByteString( rStr, RTL_TEXTENCODING_UTF8).GetBuffer())
259 for (USHORT i=0; i < nMax; ++i)
261 const String& rPODF = xPODF->getSymbol(static_cast<OpCode>(i));
262 const String& rODFF = xODFF->getSymbol(static_cast<OpCode>(i));
263 const String& rENUS = xENUS->getSymbol(static_cast<OpCode>(i));
264 if (rPODF != rODFF)
265 printf( "%d;%s;%s;%s\n", (int)i, out(rPODF), out(rODFF), out(rENUS));
267 // Actually they should all differ, so we could simply list them all, but
268 // this is correct and we would find odd things, if any.
269 const ExternalHashMap* pPODF = xPODF->getReverseExternalHashMap();
270 const ExternalHashMap* pODFF = xODFF->getReverseExternalHashMap();
271 const ExternalHashMap* pENUS = xENUS->getReverseExternalHashMap();
272 printf( "\n%s\n", "Add-In mapping");
273 for (ExternalHashMap::const_iterator it = pPODF->begin(); it != pPODF->end(); ++it)
275 ExternalHashMap::const_iterator iLookODFF = pODFF->find( (*it).first);
276 ExternalHashMap::const_iterator iLookENUS = pENUS->find( (*it).first);
277 String aNative( iLookENUS == pENUS->end() ?
278 String::CreateFromAscii( "ENGLISH_SYMBOL_NOT_FOUND") :
279 (*iLookENUS).second);
280 if (iLookODFF == pODFF->end())
281 printf( "NOT FOUND;%s;;%s\n", out((*it).first), out(aNative));
282 else if((*it).second == (*iLookODFF).second) // upper equal
283 printf( "EQUAL;%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative));
284 else
285 printf( ";%s;%s;%s\n", out((*it).first), out((*iLookODFF).second), out(aNative));
287 #undef out
288 fflush( stdout);
290 #endif // erGENERATEMAPPINGDIFF
292 // static
293 void ScCompiler::DeInit()
295 if (pCharClassEnglish)
297 delete pCharClassEnglish;
298 pCharClassEnglish = NULL;
302 bool ScCompiler::IsEnglishSymbol( const String& rName )
304 // function names are always case-insensitive
305 String aUpper( ScGlobal::pCharClass->upper( rName ) );
307 // 1. built-in function name
308 OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper );
309 if ( eOp != ocNone )
311 return true;
313 // 2. old add in functions
314 USHORT nIndex;
315 if ( ScGlobal::GetFuncCollection()->SearchFunc( aUpper, nIndex ) )
317 return true;
320 // 3. new (uno) add in functions
321 String aIntName(ScGlobal::GetAddInCollection()->FindFunction( aUpper, FALSE ));
322 if (aIntName.Len())
324 return true;
326 return false; // no valid function name
329 // static
330 void ScCompiler::InitCharClassEnglish()
332 ::com::sun::star::lang::Locale aLocale(
333 OUString( RTL_CONSTASCII_USTRINGPARAM( "en")),
334 OUString( RTL_CONSTASCII_USTRINGPARAM( "US")),
335 OUString());
336 pCharClassEnglish = new CharClass(
337 ::comphelper::getProcessServiceFactory(), aLocale);
341 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
343 DBG_ASSERT( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
344 if (eGrammar == GetGrammar())
345 return; // nothing to be done
347 if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
349 meGrammar = eGrammar;
350 mxSymbols = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
352 else
354 FormulaGrammar::Grammar eMyGrammar = eGrammar;
355 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
356 OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
357 DBG_ASSERT( xMap, "ScCompiler::SetGrammar: unknown formula language");
358 if (!xMap)
360 xMap = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
361 eMyGrammar = xMap->getGrammar();
364 // Save old grammar for call to SetGrammarAndRefConvention().
365 FormulaGrammar::Grammar eOldGrammar = GetGrammar();
366 // This also sets the grammar associated with the map!
367 SetFormulaLanguage( xMap);
369 // Override if necessary.
370 if (eMyGrammar != GetGrammar())
371 SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
375 void ScCompiler::SetEncodeUrlMode( EncodeUrlMode eMode )
377 meEncodeUrlMode = eMode;
380 ScCompiler::EncodeUrlMode ScCompiler::GetEncodeUrlMode() const
382 return meEncodeUrlMode;
385 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
387 if (xMap.get())
389 mxSymbols = xMap;
390 if (mxSymbols->isEnglish())
392 if (!pCharClassEnglish)
393 InitCharClassEnglish();
394 pCharClass = pCharClassEnglish;
396 else
397 pCharClass = ScGlobal::pCharClass;
398 SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
403 void ScCompiler::SetGrammarAndRefConvention(
404 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
406 meGrammar = eNewGrammar; //! SetRefConvention needs the new grammar set!
407 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
408 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
410 if (pDoc)
411 SetRefConvention( pDoc->GetAddressConvention());
412 else
413 SetRefConvention( pConvOOO_A1);
415 else
416 SetRefConvention( eConv );
419 String ScCompiler::FindAddInFunction( const String& rUpperName, BOOL bLocalFirst ) const
421 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=FALSE for english
425 #ifdef erDEBUG
426 void dbg_call_testcreatemapping()
428 using namespace ::com::sun::star::sheet;
429 ScCompiler::OpCodeMapPtr xMap = ScCompiler::GetOpCodeMap( FormulaLanguage::ODFF);
430 xMap->createSequenceOfAvailableMappings( FormulaMapGroup::FUNCTIONS);
432 #endif
434 //-----------------------------------------------------------------------------
436 ScCompiler::Convention::~Convention()
438 delete [] mpCharTable;
439 mpCharTable = NULL;
442 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
444 meConv( eConv )
446 int i;
447 ULONG *t= new ULONG [128];
449 ScCompiler::pConventions[ meConv ] = this;
450 mpCharTable = t;
452 for (i = 0; i < 128; i++)
453 t[i] = SC_COMPILER_C_ILLEGAL;
455 /* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
456 /* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
457 if (FormulaGrammar::CONV_ODF == meConv)
458 /* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
459 /* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
460 /* # */ t[35] = SC_COMPILER_C_WORD_SEP;
461 /* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
462 if (FormulaGrammar::CONV_ODF == meConv)
463 /* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
464 /* % */ t[37] = SC_COMPILER_C_VALUE;
465 /* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
466 /* ' */ t[39] = SC_COMPILER_C_NAME_SEP;
467 /* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
468 /* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
469 /* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
470 /* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
471 /* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE;
472 /* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
473 /* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
474 /* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
476 for (i = 48; i < 58; i++)
477 /* 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;
479 /* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD;
480 /* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
481 /* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
482 /* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
483 /* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
484 /* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_NAME;
485 /* @ */ // FREE
487 for (i = 65; i < 91; i++)
488 /* 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;
490 if (FormulaGrammar::CONV_ODF == meConv)
492 /* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET;
493 /* \ */ // FREE
494 /* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET;
496 else
498 /* [ */ // FREE
499 /* \ */ // FREE
500 /* ] */ // FREE
502 /* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
503 /* _ */ 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;
504 /* ` */ // FREE
506 for (i = 97; i < 123; i++)
507 /* 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;
509 /* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open
510 /* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific)
511 /* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close
512 /* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific
513 /* 127 */ // FREE
515 if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
517 /* */ t[32] |= SC_COMPILER_C_WORD;
518 /* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD;
519 /* " */ t[34] |= SC_COMPILER_C_WORD;
520 /* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP);
521 /* # */ t[35] |= SC_COMPILER_C_WORD;
522 /* % */ t[37] |= SC_COMPILER_C_WORD;
523 /* ' */ t[39] |= SC_COMPILER_C_WORD;
525 /* % */ t[37] |= SC_COMPILER_C_WORD;
526 /* & */ t[38] |= SC_COMPILER_C_WORD;
527 /* ' */ t[39] |= SC_COMPILER_C_WORD;
528 /* ( */ t[40] |= SC_COMPILER_C_WORD;
529 /* ) */ t[41] |= SC_COMPILER_C_WORD;
530 /* * */ t[42] |= SC_COMPILER_C_WORD;
531 /* + */ t[43] |= SC_COMPILER_C_WORD;
532 #if 0 /* this really needs to be locale specific. */
533 /* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
534 #else
535 /* , */ t[44] |= SC_COMPILER_C_WORD;
536 #endif
537 /* - */ t[45] |= SC_COMPILER_C_WORD;
539 /* ; */ t[59] |= SC_COMPILER_C_WORD;
540 /* < */ t[60] |= SC_COMPILER_C_WORD;
541 /* = */ t[61] |= SC_COMPILER_C_WORD;
542 /* > */ t[62] |= SC_COMPILER_C_WORD;
543 /* ? */ // question really is not permitted in sheet name
544 /* @ */ t[64] |= SC_COMPILER_C_WORD;
545 /* [ */ t[91] |= SC_COMPILER_C_WORD;
546 /* ] */ t[93] |= SC_COMPILER_C_WORD;
547 /* { */ t[123]|= SC_COMPILER_C_WORD;
548 /* | */ t[124]|= SC_COMPILER_C_WORD;
549 /* } */ t[125]|= SC_COMPILER_C_WORD;
550 /* ~ */ t[126]|= SC_COMPILER_C_WORD;
552 if( FormulaGrammar::CONV_XL_R1C1 == meConv )
554 /* - */ t[45] |= SC_COMPILER_C_IDENT;
555 /* [ */ t[91] |= SC_COMPILER_C_IDENT;
556 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
558 if( FormulaGrammar::CONV_XL_OOX == meConv )
560 /* [ */ t[91] |= SC_COMPILER_C_CHAR_IDENT;
561 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
566 //-----------------------------------------------------------------------------
568 static bool lcl_isValidQuotedText( const String& rFormula, xub_StrLen nSrcPos, ParseResult& rRes )
570 // Tokens that start at ' can have anything in them until a final '
571 // but '' marks an escaped '
572 // We've earlier guaranteed that a string containing '' will be
573 // surrounded by '
574 if (rFormula.GetChar(nSrcPos) == '\'')
576 xub_StrLen nPos = nSrcPos+1;
577 while (nPos < rFormula.Len())
579 if (rFormula.GetChar(nPos) == '\'')
581 if ( (nPos+1 == rFormula.Len()) || (rFormula.GetChar(nPos+1) != '\'') )
583 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
584 rRes.EndPos = nPos+1;
585 return true;
587 ++nPos;
589 ++nPos;
593 return false;
596 static bool lcl_parseExternalName(
597 const String& rSymbol,
598 String& rFile,
599 String& rName,
600 const sal_Unicode cSep,
601 const ScDocument* pDoc = NULL,
602 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
604 /* TODO: future versions will have to support sheet-local names too, thus
605 * return a possible sheet name as well. */
606 const sal_Unicode* const pStart = rSymbol.GetBuffer();
607 const sal_Unicode* p = pStart;
608 xub_StrLen nLen = rSymbol.Len();
609 sal_Unicode cPrev = 0;
610 String aTmpFile, aTmpName;
611 xub_StrLen i = 0;
612 bool bInName = false;
613 if (cSep == '!')
615 // For XL use existing parser that resolves bracketed and quoted and
616 // indexed external document names.
617 ScRange aRange;
618 String aStartTabName, aEndTabName;
619 USHORT nFlags = 0;
620 p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
621 aEndTabName, nFlags, true, pExternalLinks );
622 if (!p || p == pStart)
623 return false;
624 i = xub_StrLen(p - pStart);
625 cPrev = *(p-1);
627 for ( ; i < nLen; ++i, ++p)
629 sal_Unicode c = *p;
630 if (i == 0)
632 if (c == '.' || c == cSep)
633 return false;
635 if (c == '\'')
637 // Move to the next chart and loop until the second single
638 // quote.
639 cPrev = c;
640 ++i; ++p;
641 for (xub_StrLen j = i; j < nLen; ++j, ++p)
643 c = *p;
644 if (c == '\'')
646 if (j == i)
648 // empty quote e.g. (=''!Name)
649 return false;
652 if (cPrev == '\'')
654 // two consecutive quotes equals a single
655 // quote in the file name.
656 aTmpFile.Append(c);
657 cPrev = 'a';
659 else
660 cPrev = c;
662 continue;
665 if (cPrev == '\'' && j != i)
667 // this is not a quote but the previous one
668 // is. This ends the parsing of the quoted
669 // segment.
671 i = j;
672 bInName = true;
673 break;
675 aTmpFile.Append(c);
676 cPrev = c;
679 if (!bInName)
681 // premature ending of the quoted segment.
682 return false;
685 if (c != cSep)
687 // only the separator is allowed after the closing quote.
688 return false;
691 cPrev = c;
692 continue;
696 if (bInName)
698 if (c == cSep)
700 // A second separator ? Not a valid external name.
701 return false;
703 aTmpName.Append(c);
705 else
707 if (c == cSep)
709 bInName = true;
711 else
715 if (CharClass::isAsciiAlphaNumeric(c))
716 // allowed.
717 break;
719 if (c > 128)
720 // non-ASCII character is allowed.
721 break;
723 bool bValid = false;
724 switch (c)
726 case '_':
727 case '-':
728 case '.':
729 // these special characters are allowed.
730 bValid = true;
731 break;
733 if (bValid)
734 break;
736 return false;
738 while (false);
739 aTmpFile.Append(c);
742 cPrev = c;
745 if (!bInName)
747 // No name found - most likely the symbol has no '!'s.
748 return false;
751 rFile = aTmpFile;
752 rName = aTmpName;
753 return true;
756 static String lcl_makeExternalNameStr( const String& rFile, const String& rName,
757 const sal_Unicode cSep, bool bODF )
759 String aFile( rFile), aName( rName), aEscQuote( RTL_CONSTASCII_USTRINGPARAM("''"));
760 aFile.SearchAndReplaceAllAscii( "'", aEscQuote);
761 if (bODF)
762 aName.SearchAndReplaceAllAscii( "'", aEscQuote);
763 rtl::OUStringBuffer aBuf( aFile.Len() + aName.Len() + 9);
764 if (bODF)
765 aBuf.append( sal_Unicode( '['));
766 aBuf.append( sal_Unicode( '\''));
767 aBuf.append( aFile);
768 aBuf.append( sal_Unicode( '\''));
769 aBuf.append( cSep);
770 if (bODF)
771 aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "$$'"));
772 aBuf.append( aName);
773 if (bODF)
774 aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "']"));
775 return String( aBuf.makeStringAndClear());
778 static bool lcl_getLastTabName( String& rTabName2, const String& rTabName1,
779 const vector<String>& rTabNames, const ScComplexRefData& rRef )
781 SCsTAB nTabSpan = rRef.Ref2.nTab - rRef.Ref1.nTab;
782 if (nTabSpan > 0)
784 size_t nCount = rTabNames.size();
785 vector<String>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
786 vector<String>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
787 if (itr == rTabNames.end())
789 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
790 return false;
793 size_t nDist = ::std::distance(itrBeg, itr);
794 if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
796 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
797 return false;
800 rTabName2 = rTabNames[nDist+nTabSpan];
802 else
803 rTabName2 = rTabName1;
805 return true;
808 struct Convention_A1 : public ScCompiler::Convention
810 Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
811 static void MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol );
812 static void MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow );
814 ParseResult parseAnyToken( const String& rFormula,
815 xub_StrLen nSrcPos,
816 const CharClass* pCharClass) const
818 ParseResult aRet;
819 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
820 return aRet;
822 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
823 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
824 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
825 // '?' allowed in range names because of Xcl :-/
826 static const String aAddAllowed(String::CreateFromAscii("?#"));
827 return pCharClass->parseAnyToken( rFormula,
828 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
832 void Convention_A1::MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol )
834 if ( !ValidCol( nCol) )
835 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
836 else
837 ::ScColToAlpha( rBuffer, nCol);
840 void Convention_A1::MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow )
842 if ( !ValidRow(nRow) )
843 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
844 else
845 rBuffer.append(sal_Int32(nRow + 1));
848 //-----------------------------------------------------------------------------
850 struct ConventionOOO_A1 : public Convention_A1
852 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
853 ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
854 static String MakeTabStr( const ScCompiler& rComp, SCTAB nTab, String& aDoc )
856 String aString;
857 if (!rComp.GetDoc()->GetName( nTab, aString ))
858 aString = ScGlobal::GetRscString(STR_NO_REF_TABLE);
859 else
861 if ( aString.GetChar(0) == '\'' )
862 { // "'Doc'#Tab"
863 xub_StrLen nPos = ScGlobal::FindUnquoted( aString, SC_COMPILER_FILE_TAB_SEP);
864 if (nPos != STRING_NOTFOUND && nPos > 0 && aString.GetChar(nPos-1) == '\'')
866 aDoc = aString.Copy( 0, nPos + 1 );
867 aString.Erase( 0, nPos + 1 );
868 aDoc = INetURLObject::decode( aDoc, INET_HEX_ESCAPE,
869 INetURLObject::DECODE_UNAMBIGUOUS );
871 else
872 aDoc.Erase();
874 else
875 aDoc.Erase();
876 ScCompiler::CheckTabQuotes( aString, FormulaGrammar::CONV_OOO );
878 aString += '.';
879 return aString;
882 void MakeRefStrImpl( rtl::OUStringBuffer& rBuffer,
883 const ScCompiler& rComp,
884 const ScComplexRefData& rRef,
885 bool bSingleRef,
886 bool bODF ) const
888 if (bODF)
889 rBuffer.append(sal_Unicode('['));
890 ScComplexRefData aRef( rRef );
891 // In case absolute/relative positions weren't separately available:
892 // transform relative to absolute!
893 // AdjustReference( aRef.Ref1 );
894 // if( !bSingleRef )
895 // AdjustReference( aRef.Ref2 );
896 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
897 if( !bSingleRef )
898 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
899 if( aRef.Ref1.IsFlag3D() )
901 if (aRef.Ref1.IsTabDeleted())
903 if (!aRef.Ref1.IsTabRel())
904 rBuffer.append(sal_Unicode('$'));
905 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
906 rBuffer.append(sal_Unicode('.'));
908 else
910 String aDoc;
911 String aRefStr( MakeTabStr( rComp, aRef.Ref1.nTab, aDoc ) );
912 rBuffer.append(aDoc);
913 if (!aRef.Ref1.IsTabRel()) rBuffer.append(sal_Unicode('$'));
914 rBuffer.append(aRefStr);
917 else if (bODF)
918 rBuffer.append(sal_Unicode('.'));
919 if (!aRef.Ref1.IsColRel())
920 rBuffer.append(sal_Unicode('$'));
921 if ( aRef.Ref1.IsColDeleted() )
922 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
923 else
924 MakeColStr(rBuffer, aRef.Ref1.nCol );
925 if (!aRef.Ref1.IsRowRel())
926 rBuffer.append(sal_Unicode('$'));
927 if ( aRef.Ref1.IsRowDeleted() )
928 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
929 else
930 MakeRowStr( rBuffer, aRef.Ref1.nRow );
931 if (!bSingleRef)
933 rBuffer.append(sal_Unicode(':'));
934 if (aRef.Ref2.IsFlag3D() || aRef.Ref2.nTab != aRef.Ref1.nTab)
936 if (aRef.Ref2.IsTabDeleted())
938 if (!aRef.Ref2.IsTabRel())
939 rBuffer.append(sal_Unicode('$'));
940 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
941 rBuffer.append(sal_Unicode('.'));
943 else
945 String aDoc;
946 String aRefStr( MakeTabStr( rComp, aRef.Ref2.nTab, aDoc ) );
947 rBuffer.append(aDoc);
948 if (!aRef.Ref2.IsTabRel()) rBuffer.append(sal_Unicode('$'));
949 rBuffer.append(aRefStr);
952 else if (bODF)
953 rBuffer.append(sal_Unicode('.'));
954 if (!aRef.Ref2.IsColRel())
955 rBuffer.append(sal_Unicode('$'));
956 if ( aRef.Ref2.IsColDeleted() )
957 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
958 else
959 MakeColStr( rBuffer, aRef.Ref2.nCol );
960 if (!aRef.Ref2.IsRowRel())
961 rBuffer.append(sal_Unicode('$'));
962 if ( aRef.Ref2.IsRowDeleted() )
963 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
964 else
965 MakeRowStr( rBuffer, aRef.Ref2.nRow );
967 if (bODF)
968 rBuffer.append(sal_Unicode(']'));
971 void MakeRefStr( rtl::OUStringBuffer& rBuffer,
972 const ScCompiler& rComp,
973 const ScComplexRefData& rRef,
974 BOOL bSingleRef ) const
976 MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, false);
979 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
981 switch (eSymType)
983 case ScCompiler::Convention::ABS_SHEET_PREFIX:
984 return '$';
985 case ScCompiler::Convention::SHEET_SEPARATOR:
986 return '.';
989 return sal_Unicode(0);
992 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
993 const ScDocument* pDoc,
994 const ::com::sun::star::uno::Sequence<
995 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
997 return lcl_parseExternalName(rSymbol, rFile, rName, sal_Unicode('#'), pDoc, pExternalLinks);
1000 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1002 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), false);
1005 bool makeExternalSingleRefStr( ::rtl::OUStringBuffer& rBuffer, sal_uInt16 nFileId,
1006 const String& rTabName, const ScSingleRefData& rRef,
1007 ScExternalRefManager* pRefMgr, bool bDisplayTabName, bool bEncodeUrl ) const
1009 if (bDisplayTabName)
1011 String aFile;
1012 const String* p = pRefMgr->getExternalFileName(nFileId);
1013 if (p)
1015 if (bEncodeUrl)
1016 aFile = *p;
1017 else
1018 aFile = INetURLObject::decode(*p, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
1020 aFile.SearchAndReplaceAllAscii("'", String::CreateFromAscii("''"));
1022 rBuffer.append(sal_Unicode('\''));
1023 rBuffer.append(aFile);
1024 rBuffer.append(sal_Unicode('\''));
1025 rBuffer.append(sal_Unicode('#'));
1027 if (!rRef.IsTabRel())
1028 rBuffer.append(sal_Unicode('$'));
1029 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1031 rBuffer.append(sal_Unicode('.'));
1034 if (!rRef.IsColRel())
1035 rBuffer.append(sal_Unicode('$'));
1036 MakeColStr( rBuffer, rRef.nCol);
1037 if (!rRef.IsRowRel())
1038 rBuffer.append(sal_Unicode('$'));
1039 MakeRowStr( rBuffer, rRef.nRow);
1041 return true;
1044 void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1045 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1046 ScExternalRefManager* pRefMgr, bool bODF ) const
1048 ScSingleRefData aRef(rRef);
1049 aRef.CalcAbsIfRel(rCompiler.GetPos());
1051 if (bODF)
1052 rBuffer.append( sal_Unicode('['));
1054 bool bEncodeUrl = true;
1055 switch (rCompiler.GetEncodeUrlMode())
1057 case ScCompiler::ENCODE_BY_GRAMMAR:
1058 bEncodeUrl = bODF;
1059 break;
1060 case ScCompiler::ENCODE_ALWAYS:
1061 bEncodeUrl = true;
1062 break;
1063 case ScCompiler::ENCODE_NEVER:
1064 bEncodeUrl = false;
1065 break;
1066 default:
1069 makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef, pRefMgr, true, bEncodeUrl);
1070 if (bODF)
1071 rBuffer.append( sal_Unicode(']'));
1074 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1075 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1076 ScExternalRefManager* pRefMgr ) const
1078 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
1081 void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1082 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1083 ScExternalRefManager* pRefMgr, bool bODF ) const
1085 ScComplexRefData aRef(rRef);
1086 aRef.CalcAbsIfRel(rCompiler.GetPos());
1088 if (bODF)
1089 rBuffer.append( sal_Unicode('['));
1090 // Ensure that there's always a closing bracket, no premature returns.
1091 bool bEncodeUrl = true;
1092 switch (rCompiler.GetEncodeUrlMode())
1094 case ScCompiler::ENCODE_BY_GRAMMAR:
1095 bEncodeUrl = bODF;
1096 break;
1097 case ScCompiler::ENCODE_ALWAYS:
1098 bEncodeUrl = true;
1099 break;
1100 case ScCompiler::ENCODE_NEVER:
1101 bEncodeUrl = false;
1102 break;
1103 default:
1109 if (!makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef.Ref1, pRefMgr, true, bEncodeUrl))
1110 break;
1112 rBuffer.append(sal_Unicode(':'));
1114 String aLastTabName;
1115 bool bDisplayTabName = (aRef.Ref1.nTab != aRef.Ref2.nTab);
1116 if (bDisplayTabName)
1118 // Get the name of the last table.
1119 vector<String> aTabNames;
1120 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1121 if (aTabNames.empty())
1123 DBG_ERROR1( "ConventionOOO_A1::makeExternalRefStrImpl: no sheet names for document ID %s", nFileId);
1126 if (!lcl_getLastTabName(aLastTabName, rTabName, aTabNames, aRef))
1128 DBG_ERROR( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1129 // aLastTabName contains #REF!, proceed.
1132 else if (bODF)
1133 rBuffer.append( sal_Unicode('.')); // need at least the sheet separator in ODF
1134 makeExternalSingleRefStr( rBuffer, nFileId, aLastTabName,
1135 aRef.Ref2, pRefMgr, bDisplayTabName, bEncodeUrl);
1136 } while (0);
1137 if (bODF)
1138 rBuffer.append( sal_Unicode(']'));
1141 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1142 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1143 ScExternalRefManager* pRefMgr ) const
1145 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
1150 static const ConventionOOO_A1 ConvOOO_A1;
1151 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1 = &ConvOOO_A1;
1153 //-----------------------------------------------------------------------------
1155 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
1157 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
1158 void MakeRefStr( rtl::OUStringBuffer& rBuffer,
1159 const ScCompiler& rComp,
1160 const ScComplexRefData& rRef,
1161 BOOL bSingleRef ) const
1163 MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, true);
1166 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1168 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), true);
1171 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1172 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1173 ScExternalRefManager* pRefMgr ) const
1175 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
1178 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1179 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1180 ScExternalRefManager* pRefMgr ) const
1182 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
1186 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
1187 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1_ODF = &ConvOOO_A1_ODF;
1189 //-----------------------------------------------------------------------------
1191 struct ConventionXL
1193 static bool GetDocAndTab( const ScCompiler& rComp,
1194 const ScSingleRefData& rRef,
1195 String& rDocName,
1196 String& rTabName )
1198 bool bHasDoc = false;
1200 rDocName.Erase();
1201 if (rRef.IsTabDeleted() ||
1202 !rComp.GetDoc()->GetName( rRef.nTab, rTabName ))
1204 rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE );
1205 return false;
1208 // Cheesy hack to unparse the OOO style "'Doc'#Tab"
1209 if ( rTabName.GetChar(0) == '\'' )
1211 xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
1212 if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
1214 rDocName = rTabName.Copy( 0, nPos );
1215 // TODO : More research into how XL escapes the doc path
1216 rDocName = INetURLObject::decode( rDocName, INET_HEX_ESCAPE,
1217 INetURLObject::DECODE_UNAMBIGUOUS );
1218 rTabName.Erase( 0, nPos + 1 );
1219 bHasDoc = true;
1223 // XL uses the same sheet name quoting conventions in both modes
1224 // it is safe to use A1 here
1225 ScCompiler::CheckTabQuotes( rTabName, FormulaGrammar::CONV_XL_A1 );
1226 return bHasDoc;
1229 static void MakeDocStr( rtl::OUStringBuffer& rBuf,
1230 const ScCompiler& rComp,
1231 const ScComplexRefData& rRef,
1232 bool bSingleRef )
1234 if( rRef.Ref1.IsFlag3D() )
1236 String aStartTabName, aStartDocName, aEndTabName, aEndDocName;
1237 bool bStartHasDoc = false, bEndHasDoc = false;
1239 bStartHasDoc = GetDocAndTab( rComp, rRef.Ref1,
1240 aStartDocName, aStartTabName);
1242 if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1244 bEndHasDoc = GetDocAndTab( rComp, rRef.Ref2,
1245 aEndDocName, aEndTabName);
1247 else
1248 bEndHasDoc = bStartHasDoc;
1250 if( bStartHasDoc )
1252 // A ref across multipled workbooks ?
1253 if( !bEndHasDoc )
1254 return;
1256 rBuf.append( sal_Unicode( '[' ) );
1257 rBuf.append( aStartDocName );
1258 rBuf.append( sal_Unicode( ']' ) );
1261 rBuf.append( aStartTabName );
1262 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1264 rBuf.append( sal_Unicode( ':' ) );
1265 rBuf.append( aEndTabName );
1268 rBuf.append( sal_Unicode( '!' ) );
1272 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1274 switch (eSymType)
1276 case ScCompiler::Convention::ABS_SHEET_PREFIX:
1277 return sal_Unicode(0);
1278 case ScCompiler::Convention::SHEET_SEPARATOR:
1279 return '!';
1281 return sal_Unicode(0);
1284 static bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1285 const ScDocument* pDoc,
1286 const ::com::sun::star::uno::Sequence<
1287 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks )
1289 return lcl_parseExternalName( rSymbol, rFile, rName, sal_Unicode('!'), pDoc, pExternalLinks);
1292 static String makeExternalNameStr( const String& rFile, const String& rName )
1294 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('!'), false);
1297 static void makeExternalDocStr( ::rtl::OUStringBuffer& rBuffer, const String& rFullName, bool bEncodeUrl )
1299 // Format that is easier to deal with inside OOo, because we use file
1300 // URL, and all characetrs are allowed. Check if it makes sense to do
1301 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1302 // and allows relative file path.
1304 // ['file:///path/to/source/filename.xls']
1306 rBuffer.append(sal_Unicode('['));
1307 rBuffer.append(sal_Unicode('\''));
1308 String aFullName;
1309 if (bEncodeUrl)
1310 aFullName = rFullName;
1311 else
1312 aFullName = INetURLObject::decode(rFullName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
1314 const sal_Unicode* pBuf = aFullName.GetBuffer();
1315 xub_StrLen nLen = aFullName.Len();
1316 for (xub_StrLen i = 0; i < nLen; ++i)
1318 const sal_Unicode c = pBuf[i];
1319 if (c == sal_Unicode('\''))
1320 rBuffer.append(c);
1321 rBuffer.append(c);
1323 rBuffer.append(sal_Unicode('\''));
1324 rBuffer.append(sal_Unicode(']'));
1327 static void makeExternalTabNameRange( ::rtl::OUStringBuffer& rBuf, const String& rTabName,
1328 const vector<String>& rTabNames,
1329 const ScComplexRefData& rRef )
1331 String aLastTabName;
1332 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1334 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1335 return;
1338 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1339 if (rTabName != aLastTabName)
1341 rBuf.append(sal_Unicode(':'));
1342 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1346 static void parseExternalDocName( const String& rFormula, xub_StrLen& rSrcPos )
1348 xub_StrLen nLen = rFormula.Len();
1349 const sal_Unicode* p = rFormula.GetBuffer();
1350 sal_Unicode cPrev = 0;
1351 for (xub_StrLen i = rSrcPos; i < nLen; ++i)
1353 sal_Unicode c = p[i];
1354 if (i == rSrcPos)
1356 // first character must be '['.
1357 if (c != '[')
1358 return;
1360 else if (i == rSrcPos + 1)
1362 // second character must be a single quote.
1363 if (c != '\'')
1364 return;
1366 else if (c == '\'')
1368 if (cPrev == '\'')
1369 // two successive single quote is treated as a single
1370 // valid character.
1371 c = 'a';
1373 else if (c == ']')
1375 if (cPrev == '\'')
1377 // valid source document path found. Increment the
1378 // current position to skip the source path.
1379 rSrcPos = i + 1;
1380 if (rSrcPos >= nLen)
1381 rSrcPos = nLen - 1;
1382 return;
1384 else
1385 return;
1387 else
1389 // any other character
1390 if (i > rSrcPos + 2 && cPrev == '\'')
1391 // unless it's the 3rd character, a normal character
1392 // following immediately a single quote is invalid.
1393 return;
1395 cPrev = c;
1400 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1402 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1403 ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1405 void makeSingleCellStr( ::rtl::OUStringBuffer& rBuf, const ScSingleRefData& rRef ) const
1407 if (!rRef.IsColRel())
1408 rBuf.append(sal_Unicode('$'));
1409 MakeColStr(rBuf, rRef.nCol);
1410 if (!rRef.IsRowRel())
1411 rBuf.append(sal_Unicode('$'));
1412 MakeRowStr(rBuf, rRef.nRow);
1415 void MakeRefStr( rtl::OUStringBuffer& rBuf,
1416 const ScCompiler& rComp,
1417 const ScComplexRefData& rRef,
1418 BOOL bSingleRef ) const
1420 ScComplexRefData aRef( rRef );
1422 // Play fast and loose with invalid refs. There is not much point in producing
1423 // Foo!A1:#REF! versus #REF! at this point
1424 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
1426 MakeDocStr( rBuf, rComp, aRef, bSingleRef );
1428 if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
1430 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1431 return;
1434 if( !bSingleRef )
1436 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
1437 if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
1439 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1440 return;
1443 if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
1445 if (!aRef.Ref1.IsRowRel())
1446 rBuf.append(sal_Unicode( '$' ));
1447 MakeRowStr( rBuf, aRef.Ref1.nRow );
1448 rBuf.append(sal_Unicode( ':' ));
1449 if (!aRef.Ref2.IsRowRel())
1450 rBuf.append(sal_Unicode( '$' ));
1451 MakeRowStr( rBuf, aRef.Ref2.nRow );
1452 return;
1455 if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
1457 if (!aRef.Ref1.IsColRel())
1458 rBuf.append(sal_Unicode( '$' ));
1459 MakeColStr(rBuf, aRef.Ref1.nCol );
1460 rBuf.append(sal_Unicode( ':' ));
1461 if (!aRef.Ref2.IsColRel())
1462 rBuf.append(sal_Unicode( '$' ));
1463 MakeColStr(rBuf, aRef.Ref2.nCol );
1464 return;
1468 makeSingleCellStr(rBuf, aRef.Ref1);
1469 if (!bSingleRef)
1471 rBuf.append(sal_Unicode( ':' ));
1472 makeSingleCellStr(rBuf, aRef.Ref2);
1476 virtual ParseResult parseAnyToken( const String& rFormula,
1477 xub_StrLen nSrcPos,
1478 const CharClass* pCharClass) const
1480 ParseResult aRet;
1481 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1482 return aRet;
1484 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1485 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1486 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1487 // '?' allowed in range names
1488 static const String aAddAllowed = String::CreateFromAscii("?!");
1489 return pCharClass->parseAnyToken( rFormula,
1490 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1493 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1495 return ConventionXL::getSpecialSymbol(eSymType);
1498 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1499 const ScDocument* pDoc,
1500 const ::com::sun::star::uno::Sequence<
1501 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1503 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1506 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1508 return ConventionXL::makeExternalNameStr(rFile, rName);
1511 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1512 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1513 ScExternalRefManager* pRefMgr ) const
1515 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1516 // This is a little different from the format Excel uses, as Excel
1517 // puts [] only around the file name. But we need to enclose the
1518 // whole file path with [] because the file name can contain any
1519 // characters.
1521 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1522 if (!pFullName)
1523 return;
1525 ScSingleRefData aRef(rRef);
1526 aRef.CalcAbsIfRel(rCompiler.GetPos());
1528 ConventionXL::makeExternalDocStr(
1529 rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
1530 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1531 rBuffer.append(sal_Unicode('!'));
1533 makeSingleCellStr(rBuffer, aRef);
1536 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1537 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1538 ScExternalRefManager* pRefMgr ) const
1540 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1541 if (!pFullName)
1542 return;
1544 vector<String> aTabNames;
1545 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1546 if (aTabNames.empty())
1547 return;
1549 ScComplexRefData aRef(rRef);
1550 aRef.CalcAbsIfRel(rCompiler.GetPos());
1552 ConventionXL::makeExternalDocStr(
1553 rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
1554 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
1555 rBuffer.append(sal_Unicode('!'));
1557 makeSingleCellStr(rBuffer, aRef.Ref1);
1558 if (aRef.Ref1 != aRef.Ref2)
1560 rBuffer.append(sal_Unicode(':'));
1561 makeSingleCellStr(rBuffer, aRef.Ref2);
1566 static const ConventionXL_A1 ConvXL_A1;
1567 const ScCompiler::Convention * const ScCompiler::pConvXL_A1 = &ConvXL_A1;
1570 struct ConventionXL_OOX : public ConventionXL_A1
1572 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1575 static const ConventionXL_OOX ConvXL_OOX;
1576 const ScCompiler::Convention * const ScCompiler::pConvXL_OOX = &ConvXL_OOX;
1579 //-----------------------------------------------------------------------------
1581 static void
1582 r1c1_add_col( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
1584 rBuf.append( sal_Unicode( 'C' ) );
1585 if( rRef.IsColRel() )
1587 if (rRef.nRelCol != 0)
1589 rBuf.append( sal_Unicode( '[' ) );
1590 rBuf.append( String::CreateFromInt32( rRef.nRelCol ) );
1591 rBuf.append( sal_Unicode( ']' ) );
1594 else
1595 rBuf.append( String::CreateFromInt32( rRef.nCol + 1 ) );
1597 static void
1598 r1c1_add_row( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
1600 rBuf.append( sal_Unicode( 'R' ) );
1601 if( rRef.IsRowRel() )
1603 if (rRef.nRelRow != 0)
1605 rBuf.append( sal_Unicode( '[' ) );
1606 rBuf.append( String::CreateFromInt32( rRef.nRelRow ) );
1607 rBuf.append( sal_Unicode( ']' ) );
1610 else
1611 rBuf.append( String::CreateFromInt32( rRef.nRow + 1 ) );
1614 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1616 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1617 void MakeRefStr( rtl::OUStringBuffer& rBuf,
1618 const ScCompiler& rComp,
1619 const ScComplexRefData& rRef,
1620 BOOL bSingleRef ) const
1622 ScComplexRefData aRef( rRef );
1624 MakeDocStr( rBuf, rComp, aRef, bSingleRef );
1626 // Play fast and loose with invalid refs. There is not much point in producing
1627 // Foo!A1:#REF! versus #REF! at this point
1628 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
1629 if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
1631 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1632 return;
1635 if( !bSingleRef )
1637 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
1638 if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
1640 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1641 return;
1644 if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
1646 r1c1_add_row( rBuf, rRef.Ref1 );
1647 if( rRef.Ref1.nRow != rRef.Ref2.nRow ||
1648 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ) {
1649 rBuf.append (sal_Unicode ( ':' ) );
1650 r1c1_add_row( rBuf, rRef.Ref2 );
1652 return;
1656 if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
1658 r1c1_add_col( rBuf, rRef.Ref1 );
1659 if( rRef.Ref1.nCol != rRef.Ref2.nCol ||
1660 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel() )
1662 rBuf.append (sal_Unicode ( ':' ) );
1663 r1c1_add_col( rBuf, rRef.Ref2 );
1665 return;
1669 r1c1_add_row( rBuf, rRef.Ref1 );
1670 r1c1_add_col( rBuf, rRef.Ref1 );
1671 if (!bSingleRef)
1673 rBuf.append (sal_Unicode ( ':' ) );
1674 r1c1_add_row( rBuf, rRef.Ref2 );
1675 r1c1_add_col( rBuf, rRef.Ref2 );
1679 ParseResult parseAnyToken( const String& rFormula,
1680 xub_StrLen nSrcPos,
1681 const CharClass* pCharClass) const
1683 ConventionXL::parseExternalDocName(rFormula, nSrcPos);
1685 ParseResult aRet;
1686 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1687 return aRet;
1689 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1690 KParseTokens::ASC_UNDERSCORE ;
1691 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1692 // '?' allowed in range names
1693 static const String aAddAllowed = String::CreateFromAscii( "?-[]!" );
1695 return pCharClass->parseAnyToken( rFormula,
1696 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1699 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1701 return ConventionXL::getSpecialSymbol(eSymType);
1704 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1705 const ScDocument* pDoc,
1706 const ::com::sun::star::uno::Sequence<
1707 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1709 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1712 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1714 return ConventionXL::makeExternalNameStr(rFile, rName);
1717 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1718 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1719 ScExternalRefManager* pRefMgr ) const
1721 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1722 // This is a little different from the format Excel uses, as Excel
1723 // puts [] only around the file name. But we need to enclose the
1724 // whole file path with [] because the file name can contain any
1725 // characters.
1727 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1728 if (!pFullName)
1729 return;
1731 ScSingleRefData aRef(rRef);
1732 aRef.CalcAbsIfRel(rCompiler.GetPos());
1734 ConventionXL::makeExternalDocStr(
1735 rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
1736 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1737 rBuffer.append(sal_Unicode('!'));
1739 r1c1_add_row(rBuffer, aRef);
1740 r1c1_add_col(rBuffer, aRef);
1743 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1744 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1745 ScExternalRefManager* pRefMgr ) const
1747 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1748 if (!pFullName)
1749 return;
1751 vector<String> aTabNames;
1752 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1753 if (aTabNames.empty())
1754 return;
1756 ScComplexRefData aRef(rRef);
1757 aRef.CalcAbsIfRel(rCompiler.GetPos());
1759 ConventionXL::makeExternalDocStr(
1760 rBuffer, *pFullName, rCompiler.GetEncodeUrlMode() == ScCompiler::ENCODE_ALWAYS);
1761 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
1762 rBuffer.append(sal_Unicode('!'));
1764 if (aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted())
1766 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1767 return;
1770 if (aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL)
1772 r1c1_add_row(rBuffer, rRef.Ref1);
1773 if (rRef.Ref1.nRow != rRef.Ref2.nRow || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1775 rBuffer.append (sal_Unicode(':'));
1776 r1c1_add_row(rBuffer, rRef.Ref2);
1778 return;
1781 if (aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW)
1783 r1c1_add_col(rBuffer, aRef.Ref1);
1784 if (aRef.Ref1.nCol != aRef.Ref2.nCol || aRef.Ref1.IsColRel() != aRef.Ref2.IsColRel())
1786 rBuffer.append (sal_Unicode(':'));
1787 r1c1_add_col(rBuffer, aRef.Ref2);
1789 return;
1792 r1c1_add_row(rBuffer, aRef.Ref1);
1793 r1c1_add_col(rBuffer, aRef.Ref1);
1794 rBuffer.append (sal_Unicode (':'));
1795 r1c1_add_row(rBuffer, aRef.Ref2);
1796 r1c1_add_col(rBuffer, aRef.Ref2);
1800 static const ConventionXL_R1C1 ConvXL_R1C1;
1801 const ScCompiler::Convention * const ScCompiler::pConvXL_R1C1 = &ConvXL_R1C1;
1803 //-----------------------------------------------------------------------------
1804 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr)
1805 : FormulaCompiler(rArr),
1806 pDoc( pDocument ),
1807 aPos( rPos ),
1808 pCharClass( ScGlobal::pCharClass ),
1809 mnPredetectedReference(0),
1810 mnRangeOpPosInSymbol(-1),
1811 pConv( pConvOOO_A1 ),
1812 meEncodeUrlMode( ENCODE_BY_GRAMMAR ),
1813 mbCloseBrackets( true ),
1814 mbExtendedErrorDetection( false ),
1815 mbRewind( false )
1817 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1820 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos)
1822 pDoc( pDocument ),
1823 aPos( rPos ),
1824 pCharClass( ScGlobal::pCharClass ),
1825 mnPredetectedReference(0),
1826 mnRangeOpPosInSymbol(-1),
1827 pConv( pConvOOO_A1 ),
1828 meEncodeUrlMode( ENCODE_BY_GRAMMAR ),
1829 mbCloseBrackets( true ),
1830 mbExtendedErrorDetection( false ),
1831 mbRewind( false )
1833 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1836 void ScCompiler::CheckTabQuotes( String& rString,
1837 const FormulaGrammar::AddressConvention eConv )
1839 using namespace ::com::sun::star::i18n;
1840 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1841 sal_Int32 nContFlags = nStartFlags;
1842 ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
1843 KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_STRING, nContFlags, EMPTY_STRING);
1844 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.Len());
1846 switch ( eConv )
1848 default :
1849 case FormulaGrammar::CONV_UNSPECIFIED :
1850 break;
1851 case FormulaGrammar::CONV_OOO :
1852 case FormulaGrammar::CONV_XL_A1 :
1853 case FormulaGrammar::CONV_XL_R1C1 :
1854 case FormulaGrammar::CONV_XL_OOX :
1855 if( bNeedsQuote )
1857 static const String one_quote = static_cast<sal_Unicode>( '\'' );
1858 static const String two_quote = String::CreateFromAscii( "''" );
1859 // escape embedded quotes
1860 rString.SearchAndReplaceAll( one_quote, two_quote );
1862 break;
1865 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1867 // Prevent any possible confusion resulting from pure numeric sheet names.
1868 bNeedsQuote = true;
1871 if( bNeedsQuote )
1873 rString.Insert( '\'', 0 );
1874 rString += '\'';
1878 //---------------------------------------------------------------------------
1880 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
1882 switch ( eConv ) {
1883 case FormulaGrammar::CONV_UNSPECIFIED :
1884 break;
1885 default :
1886 case FormulaGrammar::CONV_OOO : SetRefConvention( pConvOOO_A1 ); break;
1887 case FormulaGrammar::CONV_ODF : SetRefConvention( pConvOOO_A1_ODF ); break;
1888 case FormulaGrammar::CONV_XL_A1 : SetRefConvention( pConvXL_A1 ); break;
1889 case FormulaGrammar::CONV_XL_R1C1 : SetRefConvention( pConvXL_R1C1 ); break;
1890 case FormulaGrammar::CONV_XL_OOX : SetRefConvention( pConvXL_OOX ); break;
1894 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
1896 pConv = pConvP;
1897 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
1898 DBG_ASSERT( FormulaGrammar::isSupported( meGrammar),
1899 "ScCompiler::SetRefConvention: unsupported grammar resulting");
1902 void ScCompiler::SetError(USHORT nError)
1904 if( !pArr->GetCodeError() )
1905 pArr->SetCodeError( nError);
1909 sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, xub_StrLen nMax )
1911 const sal_Unicode* const pStop = pDst + nMax;
1912 while ( *pSrc && pDst < pStop )
1914 *pDst++ = *pSrc++;
1916 *pDst = 0;
1917 return pDst;
1921 //---------------------------------------------------------------------------
1922 // NextSymbol
1923 //---------------------------------------------------------------------------
1924 // Zerlegt die Formel in einzelne Symbole fuer die weitere
1925 // Verarbeitung (Turing-Maschine).
1926 //---------------------------------------------------------------------------
1927 // Ausgangs Zustand = GetChar
1928 //---------------+-------------------+-----------------------+---------------
1929 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1930 //---------------+-------------------+-----------------------+---------------
1931 // GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop
1932 // | <> | Symbol=Zeichen | GetBool
1933 // | $ Buchstabe | Symbol=Zeichen | GetWord
1934 // | Ziffer | Symbol=Zeichen | GetValue
1935 // | " | Keine | GetString
1936 // | Sonst | Keine | GetChar
1937 //---------------+-------------------+-----------------------+---------------
1938 // GetBool | => | Symbol=Symbol+Zeichen | Stop
1939 // | Sonst | Dec(CharPos) | Stop
1940 //---------------+-------------------+-----------------------+---------------
1941 // GetWord | SepSymbol | Dec(CharPos) | Stop
1942 // | ()+-*/^=<>&~ | |
1943 // | Leerzeichen | Dec(CharPos) | Stop
1944 // | $_:. | |
1945 // | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord
1946 // | Sonst | Fehler | Stop
1947 //---------------|-------------------+-----------------------+---------------
1948 // GetValue | ;()*/^=<>& | |
1949 // | Leerzeichen | Dec(CharPos) | Stop
1950 // | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue
1951 // | Sonst | Fehler | Stop
1952 //---------------+-------------------+-----------------------+---------------
1953 // GetString | " | Keine | Stop
1954 // | Sonst | Symbol=Symbol+Zeichen | GetString
1955 //---------------+-------------------+-----------------------+---------------
1957 xub_StrLen ScCompiler::NextSymbol(bool bInArray)
1959 cSymbol[MAXSTRLEN-1] = 0; // Stopper
1960 sal_Unicode* pSym = cSymbol;
1961 const sal_Unicode* const pStart = aFormula.GetBuffer();
1962 const sal_Unicode* pSrc = pStart + nSrcPos;
1963 bool bi18n = false;
1964 sal_Unicode c = *pSrc;
1965 sal_Unicode cLast = 0;
1966 bool bQuote = false;
1967 mnRangeOpPosInSymbol = -1;
1968 ScanState eState = ssGetChar;
1969 xub_StrLen nSpaces = 0;
1970 sal_Unicode cSep = mxSymbols->getSymbol( ocSep).GetChar(0);
1971 sal_Unicode cArrayColSep = mxSymbols->getSymbol( ocArrayColSep).GetChar(0);
1972 sal_Unicode cArrayRowSep = mxSymbols->getSymbol( ocArrayRowSep).GetChar(0);
1973 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' :
1974 ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0));
1976 // special symbols specific to address convention used
1977 sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
1978 sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
1980 int nDecSeps = 0;
1981 bool bAutoIntersection = false;
1982 int nRefInName = 0;
1983 mnPredetectedReference = 0;
1984 // try to parse simple tokens before calling i18n parser
1985 while ((c != 0) && (eState != ssStop) )
1987 pSrc++;
1988 ULONG nMask = GetCharTableFlags( c );
1989 // The parameter separator and the array column and row separators end
1990 // things unconditionally if not in string or reference.
1991 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
1993 switch (eState)
1995 // these are to be continued
1996 case ssGetString:
1997 case ssSkipString:
1998 case ssGetReference:
1999 case ssSkipReference:
2000 break;
2001 default:
2002 if (eState == ssGetChar)
2003 *pSym++ = c;
2004 else
2005 pSrc--;
2006 eState = ssStop;
2009 Label_MaskStateMachine:
2010 switch (eState)
2012 case ssGetChar :
2014 // Order is important!
2015 if( nMask & SC_COMPILER_C_ODF_LABEL_OP )
2017 // '!!' automatic intersection
2018 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_LABEL_OP)
2020 /* TODO: For now the UI "space operator" is used, this
2021 * could be enhanced using a specialized OpCode to get
2022 * rid of the space ambiguity, which would need some
2023 * places to be adapted though. And we would still need
2024 * to support the ambiguous space operator for UI
2025 * purposes anyway. However, we then could check for
2026 * invalid usage of '!!', which currently isn't
2027 * possible. */
2028 if (!bAutoIntersection)
2030 ++pSrc;
2031 nSpaces += 2; // must match the character count
2032 bAutoIntersection = true;
2034 else
2036 pSrc--;
2037 eState = ssStop;
2040 else
2042 nMask &= ~SC_COMPILER_C_ODF_LABEL_OP;
2043 goto Label_MaskStateMachine;
2046 else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER )
2048 // '$$' defined name marker
2049 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_NAME_MARKER)
2051 // both eaten, not added to pSym
2052 ++pSrc;
2054 else
2056 nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER;
2057 goto Label_MaskStateMachine;
2060 else if( nMask & SC_COMPILER_C_CHAR )
2062 *pSym++ = c;
2063 eState = ssStop;
2065 else if( nMask & SC_COMPILER_C_ODF_LBRACKET )
2067 // eaten, not added to pSym
2068 eState = ssGetReference;
2069 mnPredetectedReference = 1;
2071 else if( nMask & SC_COMPILER_C_CHAR_BOOL )
2073 *pSym++ = c;
2074 eState = ssGetBool;
2076 else if( nMask & SC_COMPILER_C_CHAR_VALUE )
2078 *pSym++ = c;
2079 eState = ssGetValue;
2081 else if( nMask & SC_COMPILER_C_CHAR_STRING )
2083 *pSym++ = c;
2084 eState = ssGetString;
2086 else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
2088 nSpaces++;
2090 else if( nMask & SC_COMPILER_C_CHAR_IDENT )
2091 { // try to get a simple ASCII identifier before calling
2092 // i18n, to gain performance during import
2093 *pSym++ = c;
2094 eState = ssGetIdent;
2096 else
2098 bi18n = true;
2099 eState = ssStop;
2102 break;
2103 case ssGetIdent:
2105 if ( nMask & SC_COMPILER_C_IDENT )
2106 { // This catches also $Sheet1.A$1, for example.
2107 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2109 SetError(errStringOverflow);
2110 eState = ssStop;
2112 else
2113 *pSym++ = c;
2115 else if (c == ':' && mnRangeOpPosInSymbol < 0)
2117 // One range operator may form Sheet1.A:A, which we need to
2118 // pass as one entity to IsReference().
2119 mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2120 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2122 SetError(errStringOverflow);
2123 eState = ssStop;
2125 else
2126 *pSym++ = c;
2128 else if ( 128 <= c || '\'' == c )
2129 { // High values need reparsing with i18n,
2130 // single quoted $'sheet' names too (otherwise we'd had to
2131 // implement everything twice).
2132 bi18n = true;
2133 eState = ssStop;
2135 else
2137 pSrc--;
2138 eState = ssStop;
2141 break;
2142 case ssGetBool :
2144 if( nMask & SC_COMPILER_C_BOOL )
2146 *pSym++ = c;
2147 eState = ssStop;
2149 else
2151 pSrc--;
2152 eState = ssStop;
2155 break;
2156 case ssGetValue :
2158 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2160 SetError(errStringOverflow);
2161 eState = ssStop;
2163 else if (c == cDecSep)
2165 if (++nDecSeps > 1)
2167 // reparse with i18n, may be numeric sheet name as well
2168 bi18n = true;
2169 eState = ssStop;
2171 else
2172 *pSym++ = c;
2174 else if( nMask & SC_COMPILER_C_VALUE )
2175 *pSym++ = c;
2176 else if( nMask & SC_COMPILER_C_VALUE_SEP )
2178 pSrc--;
2179 eState = ssStop;
2181 else if (c == 'E' || c == 'e')
2183 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_EXP)
2184 *pSym++ = c;
2185 else
2187 // reparse with i18n
2188 bi18n = true;
2189 eState = ssStop;
2192 else if( nMask & SC_COMPILER_C_VALUE_SIGN )
2194 if (((cLast == 'E') || (cLast == 'e')) &&
2195 (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_VALUE))
2197 *pSym++ = c;
2199 else
2201 pSrc--;
2202 eState = ssStop;
2205 else
2207 // reparse with i18n
2208 bi18n = true;
2209 eState = ssStop;
2212 break;
2213 case ssGetString :
2215 if( nMask & SC_COMPILER_C_STRING_SEP )
2217 if ( !bQuote )
2219 if ( *pSrc == '"' )
2220 bQuote = true; // "" => literal "
2221 else
2222 eState = ssStop;
2224 else
2225 bQuote = false;
2227 if ( !bQuote )
2229 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2231 SetError(errStringOverflow);
2232 eState = ssSkipString;
2234 else
2235 *pSym++ = c;
2238 break;
2239 case ssSkipString:
2240 if( nMask & SC_COMPILER_C_STRING_SEP )
2241 eState = ssStop;
2242 break;
2243 case ssGetReference:
2244 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2246 SetError( errStringOverflow);
2247 eState = ssSkipReference;
2249 // fall through and follow logic
2250 case ssSkipReference:
2251 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2252 // mandatory also if no sheet name. 'External'# is optional,
2253 // sheet name is optional, quotes around sheet name are
2254 // optional if no quote contained.
2255 // 2nd usage: ['Sheet'.$$'DefinedName']
2256 // 3rd usage: ['External'#$$'DefinedName']
2257 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2258 // Also for all these names quotes are optional if no quote
2259 // contained.
2262 // nRefInName: 0 := not in sheet name yet. 'External'
2263 // is parsed as if it was a sheet name and nRefInName
2264 // is reset when # is encountered immediately after closing
2265 // quote. Same with 'DefinedName', nRefInName is cleared
2266 // when : is encountered.
2268 // Encountered leading $ before sheet name.
2269 static const int kDollar = (1 << 1);
2270 // Encountered ' opening quote, which may be after $ or
2271 // not.
2272 static const int kOpen = (1 << 2);
2273 // Somewhere in name.
2274 static const int kName = (1 << 3);
2275 // Encountered ' in name, will be cleared if double or
2276 // transformed to kClose if not, in which case kOpen is
2277 // cleared.
2278 static const int kQuote = (1 << 4);
2279 // Past ' closing quote.
2280 static const int kClose = (1 << 5);
2281 // Past . sheet name separator.
2282 static const int kPast = (1 << 6);
2283 // Marked name $$ follows sheet name separator, detected
2284 // while we're still on the separator. Will be cleared when
2285 // entering the name.
2286 static const int kMarkAhead = (1 << 7);
2287 // In marked defined name.
2288 static const int kDefName = (1 << 8);
2290 bool bAddToSymbol = true;
2291 if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
2293 DBG_ASSERT( nRefInName & (kPast | kDefName),
2294 "ScCompiler::NextSymbol: reference: "
2295 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2296 // eaten, not added to pSym
2297 bAddToSymbol = false;
2298 eState = ssStop;
2300 else if (cSheetSep == c && nRefInName == 0)
2302 // eat it, no sheet name [.A1]
2303 bAddToSymbol = false;
2304 nRefInName |= kPast;
2305 if ('$' == pSrc[0] && '$' == pSrc[1])
2306 nRefInName |= kMarkAhead;
2308 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2310 // Not in col/row yet.
2312 if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2314 nRefInName &= ~kMarkAhead;
2315 if (!(nRefInName & kDefName))
2317 // eaten, not added to pSym (2 chars)
2318 bAddToSymbol = false;
2319 ++pSrc;
2320 nRefInName &= kPast;
2321 nRefInName |= kDefName;
2323 else
2325 // ScAddress::Parse() will recognize this as
2326 // invalid later.
2327 if (eState != ssSkipReference)
2329 *pSym++ = c;
2330 *pSym++ = *pSrc++;
2332 bAddToSymbol = false;
2335 else if (cSheetPrefix == c && nRefInName == 0)
2336 nRefInName |= kDollar;
2337 else if ('\'' == c)
2339 // TODO: The conventions' parseExternalName()
2340 // should handle quoted names, but as long as they
2341 // don't remove non-embedded quotes here.
2342 if (!(nRefInName & kName))
2344 nRefInName |= (kOpen | kName);
2345 bAddToSymbol = !(nRefInName & kDefName);
2347 else if (!(nRefInName & kOpen))
2349 DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
2350 "a ''' without the name being enclosed in '...' violates ODF spec");
2352 else if (nRefInName & kQuote)
2354 // escaped embedded quote
2355 nRefInName &= ~kQuote;
2357 else
2359 if ('\'' == pSrc[0])
2361 // escapes embedded quote
2362 nRefInName |= kQuote;
2364 else
2366 // quote not followed by quote => close
2367 nRefInName |= kClose;
2368 nRefInName &= ~kOpen;
2370 bAddToSymbol = !(nRefInName & kDefName);
2373 else if (cSheetSep == c && !(nRefInName & kOpen))
2375 // unquoted sheet name separator
2376 nRefInName |= kPast;
2377 if ('$' == pSrc[0] && '$' == pSrc[1])
2378 nRefInName |= kMarkAhead;
2380 else if (':' == c && !(nRefInName & kOpen))
2382 DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
2383 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2384 nRefInName = 0;
2385 ++mnPredetectedReference;
2387 else if (!(nRefInName & kName))
2389 // start unquoted name
2390 nRefInName |= kName;
2393 else if (':' == c)
2395 // range operator
2396 nRefInName = 0;
2397 ++mnPredetectedReference;
2399 if (bAddToSymbol && eState != ssSkipReference)
2400 *pSym++ = c; // everything is part of reference
2402 break;
2403 case ssStop:
2404 ; // nothing, prevent warning
2405 break;
2407 cLast = c;
2408 c = *pSrc;
2410 if ( bi18n )
2412 nSrcPos = sal::static_int_cast<xub_StrLen>( nSrcPos + nSpaces );
2413 String aSymbol;
2414 mnRangeOpPosInSymbol = -1;
2415 USHORT nErr = 0;
2418 bi18n = false;
2419 // special case (e.g. $'sheetname' in OOO A1)
2420 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2421 aSymbol += pStart[nSrcPos++];
2423 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass );
2425 if ( !aRes.TokenType )
2426 SetError( nErr = errIllegalChar ); // parsed chars as string
2427 if ( aRes.EndPos <= nSrcPos )
2428 { // ?!?
2429 SetError( nErr = errIllegalChar );
2430 nSrcPos = aFormula.Len();
2431 aSymbol.Erase();
2433 else
2435 aSymbol.Append( pStart + nSrcPos, (xub_StrLen)aRes.EndPos - nSrcPos );
2436 nSrcPos = (xub_StrLen) aRes.EndPos;
2437 c = pStart[nSrcPos];
2438 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2439 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2440 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2442 // One range operator restarts parsing for second reference.
2443 if (c == ':' && mnRangeOpPosInSymbol < 0)
2445 mnRangeOpPosInSymbol = aSymbol.Len();
2446 bi18n = true;
2448 if ( bi18n )
2449 aSymbol += pStart[nSrcPos++];
2451 } while ( bi18n && !nErr );
2452 xub_StrLen nLen = aSymbol.Len();
2453 if ( nLen >= MAXSTRLEN )
2455 SetError( errStringOverflow );
2456 nLen = MAXSTRLEN-1;
2458 lcl_UnicodeStrNCpy( cSymbol, aSymbol.GetBuffer(), nLen );
2460 else
2462 nSrcPos = sal::static_int_cast<xub_StrLen>( pSrc - pStart );
2463 *pSym = 0;
2465 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2467 // This is a trailing range operator, which is nonsense. Will be caught
2468 // in next round.
2469 mnRangeOpPosInSymbol = -1;
2470 *--pSym = 0;
2471 --nSrcPos;
2473 if ( bAutoCorrect )
2474 aCorrectedSymbol = cSymbol;
2475 if (bAutoIntersection && nSpaces > 1)
2476 --nSpaces; // replace '!!' with only one space
2477 return nSpaces;
2480 //---------------------------------------------------------------------------
2481 // Convert symbol to token
2482 //---------------------------------------------------------------------------
2484 BOOL ScCompiler::IsOpCode( const String& rName, bool bInArray )
2486 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
2487 BOOL bFound = (iLook != mxSymbols->getHashMap()->end());
2488 if (bFound)
2490 ScRawToken aToken;
2491 OpCode eOp = iLook->second;
2492 if (bInArray)
2494 if (rName.Equals(mxSymbols->getSymbol(ocArrayColSep)))
2495 eOp = ocArrayColSep;
2496 else if (rName.Equals(mxSymbols->getSymbol(ocArrayRowSep)))
2497 eOp = ocArrayRowSep;
2499 aToken.SetOpCode(eOp);
2500 pRawToken = aToken.Clone();
2502 else
2504 String aIntName;
2505 if (mxSymbols->hasExternals())
2507 // If symbols are set by filters get mapping to exact name.
2508 ExternalHashMap::const_iterator iExt(
2509 mxSymbols->getExternalHashMap()->find( rName));
2510 if (iExt != mxSymbols->getExternalHashMap()->end())
2512 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2513 aIntName = (*iExt).second;
2515 if (!aIntName.Len())
2517 // If that isn't found we might continue with rName lookup as a
2518 // last resort by just falling through to FindFunction(), but
2519 // it shouldn't happen if the map was setup correctly. Don't
2520 // waste time and bail out.
2521 return FALSE;
2524 if (!aIntName.Len())
2526 // Old (deprecated) addins first for legacy.
2527 USHORT nIndex;
2528 bFound = ScGlobal::GetFuncCollection()->SearchFunc( cSymbol, nIndex);
2529 if (bFound)
2531 ScRawToken aToken;
2532 aToken.SetExternal( cSymbol );
2533 pRawToken = aToken.Clone();
2535 else
2536 // bLocalFirst=FALSE for (English) upper full original name
2537 // (service.function)
2538 aIntName = ScGlobal::GetAddInCollection()->FindFunction(
2539 rName, !mxSymbols->isEnglish());
2541 if (aIntName.Len())
2543 ScRawToken aToken;
2544 aToken.SetExternal( aIntName.GetBuffer() ); // international name
2545 pRawToken = aToken.Clone();
2546 bFound = TRUE;
2549 OpCode eOp;
2550 if (bFound && ((eOp = pRawToken->GetOpCode()) == ocSub || eOp == ocNegSub))
2552 bool bShouldBeNegSub =
2553 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
2554 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
2555 eLastOp == ocArrayOpen ||
2556 eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
2557 if (bShouldBeNegSub && eOp == ocSub)
2558 pRawToken->NewOpCode( ocNegSub );
2559 //! if ocNegSub had ForceArray we'd have to set it here
2560 else if (!bShouldBeNegSub && eOp == ocNegSub)
2561 pRawToken->NewOpCode( ocSub );
2563 return bFound;
2566 BOOL ScCompiler::IsOpCode2( const String& rName )
2568 BOOL bFound = FALSE;
2569 USHORT i;
2571 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
2572 bFound = rName.EqualsAscii( pInternal[ i-ocInternalBegin ] );
2574 if (bFound)
2576 ScRawToken aToken;
2577 aToken.SetOpCode( (OpCode) --i );
2578 pRawToken = aToken.Clone();
2580 return bFound;
2583 BOOL ScCompiler::IsValue( const String& rSym )
2585 double fVal;
2586 sal_uInt32 nIndex = ( mxSymbols->isEnglish() ?
2587 pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ) : 0 );
2588 // ULONG nIndex = 0;
2589 //// ULONG nIndex = pDoc->GetFormatTable()->GetStandardIndex(ScGlobal::eLnge);
2590 if (pDoc->GetFormatTable()->IsNumberFormat( rSym, nIndex, fVal ) )
2592 USHORT nType = pDoc->GetFormatTable()->GetType(nIndex);
2594 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
2595 // Dates should never be entered directly and automatically converted
2596 // to serial, because the serial would be wrong if null-date changed.
2597 // Usually it wouldn't be accepted anyway because the date separator
2598 // clashed with other separators or operators.
2599 if (nType & (NUMBERFORMAT_TIME | NUMBERFORMAT_DATE))
2600 return FALSE;
2602 if (nType == NUMBERFORMAT_LOGICAL)
2604 const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
2605 while( *p == ' ' )
2606 p++;
2607 if (*p == '(')
2608 return FALSE; // Boolean function instead.
2611 if( aFormula.GetChar(nSrcPos) == '.' )
2612 // numerical sheet name?
2613 return FALSE;
2615 if( nType == NUMBERFORMAT_TEXT )
2616 // HACK: number too big!
2617 SetError( errIllegalArgument );
2618 ScRawToken aToken;
2619 aToken.SetDouble( fVal );
2620 pRawToken = aToken.Clone();
2621 return TRUE;
2623 else
2624 return FALSE;
2627 BOOL ScCompiler::IsString()
2629 register const sal_Unicode* p = cSymbol;
2630 while ( *p )
2631 p++;
2632 xub_StrLen nLen = sal::static_int_cast<xub_StrLen>( p - cSymbol - 1 );
2633 BOOL bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"'));
2634 if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1)
2636 SetError(errStringOverflow);
2637 return FALSE;
2639 if ( bQuote )
2641 cSymbol[nLen] = '\0';
2642 ScRawToken aToken;
2643 aToken.SetString( cSymbol+1 );
2644 pRawToken = aToken.Clone();
2645 return TRUE;
2647 return FALSE;
2651 BOOL ScCompiler::IsPredetectedReference( const String& rName )
2653 // Speedup documents with lots of broken references, e.g. sheet deleted.
2654 xub_StrLen nPos = rName.SearchAscii( "#REF!");
2655 if (nPos != STRING_NOTFOUND)
2657 /* TODO: this may be enhanced by reusing scan information from
2658 * NextSymbol(), the positions of quotes and special characters found
2659 * there for $'sheet'.A1:... could be stored in a vector. We don't
2660 * fully rescan here whether found positions are within single quotes
2661 * for performance reasons. This code does not check for possible
2662 * occurrences of insane "valid" sheet names like
2663 * 'haha.#REF!1fooledyou' and will generate an error on such. */
2664 if (nPos == 0)
2665 return false; // #REF!.AB42 or #REF!42 or #REF!#REF!
2666 sal_Unicode c = rName.GetChar(nPos-1); // before #REF!
2667 if ('$' == c)
2669 if (nPos == 1)
2670 return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
2671 c = rName.GetChar(nPos-2); // before $#REF!
2673 sal_Unicode c2 = rName.GetChar(nPos+5); // after #REF!
2674 switch (c)
2676 case '.':
2677 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
2678 return false; // sheet.#REF!42 or sheet.#REF!#REF!
2679 break;
2680 case ':':
2681 if (mnPredetectedReference > 1 &&
2682 ('.' == c2 || '$' == c2 || '#' == c2 ||
2683 ('0' <= c2 && c2 <= '9')))
2684 return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
2685 break;
2686 default:
2687 if ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) &&
2688 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
2689 return false; // AB#REF!: or AB#REF!
2692 switch (mnPredetectedReference)
2694 case 1:
2695 return IsSingleReference( rName);
2696 case 2:
2697 return IsDoubleReference( rName);
2699 return false;
2703 BOOL ScCompiler::IsDoubleReference( const String& rName )
2705 ScRange aRange( aPos, aPos );
2706 const ScAddress::Details aDetails( pConv->meConv, aPos );
2707 ScAddress::ExternalInfo aExtInfo;
2708 USHORT nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2709 if( nFlags & SCA_VALID )
2711 ScRawToken aToken;
2712 ScComplexRefData aRef;
2713 aRef.InitRange( aRange );
2714 aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2715 aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2716 aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2717 if ( !(nFlags & SCA_VALID_TAB) )
2718 aRef.Ref1.SetTabDeleted( TRUE ); // #REF!
2719 aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2720 aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 );
2721 aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 );
2722 aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 );
2723 if ( !(nFlags & SCA_VALID_TAB2) )
2724 aRef.Ref2.SetTabDeleted( TRUE ); // #REF!
2725 aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 );
2726 aRef.CalcRelFromAbs( aPos );
2727 if (aExtInfo.mbExternal)
2729 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2730 const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2731 aToken.SetExternalDoubleRef(
2732 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2734 else
2736 aToken.SetDoubleReference(aRef);
2738 pRawToken = aToken.Clone();
2741 return ( nFlags & SCA_VALID ) != 0;
2745 BOOL ScCompiler::IsSingleReference( const String& rName )
2747 ScAddress aAddr( aPos );
2748 const ScAddress::Details aDetails( pConv->meConv, aPos );
2749 ScAddress::ExternalInfo aExtInfo;
2750 USHORT nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2751 // Something must be valid in order to recognize Sheet1.blah or blah.a1
2752 // as a (wrong) reference.
2753 if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) )
2755 ScRawToken aToken;
2756 ScSingleRefData aRef;
2757 aRef.InitAddress( aAddr );
2758 aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2759 aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2760 aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2761 aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2762 // the reference is really invalid
2763 if( !( nFlags & SCA_VALID ) )
2765 if( !( nFlags & SCA_VALID_COL ) )
2766 aRef.nCol = MAXCOL+1;
2767 if( !( nFlags & SCA_VALID_ROW ) )
2768 aRef.nRow = MAXROW+1;
2769 if( !( nFlags & SCA_VALID_TAB ) )
2770 aRef.nTab = MAXTAB+3;
2771 nFlags |= SCA_VALID;
2773 aRef.CalcRelFromAbs( aPos );
2775 if (aExtInfo.mbExternal)
2777 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2778 const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2779 aToken.SetExternalSingleRef(
2780 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2782 else
2783 aToken.SetSingleReference(aRef);
2784 pRawToken = aToken.Clone();
2787 return ( nFlags & SCA_VALID ) != 0;
2791 BOOL ScCompiler::IsReference( const String& rName )
2793 // Has to be called before IsValue
2794 sal_Unicode ch1 = rName.GetChar(0);
2795 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' :
2796 ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0) );
2797 if ( ch1 == cDecSep )
2798 return FALSE;
2799 // Who was that imbecile introducing '.' as the sheet name separator!?!
2800 if ( CharClass::isAsciiNumeric( ch1 ) )
2802 // Numerical sheet name is valid.
2803 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
2804 // Don't create a #REF! of values. But also do not bail out on
2805 // something like 3:3, meaning entire row 3.
2808 const xub_StrLen nPos = ScGlobal::FindUnquoted( rName, '.');
2809 if ( nPos == STRING_NOTFOUND )
2811 if (ScGlobal::FindUnquoted( rName, ':') != STRING_NOTFOUND)
2812 break; // may be 3:3, continue as usual
2813 return FALSE;
2815 sal_Unicode const * const pTabSep = rName.GetBuffer() + nPos;
2816 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
2817 if ( !(ch2 == '$' || CharClass::isAsciiAlpha( ch2 )) )
2818 return FALSE;
2819 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
2820 && (GetCharTableFlags( pTabSep[2] ) & SC_COMPILER_C_VALUE_EXP) )
2821 { // #91053#
2822 // If it is an 1.E2 expression check if "1" is an existent sheet
2823 // name. If so, a desired value 1.E2 would have to be entered as
2824 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
2825 // require numerical sheet names always being entered quoted, which
2826 // is not desirable (too many 1999, 2000, 2001 sheets in use).
2827 // Furthermore, XML files created with versions prior to SRC640e
2828 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
2829 // and would produce wrong formulas if the conditions here are met.
2830 // If you can live with these restrictions you may remove the
2831 // check and return an unconditional FALSE.
2832 String aTabName( rName.Copy( 0, nPos ) );
2833 SCTAB nTab;
2834 if ( !pDoc->GetTable( aTabName, nTab ) )
2835 return FALSE;
2836 // If sheet "1" exists and the expression is 1.E+2 continue as
2837 // usual, the ScRange/ScAddress parser will take care of it.
2839 } while(0);
2842 if (IsSingleReference( rName))
2843 return true;
2845 // Though the range operator is handled explicitly, when encountering
2846 // something like Sheet1.A:A we will have to treat it as one entity if it
2847 // doesn't pass as single cell reference.
2848 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
2850 if (IsDoubleReference( rName))
2851 return true;
2852 // Now try with a symbol up to the range operator, rewind source
2853 // position.
2854 sal_Int32 nLen = mnRangeOpPosInSymbol;
2855 while (cSymbol[++nLen])
2857 cSymbol[mnRangeOpPosInSymbol] = 0;
2858 nSrcPos -= static_cast<xub_StrLen>(nLen - mnRangeOpPosInSymbol);
2859 mnRangeOpPosInSymbol = -1;
2860 mbRewind = true;
2861 return true; // end all checks
2863 else
2865 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel sickness,
2866 // mnRangeOpPosInSymbol did not catch the range operator as it is
2867 // within a quoted name.
2868 switch (pConv->meConv)
2870 case FormulaGrammar::CONV_XL_A1:
2871 case FormulaGrammar::CONV_XL_R1C1:
2872 case FormulaGrammar::CONV_XL_OOX:
2873 if (rName.GetChar(0) == '\'' && IsDoubleReference( rName))
2874 return true;
2875 break;
2876 default:
2877 ; // nothing
2880 return false;
2883 BOOL ScCompiler::IsMacro( const String& rName )
2885 StarBASIC* pObj = 0;
2886 SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
2888 SfxApplication* pSfxApp = SFX_APP();
2889 pSfxApp->EnterBasicCall(); // initialize document's BASIC
2891 if( pDocSh )//XXX
2892 pObj = pDocSh->GetBasic();
2893 else
2894 pObj = pSfxApp->GetBasic();
2896 SbxMethod* pMeth = (SbxMethod*) pObj->Find( rName, SbxCLASS_METHOD );
2897 if( !pMeth )
2899 pSfxApp->LeaveBasicCall();
2900 return FALSE;
2902 // It really should be a BASIC function!
2903 if( pMeth->GetType() == SbxVOID
2904 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
2905 || !pMeth->ISA(SbMethod) )
2907 pSfxApp->LeaveBasicCall();
2908 return FALSE;
2910 ScRawToken aToken;
2911 aToken.SetExternal( rName.GetBuffer() );
2912 aToken.eOp = ocMacro;
2913 pRawToken = aToken.Clone();
2914 pSfxApp->LeaveBasicCall();
2915 return TRUE;
2918 BOOL ScCompiler::IsNamedRange( const String& rUpperName )
2920 // IsNamedRange is called only from NextNewToken, with an upper-case string
2922 USHORT n;
2923 ScRangeName* pRangeName = pDoc->GetRangeName();
2924 if (pRangeName->SearchNameUpper( rUpperName, n ) )
2926 ScRangeData* pData = (*pRangeName)[n];
2927 ScRawToken aToken;
2928 aToken.SetName( pData->GetIndex() );
2929 pRawToken = aToken.Clone();
2930 return TRUE;
2932 else
2933 return FALSE;
2936 bool ScCompiler::IsExternalNamedRange( const String& rSymbol )
2938 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
2939 * correctly parses external named references in OOo, as required per RFE
2940 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
2941 * spec first. Until then don't pretend to support external names that
2942 * wouldn't survive a save and reload cycle, return false instead. */
2944 #if 1
2945 if (!pConv)
2946 return false;
2948 String aFile, aName;
2949 if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
2950 return false;
2952 ScRawToken aToken;
2953 if (aFile.Len() > MAXSTRLEN || aName.Len() > MAXSTRLEN)
2954 return false;
2956 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2957 pRefMgr->convertToAbsName(aFile);
2958 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
2959 if (!pRefMgr->getRangeNameTokens(nFileId, aName).get())
2960 // range name doesn't exist in the source document.
2961 return false;
2963 const String* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
2964 aToken.SetExternalName(nFileId, pRealName ? *pRealName : aName);
2965 pRawToken = aToken.Clone();
2966 return true;
2967 #else
2968 (void)rSymbol;
2969 return false;
2970 #endif
2973 BOOL ScCompiler::IsDBRange( const String& rName )
2975 USHORT n;
2976 ScDBCollection* pDBColl = pDoc->GetDBCollection();
2977 if (pDBColl->SearchName( rName, n ) )
2979 ScDBData* pData = (*pDBColl)[n];
2980 ScRawToken aToken;
2981 aToken.SetName( pData->GetIndex() );
2982 aToken.eOp = ocDBArea;
2983 pRawToken = aToken.Clone();
2984 return TRUE;
2986 else
2987 return FALSE;
2990 BOOL ScCompiler::IsColRowName( const String& rName )
2992 BOOL bInList = FALSE;
2993 BOOL bFound = FALSE;
2994 ScSingleRefData aRef;
2995 String aName( rName );
2996 DeQuote( aName );
2997 SCTAB nThisTab = aPos.Tab();
2998 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
2999 { // #50300# first check ranges on this sheet, in case of duplicated names
3000 for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3002 ScRangePairList* pRL;
3003 if ( !jRow )
3004 pRL = pDoc->GetColNameRanges();
3005 else
3006 pRL = pDoc->GetRowNameRanges();
3007 for ( ScRangePair* pR = pRL->First(); pR && !bInList; pR = pRL->Next() )
3009 const ScRange& rNameRange = pR->GetRange(0);
3010 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
3011 nThisTab <= rNameRange.aEnd.Tab()) )
3012 continue; // for
3013 ScCellIterator aIter( pDoc, rNameRange );
3014 for ( ScBaseCell* pCell = aIter.GetFirst(); pCell && !bInList;
3015 pCell = aIter.GetNext() )
3017 // Don't crash if cell (via CompileNameFormula) encounters
3018 // a formula cell without code and
3019 // HasStringData/Interpret/Compile is executed and all that
3020 // recursive..
3021 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3022 CellType eType = pCell->GetCellType();
3023 BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ?
3024 ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
3025 && ((ScFormulaCell*)pCell)->aPos != aPos // noIter
3026 : TRUE ) );
3027 if ( bOk && pCell->HasStringData() )
3029 String aStr;
3030 switch ( eType )
3032 case CELLTYPE_STRING:
3033 ((ScStringCell*)pCell)->GetString( aStr );
3034 break;
3035 case CELLTYPE_FORMULA:
3036 ((ScFormulaCell*)pCell)->GetString( aStr );
3037 break;
3038 case CELLTYPE_EDIT:
3039 ((ScEditCell*)pCell)->GetString( aStr );
3040 break;
3041 case CELLTYPE_NONE:
3042 case CELLTYPE_VALUE:
3043 case CELLTYPE_NOTE:
3044 case CELLTYPE_SYMBOLS:
3045 #if DBG_UTIL
3046 case CELLTYPE_DESTROYED:
3047 #endif
3048 ; // nothing, prevent compiler warning
3049 break;
3051 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3053 aRef.InitFlags();
3054 aRef.nCol = aIter.GetCol();
3055 aRef.nRow = aIter.GetRow();
3056 aRef.nTab = aIter.GetTab();
3057 if ( !jRow )
3058 aRef.SetColRel( TRUE ); // ColName
3059 else
3060 aRef.SetRowRel( TRUE ); // RowName
3061 aRef.CalcRelFromAbs( aPos );
3062 bInList = bFound = TRUE;
3069 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
3070 { // search in current sheet
3071 long nDistance = 0, nMax = 0;
3072 long nMyCol = (long) aPos.Col();
3073 long nMyRow = (long) aPos.Row();
3074 BOOL bTwo = FALSE;
3075 ScAddress aOne( 0, 0, aPos.Tab() );
3076 ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
3078 ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
3079 if ( pNameCache )
3081 // #b6355215# use GetNameOccurences to collect all positions of aName on the sheet
3082 // (only once), similar to the outer part of the loop in the "else" branch.
3084 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurences( aName, aPos.Tab() );
3086 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3087 // The order of addresses in the vector is the same as from ScCellIterator.
3089 ScAutoNameAddresses::const_iterator aEnd(rAddresses.end());
3090 for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter )
3092 ScAddress aAddress( *aAdrIter ); // cell address with an equal string
3094 if ( bFound )
3095 { // stop if everything else is further away
3096 if ( nMax < (long)aAddress.Col() )
3097 break; // aIter
3099 if ( aAddress != aPos )
3101 // same treatment as in isEqual case below
3103 SCCOL nCol = aAddress.Col();
3104 SCROW nRow = aAddress.Row();
3105 long nC = nMyCol - nCol;
3106 long nR = nMyRow - nRow;
3107 if ( bFound )
3109 long nD = nC * nC + nR * nR;
3110 if ( nD < nDistance )
3112 if ( nC < 0 || nR < 0 )
3113 { // right or below
3114 bTwo = TRUE;
3115 aTwo.Set( nCol, nRow, aAddress.Tab() );
3116 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3117 nDistance = nD;
3119 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3121 // upper left, only if not further up than the
3122 // current entry and nMyRow is below (CellIter
3123 // runs column-wise)
3124 bTwo = FALSE;
3125 aOne.Set( nCol, nRow, aAddress.Tab() );
3126 nMax = Max( nMyCol + nC, nMyRow + nR );
3127 nDistance = nD;
3131 else
3133 aOne.Set( nCol, nRow, aAddress.Tab() );
3134 nDistance = nC * nC + nR * nR;
3135 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3137 bFound = TRUE;
3141 else
3143 ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
3144 for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() )
3146 if ( bFound )
3147 { // stop if everything else is further away
3148 if ( nMax < (long)aIter.GetCol() )
3149 break; // aIter
3151 CellType eType = pCell->GetCellType();
3152 BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ?
3153 ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
3154 && ((ScFormulaCell*)pCell)->aPos != aPos // noIter
3155 : TRUE ) );
3156 if ( bOk && pCell->HasStringData() )
3158 String aStr;
3159 switch ( eType )
3161 case CELLTYPE_STRING:
3162 ((ScStringCell*)pCell)->GetString( aStr );
3163 break;
3164 case CELLTYPE_FORMULA:
3165 ((ScFormulaCell*)pCell)->GetString( aStr );
3166 break;
3167 case CELLTYPE_EDIT:
3168 ((ScEditCell*)pCell)->GetString( aStr );
3169 break;
3170 case CELLTYPE_NONE:
3171 case CELLTYPE_VALUE:
3172 case CELLTYPE_NOTE:
3173 case CELLTYPE_SYMBOLS:
3174 #if DBG_UTIL
3175 case CELLTYPE_DESTROYED:
3176 #endif
3177 ; // nothing, prevent compiler warning
3178 break;
3180 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3182 SCCOL nCol = aIter.GetCol();
3183 SCROW nRow = aIter.GetRow();
3184 long nC = nMyCol - nCol;
3185 long nR = nMyRow - nRow;
3186 if ( bFound )
3188 long nD = nC * nC + nR * nR;
3189 if ( nD < nDistance )
3191 if ( nC < 0 || nR < 0 )
3192 { // right or below
3193 bTwo = TRUE;
3194 aTwo.Set( nCol, nRow, aIter.GetTab() );
3195 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3196 nDistance = nD;
3198 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3200 // upper left, only if not further up than the
3201 // current entry and nMyRow is below (CellIter
3202 // runs column-wise)
3203 bTwo = FALSE;
3204 aOne.Set( nCol, nRow, aIter.GetTab() );
3205 nMax = Max( nMyCol + nC, nMyRow + nR );
3206 nDistance = nD;
3210 else
3212 aOne.Set( nCol, nRow, aIter.GetTab() );
3213 nDistance = nC * nC + nR * nR;
3214 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3216 bFound = TRUE;
3222 if ( bFound )
3224 ScAddress aAdr;
3225 if ( bTwo )
3227 if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() )
3228 aAdr = aOne; // upper left takes precedence
3229 else
3231 if ( nMyCol < (long)aOne.Col() )
3232 { // two to the right
3233 if ( nMyRow >= (long)aTwo.Row() )
3234 aAdr = aTwo; // directly right
3235 else
3236 aAdr = aOne;
3238 else
3239 { // two below or below and right, take the nearest
3240 long nC1 = nMyCol - aOne.Col();
3241 long nR1 = nMyRow - aOne.Row();
3242 long nC2 = nMyCol - aTwo.Col();
3243 long nR2 = nMyRow - aTwo.Row();
3244 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3245 aAdr = aOne;
3246 else
3247 aAdr = aTwo;
3251 else
3252 aAdr = aOne;
3253 aRef.InitAddress( aAdr );
3254 if ( (aRef.nRow != MAXROW && pDoc->HasStringData(
3255 aRef.nCol, aRef.nRow + 1, aRef.nTab ))
3256 || (aRef.nRow != 0 && pDoc->HasStringData(
3257 aRef.nCol, aRef.nRow - 1, aRef.nTab )) )
3258 aRef.SetRowRel( TRUE ); // RowName
3259 else
3260 aRef.SetColRel( TRUE ); // ColName
3261 aRef.CalcRelFromAbs( aPos );
3264 if ( bFound )
3266 ScRawToken aToken;
3267 aToken.SetSingleReference( aRef );
3268 aToken.eOp = ocColRowName;
3269 pRawToken = aToken.Clone();
3270 return TRUE;
3272 else
3273 return FALSE;
3276 BOOL ScCompiler::IsBoolean( const String& rName )
3278 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) );
3279 if( iLook != mxSymbols->getHashMap()->end() &&
3280 ((*iLook).second == ocTrue ||
3281 (*iLook).second == ocFalse) )
3283 ScRawToken aToken;
3284 aToken.SetOpCode( (*iLook).second );
3285 pRawToken = aToken.Clone();
3286 return TRUE;
3288 else
3289 return FALSE;
3292 //---------------------------------------------------------------------------
3294 void ScCompiler::AutoCorrectParsedSymbol()
3296 xub_StrLen nPos = aCorrectedSymbol.Len();
3297 if ( nPos )
3299 nPos--;
3300 const sal_Unicode cQuote = '\"';
3301 const sal_Unicode cx = 'x';
3302 const sal_Unicode cX = 'X';
3303 sal_Unicode c1 = aCorrectedSymbol.GetChar( 0 );
3304 sal_Unicode c2 = aCorrectedSymbol.GetChar( nPos );
3305 if ( c1 == cQuote && c2 != cQuote )
3306 { // "...
3307 // What's not a word doesn't belong to it.
3308 // Don't be pedantic: c < 128 should be sufficient here.
3309 while ( nPos && ((aCorrectedSymbol.GetChar(nPos) < 128) &&
3310 ((GetCharTableFlags( aCorrectedSymbol.GetChar(nPos) ) &
3311 (SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) )
3312 nPos--;
3313 if ( nPos == MAXSTRLEN - 2 )
3314 aCorrectedSymbol.SetChar( nPos, cQuote ); // '"' the 255th character
3315 else
3316 aCorrectedSymbol.Insert( cQuote, nPos + 1 );
3317 bCorrected = TRUE;
3319 else if ( c1 != cQuote && c2 == cQuote )
3320 { // ..."
3321 aCorrectedSymbol.Insert( cQuote, 0 );
3322 bCorrected = TRUE;
3324 else if ( nPos == 0 && (c1 == cx || c1 == cX) )
3325 { // x => *
3326 aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
3327 bCorrected = TRUE;
3329 else if ( (GetCharTableFlags( c1 ) & SC_COMPILER_C_CHAR_VALUE)
3330 && (GetCharTableFlags( c2 ) & SC_COMPILER_C_CHAR_VALUE) )
3332 xub_StrLen nXcount;
3333 if ( (nXcount = aCorrectedSymbol.GetTokenCount( cx )) > 1 )
3334 { // x => *
3335 xub_StrLen nIndex = 0;
3336 sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
3337 while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
3338 cx, c, nIndex )) != STRING_NOTFOUND )
3339 nIndex++;
3340 bCorrected = TRUE;
3342 if ( (nXcount = aCorrectedSymbol.GetTokenCount( cX )) > 1 )
3343 { // X => *
3344 xub_StrLen nIndex = 0;
3345 sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
3346 while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
3347 cX, c, nIndex )) != STRING_NOTFOUND )
3348 nIndex++;
3349 bCorrected = TRUE;
3352 else
3354 String aSymbol( aCorrectedSymbol );
3355 String aDoc;
3356 xub_StrLen nPosition;
3357 if ( aSymbol.GetChar(0) == '\''
3358 && ((nPosition = aSymbol.SearchAscii( "'#" )) != STRING_NOTFOUND) )
3359 { // Split off 'Doc'#, may be d:\... or whatever
3360 aDoc = aSymbol.Copy( 0, nPosition + 2 );
3361 aSymbol.Erase( 0, nPosition + 2 );
3363 xub_StrLen nRefs = aSymbol.GetTokenCount( ':' );
3364 BOOL bColons;
3365 if ( nRefs > 2 )
3366 { // duplicated or too many ':'? B:2::C10 => B2:C10
3367 bColons = TRUE;
3368 xub_StrLen nIndex = 0;
3369 String aTmp1( aSymbol.GetToken( 0, ':', nIndex ) );
3370 xub_StrLen nLen1 = aTmp1.Len();
3371 String aSym, aTmp2;
3372 BOOL bLastAlp, bNextNum;
3373 bLastAlp = bNextNum = TRUE;
3374 xub_StrLen nStrip = 0;
3375 xub_StrLen nCount = nRefs;
3376 for ( xub_StrLen j=1; j<nCount; j++ )
3378 aTmp2 = aSymbol.GetToken( 0, ':', nIndex );
3379 xub_StrLen nLen2 = aTmp2.Len();
3380 if ( nLen1 || nLen2 )
3382 if ( nLen1 )
3384 aSym += aTmp1;
3385 bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
3387 if ( nLen2 )
3389 bNextNum = CharClass::isAsciiNumeric( aTmp2 );
3390 if ( bLastAlp == bNextNum && nStrip < 1 )
3392 // Must be alternating number/string, only
3393 // strip within a reference.
3394 nRefs--;
3395 nStrip++;
3397 else
3399 xub_StrLen nSymLen = aSym.Len();
3400 if ( nSymLen
3401 && (aSym.GetChar( nSymLen - 1 ) != ':') )
3402 aSym += ':';
3403 nStrip = 0;
3405 bLastAlp = !bNextNum;
3407 else
3408 { // ::
3409 nRefs--;
3410 if ( nLen1 )
3411 { // B10::C10 ? append ':' on next round
3412 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
3413 nStrip++;
3415 bNextNum = !bLastAlp;
3417 aTmp1 = aTmp2;
3418 nLen1 = nLen2;
3420 else
3421 nRefs--;
3423 aSymbol = aSym;
3424 aSymbol += aTmp1;
3426 else
3427 bColons = FALSE;
3428 if ( nRefs && nRefs <= 2 )
3429 { // reference twisted? 4A => A4 etc.
3430 String aTab[2], aRef[2];
3431 const ScAddress::Details aDetails( pConv->meConv, aPos );
3432 if ( nRefs == 2 )
3434 aRef[0] = aSymbol.GetToken( 0, ':' );
3435 aRef[1] = aSymbol.GetToken( 1, ':' );
3437 else
3438 aRef[0] = aSymbol;
3440 BOOL bChanged = FALSE;
3441 BOOL bOk = TRUE;
3442 USHORT nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW;
3443 for ( int j=0; j<nRefs; j++ )
3445 xub_StrLen nTmp = 0;
3446 xub_StrLen nDotPos = STRING_NOTFOUND;
3447 while ( (nTmp = aRef[j].Search( '.', nTmp )) != STRING_NOTFOUND )
3448 nDotPos = nTmp++; // the last one counts
3449 if ( nDotPos != STRING_NOTFOUND )
3451 aTab[j] = aRef[j].Copy( 0, nDotPos + 1 ); // with '.'
3452 aRef[j].Erase( 0, nDotPos + 1 );
3454 String aOld( aRef[j] );
3455 String aStr2;
3456 const sal_Unicode* p = aRef[j].GetBuffer();
3457 while ( *p && CharClass::isAsciiNumeric( *p ) )
3458 aStr2 += *p++;
3459 aRef[j] = String( p );
3460 aRef[j] += aStr2;
3461 if ( bColons || aRef[j] != aOld )
3463 bChanged = TRUE;
3464 ScAddress aAdr;
3465 bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
3468 if ( bChanged && bOk )
3470 aCorrectedSymbol = aDoc;
3471 aCorrectedSymbol += aTab[0];
3472 aCorrectedSymbol += aRef[0];
3473 if ( nRefs == 2 )
3475 aCorrectedSymbol += ':';
3476 aCorrectedSymbol += aTab[1];
3477 aCorrectedSymbol += aRef[1];
3479 bCorrected = TRUE;
3486 inline bool lcl_UpperAsciiOrI18n( String& rUpper, const String& rOrg, FormulaGrammar::Grammar eGrammar )
3488 if (FormulaGrammar::isODFF( eGrammar ))
3490 // ODFF has a defined set of English function names, avoid i18n
3491 // overhead.
3492 rUpper = rOrg;
3493 rUpper.ToUpperAscii();
3494 return true;
3496 else
3498 rUpper = ScGlobal::pCharClass->upper( rOrg );
3499 return false;
3503 BOOL ScCompiler::NextNewToken( bool bInArray )
3505 bool bAllowBooleans = bInArray;
3506 xub_StrLen nSpaces = NextSymbol(bInArray);
3508 #if 0
3509 fprintf( stderr, "NextNewToken '%s' (spaces = %d)\n",
3510 rtl::OUStringToOString( cSymbol, RTL_TEXTENCODING_UTF8 ).getStr(), nSpaces );
3511 #endif
3513 if (!cSymbol[0])
3514 return false;
3516 if( nSpaces )
3518 ScRawToken aToken;
3519 aToken.SetOpCode( ocSpaces );
3520 aToken.sbyte.cByte = (BYTE) ( nSpaces > 255 ? 255 : nSpaces );
3521 if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
3523 SetError(errCodeOverflow);
3524 return false;
3528 // Short cut for references when reading ODF to speedup things.
3529 if (mnPredetectedReference)
3531 String aStr( cSymbol);
3532 if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
3534 /* TODO: it would be nice to generate a #REF! error here, which
3535 * would need an ocBad token with additional error value.
3536 * FormulaErrorToken wouldn't do because we want to preserve the
3537 * original string containing partial valid address
3538 * information. */
3539 ScRawToken aToken;
3540 aToken.SetString( aStr.GetBuffer() );
3541 aToken.NewOpCode( ocBad );
3542 pRawToken = aToken.Clone();
3544 return true;
3547 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
3548 !bAutoCorrect )
3549 { // #101100# special case to speed up broken [$]#REF documents
3550 /* FIXME: ISERROR(#REF!) would be valid and TRUE and the formula to
3551 * be processed as usual. That would need some special treatment,
3552 * also in NextSymbol() because of possible combinations of
3553 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
3554 * handled by IsPredetectedReference(), this case here remains for
3555 * manual/API input. */
3556 String aBad( aFormula.Copy( nSrcPos-1 ) );
3557 eLastOp = pArr->AddBad( aBad )->GetOpCode();
3558 return false;
3561 if( IsString() )
3562 return true;
3564 bool bMayBeFuncName;
3565 bool bAsciiNonAlnum; // operators, separators, ...
3566 if ( cSymbol[0] < 128 )
3568 bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] );
3569 bAsciiNonAlnum = !bMayBeFuncName && !CharClass::isAsciiDigit( cSymbol[0] );
3571 else
3573 String aTmpStr( cSymbol[0] );
3574 bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
3575 bAsciiNonAlnum = false;
3577 if ( bMayBeFuncName )
3579 // a function name must be followed by a parenthesis
3580 const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
3581 while( *p == ' ' )
3582 p++;
3583 bMayBeFuncName = ( *p == '(' );
3586 #if 0
3587 fprintf( stderr, "Token '%s'\n",
3588 rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() );
3589 #endif
3591 // #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
3592 // IsReference().
3594 String aUpper;
3598 mbRewind = false;
3599 const String aOrg( cSymbol );
3601 if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray ))
3602 return true;
3604 aUpper.Erase();
3605 bool bAsciiUpper = false;
3606 if (bMayBeFuncName)
3608 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3609 if (IsOpCode( aUpper, bInArray ))
3610 return true;
3613 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
3614 // referred => IsReference() before IsValue().
3615 // Preserve case of file names in external references.
3616 if (IsReference( aOrg ))
3618 if (mbRewind) // Range operator, but no direct reference.
3619 continue; // do; up to range operator.
3620 return true;
3623 if (!aUpper.Len())
3624 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3626 // IsBoolean() before IsValue() to catch inline bools without the kludge
3627 // for inline arrays.
3628 if (bAllowBooleans && IsBoolean( aUpper ))
3629 return true;
3631 if (IsValue( aUpper ))
3632 return true;
3634 // User defined names and such do need i18n upper also in ODF.
3635 if (bAsciiUpper)
3636 aUpper = ScGlobal::pCharClass->upper( aOrg );
3638 if (IsNamedRange( aUpper ))
3639 return true;
3640 // Preserve case of file names in external references.
3641 if (IsExternalNamedRange( aOrg ))
3642 return true;
3643 if (IsDBRange( aUpper ))
3644 return true;
3645 if (IsColRowName( aUpper ))
3646 return true;
3647 if (bMayBeFuncName && IsMacro( aUpper ))
3648 return true;
3649 if (bMayBeFuncName && IsOpCode2( aUpper ))
3650 return true;
3652 } while (mbRewind);
3654 if ( mbExtendedErrorDetection )
3656 // set an error and end compilation
3657 SetError( errNoName );
3658 return false;
3661 // Provide single token information and continue. Do not set an error, that
3662 // would prematurely end compilation. Simple unknown names are handled by
3663 // the interpreter.
3664 ScGlobal::pCharClass->toLower( aUpper );
3665 ScRawToken aToken;
3666 aToken.SetString( aUpper.GetBuffer() );
3667 aToken.NewOpCode( ocBad );
3668 pRawToken = aToken.Clone();
3669 if ( bAutoCorrect )
3670 AutoCorrectParsedSymbol();
3671 return true;
3674 void ScCompiler::CreateStringFromXMLTokenArray( String& rFormula, String& rFormulaNmsp )
3676 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
3677 USHORT nExpectedCount = bExternal ? 2 : 1;
3678 DBG_ASSERT( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
3679 if( pArr->GetLen() == nExpectedCount )
3681 FormulaToken** ppTokens = pArr->GetArray();
3682 // string tokens expected, GetString() will assert if token type is wrong
3683 rFormula = ppTokens[ 0 ]->GetString();
3684 if( bExternal )
3685 rFormulaNmsp = ppTokens[ 1 ]->GetString();
3689 ScTokenArray* ScCompiler::CompileString( const String& rFormula )
3691 #if 0
3692 fprintf( stderr, "CompileString '%s'\n",
3693 rtl::OUStringToOString( rFormula, RTL_TEXTENCODING_UTF8 ).getStr() );
3694 #endif
3696 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
3697 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
3698 SetGrammar( FormulaGrammar::GRAM_PODF );
3700 ScTokenArray aArr;
3701 pArr = &aArr;
3702 aFormula = rFormula;
3704 aFormula.EraseLeadingChars();
3705 aFormula.EraseTrailingChars();
3706 nSrcPos = 0;
3707 bCorrected = FALSE;
3708 if ( bAutoCorrect )
3710 aCorrectedFormula.Erase();
3711 aCorrectedSymbol.Erase();
3713 BYTE nForced = 0; // ==formula forces recalc even if cell is not visible
3714 if( aFormula.GetChar(nSrcPos) == '=' )
3716 nSrcPos++;
3717 nForced++;
3718 if ( bAutoCorrect )
3719 aCorrectedFormula += '=';
3721 if( aFormula.GetChar(nSrcPos) == '=' )
3723 nSrcPos++;
3724 nForced++;
3725 if ( bAutoCorrect )
3726 aCorrectedFormula += '=';
3728 struct FunctionStack
3730 OpCode eOp;
3731 short nPar;
3733 // FunctionStack only used if PODF!
3734 bool bPODF = FormulaGrammar::isPODF( meGrammar);
3735 const size_t nAlloc = 512;
3736 FunctionStack aFuncs[ nAlloc ];
3737 FunctionStack* pFunctionStack = (bPODF && rFormula.Len() > nAlloc ?
3738 new FunctionStack[ rFormula.Len() ] : &aFuncs[0]);
3739 pFunctionStack[0].eOp = ocNone;
3740 pFunctionStack[0].nPar = 0;
3741 size_t nFunction = 0;
3742 short nBrackets = 0;
3743 bool bInArray = false;
3744 eLastOp = ocOpen;
3745 while( NextNewToken( bInArray ) )
3747 const OpCode eOp = pRawToken->GetOpCode();
3748 switch (eOp)
3750 case ocOpen:
3752 ++nBrackets;
3753 if (bPODF)
3755 ++nFunction;
3756 pFunctionStack[ nFunction ].eOp = eLastOp;
3757 pFunctionStack[ nFunction ].nPar = 0;
3760 break;
3761 case ocClose:
3763 if( !nBrackets )
3765 SetError( errPairExpected );
3766 if ( bAutoCorrect )
3768 bCorrected = TRUE;
3769 aCorrectedSymbol.Erase();
3772 else
3773 nBrackets--;
3774 if (bPODF && nFunction)
3775 --nFunction;
3777 break;
3778 case ocSep:
3780 if (bPODF)
3781 ++pFunctionStack[ nFunction ].nPar;
3783 break;
3784 case ocArrayOpen:
3786 if( bInArray )
3787 SetError( errNestedArray );
3788 else
3789 bInArray = true;
3790 // Don't count following column separator as parameter separator.
3791 if (bPODF)
3793 ++nFunction;
3794 pFunctionStack[ nFunction ].eOp = eOp;
3795 pFunctionStack[ nFunction ].nPar = 0;
3798 break;
3799 case ocArrayClose:
3801 if( bInArray )
3803 bInArray = false;
3805 else
3807 SetError( errPairExpected );
3808 if ( bAutoCorrect )
3810 bCorrected = TRUE;
3811 aCorrectedSymbol.Erase();
3814 if (bPODF && nFunction)
3815 --nFunction;
3817 default:
3818 break;
3820 if( (eLastOp == ocSep ||
3821 eLastOp == ocArrayRowSep ||
3822 eLastOp == ocArrayColSep ||
3823 eLastOp == ocArrayOpen) &&
3824 (eOp == ocSep ||
3825 eOp == ocArrayRowSep ||
3826 eOp == ocArrayColSep ||
3827 eOp == ocArrayClose) )
3829 // FIXME: should we check for known functions with optional empty
3830 // args so the correction dialog can do better?
3831 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
3833 SetError(errCodeOverflow); break;
3836 if (bPODF)
3838 /* TODO: for now this is the only PODF adapter. If there were more,
3839 * factor this out. */
3840 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
3841 if (eOp == ocSep &&
3842 pFunctionStack[ nFunction ].eOp == ocAddress &&
3843 pFunctionStack[ nFunction ].nPar == 3)
3845 if (!static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep,ocSep)) ||
3846 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
3848 SetError(errCodeOverflow); break;
3850 ++pFunctionStack[ nFunction ].nPar;
3853 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( pRawToken->CreateToken());
3854 if (!pNewToken)
3856 SetError(errCodeOverflow); break;
3858 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush &&
3859 pNewToken->GetType() == svSingleRef)
3860 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
3861 eLastOp = pRawToken->GetOpCode();
3862 if ( bAutoCorrect )
3863 aCorrectedFormula += aCorrectedSymbol;
3865 if ( mbCloseBrackets )
3867 if( bInArray )
3869 FormulaByteToken aToken( ocArrayClose );
3870 if( !pArr->AddToken( aToken ) )
3872 SetError(errCodeOverflow);
3874 else if ( bAutoCorrect )
3875 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
3878 FormulaByteToken aToken( ocClose );
3879 while( nBrackets-- )
3881 if( !pArr->AddToken( aToken ) )
3883 SetError(errCodeOverflow); break;
3885 if ( bAutoCorrect )
3886 aCorrectedFormula += mxSymbols->getSymbol(ocClose);
3889 if ( nForced >= 2 )
3890 pArr->SetRecalcModeForced();
3892 if (pFunctionStack != &aFuncs[0])
3893 delete [] pFunctionStack;
3895 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3896 ScTokenArray* pNew = new ScTokenArray( aArr );
3897 pArr = pNew;
3898 return pNew;
3902 ScTokenArray* ScCompiler::CompileString( const String& rFormula, const String& rFormulaNmsp )
3904 DBG_ASSERT( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || (rFormulaNmsp.Len() == 0),
3905 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
3906 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
3908 ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
3909 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
3910 table::CellAddress aReferencePos;
3911 ScUnoConversion::FillApiAddress( aReferencePos, aPos );
3912 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
3913 ScTokenArray aTokenArray;
3914 if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
3916 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3917 ScTokenArray* pNew = new ScTokenArray( aTokenArray );
3918 pArr = pNew;
3919 return pNew;
3922 catch( uno::Exception& )
3925 // no success - fallback to some internal grammar and hope the best
3926 return CompileString( rFormula );
3930 BOOL ScCompiler::HandleRange()
3932 ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( pToken->GetIndex() );
3933 if (pRangeData)
3935 USHORT nErr = pRangeData->GetErrCode();
3936 if( nErr )
3937 SetError( errNoName );
3938 else if ( !bCompileForFAP )
3940 ScTokenArray* pNew;
3941 // #35168# put named formula into parentheses.
3942 // #37680# But only if there aren't any yet, parenthetical
3943 // ocSep doesn't work, e.g. SUM((...;...))
3944 // or if not directly between ocSep/parenthesis,
3945 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
3946 // in short: if it isn't a self-contained expression.
3947 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
3948 FormulaToken* p2 = pArr->PeekNextNoSpaces();
3949 OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) );
3950 OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) );
3951 BOOL bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
3952 BOOL bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
3953 BOOL bAddPair = !(bBorder1 && bBorder2);
3954 if ( bAddPair )
3956 pNew = new ScTokenArray();
3957 pNew->AddOpCode( ocClose );
3958 PushTokenArray( pNew, TRUE );
3959 pNew->Reset();
3961 pNew = pRangeData->GetCode()->Clone();
3962 PushTokenArray( pNew, TRUE );
3963 if( pRangeData->HasReferences() )
3965 SetRelNameReference();
3966 MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
3968 pNew->Reset();
3969 if ( bAddPair )
3971 pNew = new ScTokenArray();
3972 pNew->AddOpCode( ocOpen );
3973 PushTokenArray( pNew, TRUE );
3974 pNew->Reset();
3976 return GetToken();
3979 else
3980 SetError(errNoName);
3981 return TRUE;
3983 // -----------------------------------------------------------------------------
3984 BOOL ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
3986 // Handle external range names.
3987 switch (_aToken.GetType())
3989 case svExternalSingleRef:
3990 case svExternalDoubleRef:
3991 pArr->IncrementRefs();
3992 break;
3993 case svExternalName:
3995 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3996 const String* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
3997 if (!pFile)
3999 SetError(errNoName);
4000 return true;
4003 const String& rName = _aToken.GetString();
4004 ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
4005 _aToken.GetIndex(), rName, &aPos);
4007 if (!xNew)
4009 SetError(errNoName);
4010 return true;
4013 ScTokenArray* pNew = xNew->Clone();
4014 PushTokenArray( pNew, true);
4015 if (pNew->GetNextReference() != NULL)
4017 SetRelNameReference();
4018 MoveRelWrap(MAXCOL, MAXROW);
4020 pNew->Reset();
4021 return GetToken();
4023 default:
4024 DBG_ERROR("Wrong type for external reference!");
4025 return FALSE;
4027 return TRUE;
4031 //---------------------------------------------------------------------------
4034 //---------------------------------------------------------------------------
4035 // Append token to RPN code
4036 //---------------------------------------------------------------------------
4039 //-----------------------------------------------------------------------------
4041 //---------------------------------------------------------------------------
4042 // RPN creation by recursion
4043 //---------------------------------------------------------------------------
4047 //-----------------------------------------------------------------------------
4049 BOOL ScCompiler::HasModifiedRange()
4051 pArr->Reset();
4052 for ( FormulaToken* t = pArr->Next(); t; t = pArr->Next() )
4054 OpCode eOpCode = t->GetOpCode();
4055 if ( eOpCode == ocName )
4057 ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4059 if (pRangeData && pRangeData->IsModified())
4060 return TRUE;
4062 else if ( eOpCode == ocDBArea )
4064 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(t->GetIndex());
4066 if (pDBData && pDBData->IsModified())
4067 return TRUE;
4070 return FALSE;
4074 //---------------------------------------------------------------------------
4076 template< typename T, typename S >
4077 S lcl_adjval( S& n, T pos, T max, BOOL bRel )
4079 max++;
4080 if( bRel )
4081 n = sal::static_int_cast<S>( n + pos );
4082 if( n < 0 )
4083 n = sal::static_int_cast<S>( n + max );
4084 else if( n >= max )
4085 n = sal::static_int_cast<S>( n - max );
4086 if( bRel )
4087 n = sal::static_int_cast<S>( n - pos );
4088 return n;
4091 // reference of named range with relative references
4093 void ScCompiler::SetRelNameReference()
4095 pArr->Reset();
4096 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4097 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4099 ScSingleRefData& rRef1 = t->GetSingleRef();
4100 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
4101 rRef1.SetRelName( TRUE );
4102 if ( t->GetType() == svDoubleRef )
4104 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4105 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
4106 rRef2.SetRelName( TRUE );
4111 // Wrap-adjust relative references of a RangeName to current position,
4112 // don't call for other token arrays!
4113 void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow )
4115 pArr->Reset();
4116 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4117 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4119 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4120 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4121 else
4122 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4126 // static
4127 // Wrap-adjust relative references of a RangeName to current position,
4128 // don't call for other token arrays!
4129 void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos,
4130 SCCOL nMaxCol, SCROW nMaxRow )
4132 rArr.Reset();
4133 for( ScToken* t = static_cast<ScToken*>(rArr.GetNextReference()); t;
4134 t = static_cast<ScToken*>(rArr.GetNextReference()) )
4136 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4137 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4138 else
4139 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4143 ScRangeData* ScCompiler::UpdateReference(UpdateRefMode eUpdateRefMode,
4144 const ScAddress& rOldPos, const ScRange& r,
4145 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
4146 BOOL& rChanged, BOOL& rRefSizeChanged )
4148 rChanged = rRefSizeChanged = FALSE;
4149 if ( eUpdateRefMode == URM_COPY )
4150 { // Normally nothing has to be done here since RelRefs are used, also
4151 // SharedFormulas don't need any special handling, except if they
4152 // wrapped around sheet borders.
4153 // #67383# But ColRowName tokens pointing to a ColRow header which was
4154 // copied along with this formula need to be updated to point to the
4155 // copied header instead of the old position's new intersection.
4156 ScToken* t;
4157 pArr->Reset();
4158 while( (t = static_cast<ScToken*>(pArr->GetNextColRowName())) != NULL )
4160 ScSingleRefData& rRef = t->GetSingleRef();
4161 rRef.CalcAbsIfRel( rOldPos );
4162 ScAddress aNewRef( rRef.nCol + nDx, rRef.nRow + nDy, rRef.nTab + nDz );
4163 if ( r.In( aNewRef ) )
4164 { // yes, this is URM_MOVE
4165 if ( ScRefUpdate::Update( pDoc, URM_MOVE, aPos,
4166 r, nDx, nDy, nDz,
4167 SingleDoubleRefModifier( rRef ).Ref() )
4168 != UR_NOTHING
4170 rChanged = TRUE;
4173 // Check for SharedFormulas.
4174 ScRangeData* pRangeData = NULL;
4175 pArr->Reset();
4176 for( FormulaToken* j = pArr->GetNextName(); j && !pRangeData;
4177 j = pArr->GetNextName() )
4179 if( j->GetOpCode() == ocName )
4181 ScRangeData* pName = pDoc->GetRangeName()->FindIndex( j->GetIndex() );
4182 if (pName && pName->HasType(RT_SHARED))
4183 pRangeData = pName;
4186 // Check SharedFormulas for wraps.
4187 if (pRangeData)
4189 ScRangeData* pName = pRangeData;
4190 pRangeData = NULL;
4191 pArr->Reset();
4192 for( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()); t && !pRangeData;
4193 t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) )
4195 BOOL bRelName = (t->GetType() == svSingleRef ?
4196 t->GetSingleRef().IsRelName() :
4197 (t->GetDoubleRef().Ref1.IsRelName() ||
4198 t->GetDoubleRef().Ref2.IsRelName()));
4199 if (bRelName)
4201 t->CalcAbsIfRel( rOldPos);
4202 BOOL bValid = (t->GetType() == svSingleRef ?
4203 t->GetSingleRef().Valid() :
4204 t->GetDoubleRef().Valid());
4205 // If the reference isn't valid, copying the formula
4206 // wrapped it. Replace SharedFormula.
4207 if (!bValid)
4209 pRangeData = pName;
4210 rChanged = TRUE;
4215 return pRangeData;
4217 else
4220 * Set SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE to 1 if we wanted to preserve as
4221 * many shared formulas as possible instead of replacing them with direct code.
4222 * Note that this may produce shared formula usage Excel doesn't understand,
4223 * which would have to be adapted for in the export filter. Advisable as a long
4224 * term goal, since it could decrease memory footprint.
4226 #define SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE 0
4227 ScRangeData* pRangeData = NULL;
4228 ScToken* t;
4229 pArr->Reset();
4230 while( (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL )
4232 if( t->GetOpCode() == ocName )
4234 ScRangeData* pName = pDoc->GetRangeName()->FindIndex( t->GetIndex() );
4235 if (pName && pName->HasType(RT_SHAREDMOD))
4237 pRangeData = pName; // maybe need a replacement of shared with own code
4238 #if ! SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4239 rChanged = TRUE;
4240 #endif
4243 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4245 t->CalcAbsIfRel( rOldPos );
4246 switch (t->GetType())
4248 case svExternalSingleRef:
4249 case svExternalDoubleRef:
4250 // External references never change their positioning
4251 // nor point to parts that will be removed or expanded.
4252 // In fact, calling ScRefUpdate::Update() for URM_MOVE
4253 // may have negative side effects. Simply adapt
4254 // relative references to the new position.
4255 t->CalcRelFromAbs( aPos);
4256 break;
4257 case svSingleRef:
4259 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
4260 aPos, r, nDx, nDy, nDz,
4261 SingleDoubleRefModifier(
4262 t->GetSingleRef()).Ref())
4263 != UR_NOTHING)
4264 rChanged = TRUE;
4266 break;
4267 default:
4269 ScComplexRefData& rRef = t->GetDoubleRef();
4270 SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
4271 SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
4272 SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
4273 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
4274 aPos, r, nDx, nDy, nDz,
4275 t->GetDoubleRef()) != UR_NOTHING)
4277 rChanged = TRUE;
4278 if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
4279 rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
4280 rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
4281 rRefSizeChanged = TRUE;
4287 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4288 BOOL bEasyShared, bPosInRange;
4289 if ( !pRangeData )
4290 bEasyShared = bPosInRange = FALSE;
4291 else
4293 bEasyShared = TRUE;
4294 bPosInRange = r.In( eUpdateRefMode == URM_MOVE ? aPos : rOldPos );
4296 #endif
4297 pArr->Reset();
4298 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4300 if ( t->GetRef() != 1 )
4302 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4303 bEasyShared = FALSE;
4304 #endif
4306 else
4307 { // if nRefCnt>1 it's already updated in token code
4308 if ( t->GetType() == svSingleRef )
4310 ScSingleRefData& rRef = t->GetSingleRef();
4311 SingleDoubleRefModifier aMod( rRef );
4312 if ( rRef.IsRelName() )
4314 ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, aMod.Ref() );
4315 rChanged = TRUE;
4317 else
4319 aMod.Ref().CalcAbsIfRel( rOldPos );
4320 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4321 r, nDx, nDy, nDz, aMod.Ref() )
4322 != UR_NOTHING
4324 rChanged = TRUE;
4326 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4327 if ( bEasyShared )
4329 const ScSingleRefData& rSRD = aMod.Ref().Ref1;
4330 ScAddress aRef( rSRD.nCol, rSRD.nRow, rSRD.nTab );
4331 if ( r.In( aRef ) != bPosInRange )
4332 bEasyShared = FALSE;
4334 #endif
4336 else
4338 ScComplexRefData& rRef = t->GetDoubleRef();
4339 SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
4340 SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
4341 SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
4342 if ( rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName() )
4344 ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, rRef );
4345 rChanged = TRUE;
4347 else
4349 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4350 r, nDx, nDy, nDz, rRef )
4351 != UR_NOTHING
4354 rChanged = TRUE;
4355 if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
4356 rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
4357 rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
4359 rRefSizeChanged = TRUE;
4360 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4361 bEasyShared = FALSE;
4362 #endif
4366 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4367 if ( bEasyShared )
4369 ScRange aRef( rRef.Ref1.nCol, rRef.Ref1.nRow,
4370 rRef.Ref1.nTab, rRef.Ref2.nCol, rRef.Ref2.nRow,
4371 rRef.Ref2.nTab );
4372 if ( r.In( aRef ) != bPosInRange )
4373 bEasyShared = FALSE;
4375 #endif
4379 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4380 if ( pRangeData )
4382 if ( bEasyShared )
4383 pRangeData = 0;
4384 else
4385 rChanged = TRUE;
4387 #endif
4388 #undef SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4389 return pRangeData;
4393 BOOL ScCompiler::UpdateNameReference(UpdateRefMode eUpdateRefMode,
4394 const ScRange& r,
4395 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
4396 BOOL& rChanged, BOOL bSharedFormula)
4398 BOOL bRelRef = FALSE; // set if relative reference
4399 rChanged = FALSE;
4400 pArr->Reset();
4401 ScToken* t;
4402 while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
4404 SingleDoubleRefModifier aMod( *t );
4405 ScComplexRefData& rRef = aMod.Ref();
4406 bRelRef = rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() ||
4407 rRef.Ref1.IsTabRel();
4408 if (!bRelRef && t->GetType() == svDoubleRef)
4409 bRelRef = rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() ||
4410 rRef.Ref2.IsTabRel();
4411 bool bUpdate = !rRef.Ref1.IsColRel() || !rRef.Ref1.IsRowRel() ||
4412 !rRef.Ref1.IsTabRel();
4413 if (!bUpdate && t->GetType() == svDoubleRef)
4414 bUpdate = !rRef.Ref2.IsColRel() || !rRef.Ref2.IsRowRel() ||
4415 !rRef.Ref2.IsTabRel();
4416 if (!bSharedFormula)
4418 // We cannot update names with sheet-relative references, they may
4419 // be used on other sheets as well and the resulting reference
4420 // would be wrong. This is a dilemma if col/row would need to be
4421 // updated for the current usage.
4422 // TODO: seems the only way out of this would be to not allow
4423 // relative sheet references and have sheet-local names that can be
4424 // copied along with sheets.
4425 bUpdate = bUpdate && !rRef.Ref1.IsTabRel() && !rRef.Ref2.IsTabRel();
4427 if (bUpdate)
4429 rRef.CalcAbsIfRel( aPos);
4430 if (ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, r,
4431 nDx, nDy, nDz, rRef, ScRefUpdate::ABSOLUTE)
4432 != UR_NOTHING )
4433 rChanged = TRUE;
4436 return bRelRef;
4440 void ScCompiler::UpdateSharedFormulaReference( UpdateRefMode eUpdateRefMode,
4441 const ScAddress& rOldPos, const ScRange& r,
4442 SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
4444 if ( eUpdateRefMode == URM_COPY )
4445 return ;
4446 else
4448 ScToken* t;
4449 pArr->Reset();
4450 while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
4452 if( t->GetType() != svIndex ) // it may be a DB area!!!
4454 t->CalcAbsIfRel( rOldPos );
4455 // Absolute references have been already adjusted in the named
4456 // shared formula itself prior to breaking the shared formula
4457 // and calling this function. Don't readjust them again.
4458 SingleDoubleRefModifier aMod( *t );
4459 ScComplexRefData& rRef = aMod.Ref();
4460 ScComplexRefData aBkp = rRef;
4461 ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4462 r, nDx, nDy, nDz, rRef );
4463 // restore absolute parts
4464 if ( !aBkp.Ref1.IsColRel() )
4466 rRef.Ref1.nCol = aBkp.Ref1.nCol;
4467 rRef.Ref1.nRelCol = aBkp.Ref1.nRelCol;
4468 rRef.Ref1.SetColDeleted( aBkp.Ref1.IsColDeleted() );
4470 if ( !aBkp.Ref1.IsRowRel() )
4472 rRef.Ref1.nRow = aBkp.Ref1.nRow;
4473 rRef.Ref1.nRelRow = aBkp.Ref1.nRelRow;
4474 rRef.Ref1.SetRowDeleted( aBkp.Ref1.IsRowDeleted() );
4476 if ( !aBkp.Ref1.IsTabRel() )
4478 rRef.Ref1.nTab = aBkp.Ref1.nTab;
4479 rRef.Ref1.nRelTab = aBkp.Ref1.nRelTab;
4480 rRef.Ref1.SetTabDeleted( aBkp.Ref1.IsTabDeleted() );
4482 if ( t->GetType() == svDoubleRef )
4484 if ( !aBkp.Ref2.IsColRel() )
4486 rRef.Ref2.nCol = aBkp.Ref2.nCol;
4487 rRef.Ref2.nRelCol = aBkp.Ref2.nRelCol;
4488 rRef.Ref2.SetColDeleted( aBkp.Ref2.IsColDeleted() );
4490 if ( !aBkp.Ref2.IsRowRel() )
4492 rRef.Ref2.nRow = aBkp.Ref2.nRow;
4493 rRef.Ref2.nRelRow = aBkp.Ref2.nRelRow;
4494 rRef.Ref2.SetRowDeleted( aBkp.Ref2.IsRowDeleted() );
4496 if ( !aBkp.Ref2.IsTabRel() )
4498 rRef.Ref2.nTab = aBkp.Ref2.nTab;
4499 rRef.Ref2.nRelTab = aBkp.Ref2.nRelTab;
4500 rRef.Ref2.SetTabDeleted( aBkp.Ref2.IsTabDeleted() );
4509 ScRangeData* ScCompiler::UpdateInsertTab( SCTAB nTable, BOOL bIsName )
4511 ScRangeData* pRangeData = NULL;
4512 SCTAB nPosTab = aPos.Tab(); // _after_ incremented!
4513 SCTAB nOldPosTab = ((nPosTab > nTable) ? (nPosTab - 1) : nPosTab);
4514 BOOL bIsRel = FALSE;
4515 ScToken* t;
4516 pArr->Reset();
4517 if (bIsName)
4518 t = static_cast<ScToken*>(pArr->GetNextReference());
4519 else
4520 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4521 while( t )
4523 if( t->GetOpCode() == ocName )
4525 if (!bIsName)
4527 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4528 if (pName && pName->HasType(RT_SHAREDMOD))
4529 pRangeData = pName;
4532 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4534 if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
4535 { // of names only adjust absolute references
4536 ScSingleRefData& rRef = t->GetSingleRef();
4537 if ( rRef.IsTabRel() )
4539 rRef.nTab = rRef.nRelTab + nOldPosTab;
4540 if ( rRef.nTab < 0 )
4541 rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
4543 if (nTable <= rRef.nTab)
4544 ++rRef.nTab;
4545 rRef.nRelTab = rRef.nTab - nPosTab;
4547 else
4548 bIsRel = TRUE;
4549 if ( t->GetType() == svDoubleRef )
4551 if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
4552 { // of names only adjust absolute references
4553 ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
4554 if ( rRef.IsTabRel() )
4556 rRef.nTab = rRef.nRelTab + nOldPosTab;
4557 if ( rRef.nTab < 0 )
4558 rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
4560 if (nTable <= rRef.nTab)
4561 ++rRef.nTab;
4562 rRef.nRelTab = rRef.nTab - nPosTab;
4564 else
4565 bIsRel = TRUE;
4567 if ( bIsName && bIsRel )
4568 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4570 if (bIsName)
4571 t = static_cast<ScToken*>(pArr->GetNextReference());
4572 else
4573 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4575 if ( !bIsName )
4577 pArr->Reset();
4578 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4580 if ( t->GetRef() == 1 )
4582 ScSingleRefData& rRef1 = t->GetSingleRef();
4583 if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
4584 { // of names only adjust absolute references
4585 if ( rRef1.IsTabRel() )
4587 rRef1.nTab = rRef1.nRelTab + nOldPosTab;
4588 if ( rRef1.nTab < 0 )
4589 rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + pDoc->GetTableCount() ); // was a wrap
4591 if (nTable <= rRef1.nTab)
4592 ++rRef1.nTab;
4593 rRef1.nRelTab = rRef1.nTab - nPosTab;
4595 if ( t->GetType() == svDoubleRef )
4597 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4598 if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
4599 { // of names only adjust absolute references
4600 if ( rRef2.IsTabRel() )
4602 rRef2.nTab = rRef2.nRelTab + nOldPosTab;
4603 if ( rRef2.nTab < 0 )
4604 rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + pDoc->GetTableCount() ); // was a wrap
4606 if (nTable <= rRef2.nTab)
4607 ++rRef2.nTab;
4608 rRef2.nRelTab = rRef2.nTab - nPosTab;
4614 return pRangeData;
4617 ScRangeData* ScCompiler::UpdateDeleteTab(SCTAB nTable, BOOL /* bIsMove */, BOOL bIsName,
4618 BOOL& rChanged)
4620 ScRangeData* pRangeData = NULL;
4621 SCTAB nTab, nTab2;
4622 SCTAB nPosTab = aPos.Tab(); // _after_ decremented!
4623 SCTAB nOldPosTab = ((nPosTab >= nTable) ? (nPosTab + 1) : nPosTab);
4624 rChanged = FALSE;
4625 BOOL bIsRel = FALSE;
4626 ScToken* t;
4627 pArr->Reset();
4628 if (bIsName)
4629 t = static_cast<ScToken*>(pArr->GetNextReference());
4630 else
4631 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4632 while( t )
4634 if( t->GetOpCode() == ocName )
4636 if (!bIsName)
4638 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4639 if (pName && pName->HasType(RT_SHAREDMOD))
4640 pRangeData = pName;
4642 rChanged = TRUE;
4644 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4646 if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
4647 { // of names only adjust absolute references
4648 ScSingleRefData& rRef = t->GetSingleRef();
4649 if ( rRef.IsTabRel() )
4650 nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
4651 else
4652 nTab = rRef.nTab;
4653 if ( nTable < nTab )
4655 rRef.nTab = nTab - 1;
4656 rChanged = TRUE;
4658 else if ( nTable == nTab )
4660 if ( t->GetType() == svDoubleRef )
4662 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4663 if ( rRef2.IsTabRel() )
4664 nTab2 = rRef2.nRelTab + nOldPosTab;
4665 else
4666 nTab2 = rRef2.nTab;
4667 if ( nTab == nTab2
4668 || (nTab+1) >= pDoc->GetTableCount() )
4670 rRef.nTab = MAXTAB+1;
4671 rRef.SetTabDeleted( TRUE );
4673 // else: nTab later points to what's nTable+1 now
4674 // => area shrunk
4676 else
4678 rRef.nTab = MAXTAB+1;
4679 rRef.SetTabDeleted( TRUE );
4681 rChanged = TRUE;
4683 rRef.nRelTab = rRef.nTab - nPosTab;
4685 else
4686 bIsRel = TRUE;
4687 if ( t->GetType() == svDoubleRef )
4689 if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
4690 { // of names only adjust absolute references
4691 ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
4692 if ( rRef.IsTabRel() )
4693 nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
4694 else
4695 nTab = rRef.nTab;
4696 if ( nTable < nTab )
4698 rRef.nTab = nTab - 1;
4699 rChanged = TRUE;
4701 else if ( nTable == nTab )
4703 if ( !t->GetDoubleRef().Ref1.IsTabDeleted() )
4704 rRef.nTab = nTab - 1; // shrink area
4705 else
4707 rRef.nTab = MAXTAB+1;
4708 rRef.SetTabDeleted( TRUE );
4710 rChanged = TRUE;
4712 rRef.nRelTab = rRef.nTab - nPosTab;
4714 else
4715 bIsRel = TRUE;
4717 if ( bIsName && bIsRel )
4718 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4720 if (bIsName)
4721 t = static_cast<ScToken*>(pArr->GetNextReference());
4722 else
4723 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4725 if ( !bIsName )
4727 pArr->Reset();
4728 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4730 if ( t->GetRef() == 1 )
4732 ScSingleRefData& rRef1 = t->GetSingleRef();
4733 if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
4734 { // of names only adjust absolute references
4735 if ( rRef1.IsTabRel() )
4736 nTab = rRef1.nTab = rRef1.nRelTab + nOldPosTab;
4737 else
4738 nTab = rRef1.nTab;
4739 if ( nTable < nTab )
4741 rRef1.nTab = nTab - 1;
4742 rChanged = TRUE;
4744 else if ( nTable == nTab )
4746 if ( t->GetType() == svDoubleRef )
4748 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4749 if ( rRef2.IsTabRel() )
4750 nTab2 = rRef2.nRelTab + nOldPosTab;
4751 else
4752 nTab2 = rRef2.nTab;
4753 if ( nTab == nTab2
4754 || (nTab+1) >= pDoc->GetTableCount() )
4756 rRef1.nTab = MAXTAB+1;
4757 rRef1.SetTabDeleted( TRUE );
4759 // else: nTab later points to what's nTable+1 now
4760 // => area shrunk
4762 else
4764 rRef1.nTab = MAXTAB+1;
4765 rRef1.SetTabDeleted( TRUE );
4767 rChanged = TRUE;
4769 rRef1.nRelTab = rRef1.nTab - nPosTab;
4771 if ( t->GetType() == svDoubleRef )
4773 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4774 if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
4775 { // of names only adjust absolute references
4776 if ( rRef2.IsTabRel() )
4777 nTab = rRef2.nTab = rRef2.nRelTab + nOldPosTab;
4778 else
4779 nTab = rRef2.nTab;
4780 if ( nTable < nTab )
4782 rRef2.nTab = nTab - 1;
4783 rChanged = TRUE;
4785 else if ( nTable == nTab )
4787 if ( !rRef1.IsTabDeleted() )
4788 rRef2.nTab = nTab - 1; // shrink area
4789 else
4791 rRef2.nTab = MAXTAB+1;
4792 rRef2.SetTabDeleted( TRUE );
4794 rChanged = TRUE;
4796 rRef2.nRelTab = rRef2.nTab - nPosTab;
4802 return pRangeData;
4805 // aPos.Tab() must be already adjusted!
4806 ScRangeData* ScCompiler::UpdateMoveTab( SCTAB nOldTab, SCTAB nNewTab,
4807 BOOL bIsName )
4809 ScRangeData* pRangeData = NULL;
4810 SCsTAB nTab;
4812 SCTAB nStart, nEnd;
4813 short nDir; // direction in which others move
4814 if ( nOldTab < nNewTab )
4816 nDir = -1;
4817 nStart = nOldTab;
4818 nEnd = nNewTab;
4820 else
4822 nDir = 1;
4823 nStart = nNewTab;
4824 nEnd = nOldTab;
4826 SCTAB nPosTab = aPos.Tab(); // current sheet
4827 SCTAB nOldPosTab; // previously it was this one
4828 if ( nPosTab == nNewTab )
4829 nOldPosTab = nOldTab; // look, it's me!
4830 else if ( nPosTab < nStart || nEnd < nPosTab )
4831 nOldPosTab = nPosTab; // wasn't moved
4832 else
4833 nOldPosTab = nPosTab - nDir; // moved by one
4835 BOOL bIsRel = FALSE;
4836 ScToken* t;
4837 pArr->Reset();
4838 if (bIsName)
4839 t = static_cast<ScToken*>(pArr->GetNextReference());
4840 else
4841 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4842 while( t )
4844 if( t->GetOpCode() == ocName )
4846 if (!bIsName)
4848 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4849 if (pName && pName->HasType(RT_SHAREDMOD))
4850 pRangeData = pName;
4853 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4855 ScSingleRefData& rRef1 = t->GetSingleRef();
4856 if ( !(bIsName && rRef1.IsTabRel()) )
4857 { // of names only adjust absolute references
4858 if ( rRef1.IsTabRel() )
4859 nTab = rRef1.nRelTab + nOldPosTab;
4860 else
4861 nTab = rRef1.nTab;
4862 if ( nTab == nOldTab )
4863 rRef1.nTab = nNewTab;
4864 else if ( nStart <= nTab && nTab <= nEnd )
4865 rRef1.nTab = nTab + nDir;
4866 rRef1.nRelTab = rRef1.nTab - nPosTab;
4868 else
4869 bIsRel = TRUE;
4870 if ( t->GetType() == svDoubleRef )
4872 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4873 if ( !(bIsName && rRef2.IsTabRel()) )
4874 { // of names only adjust absolute references
4875 if ( rRef2.IsTabRel() )
4876 nTab = rRef2.nRelTab + nOldPosTab;
4877 else
4878 nTab = rRef2.nTab;
4879 if ( nTab == nOldTab )
4880 rRef2.nTab = nNewTab;
4881 else if ( nStart <= nTab && nTab <= nEnd )
4882 rRef2.nTab = nTab + nDir;
4883 rRef2.nRelTab = rRef2.nTab - nPosTab;
4885 else
4886 bIsRel = TRUE;
4887 SCsTAB nTab1, nTab2;
4888 if ( rRef1.IsTabRel() )
4889 nTab1 = rRef1.nRelTab + nPosTab;
4890 else
4891 nTab1 = rRef1.nTab;
4892 if ( rRef2.IsTabRel() )
4893 nTab2 = rRef2.nRelTab + nPosTab;
4894 else
4895 nTab2 = rRef1.nTab;
4896 if ( nTab2 < nTab1 )
4897 { // PutInOrder
4898 rRef1.nTab = nTab2;
4899 rRef2.nTab = nTab1;
4900 rRef1.nRelTab = rRef1.nTab - nPosTab;
4901 rRef2.nRelTab = rRef2.nTab - nPosTab;
4904 if ( bIsName && bIsRel )
4905 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4907 if (bIsName)
4908 t = static_cast<ScToken*>(pArr->GetNextReference());
4909 else
4910 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4912 if ( !bIsName )
4914 SCsTAB nMaxTabMod = (SCsTAB) pDoc->GetTableCount();
4915 pArr->Reset();
4916 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4918 if ( t->GetRef() == 1 )
4920 ScSingleRefData& rRef1 = t->GetSingleRef();
4921 if ( rRef1.IsRelName() && rRef1.IsTabRel() )
4922 { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
4923 nTab = rRef1.nRelTab + nPosTab;
4924 if ( nTab < 0 )
4925 nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
4926 else if ( nTab > nMaxTab )
4927 nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
4928 rRef1.nRelTab = nTab - nPosTab;
4930 else
4932 if ( rRef1.IsTabRel() )
4933 nTab = rRef1.nRelTab + nOldPosTab;
4934 else
4935 nTab = rRef1.nTab;
4936 if ( nTab == nOldTab )
4937 rRef1.nTab = nNewTab;
4938 else if ( nStart <= nTab && nTab <= nEnd )
4939 rRef1.nTab = nTab + nDir;
4940 rRef1.nRelTab = rRef1.nTab - nPosTab;
4942 if( t->GetType() == svDoubleRef )
4944 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4945 if ( rRef2.IsRelName() && rRef2.IsTabRel() )
4946 { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
4947 nTab = rRef2.nRelTab + nPosTab;
4948 if ( nTab < 0 )
4949 nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
4950 else if ( nTab > nMaxTab )
4951 nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
4952 rRef2.nRelTab = nTab - nPosTab;
4954 else
4956 if ( rRef2.IsTabRel() )
4957 nTab = rRef2.nRelTab + nOldPosTab;
4958 else
4959 nTab = rRef2.nTab;
4960 if ( nTab == nOldTab )
4961 rRef2.nTab = nNewTab;
4962 else if ( nStart <= nTab && nTab <= nEnd )
4963 rRef2.nTab = nTab + nDir;
4964 rRef2.nRelTab = rRef2.nTab - nPosTab;
4966 SCsTAB nTab1, nTab2;
4967 if ( rRef1.IsTabRel() )
4968 nTab1 = rRef1.nRelTab + nPosTab;
4969 else
4970 nTab1 = rRef1.nTab;
4971 if ( rRef2.IsTabRel() )
4972 nTab2 = rRef2.nRelTab + nPosTab;
4973 else
4974 nTab2 = rRef1.nTab;
4975 if ( nTab2 < nTab1 )
4976 { // PutInOrder
4977 rRef1.nTab = nTab2;
4978 rRef2.nTab = nTab1;
4979 rRef1.nRelTab = rRef1.nTab - nPosTab;
4980 rRef2.nRelTab = rRef2.nTab - nPosTab;
4986 return pRangeData;
4990 void ScCompiler::CreateStringFromExternal(rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP)
4992 FormulaToken* t = pTokenP;
4993 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4994 switch (t->GetType())
4996 case svExternalName:
4998 const String *pStr = pRefMgr->getExternalFileName(t->GetIndex());
4999 String aFileName = pStr ? *pStr : ScGlobal::GetRscString(STR_NO_NAME_REF);
5000 rBuffer.append(pConv->makeExternalNameStr( aFileName, t->GetString()));
5002 break;
5003 case svExternalSingleRef:
5004 pConv->makeExternalRefStr(
5005 rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetSingleRef(), pRefMgr);
5006 break;
5007 case svExternalDoubleRef:
5008 pConv->makeExternalRefStr(
5009 rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetDoubleRef(), pRefMgr);
5010 break;
5011 default:
5012 // warning, not error, otherwise we may end up with a never
5013 // ending message box loop if this was the cursor cell to be redrawn.
5014 DBG_WARNING("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
5018 void ScCompiler::CreateStringFromMatrix( rtl::OUStringBuffer& rBuffer,
5019 FormulaToken* pTokenP)
5021 const ScMatrix* pMatrix = static_cast<ScToken*>(pTokenP)->GetMatrix();
5022 SCSIZE nC, nMaxC, nR, nMaxR;
5024 pMatrix->GetDimensions( nMaxC, nMaxR);
5026 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
5027 for( nR = 0 ; nR < nMaxR ; nR++)
5029 if( nR > 0)
5031 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
5034 for( nC = 0 ; nC < nMaxC ; nC++)
5036 if( nC > 0)
5038 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
5041 if( pMatrix->IsValue( nC, nR ) )
5043 ScMatValType nType;
5044 const ScMatrixValue* pVal = pMatrix->Get( nC, nR, nType);
5046 if( nType == SC_MATVAL_BOOLEAN )
5047 AppendBoolean( rBuffer, pVal->GetBoolean() );
5048 else
5050 USHORT nErr = pVal->GetError();
5051 if( nErr )
5052 rBuffer.append( ScGlobal::GetErrorString( nErr ) );
5053 else
5054 AppendDouble( rBuffer, pVal->fVal );
5057 else if( pMatrix->IsEmpty( nC, nR ) )
5059 else if( pMatrix->IsString( nC, nR ) )
5060 AppendString( rBuffer, pMatrix->GetString( nC, nR ) );
5063 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
5066 void ScCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
5068 const OpCode eOp = _pTokenP->GetOpCode();
5069 ScSingleRefData& rRef = static_cast<ScToken*>(_pTokenP)->GetSingleRef();
5070 ScComplexRefData aRef;
5071 aRef.Ref1 = aRef.Ref2 = rRef;
5072 if ( eOp == ocColRowName )
5074 rRef.CalcAbsIfRel( aPos );
5075 if ( pDoc->HasStringData( rRef.nCol, rRef.nRow, rRef.nTab ) )
5077 String aStr;
5078 pDoc->GetString( rRef.nCol, rRef.nRow, rRef.nTab, aStr );
5079 EnQuote( aStr );
5080 rBuffer.append(aStr);
5082 else
5084 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
5085 pConv->MakeRefStr (rBuffer, *this, aRef, TRUE );
5088 else
5089 pConv->MakeRefStr( rBuffer, *this, aRef, TRUE );
5091 // -----------------------------------------------------------------------------
5092 void ScCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
5094 pConv->MakeRefStr( rBuffer, *this, static_cast<ScToken*>(_pTokenP)->GetDoubleRef(), FALSE );
5096 // -----------------------------------------------------------------------------
5097 void ScCompiler::CreateStringFromIndex(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
5099 const OpCode eOp = _pTokenP->GetOpCode();
5100 rtl::OUStringBuffer aBuffer;
5101 switch ( eOp )
5103 case ocName:
5105 ScRangeData* pData = pDoc->GetRangeName()->FindIndex(_pTokenP->GetIndex());
5106 if (pData)
5108 if (pData->HasType(RT_SHARED))
5109 pData->UpdateSymbol( aBuffer, aPos, GetGrammar());
5110 else
5111 aBuffer.append(pData->GetName());
5114 break;
5115 case ocDBArea:
5117 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(_pTokenP->GetIndex());
5118 if (pDBData)
5119 aBuffer.append(pDBData->GetName());
5121 break;
5122 default:
5123 ; // nothing
5125 if ( aBuffer.getLength() )
5126 rBuffer.append(aBuffer);
5127 else
5128 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
5130 // -----------------------------------------------------------------------------
5131 void ScCompiler::LocalizeString( String& rName )
5133 ScGlobal::GetAddInCollection()->LocalizeString( rName );
5135 // -----------------------------------------------------------------------------
5136 BOOL ScCompiler::IsImportingXML() const
5138 return pDoc->IsImportingXML();
5141 // Put quotes around string if non-alphanumeric characters are contained,
5142 // quote characters contained within are escaped by '\\'.
5143 BOOL ScCompiler::EnQuote( String& rStr )
5145 sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.Len() );
5146 if ( !CharClass::isNumericType( nType )
5147 && CharClass::isAlphaNumericType( nType ) )
5148 return FALSE;
5150 xub_StrLen nPos = 0;
5151 while ( (nPos = rStr.Search( '\'', nPos)) != STRING_NOTFOUND )
5153 rStr.Insert( '\\', nPos );
5154 nPos += 2;
5156 rStr.Insert( '\'', 0 );
5157 rStr += '\'';
5158 return TRUE;
5161 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
5163 return pConv->getSpecialSymbol(eType);
5166 void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
5168 // All known AddIn functions.
5169 sheet::FormulaOpCodeMapEntry aEntry;
5170 aEntry.Token.OpCode = ocExternal;
5172 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
5173 const long nCount = pColl->GetFuncCount();
5174 for (long i=0; i < nCount; ++i)
5176 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
5177 if (pFuncData)
5179 if ( _bIsEnglish )
5181 String aName;
5182 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
5183 aEntry.Name = aName;
5184 else
5185 aEntry.Name = pFuncData->GetUpperName();
5187 else
5188 aEntry.Name = pFuncData->GetUpperLocal();
5189 aEntry.Token.Data <<= ::rtl::OUString( pFuncData->GetOriginalName());
5190 _rVec.push_back( aEntry);
5193 // FIXME: what about those old non-UNO AddIns?
5195 // -----------------------------------------------------------------------------
5196 BOOL ScCompiler::HandleSingleRef()
5198 ScSingleRefData& rRef = static_cast<ScToken*>((FormulaToken*)pToken)->GetSingleRef();
5199 rRef.CalcAbsIfRel( aPos );
5200 if ( !rRef.Valid() )
5202 SetError( errNoRef );
5203 return TRUE;
5205 SCCOL nCol = rRef.nCol;
5206 SCROW nRow = rRef.nRow;
5207 SCTAB nTab = rRef.nTab;
5208 ScAddress aLook( nCol, nRow, nTab );
5209 BOOL bColName = rRef.IsColRel();
5210 SCCOL nMyCol = aPos.Col();
5211 SCROW nMyRow = aPos.Row();
5212 BOOL bInList = FALSE;
5213 BOOL bValidName = FALSE;
5214 ScRangePairList* pRL = (bColName ?
5215 pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
5216 ScRange aRange;
5217 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5219 if ( pR->GetRange(0).In( aLook ) )
5221 bInList = bValidName = TRUE;
5222 aRange = pR->GetRange(1);
5223 if ( bColName )
5225 aRange.aStart.SetCol( nCol );
5226 aRange.aEnd.SetCol( nCol );
5228 else
5230 aRange.aStart.SetRow( nRow );
5231 aRange.aEnd.SetRow( nRow );
5233 break; // for
5236 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
5237 { // automagically or created by copying and NamePos isn't in list
5238 BOOL bString = pDoc->HasStringData( nCol, nRow, nTab );
5239 if ( !bString && !pDoc->GetCell( aLook ) )
5240 bString = TRUE; // empty cell is ok
5241 if ( bString )
5242 { //! coresponds with ScInterpreter::ScColRowNameAuto()
5243 bValidName = TRUE;
5244 if ( bColName )
5245 { // ColName
5246 SCROW nStartRow = nRow + 1;
5247 if ( nStartRow > MAXROW )
5248 nStartRow = MAXROW;
5249 SCROW nMaxRow = MAXROW;
5250 if ( nMyCol == nCol )
5251 { // formula cell in same column
5252 if ( nMyRow == nStartRow )
5253 { // take remainder under name cell
5254 nStartRow++;
5255 if ( nStartRow > MAXROW )
5256 nStartRow = MAXROW;
5258 else if ( nMyRow > nStartRow )
5259 { // from name cell down to formula cell
5260 nMaxRow = nMyRow - 1;
5263 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5264 { // next defined ColNameRange below limits row
5265 const ScRange& rRange = pR->GetRange(1);
5266 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
5267 { // identical column range
5268 SCROW nTmp = rRange.aStart.Row();
5269 if ( nStartRow < nTmp && nTmp <= nMaxRow )
5270 nMaxRow = nTmp - 1;
5273 aRange.aStart.Set( nCol, nStartRow, nTab );
5274 aRange.aEnd.Set( nCol, nMaxRow, nTab );
5276 else
5277 { // RowName
5278 SCCOL nStartCol = nCol + 1;
5279 if ( nStartCol > MAXCOL )
5280 nStartCol = MAXCOL;
5281 SCCOL nMaxCol = MAXCOL;
5282 if ( nMyRow == nRow )
5283 { // formula cell in same row
5284 if ( nMyCol == nStartCol )
5285 { // take remainder right from name cell
5286 nStartCol++;
5287 if ( nStartCol > MAXCOL )
5288 nStartCol = MAXCOL;
5290 else if ( nMyCol > nStartCol )
5291 { // from name cell right to formula cell
5292 nMaxCol = nMyCol - 1;
5295 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5296 { // next defined RowNameRange to the right limits column
5297 const ScRange& rRange = pR->GetRange(1);
5298 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
5299 { // identical row range
5300 SCCOL nTmp = rRange.aStart.Col();
5301 if ( nStartCol < nTmp && nTmp <= nMaxCol )
5302 nMaxCol = nTmp - 1;
5305 aRange.aStart.Set( nStartCol, nRow, nTab );
5306 aRange.aEnd.Set( nMaxCol, nRow, nTab );
5310 if ( bValidName )
5312 // And now the magic to distinguish between a range and a single
5313 // cell thereof, which is picked position-dependent of the formula
5314 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5315 // SingleRef matching the column/row of the formula cell is
5316 // generated. A ocColRowName or ocIntersect as a neighbor results
5317 // in a range. Special case: if label is valid for a single cell, a
5318 // position independent SingleRef is generated.
5319 BOOL bSingle = (aRange.aStart == aRange.aEnd);
5320 BOOL bFound;
5321 if ( bSingle )
5322 bFound = TRUE;
5323 else
5325 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
5326 FormulaToken* p2 = pArr->PeekNextNoSpaces();
5327 // begin/end of a formula => single
5328 OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd );
5329 OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd );
5330 if ( eOp1 != ocColRowName && eOp1 != ocIntersect
5331 && eOp2 != ocColRowName && eOp2 != ocIntersect )
5333 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
5334 (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
5335 bSingle = TRUE;
5337 if ( bSingle )
5338 { // column and/or row must match range
5339 if ( bColName )
5341 bFound = (aRange.aStart.Row() <= nMyRow
5342 && nMyRow <= aRange.aEnd.Row());
5343 if ( bFound )
5344 aRange.aStart.SetRow( nMyRow );
5346 else
5348 bFound = (aRange.aStart.Col() <= nMyCol
5349 && nMyCol <= aRange.aEnd.Col());
5350 if ( bFound )
5351 aRange.aStart.SetCol( nMyCol );
5354 else
5355 bFound = TRUE;
5357 if ( !bFound )
5358 SetError(errNoRef);
5359 else if ( !bCompileForFAP )
5361 ScTokenArray* pNew = new ScTokenArray();
5362 if ( bSingle )
5364 ScSingleRefData aRefData;
5365 aRefData.InitAddress( aRange.aStart );
5366 if ( bColName )
5367 aRefData.SetColRel( TRUE );
5368 else
5369 aRefData.SetRowRel( TRUE );
5370 aRefData.CalcRelFromAbs( aPos );
5371 pNew->AddSingleReference( aRefData );
5373 else
5375 ScComplexRefData aRefData;
5376 aRefData.InitRange( aRange );
5377 if ( bColName )
5379 aRefData.Ref1.SetColRel( TRUE );
5380 aRefData.Ref2.SetColRel( TRUE );
5382 else
5384 aRefData.Ref1.SetRowRel( TRUE );
5385 aRefData.Ref2.SetRowRel( TRUE );
5387 aRefData.CalcRelFromAbs( aPos );
5388 if ( bInList )
5389 pNew->AddDoubleReference( aRefData );
5390 else
5391 { // automagically
5392 pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) );
5395 PushTokenArray( pNew, TRUE );
5396 pNew->Reset();
5397 return GetToken();
5400 else
5401 SetError(errNoName);
5402 return TRUE;
5404 // -----------------------------------------------------------------------------
5405 BOOL ScCompiler::HandleDbData()
5407 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex( pToken->GetIndex() );
5408 if ( !pDBData )
5409 SetError(errNoName);
5410 else if ( !bCompileForFAP )
5412 ScComplexRefData aRefData;
5413 aRefData.InitFlags();
5414 pDBData->GetArea( (SCTAB&) aRefData.Ref1.nTab,
5415 (SCCOL&) aRefData.Ref1.nCol,
5416 (SCROW&) aRefData.Ref1.nRow,
5417 (SCCOL&) aRefData.Ref2.nCol,
5418 (SCROW&) aRefData.Ref2.nRow);
5419 aRefData.Ref2.nTab = aRefData.Ref1.nTab;
5420 aRefData.CalcRelFromAbs( aPos );
5421 ScTokenArray* pNew = new ScTokenArray();
5422 pNew->AddDoubleReference( aRefData );
5423 PushTokenArray( pNew, TRUE );
5424 pNew->Reset();
5425 return GetToken();
5427 return TRUE;
5430 String GetScCompilerNativeSymbol( OpCode eOp )
5432 return ScCompiler::GetNativeSymbol( eOp );
5434 // -----------------------------------------------------------------------------
5435 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
5437 return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );