update dev300-m57
[ooovba.git] / sc / source / core / tool / compiler.cxx
blob63634f219828c102e27bc46ab0181b9ad7caf0d3
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);
376 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
378 if (xMap.get())
380 mxSymbols = xMap;
381 if (mxSymbols->isEnglish())
383 if (!pCharClassEnglish)
384 InitCharClassEnglish();
385 pCharClass = pCharClassEnglish;
387 else
388 pCharClass = ScGlobal::pCharClass;
389 SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
394 void ScCompiler::SetGrammarAndRefConvention(
395 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
397 meGrammar = eNewGrammar; //! SetRefConvention needs the new grammar set!
398 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
399 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
401 if (pDoc)
402 SetRefConvention( pDoc->GetAddressConvention());
403 else
404 SetRefConvention( pConvOOO_A1);
406 else
407 SetRefConvention( eConv );
410 String ScCompiler::FindAddInFunction( const String& rUpperName, BOOL bLocalFirst ) const
412 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=FALSE for english
416 #ifdef erDEBUG
417 void dbg_call_testcreatemapping()
419 using namespace ::com::sun::star::sheet;
420 ScCompiler::OpCodeMapPtr xMap = ScCompiler::GetOpCodeMap( FormulaLanguage::ODFF);
421 xMap->createSequenceOfAvailableMappings( FormulaMapGroup::FUNCTIONS);
423 #endif
425 //-----------------------------------------------------------------------------
427 ScCompiler::Convention::~Convention()
429 delete [] mpCharTable;
430 mpCharTable = NULL;
433 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
435 meConv( eConv )
437 int i;
438 ULONG *t= new ULONG [128];
440 ScCompiler::pConventions[ meConv ] = this;
441 mpCharTable = t;
443 for (i = 0; i < 128; i++)
444 t[i] = SC_COMPILER_C_ILLEGAL;
446 /* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
447 /* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
448 if (FormulaGrammar::CONV_ODF == meConv)
449 /* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
450 /* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
451 /* # */ t[35] = SC_COMPILER_C_WORD_SEP;
452 /* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
453 if (FormulaGrammar::CONV_ODF == meConv)
454 /* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
455 /* % */ t[37] = SC_COMPILER_C_VALUE;
456 /* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
457 /* ' */ t[39] = SC_COMPILER_C_NAME_SEP;
458 /* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
459 /* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
460 /* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
461 /* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
462 /* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE;
463 /* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
464 /* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT;
465 /* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
467 for (i = 48; i < 58; i++)
468 /* 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;
470 /* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD;
471 /* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
472 /* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
473 /* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
474 /* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
475 /* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD;
476 /* @ */ // FREE
478 for (i = 65; i < 91; i++)
479 /* A-Z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
481 if (FormulaGrammar::CONV_ODF == meConv)
483 /* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET;
484 /* \ */ // FREE
485 /* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET;
487 else
489 /* [ */ // FREE
490 /* \ */ // FREE
491 /* ] */ // FREE
493 /* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
494 /* _ */ t[95] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
495 /* ` */ // FREE
497 for (i = 97; i < 123; i++)
498 /* a-z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
500 /* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open
501 /* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific)
502 /* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close
503 /* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific
504 /* 127 */ // FREE
506 if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
508 /* */ t[32] |= SC_COMPILER_C_WORD;
509 /* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD;
510 /* " */ t[34] |= SC_COMPILER_C_WORD;
511 /* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP);
512 /* # */ t[35] |= SC_COMPILER_C_WORD;
513 /* % */ t[37] |= SC_COMPILER_C_WORD;
514 /* ' */ t[39] |= SC_COMPILER_C_WORD;
516 /* % */ t[37] |= SC_COMPILER_C_WORD;
517 /* & */ t[38] |= SC_COMPILER_C_WORD;
518 /* ' */ t[39] |= SC_COMPILER_C_WORD;
519 /* ( */ t[40] |= SC_COMPILER_C_WORD;
520 /* ) */ t[41] |= SC_COMPILER_C_WORD;
521 /* * */ t[42] |= SC_COMPILER_C_WORD;
522 /* + */ t[43] |= SC_COMPILER_C_WORD;
523 #if 0 /* this really needs to be locale specific. */
524 /* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
525 #else
526 /* , */ t[44] |= SC_COMPILER_C_WORD;
527 #endif
528 /* - */ t[45] |= SC_COMPILER_C_WORD;
530 /* ; */ t[59] |= SC_COMPILER_C_WORD;
531 /* < */ t[60] |= SC_COMPILER_C_WORD;
532 /* = */ t[61] |= SC_COMPILER_C_WORD;
533 /* > */ t[62] |= SC_COMPILER_C_WORD;
534 /* ? */ // question really is not permitted in sheet name
535 /* @ */ t[64] |= SC_COMPILER_C_WORD;
536 /* [ */ t[91] |= SC_COMPILER_C_WORD;
537 /* ] */ t[93] |= SC_COMPILER_C_WORD;
538 /* { */ t[123]|= SC_COMPILER_C_WORD;
539 /* | */ t[124]|= SC_COMPILER_C_WORD;
540 /* } */ t[125]|= SC_COMPILER_C_WORD;
541 /* ~ */ t[126]|= SC_COMPILER_C_WORD;
543 if( FormulaGrammar::CONV_XL_R1C1 == meConv )
545 /* - */ t[45] |= SC_COMPILER_C_IDENT;
546 /* [ */ t[91] |= SC_COMPILER_C_IDENT;
547 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
549 if( FormulaGrammar::CONV_XL_OOX == meConv )
551 /* [ */ t[91] |= SC_COMPILER_C_CHAR_IDENT;
552 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
557 //-----------------------------------------------------------------------------
559 static bool lcl_isValidQuotedText( const String& rFormula, xub_StrLen nSrcPos, ParseResult& rRes )
561 // Tokens that start at ' can have anything in them until a final '
562 // but '' marks an escaped '
563 // We've earlier guaranteed that a string containing '' will be
564 // surrounded by '
565 if (rFormula.GetChar(nSrcPos) == '\'')
567 xub_StrLen nPos = nSrcPos+1;
568 while (nPos < rFormula.Len())
570 if (rFormula.GetChar(nPos) == '\'')
572 if ( (nPos+1 == rFormula.Len()) || (rFormula.GetChar(nPos+1) != '\'') )
574 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
575 rRes.EndPos = nPos+1;
576 return true;
578 ++nPos;
580 ++nPos;
584 return false;
587 static bool lcl_parseExternalName(
588 const String& rSymbol,
589 String& rFile,
590 String& rName,
591 const sal_Unicode cSep,
592 const ScDocument* pDoc = NULL,
593 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
595 /* TODO: future versions will have to support sheet-local names too, thus
596 * return a possible sheet name as well. */
597 const sal_Unicode* const pStart = rSymbol.GetBuffer();
598 const sal_Unicode* p = pStart;
599 xub_StrLen nLen = rSymbol.Len();
600 sal_Unicode cPrev = 0;
601 String aTmpFile, aTmpName;
602 xub_StrLen i = 0;
603 bool bInName = false;
604 if (cSep == '!')
606 // For XL use existing parser that resolves bracketed and quoted and
607 // indexed external document names.
608 ScRange aRange;
609 String aStartTabName, aEndTabName;
610 USHORT nFlags = 0;
611 p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
612 aEndTabName, nFlags, true, pExternalLinks );
613 if (!p || p == pStart)
614 return false;
615 i = xub_StrLen(p - pStart);
616 cPrev = *(p-1);
618 for ( ; i < nLen; ++i, ++p)
620 sal_Unicode c = *p;
621 if (i == 0)
623 if (c == '.' || c == cSep)
624 return false;
626 if (c == '\'')
628 // Move to the next chart and loop until the second single
629 // quote.
630 cPrev = c;
631 ++i; ++p;
632 for (xub_StrLen j = i; j < nLen; ++j, ++p)
634 c = *p;
635 if (c == '\'')
637 if (j == i)
639 // empty quote e.g. (=''!Name)
640 return false;
643 if (cPrev == '\'')
645 // two consecutive quotes equals a single
646 // quote in the file name.
647 aTmpFile.Append(c);
648 cPrev = 'a';
650 else
651 cPrev = c;
653 continue;
656 if (cPrev == '\'' && j != i)
658 // this is not a quote but the previous one
659 // is. This ends the parsing of the quoted
660 // segment.
662 i = j;
663 bInName = true;
664 break;
666 aTmpFile.Append(c);
667 cPrev = c;
670 if (!bInName)
672 // premature ending of the quoted segment.
673 return false;
676 if (c != cSep)
678 // only the separator is allowed after the closing quote.
679 return false;
682 cPrev = c;
683 continue;
687 if (bInName)
689 if (c == cSep)
691 // A second separator ? Not a valid external name.
692 return false;
694 aTmpName.Append(c);
696 else
698 if (c == cSep)
700 bInName = true;
702 else
706 if (CharClass::isAsciiAlphaNumeric(c))
707 // allowed.
708 break;
710 if (c > 128)
711 // non-ASCII character is allowed.
712 break;
714 bool bValid = false;
715 switch (c)
717 case '_':
718 case '-':
719 case '.':
720 // these special characters are allowed.
721 bValid = true;
722 break;
724 if (bValid)
725 break;
727 return false;
729 while (false);
730 aTmpFile.Append(c);
733 cPrev = c;
736 if (!bInName)
738 // No name found - most likely the symbol has no '!'s.
739 return false;
742 rFile = aTmpFile;
743 rName = aTmpName;
744 return true;
747 static String lcl_makeExternalNameStr( const String& rFile, const String& rName,
748 const sal_Unicode cSep, bool bODF )
750 String aFile( rFile), aName( rName), aEscQuote( RTL_CONSTASCII_USTRINGPARAM("''"));
751 aFile.SearchAndReplaceAllAscii( "'", aEscQuote);
752 if (bODF)
753 aName.SearchAndReplaceAllAscii( "'", aEscQuote);
754 rtl::OUStringBuffer aBuf( aFile.Len() + aName.Len() + 9);
755 if (bODF)
756 aBuf.append( sal_Unicode( '['));
757 aBuf.append( sal_Unicode( '\''));
758 aBuf.append( aFile);
759 aBuf.append( sal_Unicode( '\''));
760 aBuf.append( cSep);
761 if (bODF)
762 aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "$$'"));
763 aBuf.append( aName);
764 if (bODF)
765 aBuf.appendAscii( RTL_CONSTASCII_STRINGPARAM( "']"));
766 return String( aBuf.makeStringAndClear());
769 static bool lcl_getLastTabName( String& rTabName2, const String& rTabName1,
770 const vector<String>& rTabNames, const ScComplexRefData& rRef )
772 SCsTAB nTabSpan = rRef.Ref2.nTab - rRef.Ref1.nTab;
773 if (nTabSpan > 0)
775 size_t nCount = rTabNames.size();
776 vector<String>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
777 vector<String>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
778 if (itr == rTabNames.end())
780 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
781 return false;
784 size_t nDist = ::std::distance(itrBeg, itr);
785 if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
787 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
788 return false;
791 rTabName2 = rTabNames[nDist+nTabSpan];
793 else
794 rTabName2 = rTabName1;
796 return true;
799 struct Convention_A1 : public ScCompiler::Convention
801 Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
802 static void MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol );
803 static void MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow );
805 ParseResult parseAnyToken( const String& rFormula,
806 xub_StrLen nSrcPos,
807 const CharClass* pCharClass) const
809 ParseResult aRet;
810 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
811 return aRet;
813 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
814 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
815 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
816 // '?' allowed in range names because of Xcl :-/
817 static const String aAddAllowed(String::CreateFromAscii("?#"));
818 return pCharClass->parseAnyToken( rFormula,
819 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
823 void Convention_A1::MakeColStr( rtl::OUStringBuffer& rBuffer, SCCOL nCol )
825 if ( !ValidCol( nCol) )
826 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
827 else
828 ::ScColToAlpha( rBuffer, nCol);
831 void Convention_A1::MakeRowStr( rtl::OUStringBuffer& rBuffer, SCROW nRow )
833 if ( !ValidRow(nRow) )
834 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
835 else
836 rBuffer.append(sal_Int32(nRow + 1));
839 //-----------------------------------------------------------------------------
841 struct ConventionOOO_A1 : public Convention_A1
843 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
844 ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
845 static String MakeTabStr( const ScCompiler& rComp, SCTAB nTab, String& aDoc )
847 String aString;
848 if (!rComp.GetDoc()->GetName( nTab, aString ))
849 aString = ScGlobal::GetRscString(STR_NO_REF_TABLE);
850 else
852 if ( aString.GetChar(0) == '\'' )
853 { // "'Doc'#Tab"
854 xub_StrLen nPos = ScGlobal::FindUnquoted( aString, SC_COMPILER_FILE_TAB_SEP);
855 if (nPos != STRING_NOTFOUND && nPos > 0 && aString.GetChar(nPos-1) == '\'')
857 aDoc = aString.Copy( 0, nPos + 1 );
858 aString.Erase( 0, nPos + 1 );
859 aDoc = INetURLObject::decode( aDoc, INET_HEX_ESCAPE,
860 INetURLObject::DECODE_UNAMBIGUOUS );
862 else
863 aDoc.Erase();
865 else
866 aDoc.Erase();
867 ScCompiler::CheckTabQuotes( aString, FormulaGrammar::CONV_OOO );
869 aString += '.';
870 return aString;
873 void MakeRefStrImpl( rtl::OUStringBuffer& rBuffer,
874 const ScCompiler& rComp,
875 const ScComplexRefData& rRef,
876 bool bSingleRef,
877 bool bODF ) const
879 if (bODF)
880 rBuffer.append(sal_Unicode('['));
881 ScComplexRefData aRef( rRef );
882 // In case absolute/relative positions weren't separately available:
883 // transform relative to absolute!
884 // AdjustReference( aRef.Ref1 );
885 // if( !bSingleRef )
886 // AdjustReference( aRef.Ref2 );
887 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
888 if( !bSingleRef )
889 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
890 if( aRef.Ref1.IsFlag3D() )
892 if (aRef.Ref1.IsTabDeleted())
894 if (!aRef.Ref1.IsTabRel())
895 rBuffer.append(sal_Unicode('$'));
896 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
897 rBuffer.append(sal_Unicode('.'));
899 else
901 String aDoc;
902 String aRefStr( MakeTabStr( rComp, aRef.Ref1.nTab, aDoc ) );
903 rBuffer.append(aDoc);
904 if (!aRef.Ref1.IsTabRel()) rBuffer.append(sal_Unicode('$'));
905 rBuffer.append(aRefStr);
908 else if (bODF)
909 rBuffer.append(sal_Unicode('.'));
910 if (!aRef.Ref1.IsColRel())
911 rBuffer.append(sal_Unicode('$'));
912 if ( aRef.Ref1.IsColDeleted() )
913 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
914 else
915 MakeColStr(rBuffer, aRef.Ref1.nCol );
916 if (!aRef.Ref1.IsRowRel())
917 rBuffer.append(sal_Unicode('$'));
918 if ( aRef.Ref1.IsRowDeleted() )
919 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
920 else
921 MakeRowStr( rBuffer, aRef.Ref1.nRow );
922 if (!bSingleRef)
924 rBuffer.append(sal_Unicode(':'));
925 if (aRef.Ref2.IsFlag3D() || aRef.Ref2.nTab != aRef.Ref1.nTab)
927 if (aRef.Ref2.IsTabDeleted())
929 if (!aRef.Ref2.IsTabRel())
930 rBuffer.append(sal_Unicode('$'));
931 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
932 rBuffer.append(sal_Unicode('.'));
934 else
936 String aDoc;
937 String aRefStr( MakeTabStr( rComp, aRef.Ref2.nTab, aDoc ) );
938 rBuffer.append(aDoc);
939 if (!aRef.Ref2.IsTabRel()) rBuffer.append(sal_Unicode('$'));
940 rBuffer.append(aRefStr);
943 else if (bODF)
944 rBuffer.append(sal_Unicode('.'));
945 if (!aRef.Ref2.IsColRel())
946 rBuffer.append(sal_Unicode('$'));
947 if ( aRef.Ref2.IsColDeleted() )
948 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
949 else
950 MakeColStr( rBuffer, aRef.Ref2.nCol );
951 if (!aRef.Ref2.IsRowRel())
952 rBuffer.append(sal_Unicode('$'));
953 if ( aRef.Ref2.IsRowDeleted() )
954 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
955 else
956 MakeRowStr( rBuffer, aRef.Ref2.nRow );
958 if (bODF)
959 rBuffer.append(sal_Unicode(']'));
962 void MakeRefStr( rtl::OUStringBuffer& rBuffer,
963 const ScCompiler& rComp,
964 const ScComplexRefData& rRef,
965 BOOL bSingleRef ) const
967 MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, false);
970 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
972 switch (eSymType)
974 case ScCompiler::Convention::ABS_SHEET_PREFIX:
975 return '$';
976 case ScCompiler::Convention::SHEET_SEPARATOR:
977 return '.';
980 return sal_Unicode(0);
983 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
984 const ScDocument* pDoc,
985 const ::com::sun::star::uno::Sequence<
986 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
988 return lcl_parseExternalName(rSymbol, rFile, rName, sal_Unicode('#'), pDoc, pExternalLinks);
991 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
993 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), false);
996 bool makeExternalSingleRefStr( ::rtl::OUStringBuffer& rBuffer, sal_uInt16 nFileId,
997 const String& rTabName, const ScSingleRefData& rRef,
998 ScExternalRefManager* pRefMgr, bool bDisplayTabName ) const
1000 if (bDisplayTabName)
1002 String aFile;
1003 const String* p = pRefMgr->getExternalFileName(nFileId);
1004 if (p)
1005 aFile = INetURLObject::decode(*p, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
1006 aFile.SearchAndReplaceAllAscii("'", String::CreateFromAscii("''"));
1008 rBuffer.append(sal_Unicode('\''));
1009 rBuffer.append(aFile);
1010 rBuffer.append(sal_Unicode('\''));
1011 rBuffer.append(sal_Unicode('#'));
1013 if (!rRef.IsTabRel())
1014 rBuffer.append(sal_Unicode('$'));
1015 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1017 rBuffer.append(sal_Unicode('.'));
1020 if (!rRef.IsColRel())
1021 rBuffer.append(sal_Unicode('$'));
1022 MakeColStr( rBuffer, rRef.nCol);
1023 if (!rRef.IsRowRel())
1024 rBuffer.append(sal_Unicode('$'));
1025 MakeRowStr( rBuffer, rRef.nRow);
1027 return true;
1030 void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1031 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1032 ScExternalRefManager* pRefMgr, bool bODF ) const
1034 ScSingleRefData aRef(rRef);
1035 aRef.CalcAbsIfRel(rCompiler.GetPos());
1037 if (bODF)
1038 rBuffer.append( sal_Unicode('['));
1039 makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef, pRefMgr, true);
1040 if (bODF)
1041 rBuffer.append( sal_Unicode(']'));
1044 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1045 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1046 ScExternalRefManager* pRefMgr ) const
1048 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
1051 void makeExternalRefStrImpl( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1052 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1053 ScExternalRefManager* pRefMgr, bool bODF ) const
1055 ScComplexRefData aRef(rRef);
1056 aRef.CalcAbsIfRel(rCompiler.GetPos());
1058 if (bODF)
1059 rBuffer.append( sal_Unicode('['));
1060 // Ensure that there's always a closing bracket, no premature returns.
1063 if (!makeExternalSingleRefStr(rBuffer, nFileId, rTabName, aRef.Ref1, pRefMgr, true))
1064 break;
1066 rBuffer.append(sal_Unicode(':'));
1068 String aLastTabName;
1069 bool bDisplayTabName = (aRef.Ref1.nTab != aRef.Ref2.nTab);
1070 if (bDisplayTabName)
1072 // Get the name of the last table.
1073 vector<String> aTabNames;
1074 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1075 if (aTabNames.empty())
1077 DBG_ERROR1( "ConventionOOO_A1::makeExternalRefStrImpl: no sheet names for document ID %s", nFileId);
1080 if (!lcl_getLastTabName(aLastTabName, rTabName, aTabNames, aRef))
1082 DBG_ERROR( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1083 // aLastTabName contains #REF!, proceed.
1086 else if (bODF)
1087 rBuffer.append( sal_Unicode('.')); // need at least the sheet separator in ODF
1088 makeExternalSingleRefStr( rBuffer, nFileId, aLastTabName,
1089 aRef.Ref2, pRefMgr, bDisplayTabName);
1090 } while (0);
1091 if (bODF)
1092 rBuffer.append( sal_Unicode(']'));
1095 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1096 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1097 ScExternalRefManager* pRefMgr ) const
1099 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, false);
1104 static const ConventionOOO_A1 ConvOOO_A1;
1105 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1 = &ConvOOO_A1;
1107 //-----------------------------------------------------------------------------
1109 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
1111 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
1112 void MakeRefStr( rtl::OUStringBuffer& rBuffer,
1113 const ScCompiler& rComp,
1114 const ScComplexRefData& rRef,
1115 BOOL bSingleRef ) const
1117 MakeRefStrImpl( rBuffer, rComp, rRef, bSingleRef, true);
1120 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1122 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('#'), true);
1125 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1126 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1127 ScExternalRefManager* pRefMgr ) const
1129 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
1132 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1133 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1134 ScExternalRefManager* pRefMgr ) const
1136 makeExternalRefStrImpl( rBuffer, rCompiler, nFileId, rTabName, rRef, pRefMgr, true);
1140 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
1141 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1_ODF = &ConvOOO_A1_ODF;
1143 //-----------------------------------------------------------------------------
1145 struct ConventionXL
1147 static bool GetDocAndTab( const ScCompiler& rComp,
1148 const ScSingleRefData& rRef,
1149 String& rDocName,
1150 String& rTabName )
1152 bool bHasDoc = false;
1154 rDocName.Erase();
1155 if (rRef.IsTabDeleted() ||
1156 !rComp.GetDoc()->GetName( rRef.nTab, rTabName ))
1158 rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE );
1159 return false;
1162 // Cheesy hack to unparse the OOO style "'Doc'#Tab"
1163 if ( rTabName.GetChar(0) == '\'' )
1165 xub_StrLen nPos = ScGlobal::FindUnquoted( rTabName, SC_COMPILER_FILE_TAB_SEP);
1166 if (nPos != STRING_NOTFOUND && nPos > 0 && rTabName.GetChar(nPos-1) == '\'')
1168 rDocName = rTabName.Copy( 0, nPos );
1169 // TODO : More research into how XL escapes the doc path
1170 rDocName = INetURLObject::decode( rDocName, INET_HEX_ESCAPE,
1171 INetURLObject::DECODE_UNAMBIGUOUS );
1172 rTabName.Erase( 0, nPos + 1 );
1173 bHasDoc = true;
1177 // XL uses the same sheet name quoting conventions in both modes
1178 // it is safe to use A1 here
1179 ScCompiler::CheckTabQuotes( rTabName, FormulaGrammar::CONV_XL_A1 );
1180 return bHasDoc;
1183 static void MakeDocStr( rtl::OUStringBuffer& rBuf,
1184 const ScCompiler& rComp,
1185 const ScComplexRefData& rRef,
1186 bool bSingleRef )
1188 if( rRef.Ref1.IsFlag3D() )
1190 String aStartTabName, aStartDocName, aEndTabName, aEndDocName;
1191 bool bStartHasDoc = false, bEndHasDoc = false;
1193 bStartHasDoc = GetDocAndTab( rComp, rRef.Ref1,
1194 aStartDocName, aStartTabName);
1196 if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1198 bEndHasDoc = GetDocAndTab( rComp, rRef.Ref2,
1199 aEndDocName, aEndTabName);
1201 else
1202 bEndHasDoc = bStartHasDoc;
1204 if( bStartHasDoc )
1206 // A ref across multipled workbooks ?
1207 if( !bEndHasDoc )
1208 return;
1210 rBuf.append( sal_Unicode( '[' ) );
1211 rBuf.append( aStartDocName );
1212 rBuf.append( sal_Unicode( ']' ) );
1215 rBuf.append( aStartTabName );
1216 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1218 rBuf.append( sal_Unicode( ':' ) );
1219 rBuf.append( aEndTabName );
1222 rBuf.append( sal_Unicode( '!' ) );
1226 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1228 switch (eSymType)
1230 case ScCompiler::Convention::ABS_SHEET_PREFIX:
1231 return sal_Unicode(0);
1232 case ScCompiler::Convention::SHEET_SEPARATOR:
1233 return '!';
1235 return sal_Unicode(0);
1238 static bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1239 const ScDocument* pDoc,
1240 const ::com::sun::star::uno::Sequence<
1241 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks )
1243 return lcl_parseExternalName( rSymbol, rFile, rName, sal_Unicode('!'), pDoc, pExternalLinks);
1246 static String makeExternalNameStr( const String& rFile, const String& rName )
1248 return lcl_makeExternalNameStr( rFile, rName, sal_Unicode('!'), false);
1251 static void makeExternalDocStr( ::rtl::OUStringBuffer& rBuffer, const String& rFullName )
1253 // Format that is easier to deal with inside OOo, because we use file
1254 // URL, and all characetrs are allowed. Check if it makes sense to do
1255 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1256 // and allows relative file path.
1258 // ['file:///path/to/source/filename.xls']
1260 rBuffer.append(sal_Unicode('['));
1261 rBuffer.append(sal_Unicode('\''));
1262 String aFullName = INetURLObject::decode(rFullName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
1263 const sal_Unicode* pBuf = aFullName.GetBuffer();
1264 xub_StrLen nLen = aFullName.Len();
1265 for (xub_StrLen i = 0; i < nLen; ++i)
1267 const sal_Unicode c = pBuf[i];
1268 if (c == sal_Unicode('\''))
1269 rBuffer.append(c);
1270 rBuffer.append(c);
1272 rBuffer.append(sal_Unicode('\''));
1273 rBuffer.append(sal_Unicode(']'));
1276 static void makeExternalTabNameRange( ::rtl::OUStringBuffer& rBuf, const String& rTabName,
1277 const vector<String>& rTabNames,
1278 const ScComplexRefData& rRef )
1280 String aLastTabName;
1281 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1283 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1284 return;
1287 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1288 if (rTabName != aLastTabName)
1290 rBuf.append(sal_Unicode(':'));
1291 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1295 static void parseExternalDocName( const String& rFormula, xub_StrLen& rSrcPos )
1297 xub_StrLen nLen = rFormula.Len();
1298 const sal_Unicode* p = rFormula.GetBuffer();
1299 sal_Unicode cPrev = 0;
1300 for (xub_StrLen i = rSrcPos; i < nLen; ++i)
1302 sal_Unicode c = p[i];
1303 if (i == rSrcPos)
1305 // first character must be '['.
1306 if (c != '[')
1307 return;
1309 else if (i == rSrcPos + 1)
1311 // second character must be a single quote.
1312 if (c != '\'')
1313 return;
1315 else if (c == '\'')
1317 if (cPrev == '\'')
1318 // two successive single quote is treated as a single
1319 // valid character.
1320 c = 'a';
1322 else if (c == ']')
1324 if (cPrev == '\'')
1326 // valid source document path found. Increment the
1327 // current position to skip the source path.
1328 rSrcPos = i + 1;
1329 if (rSrcPos >= nLen)
1330 rSrcPos = nLen - 1;
1331 return;
1333 else
1334 return;
1336 else
1338 // any other character
1339 if (i > rSrcPos + 2 && cPrev == '\'')
1340 // unless it's the 3rd character, a normal character
1341 // following immediately a single quote is invalid.
1342 return;
1344 cPrev = c;
1349 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1351 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1352 ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1354 void makeSingleCellStr( ::rtl::OUStringBuffer& rBuf, const ScSingleRefData& rRef ) const
1356 if (!rRef.IsColRel())
1357 rBuf.append(sal_Unicode('$'));
1358 MakeColStr(rBuf, rRef.nCol);
1359 if (!rRef.IsRowRel())
1360 rBuf.append(sal_Unicode('$'));
1361 MakeRowStr(rBuf, rRef.nRow);
1364 void MakeRefStr( rtl::OUStringBuffer& rBuf,
1365 const ScCompiler& rComp,
1366 const ScComplexRefData& rRef,
1367 BOOL bSingleRef ) const
1369 ScComplexRefData aRef( rRef );
1371 // Play fast and loose with invalid refs. There is not much point in producing
1372 // Foo!A1:#REF! versus #REF! at this point
1373 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
1375 MakeDocStr( rBuf, rComp, aRef, bSingleRef );
1377 if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
1379 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1380 return;
1383 if( !bSingleRef )
1385 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
1386 if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
1388 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1389 return;
1392 if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
1394 if (!aRef.Ref1.IsRowRel())
1395 rBuf.append(sal_Unicode( '$' ));
1396 MakeRowStr( rBuf, aRef.Ref1.nRow );
1397 rBuf.append(sal_Unicode( ':' ));
1398 if (!aRef.Ref2.IsRowRel())
1399 rBuf.append(sal_Unicode( '$' ));
1400 MakeRowStr( rBuf, aRef.Ref2.nRow );
1401 return;
1404 if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
1406 if (!aRef.Ref1.IsColRel())
1407 rBuf.append(sal_Unicode( '$' ));
1408 MakeColStr(rBuf, aRef.Ref1.nCol );
1409 rBuf.append(sal_Unicode( ':' ));
1410 if (!aRef.Ref2.IsColRel())
1411 rBuf.append(sal_Unicode( '$' ));
1412 MakeColStr(rBuf, aRef.Ref2.nCol );
1413 return;
1417 makeSingleCellStr(rBuf, aRef.Ref1);
1418 if (!bSingleRef)
1420 rBuf.append(sal_Unicode( ':' ));
1421 makeSingleCellStr(rBuf, aRef.Ref2);
1425 virtual ParseResult parseAnyToken( const String& rFormula,
1426 xub_StrLen nSrcPos,
1427 const CharClass* pCharClass) const
1429 ParseResult aRet;
1430 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1431 return aRet;
1433 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1434 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1435 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1436 // '?' allowed in range names
1437 static const String aAddAllowed = String::CreateFromAscii("?!");
1438 return pCharClass->parseAnyToken( rFormula,
1439 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1442 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1444 return ConventionXL::getSpecialSymbol(eSymType);
1447 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1448 const ScDocument* pDoc,
1449 const ::com::sun::star::uno::Sequence<
1450 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1452 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1455 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1457 return ConventionXL::makeExternalNameStr(rFile, rName);
1460 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1461 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1462 ScExternalRefManager* pRefMgr ) const
1464 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1465 // This is a little different from the format Excel uses, as Excel
1466 // puts [] only around the file name. But we need to enclose the
1467 // whole file path with [] because the file name can contain any
1468 // characters.
1470 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1471 if (!pFullName)
1472 return;
1474 ScSingleRefData aRef(rRef);
1475 aRef.CalcAbsIfRel(rCompiler.GetPos());
1477 ConventionXL::makeExternalDocStr(rBuffer, *pFullName);
1478 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1479 rBuffer.append(sal_Unicode('!'));
1481 makeSingleCellStr(rBuffer, aRef);
1484 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1485 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1486 ScExternalRefManager* pRefMgr ) const
1488 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1489 if (!pFullName)
1490 return;
1492 vector<String> aTabNames;
1493 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1494 if (aTabNames.empty())
1495 return;
1497 ScComplexRefData aRef(rRef);
1498 aRef.CalcAbsIfRel(rCompiler.GetPos());
1500 ConventionXL::makeExternalDocStr(rBuffer, *pFullName);
1501 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
1502 rBuffer.append(sal_Unicode('!'));
1504 makeSingleCellStr(rBuffer, aRef.Ref1);
1505 if (aRef.Ref1 != aRef.Ref2)
1507 rBuffer.append(sal_Unicode(':'));
1508 makeSingleCellStr(rBuffer, aRef.Ref2);
1513 static const ConventionXL_A1 ConvXL_A1;
1514 const ScCompiler::Convention * const ScCompiler::pConvXL_A1 = &ConvXL_A1;
1517 struct ConventionXL_OOX : public ConventionXL_A1
1519 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1522 static const ConventionXL_OOX ConvXL_OOX;
1523 const ScCompiler::Convention * const ScCompiler::pConvXL_OOX = &ConvXL_OOX;
1526 //-----------------------------------------------------------------------------
1528 static void
1529 r1c1_add_col( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
1531 rBuf.append( sal_Unicode( 'C' ) );
1532 if( rRef.IsColRel() )
1534 if (rRef.nRelCol != 0)
1536 rBuf.append( sal_Unicode( '[' ) );
1537 rBuf.append( String::CreateFromInt32( rRef.nRelCol ) );
1538 rBuf.append( sal_Unicode( ']' ) );
1541 else
1542 rBuf.append( String::CreateFromInt32( rRef.nCol + 1 ) );
1544 static void
1545 r1c1_add_row( rtl::OUStringBuffer &rBuf, const ScSingleRefData& rRef )
1547 rBuf.append( sal_Unicode( 'R' ) );
1548 if( rRef.IsRowRel() )
1550 if (rRef.nRelRow != 0)
1552 rBuf.append( sal_Unicode( '[' ) );
1553 rBuf.append( String::CreateFromInt32( rRef.nRelRow ) );
1554 rBuf.append( sal_Unicode( ']' ) );
1557 else
1558 rBuf.append( String::CreateFromInt32( rRef.nRow + 1 ) );
1561 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1563 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1564 void MakeRefStr( rtl::OUStringBuffer& rBuf,
1565 const ScCompiler& rComp,
1566 const ScComplexRefData& rRef,
1567 BOOL bSingleRef ) const
1569 ScComplexRefData aRef( rRef );
1571 MakeDocStr( rBuf, rComp, aRef, bSingleRef );
1573 // Play fast and loose with invalid refs. There is not much point in producing
1574 // Foo!A1:#REF! versus #REF! at this point
1575 aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
1576 if( aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() )
1578 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1579 return;
1582 if( !bSingleRef )
1584 aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
1585 if( aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() )
1587 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1588 return;
1591 if( aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL )
1593 r1c1_add_row( rBuf, rRef.Ref1 );
1594 if( rRef.Ref1.nRow != rRef.Ref2.nRow ||
1595 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ) {
1596 rBuf.append (sal_Unicode ( ':' ) );
1597 r1c1_add_row( rBuf, rRef.Ref2 );
1599 return;
1603 if( aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW )
1605 r1c1_add_col( rBuf, rRef.Ref1 );
1606 if( rRef.Ref1.nCol != rRef.Ref2.nCol ||
1607 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel() )
1609 rBuf.append (sal_Unicode ( ':' ) );
1610 r1c1_add_col( rBuf, rRef.Ref2 );
1612 return;
1616 r1c1_add_row( rBuf, rRef.Ref1 );
1617 r1c1_add_col( rBuf, rRef.Ref1 );
1618 if (!bSingleRef)
1620 rBuf.append (sal_Unicode ( ':' ) );
1621 r1c1_add_row( rBuf, rRef.Ref2 );
1622 r1c1_add_col( rBuf, rRef.Ref2 );
1626 ParseResult parseAnyToken( const String& rFormula,
1627 xub_StrLen nSrcPos,
1628 const CharClass* pCharClass) const
1630 ConventionXL::parseExternalDocName(rFormula, nSrcPos);
1632 ParseResult aRet;
1633 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1634 return aRet;
1636 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1637 KParseTokens::ASC_UNDERSCORE ;
1638 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1639 // '?' allowed in range names
1640 static const String aAddAllowed = String::CreateFromAscii( "?-[]!" );
1642 return pCharClass->parseAnyToken( rFormula,
1643 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1646 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1648 return ConventionXL::getSpecialSymbol(eSymType);
1651 virtual bool parseExternalName( const String& rSymbol, String& rFile, String& rName,
1652 const ScDocument* pDoc,
1653 const ::com::sun::star::uno::Sequence<
1654 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1656 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1659 virtual String makeExternalNameStr( const String& rFile, const String& rName ) const
1661 return ConventionXL::makeExternalNameStr(rFile, rName);
1664 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1665 sal_uInt16 nFileId, const String& rTabName, const ScSingleRefData& rRef,
1666 ScExternalRefManager* pRefMgr ) const
1668 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1669 // This is a little different from the format Excel uses, as Excel
1670 // puts [] only around the file name. But we need to enclose the
1671 // whole file path with [] because the file name can contain any
1672 // characters.
1674 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1675 if (!pFullName)
1676 return;
1678 ScSingleRefData aRef(rRef);
1679 aRef.CalcAbsIfRel(rCompiler.GetPos());
1681 ConventionXL::makeExternalDocStr(rBuffer, *pFullName);
1682 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1683 rBuffer.append(sal_Unicode('!'));
1685 r1c1_add_row(rBuffer, aRef);
1686 r1c1_add_col(rBuffer, aRef);
1689 virtual void makeExternalRefStr( ::rtl::OUStringBuffer& rBuffer, const ScCompiler& rCompiler,
1690 sal_uInt16 nFileId, const String& rTabName, const ScComplexRefData& rRef,
1691 ScExternalRefManager* pRefMgr ) const
1693 const String* pFullName = pRefMgr->getExternalFileName(nFileId);
1694 if (!pFullName)
1695 return;
1697 vector<String> aTabNames;
1698 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
1699 if (aTabNames.empty())
1700 return;
1702 ScComplexRefData aRef(rRef);
1703 aRef.CalcAbsIfRel(rCompiler.GetPos());
1705 ConventionXL::makeExternalDocStr(rBuffer, *pFullName);
1706 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, aTabNames, aRef);
1707 rBuffer.append(sal_Unicode('!'));
1709 if (aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted())
1711 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1712 return;
1715 if (aRef.Ref1.nCol == 0 && aRef.Ref2.nCol >= MAXCOL)
1717 r1c1_add_row(rBuffer, rRef.Ref1);
1718 if (rRef.Ref1.nRow != rRef.Ref2.nRow || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1720 rBuffer.append (sal_Unicode(':'));
1721 r1c1_add_row(rBuffer, rRef.Ref2);
1723 return;
1726 if (aRef.Ref1.nRow == 0 && aRef.Ref2.nRow >= MAXROW)
1728 r1c1_add_col(rBuffer, aRef.Ref1);
1729 if (aRef.Ref1.nCol != aRef.Ref2.nCol || aRef.Ref1.IsColRel() != aRef.Ref2.IsColRel())
1731 rBuffer.append (sal_Unicode(':'));
1732 r1c1_add_col(rBuffer, aRef.Ref2);
1734 return;
1737 r1c1_add_row(rBuffer, aRef.Ref1);
1738 r1c1_add_col(rBuffer, aRef.Ref1);
1739 rBuffer.append (sal_Unicode (':'));
1740 r1c1_add_row(rBuffer, aRef.Ref2);
1741 r1c1_add_col(rBuffer, aRef.Ref2);
1745 static const ConventionXL_R1C1 ConvXL_R1C1;
1746 const ScCompiler::Convention * const ScCompiler::pConvXL_R1C1 = &ConvXL_R1C1;
1748 //-----------------------------------------------------------------------------
1749 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr)
1750 : FormulaCompiler(rArr),
1751 pDoc( pDocument ),
1752 aPos( rPos ),
1753 pCharClass( ScGlobal::pCharClass ),
1754 mnPredetectedReference(0),
1755 mnRangeOpPosInSymbol(-1),
1756 pConv( pConvOOO_A1 ),
1757 mbCloseBrackets( true ),
1758 mbExtendedErrorDetection( false ),
1759 mbRewind( false )
1761 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1764 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos)
1766 pDoc( pDocument ),
1767 aPos( rPos ),
1768 pCharClass( ScGlobal::pCharClass ),
1769 mnPredetectedReference(0),
1770 mnRangeOpPosInSymbol(-1),
1771 pConv( pConvOOO_A1 ),
1772 mbCloseBrackets( true ),
1773 mbExtendedErrorDetection( false ),
1774 mbRewind( false )
1776 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1779 void ScCompiler::CheckTabQuotes( String& rString,
1780 const FormulaGrammar::AddressConvention eConv )
1782 using namespace ::com::sun::star::i18n;
1783 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1784 sal_Int32 nContFlags = nStartFlags;
1785 ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
1786 KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_STRING, nContFlags, EMPTY_STRING);
1787 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.Len());
1789 switch ( eConv )
1791 default :
1792 case FormulaGrammar::CONV_UNSPECIFIED :
1793 break;
1794 case FormulaGrammar::CONV_OOO :
1795 case FormulaGrammar::CONV_XL_A1 :
1796 case FormulaGrammar::CONV_XL_R1C1 :
1797 case FormulaGrammar::CONV_XL_OOX :
1798 if( bNeedsQuote )
1800 static const String one_quote = static_cast<sal_Unicode>( '\'' );
1801 static const String two_quote = String::CreateFromAscii( "''" );
1802 // escape embedded quotes
1803 rString.SearchAndReplaceAll( one_quote, two_quote );
1805 break;
1808 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1810 // Prevent any possible confusion resulting from pure numeric sheet names.
1811 bNeedsQuote = true;
1814 if( bNeedsQuote )
1816 rString.Insert( '\'', 0 );
1817 rString += '\'';
1821 //---------------------------------------------------------------------------
1823 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
1825 switch ( eConv ) {
1826 case FormulaGrammar::CONV_UNSPECIFIED :
1827 break;
1828 default :
1829 case FormulaGrammar::CONV_OOO : SetRefConvention( pConvOOO_A1 ); break;
1830 case FormulaGrammar::CONV_ODF : SetRefConvention( pConvOOO_A1_ODF ); break;
1831 case FormulaGrammar::CONV_XL_A1 : SetRefConvention( pConvXL_A1 ); break;
1832 case FormulaGrammar::CONV_XL_R1C1 : SetRefConvention( pConvXL_R1C1 ); break;
1833 case FormulaGrammar::CONV_XL_OOX : SetRefConvention( pConvXL_OOX ); break;
1837 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
1839 pConv = pConvP;
1840 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
1841 DBG_ASSERT( FormulaGrammar::isSupported( meGrammar),
1842 "ScCompiler::SetRefConvention: unsupported grammar resulting");
1845 void ScCompiler::SetError(USHORT nError)
1847 if( !pArr->GetCodeError() )
1848 pArr->SetCodeError( nError);
1852 sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, xub_StrLen nMax )
1854 const sal_Unicode* const pStop = pDst + nMax;
1855 while ( *pSrc && pDst < pStop )
1857 *pDst++ = *pSrc++;
1859 *pDst = 0;
1860 return pDst;
1864 //---------------------------------------------------------------------------
1865 // NextSymbol
1866 //---------------------------------------------------------------------------
1867 // Zerlegt die Formel in einzelne Symbole fuer die weitere
1868 // Verarbeitung (Turing-Maschine).
1869 //---------------------------------------------------------------------------
1870 // Ausgangs Zustand = GetChar
1871 //---------------+-------------------+-----------------------+---------------
1872 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1873 //---------------+-------------------+-----------------------+---------------
1874 // GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop
1875 // | <> | Symbol=Zeichen | GetBool
1876 // | $ Buchstabe | Symbol=Zeichen | GetWord
1877 // | Ziffer | Symbol=Zeichen | GetValue
1878 // | " | Keine | GetString
1879 // | Sonst | Keine | GetChar
1880 //---------------+-------------------+-----------------------+---------------
1881 // GetBool | => | Symbol=Symbol+Zeichen | Stop
1882 // | Sonst | Dec(CharPos) | Stop
1883 //---------------+-------------------+-----------------------+---------------
1884 // GetWord | SepSymbol | Dec(CharPos) | Stop
1885 // | ()+-*/^=<>&~ | |
1886 // | Leerzeichen | Dec(CharPos) | Stop
1887 // | $_:. | |
1888 // | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord
1889 // | Sonst | Fehler | Stop
1890 //---------------|-------------------+-----------------------+---------------
1891 // GetValue | ;()*/^=<>& | |
1892 // | Leerzeichen | Dec(CharPos) | Stop
1893 // | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue
1894 // | Sonst | Fehler | Stop
1895 //---------------+-------------------+-----------------------+---------------
1896 // GetString | " | Keine | Stop
1897 // | Sonst | Symbol=Symbol+Zeichen | GetString
1898 //---------------+-------------------+-----------------------+---------------
1900 xub_StrLen ScCompiler::NextSymbol(bool bInArray)
1902 cSymbol[MAXSTRLEN-1] = 0; // Stopper
1903 sal_Unicode* pSym = cSymbol;
1904 const sal_Unicode* const pStart = aFormula.GetBuffer();
1905 const sal_Unicode* pSrc = pStart + nSrcPos;
1906 bool bi18n = false;
1907 sal_Unicode c = *pSrc;
1908 sal_Unicode cLast = 0;
1909 bool bQuote = false;
1910 mnRangeOpPosInSymbol = -1;
1911 ScanState eState = ssGetChar;
1912 xub_StrLen nSpaces = 0;
1913 sal_Unicode cSep = mxSymbols->getSymbol( ocSep).GetChar(0);
1914 sal_Unicode cArrayColSep = mxSymbols->getSymbol( ocArrayColSep).GetChar(0);
1915 sal_Unicode cArrayRowSep = mxSymbols->getSymbol( ocArrayRowSep).GetChar(0);
1916 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' :
1917 ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0));
1919 // special symbols specific to address convention used
1920 sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
1921 sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
1923 int nDecSeps = 0;
1924 bool bAutoIntersection = false;
1925 int nRefInName = 0;
1926 mnPredetectedReference = 0;
1927 // try to parse simple tokens before calling i18n parser
1928 while ((c != 0) && (eState != ssStop) )
1930 pSrc++;
1931 ULONG nMask = GetCharTableFlags( c );
1932 // The parameter separator and the array column and row separators end
1933 // things unconditionally if not in string or reference.
1934 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
1936 switch (eState)
1938 // these are to be continued
1939 case ssGetString:
1940 case ssSkipString:
1941 case ssGetReference:
1942 case ssSkipReference:
1943 break;
1944 default:
1945 if (eState == ssGetChar)
1946 *pSym++ = c;
1947 else
1948 pSrc--;
1949 eState = ssStop;
1952 Label_MaskStateMachine:
1953 switch (eState)
1955 case ssGetChar :
1957 // Order is important!
1958 if( nMask & SC_COMPILER_C_ODF_LABEL_OP )
1960 // '!!' automatic intersection
1961 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_LABEL_OP)
1963 /* TODO: For now the UI "space operator" is used, this
1964 * could be enhanced using a specialized OpCode to get
1965 * rid of the space ambiguity, which would need some
1966 * places to be adapted though. And we would still need
1967 * to support the ambiguous space operator for UI
1968 * purposes anyway. However, we then could check for
1969 * invalid usage of '!!', which currently isn't
1970 * possible. */
1971 if (!bAutoIntersection)
1973 ++pSrc;
1974 nSpaces += 2; // must match the character count
1975 bAutoIntersection = true;
1977 else
1979 pSrc--;
1980 eState = ssStop;
1983 else
1985 nMask &= ~SC_COMPILER_C_ODF_LABEL_OP;
1986 goto Label_MaskStateMachine;
1989 else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER )
1991 // '$$' defined name marker
1992 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_ODF_NAME_MARKER)
1994 // both eaten, not added to pSym
1995 ++pSrc;
1997 else
1999 nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER;
2000 goto Label_MaskStateMachine;
2003 else if( nMask & SC_COMPILER_C_CHAR )
2005 *pSym++ = c;
2006 eState = ssStop;
2008 else if( nMask & SC_COMPILER_C_ODF_LBRACKET )
2010 // eaten, not added to pSym
2011 eState = ssGetReference;
2012 mnPredetectedReference = 1;
2014 else if( nMask & SC_COMPILER_C_CHAR_BOOL )
2016 *pSym++ = c;
2017 eState = ssGetBool;
2019 else if( nMask & SC_COMPILER_C_CHAR_VALUE )
2021 *pSym++ = c;
2022 eState = ssGetValue;
2024 else if( nMask & SC_COMPILER_C_CHAR_STRING )
2026 *pSym++ = c;
2027 eState = ssGetString;
2029 else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
2031 nSpaces++;
2033 else if( nMask & SC_COMPILER_C_CHAR_IDENT )
2034 { // try to get a simple ASCII identifier before calling
2035 // i18n, to gain performance during import
2036 *pSym++ = c;
2037 eState = ssGetIdent;
2039 else
2041 bi18n = true;
2042 eState = ssStop;
2045 break;
2046 case ssGetIdent:
2048 if ( nMask & SC_COMPILER_C_IDENT )
2049 { // This catches also $Sheet1.A$1, for example.
2050 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2052 SetError(errStringOverflow);
2053 eState = ssStop;
2055 else
2056 *pSym++ = c;
2058 else if (c == ':' && mnRangeOpPosInSymbol < 0)
2060 // One range operator may form Sheet1.A:A, which we need to
2061 // pass as one entity to IsReference().
2062 mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2063 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2065 SetError(errStringOverflow);
2066 eState = ssStop;
2068 else
2069 *pSym++ = c;
2071 else if ( 128 <= c || '\'' == c )
2072 { // High values need reparsing with i18n,
2073 // single quoted $'sheet' names too (otherwise we'd had to
2074 // implement everything twice).
2075 bi18n = true;
2076 eState = ssStop;
2078 else
2080 pSrc--;
2081 eState = ssStop;
2084 break;
2085 case ssGetBool :
2087 if( nMask & SC_COMPILER_C_BOOL )
2089 *pSym++ = c;
2090 eState = ssStop;
2092 else
2094 pSrc--;
2095 eState = ssStop;
2098 break;
2099 case ssGetValue :
2101 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2103 SetError(errStringOverflow);
2104 eState = ssStop;
2106 else if (c == cDecSep)
2108 if (++nDecSeps > 1)
2110 // reparse with i18n, may be numeric sheet name as well
2111 bi18n = true;
2112 eState = ssStop;
2114 else
2115 *pSym++ = c;
2117 else if( nMask & SC_COMPILER_C_VALUE )
2118 *pSym++ = c;
2119 else if( nMask & SC_COMPILER_C_VALUE_SEP )
2121 pSrc--;
2122 eState = ssStop;
2124 else if (c == 'E' || c == 'e')
2126 if (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_EXP)
2127 *pSym++ = c;
2128 else
2130 // reparse with i18n
2131 bi18n = true;
2132 eState = ssStop;
2135 else if( nMask & SC_COMPILER_C_VALUE_SIGN )
2137 if (((cLast == 'E') || (cLast == 'e')) &&
2138 (GetCharTableFlags( pSrc[0] ) & SC_COMPILER_C_VALUE_VALUE))
2140 *pSym++ = c;
2142 else
2144 pSrc--;
2145 eState = ssStop;
2148 else
2150 // reparse with i18n
2151 bi18n = true;
2152 eState = ssStop;
2155 break;
2156 case ssGetString :
2158 if( nMask & SC_COMPILER_C_STRING_SEP )
2160 if ( !bQuote )
2162 if ( *pSrc == '"' )
2163 bQuote = true; // "" => literal "
2164 else
2165 eState = ssStop;
2167 else
2168 bQuote = false;
2170 if ( !bQuote )
2172 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2174 SetError(errStringOverflow);
2175 eState = ssSkipString;
2177 else
2178 *pSym++ = c;
2181 break;
2182 case ssSkipString:
2183 if( nMask & SC_COMPILER_C_STRING_SEP )
2184 eState = ssStop;
2185 break;
2186 case ssGetReference:
2187 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2189 SetError( errStringOverflow);
2190 eState = ssSkipReference;
2192 // fall through and follow logic
2193 case ssSkipReference:
2194 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2195 // mandatory also if no sheet name. 'External'# is optional,
2196 // sheet name is optional, quotes around sheet name are
2197 // optional if no quote contained.
2198 // 2nd usage: ['Sheet'.$$'DefinedName']
2199 // 3rd usage: ['External'#$$'DefinedName']
2200 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2201 // Also for all these names quotes are optional if no quote
2202 // contained.
2205 // nRefInName: 0 := not in sheet name yet. 'External'
2206 // is parsed as if it was a sheet name and nRefInName
2207 // is reset when # is encountered immediately after closing
2208 // quote. Same with 'DefinedName', nRefInName is cleared
2209 // when : is encountered.
2211 // Encountered leading $ before sheet name.
2212 static const int kDollar = (1 << 1);
2213 // Encountered ' opening quote, which may be after $ or
2214 // not.
2215 static const int kOpen = (1 << 2);
2216 // Somewhere in name.
2217 static const int kName = (1 << 3);
2218 // Encountered ' in name, will be cleared if double or
2219 // transformed to kClose if not, in which case kOpen is
2220 // cleared.
2221 static const int kQuote = (1 << 4);
2222 // Past ' closing quote.
2223 static const int kClose = (1 << 5);
2224 // Past . sheet name separator.
2225 static const int kPast = (1 << 6);
2226 // Marked name $$ follows sheet name separator, detected
2227 // while we're still on the separator. Will be cleared when
2228 // entering the name.
2229 static const int kMarkAhead = (1 << 7);
2230 // In marked defined name.
2231 static const int kDefName = (1 << 8);
2233 bool bAddToSymbol = true;
2234 if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
2236 DBG_ASSERT( nRefInName & (kPast | kDefName),
2237 "ScCompiler::NextSymbol: reference: "
2238 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2239 // eaten, not added to pSym
2240 bAddToSymbol = false;
2241 eState = ssStop;
2243 else if (cSheetSep == c && nRefInName == 0)
2245 // eat it, no sheet name [.A1]
2246 bAddToSymbol = false;
2247 nRefInName |= kPast;
2248 if ('$' == pSrc[0] && '$' == pSrc[1])
2249 nRefInName |= kMarkAhead;
2251 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2253 // Not in col/row yet.
2255 if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2257 nRefInName &= ~kMarkAhead;
2258 if (!(nRefInName & kDefName))
2260 // eaten, not added to pSym (2 chars)
2261 bAddToSymbol = false;
2262 ++pSrc;
2263 nRefInName &= kPast;
2264 nRefInName |= kDefName;
2266 else
2268 // ScAddress::Parse() will recognize this as
2269 // invalid later.
2270 if (eState != ssSkipReference)
2272 *pSym++ = c;
2273 *pSym++ = *pSrc++;
2275 bAddToSymbol = false;
2278 else if (cSheetPrefix == c && nRefInName == 0)
2279 nRefInName |= kDollar;
2280 else if ('\'' == c)
2282 // TODO: The conventions' parseExternalName()
2283 // should handle quoted names, but as long as they
2284 // don't remove non-embedded quotes here.
2285 if (!(nRefInName & kName))
2287 nRefInName |= (kOpen | kName);
2288 bAddToSymbol = !(nRefInName & kDefName);
2290 else if (!(nRefInName & kOpen))
2292 DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
2293 "a ''' without the name being enclosed in '...' violates ODF spec");
2295 else if (nRefInName & kQuote)
2297 // escaped embedded quote
2298 nRefInName &= ~kQuote;
2300 else
2302 if ('\'' == pSrc[0])
2304 // escapes embedded quote
2305 nRefInName |= kQuote;
2307 else
2309 // quote not followed by quote => close
2310 nRefInName |= kClose;
2311 nRefInName &= ~kOpen;
2313 bAddToSymbol = !(nRefInName & kDefName);
2316 else if (cSheetSep == c && !(nRefInName & kOpen))
2318 // unquoted sheet name separator
2319 nRefInName |= kPast;
2320 if ('$' == pSrc[0] && '$' == pSrc[1])
2321 nRefInName |= kMarkAhead;
2323 else if (':' == c && !(nRefInName & kOpen))
2325 DBG_ERRORFILE("ScCompiler::NextSymbol: reference: "
2326 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2327 nRefInName = 0;
2328 ++mnPredetectedReference;
2330 else if (!(nRefInName & kName))
2332 // start unquoted name
2333 nRefInName |= kName;
2336 else if (':' == c)
2338 // range operator
2339 nRefInName = 0;
2340 ++mnPredetectedReference;
2342 if (bAddToSymbol && eState != ssSkipReference)
2343 *pSym++ = c; // everything is part of reference
2345 break;
2346 case ssStop:
2347 ; // nothing, prevent warning
2348 break;
2350 cLast = c;
2351 c = *pSrc;
2353 if ( bi18n )
2355 nSrcPos = sal::static_int_cast<xub_StrLen>( nSrcPos + nSpaces );
2356 String aSymbol;
2357 mnRangeOpPosInSymbol = -1;
2358 USHORT nErr = 0;
2361 bi18n = false;
2362 // special case (e.g. $'sheetname' in OOO A1)
2363 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2364 aSymbol += pStart[nSrcPos++];
2366 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass );
2368 if ( !aRes.TokenType )
2369 SetError( nErr = errIllegalChar ); // parsed chars as string
2370 if ( aRes.EndPos <= nSrcPos )
2371 { // ?!?
2372 SetError( nErr = errIllegalChar );
2373 nSrcPos = aFormula.Len();
2374 aSymbol.Erase();
2376 else
2378 aSymbol.Append( pStart + nSrcPos, (xub_StrLen)aRes.EndPos - nSrcPos );
2379 nSrcPos = (xub_StrLen) aRes.EndPos;
2380 c = pStart[nSrcPos];
2381 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2382 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2383 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2385 // One range operator restarts parsing for second reference.
2386 if (c == ':' && mnRangeOpPosInSymbol < 0)
2388 mnRangeOpPosInSymbol = aSymbol.Len();
2389 bi18n = true;
2391 if ( bi18n )
2392 aSymbol += pStart[nSrcPos++];
2394 } while ( bi18n && !nErr );
2395 xub_StrLen nLen = aSymbol.Len();
2396 if ( nLen >= MAXSTRLEN )
2398 SetError( errStringOverflow );
2399 nLen = MAXSTRLEN-1;
2401 lcl_UnicodeStrNCpy( cSymbol, aSymbol.GetBuffer(), nLen );
2403 else
2405 nSrcPos = sal::static_int_cast<xub_StrLen>( pSrc - pStart );
2406 *pSym = 0;
2408 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2410 // This is a trailing range operator, which is nonsense. Will be caught
2411 // in next round.
2412 mnRangeOpPosInSymbol = -1;
2413 *--pSym = 0;
2414 --nSrcPos;
2416 if ( bAutoCorrect )
2417 aCorrectedSymbol = cSymbol;
2418 if (bAutoIntersection && nSpaces > 1)
2419 --nSpaces; // replace '!!' with only one space
2420 return nSpaces;
2423 //---------------------------------------------------------------------------
2424 // Convert symbol to token
2425 //---------------------------------------------------------------------------
2427 BOOL ScCompiler::IsOpCode( const String& rName, bool bInArray )
2429 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
2430 BOOL bFound = (iLook != mxSymbols->getHashMap()->end());
2431 if (bFound)
2433 ScRawToken aToken;
2434 OpCode eOp = iLook->second;
2435 if (bInArray)
2437 if (rName.Equals(mxSymbols->getSymbol(ocArrayColSep)))
2438 eOp = ocArrayColSep;
2439 else if (rName.Equals(mxSymbols->getSymbol(ocArrayRowSep)))
2440 eOp = ocArrayRowSep;
2442 aToken.SetOpCode(eOp);
2443 pRawToken = aToken.Clone();
2445 else
2447 String aIntName;
2448 if (mxSymbols->hasExternals())
2450 // If symbols are set by filters get mapping to exact name.
2451 ExternalHashMap::const_iterator iExt(
2452 mxSymbols->getExternalHashMap()->find( rName));
2453 if (iExt != mxSymbols->getExternalHashMap()->end())
2455 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2456 aIntName = (*iExt).second;
2458 if (!aIntName.Len())
2460 // If that isn't found we might continue with rName lookup as a
2461 // last resort by just falling through to FindFunction(), but
2462 // it shouldn't happen if the map was setup correctly. Don't
2463 // waste time and bail out.
2464 return FALSE;
2467 if (!aIntName.Len())
2469 // Old (deprecated) addins first for legacy.
2470 USHORT nIndex;
2471 bFound = ScGlobal::GetFuncCollection()->SearchFunc( cSymbol, nIndex);
2472 if (bFound)
2474 ScRawToken aToken;
2475 aToken.SetExternal( cSymbol );
2476 pRawToken = aToken.Clone();
2478 else
2479 // bLocalFirst=FALSE for (English) upper full original name
2480 // (service.function)
2481 aIntName = ScGlobal::GetAddInCollection()->FindFunction(
2482 rName, !mxSymbols->isEnglish());
2484 if (aIntName.Len())
2486 ScRawToken aToken;
2487 aToken.SetExternal( aIntName.GetBuffer() ); // international name
2488 pRawToken = aToken.Clone();
2489 bFound = TRUE;
2492 OpCode eOp;
2493 if (bFound && ((eOp = pRawToken->GetOpCode()) == ocSub || eOp == ocNegSub))
2495 bool bShouldBeNegSub =
2496 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
2497 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
2498 eLastOp == ocArrayOpen ||
2499 eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
2500 if (bShouldBeNegSub && eOp == ocSub)
2501 pRawToken->NewOpCode( ocNegSub );
2502 //! if ocNegSub had ForceArray we'd have to set it here
2503 else if (!bShouldBeNegSub && eOp == ocNegSub)
2504 pRawToken->NewOpCode( ocSub );
2506 return bFound;
2509 BOOL ScCompiler::IsOpCode2( const String& rName )
2511 BOOL bFound = FALSE;
2512 USHORT i;
2514 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
2515 bFound = rName.EqualsAscii( pInternal[ i-ocInternalBegin ] );
2517 if (bFound)
2519 ScRawToken aToken;
2520 aToken.SetOpCode( (OpCode) --i );
2521 pRawToken = aToken.Clone();
2523 return bFound;
2526 BOOL ScCompiler::IsValue( const String& rSym )
2528 double fVal;
2529 sal_uInt32 nIndex = ( mxSymbols->isEnglish() ?
2530 pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ) : 0 );
2531 // ULONG nIndex = 0;
2532 //// ULONG nIndex = pDoc->GetFormatTable()->GetStandardIndex(ScGlobal::eLnge);
2533 if (pDoc->GetFormatTable()->IsNumberFormat( rSym, nIndex, fVal ) )
2535 USHORT nType = pDoc->GetFormatTable()->GetType(nIndex);
2537 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
2538 // Dates should never be entered directly and automatically converted
2539 // to serial, because the serial would be wrong if null-date changed.
2540 // Usually it wouldn't be accepted anyway because the date separator
2541 // clashed with other separators or operators.
2542 if (nType & (NUMBERFORMAT_TIME | NUMBERFORMAT_DATE))
2543 return FALSE;
2545 if (nType == NUMBERFORMAT_LOGICAL)
2547 const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
2548 while( *p == ' ' )
2549 p++;
2550 if (*p == '(')
2551 return FALSE; // Boolean function instead.
2554 if( aFormula.GetChar(nSrcPos) == '.' )
2555 // numerical sheet name?
2556 return FALSE;
2558 if( nType == NUMBERFORMAT_TEXT )
2559 // HACK: number too big!
2560 SetError( errIllegalArgument );
2561 ScRawToken aToken;
2562 aToken.SetDouble( fVal );
2563 pRawToken = aToken.Clone();
2564 return TRUE;
2566 else
2567 return FALSE;
2570 BOOL ScCompiler::IsString()
2572 register const sal_Unicode* p = cSymbol;
2573 while ( *p )
2574 p++;
2575 xub_StrLen nLen = sal::static_int_cast<xub_StrLen>( p - cSymbol - 1 );
2576 BOOL bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"'));
2577 if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1)
2579 SetError(errStringOverflow);
2580 return FALSE;
2582 if ( bQuote )
2584 cSymbol[nLen] = '\0';
2585 ScRawToken aToken;
2586 aToken.SetString( cSymbol+1 );
2587 pRawToken = aToken.Clone();
2588 return TRUE;
2590 return FALSE;
2594 BOOL ScCompiler::IsPredetectedReference( const String& rName )
2596 // Speedup documents with lots of broken references, e.g. sheet deleted.
2597 xub_StrLen nPos = rName.SearchAscii( "#REF!");
2598 if (nPos != STRING_NOTFOUND)
2600 /* TODO: this may be enhanced by reusing scan information from
2601 * NextSymbol(), the positions of quotes and special characters found
2602 * there for $'sheet'.A1:... could be stored in a vector. We don't
2603 * fully rescan here whether found positions are within single quotes
2604 * for performance reasons. This code does not check for possible
2605 * occurrences of insane "valid" sheet names like
2606 * 'haha.#REF!1fooledyou' and will generate an error on such. */
2607 if (nPos == 0)
2608 return false; // #REF!.AB42 or #REF!42 or #REF!#REF!
2609 sal_Unicode c = rName.GetChar(nPos-1); // before #REF!
2610 if ('$' == c)
2612 if (nPos == 1)
2613 return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
2614 c = rName.GetChar(nPos-2); // before $#REF!
2616 sal_Unicode c2 = rName.GetChar(nPos+5); // after #REF!
2617 switch (c)
2619 case '.':
2620 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
2621 return false; // sheet.#REF!42 or sheet.#REF!#REF!
2622 break;
2623 case ':':
2624 if (mnPredetectedReference > 1 &&
2625 ('.' == c2 || '$' == c2 || '#' == c2 ||
2626 ('0' <= c2 && c2 <= '9')))
2627 return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
2628 break;
2629 default:
2630 if ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')) &&
2631 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
2632 return false; // AB#REF!: or AB#REF!
2635 switch (mnPredetectedReference)
2637 case 1:
2638 return IsSingleReference( rName);
2639 case 2:
2640 return IsDoubleReference( rName);
2642 return false;
2646 BOOL ScCompiler::IsDoubleReference( const String& rName )
2648 ScRange aRange( aPos, aPos );
2649 const ScAddress::Details aDetails( pConv->meConv, aPos );
2650 ScAddress::ExternalInfo aExtInfo;
2651 USHORT nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2652 if( nFlags & SCA_VALID )
2654 ScRawToken aToken;
2655 ScComplexRefData aRef;
2656 aRef.InitRange( aRange );
2657 aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2658 aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2659 aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2660 if ( !(nFlags & SCA_VALID_TAB) )
2661 aRef.Ref1.SetTabDeleted( TRUE ); // #REF!
2662 aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2663 aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 );
2664 aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 );
2665 aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 );
2666 if ( !(nFlags & SCA_VALID_TAB2) )
2667 aRef.Ref2.SetTabDeleted( TRUE ); // #REF!
2668 aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 );
2669 aRef.CalcRelFromAbs( aPos );
2670 if (aExtInfo.mbExternal)
2672 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2673 const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2674 aToken.SetExternalDoubleRef(
2675 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2677 else
2679 aToken.SetDoubleReference(aRef);
2681 pRawToken = aToken.Clone();
2684 return ( nFlags & SCA_VALID ) != 0;
2688 BOOL ScCompiler::IsSingleReference( const String& rName )
2690 ScAddress aAddr( aPos );
2691 const ScAddress::Details aDetails( pConv->meConv, aPos );
2692 ScAddress::ExternalInfo aExtInfo;
2693 USHORT nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2694 // Something must be valid in order to recognize Sheet1.blah or blah.a1
2695 // as a (wrong) reference.
2696 if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) )
2698 ScRawToken aToken;
2699 ScSingleRefData aRef;
2700 aRef.InitAddress( aAddr );
2701 aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2702 aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2703 aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2704 aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2705 // the reference is really invalid
2706 if( !( nFlags & SCA_VALID ) )
2708 if( !( nFlags & SCA_VALID_COL ) )
2709 aRef.nCol = MAXCOL+1;
2710 if( !( nFlags & SCA_VALID_ROW ) )
2711 aRef.nRow = MAXROW+1;
2712 if( !( nFlags & SCA_VALID_TAB ) )
2713 aRef.nTab = MAXTAB+3;
2714 nFlags |= SCA_VALID;
2716 aRef.CalcRelFromAbs( aPos );
2718 if (aExtInfo.mbExternal)
2720 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2721 const String* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2722 aToken.SetExternalSingleRef(
2723 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2725 else
2726 aToken.SetSingleReference(aRef);
2727 pRawToken = aToken.Clone();
2730 return ( nFlags & SCA_VALID ) != 0;
2734 BOOL ScCompiler::IsReference( const String& rName )
2736 // Has to be called before IsValue
2737 sal_Unicode ch1 = rName.GetChar(0);
2738 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' :
2739 ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0) );
2740 if ( ch1 == cDecSep )
2741 return FALSE;
2742 // Who was that imbecile introducing '.' as the sheet name separator!?!
2743 if ( CharClass::isAsciiNumeric( ch1 ) )
2745 // Numerical sheet name is valid.
2746 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
2747 // Don't create a #REF! of values. But also do not bail out on
2748 // something like 3:3, meaning entire row 3.
2751 const xub_StrLen nPos = ScGlobal::FindUnquoted( rName, '.');
2752 if ( nPos == STRING_NOTFOUND )
2754 if (ScGlobal::FindUnquoted( rName, ':') != STRING_NOTFOUND)
2755 break; // may be 3:3, continue as usual
2756 return FALSE;
2758 sal_Unicode const * const pTabSep = rName.GetBuffer() + nPos;
2759 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
2760 if ( !(ch2 == '$' || CharClass::isAsciiAlpha( ch2 )) )
2761 return FALSE;
2762 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
2763 && (GetCharTableFlags( pTabSep[2] ) & SC_COMPILER_C_VALUE_EXP) )
2764 { // #91053#
2765 // If it is an 1.E2 expression check if "1" is an existent sheet
2766 // name. If so, a desired value 1.E2 would have to be entered as
2767 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
2768 // require numerical sheet names always being entered quoted, which
2769 // is not desirable (too many 1999, 2000, 2001 sheets in use).
2770 // Furthermore, XML files created with versions prior to SRC640e
2771 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
2772 // and would produce wrong formulas if the conditions here are met.
2773 // If you can live with these restrictions you may remove the
2774 // check and return an unconditional FALSE.
2775 String aTabName( rName.Copy( 0, nPos ) );
2776 SCTAB nTab;
2777 if ( !pDoc->GetTable( aTabName, nTab ) )
2778 return FALSE;
2779 // If sheet "1" exists and the expression is 1.E+2 continue as
2780 // usual, the ScRange/ScAddress parser will take care of it.
2782 } while(0);
2785 if (IsSingleReference( rName))
2786 return true;
2788 // Though the range operator is handled explicitly, when encountering
2789 // something like Sheet1.A:A we will have to treat it as one entity if it
2790 // doesn't pass as single cell reference.
2791 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
2793 if (IsDoubleReference( rName))
2794 return true;
2795 // Now try with a symbol up to the range operator, rewind source
2796 // position.
2797 sal_Int32 nLen = mnRangeOpPosInSymbol;
2798 while (cSymbol[++nLen])
2800 cSymbol[mnRangeOpPosInSymbol] = 0;
2801 nSrcPos -= static_cast<xub_StrLen>(nLen - mnRangeOpPosInSymbol);
2802 mnRangeOpPosInSymbol = -1;
2803 mbRewind = true;
2804 return true; // end all checks
2806 return false;
2809 BOOL ScCompiler::IsMacro( const String& rName )
2811 StarBASIC* pObj = 0;
2812 SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
2814 SfxApplication* pSfxApp = SFX_APP();
2815 pSfxApp->EnterBasicCall(); // initialize document's BASIC
2817 if( pDocSh )//XXX
2818 pObj = pDocSh->GetBasic();
2819 else
2820 pObj = pSfxApp->GetBasic();
2822 SbxMethod* pMeth = (SbxMethod*) pObj->Find( rName, SbxCLASS_METHOD );
2823 if( !pMeth )
2825 pSfxApp->LeaveBasicCall();
2826 return FALSE;
2828 // It really should be a BASIC function!
2829 if( pMeth->GetType() == SbxVOID
2830 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
2831 || !pMeth->ISA(SbMethod) )
2833 pSfxApp->LeaveBasicCall();
2834 return FALSE;
2836 ScRawToken aToken;
2837 aToken.SetExternal( rName.GetBuffer() );
2838 aToken.eOp = ocMacro;
2839 pRawToken = aToken.Clone();
2840 pSfxApp->LeaveBasicCall();
2841 return TRUE;
2844 BOOL ScCompiler::IsNamedRange( const String& rUpperName )
2846 // IsNamedRange is called only from NextNewToken, with an upper-case string
2848 USHORT n;
2849 ScRangeName* pRangeName = pDoc->GetRangeName();
2850 if (pRangeName->SearchNameUpper( rUpperName, n ) )
2852 ScRangeData* pData = (*pRangeName)[n];
2853 ScRawToken aToken;
2854 aToken.SetName( pData->GetIndex() );
2855 pRawToken = aToken.Clone();
2856 return TRUE;
2858 else
2859 return FALSE;
2862 bool ScCompiler::IsExternalNamedRange( const String& rSymbol )
2864 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
2865 * correctly parses external named references in OOo, as required per RFE
2866 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
2867 * spec first. Until then don't pretend to support external names that
2868 * wouldn't survive a save and reload cycle, return false instead. */
2870 #if 1
2871 if (!pConv)
2872 return false;
2874 String aFile, aName;
2875 if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
2876 return false;
2878 ScRawToken aToken;
2879 if (aFile.Len() > MAXSTRLEN || aName.Len() > MAXSTRLEN)
2880 return false;
2882 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2883 pRefMgr->convertToAbsName(aFile);
2884 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
2885 if (!pRefMgr->getRangeNameTokens(nFileId, aName).get())
2886 // range name doesn't exist in the source document.
2887 return false;
2889 const String* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
2890 aToken.SetExternalName(nFileId, pRealName ? *pRealName : aName);
2891 pRawToken = aToken.Clone();
2892 return true;
2893 #else
2894 (void)rSymbol;
2895 return false;
2896 #endif
2899 BOOL ScCompiler::IsDBRange( const String& rName )
2901 USHORT n;
2902 ScDBCollection* pDBColl = pDoc->GetDBCollection();
2903 if (pDBColl->SearchName( rName, n ) )
2905 ScDBData* pData = (*pDBColl)[n];
2906 ScRawToken aToken;
2907 aToken.SetName( pData->GetIndex() );
2908 aToken.eOp = ocDBArea;
2909 pRawToken = aToken.Clone();
2910 return TRUE;
2912 else
2913 return FALSE;
2916 BOOL ScCompiler::IsColRowName( const String& rName )
2918 BOOL bInList = FALSE;
2919 BOOL bFound = FALSE;
2920 ScSingleRefData aRef;
2921 String aName( rName );
2922 DeQuote( aName );
2923 SCTAB nThisTab = aPos.Tab();
2924 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
2925 { // #50300# first check ranges on this sheet, in case of duplicated names
2926 for ( short jRow=0; jRow<2 && !bInList; jRow++ )
2928 ScRangePairList* pRL;
2929 if ( !jRow )
2930 pRL = pDoc->GetColNameRanges();
2931 else
2932 pRL = pDoc->GetRowNameRanges();
2933 for ( ScRangePair* pR = pRL->First(); pR && !bInList; pR = pRL->Next() )
2935 const ScRange& rNameRange = pR->GetRange(0);
2936 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
2937 nThisTab <= rNameRange.aEnd.Tab()) )
2938 continue; // for
2939 ScCellIterator aIter( pDoc, rNameRange );
2940 for ( ScBaseCell* pCell = aIter.GetFirst(); pCell && !bInList;
2941 pCell = aIter.GetNext() )
2943 // Don't crash if cell (via CompileNameFormula) encounters
2944 // a formula cell without code and
2945 // HasStringData/Interpret/Compile is executed and all that
2946 // recursive..
2947 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
2948 CellType eType = pCell->GetCellType();
2949 BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ?
2950 ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
2951 && ((ScFormulaCell*)pCell)->aPos != aPos // noIter
2952 : TRUE ) );
2953 if ( bOk && pCell->HasStringData() )
2955 String aStr;
2956 switch ( eType )
2958 case CELLTYPE_STRING:
2959 ((ScStringCell*)pCell)->GetString( aStr );
2960 break;
2961 case CELLTYPE_FORMULA:
2962 ((ScFormulaCell*)pCell)->GetString( aStr );
2963 break;
2964 case CELLTYPE_EDIT:
2965 ((ScEditCell*)pCell)->GetString( aStr );
2966 break;
2967 case CELLTYPE_NONE:
2968 case CELLTYPE_VALUE:
2969 case CELLTYPE_NOTE:
2970 case CELLTYPE_SYMBOLS:
2971 #if DBG_UTIL
2972 case CELLTYPE_DESTROYED:
2973 #endif
2974 ; // nothing, prevent compiler warning
2975 break;
2977 if ( ScGlobal::pTransliteration->isEqual( aStr, aName ) )
2979 aRef.InitFlags();
2980 aRef.nCol = aIter.GetCol();
2981 aRef.nRow = aIter.GetRow();
2982 aRef.nTab = aIter.GetTab();
2983 if ( !jRow )
2984 aRef.SetColRel( TRUE ); // ColName
2985 else
2986 aRef.SetRowRel( TRUE ); // RowName
2987 aRef.CalcRelFromAbs( aPos );
2988 bInList = bFound = TRUE;
2995 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
2996 { // search in current sheet
2997 long nDistance = 0, nMax = 0;
2998 long nMyCol = (long) aPos.Col();
2999 long nMyRow = (long) aPos.Row();
3000 BOOL bTwo = FALSE;
3001 ScAddress aOne( 0, 0, aPos.Tab() );
3002 ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
3004 ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
3005 if ( pNameCache )
3007 // #b6355215# use GetNameOccurences to collect all positions of aName on the sheet
3008 // (only once), similar to the outer part of the loop in the "else" branch.
3010 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurences( aName, aPos.Tab() );
3012 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3013 // The order of addresses in the vector is the same as from ScCellIterator.
3015 ScAutoNameAddresses::const_iterator aEnd(rAddresses.end());
3016 for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter )
3018 ScAddress aAddress( *aAdrIter ); // cell address with an equal string
3020 if ( bFound )
3021 { // stop if everything else is further away
3022 if ( nMax < (long)aAddress.Col() )
3023 break; // aIter
3025 if ( aAddress != aPos )
3027 // same treatment as in isEqual case below
3029 SCCOL nCol = aAddress.Col();
3030 SCROW nRow = aAddress.Row();
3031 long nC = nMyCol - nCol;
3032 long nR = nMyRow - nRow;
3033 if ( bFound )
3035 long nD = nC * nC + nR * nR;
3036 if ( nD < nDistance )
3038 if ( nC < 0 || nR < 0 )
3039 { // right or below
3040 bTwo = TRUE;
3041 aTwo.Set( nCol, nRow, aAddress.Tab() );
3042 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3043 nDistance = nD;
3045 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3047 // upper left, only if not further up than the
3048 // current entry and nMyRow is below (CellIter
3049 // runs column-wise)
3050 bTwo = FALSE;
3051 aOne.Set( nCol, nRow, aAddress.Tab() );
3052 nMax = Max( nMyCol + nC, nMyRow + nR );
3053 nDistance = nD;
3057 else
3059 aOne.Set( nCol, nRow, aAddress.Tab() );
3060 nDistance = nC * nC + nR * nR;
3061 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3063 bFound = TRUE;
3067 else
3069 ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
3070 for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() )
3072 if ( bFound )
3073 { // stop if everything else is further away
3074 if ( nMax < (long)aIter.GetCol() )
3075 break; // aIter
3077 CellType eType = pCell->GetCellType();
3078 BOOL bOk = sal::static_int_cast<BOOL>( (eType == CELLTYPE_FORMULA ?
3079 ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen() > 0
3080 && ((ScFormulaCell*)pCell)->aPos != aPos // noIter
3081 : TRUE ) );
3082 if ( bOk && pCell->HasStringData() )
3084 String aStr;
3085 switch ( eType )
3087 case CELLTYPE_STRING:
3088 ((ScStringCell*)pCell)->GetString( aStr );
3089 break;
3090 case CELLTYPE_FORMULA:
3091 ((ScFormulaCell*)pCell)->GetString( aStr );
3092 break;
3093 case CELLTYPE_EDIT:
3094 ((ScEditCell*)pCell)->GetString( aStr );
3095 break;
3096 case CELLTYPE_NONE:
3097 case CELLTYPE_VALUE:
3098 case CELLTYPE_NOTE:
3099 case CELLTYPE_SYMBOLS:
3100 #if DBG_UTIL
3101 case CELLTYPE_DESTROYED:
3102 #endif
3103 ; // nothing, prevent compiler warning
3104 break;
3106 if ( ScGlobal::pTransliteration->isEqual( aStr, aName ) )
3108 SCCOL nCol = aIter.GetCol();
3109 SCROW nRow = aIter.GetRow();
3110 long nC = nMyCol - nCol;
3111 long nR = nMyRow - nRow;
3112 if ( bFound )
3114 long nD = nC * nC + nR * nR;
3115 if ( nD < nDistance )
3117 if ( nC < 0 || nR < 0 )
3118 { // right or below
3119 bTwo = TRUE;
3120 aTwo.Set( nCol, nRow, aIter.GetTab() );
3121 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3122 nDistance = nD;
3124 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3126 // upper left, only if not further up than the
3127 // current entry and nMyRow is below (CellIter
3128 // runs column-wise)
3129 bTwo = FALSE;
3130 aOne.Set( nCol, nRow, aIter.GetTab() );
3131 nMax = Max( nMyCol + nC, nMyRow + nR );
3132 nDistance = nD;
3136 else
3138 aOne.Set( nCol, nRow, aIter.GetTab() );
3139 nDistance = nC * nC + nR * nR;
3140 nMax = Max( nMyCol + Abs( nC ), nMyRow + Abs( nR ) );
3142 bFound = TRUE;
3148 if ( bFound )
3150 ScAddress aAdr;
3151 if ( bTwo )
3153 if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() )
3154 aAdr = aOne; // upper left takes precedence
3155 else
3157 if ( nMyCol < (long)aOne.Col() )
3158 { // two to the right
3159 if ( nMyRow >= (long)aTwo.Row() )
3160 aAdr = aTwo; // directly right
3161 else
3162 aAdr = aOne;
3164 else
3165 { // two below or below and right, take the nearest
3166 long nC1 = nMyCol - aOne.Col();
3167 long nR1 = nMyRow - aOne.Row();
3168 long nC2 = nMyCol - aTwo.Col();
3169 long nR2 = nMyRow - aTwo.Row();
3170 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3171 aAdr = aOne;
3172 else
3173 aAdr = aTwo;
3177 else
3178 aAdr = aOne;
3179 aRef.InitAddress( aAdr );
3180 if ( (aRef.nRow != MAXROW && pDoc->HasStringData(
3181 aRef.nCol, aRef.nRow + 1, aRef.nTab ))
3182 || (aRef.nRow != 0 && pDoc->HasStringData(
3183 aRef.nCol, aRef.nRow - 1, aRef.nTab )) )
3184 aRef.SetRowRel( TRUE ); // RowName
3185 else
3186 aRef.SetColRel( TRUE ); // ColName
3187 aRef.CalcRelFromAbs( aPos );
3190 if ( bFound )
3192 ScRawToken aToken;
3193 aToken.SetSingleReference( aRef );
3194 aToken.eOp = ocColRowName;
3195 pRawToken = aToken.Clone();
3196 return TRUE;
3198 else
3199 return FALSE;
3202 BOOL ScCompiler::IsBoolean( const String& rName )
3204 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) );
3205 if( iLook != mxSymbols->getHashMap()->end() &&
3206 ((*iLook).second == ocTrue ||
3207 (*iLook).second == ocFalse) )
3209 ScRawToken aToken;
3210 aToken.SetOpCode( (*iLook).second );
3211 pRawToken = aToken.Clone();
3212 return TRUE;
3214 else
3215 return FALSE;
3218 //---------------------------------------------------------------------------
3220 void ScCompiler::AutoCorrectParsedSymbol()
3222 xub_StrLen nPos = aCorrectedSymbol.Len();
3223 if ( nPos )
3225 nPos--;
3226 const sal_Unicode cQuote = '\"';
3227 const sal_Unicode cx = 'x';
3228 const sal_Unicode cX = 'X';
3229 sal_Unicode c1 = aCorrectedSymbol.GetChar( 0 );
3230 sal_Unicode c2 = aCorrectedSymbol.GetChar( nPos );
3231 if ( c1 == cQuote && c2 != cQuote )
3232 { // "...
3233 // What's not a word doesn't belong to it.
3234 // Don't be pedantic: c < 128 should be sufficient here.
3235 while ( nPos && ((aCorrectedSymbol.GetChar(nPos) < 128) &&
3236 ((GetCharTableFlags( aCorrectedSymbol.GetChar(nPos) ) &
3237 (SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) )
3238 nPos--;
3239 if ( nPos == MAXSTRLEN - 2 )
3240 aCorrectedSymbol.SetChar( nPos, cQuote ); // '"' the 255th character
3241 else
3242 aCorrectedSymbol.Insert( cQuote, nPos + 1 );
3243 bCorrected = TRUE;
3245 else if ( c1 != cQuote && c2 == cQuote )
3246 { // ..."
3247 aCorrectedSymbol.Insert( cQuote, 0 );
3248 bCorrected = TRUE;
3250 else if ( nPos == 0 && (c1 == cx || c1 == cX) )
3251 { // x => *
3252 aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
3253 bCorrected = TRUE;
3255 else if ( (GetCharTableFlags( c1 ) & SC_COMPILER_C_CHAR_VALUE)
3256 && (GetCharTableFlags( c2 ) & SC_COMPILER_C_CHAR_VALUE) )
3258 xub_StrLen nXcount;
3259 if ( (nXcount = aCorrectedSymbol.GetTokenCount( cx )) > 1 )
3260 { // x => *
3261 xub_StrLen nIndex = 0;
3262 sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
3263 while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
3264 cx, c, nIndex )) != STRING_NOTFOUND )
3265 nIndex++;
3266 bCorrected = TRUE;
3268 if ( (nXcount = aCorrectedSymbol.GetTokenCount( cX )) > 1 )
3269 { // X => *
3270 xub_StrLen nIndex = 0;
3271 sal_Unicode c = mxSymbols->getSymbol(ocMul).GetChar(0);
3272 while ( (nIndex = aCorrectedSymbol.SearchAndReplace(
3273 cX, c, nIndex )) != STRING_NOTFOUND )
3274 nIndex++;
3275 bCorrected = TRUE;
3278 else
3280 String aSymbol( aCorrectedSymbol );
3281 String aDoc;
3282 xub_StrLen nPosition;
3283 if ( aSymbol.GetChar(0) == '\''
3284 && ((nPosition = aSymbol.SearchAscii( "'#" )) != STRING_NOTFOUND) )
3285 { // Split off 'Doc'#, may be d:\... or whatever
3286 aDoc = aSymbol.Copy( 0, nPosition + 2 );
3287 aSymbol.Erase( 0, nPosition + 2 );
3289 xub_StrLen nRefs = aSymbol.GetTokenCount( ':' );
3290 BOOL bColons;
3291 if ( nRefs > 2 )
3292 { // duplicated or too many ':'? B:2::C10 => B2:C10
3293 bColons = TRUE;
3294 xub_StrLen nIndex = 0;
3295 String aTmp1( aSymbol.GetToken( 0, ':', nIndex ) );
3296 xub_StrLen nLen1 = aTmp1.Len();
3297 String aSym, aTmp2;
3298 BOOL bLastAlp, bNextNum;
3299 bLastAlp = bNextNum = TRUE;
3300 xub_StrLen nStrip = 0;
3301 xub_StrLen nCount = nRefs;
3302 for ( xub_StrLen j=1; j<nCount; j++ )
3304 aTmp2 = aSymbol.GetToken( 0, ':', nIndex );
3305 xub_StrLen nLen2 = aTmp2.Len();
3306 if ( nLen1 || nLen2 )
3308 if ( nLen1 )
3310 aSym += aTmp1;
3311 bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
3313 if ( nLen2 )
3315 bNextNum = CharClass::isAsciiNumeric( aTmp2 );
3316 if ( bLastAlp == bNextNum && nStrip < 1 )
3318 // Must be alternating number/string, only
3319 // strip within a reference.
3320 nRefs--;
3321 nStrip++;
3323 else
3325 xub_StrLen nSymLen = aSym.Len();
3326 if ( nSymLen
3327 && (aSym.GetChar( nSymLen - 1 ) != ':') )
3328 aSym += ':';
3329 nStrip = 0;
3331 bLastAlp = !bNextNum;
3333 else
3334 { // ::
3335 nRefs--;
3336 if ( nLen1 )
3337 { // B10::C10 ? append ':' on next round
3338 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
3339 nStrip++;
3341 bNextNum = !bLastAlp;
3343 aTmp1 = aTmp2;
3344 nLen1 = nLen2;
3346 else
3347 nRefs--;
3349 aSymbol = aSym;
3350 aSymbol += aTmp1;
3352 else
3353 bColons = FALSE;
3354 if ( nRefs && nRefs <= 2 )
3355 { // reference twisted? 4A => A4 etc.
3356 String aTab[2], aRef[2];
3357 const ScAddress::Details aDetails( pConv->meConv, aPos );
3358 if ( nRefs == 2 )
3360 aRef[0] = aSymbol.GetToken( 0, ':' );
3361 aRef[1] = aSymbol.GetToken( 1, ':' );
3363 else
3364 aRef[0] = aSymbol;
3366 BOOL bChanged = FALSE;
3367 BOOL bOk = TRUE;
3368 USHORT nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW;
3369 for ( int j=0; j<nRefs; j++ )
3371 xub_StrLen nTmp = 0;
3372 xub_StrLen nDotPos = STRING_NOTFOUND;
3373 while ( (nTmp = aRef[j].Search( '.', nTmp )) != STRING_NOTFOUND )
3374 nDotPos = nTmp++; // the last one counts
3375 if ( nDotPos != STRING_NOTFOUND )
3377 aTab[j] = aRef[j].Copy( 0, nDotPos + 1 ); // with '.'
3378 aRef[j].Erase( 0, nDotPos + 1 );
3380 String aOld( aRef[j] );
3381 String aStr2;
3382 const sal_Unicode* p = aRef[j].GetBuffer();
3383 while ( *p && CharClass::isAsciiNumeric( *p ) )
3384 aStr2 += *p++;
3385 aRef[j] = String( p );
3386 aRef[j] += aStr2;
3387 if ( bColons || aRef[j] != aOld )
3389 bChanged = TRUE;
3390 ScAddress aAdr;
3391 bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
3394 if ( bChanged && bOk )
3396 aCorrectedSymbol = aDoc;
3397 aCorrectedSymbol += aTab[0];
3398 aCorrectedSymbol += aRef[0];
3399 if ( nRefs == 2 )
3401 aCorrectedSymbol += ':';
3402 aCorrectedSymbol += aTab[1];
3403 aCorrectedSymbol += aRef[1];
3405 bCorrected = TRUE;
3412 inline bool lcl_UpperAsciiOrI18n( String& rUpper, const String& rOrg, FormulaGrammar::Grammar eGrammar )
3414 if (FormulaGrammar::isODFF( eGrammar ))
3416 // ODFF has a defined set of English function names, avoid i18n
3417 // overhead.
3418 rUpper = rOrg;
3419 rUpper.ToUpperAscii();
3420 return true;
3422 else
3424 rUpper = ScGlobal::pCharClass->upper( rOrg );
3425 return false;
3429 BOOL ScCompiler::NextNewToken( bool bInArray )
3431 bool bAllowBooleans = bInArray;
3432 xub_StrLen nSpaces = NextSymbol(bInArray);
3434 #if 0
3435 fprintf( stderr, "NextNewToken '%s' (spaces = %d)\n",
3436 rtl::OUStringToOString( cSymbol, RTL_TEXTENCODING_UTF8 ).getStr(), nSpaces );
3437 #endif
3439 if (!cSymbol[0])
3440 return false;
3442 if( nSpaces )
3444 ScRawToken aToken;
3445 aToken.SetOpCode( ocSpaces );
3446 aToken.sbyte.cByte = (BYTE) ( nSpaces > 255 ? 255 : nSpaces );
3447 if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
3449 SetError(errCodeOverflow);
3450 return false;
3454 // Short cut for references when reading ODF to speedup things.
3455 if (mnPredetectedReference)
3457 String aStr( cSymbol);
3458 if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
3460 /* TODO: it would be nice to generate a #REF! error here, which
3461 * would need an ocBad token with additional error value.
3462 * FormulaErrorToken wouldn't do because we want to preserve the
3463 * original string containing partial valid address
3464 * information. */
3465 ScRawToken aToken;
3466 aToken.SetString( aStr.GetBuffer() );
3467 aToken.NewOpCode( ocBad );
3468 pRawToken = aToken.Clone();
3470 return true;
3473 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
3474 !bAutoCorrect )
3475 { // #101100# special case to speed up broken [$]#REF documents
3476 /* FIXME: ISERROR(#REF!) would be valid and TRUE and the formula to
3477 * be processed as usual. That would need some special treatment,
3478 * also in NextSymbol() because of possible combinations of
3479 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
3480 * handled by IsPredetectedReference(), this case here remains for
3481 * manual/API input. */
3482 String aBad( aFormula.Copy( nSrcPos-1 ) );
3483 eLastOp = pArr->AddBad( aBad )->GetOpCode();
3484 return false;
3487 if( IsString() )
3488 return true;
3490 bool bMayBeFuncName;
3491 bool bAsciiNonAlnum; // operators, separators, ...
3492 if ( cSymbol[0] < 128 )
3494 bMayBeFuncName = CharClass::isAsciiAlpha( cSymbol[0] );
3495 bAsciiNonAlnum = !bMayBeFuncName && !CharClass::isAsciiDigit( cSymbol[0] );
3497 else
3499 String aTmpStr( cSymbol[0] );
3500 bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
3501 bAsciiNonAlnum = false;
3503 if ( bMayBeFuncName )
3505 // a function name must be followed by a parenthesis
3506 const sal_Unicode* p = aFormula.GetBuffer() + nSrcPos;
3507 while( *p == ' ' )
3508 p++;
3509 bMayBeFuncName = ( *p == '(' );
3512 #if 0
3513 fprintf( stderr, "Token '%s'\n",
3514 rtl::OUStringToOString( aUpper, RTL_TEXTENCODING_UTF8 ).getStr() );
3515 #endif
3517 // #42016# Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
3518 // IsReference().
3520 String aUpper;
3524 mbRewind = false;
3525 const String aOrg( cSymbol );
3527 if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray ))
3528 return true;
3530 aUpper.Erase();
3531 bool bAsciiUpper = false;
3532 if (bMayBeFuncName)
3534 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3535 if (IsOpCode( aUpper, bInArray ))
3536 return true;
3539 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
3540 // referred => IsReference() before IsValue().
3541 // Preserve case of file names in external references.
3542 if (IsReference( aOrg ))
3544 if (mbRewind) // Range operator, but no direct reference.
3545 continue; // do; up to range operator.
3546 return true;
3549 if (!aUpper.Len())
3550 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3552 // IsBoolean() before IsValue() to catch inline bools without the kludge
3553 // for inline arrays.
3554 if (bAllowBooleans && IsBoolean( aUpper ))
3555 return true;
3557 if (IsValue( aUpper ))
3558 return true;
3560 // User defined names and such do need i18n upper also in ODF.
3561 if (bAsciiUpper)
3562 aUpper = ScGlobal::pCharClass->upper( aOrg );
3564 if (IsNamedRange( aUpper ))
3565 return true;
3566 // Preserve case of file names in external references.
3567 if (IsExternalNamedRange( aOrg ))
3568 return true;
3569 if (IsDBRange( aUpper ))
3570 return true;
3571 if (IsColRowName( aUpper ))
3572 return true;
3573 if (bMayBeFuncName && IsMacro( aUpper ))
3574 return true;
3575 if (bMayBeFuncName && IsOpCode2( aUpper ))
3576 return true;
3578 } while (mbRewind);
3580 if ( mbExtendedErrorDetection )
3582 // set an error and end compilation
3583 SetError( errNoName );
3584 return false;
3587 // Provide single token information and continue. Do not set an error, that
3588 // would prematurely end compilation. Simple unknown names are handled by
3589 // the interpreter.
3590 ScGlobal::pCharClass->toLower( aUpper );
3591 ScRawToken aToken;
3592 aToken.SetString( aUpper.GetBuffer() );
3593 aToken.NewOpCode( ocBad );
3594 pRawToken = aToken.Clone();
3595 if ( bAutoCorrect )
3596 AutoCorrectParsedSymbol();
3597 return true;
3600 void ScCompiler::CreateStringFromXMLTokenArray( String& rFormula, String& rFormulaNmsp )
3602 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
3603 USHORT nExpectedCount = bExternal ? 2 : 1;
3604 DBG_ASSERT( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
3605 if( pArr->GetLen() == nExpectedCount )
3607 FormulaToken** ppTokens = pArr->GetArray();
3608 // string tokens expected, GetString() will assert if token type is wrong
3609 rFormula = ppTokens[ 0 ]->GetString();
3610 if( bExternal )
3611 rFormulaNmsp = ppTokens[ 1 ]->GetString();
3615 ScTokenArray* ScCompiler::CompileString( const String& rFormula )
3617 #if 0
3618 fprintf( stderr, "CompileString '%s'\n",
3619 rtl::OUStringToOString( rFormula, RTL_TEXTENCODING_UTF8 ).getStr() );
3620 #endif
3622 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
3623 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
3624 SetGrammar( FormulaGrammar::GRAM_PODF );
3626 ScTokenArray aArr;
3627 pArr = &aArr;
3628 aFormula = rFormula;
3630 aFormula.EraseLeadingChars();
3631 aFormula.EraseTrailingChars();
3632 nSrcPos = 0;
3633 bCorrected = FALSE;
3634 if ( bAutoCorrect )
3636 aCorrectedFormula.Erase();
3637 aCorrectedSymbol.Erase();
3639 BYTE nForced = 0; // ==formula forces recalc even if cell is not visible
3640 if( aFormula.GetChar(nSrcPos) == '=' )
3642 nSrcPos++;
3643 nForced++;
3644 if ( bAutoCorrect )
3645 aCorrectedFormula += '=';
3647 if( aFormula.GetChar(nSrcPos) == '=' )
3649 nSrcPos++;
3650 nForced++;
3651 if ( bAutoCorrect )
3652 aCorrectedFormula += '=';
3654 struct FunctionStack
3656 OpCode eOp;
3657 short nPar;
3659 // FunctionStack only used if PODF!
3660 bool bPODF = FormulaGrammar::isPODF( meGrammar);
3661 const size_t nAlloc = 512;
3662 FunctionStack aFuncs[ nAlloc ];
3663 FunctionStack* pFunctionStack = (bPODF && rFormula.Len() > nAlloc ?
3664 new FunctionStack[ rFormula.Len() ] : &aFuncs[0]);
3665 pFunctionStack[0].eOp = ocNone;
3666 pFunctionStack[0].nPar = 0;
3667 size_t nFunction = 0;
3668 short nBrackets = 0;
3669 bool bInArray = false;
3670 eLastOp = ocOpen;
3671 while( NextNewToken( bInArray ) )
3673 const OpCode eOp = pRawToken->GetOpCode();
3674 switch (eOp)
3676 case ocOpen:
3678 ++nBrackets;
3679 if (bPODF)
3681 ++nFunction;
3682 pFunctionStack[ nFunction ].eOp = eLastOp;
3683 pFunctionStack[ nFunction ].nPar = 0;
3686 break;
3687 case ocClose:
3689 if( !nBrackets )
3691 SetError( errPairExpected );
3692 if ( bAutoCorrect )
3694 bCorrected = TRUE;
3695 aCorrectedSymbol.Erase();
3698 else
3699 nBrackets--;
3700 if (bPODF && nFunction)
3701 --nFunction;
3703 break;
3704 case ocSep:
3706 if (bPODF)
3707 ++pFunctionStack[ nFunction ].nPar;
3709 break;
3710 case ocArrayOpen:
3712 if( bInArray )
3713 SetError( errNestedArray );
3714 else
3715 bInArray = true;
3716 // Don't count following column separator as parameter separator.
3717 if (bPODF)
3719 ++nFunction;
3720 pFunctionStack[ nFunction ].eOp = eOp;
3721 pFunctionStack[ nFunction ].nPar = 0;
3724 break;
3725 case ocArrayClose:
3727 if( bInArray )
3729 bInArray = false;
3731 else
3733 SetError( errPairExpected );
3734 if ( bAutoCorrect )
3736 bCorrected = TRUE;
3737 aCorrectedSymbol.Erase();
3740 if (bPODF && nFunction)
3741 --nFunction;
3743 default:
3744 break;
3746 if( (eLastOp == ocSep ||
3747 eLastOp == ocArrayRowSep ||
3748 eLastOp == ocArrayColSep ||
3749 eLastOp == ocArrayOpen) &&
3750 (eOp == ocSep ||
3751 eOp == ocArrayRowSep ||
3752 eOp == ocArrayColSep ||
3753 eOp == ocArrayClose) )
3755 // FIXME: should we check for known functions with optional empty
3756 // args so the correction dialog can do better?
3757 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
3759 SetError(errCodeOverflow); break;
3762 if (bPODF)
3764 /* TODO: for now this is the only PODF adapter. If there were more,
3765 * factor this out. */
3766 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
3767 if (eOp == ocSep &&
3768 pFunctionStack[ nFunction ].eOp == ocAddress &&
3769 pFunctionStack[ nFunction ].nPar == 3)
3771 if (!static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep,ocSep)) ||
3772 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
3774 SetError(errCodeOverflow); break;
3776 ++pFunctionStack[ nFunction ].nPar;
3779 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( pRawToken->CreateToken());
3780 if (!pNewToken)
3782 SetError(errCodeOverflow); break;
3784 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush &&
3785 pNewToken->GetType() == svSingleRef)
3786 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
3787 eLastOp = pRawToken->GetOpCode();
3788 if ( bAutoCorrect )
3789 aCorrectedFormula += aCorrectedSymbol;
3791 if ( mbCloseBrackets )
3793 if( bInArray )
3795 FormulaByteToken aToken( ocArrayClose );
3796 if( !pArr->AddToken( aToken ) )
3798 SetError(errCodeOverflow);
3800 else if ( bAutoCorrect )
3801 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
3804 FormulaByteToken aToken( ocClose );
3805 while( nBrackets-- )
3807 if( !pArr->AddToken( aToken ) )
3809 SetError(errCodeOverflow); break;
3811 if ( bAutoCorrect )
3812 aCorrectedFormula += mxSymbols->getSymbol(ocClose);
3815 if ( nForced >= 2 )
3816 pArr->SetRecalcModeForced();
3818 if (pFunctionStack != &aFuncs[0])
3819 delete [] pFunctionStack;
3821 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3822 ScTokenArray* pNew = new ScTokenArray( aArr );
3823 pArr = pNew;
3824 return pNew;
3828 ScTokenArray* ScCompiler::CompileString( const String& rFormula, const String& rFormulaNmsp )
3830 DBG_ASSERT( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || (rFormulaNmsp.Len() == 0),
3831 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
3832 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
3834 ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
3835 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
3836 table::CellAddress aReferencePos;
3837 ScUnoConversion::FillApiAddress( aReferencePos, aPos );
3838 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
3839 ScTokenArray aTokenArray;
3840 if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
3842 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3843 ScTokenArray* pNew = new ScTokenArray( aTokenArray );
3844 pArr = pNew;
3845 return pNew;
3848 catch( uno::Exception& )
3851 // no success - fallback to some internal grammar and hope the best
3852 return CompileString( rFormula );
3856 BOOL ScCompiler::HandleRange()
3858 ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( pToken->GetIndex() );
3859 if (pRangeData)
3861 USHORT nErr = pRangeData->GetErrCode();
3862 if( nErr )
3863 SetError( errNoName );
3864 else if ( !bCompileForFAP )
3866 ScTokenArray* pNew;
3867 // #35168# put named formula into parentheses.
3868 // #37680# But only if there aren't any yet, parenthetical
3869 // ocSep doesn't work, e.g. SUM((...;...))
3870 // or if not directly between ocSep/parenthesis,
3871 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
3872 // in short: if it isn't a self-contained expression.
3873 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
3874 FormulaToken* p2 = pArr->PeekNextNoSpaces();
3875 OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) );
3876 OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) );
3877 BOOL bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
3878 BOOL bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
3879 BOOL bAddPair = !(bBorder1 && bBorder2);
3880 if ( bAddPair )
3882 pNew = new ScTokenArray();
3883 pNew->AddOpCode( ocClose );
3884 PushTokenArray( pNew, TRUE );
3885 pNew->Reset();
3887 pNew = pRangeData->GetCode()->Clone();
3888 PushTokenArray( pNew, TRUE );
3889 if( pRangeData->HasReferences() )
3891 SetRelNameReference();
3892 MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
3894 pNew->Reset();
3895 if ( bAddPair )
3897 pNew = new ScTokenArray();
3898 pNew->AddOpCode( ocOpen );
3899 PushTokenArray( pNew, TRUE );
3900 pNew->Reset();
3902 return GetToken();
3905 else
3906 SetError(errNoName);
3907 return TRUE;
3909 // -----------------------------------------------------------------------------
3910 BOOL ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
3912 // Handle external range names.
3913 switch (_aToken.GetType())
3915 case svExternalSingleRef:
3916 case svExternalDoubleRef:
3917 pArr->IncrementRefs();
3918 break;
3919 case svExternalName:
3921 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3922 const String* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
3923 if (!pFile)
3925 SetError(errNoName);
3926 return true;
3929 const String& rName = _aToken.GetString();
3930 ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
3931 _aToken.GetIndex(), rName, &aPos);
3933 if (!xNew)
3935 SetError(errNoName);
3936 return true;
3939 ScTokenArray* pNew = xNew->Clone();
3940 PushTokenArray( pNew, true);
3941 if (pNew->GetNextReference() != NULL)
3943 SetRelNameReference();
3944 MoveRelWrap(MAXCOL, MAXROW);
3946 pNew->Reset();
3947 return GetToken();
3949 default:
3950 DBG_ERROR("Wrong type for external reference!");
3951 return FALSE;
3953 return TRUE;
3957 //---------------------------------------------------------------------------
3960 //---------------------------------------------------------------------------
3961 // Append token to RPN code
3962 //---------------------------------------------------------------------------
3965 //-----------------------------------------------------------------------------
3967 //---------------------------------------------------------------------------
3968 // RPN creation by recursion
3969 //---------------------------------------------------------------------------
3973 //-----------------------------------------------------------------------------
3975 BOOL ScCompiler::HasModifiedRange()
3977 pArr->Reset();
3978 for ( FormulaToken* t = pArr->Next(); t; t = pArr->Next() )
3980 OpCode eOpCode = t->GetOpCode();
3981 if ( eOpCode == ocName )
3983 ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex(t->GetIndex());
3985 if (pRangeData && pRangeData->IsModified())
3986 return TRUE;
3988 else if ( eOpCode == ocDBArea )
3990 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(t->GetIndex());
3992 if (pDBData && pDBData->IsModified())
3993 return TRUE;
3996 return FALSE;
4000 //---------------------------------------------------------------------------
4002 template< typename T, typename S >
4003 S lcl_adjval( S& n, T pos, T max, BOOL bRel )
4005 max++;
4006 if( bRel )
4007 n = sal::static_int_cast<S>( n + pos );
4008 if( n < 0 )
4009 n = sal::static_int_cast<S>( n + max );
4010 else if( n >= max )
4011 n = sal::static_int_cast<S>( n - max );
4012 if( bRel )
4013 n = sal::static_int_cast<S>( n - pos );
4014 return n;
4017 // reference of named range with relative references
4019 void ScCompiler::SetRelNameReference()
4021 pArr->Reset();
4022 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4023 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4025 ScSingleRefData& rRef1 = t->GetSingleRef();
4026 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
4027 rRef1.SetRelName( TRUE );
4028 if ( t->GetType() == svDoubleRef )
4030 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4031 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
4032 rRef2.SetRelName( TRUE );
4037 // Wrap-adjust relative references of a RangeName to current position,
4038 // don't call for other token arrays!
4039 void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow )
4041 pArr->Reset();
4042 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4043 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4045 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4046 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4047 else
4048 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4052 // static
4053 // Wrap-adjust relative references of a RangeName to current position,
4054 // don't call for other token arrays!
4055 void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos,
4056 SCCOL nMaxCol, SCROW nMaxRow )
4058 rArr.Reset();
4059 for( ScToken* t = static_cast<ScToken*>(rArr.GetNextReference()); t;
4060 t = static_cast<ScToken*>(rArr.GetNextReference()) )
4062 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4063 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4064 else
4065 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4069 ScRangeData* ScCompiler::UpdateReference(UpdateRefMode eUpdateRefMode,
4070 const ScAddress& rOldPos, const ScRange& r,
4071 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
4072 BOOL& rChanged, BOOL& rRefSizeChanged )
4074 rChanged = rRefSizeChanged = FALSE;
4075 if ( eUpdateRefMode == URM_COPY )
4076 { // Normally nothing has to be done here since RelRefs are used, also
4077 // SharedFormulas don't need any special handling, except if they
4078 // wrapped around sheet borders.
4079 // #67383# But ColRowName tokens pointing to a ColRow header which was
4080 // copied along with this formula need to be updated to point to the
4081 // copied header instead of the old position's new intersection.
4082 ScToken* t;
4083 pArr->Reset();
4084 while( (t = static_cast<ScToken*>(pArr->GetNextColRowName())) != NULL )
4086 ScSingleRefData& rRef = t->GetSingleRef();
4087 rRef.CalcAbsIfRel( rOldPos );
4088 ScAddress aNewRef( rRef.nCol + nDx, rRef.nRow + nDy, rRef.nTab + nDz );
4089 if ( r.In( aNewRef ) )
4090 { // yes, this is URM_MOVE
4091 if ( ScRefUpdate::Update( pDoc, URM_MOVE, aPos,
4092 r, nDx, nDy, nDz,
4093 SingleDoubleRefModifier( rRef ).Ref() )
4094 != UR_NOTHING
4096 rChanged = TRUE;
4099 // Check for SharedFormulas.
4100 ScRangeData* pRangeData = NULL;
4101 pArr->Reset();
4102 for( FormulaToken* j = pArr->GetNextName(); j && !pRangeData;
4103 j = pArr->GetNextName() )
4105 if( j->GetOpCode() == ocName )
4107 ScRangeData* pName = pDoc->GetRangeName()->FindIndex( j->GetIndex() );
4108 if (pName && pName->HasType(RT_SHARED))
4109 pRangeData = pName;
4112 // Check SharedFormulas for wraps.
4113 if (pRangeData)
4115 ScRangeData* pName = pRangeData;
4116 pRangeData = NULL;
4117 pArr->Reset();
4118 for( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()); t && !pRangeData;
4119 t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) )
4121 BOOL bRelName = (t->GetType() == svSingleRef ?
4122 t->GetSingleRef().IsRelName() :
4123 (t->GetDoubleRef().Ref1.IsRelName() ||
4124 t->GetDoubleRef().Ref2.IsRelName()));
4125 if (bRelName)
4127 t->CalcAbsIfRel( rOldPos);
4128 BOOL bValid = (t->GetType() == svSingleRef ?
4129 t->GetSingleRef().Valid() :
4130 t->GetDoubleRef().Valid());
4131 // If the reference isn't valid, copying the formula
4132 // wrapped it. Replace SharedFormula.
4133 if (!bValid)
4135 pRangeData = pName;
4136 rChanged = TRUE;
4141 return pRangeData;
4143 else
4146 * Set SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE to 1 if we wanted to preserve as
4147 * many shared formulas as possible instead of replacing them with direct code.
4148 * Note that this may produce shared formula usage Excel doesn't understand,
4149 * which would have to be adapted for in the export filter. Advisable as a long
4150 * term goal, since it could decrease memory footprint.
4152 #define SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE 0
4153 ScRangeData* pRangeData = NULL;
4154 ScToken* t;
4155 pArr->Reset();
4156 while( (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL )
4158 if( t->GetOpCode() == ocName )
4160 ScRangeData* pName = pDoc->GetRangeName()->FindIndex( t->GetIndex() );
4161 if (pName && pName->HasType(RT_SHAREDMOD))
4163 pRangeData = pName; // maybe need a replacement of shared with own code
4164 #if ! SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4165 rChanged = TRUE;
4166 #endif
4169 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4171 t->CalcAbsIfRel( rOldPos );
4172 switch (t->GetType())
4174 case svExternalSingleRef:
4175 case svExternalDoubleRef:
4176 // External references never change their positioning
4177 // nor point to parts that will be removed or expanded.
4178 // In fact, calling ScRefUpdate::Update() for URM_MOVE
4179 // may have negative side effects. Simply adapt
4180 // relative references to the new position.
4181 t->CalcRelFromAbs( aPos);
4182 break;
4183 case svSingleRef:
4185 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
4186 aPos, r, nDx, nDy, nDz,
4187 SingleDoubleRefModifier(
4188 t->GetSingleRef()).Ref())
4189 != UR_NOTHING)
4190 rChanged = TRUE;
4192 break;
4193 default:
4195 ScComplexRefData& rRef = t->GetDoubleRef();
4196 SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
4197 SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
4198 SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
4199 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
4200 aPos, r, nDx, nDy, nDz,
4201 t->GetDoubleRef()) != UR_NOTHING)
4203 rChanged = TRUE;
4204 if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
4205 rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
4206 rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
4207 rRefSizeChanged = TRUE;
4213 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4214 BOOL bEasyShared, bPosInRange;
4215 if ( !pRangeData )
4216 bEasyShared = bPosInRange = FALSE;
4217 else
4219 bEasyShared = TRUE;
4220 bPosInRange = r.In( eUpdateRefMode == URM_MOVE ? aPos : rOldPos );
4222 #endif
4223 pArr->Reset();
4224 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4226 if ( t->GetRef() != 1 )
4228 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4229 bEasyShared = FALSE;
4230 #endif
4232 else
4233 { // if nRefCnt>1 it's already updated in token code
4234 if ( t->GetType() == svSingleRef )
4236 ScSingleRefData& rRef = t->GetSingleRef();
4237 SingleDoubleRefModifier aMod( rRef );
4238 if ( rRef.IsRelName() )
4240 ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, aMod.Ref() );
4241 rChanged = TRUE;
4243 else
4245 aMod.Ref().CalcAbsIfRel( rOldPos );
4246 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4247 r, nDx, nDy, nDz, aMod.Ref() )
4248 != UR_NOTHING
4250 rChanged = TRUE;
4252 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4253 if ( bEasyShared )
4255 const ScSingleRefData& rSRD = aMod.Ref().Ref1;
4256 ScAddress aRef( rSRD.nCol, rSRD.nRow, rSRD.nTab );
4257 if ( r.In( aRef ) != bPosInRange )
4258 bEasyShared = FALSE;
4260 #endif
4262 else
4264 ScComplexRefData& rRef = t->GetDoubleRef();
4265 SCCOL nCols = rRef.Ref2.nCol - rRef.Ref1.nCol;
4266 SCROW nRows = rRef.Ref2.nRow - rRef.Ref1.nRow;
4267 SCTAB nTabs = rRef.Ref2.nTab - rRef.Ref1.nTab;
4268 if ( rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName() )
4270 ScRefUpdate::MoveRelWrap( pDoc, aPos, MAXCOL, MAXROW, rRef );
4271 rChanged = TRUE;
4273 else
4275 if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4276 r, nDx, nDy, nDz, rRef )
4277 != UR_NOTHING
4280 rChanged = TRUE;
4281 if (rRef.Ref2.nCol - rRef.Ref1.nCol != nCols ||
4282 rRef.Ref2.nRow - rRef.Ref1.nRow != nRows ||
4283 rRef.Ref2.nTab - rRef.Ref1.nTab != nTabs)
4285 rRefSizeChanged = TRUE;
4286 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4287 bEasyShared = FALSE;
4288 #endif
4292 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4293 if ( bEasyShared )
4295 ScRange aRef( rRef.Ref1.nCol, rRef.Ref1.nRow,
4296 rRef.Ref1.nTab, rRef.Ref2.nCol, rRef.Ref2.nRow,
4297 rRef.Ref2.nTab );
4298 if ( r.In( aRef ) != bPosInRange )
4299 bEasyShared = FALSE;
4301 #endif
4305 #if SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4306 if ( pRangeData )
4308 if ( bEasyShared )
4309 pRangeData = 0;
4310 else
4311 rChanged = TRUE;
4313 #endif
4314 #undef SC_PRESERVE_SHARED_FORMULAS_IF_POSSIBLE
4315 return pRangeData;
4319 BOOL ScCompiler::UpdateNameReference(UpdateRefMode eUpdateRefMode,
4320 const ScRange& r,
4321 SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
4322 BOOL& rChanged, BOOL bSharedFormula)
4324 BOOL bRelRef = FALSE; // set if relative reference
4325 rChanged = FALSE;
4326 pArr->Reset();
4327 ScToken* t;
4328 while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
4330 SingleDoubleRefModifier aMod( *t );
4331 ScComplexRefData& rRef = aMod.Ref();
4332 bRelRef = rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() ||
4333 rRef.Ref1.IsTabRel();
4334 if (!bRelRef && t->GetType() == svDoubleRef)
4335 bRelRef = rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() ||
4336 rRef.Ref2.IsTabRel();
4337 bool bUpdate = !rRef.Ref1.IsColRel() || !rRef.Ref1.IsRowRel() ||
4338 !rRef.Ref1.IsTabRel();
4339 if (!bUpdate && t->GetType() == svDoubleRef)
4340 bUpdate = !rRef.Ref2.IsColRel() || !rRef.Ref2.IsRowRel() ||
4341 !rRef.Ref2.IsTabRel();
4342 if (!bSharedFormula)
4344 // We cannot update names with sheet-relative references, they may
4345 // be used on other sheets as well and the resulting reference
4346 // would be wrong. This is a dilemma if col/row would need to be
4347 // updated for the current usage.
4348 // TODO: seems the only way out of this would be to not allow
4349 // relative sheet references and have sheet-local names that can be
4350 // copied along with sheets.
4351 bUpdate = bUpdate && !rRef.Ref1.IsTabRel() && !rRef.Ref2.IsTabRel();
4353 if (bUpdate)
4355 rRef.CalcAbsIfRel( aPos);
4356 if (ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos, r,
4357 nDx, nDy, nDz, rRef, ScRefUpdate::ABSOLUTE)
4358 != UR_NOTHING )
4359 rChanged = TRUE;
4362 return bRelRef;
4366 void ScCompiler::UpdateSharedFormulaReference( UpdateRefMode eUpdateRefMode,
4367 const ScAddress& rOldPos, const ScRange& r,
4368 SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
4370 if ( eUpdateRefMode == URM_COPY )
4371 return ;
4372 else
4374 ScToken* t;
4375 pArr->Reset();
4376 while ( (t = static_cast<ScToken*>(pArr->GetNextReference())) != NULL )
4378 if( t->GetType() != svIndex ) // it may be a DB area!!!
4380 t->CalcAbsIfRel( rOldPos );
4381 // Absolute references have been already adjusted in the named
4382 // shared formula itself prior to breaking the shared formula
4383 // and calling this function. Don't readjust them again.
4384 SingleDoubleRefModifier aMod( *t );
4385 ScComplexRefData& rRef = aMod.Ref();
4386 ScComplexRefData aBkp = rRef;
4387 ScRefUpdate::Update( pDoc, eUpdateRefMode, aPos,
4388 r, nDx, nDy, nDz, rRef );
4389 // restore absolute parts
4390 if ( !aBkp.Ref1.IsColRel() )
4392 rRef.Ref1.nCol = aBkp.Ref1.nCol;
4393 rRef.Ref1.nRelCol = aBkp.Ref1.nRelCol;
4394 rRef.Ref1.SetColDeleted( aBkp.Ref1.IsColDeleted() );
4396 if ( !aBkp.Ref1.IsRowRel() )
4398 rRef.Ref1.nRow = aBkp.Ref1.nRow;
4399 rRef.Ref1.nRelRow = aBkp.Ref1.nRelRow;
4400 rRef.Ref1.SetRowDeleted( aBkp.Ref1.IsRowDeleted() );
4402 if ( !aBkp.Ref1.IsTabRel() )
4404 rRef.Ref1.nTab = aBkp.Ref1.nTab;
4405 rRef.Ref1.nRelTab = aBkp.Ref1.nRelTab;
4406 rRef.Ref1.SetTabDeleted( aBkp.Ref1.IsTabDeleted() );
4408 if ( t->GetType() == svDoubleRef )
4410 if ( !aBkp.Ref2.IsColRel() )
4412 rRef.Ref2.nCol = aBkp.Ref2.nCol;
4413 rRef.Ref2.nRelCol = aBkp.Ref2.nRelCol;
4414 rRef.Ref2.SetColDeleted( aBkp.Ref2.IsColDeleted() );
4416 if ( !aBkp.Ref2.IsRowRel() )
4418 rRef.Ref2.nRow = aBkp.Ref2.nRow;
4419 rRef.Ref2.nRelRow = aBkp.Ref2.nRelRow;
4420 rRef.Ref2.SetRowDeleted( aBkp.Ref2.IsRowDeleted() );
4422 if ( !aBkp.Ref2.IsTabRel() )
4424 rRef.Ref2.nTab = aBkp.Ref2.nTab;
4425 rRef.Ref2.nRelTab = aBkp.Ref2.nRelTab;
4426 rRef.Ref2.SetTabDeleted( aBkp.Ref2.IsTabDeleted() );
4435 ScRangeData* ScCompiler::UpdateInsertTab( SCTAB nTable, BOOL bIsName )
4437 ScRangeData* pRangeData = NULL;
4438 SCTAB nPosTab = aPos.Tab(); // _after_ incremented!
4439 SCTAB nOldPosTab = ((nPosTab > nTable) ? (nPosTab - 1) : nPosTab);
4440 BOOL bIsRel = FALSE;
4441 ScToken* t;
4442 pArr->Reset();
4443 if (bIsName)
4444 t = static_cast<ScToken*>(pArr->GetNextReference());
4445 else
4446 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4447 while( t )
4449 if( t->GetOpCode() == ocName )
4451 if (!bIsName)
4453 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4454 if (pName && pName->HasType(RT_SHAREDMOD))
4455 pRangeData = pName;
4458 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4460 if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
4461 { // of names only adjust absolute references
4462 ScSingleRefData& rRef = t->GetSingleRef();
4463 if ( rRef.IsTabRel() )
4465 rRef.nTab = rRef.nRelTab + nOldPosTab;
4466 if ( rRef.nTab < 0 )
4467 rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
4469 if (nTable <= rRef.nTab)
4470 ++rRef.nTab;
4471 rRef.nRelTab = rRef.nTab - nPosTab;
4473 else
4474 bIsRel = TRUE;
4475 if ( t->GetType() == svDoubleRef )
4477 if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
4478 { // of names only adjust absolute references
4479 ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
4480 if ( rRef.IsTabRel() )
4482 rRef.nTab = rRef.nRelTab + nOldPosTab;
4483 if ( rRef.nTab < 0 )
4484 rRef.nTab = sal::static_int_cast<SCsTAB>( rRef.nTab + pDoc->GetTableCount() ); // was a wrap
4486 if (nTable <= rRef.nTab)
4487 ++rRef.nTab;
4488 rRef.nRelTab = rRef.nTab - nPosTab;
4490 else
4491 bIsRel = TRUE;
4493 if ( bIsName && bIsRel )
4494 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4496 if (bIsName)
4497 t = static_cast<ScToken*>(pArr->GetNextReference());
4498 else
4499 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4501 if ( !bIsName )
4503 pArr->Reset();
4504 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4506 if ( t->GetRef() == 1 )
4508 ScSingleRefData& rRef1 = t->GetSingleRef();
4509 if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
4510 { // of names only adjust absolute references
4511 if ( rRef1.IsTabRel() )
4513 rRef1.nTab = rRef1.nRelTab + nOldPosTab;
4514 if ( rRef1.nTab < 0 )
4515 rRef1.nTab = sal::static_int_cast<SCsTAB>( rRef1.nTab + pDoc->GetTableCount() ); // was a wrap
4517 if (nTable <= rRef1.nTab)
4518 ++rRef1.nTab;
4519 rRef1.nRelTab = rRef1.nTab - nPosTab;
4521 if ( t->GetType() == svDoubleRef )
4523 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4524 if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
4525 { // of names only adjust absolute references
4526 if ( rRef2.IsTabRel() )
4528 rRef2.nTab = rRef2.nRelTab + nOldPosTab;
4529 if ( rRef2.nTab < 0 )
4530 rRef2.nTab = sal::static_int_cast<SCsTAB>( rRef2.nTab + pDoc->GetTableCount() ); // was a wrap
4532 if (nTable <= rRef2.nTab)
4533 ++rRef2.nTab;
4534 rRef2.nRelTab = rRef2.nTab - nPosTab;
4540 return pRangeData;
4543 ScRangeData* ScCompiler::UpdateDeleteTab(SCTAB nTable, BOOL /* bIsMove */, BOOL bIsName,
4544 BOOL& rChanged)
4546 ScRangeData* pRangeData = NULL;
4547 SCTAB nTab, nTab2;
4548 SCTAB nPosTab = aPos.Tab(); // _after_ decremented!
4549 SCTAB nOldPosTab = ((nPosTab >= nTable) ? (nPosTab + 1) : nPosTab);
4550 rChanged = FALSE;
4551 BOOL bIsRel = FALSE;
4552 ScToken* t;
4553 pArr->Reset();
4554 if (bIsName)
4555 t = static_cast<ScToken*>(pArr->GetNextReference());
4556 else
4557 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4558 while( t )
4560 if( t->GetOpCode() == ocName )
4562 if (!bIsName)
4564 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4565 if (pName && pName->HasType(RT_SHAREDMOD))
4566 pRangeData = pName;
4568 rChanged = TRUE;
4570 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4572 if ( !(bIsName && t->GetSingleRef().IsTabRel()) )
4573 { // of names only adjust absolute references
4574 ScSingleRefData& rRef = t->GetSingleRef();
4575 if ( rRef.IsTabRel() )
4576 nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
4577 else
4578 nTab = rRef.nTab;
4579 if ( nTable < nTab )
4581 rRef.nTab = nTab - 1;
4582 rChanged = TRUE;
4584 else if ( nTable == nTab )
4586 if ( t->GetType() == svDoubleRef )
4588 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4589 if ( rRef2.IsTabRel() )
4590 nTab2 = rRef2.nRelTab + nOldPosTab;
4591 else
4592 nTab2 = rRef2.nTab;
4593 if ( nTab == nTab2
4594 || (nTab+1) >= pDoc->GetTableCount() )
4596 rRef.nTab = MAXTAB+1;
4597 rRef.SetTabDeleted( TRUE );
4599 // else: nTab later points to what's nTable+1 now
4600 // => area shrunk
4602 else
4604 rRef.nTab = MAXTAB+1;
4605 rRef.SetTabDeleted( TRUE );
4607 rChanged = TRUE;
4609 rRef.nRelTab = rRef.nTab - nPosTab;
4611 else
4612 bIsRel = TRUE;
4613 if ( t->GetType() == svDoubleRef )
4615 if ( !(bIsName && t->GetDoubleRef().Ref2.IsTabRel()) )
4616 { // of names only adjust absolute references
4617 ScSingleRefData& rRef = t->GetDoubleRef().Ref2;
4618 if ( rRef.IsTabRel() )
4619 nTab = rRef.nTab = rRef.nRelTab + nOldPosTab;
4620 else
4621 nTab = rRef.nTab;
4622 if ( nTable < nTab )
4624 rRef.nTab = nTab - 1;
4625 rChanged = TRUE;
4627 else if ( nTable == nTab )
4629 if ( !t->GetDoubleRef().Ref1.IsTabDeleted() )
4630 rRef.nTab = nTab - 1; // shrink area
4631 else
4633 rRef.nTab = MAXTAB+1;
4634 rRef.SetTabDeleted( TRUE );
4636 rChanged = TRUE;
4638 rRef.nRelTab = rRef.nTab - nPosTab;
4640 else
4641 bIsRel = TRUE;
4643 if ( bIsName && bIsRel )
4644 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4646 if (bIsName)
4647 t = static_cast<ScToken*>(pArr->GetNextReference());
4648 else
4649 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4651 if ( !bIsName )
4653 pArr->Reset();
4654 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4656 if ( t->GetRef() == 1 )
4658 ScSingleRefData& rRef1 = t->GetSingleRef();
4659 if ( !(rRef1.IsRelName() && rRef1.IsTabRel()) )
4660 { // of names only adjust absolute references
4661 if ( rRef1.IsTabRel() )
4662 nTab = rRef1.nTab = rRef1.nRelTab + nOldPosTab;
4663 else
4664 nTab = rRef1.nTab;
4665 if ( nTable < nTab )
4667 rRef1.nTab = nTab - 1;
4668 rChanged = TRUE;
4670 else if ( nTable == nTab )
4672 if ( t->GetType() == svDoubleRef )
4674 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4675 if ( rRef2.IsTabRel() )
4676 nTab2 = rRef2.nRelTab + nOldPosTab;
4677 else
4678 nTab2 = rRef2.nTab;
4679 if ( nTab == nTab2
4680 || (nTab+1) >= pDoc->GetTableCount() )
4682 rRef1.nTab = MAXTAB+1;
4683 rRef1.SetTabDeleted( TRUE );
4685 // else: nTab later points to what's nTable+1 now
4686 // => area shrunk
4688 else
4690 rRef1.nTab = MAXTAB+1;
4691 rRef1.SetTabDeleted( TRUE );
4693 rChanged = TRUE;
4695 rRef1.nRelTab = rRef1.nTab - nPosTab;
4697 if ( t->GetType() == svDoubleRef )
4699 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4700 if ( !(rRef2.IsRelName() && rRef2.IsTabRel()) )
4701 { // of names only adjust absolute references
4702 if ( rRef2.IsTabRel() )
4703 nTab = rRef2.nTab = rRef2.nRelTab + nOldPosTab;
4704 else
4705 nTab = rRef2.nTab;
4706 if ( nTable < nTab )
4708 rRef2.nTab = nTab - 1;
4709 rChanged = TRUE;
4711 else if ( nTable == nTab )
4713 if ( !rRef1.IsTabDeleted() )
4714 rRef2.nTab = nTab - 1; // shrink area
4715 else
4717 rRef2.nTab = MAXTAB+1;
4718 rRef2.SetTabDeleted( TRUE );
4720 rChanged = TRUE;
4722 rRef2.nRelTab = rRef2.nTab - nPosTab;
4728 return pRangeData;
4731 // aPos.Tab() must be already adjusted!
4732 ScRangeData* ScCompiler::UpdateMoveTab( SCTAB nOldTab, SCTAB nNewTab,
4733 BOOL bIsName )
4735 ScRangeData* pRangeData = NULL;
4736 SCsTAB nTab;
4738 SCTAB nStart, nEnd;
4739 short nDir; // direction in which others move
4740 if ( nOldTab < nNewTab )
4742 nDir = -1;
4743 nStart = nOldTab;
4744 nEnd = nNewTab;
4746 else
4748 nDir = 1;
4749 nStart = nNewTab;
4750 nEnd = nOldTab;
4752 SCTAB nPosTab = aPos.Tab(); // current sheet
4753 SCTAB nOldPosTab; // previously it was this one
4754 if ( nPosTab == nNewTab )
4755 nOldPosTab = nOldTab; // look, it's me!
4756 else if ( nPosTab < nStart || nEnd < nPosTab )
4757 nOldPosTab = nPosTab; // wasn't moved
4758 else
4759 nOldPosTab = nPosTab - nDir; // moved by one
4761 BOOL bIsRel = FALSE;
4762 ScToken* t;
4763 pArr->Reset();
4764 if (bIsName)
4765 t = static_cast<ScToken*>(pArr->GetNextReference());
4766 else
4767 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4768 while( t )
4770 if( t->GetOpCode() == ocName )
4772 if (!bIsName)
4774 ScRangeData* pName = pDoc->GetRangeName()->FindIndex(t->GetIndex());
4775 if (pName && pName->HasType(RT_SHAREDMOD))
4776 pRangeData = pName;
4779 else if( t->GetType() != svIndex ) // it may be a DB area!!!
4781 ScSingleRefData& rRef1 = t->GetSingleRef();
4782 if ( !(bIsName && rRef1.IsTabRel()) )
4783 { // of names only adjust absolute references
4784 if ( rRef1.IsTabRel() )
4785 nTab = rRef1.nRelTab + nOldPosTab;
4786 else
4787 nTab = rRef1.nTab;
4788 if ( nTab == nOldTab )
4789 rRef1.nTab = nNewTab;
4790 else if ( nStart <= nTab && nTab <= nEnd )
4791 rRef1.nTab = nTab + nDir;
4792 rRef1.nRelTab = rRef1.nTab - nPosTab;
4794 else
4795 bIsRel = TRUE;
4796 if ( t->GetType() == svDoubleRef )
4798 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4799 if ( !(bIsName && rRef2.IsTabRel()) )
4800 { // of names only adjust absolute references
4801 if ( rRef2.IsTabRel() )
4802 nTab = rRef2.nRelTab + nOldPosTab;
4803 else
4804 nTab = rRef2.nTab;
4805 if ( nTab == nOldTab )
4806 rRef2.nTab = nNewTab;
4807 else if ( nStart <= nTab && nTab <= nEnd )
4808 rRef2.nTab = nTab + nDir;
4809 rRef2.nRelTab = rRef2.nTab - nPosTab;
4811 else
4812 bIsRel = TRUE;
4813 SCsTAB nTab1, nTab2;
4814 if ( rRef1.IsTabRel() )
4815 nTab1 = rRef1.nRelTab + nPosTab;
4816 else
4817 nTab1 = rRef1.nTab;
4818 if ( rRef2.IsTabRel() )
4819 nTab2 = rRef2.nRelTab + nPosTab;
4820 else
4821 nTab2 = rRef1.nTab;
4822 if ( nTab2 < nTab1 )
4823 { // PutInOrder
4824 rRef1.nTab = nTab2;
4825 rRef2.nTab = nTab1;
4826 rRef1.nRelTab = rRef1.nTab - nPosTab;
4827 rRef2.nRelTab = rRef2.nTab - nPosTab;
4830 if ( bIsName && bIsRel )
4831 pRangeData = (ScRangeData*) this; // not dereferenced in rangenam
4833 if (bIsName)
4834 t = static_cast<ScToken*>(pArr->GetNextReference());
4835 else
4836 t = static_cast<ScToken*>(pArr->GetNextReferenceOrName());
4838 if ( !bIsName )
4840 SCsTAB nMaxTabMod = (SCsTAB) pDoc->GetTableCount();
4841 pArr->Reset();
4842 while ( (t = static_cast<ScToken*>(pArr->GetNextReferenceRPN())) != NULL )
4844 if ( t->GetRef() == 1 )
4846 ScSingleRefData& rRef1 = t->GetSingleRef();
4847 if ( rRef1.IsRelName() && rRef1.IsTabRel() )
4848 { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
4849 nTab = rRef1.nRelTab + nPosTab;
4850 if ( nTab < 0 )
4851 nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
4852 else if ( nTab > nMaxTab )
4853 nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
4854 rRef1.nRelTab = nTab - nPosTab;
4856 else
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 if( t->GetType() == svDoubleRef )
4870 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4871 if ( rRef2.IsRelName() && rRef2.IsTabRel() )
4872 { // possibly wrap RelName, like lcl_MoveItWrap in refupdat.cxx
4873 nTab = rRef2.nRelTab + nPosTab;
4874 if ( nTab < 0 )
4875 nTab = sal::static_int_cast<SCsTAB>( nTab + nMaxTabMod );
4876 else if ( nTab > nMaxTab )
4877 nTab = sal::static_int_cast<SCsTAB>( nTab - nMaxTabMod );
4878 rRef2.nRelTab = nTab - nPosTab;
4880 else
4882 if ( rRef2.IsTabRel() )
4883 nTab = rRef2.nRelTab + nOldPosTab;
4884 else
4885 nTab = rRef2.nTab;
4886 if ( nTab == nOldTab )
4887 rRef2.nTab = nNewTab;
4888 else if ( nStart <= nTab && nTab <= nEnd )
4889 rRef2.nTab = nTab + nDir;
4890 rRef2.nRelTab = rRef2.nTab - nPosTab;
4892 SCsTAB nTab1, nTab2;
4893 if ( rRef1.IsTabRel() )
4894 nTab1 = rRef1.nRelTab + nPosTab;
4895 else
4896 nTab1 = rRef1.nTab;
4897 if ( rRef2.IsTabRel() )
4898 nTab2 = rRef2.nRelTab + nPosTab;
4899 else
4900 nTab2 = rRef1.nTab;
4901 if ( nTab2 < nTab1 )
4902 { // PutInOrder
4903 rRef1.nTab = nTab2;
4904 rRef2.nTab = nTab1;
4905 rRef1.nRelTab = rRef1.nTab - nPosTab;
4906 rRef2.nRelTab = rRef2.nTab - nPosTab;
4912 return pRangeData;
4916 void ScCompiler::CreateStringFromExternal(rtl::OUStringBuffer& rBuffer, FormulaToken* pTokenP)
4918 FormulaToken* t = pTokenP;
4919 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4920 switch (t->GetType())
4922 case svExternalName:
4924 const String *pStr = pRefMgr->getExternalFileName(t->GetIndex());
4925 String aFileName = pStr ? *pStr : ScGlobal::GetRscString(STR_NO_NAME_REF);
4926 rBuffer.append(pConv->makeExternalNameStr( aFileName, t->GetString()));
4928 break;
4929 case svExternalSingleRef:
4930 pConv->makeExternalRefStr(
4931 rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetSingleRef(), pRefMgr);
4932 break;
4933 case svExternalDoubleRef:
4934 pConv->makeExternalRefStr(
4935 rBuffer, *this, t->GetIndex(), t->GetString(), static_cast<ScToken*>(t)->GetDoubleRef(), pRefMgr);
4936 break;
4937 default:
4938 // warning, not error, otherwise we may end up with a never
4939 // ending message box loop if this was the cursor cell to be redrawn.
4940 DBG_WARNING("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
4944 void ScCompiler::CreateStringFromMatrix( rtl::OUStringBuffer& rBuffer,
4945 FormulaToken* pTokenP)
4947 const ScMatrix* pMatrix = static_cast<ScToken*>(pTokenP)->GetMatrix();
4948 SCSIZE nC, nMaxC, nR, nMaxR;
4950 pMatrix->GetDimensions( nMaxC, nMaxR);
4952 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
4953 for( nR = 0 ; nR < nMaxR ; nR++)
4955 if( nR > 0)
4957 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
4960 for( nC = 0 ; nC < nMaxC ; nC++)
4962 if( nC > 0)
4964 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
4967 if( pMatrix->IsValue( nC, nR ) )
4969 ScMatValType nType;
4970 const ScMatrixValue* pVal = pMatrix->Get( nC, nR, nType);
4972 if( nType == SC_MATVAL_BOOLEAN )
4973 AppendBoolean( rBuffer, pVal->GetBoolean() );
4974 else
4976 USHORT nErr = pVal->GetError();
4977 if( nErr )
4978 rBuffer.append( ScGlobal::GetErrorString( nErr ) );
4979 else
4980 AppendDouble( rBuffer, pVal->fVal );
4983 else if( pMatrix->IsEmpty( nC, nR ) )
4985 else if( pMatrix->IsString( nC, nR ) )
4986 AppendString( rBuffer, pMatrix->GetString( nC, nR ) );
4989 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
4992 void ScCompiler::CreateStringFromSingleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
4994 const OpCode eOp = _pTokenP->GetOpCode();
4995 ScSingleRefData& rRef = static_cast<ScToken*>(_pTokenP)->GetSingleRef();
4996 ScComplexRefData aRef;
4997 aRef.Ref1 = aRef.Ref2 = rRef;
4998 if ( eOp == ocColRowName )
5000 rRef.CalcAbsIfRel( aPos );
5001 if ( pDoc->HasStringData( rRef.nCol, rRef.nRow, rRef.nTab ) )
5003 String aStr;
5004 pDoc->GetString( rRef.nCol, rRef.nRow, rRef.nTab, aStr );
5005 EnQuote( aStr );
5006 rBuffer.append(aStr);
5008 else
5010 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
5011 pConv->MakeRefStr (rBuffer, *this, aRef, TRUE );
5014 else
5015 pConv->MakeRefStr( rBuffer, *this, aRef, TRUE );
5017 // -----------------------------------------------------------------------------
5018 void ScCompiler::CreateStringFromDoubleRef(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
5020 pConv->MakeRefStr( rBuffer, *this, static_cast<ScToken*>(_pTokenP)->GetDoubleRef(), FALSE );
5022 // -----------------------------------------------------------------------------
5023 void ScCompiler::CreateStringFromIndex(rtl::OUStringBuffer& rBuffer,FormulaToken* _pTokenP)
5025 const OpCode eOp = _pTokenP->GetOpCode();
5026 rtl::OUStringBuffer aBuffer;
5027 switch ( eOp )
5029 case ocName:
5031 ScRangeData* pData = pDoc->GetRangeName()->FindIndex(_pTokenP->GetIndex());
5032 if (pData)
5034 if (pData->HasType(RT_SHARED))
5035 pData->UpdateSymbol( aBuffer, aPos, GetGrammar());
5036 else
5037 aBuffer.append(pData->GetName());
5040 break;
5041 case ocDBArea:
5043 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex(_pTokenP->GetIndex());
5044 if (pDBData)
5045 aBuffer.append(pDBData->GetName());
5047 break;
5048 default:
5049 ; // nothing
5051 if ( aBuffer.getLength() )
5052 rBuffer.append(aBuffer);
5053 else
5054 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
5056 // -----------------------------------------------------------------------------
5057 void ScCompiler::LocalizeString( String& rName )
5059 ScGlobal::GetAddInCollection()->LocalizeString( rName );
5061 // -----------------------------------------------------------------------------
5062 BOOL ScCompiler::IsImportingXML() const
5064 return pDoc->IsImportingXML();
5067 // Put quotes around string if non-alphanumeric characters are contained,
5068 // quote characters contained within are escaped by '\\'.
5069 BOOL ScCompiler::EnQuote( String& rStr )
5071 sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.Len() );
5072 if ( !CharClass::isNumericType( nType )
5073 && CharClass::isAlphaNumericType( nType ) )
5074 return FALSE;
5076 xub_StrLen nPos = 0;
5077 while ( (nPos = rStr.Search( '\'', nPos)) != STRING_NOTFOUND )
5079 rStr.Insert( '\\', nPos );
5080 nPos += 2;
5082 rStr.Insert( '\'', 0 );
5083 rStr += '\'';
5084 return TRUE;
5087 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
5089 return pConv->getSpecialSymbol(eType);
5092 void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
5094 // All known AddIn functions.
5095 sheet::FormulaOpCodeMapEntry aEntry;
5096 aEntry.Token.OpCode = ocExternal;
5098 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
5099 const long nCount = pColl->GetFuncCount();
5100 for (long i=0; i < nCount; ++i)
5102 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
5103 if (pFuncData)
5105 if ( _bIsEnglish )
5107 String aName;
5108 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
5109 aEntry.Name = aName;
5110 else
5111 aEntry.Name = pFuncData->GetUpperName();
5113 else
5114 aEntry.Name = pFuncData->GetUpperLocal();
5115 aEntry.Token.Data <<= ::rtl::OUString( pFuncData->GetOriginalName());
5116 _rVec.push_back( aEntry);
5119 // FIXME: what about those old non-UNO AddIns?
5121 // -----------------------------------------------------------------------------
5122 BOOL ScCompiler::HandleSingleRef()
5124 ScSingleRefData& rRef = static_cast<ScToken*>((FormulaToken*)pToken)->GetSingleRef();
5125 rRef.CalcAbsIfRel( aPos );
5126 if ( !rRef.Valid() )
5128 SetError( errNoRef );
5129 return TRUE;
5131 SCCOL nCol = rRef.nCol;
5132 SCROW nRow = rRef.nRow;
5133 SCTAB nTab = rRef.nTab;
5134 ScAddress aLook( nCol, nRow, nTab );
5135 BOOL bColName = rRef.IsColRel();
5136 SCCOL nMyCol = aPos.Col();
5137 SCROW nMyRow = aPos.Row();
5138 BOOL bInList = FALSE;
5139 BOOL bValidName = FALSE;
5140 ScRangePairList* pRL = (bColName ?
5141 pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
5142 ScRange aRange;
5143 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5145 if ( pR->GetRange(0).In( aLook ) )
5147 bInList = bValidName = TRUE;
5148 aRange = pR->GetRange(1);
5149 if ( bColName )
5151 aRange.aStart.SetCol( nCol );
5152 aRange.aEnd.SetCol( nCol );
5154 else
5156 aRange.aStart.SetRow( nRow );
5157 aRange.aEnd.SetRow( nRow );
5159 break; // for
5162 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
5163 { // automagically or created by copying and NamePos isn't in list
5164 BOOL bString = pDoc->HasStringData( nCol, nRow, nTab );
5165 if ( !bString && !pDoc->GetCell( aLook ) )
5166 bString = TRUE; // empty cell is ok
5167 if ( bString )
5168 { //! coresponds with ScInterpreter::ScColRowNameAuto()
5169 bValidName = TRUE;
5170 if ( bColName )
5171 { // ColName
5172 SCROW nStartRow = nRow + 1;
5173 if ( nStartRow > MAXROW )
5174 nStartRow = MAXROW;
5175 SCROW nMaxRow = MAXROW;
5176 if ( nMyCol == nCol )
5177 { // formula cell in same column
5178 if ( nMyRow == nStartRow )
5179 { // take remainder under name cell
5180 nStartRow++;
5181 if ( nStartRow > MAXROW )
5182 nStartRow = MAXROW;
5184 else if ( nMyRow > nStartRow )
5185 { // from name cell down to formula cell
5186 nMaxRow = nMyRow - 1;
5189 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5190 { // next defined ColNameRange below limits row
5191 const ScRange& rRange = pR->GetRange(1);
5192 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
5193 { // identical column range
5194 SCROW nTmp = rRange.aStart.Row();
5195 if ( nStartRow < nTmp && nTmp <= nMaxRow )
5196 nMaxRow = nTmp - 1;
5199 aRange.aStart.Set( nCol, nStartRow, nTab );
5200 aRange.aEnd.Set( nCol, nMaxRow, nTab );
5202 else
5203 { // RowName
5204 SCCOL nStartCol = nCol + 1;
5205 if ( nStartCol > MAXCOL )
5206 nStartCol = MAXCOL;
5207 SCCOL nMaxCol = MAXCOL;
5208 if ( nMyRow == nRow )
5209 { // formula cell in same row
5210 if ( nMyCol == nStartCol )
5211 { // take remainder right from name cell
5212 nStartCol++;
5213 if ( nStartCol > MAXCOL )
5214 nStartCol = MAXCOL;
5216 else if ( nMyCol > nStartCol )
5217 { // from name cell right to formula cell
5218 nMaxCol = nMyCol - 1;
5221 for ( ScRangePair* pR = pRL->First(); pR; pR = pRL->Next() )
5222 { // next defined RowNameRange to the right limits column
5223 const ScRange& rRange = pR->GetRange(1);
5224 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
5225 { // identical row range
5226 SCCOL nTmp = rRange.aStart.Col();
5227 if ( nStartCol < nTmp && nTmp <= nMaxCol )
5228 nMaxCol = nTmp - 1;
5231 aRange.aStart.Set( nStartCol, nRow, nTab );
5232 aRange.aEnd.Set( nMaxCol, nRow, nTab );
5236 if ( bValidName )
5238 // And now the magic to distinguish between a range and a single
5239 // cell thereof, which is picked position-dependent of the formula
5240 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5241 // SingleRef matching the column/row of the formula cell is
5242 // generated. A ocColRowName or ocIntersect as a neighbor results
5243 // in a range. Special case: if label is valid for a single cell, a
5244 // position independent SingleRef is generated.
5245 BOOL bSingle = (aRange.aStart == aRange.aEnd);
5246 BOOL bFound;
5247 if ( bSingle )
5248 bFound = TRUE;
5249 else
5251 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
5252 FormulaToken* p2 = pArr->PeekNextNoSpaces();
5253 // begin/end of a formula => single
5254 OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd );
5255 OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd );
5256 if ( eOp1 != ocColRowName && eOp1 != ocIntersect
5257 && eOp2 != ocColRowName && eOp2 != ocIntersect )
5259 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
5260 (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
5261 bSingle = TRUE;
5263 if ( bSingle )
5264 { // column and/or row must match range
5265 if ( bColName )
5267 bFound = (aRange.aStart.Row() <= nMyRow
5268 && nMyRow <= aRange.aEnd.Row());
5269 if ( bFound )
5270 aRange.aStart.SetRow( nMyRow );
5272 else
5274 bFound = (aRange.aStart.Col() <= nMyCol
5275 && nMyCol <= aRange.aEnd.Col());
5276 if ( bFound )
5277 aRange.aStart.SetCol( nMyCol );
5280 else
5281 bFound = TRUE;
5283 if ( !bFound )
5284 SetError(errNoRef);
5285 else if ( !bCompileForFAP )
5287 ScTokenArray* pNew = new ScTokenArray();
5288 if ( bSingle )
5290 ScSingleRefData aRefData;
5291 aRefData.InitAddress( aRange.aStart );
5292 if ( bColName )
5293 aRefData.SetColRel( TRUE );
5294 else
5295 aRefData.SetRowRel( TRUE );
5296 aRefData.CalcRelFromAbs( aPos );
5297 pNew->AddSingleReference( aRefData );
5299 else
5301 ScComplexRefData aRefData;
5302 aRefData.InitRange( aRange );
5303 if ( bColName )
5305 aRefData.Ref1.SetColRel( TRUE );
5306 aRefData.Ref2.SetColRel( TRUE );
5308 else
5310 aRefData.Ref1.SetRowRel( TRUE );
5311 aRefData.Ref2.SetRowRel( TRUE );
5313 aRefData.CalcRelFromAbs( aPos );
5314 if ( bInList )
5315 pNew->AddDoubleReference( aRefData );
5316 else
5317 { // automagically
5318 pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) );
5321 PushTokenArray( pNew, TRUE );
5322 pNew->Reset();
5323 return GetToken();
5326 else
5327 SetError(errNoName);
5328 return TRUE;
5330 // -----------------------------------------------------------------------------
5331 BOOL ScCompiler::HandleDbData()
5333 ScDBData* pDBData = pDoc->GetDBCollection()->FindIndex( pToken->GetIndex() );
5334 if ( !pDBData )
5335 SetError(errNoName);
5336 else if ( !bCompileForFAP )
5338 ScComplexRefData aRefData;
5339 aRefData.InitFlags();
5340 pDBData->GetArea( (SCTAB&) aRefData.Ref1.nTab,
5341 (SCCOL&) aRefData.Ref1.nCol,
5342 (SCROW&) aRefData.Ref1.nRow,
5343 (SCCOL&) aRefData.Ref2.nCol,
5344 (SCROW&) aRefData.Ref2.nRow);
5345 aRefData.Ref2.nTab = aRefData.Ref1.nTab;
5346 aRefData.CalcRelFromAbs( aPos );
5347 ScTokenArray* pNew = new ScTokenArray();
5348 pNew->AddDoubleReference( aRefData );
5349 PushTokenArray( pNew, TRUE );
5350 pNew->Reset();
5351 return GetToken();
5353 return TRUE;
5356 String GetScCompilerNativeSymbol( OpCode eOp )
5358 return ScCompiler::GetNativeSymbol( eOp );
5360 // -----------------------------------------------------------------------------
5361 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
5363 return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );