Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / compiler.cxx
blobdcb676e4a0018840f405c915c4f6cd2f0881e8fc
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "compiler.hxx"
22 #include <sfx2/app.hxx>
23 #include <sfx2/objsh.hxx>
24 #include <basic/sbmeth.hxx>
25 #include <basic/sbstar.hxx>
26 #include <svl/zforlist.hxx>
27 #include "svl/sharedstringpool.hxx"
28 #include <sal/macros.h>
29 #include <tools/rcid.h>
30 #include <tools/solar.h>
31 #include <tools/string.hxx>
32 #include <unotools/charclass.hxx>
33 #include <com/sun/star/lang/Locale.hpp>
34 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
35 #include <com/sun/star/sheet/FormulaLanguage.hpp>
36 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/string.hxx>
39 #include <unotools/transliterationwrapper.hxx>
40 #include <tools/urlobj.hxx>
41 #include <rtl/math.hxx>
42 #include <rtl/ustring.hxx>
43 #include <svtools/miscopt.hxx>
44 #include <ctype.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <math.h>
49 #include "rangenam.hxx"
50 #include "dbdata.hxx"
51 #include "document.hxx"
52 #include "callform.hxx"
53 #include "addincol.hxx"
54 #include "refupdat.hxx"
55 #include "scresid.hxx"
56 #include "sc.hrc"
57 #include "globstr.hrc"
58 #include "formulacell.hxx"
59 #include "dociter.hxx"
60 #include "docoptio.hxx"
61 #include <formula/errorcodes.hxx>
62 #include "parclass.hxx"
63 #include "autonamecache.hxx"
64 #include "externalrefmgr.hxx"
65 #include "rangeutl.hxx"
66 #include "convuno.hxx"
67 #include "tokenuno.hxx"
68 #include "formulaparserpool.hxx"
69 #include "tokenarray.hxx"
70 #include "scmatrix.hxx"
72 using namespace formula;
73 using namespace ::com::sun::star;
74 using ::std::vector;
76 CharClass* ScCompiler::pCharClassEnglish = NULL;
77 const ScCompiler::Convention* ScCompiler::pConventions[ ] = { NULL, NULL, NULL, NULL, NULL, NULL };
79 enum ScanState
81 ssGetChar,
82 ssGetBool,
83 ssGetValue,
84 ssGetString,
85 ssSkipString,
86 ssGetIdent,
87 ssGetReference,
88 ssSkipReference,
89 ssGetErrorConstant,
90 ssStop
93 static const sal_Char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
95 using namespace ::com::sun::star::i18n;
97 class ScCompilerRecursionGuard
99 private:
100 short& rRecursion;
101 public:
102 ScCompilerRecursionGuard( short& rRec )
103 : rRecursion( rRec ) { ++rRecursion; }
104 ~ScCompilerRecursionGuard() { --rRecursion; }
107 void ScCompiler::fillFromAddInMap( NonConstOpCodeMapPtr xMap,FormulaGrammar::Grammar _eGrammar ) const
109 size_t nSymbolOffset;
110 switch( _eGrammar )
112 case FormulaGrammar::GRAM_PODF:
113 nSymbolOffset = offsetof( AddInMap, pUpper);
114 break;
115 default:
116 case FormulaGrammar::GRAM_ODFF:
117 nSymbolOffset = offsetof( AddInMap, pODFF);
118 break;
119 case FormulaGrammar::GRAM_ENGLISH:
120 nSymbolOffset = offsetof( AddInMap, pEnglish);
121 break;
123 const AddInMap* pMap = GetAddInMap();
124 const AddInMap* const pStop = pMap + GetAddInMapCount();
125 for ( ; pMap < pStop; ++pMap)
127 char const * const * ppSymbol =
128 reinterpret_cast< char const * const * >(
129 reinterpret_cast< char const * >(pMap) + nSymbolOffset);
130 xMap->putExternal( OUString::createFromAscii( *ppSymbol),
131 OUString::createFromAscii( pMap->pOriginal));
135 void ScCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr xMap ) const
137 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
138 long nCount = pColl->GetFuncCount();
139 for (long i=0; i < nCount; ++i)
141 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
142 if (pFuncData)
143 xMap->putExternalSoftly( pFuncData->GetUpperName(),
144 pFuncData->GetOriginalName());
148 void ScCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr xMap ) const
150 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
151 long nCount = pColl->GetFuncCount();
152 for (long i=0; i < nCount; ++i)
154 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
155 if (pFuncData)
157 OUString aName;
158 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
159 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
160 else
161 xMap->putExternalSoftly( pFuncData->GetUpperName(),
162 pFuncData->GetOriginalName());
167 void ScCompiler::DeInit()
169 if (pCharClassEnglish)
171 delete pCharClassEnglish;
172 pCharClassEnglish = NULL;
176 bool ScCompiler::IsEnglishSymbol( const OUString& rName )
178 // function names are always case-insensitive
179 OUString aUpper = ScGlobal::pCharClass->uppercase(rName);
181 // 1. built-in function name
182 OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper );
183 if ( eOp != ocNone )
185 return true;
187 // 2. old add in functions
188 if (ScGlobal::GetFuncCollection()->findByName(aUpper))
190 return true;
193 // 3. new (uno) add in functions
194 OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
195 if (!aIntName.isEmpty())
197 return true;
199 return false; // no valid function name
202 void ScCompiler::InitCharClassEnglish()
204 ::com::sun::star::lang::Locale aLocale( "en", "US", "");
205 pCharClassEnglish = new CharClass(
206 ::comphelper::getProcessComponentContext(), LanguageTag( aLocale));
209 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
211 OSL_ENSURE( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
212 if (eGrammar == GetGrammar())
213 return; // nothing to be done
215 if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
217 meGrammar = eGrammar;
218 mxSymbols = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
220 else
222 FormulaGrammar::Grammar eMyGrammar = eGrammar;
223 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
224 OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
225 OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
226 if (!xMap)
228 xMap = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
229 eMyGrammar = xMap->getGrammar();
232 // Save old grammar for call to SetGrammarAndRefConvention().
233 FormulaGrammar::Grammar eOldGrammar = GetGrammar();
234 // This also sets the grammar associated with the map!
235 SetFormulaLanguage( xMap);
237 // Override if necessary.
238 if (eMyGrammar != GetGrammar())
239 SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
243 void ScCompiler::SetNumberFormatter( SvNumberFormatter* pFormatter )
245 mpFormatter = pFormatter;
248 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
250 if (xMap.get())
252 mxSymbols = xMap;
253 if (mxSymbols->isEnglish())
255 if (!pCharClassEnglish)
256 InitCharClassEnglish();
257 pCharClass = pCharClassEnglish;
259 else
260 pCharClass = ScGlobal::pCharClass;
261 SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
265 void ScCompiler::SetGrammarAndRefConvention(
266 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
268 meGrammar = eNewGrammar; //! SetRefConvention needs the new grammar set!
269 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
270 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
272 if (pDoc)
273 SetRefConvention( pDoc->GetAddressConvention());
274 else
275 SetRefConvention( pConvOOO_A1);
277 else
278 SetRefConvention( eConv );
281 OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
283 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english
286 ScCompiler::Convention::~Convention()
288 delete [] mpCharTable;
289 mpCharTable = NULL;
292 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
294 meConv( eConv )
296 int i;
297 sal_uLong *t= new sal_uLong [128];
299 ScCompiler::pConventions[ meConv ] = this;
300 mpCharTable = t;
302 for (i = 0; i < 128; i++)
303 t[i] = SC_COMPILER_C_ILLEGAL;
305 /* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
306 /* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
307 if (FormulaGrammar::CONV_ODF == meConv)
308 /* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
309 /* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
310 /* # */ t[35] = SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_CHAR_ERRCONST;
311 /* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
312 if (FormulaGrammar::CONV_ODF == meConv)
313 /* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
314 /* % */ t[37] = SC_COMPILER_C_VALUE;
315 /* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
316 /* ' */ t[39] = SC_COMPILER_C_NAME_SEP;
317 /* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
318 /* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
319 /* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
320 /* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
321 /* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE;
322 /* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
323 /* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
324 /* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
326 for (i = 48; i < 58; i++)
327 /* 0-9 */ t[i] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_WORD | SC_COMPILER_C_VALUE | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
329 /* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD;
330 /* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
331 /* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
332 /* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
333 /* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
334 /* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_NAME;
335 /* @ */ // FREE
337 for (i = 65; i < 91; i++)
338 /* A-Z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
340 if (FormulaGrammar::CONV_ODF == meConv)
342 /* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET;
343 /* \ */ // FREE
344 /* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET;
346 else
348 /* [ */ // FREE
349 /* \ */ // FREE
350 /* ] */ // FREE
352 /* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
353 /* _ */ t[95] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
354 /* ` */ // FREE
356 for (i = 97; i < 123; i++)
357 /* a-z */ t[i] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT | SC_COMPILER_C_CHAR_NAME | SC_COMPILER_C_NAME;
359 /* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open
360 /* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific)
361 /* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close
362 /* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific
363 /* 127 */ // FREE
365 if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
367 /* */ t[32] |= SC_COMPILER_C_WORD;
368 /* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD;
369 /* " */ t[34] |= SC_COMPILER_C_WORD;
370 /* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP);
371 /* # */ t[35] |= SC_COMPILER_C_WORD;
372 /* % */ t[37] |= SC_COMPILER_C_WORD;
373 /* ' */ t[39] |= SC_COMPILER_C_WORD;
375 /* % */ t[37] |= SC_COMPILER_C_WORD;
376 /* & */ t[38] |= SC_COMPILER_C_WORD;
377 /* ' */ t[39] |= SC_COMPILER_C_WORD;
378 /* ( */ t[40] |= SC_COMPILER_C_WORD;
379 /* ) */ t[41] |= SC_COMPILER_C_WORD;
380 /* * */ t[42] |= SC_COMPILER_C_WORD;
381 /* + */ t[43] |= SC_COMPILER_C_WORD;
382 #if 0 /* this really needs to be locale specific. */
383 /* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
384 #else
385 /* , */ t[44] |= SC_COMPILER_C_WORD;
386 #endif
387 /* - */ t[45] |= SC_COMPILER_C_WORD;
389 /* ; */ t[59] |= SC_COMPILER_C_WORD;
390 /* < */ t[60] |= SC_COMPILER_C_WORD;
391 /* = */ t[61] |= SC_COMPILER_C_WORD;
392 /* > */ t[62] |= SC_COMPILER_C_WORD;
393 /* ? */ // question really is not permitted in sheet name
394 /* @ */ t[64] |= SC_COMPILER_C_WORD;
395 /* [ */ t[91] |= SC_COMPILER_C_WORD;
396 /* ] */ t[93] |= SC_COMPILER_C_WORD;
397 /* { */ t[123]|= SC_COMPILER_C_WORD;
398 /* | */ t[124]|= SC_COMPILER_C_WORD;
399 /* } */ t[125]|= SC_COMPILER_C_WORD;
400 /* ~ */ t[126]|= SC_COMPILER_C_WORD;
402 if( FormulaGrammar::CONV_XL_R1C1 == meConv )
404 /* [ */ t[91] |= SC_COMPILER_C_IDENT;
405 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
407 if( FormulaGrammar::CONV_XL_OOX == meConv )
409 /* [ */ t[91] |= SC_COMPILER_C_CHAR_IDENT;
410 /* ] */ t[93] |= SC_COMPILER_C_IDENT;
415 static bool lcl_isValidQuotedText( const OUString& rFormula, sal_Int32 nSrcPos, ParseResult& rRes )
417 // Tokens that start at ' can have anything in them until a final '
418 // but '' marks an escaped '
419 // We've earlier guaranteed that a string containing '' will be
420 // surrounded by '
421 if (rFormula[nSrcPos] == '\'')
423 sal_Int32 nPos = nSrcPos+1;
424 while (nPos < rFormula.getLength())
426 if (rFormula[nPos] == '\'')
428 if ( (nPos+1 == rFormula.getLength()) || (rFormula[nPos+1] != '\'') )
430 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
431 rRes.EndPos = nPos+1;
432 return true;
434 ++nPos;
436 ++nPos;
440 return false;
443 static bool lcl_parseExternalName(
444 const OUString& rSymbol,
445 OUString& rFile,
446 OUString& rName,
447 const sal_Unicode cSep,
448 const ScDocument* pDoc = NULL,
449 const uno::Sequence< const sheet::ExternalLinkInfo > * pExternalLinks = NULL )
451 /* TODO: future versions will have to support sheet-local names too, thus
452 * return a possible sheet name as well. */
453 const sal_Unicode* const pStart = rSymbol.getStr();
454 const sal_Unicode* p = pStart;
455 sal_Int32 nLen = rSymbol.getLength();
456 sal_Unicode cPrev = 0;
457 OUString aTmpFile, aTmpName;
458 sal_Int32 i = 0;
459 bool bInName = false;
460 if (cSep == '!')
462 // For XL use existing parser that resolves bracketed and quoted and
463 // indexed external document names.
464 ScRange aRange;
465 OUString aStartTabName, aEndTabName;
466 sal_uInt16 nFlags = 0;
467 p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
468 aEndTabName, nFlags, true, pExternalLinks );
469 if (!p || p == pStart)
470 return false;
471 i = xub_StrLen(p - pStart);
472 cPrev = *(p-1);
474 for ( ; i < nLen; ++i, ++p)
476 sal_Unicode c = *p;
477 if (i == 0)
479 if (c == '.' || c == cSep)
480 return false;
482 if (c == '\'')
484 // Move to the next char and loop until the second single
485 // quote.
486 cPrev = c;
487 ++i; ++p;
488 for (sal_Int32 j = i; j < nLen; ++j, ++p)
490 c = *p;
491 if (c == '\'')
493 if (j == i)
495 // empty quote e.g. (=''!Name)
496 return false;
499 if (cPrev == '\'')
501 // two consecutive quotes equal a single quote in
502 // the file name.
503 aTmpFile += OUString(c);
504 cPrev = 'a';
506 else
507 cPrev = c;
509 continue;
512 if (cPrev == '\'' && j != i)
514 // this is not a quote but the previous one is. This
515 // ends the parsing of the quoted segment. At this
516 // point, the current char must equal the separator
517 // char.
519 i = j;
520 bInName = true;
521 aTmpName += OUString(c); // Keep the separator as part of the name.
522 break;
524 aTmpFile += OUString(c);
525 cPrev = c;
528 if (!bInName)
530 // premature ending of the quoted segment.
531 return false;
534 if (c != cSep)
536 // only the separator is allowed after the closing quote.
537 return false;
540 cPrev = c;
541 continue;
545 if (bInName)
547 if (c == cSep)
549 // A second separator ? Not a valid external name.
550 return false;
552 aTmpName += OUString(c);
554 else
556 if (c == cSep)
558 bInName = true;
559 aTmpName += OUString(c); // Keep the separator as part of the name.
561 else
565 if (rtl::isAsciiAlphanumeric(c))
566 // allowed.
567 break;
569 if (c > 128)
570 // non-ASCII character is allowed.
571 break;
573 bool bValid = false;
574 switch (c)
576 case '_':
577 case '-':
578 case '.':
579 // these special characters are allowed.
580 bValid = true;
581 break;
583 if (bValid)
584 break;
586 return false;
588 while (false);
589 aTmpFile += OUString(c);
592 cPrev = c;
595 if (!bInName)
597 // No name found - most likely the symbol has no '!'s.
598 return false;
601 sal_Int32 nNameLen = aTmpName.getLength();
602 if (nNameLen < 2)
604 // Name must be at least 2-char long (separator plus name).
605 return false;
608 if (aTmpName[0] != cSep)
610 // 1st char of the name must equal the separator.
611 return false;
614 if (aTmpName[nNameLen-1] == '!')
616 // Check against #REF!.
617 if (aTmpName.equalsAscii("#REF!"))
618 return false;
621 rFile = aTmpFile;
622 rName = aTmpName.copy(1); // Skip the first char as it is always the separator.
623 return true;
626 static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
627 const sal_Unicode cSep, bool bODF )
629 OUString aEscQuote("''");
630 OUString aFile(rFile.replaceAll("'", aEscQuote));
631 OUString aName(rName);
632 if (bODF)
633 aName = aName.replaceAll("'", aEscQuote);
634 OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
635 if (bODF)
636 aBuf.append( '[');
637 aBuf.append( "'" + aFile + "'" + OUString(cSep));
638 if (bODF)
639 aBuf.append( "$$'" );
640 aBuf.append( aName);
641 if (bODF)
642 aBuf.append( "']" );
643 return aBuf.makeStringAndClear();
646 static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
647 const vector<OUString>& rTabNames, const ScRange& rRef )
649 SCsTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
650 if (nTabSpan > 0)
652 size_t nCount = rTabNames.size();
653 vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
654 vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
655 if (itr == rTabNames.end())
657 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
658 return false;
661 size_t nDist = ::std::distance(itrBeg, itr);
662 if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
664 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
665 return false;
668 rTabName2 = rTabNames[nDist+nTabSpan];
670 else
671 rTabName2 = rTabName1;
673 return true;
676 struct Convention_A1 : public ScCompiler::Convention
678 Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
679 static void MakeColStr( OUStringBuffer& rBuffer, SCCOL nCol );
680 static void MakeRowStr( OUStringBuffer& rBuffer, SCROW nRow );
682 ParseResult parseAnyToken( const OUString& rFormula,
683 sal_Int32 nSrcPos,
684 const CharClass* pCharClass) const
686 ParseResult aRet;
687 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
688 return aRet;
690 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
691 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
692 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
693 // '?' allowed in range names because of Xcl :-/
694 static const OUString aAddAllowed("?#");
695 return pCharClass->parseAnyToken( rFormula,
696 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
699 virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const
701 return mpCharTable[static_cast<sal_uInt8>(c)];
705 void Convention_A1::MakeColStr( OUStringBuffer& rBuffer, SCCOL nCol )
707 if ( !ValidCol( nCol) )
708 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
709 else
710 ::ScColToAlpha( rBuffer, nCol);
713 void Convention_A1::MakeRowStr( OUStringBuffer& rBuffer, SCROW nRow )
715 if ( !ValidRow(nRow) )
716 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
717 else
718 rBuffer.append(sal_Int32(nRow + 1));
721 struct ConventionOOO_A1 : public Convention_A1
723 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
724 ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
726 static OUString MakeTabStr( const std::vector<OUString>& rTabNames, SCTAB nTab )
728 OUString aString;
729 if (static_cast<size_t>(nTab) >= rTabNames.size())
730 aString = ScGlobal::GetRscString(STR_NO_REF_TABLE);
731 else
732 aString = rTabNames[nTab];
733 aString += ".";
734 return aString;
737 void MakeOneRefStrImpl(
738 OUStringBuffer& rBuffer,
739 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
740 const ScSingleRefData& rRef, const ScAddress& rAbsRef,
741 bool bForceTab, bool bODF ) const
743 if( rRef.IsFlag3D() || bForceTab )
745 if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
747 if (!rRef.IsTabRel())
748 rBuffer.append('$');
749 rBuffer.append(rErrRef);
750 rBuffer.append('.');
752 else
754 OUString aRefStr(MakeTabStr(rTabNames, rAbsRef.Tab()));
755 if (!rRef.IsTabRel())
756 rBuffer.append('$');
757 rBuffer.append(aRefStr);
760 else if (bODF)
761 rBuffer.append('.');
762 if (!rRef.IsColRel())
763 rBuffer.append('$');
764 if (!ValidCol(rAbsRef.Col()))
765 rBuffer.append(rErrRef);
766 else
767 MakeColStr(rBuffer, rAbsRef.Col());
768 if (!rRef.IsRowRel())
769 rBuffer.append('$');
770 if (!ValidRow(rAbsRef.Row()))
771 rBuffer.append(rErrRef);
772 else
773 MakeRowStr(rBuffer, rAbsRef.Row());
776 void makeRefStr( OUStringBuffer& rBuffer,
777 formula::FormulaGrammar::Grammar /*eGram*/,
778 const ScAddress& rPos,
779 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
780 const ScComplexRefData& rRef,
781 bool bSingleRef ) const
783 ScComplexRefData aRef( rRef );
784 // In case absolute/relative positions weren't separately available:
785 // transform relative to absolute!
786 ScAddress aAbs1 = aRef.Ref1.toAbs(rPos), aAbs2;
787 if( !bSingleRef )
788 aAbs2 = aRef.Ref2.toAbs(rPos);
790 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, aRef.Ref1, aAbs1, false, false);
791 if (!bSingleRef)
793 rBuffer.append(':');
794 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, aRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false);
798 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
800 switch (eSymType)
802 case ScCompiler::Convention::ABS_SHEET_PREFIX:
803 return '$';
804 case ScCompiler::Convention::SHEET_SEPARATOR:
805 return '.';
808 return sal_Unicode(0);
811 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
812 const ScDocument* pDoc,
813 const ::com::sun::star::uno::Sequence<
814 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
816 return lcl_parseExternalName(rSymbol, rFile, rName, '#', pDoc, pExternalLinks);
819 virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const
821 return lcl_makeExternalNameStr( rFile, rName, '#', false);
824 bool makeExternalSingleRefStr(
825 OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
826 const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl ) const
828 ScAddress aAbsRef = rRef.toAbs(rPos);
829 if (bDisplayTabName)
831 OUString aFile;
832 if (bEncodeUrl)
833 aFile = rFileName;
834 else
835 aFile = INetURLObject::decode(rFileName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
837 rBuffer.append("'" + aFile.replaceAll("'", "''") + "'#");
839 if (!rRef.IsTabRel())
840 rBuffer.append('$');
841 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
843 rBuffer.append('.');
846 if (!rRef.IsColRel())
847 rBuffer.append('$');
848 MakeColStr( rBuffer, aAbsRef.Col());
849 if (!rRef.IsRowRel())
850 rBuffer.append('$');
851 MakeRowStr( rBuffer, aAbsRef.Row());
853 return true;
856 void makeExternalRefStrImpl(
857 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
858 const OUString& rTabName, const ScSingleRefData& rRef, bool bODF ) const
860 if (bODF)
861 rBuffer.append( '[');
863 bool bEncodeUrl = bODF;
864 makeExternalSingleRefStr(rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
865 if (bODF)
866 rBuffer.append( ']');
869 virtual void makeExternalRefStr(
870 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
871 const OUString& rTabName, const ScSingleRefData& rRef ) const
873 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, false);
876 void makeExternalRefStrImpl(
877 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
878 const std::vector<OUString>& rTabNames, const OUString& rTabName,
879 const ScComplexRefData& rRef, bool bODF ) const
881 ScRange aAbsRange = rRef.toAbs(rPos);
883 if (bODF)
884 rBuffer.append( '[');
885 // Ensure that there's always a closing bracket, no premature returns.
886 bool bEncodeUrl = bODF;
890 if (!makeExternalSingleRefStr(rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
891 break;
893 rBuffer.append(':');
895 OUString aLastTabName;
896 bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
897 if (bDisplayTabName)
899 // Get the name of the last table.
900 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
902 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
903 // aLastTabName contains #REF!, proceed.
906 else if (bODF)
907 rBuffer.append( '.'); // need at least the sheet separator in ODF
908 makeExternalSingleRefStr(
909 rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
910 } while (0);
912 if (bODF)
913 rBuffer.append( ']');
916 virtual void makeExternalRefStr(
917 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
918 const std::vector<OUString>& rTabNames, const OUString& rTabName,
919 const ScComplexRefData& rRef ) const
921 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
925 static const ConventionOOO_A1 ConvOOO_A1;
926 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1 = &ConvOOO_A1;
928 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
930 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
931 void makeRefStr( OUStringBuffer& rBuffer,
932 formula::FormulaGrammar::Grammar eGram,
933 const ScAddress& rPos,
934 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
935 const ScComplexRefData& rRef,
936 bool bSingleRef ) const
938 rBuffer.append(sal_Unicode('['));
939 ScComplexRefData aRef( rRef );
940 // In case absolute/relative positions weren't separately available:
941 // transform relative to absolute!
942 ScAddress aAbs1 = aRef.Ref1.toAbs(rPos), aAbs2;
943 if( !bSingleRef )
944 aAbs2 = aRef.Ref2.toAbs(rPos);
946 if (FormulaGrammar::isODFF(eGram) && (!ValidAddress(aAbs1) || !ValidAddress(aAbs2)))
948 rBuffer.append(rErrRef);
949 // For ODFF write [#REF!], but not for PODF so apps reading ODF
950 // 1.0/1.1 may have a better chance if they implemented the old
951 // form.
953 else
955 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, aRef.Ref1, aAbs1, false, true);
956 if (!bSingleRef)
958 rBuffer.append(sal_Unicode(':'));
959 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, aRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true);
962 rBuffer.append(sal_Unicode(']'));
965 virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const
967 return lcl_makeExternalNameStr( rFile, rName, '#', true);
970 virtual void makeExternalRefStr(
971 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
972 const OUString& rTabName, const ScSingleRefData& rRef ) const
974 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, true);
977 virtual void makeExternalRefStr(
978 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
979 const std::vector<OUString>& rTabNames,
980 const OUString& rTabName, const ScComplexRefData& rRef ) const
982 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
986 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
987 const ScCompiler::Convention * const ScCompiler::pConvOOO_A1_ODF = &ConvOOO_A1_ODF;
989 struct ConventionXL
991 static void GetTab(
992 const ScAddress& rPos, const std::vector<OUString>& rTabNames,
993 const ScSingleRefData& rRef, OUString& rTabName )
995 ScAddress aAbs = rRef.toAbs(rPos);
996 if (rRef.IsTabDeleted() || static_cast<size_t>(aAbs.Tab()) >= rTabNames.size())
998 rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE );
999 return;
1001 rTabName = rTabNames[aAbs.Tab()];
1004 static void MakeTabStr( OUStringBuffer& rBuf,
1005 const ScAddress& rPos,
1006 const std::vector<OUString>& rTabNames,
1007 const ScComplexRefData& rRef,
1008 bool bSingleRef )
1010 if( rRef.Ref1.IsFlag3D() )
1012 OUString aStartTabName, aEndTabName;
1014 GetTab(rPos, rTabNames, rRef.Ref1, aStartTabName);
1016 if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1018 GetTab(rPos, rTabNames, rRef.Ref2, aEndTabName);
1021 rBuf.append( aStartTabName );
1022 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1024 rBuf.append( ':' );
1025 rBuf.append( aEndTabName );
1028 rBuf.append( '!' );
1032 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1034 switch (eSymType)
1036 case ScCompiler::Convention::ABS_SHEET_PREFIX:
1037 return sal_Unicode(0);
1038 case ScCompiler::Convention::SHEET_SEPARATOR:
1039 return '!';
1041 return sal_Unicode(0);
1044 static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1045 const ScDocument* pDoc,
1046 const ::com::sun::star::uno::Sequence<
1047 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks )
1049 return lcl_parseExternalName( rSymbol, rFile, rName, '!', pDoc, pExternalLinks);
1052 static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
1054 return lcl_makeExternalNameStr( rFile, rName, '!', false);
1057 static void makeExternalDocStr( OUStringBuffer& rBuffer, const OUString& rFullName, bool bEncodeUrl )
1059 // Format that is easier to deal with inside OOo, because we use file
1060 // URL, and all characetrs are allowed. Check if it makes sense to do
1061 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1062 // and allows relative file path.
1064 // ['file:///path/to/source/filename.xls']
1066 rBuffer.append('[');
1067 rBuffer.append('\'');
1068 OUString aFullName;
1069 if (bEncodeUrl)
1070 aFullName = rFullName;
1071 else
1072 aFullName = INetURLObject::decode(rFullName, INET_HEX_ESCAPE, INetURLObject::DECODE_UNAMBIGUOUS);
1074 const sal_Unicode* pBuf = aFullName.getStr();
1075 sal_Int32 nLen = aFullName.getLength();
1076 for (sal_Int32 i = 0; i < nLen; ++i)
1078 const sal_Unicode c = pBuf[i];
1079 if (c == '\'')
1080 rBuffer.append(c);
1081 rBuffer.append(c);
1083 rBuffer.append('\'');
1084 rBuffer.append(']');
1087 static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
1088 const vector<OUString>& rTabNames,
1089 const ScRange& rRef )
1091 OUString aLastTabName;
1092 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1094 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1095 return;
1098 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1099 if (rTabName != aLastTabName)
1101 rBuf.append(':');
1102 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1106 static void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos )
1108 sal_Int32 nLen = rFormula.getLength();
1109 const sal_Unicode* p = rFormula.getStr();
1110 sal_Unicode cPrev = 0;
1111 for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1113 sal_Unicode c = p[i];
1114 if (i == rSrcPos)
1116 // first character must be '['.
1117 if (c != '[')
1118 return;
1120 else if (i == rSrcPos + 1)
1122 // second character must be a single quote.
1123 if (c != '\'')
1124 return;
1126 else if (c == '\'')
1128 if (cPrev == '\'')
1129 // two successive single quote is treated as a single
1130 // valid character.
1131 c = 'a';
1133 else if (c == ']')
1135 if (cPrev == '\'')
1137 // valid source document path found. Increment the
1138 // current position to skip the source path.
1139 rSrcPos = i + 1;
1140 if (rSrcPos >= nLen)
1141 rSrcPos = nLen - 1;
1142 return;
1144 else
1145 return;
1147 else
1149 // any other character
1150 if (i > rSrcPos + 2 && cPrev == '\'')
1151 // unless it's the 3rd character, a normal character
1152 // following immediately a single quote is invalid.
1153 return;
1155 cPrev = c;
1160 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1162 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1163 ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1165 void makeSingleCellStr( OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs ) const
1167 if (!rRef.IsColRel())
1168 rBuf.append('$');
1169 MakeColStr(rBuf, rAbs.Col());
1170 if (!rRef.IsRowRel())
1171 rBuf.append('$');
1172 MakeRowStr(rBuf, rAbs.Row());
1175 void makeRefStr( OUStringBuffer& rBuf,
1176 formula::FormulaGrammar::Grammar /*eGram*/,
1177 const ScAddress& rPos,
1178 const OUString& /*rErrRef*/, const std::vector<OUString>& rTabNames,
1179 const ScComplexRefData& rRef,
1180 bool bSingleRef ) const
1182 ScComplexRefData aRef( rRef );
1184 // Play fast and loose with invalid refs. There is not much point in producing
1185 // Foo!A1:#REF! versus #REF! at this point
1186 ScAddress aAbs1 = aRef.Ref1.toAbs(rPos), aAbs2;
1188 MakeTabStr(rBuf, rPos, rTabNames, aRef, bSingleRef);
1190 if (!ValidAddress(aAbs1))
1192 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1193 return;
1196 if( !bSingleRef )
1198 aAbs2 = aRef.Ref2.toAbs(rPos);
1199 if (!ValidAddress(aAbs2))
1201 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1202 return;
1205 if (aAbs1.Col() == 0 && aAbs2.Col() >= MAXCOL)
1207 if (!aRef.Ref1.IsRowRel())
1208 rBuf.append( '$' );
1209 MakeRowStr(rBuf, aAbs1.Row());
1210 rBuf.append( ':' );
1211 if (!aRef.Ref2.IsRowRel())
1212 rBuf.append( '$' );
1213 MakeRowStr(rBuf, aAbs2.Row());
1214 return;
1217 if (aAbs1.Row() == 0 && aAbs2.Row() >= MAXROW)
1219 if (!aRef.Ref1.IsColRel())
1220 rBuf.append( '$' );
1221 MakeColStr(rBuf, aAbs1.Col());
1222 rBuf.append( ':' );
1223 if (!aRef.Ref2.IsColRel())
1224 rBuf.append( '$' );
1225 MakeColStr(rBuf, aAbs2.Col());
1226 return;
1230 makeSingleCellStr(rBuf, aRef.Ref1, aAbs1);
1231 if (!bSingleRef)
1233 rBuf.append( ':' );
1234 makeSingleCellStr(rBuf, aRef.Ref2, aAbs2);
1238 virtual ParseResult parseAnyToken( const OUString& rFormula,
1239 sal_Int32 nSrcPos,
1240 const CharClass* pCharClass) const
1242 ParseResult aRet;
1243 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1244 return aRet;
1246 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1247 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1248 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1249 // '?' allowed in range names
1250 const OUString aAddAllowed("?!");
1251 return pCharClass->parseAnyToken( rFormula,
1252 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1255 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1257 return ConventionXL::getSpecialSymbol(eSymType);
1260 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1261 const ScDocument* pDoc,
1262 const ::com::sun::star::uno::Sequence<
1263 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1265 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1268 virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const
1270 return ConventionXL::makeExternalNameStr(rFile, rName);
1273 virtual void makeExternalRefStr(
1274 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
1275 const OUString& rTabName, const ScSingleRefData& rRef ) const
1277 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1278 // This is a little different from the format Excel uses, as Excel
1279 // puts [] only around the file name. But we need to enclose the
1280 // whole file path with [] because the file name can contain any
1281 // characters.
1283 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1284 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1285 rBuffer.append('!');
1287 makeSingleCellStr(rBuffer, rRef, rRef.toAbs(rPos));
1290 virtual void makeExternalRefStr(
1291 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
1292 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1293 const ScComplexRefData& rRef ) const
1295 ScRange aAbsRef = rRef.toAbs(rPos);
1297 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1298 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1299 rBuffer.append('!');
1301 makeSingleCellStr(rBuffer, rRef.Ref1, aAbsRef.aStart);
1302 if (aAbsRef.aStart != aAbsRef.aEnd)
1304 rBuffer.append(':');
1305 makeSingleCellStr(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1310 static const ConventionXL_A1 ConvXL_A1;
1311 const ScCompiler::Convention * const ScCompiler::pConvXL_A1 = &ConvXL_A1;
1313 struct ConventionXL_OOX : public ConventionXL_A1
1315 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1318 static const ConventionXL_OOX ConvXL_OOX;
1319 const ScCompiler::Convention * const ScCompiler::pConvXL_OOX = &ConvXL_OOX;
1321 static void
1322 r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1324 rBuf.append( 'C' );
1325 if( rRef.IsColRel() )
1327 SCCOL nCol = rRef.Col();
1328 if (nCol != 0)
1329 rBuf.append("[").append(OUString::number(nCol)).append("]");
1331 else
1332 rBuf.append( OUString::number( rAbsRef.Col() + 1 ) );
1334 static void
1335 r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1337 rBuf.append( 'R' );
1338 if( rRef.IsRowRel() )
1340 if (rRef.Row() != 0)
1342 rBuf.append("[").append( OUString::number(rRef.Row()) ).append("]");
1345 else
1346 rBuf.append( OUString::number( rAbsRef.Row() + 1 ) );
1349 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1351 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1352 void makeRefStr( OUStringBuffer& rBuf,
1353 formula::FormulaGrammar::Grammar /*eGram*/,
1354 const ScAddress& rPos,
1355 const OUString& /*rErrRef*/, const std::vector<OUString>& rTabNames,
1356 const ScComplexRefData& rRef,
1357 bool bSingleRef ) const
1359 ScRange aAbsRef = rRef.toAbs(rPos);
1360 ScComplexRefData aRef( rRef );
1362 MakeTabStr(rBuf, rPos, rTabNames, aRef, bSingleRef);
1364 // Play fast and loose with invalid refs. There is not much point in producing
1365 // Foo!A1:#REF! versus #REF! at this point
1366 if (!ValidCol(aAbsRef.aStart.Col()) || !ValidRow(aAbsRef.aStart.Row()))
1368 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1369 return;
1372 if( !bSingleRef )
1374 if (!ValidCol(aAbsRef.aEnd.Col()) || !ValidRow(aAbsRef.aEnd.Row()))
1376 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1377 return;
1380 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= MAXCOL)
1382 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1383 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
1384 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
1386 rBuf.append( ':' );
1387 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1389 return;
1393 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= MAXROW)
1395 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1396 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
1397 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1399 rBuf.append( ':' );
1400 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1402 return;
1406 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1407 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1408 if (!bSingleRef)
1410 rBuf.append( ':' );
1411 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1412 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1416 ParseResult parseAnyToken( const OUString& rFormula,
1417 sal_Int32 nSrcPos,
1418 const CharClass* pCharClass) const
1420 ConventionXL::parseExternalDocName(rFormula, nSrcPos);
1422 ParseResult aRet;
1423 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1424 return aRet;
1426 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1427 KParseTokens::ASC_UNDERSCORE ;
1428 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1429 // '?' allowed in range names
1430 const OUString aAddAllowed("?-[]!");
1432 return pCharClass->parseAnyToken( rFormula,
1433 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1436 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const
1438 return ConventionXL::getSpecialSymbol(eSymType);
1441 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1442 const ScDocument* pDoc,
1443 const ::com::sun::star::uno::Sequence<
1444 const ::com::sun::star::sheet::ExternalLinkInfo > * pExternalLinks ) const
1446 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1449 virtual OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) const
1451 return ConventionXL::makeExternalNameStr(rFile, rName);
1454 virtual void makeExternalRefStr(
1455 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
1456 const OUString& rTabName, const ScSingleRefData& rRef ) const
1458 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1459 // This is a little different from the format Excel uses, as Excel
1460 // puts [] only around the file name. But we need to enclose the
1461 // whole file path with [] because the file name can contain any
1462 // characters.
1464 ScAddress aAbsRef = rRef.toAbs(rPos);
1465 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1466 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1467 rBuffer.append('!');
1469 r1c1_add_row(rBuffer, rRef, aAbsRef);
1470 r1c1_add_col(rBuffer, rRef, aAbsRef);
1473 virtual void makeExternalRefStr(
1474 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
1475 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1476 const ScComplexRefData& rRef ) const
1478 ScRange aAbsRef = rRef.toAbs(rPos);
1480 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1481 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1482 rBuffer.append('!');
1484 if (!ValidCol(aAbsRef.aEnd.Col()) || !ValidRow(aAbsRef.aEnd.Row()))
1486 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1487 return;
1490 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= MAXCOL)
1492 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1493 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1495 rBuffer.append(':');
1496 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1498 return;
1501 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= MAXROW)
1503 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1504 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1506 rBuffer.append(':');
1507 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1509 return;
1512 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1513 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1514 rBuffer.append(':');
1515 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1516 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1519 virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const
1521 sal_uLong nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
1522 if (c == '-' && cLast == '[')
1523 // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1524 nFlags |= SC_COMPILER_C_IDENT;
1525 return nFlags;
1529 static const ConventionXL_R1C1 ConvXL_R1C1;
1530 const ScCompiler::Convention * const ScCompiler::pConvXL_R1C1 = &ConvXL_R1C1;
1532 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr)
1533 : FormulaCompiler(rArr),
1534 pDoc( pDocument ),
1535 aPos( rPos ),
1536 mpFormatter(pDoc->GetFormatTable()),
1537 pCharClass( ScGlobal::pCharClass ),
1538 mnPredetectedReference(0),
1539 mnRangeOpPosInSymbol(-1),
1540 pConv( pConvOOO_A1 ),
1541 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1542 mbCloseBrackets( true ),
1543 mbRewind( false )
1545 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1547 if (pDoc)
1549 maTabNames = pDoc->GetAllTableNames();
1551 std::vector<OUString>::iterator it = maTabNames.begin(), itEnd = maTabNames.end();
1552 for (; it != itEnd; ++it)
1553 ScCompiler::CheckTabQuotes(*it, formula::FormulaGrammar::extractRefConvention(meGrammar));
1558 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos)
1560 pDoc( pDocument ),
1561 aPos( rPos ),
1562 mpFormatter(pDoc ? pDoc->GetFormatTable() : NULL),
1563 pCharClass( ScGlobal::pCharClass ),
1564 mnPredetectedReference(0),
1565 mnRangeOpPosInSymbol(-1),
1566 pConv( pConvOOO_A1 ),
1567 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1568 mbCloseBrackets( true ),
1569 mbRewind( false )
1571 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1573 if (pDoc)
1575 maTabNames = pDoc->GetAllTableNames();
1577 std::vector<OUString>::iterator it = maTabNames.begin(), itEnd = maTabNames.end();
1578 for (; it != itEnd; ++it)
1579 ScCompiler::CheckTabQuotes(*it, formula::FormulaGrammar::extractRefConvention(meGrammar));
1584 ScCompiler::~ScCompiler()
1588 void ScCompiler::CheckTabQuotes( OUString& rString,
1589 const FormulaGrammar::AddressConvention eConv )
1591 using namespace ::com::sun::star::i18n;
1592 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1593 sal_Int32 nContFlags = nStartFlags;
1594 ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
1595 KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_OUSTRING, nContFlags, EMPTY_OUSTRING);
1596 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
1598 switch ( eConv )
1600 default :
1601 case FormulaGrammar::CONV_UNSPECIFIED :
1602 break;
1603 case FormulaGrammar::CONV_OOO :
1604 case FormulaGrammar::CONV_XL_A1 :
1605 case FormulaGrammar::CONV_XL_R1C1 :
1606 case FormulaGrammar::CONV_XL_OOX :
1607 if( bNeedsQuote )
1609 const OUString one_quote('\'');
1610 const OUString two_quote("''");
1611 // escape embedded quotes
1612 rString = rString.replaceAll( one_quote, two_quote );
1614 break;
1617 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1619 // Prevent any possible confusion resulting from pure numeric sheet names.
1620 bNeedsQuote = true;
1623 if( bNeedsQuote )
1625 rString = "'" + rString + "'";
1629 sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
1631 if (rString[0] != '\'')
1632 return -1;
1633 sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
1634 // it must be 'Doc'#
1635 if (nPos != -1 && rString[nPos-1] != '\'')
1636 nPos = -1;
1637 return nPos;
1640 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
1642 const Convention* p = GetRefConvention(eConv);
1643 if (p)
1644 SetRefConvention(p);
1647 const ScCompiler::Convention* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv )
1649 switch (eConv)
1651 case FormulaGrammar::CONV_OOO:
1652 return pConvOOO_A1;
1653 case FormulaGrammar::CONV_ODF:
1654 return pConvOOO_A1_ODF;
1655 case FormulaGrammar::CONV_XL_A1:
1656 return pConvXL_A1;
1657 case FormulaGrammar::CONV_XL_R1C1:
1658 return pConvXL_R1C1;
1659 case FormulaGrammar::CONV_XL_OOX:
1660 return pConvXL_OOX;
1661 case FormulaGrammar::CONV_UNSPECIFIED:
1662 default:
1666 return NULL;
1669 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
1671 pConv = pConvP;
1672 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
1673 OSL_ENSURE( FormulaGrammar::isSupported( meGrammar),
1674 "ScCompiler::SetRefConvention: unsupported grammar resulting");
1677 void ScCompiler::SetError(sal_uInt16 nError)
1679 if( !pArr->GetCodeError() )
1680 pArr->SetCodeError( nError);
1683 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
1685 const sal_Unicode* const pStop = pDst + nMax;
1686 while ( *pSrc && pDst < pStop )
1688 *pDst++ = *pSrc++;
1690 *pDst = 0;
1691 return pDst;
1694 // NextSymbol
1696 // Zerlegt die Formel in einzelne Symbole fuer die weitere
1697 // Verarbeitung (Turing-Maschine).
1699 // Ausgangs Zustand = GetChar
1701 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1702 //---------------+-------------------+-----------------------+---------------
1703 // GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop
1704 // | <> | Symbol=Zeichen | GetBool
1705 // | $ Buchstabe | Symbol=Zeichen | GetWord
1706 // | Ziffer | Symbol=Zeichen | GetValue
1707 // | " | Keine | GetString
1708 // | Sonst | Keine | GetChar
1709 //---------------+-------------------+-----------------------+---------------
1710 // GetBool | => | Symbol=Symbol+Zeichen | Stop
1711 // | Sonst | Dec(CharPos) | Stop
1712 //---------------+-------------------+-----------------------+---------------
1713 // GetWord | SepSymbol | Dec(CharPos) | Stop
1714 // | ()+-*/^=<>&~ | |
1715 // | Leerzeichen | Dec(CharPos) | Stop
1716 // | $_:. | |
1717 // | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord
1718 // | Sonst | Fehler | Stop
1719 //---------------+-------------------+-----------------------+---------------
1720 // GetValue | ;()*/^=<>& | |
1721 // | Leerzeichen | Dec(CharPos) | Stop
1722 // | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue
1723 // | Sonst | Fehler | Stop
1724 //---------------+-------------------+-----------------------+---------------
1725 // GetString | " | Keine | Stop
1726 // | Sonst | Symbol=Symbol+Zeichen | GetString
1727 //---------------+-------------------+-----------------------+---------------
1729 xub_StrLen ScCompiler::NextSymbol(bool bInArray)
1731 cSymbol[MAXSTRLEN-1] = 0; // Stopper
1732 sal_Unicode* pSym = cSymbol;
1733 const sal_Unicode* const pStart = aFormula.getStr();
1734 const sal_Unicode* pSrc = pStart + nSrcPos;
1735 bool bi18n = false;
1736 sal_Unicode c = *pSrc;
1737 sal_Unicode cLast = 0;
1738 bool bQuote = false;
1739 mnRangeOpPosInSymbol = -1;
1740 ScanState eState = ssGetChar;
1741 xub_StrLen nSpaces = 0;
1742 sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
1743 sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
1744 sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
1745 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' :
1746 ScGlobal::pLocaleData->getNumDecimalSep()[0]);
1748 // special symbols specific to address convention used
1749 sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
1750 sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
1752 int nDecSeps = 0;
1753 bool bAutoIntersection = false;
1754 int nRefInName = 0;
1755 bool bErrorConstantHadSlash = false;
1756 mnPredetectedReference = 0;
1757 // try to parse simple tokens before calling i18n parser
1758 while ((c != 0) && (eState != ssStop) )
1760 pSrc++;
1761 sal_uLong nMask = GetCharTableFlags( c, cLast );
1763 // The parameter separator and the array column and row separators end
1764 // things unconditionally if not in string or reference.
1765 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
1767 switch (eState)
1769 // these are to be continued
1770 case ssGetString:
1771 case ssSkipString:
1772 case ssGetReference:
1773 case ssSkipReference:
1774 break;
1775 default:
1776 if (eState == ssGetChar)
1777 *pSym++ = c;
1778 else
1779 pSrc--;
1780 eState = ssStop;
1783 Label_MaskStateMachine:
1784 switch (eState)
1786 case ssGetChar :
1788 // Order is important!
1789 if( nMask & SC_COMPILER_C_ODF_LABEL_OP )
1791 // '!!' automatic intersection
1792 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_ODF_LABEL_OP)
1794 /* TODO: For now the UI "space operator" is used, this
1795 * could be enhanced using a specialized OpCode to get
1796 * rid of the space ambiguity, which would need some
1797 * places to be adapted though. And we would still need
1798 * to support the ambiguous space operator for UI
1799 * purposes anyway. However, we then could check for
1800 * invalid usage of '!!', which currently isn't
1801 * possible. */
1802 if (!bAutoIntersection)
1804 ++pSrc;
1805 nSpaces += 2; // must match the character count
1806 bAutoIntersection = true;
1808 else
1810 pSrc--;
1811 eState = ssStop;
1814 else
1816 nMask &= ~SC_COMPILER_C_ODF_LABEL_OP;
1817 goto Label_MaskStateMachine;
1820 else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER )
1822 // '$$' defined name marker
1823 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_ODF_NAME_MARKER)
1825 // both eaten, not added to pSym
1826 ++pSrc;
1828 else
1830 nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER;
1831 goto Label_MaskStateMachine;
1834 else if( nMask & SC_COMPILER_C_CHAR )
1836 *pSym++ = c;
1837 eState = ssStop;
1839 else if( nMask & SC_COMPILER_C_ODF_LBRACKET )
1841 // eaten, not added to pSym
1842 eState = ssGetReference;
1843 mnPredetectedReference = 1;
1845 else if( nMask & SC_COMPILER_C_CHAR_BOOL )
1847 *pSym++ = c;
1848 eState = ssGetBool;
1850 else if( nMask & SC_COMPILER_C_CHAR_VALUE )
1852 *pSym++ = c;
1853 eState = ssGetValue;
1855 else if( nMask & SC_COMPILER_C_CHAR_STRING )
1857 *pSym++ = c;
1858 eState = ssGetString;
1860 else if( nMask & SC_COMPILER_C_CHAR_ERRCONST )
1862 *pSym++ = c;
1863 eState = ssGetErrorConstant;
1865 else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
1867 nSpaces++;
1869 else if( nMask & SC_COMPILER_C_CHAR_IDENT )
1870 { // try to get a simple ASCII identifier before calling
1871 // i18n, to gain performance during import
1872 *pSym++ = c;
1873 eState = ssGetIdent;
1875 else
1877 bi18n = true;
1878 eState = ssStop;
1881 break;
1882 case ssGetIdent:
1884 if ( nMask & SC_COMPILER_C_IDENT )
1885 { // This catches also $Sheet1.A$1, for example.
1886 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
1888 SetError(errStringOverflow);
1889 eState = ssStop;
1891 else
1892 *pSym++ = c;
1894 else if (c == ':' && mnRangeOpPosInSymbol < 0)
1896 // One range operator may form Sheet1.A:A, which we need to
1897 // pass as one entity to IsReference().
1898 mnRangeOpPosInSymbol = pSym - &cSymbol[0];
1899 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
1901 SetError(errStringOverflow);
1902 eState = ssStop;
1904 else
1905 *pSym++ = c;
1907 else if ( 128 <= c || '\'' == c )
1908 { // High values need reparsing with i18n,
1909 // single quoted $'sheet' names too (otherwise we'd had to
1910 // implement everything twice).
1911 bi18n = true;
1912 eState = ssStop;
1914 else
1916 pSrc--;
1917 eState = ssStop;
1920 break;
1921 case ssGetBool :
1923 if( nMask & SC_COMPILER_C_BOOL )
1925 *pSym++ = c;
1926 eState = ssStop;
1928 else
1930 pSrc--;
1931 eState = ssStop;
1934 break;
1935 case ssGetValue :
1937 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
1939 SetError(errStringOverflow);
1940 eState = ssStop;
1942 else if (c == cDecSep)
1944 if (++nDecSeps > 1)
1946 // reparse with i18n, may be numeric sheet name as well
1947 bi18n = true;
1948 eState = ssStop;
1950 else
1951 *pSym++ = c;
1953 else if( nMask & SC_COMPILER_C_VALUE )
1954 *pSym++ = c;
1955 else if( nMask & SC_COMPILER_C_VALUE_SEP )
1957 pSrc--;
1958 eState = ssStop;
1960 else if (c == 'E' || c == 'e')
1962 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_VALUE_EXP)
1963 *pSym++ = c;
1964 else
1966 // reparse with i18n
1967 bi18n = true;
1968 eState = ssStop;
1971 else if( nMask & SC_COMPILER_C_VALUE_SIGN )
1973 if (((cLast == 'E') || (cLast == 'e')) &&
1974 (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_VALUE_VALUE))
1976 *pSym++ = c;
1978 else
1980 pSrc--;
1981 eState = ssStop;
1984 else
1986 // reparse with i18n
1987 bi18n = true;
1988 eState = ssStop;
1991 break;
1992 case ssGetString :
1994 if( nMask & SC_COMPILER_C_STRING_SEP )
1996 if ( !bQuote )
1998 if ( *pSrc == '"' )
1999 bQuote = true; // "" => literal "
2000 else
2001 eState = ssStop;
2003 else
2004 bQuote = false;
2006 if ( !bQuote )
2008 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2010 SetError(errStringOverflow);
2011 eState = ssSkipString;
2013 else
2014 *pSym++ = c;
2017 break;
2018 case ssSkipString:
2019 if( nMask & SC_COMPILER_C_STRING_SEP )
2020 eState = ssStop;
2021 break;
2022 case ssGetErrorConstant:
2024 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2025 // BUT, in UI these may have been translated! So don't
2026 // check for ASCII alnum. Note that this construct can't be
2027 // parsed with i18n.
2028 /* TODO: be strict when reading ODFF, check for ASCII alnum
2029 * and proper continuation after '/'. However, even with
2030 * the lax parsing only the error constants we have defined
2031 * as opcode symbols will be recognized and others result
2032 * in ocBad, so the result is actually conformant. */
2033 bool bAdd = true;
2034 if ('!' == c || '?' == c)
2035 eState = ssStop;
2036 else if ('/' == c)
2038 if (!bErrorConstantHadSlash)
2039 bErrorConstantHadSlash = true;
2040 else
2042 bAdd = false;
2043 eState = ssStop;
2046 else if ((nMask & SC_COMPILER_C_WORD_SEP) ||
2047 (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2049 bAdd = false;
2050 eState = ssStop;
2052 if (!bAdd)
2053 --pSrc;
2054 else
2056 if (pSym == &cSymbol[ MAXSTRLEN-1 ])
2058 SetError( errStringOverflow);
2059 eState = ssStop;
2061 else
2062 *pSym++ = c;
2065 break;
2066 case ssGetReference:
2067 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2069 SetError( errStringOverflow);
2070 eState = ssSkipReference;
2072 // fall through and follow logic
2073 case ssSkipReference:
2074 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2075 // mandatory also if no sheet name. 'External'# is optional,
2076 // sheet name is optional, quotes around sheet name are
2077 // optional if no quote contained. [#REF!] is valid.
2078 // 2nd usage: ['Sheet'.$$'DefinedName']
2079 // 3rd usage: ['External'#$$'DefinedName']
2080 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2081 // Also for all these names quotes are optional if no quote
2082 // contained.
2085 // nRefInName: 0 := not in sheet name yet. 'External'
2086 // is parsed as if it was a sheet name and nRefInName
2087 // is reset when # is encountered immediately after closing
2088 // quote. Same with 'DefinedName', nRefInName is cleared
2089 // when : is encountered.
2091 // Encountered leading $ before sheet name.
2092 static const int kDollar = (1 << 1);
2093 // Encountered ' opening quote, which may be after $ or
2094 // not.
2095 static const int kOpen = (1 << 2);
2096 // Somewhere in name.
2097 static const int kName = (1 << 3);
2098 // Encountered ' in name, will be cleared if double or
2099 // transformed to kClose if not, in which case kOpen is
2100 // cleared.
2101 static const int kQuote = (1 << 4);
2102 // Past ' closing quote.
2103 static const int kClose = (1 << 5);
2104 // Encountered # file/sheet separator.
2105 static const int kFileSep = (1 << 6);
2106 // Past . sheet name separator.
2107 static const int kPast = (1 << 7);
2108 // Marked name $$ follows sheet name separator, detected
2109 // while we're still on the separator. Will be cleared when
2110 // entering the name.
2111 static const int kMarkAhead = (1 << 8);
2112 // In marked defined name.
2113 static const int kDefName = (1 << 9);
2114 // Encountered # of #REF!
2115 static const int kRefErr = (1 << 10);
2117 bool bAddToSymbol = true;
2118 if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
2120 OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2121 "ScCompiler::NextSymbol: reference: "
2122 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2123 // eaten, not added to pSym
2124 bAddToSymbol = false;
2125 eState = ssStop;
2127 else if (cSheetSep == c && nRefInName == 0)
2129 // eat it, no sheet name [.A1]
2130 bAddToSymbol = false;
2131 nRefInName |= kPast;
2132 if ('$' == pSrc[0] && '$' == pSrc[1])
2133 nRefInName |= kMarkAhead;
2135 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2137 // Not in col/row yet.
2139 if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2140 nRefInName = 0;
2141 else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2143 nRefInName &= ~kMarkAhead;
2144 if (!(nRefInName & kDefName))
2146 // eaten, not added to pSym (2 chars)
2147 bAddToSymbol = false;
2148 ++pSrc;
2149 nRefInName &= kPast;
2150 nRefInName |= kDefName;
2152 else
2154 // ScAddress::Parse() will recognize this as
2155 // invalid later.
2156 if (eState != ssSkipReference)
2158 *pSym++ = c;
2159 *pSym++ = *pSrc++;
2161 bAddToSymbol = false;
2164 else if (cSheetPrefix == c && nRefInName == 0)
2165 nRefInName |= kDollar;
2166 else if ('\'' == c)
2168 // TODO: The conventions' parseExternalName()
2169 // should handle quoted names, but as long as they
2170 // don't remove non-embedded quotes here.
2171 if (!(nRefInName & kName))
2173 nRefInName |= (kOpen | kName);
2174 bAddToSymbol = !(nRefInName & kDefName);
2176 else if (!(nRefInName & kOpen))
2178 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2179 "a ''' without the name being enclosed in '...' violates ODF spec");
2181 else if (nRefInName & kQuote)
2183 // escaped embedded quote
2184 nRefInName &= ~kQuote;
2186 else
2188 switch (pSrc[0])
2190 case '\'':
2191 // escapes embedded quote
2192 nRefInName |= kQuote;
2193 break;
2194 case SC_COMPILER_FILE_TAB_SEP:
2195 // sheet name should follow
2196 nRefInName |= kFileSep;
2197 // fallthru
2198 default:
2199 // quote not followed by quote => close
2200 nRefInName |= kClose;
2201 nRefInName &= ~kOpen;
2203 bAddToSymbol = !(nRefInName & kDefName);
2206 else if ('#' == c && nRefInName == 0)
2207 nRefInName |= kRefErr;
2208 else if (cSheetSep == c && !(nRefInName & kOpen))
2210 // unquoted sheet name separator
2211 nRefInName |= kPast;
2212 if ('$' == pSrc[0] && '$' == pSrc[1])
2213 nRefInName |= kMarkAhead;
2215 else if (':' == c && !(nRefInName & kOpen))
2217 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2218 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2219 nRefInName = 0;
2220 ++mnPredetectedReference;
2222 else if (!(nRefInName & kName))
2224 // start unquoted name
2225 nRefInName |= kName;
2228 else if (':' == c)
2230 // range operator
2231 nRefInName = 0;
2232 ++mnPredetectedReference;
2234 if (bAddToSymbol && eState != ssSkipReference)
2235 *pSym++ = c; // everything is part of reference
2237 break;
2238 case ssStop:
2239 ; // nothing, prevent warning
2240 break;
2242 cLast = c;
2243 c = *pSrc;
2245 if ( bi18n )
2247 nSrcPos = nSrcPos + nSpaces;
2248 OUStringBuffer aSymbol;
2249 mnRangeOpPosInSymbol = -1;
2250 sal_uInt16 nErr = 0;
2253 bi18n = false;
2254 // special case (e.g. $'sheetname' in OOO A1)
2255 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2256 aSymbol.append(pStart[nSrcPos++]);
2258 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass );
2260 if ( !aRes.TokenType )
2261 SetError( nErr = errIllegalChar ); // parsed chars as string
2262 if ( aRes.EndPos <= nSrcPos )
2263 { // ?!?
2264 SetError( nErr = errIllegalChar );
2265 nSrcPos = aFormula.getLength();
2266 aSymbol.truncate(0);
2268 else
2270 aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2271 nSrcPos = aRes.EndPos;
2272 c = pStart[nSrcPos];
2273 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2274 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2275 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2277 // One range operator restarts parsing for second reference.
2278 if (c == ':' && mnRangeOpPosInSymbol < 0)
2280 mnRangeOpPosInSymbol = aSymbol.getLength();
2281 bi18n = true;
2283 if ( bi18n )
2284 aSymbol.append(pStart[nSrcPos++]);
2286 } while ( bi18n && !nErr );
2287 sal_Int32 nLen = aSymbol.getLength();
2288 if ( nLen >= MAXSTRLEN )
2290 SetError( errStringOverflow );
2291 nLen = MAXSTRLEN-1;
2293 lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2294 pSym = &cSymbol[nLen];
2296 else
2298 nSrcPos = pSrc - pStart;
2299 *pSym = 0;
2301 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2303 // This is a trailing range operator, which is nonsense. Will be caught
2304 // in next round.
2305 mnRangeOpPosInSymbol = -1;
2306 *--pSym = 0;
2307 --nSrcPos;
2309 if ( bAutoCorrect )
2310 aCorrectedSymbol = cSymbol;
2311 if (bAutoIntersection && nSpaces > 1)
2312 --nSpaces; // replace '!!' with only one space
2313 return nSpaces;
2316 // Convert symbol to token
2318 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray )
2320 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
2321 bool bFound = (iLook != mxSymbols->getHashMap()->end());
2322 if (bFound)
2324 ScRawToken aToken;
2325 OpCode eOp = iLook->second;
2326 if (bInArray)
2328 if (rName.equals(mxSymbols->getSymbol(ocArrayColSep)))
2329 eOp = ocArrayColSep;
2330 else if (rName.equals(mxSymbols->getSymbol(ocArrayRowSep)))
2331 eOp = ocArrayRowSep;
2333 aToken.SetOpCode(eOp);
2334 pRawToken = aToken.Clone();
2336 else if (mxSymbols->isODFF())
2338 // ODFF names that are not written in the current mapping but to be
2339 // recognized. New names will be written in a future relase, then
2340 // exchange (!) with the names in
2341 // formula/source/core/resource/core_resource.src to be able to still
2342 // read the old names as well.
2343 struct FunctionName
2345 const sal_Char* pName;
2346 OpCode eOp;
2348 static const FunctionName aOdffAliases[] = {
2349 // Renamed old names, still accept them:
2350 { "B", ocB }, // B -> BINOM.DIST.RANGE
2351 { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
2352 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2353 { "ZGZ", ocZGZ }, // ZGZ -> RRI
2354 // Renamed new names, prepare to read future names:
2355 { "ORG.OPENOFFICE.GOALSEEK", ocBackSolver } // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2357 static const size_t nOdffAliases = sizeof(aOdffAliases) / sizeof(aOdffAliases[0]);
2358 for (size_t i=0; i<nOdffAliases; ++i)
2360 if (rName.equalsIgnoreAsciiCaseAscii( aOdffAliases[i].pName))
2362 ScRawToken aToken;
2363 aToken.SetOpCode( aOdffAliases[i].eOp);
2364 pRawToken = aToken.Clone();
2365 bFound = true;
2366 break; // for
2370 if (!bFound)
2372 OUString aIntName;
2373 if (mxSymbols->hasExternals())
2375 // If symbols are set by filters get mapping to exact name.
2376 ExternalHashMap::const_iterator iExt(
2377 mxSymbols->getExternalHashMap()->find( rName));
2378 if (iExt != mxSymbols->getExternalHashMap()->end())
2380 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2381 aIntName = (*iExt).second;
2383 if (aIntName.isEmpty())
2385 // If that isn't found we might continue with rName lookup as a
2386 // last resort by just falling through to FindFunction(), but
2387 // it shouldn't happen if the map was setup correctly. Don't
2388 // waste time and bail out.
2389 return false;
2392 if (aIntName.isEmpty())
2394 // Old (deprecated) addins first for legacy.
2395 if (ScGlobal::GetFuncCollection()->findByName(cSymbol))
2397 ScRawToken aToken;
2398 aToken.SetExternal( cSymbol );
2399 pRawToken = aToken.Clone();
2401 else
2402 // bLocalFirst=false for (English) upper full original name
2403 // (service.function)
2404 aIntName = ScGlobal::GetAddInCollection()->FindFunction(
2405 rName, !mxSymbols->isEnglish());
2407 if (!aIntName.isEmpty())
2409 ScRawToken aToken;
2410 aToken.SetExternal( aIntName.getStr() ); // international name
2411 pRawToken = aToken.Clone();
2412 bFound = true;
2415 OpCode eOp;
2416 if (bFound && ((eOp = pRawToken->GetOpCode()) == ocSub || eOp == ocNegSub))
2418 bool bShouldBeNegSub =
2419 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
2420 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
2421 eLastOp == ocArrayOpen ||
2422 eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
2423 if (bShouldBeNegSub && eOp == ocSub)
2424 pRawToken->NewOpCode( ocNegSub );
2425 //! if ocNegSub had ForceArray we'd have to set it here
2426 else if (!bShouldBeNegSub && eOp == ocNegSub)
2427 pRawToken->NewOpCode( ocSub );
2429 return bFound;
2432 bool ScCompiler::IsOpCode2( const OUString& rName )
2434 bool bFound = false;
2435 sal_uInt16 i;
2437 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
2438 bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] );
2440 if (bFound)
2442 ScRawToken aToken;
2443 aToken.SetOpCode( (OpCode) --i );
2444 pRawToken = aToken.Clone();
2446 return bFound;
2449 bool ScCompiler::IsValue( const OUString& rSym )
2451 double fVal;
2452 sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
2454 if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
2455 return false;
2457 sal_uInt16 nType = mpFormatter->GetType(nIndex);
2459 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
2460 // Dates should never be entered directly and automatically converted
2461 // to serial, because the serial would be wrong if null-date changed.
2462 // Usually it wouldn't be accepted anyway because the date separator
2463 // clashed with other separators or operators.
2464 if (nType & (NUMBERFORMAT_TIME | NUMBERFORMAT_DATE))
2465 return false;
2467 if (nType == NUMBERFORMAT_LOGICAL)
2469 const sal_Unicode* p = aFormula.getStr() + nSrcPos;
2470 while( *p == ' ' )
2471 p++;
2472 if (*p == '(')
2473 return false; // Boolean function instead.
2476 if( nType == NUMBERFORMAT_TEXT )
2477 // HACK: number too big!
2478 SetError( errIllegalArgument );
2479 ScRawToken aToken;
2480 aToken.SetDouble( fVal );
2481 pRawToken = aToken.Clone();
2482 return true;
2485 bool ScCompiler::IsString()
2487 const sal_Unicode* p = cSymbol;
2488 while ( *p )
2489 p++;
2490 xub_StrLen nLen = sal::static_int_cast<xub_StrLen>( p - cSymbol - 1 );
2491 bool bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"'));
2492 if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1)
2494 SetError(errStringOverflow);
2495 return false;
2497 if ( bQuote )
2499 cSymbol[nLen] = '\0';
2500 ScRawToken aToken;
2501 const sal_Unicode* pStr = cSymbol+1;
2502 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(OUString(pStr));
2503 aToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
2504 pRawToken = aToken.Clone();
2505 return true;
2507 return false;
2510 bool ScCompiler::IsPredetectedReference(const OUString& rName)
2512 // Speedup documents with lots of broken references, e.g. sheet deleted.
2513 sal_Int32 nPos = rName.indexOf("#REF!");
2514 if (nPos != -1)
2516 /* TODO: this may be enhanced by reusing scan information from
2517 * NextSymbol(), the positions of quotes and special characters found
2518 * there for $'sheet'.A1:... could be stored in a vector. We don't
2519 * fully rescan here whether found positions are within single quotes
2520 * for performance reasons. This code does not check for possible
2521 * occurrences of insane "valid" sheet names like
2522 * 'haha.#REF!1fooledyou' and will generate an error on such. */
2523 if (nPos == 0)
2525 // Per ODFF the correct string for a reference error is just #REF!,
2526 // so pass it on.
2527 if (rName.getLength() == 5)
2528 return IsErrorConstant( rName);
2529 return false; // #REF!.AB42 or #REF!42 or #REF!#REF!
2531 sal_Unicode c = rName[nPos-1]; // before #REF!
2532 if ('$' == c)
2534 if (nPos == 1)
2535 return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
2536 c = rName[nPos-2]; // before $#REF!
2538 sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
2539 switch (c)
2541 case '.':
2542 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
2543 return false; // sheet.#REF!42 or sheet.#REF!#REF!
2544 break;
2545 case ':':
2546 if (mnPredetectedReference > 1 &&
2547 ('.' == c2 || '$' == c2 || '#' == c2 ||
2548 ('0' <= c2 && c2 <= '9')))
2549 return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
2550 break;
2551 default:
2552 if (comphelper::string::isalphaAscii(c) &&
2553 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
2554 return false; // AB#REF!: or AB#REF!
2557 switch (mnPredetectedReference)
2559 case 1:
2560 return IsSingleReference( rName);
2561 case 2:
2562 return IsDoubleReference( rName);
2564 return false;
2567 bool ScCompiler::IsDoubleReference( const OUString& rName )
2569 ScRange aRange( aPos, aPos );
2570 const ScAddress::Details aDetails( pConv->meConv, aPos );
2571 ScAddress::ExternalInfo aExtInfo;
2572 sal_uInt16 nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2573 if( nFlags & SCA_VALID )
2575 ScRawToken aToken;
2576 ScComplexRefData aRef;
2577 aRef.InitRange( aRange );
2578 aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2579 aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2580 aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2581 if ( !(nFlags & SCA_VALID_TAB) )
2582 aRef.Ref1.SetTabDeleted( true ); // #REF!
2583 aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2584 aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 );
2585 aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 );
2586 aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 );
2587 if ( !(nFlags & SCA_VALID_TAB2) )
2588 aRef.Ref2.SetTabDeleted( true ); // #REF!
2589 aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 );
2590 aRef.SetRange(aRange, aPos);
2591 if (aExtInfo.mbExternal)
2593 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2594 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2595 aToken.SetExternalDoubleRef(
2596 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2597 maExternalFiles.push_back(aExtInfo.mnFileId);
2599 else
2601 aToken.SetDoubleReference(aRef);
2603 pRawToken = aToken.Clone();
2606 return ( nFlags & SCA_VALID ) != 0;
2609 bool ScCompiler::IsSingleReference( const OUString& rName )
2611 ScAddress aAddr( aPos );
2612 const ScAddress::Details aDetails( pConv->meConv, aPos );
2613 ScAddress::ExternalInfo aExtInfo;
2614 sal_uInt16 nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2615 // Something must be valid in order to recognize Sheet1.blah or blah.a1
2616 // as a (wrong) reference.
2617 if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) )
2619 ScRawToken aToken;
2620 ScSingleRefData aRef;
2621 aRef.InitAddress( aAddr );
2622 aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2623 aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2624 aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2625 aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2626 // the reference is really invalid
2627 if( !( nFlags & SCA_VALID ) )
2629 if( !( nFlags & SCA_VALID_COL ) )
2630 aRef.SetColDeleted(true);
2631 if( !( nFlags & SCA_VALID_ROW ) )
2632 aRef.SetRowDeleted(true);
2633 if( !( nFlags & SCA_VALID_TAB ) )
2634 aRef.SetTabDeleted(true);
2635 nFlags |= SCA_VALID;
2637 aRef.SetAddress(aAddr, aPos);
2639 if (aExtInfo.mbExternal)
2641 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2642 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2643 aToken.SetExternalSingleRef(
2644 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2645 maExternalFiles.push_back(aExtInfo.mnFileId);
2647 else
2648 aToken.SetSingleReference(aRef);
2649 pRawToken = aToken.Clone();
2652 return ( nFlags & SCA_VALID ) != 0;
2655 bool ScCompiler::IsReference( const OUString& rName )
2657 // Has to be called before IsValue
2658 sal_Unicode ch1 = rName[0];
2659 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' :
2660 ScGlobal::pLocaleData->getNumDecimalSep()[0] );
2661 if ( ch1 == cDecSep )
2662 return false;
2663 // Who was that imbecile introducing '.' as the sheet name separator!?!
2664 if ( rtl::isAsciiDigit( ch1 ) )
2666 // Numerical sheet name is valid.
2667 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
2668 // Don't create a #REF! of values. But also do not bail out on
2669 // something like 3:3, meaning entire row 3.
2672 const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
2673 if ( nPos == -1 )
2675 if (ScGlobal::FindUnquoted( rName, ':') != -1)
2676 break; // may be 3:3, continue as usual
2677 return false;
2679 sal_Unicode const * const pTabSep = rName.getStr() + nPos;
2680 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
2681 if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
2682 return false;
2683 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
2684 && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & SC_COMPILER_C_VALUE_EXP) )
2685 { // #91053#
2686 // If it is an 1.E2 expression check if "1" is an existent sheet
2687 // name. If so, a desired value 1.E2 would have to be entered as
2688 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
2689 // require numerical sheet names always being entered quoted, which
2690 // is not desirable (too many 1999, 2000, 2001 sheets in use).
2691 // Furthermore, XML files created with versions prior to SRC640e
2692 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
2693 // and would produce wrong formulas if the conditions here are met.
2694 // If you can live with these restrictions you may remove the
2695 // check and return an unconditional FALSE.
2696 OUString aTabName( rName.copy( 0, nPos ) );
2697 SCTAB nTab;
2698 if ( !pDoc->GetTable( aTabName, nTab ) )
2699 return false;
2700 // If sheet "1" exists and the expression is 1.E+2 continue as
2701 // usual, the ScRange/ScAddress parser will take care of it.
2703 } while(0);
2706 if (IsSingleReference( rName))
2707 return true;
2709 // Though the range operator is handled explicitly, when encountering
2710 // something like Sheet1.A:A we will have to treat it as one entity if it
2711 // doesn't pass as single cell reference.
2712 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
2714 if (IsDoubleReference( rName))
2715 return true;
2716 // Now try with a symbol up to the range operator, rewind source
2717 // position.
2718 sal_Int32 nLen = mnRangeOpPosInSymbol;
2719 while (cSymbol[++nLen])
2721 cSymbol[mnRangeOpPosInSymbol] = 0;
2722 nSrcPos -= (nLen - mnRangeOpPosInSymbol);
2723 mnRangeOpPosInSymbol = -1;
2724 mbRewind = true;
2725 return true; // end all checks
2727 else
2729 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel sickness,
2730 // mnRangeOpPosInSymbol did not catch the range operator as it is
2731 // within a quoted name.
2732 switch (pConv->meConv)
2734 case FormulaGrammar::CONV_XL_A1:
2735 case FormulaGrammar::CONV_XL_R1C1:
2736 case FormulaGrammar::CONV_XL_OOX:
2737 if (rName[0] == '\'' && IsDoubleReference( rName))
2738 return true;
2739 break;
2740 default:
2741 ; // nothing
2744 return false;
2747 bool ScCompiler::IsMacro( const OUString& rName )
2749 #ifdef DISABLE_SCRIPTING
2750 (void) rName;
2752 return false;
2753 #else
2755 // Calling SfxObjectShell::GetBasic() may result in all sort of things
2756 // including obtaining the model and deep down in
2757 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
2758 // formulas are compiled from a threaded import may result in a deadlock.
2759 // Check first if we actually could acquire it and if not bail out.
2760 /* FIXME: yes, but how ... */
2761 comphelper::SolarMutex& rSolarMutex = Application::GetSolarMutex();
2762 if (!rSolarMutex.tryToAcquire())
2764 SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic");
2765 return false; // bad luck
2768 OUString aName( rName);
2769 StarBASIC* pObj = 0;
2770 SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
2772 SfxApplication* pSfxApp = SFX_APP();
2774 if( pDocSh )//XXX
2775 pObj = pDocSh->GetBasic();
2776 else
2777 pObj = pSfxApp->GetBasic();
2779 // ODFF recommends to store user-defined functions prefixed with "USER.",
2780 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
2781 // function name so a function "USER.FOO" could not exist, and macro check
2782 // is assigned the lowest priority in function name check.
2783 if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
2784 aName = aName.copy(5);
2786 SbxMethod* pMeth = (SbxMethod*) pObj->Find( aName, SbxCLASS_METHOD );
2787 if( !pMeth )
2789 rSolarMutex.release();
2790 return false;
2792 // It really should be a BASIC function!
2793 if( pMeth->GetType() == SbxVOID
2794 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
2795 || !pMeth->ISA(SbMethod) )
2797 rSolarMutex.release();
2798 return false;
2800 ScRawToken aToken;
2801 aToken.SetExternal( aName.getStr() );
2802 aToken.eOp = ocMacro;
2803 pRawToken = aToken.Clone();
2804 rSolarMutex.release();
2805 return true;
2806 #endif
2809 bool ScCompiler::IsNamedRange( const OUString& rUpperName )
2811 // IsNamedRange is called only from NextNewToken, with an upper-case string
2813 // try local names first
2814 bool bGlobal = false;
2815 ScRangeName* pRangeName = pDoc->GetRangeName(aPos.Tab());
2816 const ScRangeData* pData = NULL;
2817 if (pRangeName)
2818 pData = pRangeName->findByUpperName(rUpperName);
2819 if (!pData)
2821 pRangeName = pDoc->GetRangeName();
2822 if (pRangeName)
2823 pData = pRangeName->findByUpperName(rUpperName);
2824 if (pData)
2825 bGlobal = true;
2828 if (pData)
2830 ScRawToken aToken;
2831 aToken.SetName(bGlobal, pData->GetIndex());
2832 pRawToken = aToken.Clone();
2833 return true;
2835 else
2836 return false;
2839 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol )
2841 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
2842 * correctly parses external named references in OOo, as required per RFE
2843 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
2844 * spec first. Until then don't pretend to support external names that
2845 * wouldn't survive a save and reload cycle, return false instead. */
2847 if (!pConv)
2848 return false;
2850 OUString aFile, aName;
2851 if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
2852 return false;
2854 ScRawToken aToken;
2855 if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
2856 return false;
2858 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2859 OUString aTmp = aFile;
2860 pRefMgr->convertToAbsName(aTmp);
2861 aFile = aTmp;
2862 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
2863 if (!pRefMgr->isValidRangeName(nFileId, aName))
2864 // range name doesn't exist in the source document.
2865 return false;
2867 const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
2868 aToken.SetExternalName(nFileId, pRealName ? *pRealName : OUString(aTmp));
2869 pRawToken = aToken.Clone();
2870 maExternalFiles.push_back(nFileId);
2871 return true;
2874 bool ScCompiler::IsDBRange( const OUString& rName )
2876 if (rName.equalsAscii("[]"))
2878 if (pRawToken && pRawToken->GetOpCode() == ocDBArea)
2880 // In OOXML, a database range is named Table1[], Table2[] etc.
2881 // Skip the [] part if the previous token is a valid db range.
2882 ScRawToken aToken;
2883 aToken.eOp = ocSkip;
2884 pRawToken = aToken.Clone();
2885 return true;
2888 ScDBCollection::NamedDBs& rDBs = pDoc->GetDBCollection()->getNamedDBs();
2889 const ScDBData* p = rDBs.findByUpperName(rName);
2890 if (!p)
2891 return false;
2893 ScRawToken aToken;
2894 aToken.SetName(true, p->GetIndex()); // DB range is always global.
2895 aToken.eOp = ocDBArea;
2896 pRawToken = aToken.Clone();
2897 return true;
2900 bool ScCompiler::IsColRowName( const OUString& rName )
2902 bool bInList = false;
2903 bool bFound = false;
2904 ScSingleRefData aRef;
2905 OUString aName( rName );
2906 DeQuote( aName );
2907 SCTAB nThisTab = aPos.Tab();
2908 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
2909 { // first check ranges on this sheet, in case of duplicated names
2910 for ( short jRow=0; jRow<2 && !bInList; jRow++ )
2912 ScRangePairList* pRL;
2913 if ( !jRow )
2914 pRL = pDoc->GetColNameRanges();
2915 else
2916 pRL = pDoc->GetRowNameRanges();
2917 for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
2919 ScRangePair* pR = (*pRL)[iPair];
2920 const ScRange& rNameRange = pR->GetRange(0);
2921 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
2922 nThisTab <= rNameRange.aEnd.Tab()) )
2923 continue; // for
2924 ScCellIterator aIter( pDoc, rNameRange );
2925 for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
2927 // Don't crash if cell (via CompileNameFormula) encounters
2928 // a formula cell without code and
2929 // HasStringData/Interpret/Compile is executed and all that
2930 // recursive..
2931 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
2932 CellType eType = aIter.getType();
2933 bool bOk = false;
2934 if (eType == CELLTYPE_FORMULA)
2936 ScFormulaCell* pFC = aIter.getFormulaCell();
2937 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
2939 else
2940 bOk = true;
2942 if (bOk && aIter.hasString())
2944 OUString aStr = aIter.getString();
2945 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
2947 aRef.InitFlags();
2948 if ( !jRow )
2949 aRef.SetColRel( true ); // ColName
2950 else
2951 aRef.SetRowRel( true ); // RowName
2952 aRef.SetAddress(aIter.GetPos(), aPos);
2953 bInList = bFound = true;
2960 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
2961 { // search in current sheet
2962 long nDistance = 0, nMax = 0;
2963 long nMyCol = (long) aPos.Col();
2964 long nMyRow = (long) aPos.Row();
2965 bool bTwo = false;
2966 ScAddress aOne( 0, 0, aPos.Tab() );
2967 ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
2969 ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
2970 if ( pNameCache )
2972 // use GetNameOccurrences to collect all positions of aName on the sheet
2973 // (only once), similar to the outer part of the loop in the "else" branch.
2975 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
2977 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
2978 // The order of addresses in the vector is the same as from ScCellIterator.
2980 ScAutoNameAddresses::const_iterator aEnd(rAddresses.end());
2981 for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter )
2983 ScAddress aAddress( *aAdrIter ); // cell address with an equal string
2985 if ( bFound )
2986 { // stop if everything else is further away
2987 if ( nMax < (long)aAddress.Col() )
2988 break; // aIter
2990 if ( aAddress != aPos )
2992 // same treatment as in isEqual case below
2994 SCCOL nCol = aAddress.Col();
2995 SCROW nRow = aAddress.Row();
2996 long nC = nMyCol - nCol;
2997 long nR = nMyRow - nRow;
2998 if ( bFound )
3000 long nD = nC * nC + nR * nR;
3001 if ( nD < nDistance )
3003 if ( nC < 0 || nR < 0 )
3004 { // right or below
3005 bTwo = true;
3006 aTwo.Set( nCol, nRow, aAddress.Tab() );
3007 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3008 nDistance = nD;
3010 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3012 // upper left, only if not further up than the
3013 // current entry and nMyRow is below (CellIter
3014 // runs column-wise)
3015 bTwo = false;
3016 aOne.Set( nCol, nRow, aAddress.Tab() );
3017 nMax = std::max( nMyCol + nC, nMyRow + nR );
3018 nDistance = nD;
3022 else
3024 aOne.Set( nCol, nRow, aAddress.Tab() );
3025 nDistance = nC * nC + nR * nR;
3026 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3029 bFound = true;
3033 else
3035 ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
3036 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3038 if ( bFound )
3039 { // stop if everything else is further away
3040 if ( nMax < (long)aIter.GetPos().Col() )
3041 break; // aIter
3043 CellType eType = aIter.getType();
3044 bool bOk = false;
3045 if (eType == CELLTYPE_FORMULA)
3047 ScFormulaCell* pFC = aIter.getFormulaCell();
3048 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3050 else
3051 bOk = true;
3053 if (bOk && aIter.hasString())
3055 OUString aStr = aIter.getString();
3056 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3058 SCCOL nCol = aIter.GetPos().Col();
3059 SCROW nRow = aIter.GetPos().Row();
3060 long nC = nMyCol - nCol;
3061 long nR = nMyRow - nRow;
3062 if ( bFound )
3064 long nD = nC * nC + nR * nR;
3065 if ( nD < nDistance )
3067 if ( nC < 0 || nR < 0 )
3068 { // right or below
3069 bTwo = true;
3070 aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3071 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3072 nDistance = nD;
3074 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3076 // upper left, only if not further up than the
3077 // current entry and nMyRow is below (CellIter
3078 // runs column-wise)
3079 bTwo = false;
3080 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3081 nMax = std::max( nMyCol + nC, nMyRow + nR );
3082 nDistance = nD;
3086 else
3088 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3089 nDistance = nC * nC + nR * nR;
3090 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3092 bFound = true;
3098 if ( bFound )
3100 ScAddress aAdr;
3101 if ( bTwo )
3103 if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() )
3104 aAdr = aOne; // upper left takes precedence
3105 else
3107 if ( nMyCol < (long)aOne.Col() )
3108 { // two to the right
3109 if ( nMyRow >= (long)aTwo.Row() )
3110 aAdr = aTwo; // directly right
3111 else
3112 aAdr = aOne;
3114 else
3115 { // two below or below and right, take the nearest
3116 long nC1 = nMyCol - aOne.Col();
3117 long nR1 = nMyRow - aOne.Row();
3118 long nC2 = nMyCol - aTwo.Col();
3119 long nR2 = nMyRow - aTwo.Row();
3120 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3121 aAdr = aOne;
3122 else
3123 aAdr = aTwo;
3127 else
3128 aAdr = aOne;
3129 aRef.InitAddress( aAdr );
3130 if ( (aAdr.Row() != MAXROW && pDoc->HasStringData(
3131 aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3132 || (aAdr.Row() != 0 && pDoc->HasStringData(
3133 aAdr.Col(), aAdr.Row() - 1, aAdr.Tab())))
3134 aRef.SetRowRel( true ); // RowName
3135 else
3136 aRef.SetColRel( true ); // ColName
3137 aRef.SetAddress(aAdr, aPos);
3140 if ( bFound )
3142 ScRawToken aToken;
3143 aToken.SetSingleReference( aRef );
3144 aToken.eOp = ocColRowName;
3145 pRawToken = aToken.Clone();
3146 return true;
3148 else
3149 return false;
3152 bool ScCompiler::IsBoolean( const OUString& rName )
3154 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) );
3155 if( iLook != mxSymbols->getHashMap()->end() &&
3156 ((*iLook).second == ocTrue ||
3157 (*iLook).second == ocFalse) )
3159 ScRawToken aToken;
3160 aToken.SetOpCode( (*iLook).second );
3161 pRawToken = aToken.Clone();
3162 return true;
3164 else
3165 return false;
3168 bool ScCompiler::IsErrorConstant( const OUString& rName ) const
3170 sal_uInt16 nError = GetErrorConstant( rName);
3171 if (nError)
3173 ScRawToken aToken;
3174 aToken.SetErrorConstant( nError);
3175 pRawToken = aToken.Clone();
3176 return true;
3178 else
3179 return false;
3182 void ScCompiler::AutoCorrectParsedSymbol()
3184 sal_Int32 nPos = aCorrectedSymbol.getLength();
3185 if ( nPos )
3187 nPos--;
3188 const sal_Unicode cQuote = '\"';
3189 const sal_Unicode cx = 'x';
3190 const sal_Unicode cX = 'X';
3191 sal_Unicode c1 = aCorrectedSymbol[0];
3192 sal_Unicode c2 = aCorrectedSymbol[nPos];
3193 sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
3194 if ( c1 == cQuote && c2 != cQuote )
3195 { // "...
3196 // What's not a word doesn't belong to it.
3197 // Don't be pedantic: c < 128 should be sufficient here.
3198 while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
3199 ((GetCharTableFlags(aCorrectedSymbol[nPos], aCorrectedSymbol[nPos-1]) &
3200 (SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) )
3201 nPos--;
3202 if ( nPos == MAXSTRLEN - 2 )
3203 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) ); // '"' the 255th character
3204 else
3205 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) );
3206 bCorrected = true;
3208 else if ( c1 != cQuote && c2 == cQuote )
3209 { // ..."
3210 aCorrectedSymbol = OUString(cQuote) + aCorrectedSymbol;
3211 bCorrected = true;
3213 else if ( nPos == 0 && (c1 == cx || c1 == cX) )
3214 { // x => *
3215 aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
3216 bCorrected = true;
3218 else if ( (GetCharTableFlags( c1, 0 ) & SC_COMPILER_C_CHAR_VALUE)
3219 && (GetCharTableFlags( c2, c2p ) & SC_COMPILER_C_CHAR_VALUE) )
3221 if ( comphelper::string::getTokenCount(aCorrectedSymbol, cx) > 1 )
3222 { // x => *
3223 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
3224 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUString(cx), OUString(c));
3225 bCorrected = true;
3227 if ( comphelper::string::getTokenCount(aCorrectedSymbol, cX) > 1 )
3228 { // X => *
3229 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
3230 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUString(cX), OUString(c));
3231 bCorrected = true;
3234 else
3236 OUString aSymbol( aCorrectedSymbol );
3237 OUString aDoc;
3238 sal_Int32 nPosition;
3239 if ( aSymbol[0] == '\''
3240 && ((nPosition = aSymbol.indexOf( "'#" )) != -1) )
3241 { // Split off 'Doc'#, may be d:\... or whatever
3242 aDoc = aSymbol.copy(0, nPosition + 2);
3243 aSymbol = aSymbol.copy(nPosition + 2);
3245 sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
3246 bool bColons;
3247 if ( nRefs > 2 )
3248 { // duplicated or too many ':'? B:2::C10 => B2:C10
3249 bColons = true;
3250 sal_Int32 nIndex = 0;
3251 OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
3252 sal_Int32 nLen1 = aTmp1.getLength();
3253 OUString aSym, aTmp2;
3254 bool bLastAlp, bNextNum;
3255 bLastAlp = bNextNum = true;
3256 xub_StrLen nStrip = 0;
3257 xub_StrLen nCount = nRefs;
3258 for ( sal_Int32 j=1; j<nCount; j++ )
3260 aTmp2 = aSymbol.getToken( 0, ':', nIndex );
3261 sal_Int32 nLen2 = aTmp2.getLength();
3262 if ( nLen1 || nLen2 )
3264 if ( nLen1 )
3266 aSym += aTmp1;
3267 bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
3269 if ( nLen2 )
3271 bNextNum = CharClass::isAsciiNumeric( aTmp2 );
3272 if ( bLastAlp == bNextNum && nStrip < 1 )
3274 // Must be alternating number/string, only
3275 // strip within a reference.
3276 nRefs--;
3277 nStrip++;
3279 else
3281 if ( !aSym.isEmpty() && !aSym.endsWith(":"))
3282 aSym += ":";
3283 nStrip = 0;
3285 bLastAlp = !bNextNum;
3287 else
3288 { // ::
3289 nRefs--;
3290 if ( nLen1 )
3291 { // B10::C10 ? append ':' on next round
3292 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
3293 nStrip++;
3295 bNextNum = !bLastAlp;
3297 aTmp1 = aTmp2;
3298 nLen1 = nLen2;
3300 else
3301 nRefs--;
3303 aSymbol = aSym;
3304 aSymbol += aTmp1;
3306 else
3307 bColons = false;
3308 if ( nRefs && nRefs <= 2 )
3309 { // reference twisted? 4A => A4 etc.
3310 OUString aTab[2], aRef[2];
3311 const ScAddress::Details aDetails( pConv->meConv, aPos );
3312 if ( nRefs == 2 )
3314 aRef[0] = aSymbol.getToken( 0, ':' );
3315 aRef[1] = aSymbol.getToken( 1, ':' );
3317 else
3318 aRef[0] = aSymbol;
3320 bool bChanged = false;
3321 bool bOk = true;
3322 sal_uInt16 nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW;
3323 for ( int j=0; j<nRefs; j++ )
3325 sal_Int32 nTmp = 0;
3326 sal_Int32 nDotPos = -1;
3327 while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
3328 nDotPos = nTmp++; // the last one counts
3329 if ( nDotPos != -1 )
3331 aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
3332 aRef[j] = aRef[j].copy( nDotPos + 1 );
3334 OUString aOld( aRef[j] );
3335 OUString aStr2;
3336 const sal_Unicode* p = aRef[j].getStr();
3337 while ( *p && CharClass::isAsciiNumeric( OUString(*p) ) )
3338 aStr2 += OUString(*p++);
3339 aRef[j] = OUString( p );
3340 aRef[j] += aStr2;
3341 if ( bColons || aRef[j] != aOld )
3343 bChanged = true;
3344 ScAddress aAdr;
3345 bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
3348 if ( bChanged && bOk )
3350 aCorrectedSymbol = aDoc;
3351 aCorrectedSymbol += aTab[0];
3352 aCorrectedSymbol += aRef[0];
3353 if ( nRefs == 2 )
3355 aCorrectedSymbol += ":";
3356 aCorrectedSymbol += aTab[1];
3357 aCorrectedSymbol += aRef[1];
3359 bCorrected = true;
3366 static inline bool lcl_UpperAsciiOrI18n( OUString& rUpper, const OUString& rOrg, FormulaGrammar::Grammar eGrammar )
3368 if (FormulaGrammar::isODFF( eGrammar ))
3370 // ODFF has a defined set of English function names, avoid i18n
3371 // overhead.
3372 rUpper = rOrg.toAsciiUpperCase();
3373 return true;
3375 else
3377 rUpper = ScGlobal::pCharClass->uppercase(rOrg);
3378 return false;
3382 bool ScCompiler::NextNewToken( bool bInArray )
3384 bool bAllowBooleans = bInArray;
3385 xub_StrLen nSpaces = NextSymbol(bInArray);
3387 if (!cSymbol[0])
3388 return false;
3390 if( nSpaces )
3392 ScRawToken aToken;
3393 aToken.SetOpCode( ocSpaces );
3394 aToken.sbyte.cByte = (sal_uInt8) ( nSpaces > 255 ? 255 : nSpaces );
3395 if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
3397 SetError(errCodeOverflow);
3398 return false;
3402 // Short cut for references when reading ODF to speedup things.
3403 if (mnPredetectedReference)
3405 OUString aStr( cSymbol);
3406 if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr))
3408 /* TODO: it would be nice to generate a #REF! error here, which
3409 * would need an ocBad token with additional error value.
3410 * FormulaErrorToken wouldn't do because we want to preserve the
3411 * original string containing partial valid address
3412 * information if not ODFF (in that case it was already handled).
3413 * */
3414 ScRawToken aToken;
3415 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aStr);
3416 aToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
3417 aToken.NewOpCode( ocBad );
3418 pRawToken = aToken.Clone();
3420 return true;
3423 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
3424 !bAutoCorrect )
3425 { // special case to speed up broken [$]#REF documents
3426 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
3427 * be processed as usual. That would need some special treatment,
3428 * also in NextSymbol() because of possible combinations of
3429 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
3430 * handled by IsPredetectedReference(), this case here remains for
3431 * manual/API input. */
3432 OUString aBad( aFormula.copy( nSrcPos-1 ) );
3433 eLastOp = pArr->AddBad( aBad )->GetOpCode();
3434 return false;
3437 if( IsString() )
3438 return true;
3440 bool bMayBeFuncName;
3441 bool bAsciiNonAlnum; // operators, separators, ...
3442 if ( cSymbol[0] < 128 )
3444 bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
3445 if (!bMayBeFuncName)
3447 SvtMiscOptions aOpt;
3448 if (aOpt.IsExperimentalMode())
3449 bMayBeFuncName = (cSymbol[0] == '_' && cSymbol[1] == '_');
3452 bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
3454 else
3456 OUString aTmpStr( cSymbol[0] );
3457 bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
3458 bAsciiNonAlnum = false;
3460 if ( bMayBeFuncName )
3462 // a function name must be followed by a parenthesis
3463 const sal_Unicode* p = aFormula.getStr() + nSrcPos;
3464 while( *p == ' ' )
3465 p++;
3466 bMayBeFuncName = ( *p == '(' );
3469 // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
3470 // IsReference().
3472 OUString aUpper;
3476 mbRewind = false;
3477 const OUString aOrg( cSymbol );
3479 if (bAsciiNonAlnum)
3481 if (cSymbol[0] == '#')
3483 // This can be only an error constant, if any.
3484 lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3485 if (IsErrorConstant( aUpper))
3486 return true;
3487 break; // do; create ocBad token or set error.
3489 if (IsOpCode( aOrg, bInArray ))
3490 return true;
3493 aUpper = "";
3494 bool bAsciiUpper = false;
3495 if (bMayBeFuncName)
3497 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3498 if (IsOpCode( aUpper, bInArray ))
3499 return true;
3502 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
3503 // referred => IsReference() before IsValue().
3504 // Preserve case of file names in external references.
3505 if (IsReference( aOrg ))
3507 if (mbRewind) // Range operator, but no direct reference.
3508 continue; // do; up to range operator.
3509 // If a syntactically correct reference was recognized but invalid
3510 // e.g. because of non-existing sheet name => entire reference
3511 // ocBad to preserve input instead of #REF!.A1
3512 if (!pRawToken->IsValidReference())
3514 aUpper = aOrg; // ensure for ocBad
3515 break; // do; create ocBad token or set error.
3517 return true;
3520 if (aUpper.isEmpty())
3521 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3523 // IsBoolean() before IsValue() to catch inline bools without the kludge
3524 // for inline arrays.
3525 if (bAllowBooleans && IsBoolean( aUpper ))
3526 return true;
3528 if (IsValue( aUpper ))
3529 return true;
3531 // User defined names and such do need i18n upper also in ODF.
3532 if (bAsciiUpper)
3533 aUpper = ScGlobal::pCharClass->uppercase( aOrg );
3535 if (IsNamedRange( aUpper ))
3536 return true;
3537 // Preserve case of file names in external references.
3538 if (IsExternalNamedRange( aOrg ))
3539 return true;
3540 if (IsDBRange( aUpper ))
3541 return true;
3542 // If followed by '(' (with or without space inbetween) it can not be a
3543 // column/row label. Prevent arbitrary content detection.
3544 if (!bMayBeFuncName && IsColRowName( aUpper ))
3545 return true;
3546 if (bMayBeFuncName && IsMacro( aUpper ))
3547 return true;
3548 if (bMayBeFuncName && IsOpCode2( aUpper ))
3549 return true;
3551 } while (mbRewind);
3553 if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE )
3555 // set an error
3556 SetError( errNoName );
3557 if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_BREAK)
3558 return false; // end compilation
3561 // Provide single token information and continue. Do not set an error, that
3562 // would prematurely end compilation. Simple unknown names are handled by
3563 // the interpreter.
3564 aUpper = ScGlobal::pCharClass->lowercase( aUpper );
3565 ScRawToken aToken;
3566 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aUpper);
3567 aToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
3568 aToken.NewOpCode( ocBad );
3569 pRawToken = aToken.Clone();
3570 if ( bAutoCorrect )
3571 AutoCorrectParsedSymbol();
3572 return true;
3575 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
3577 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
3578 sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
3579 OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
3580 if( pArr->GetLen() == nExpectedCount )
3582 FormulaToken** ppTokens = pArr->GetArray();
3583 // string tokens expected, GetString() will assert if token type is wrong
3584 rFormula = ppTokens[0]->GetString().getString();
3585 if( bExternal )
3586 rFormulaNmsp = ppTokens[1]->GetString().getString();
3590 namespace {
3592 class ExternalFileInserter : std::unary_function<sal_uInt16, void>
3594 ScAddress maPos;
3595 ScExternalRefManager& mrRefMgr;
3596 public:
3597 ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
3598 maPos(rPos), mrRefMgr(rRefMgr) {}
3600 void operator() (sal_uInt16 nFileId) const
3602 mrRefMgr.insertRefCell(nFileId, maPos);
3608 ScTokenArray* ScCompiler::CompileString( const OUString& rFormula )
3610 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
3611 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
3612 SetGrammar( FormulaGrammar::GRAM_PODF );
3614 ScTokenArray aArr;
3615 pArr = &aArr;
3616 aFormula = comphelper::string::strip(rFormula, ' ');
3618 nSrcPos = 0;
3619 bCorrected = false;
3620 if ( bAutoCorrect )
3622 aCorrectedFormula = "";
3623 aCorrectedSymbol = "";
3625 sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
3626 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
3628 nSrcPos++;
3629 nForced++;
3630 if ( bAutoCorrect )
3631 aCorrectedFormula += "=";
3633 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
3635 nSrcPos++;
3636 nForced++;
3637 if ( bAutoCorrect )
3638 aCorrectedFormula += "=";
3640 struct FunctionStack
3642 OpCode eOp;
3643 short nSep;
3645 // FunctionStack only used if PODF or OOXML!
3646 bool bPODF = FormulaGrammar::isPODF( meGrammar);
3647 bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
3648 bool bUseFunctionStack = (bPODF || bOOXML);
3649 const size_t nAlloc = 512;
3650 FunctionStack aFuncs[ nAlloc ];
3651 FunctionStack* pFunctionStack = (bUseFunctionStack && static_cast<size_t>(rFormula.getLength()) > nAlloc ?
3652 new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
3653 pFunctionStack[0].eOp = ocNone;
3654 pFunctionStack[0].nSep = 0;
3655 size_t nFunction = 0;
3656 short nBrackets = 0;
3657 bool bInArray = false;
3658 eLastOp = ocOpen;
3659 while( NextNewToken( bInArray ) )
3661 const OpCode eOp = pRawToken->GetOpCode();
3662 if (eOp == ocSkip)
3663 continue;
3665 switch (eOp)
3667 case ocOpen:
3669 ++nBrackets;
3670 if (bUseFunctionStack)
3672 ++nFunction;
3673 pFunctionStack[ nFunction ].eOp = eLastOp;
3674 pFunctionStack[ nFunction ].nSep = 0;
3677 break;
3678 case ocClose:
3680 if( !nBrackets )
3682 SetError( errPairExpected );
3683 if ( bAutoCorrect )
3685 bCorrected = true;
3686 aCorrectedSymbol = "";
3689 else
3690 nBrackets--;
3691 if (bUseFunctionStack && nFunction)
3692 --nFunction;
3694 break;
3695 case ocSep:
3697 if (bUseFunctionStack)
3698 ++pFunctionStack[ nFunction ].nSep;
3700 break;
3701 case ocArrayOpen:
3703 if( bInArray )
3704 SetError( errNestedArray );
3705 else
3706 bInArray = true;
3707 // Don't count following column separator as parameter separator.
3708 if (bUseFunctionStack)
3710 ++nFunction;
3711 pFunctionStack[ nFunction ].eOp = eOp;
3712 pFunctionStack[ nFunction ].nSep = 0;
3715 break;
3716 case ocArrayClose:
3718 if( bInArray )
3720 bInArray = false;
3722 else
3724 SetError( errPairExpected );
3725 if ( bAutoCorrect )
3727 bCorrected = true;
3728 aCorrectedSymbol = "";
3731 if (bUseFunctionStack && nFunction)
3732 --nFunction;
3734 default:
3735 break;
3737 if( (eLastOp == ocSep ||
3738 eLastOp == ocArrayRowSep ||
3739 eLastOp == ocArrayColSep ||
3740 eLastOp == ocArrayOpen) &&
3741 (eOp == ocSep ||
3742 eOp == ocClose ||
3743 eOp == ocArrayRowSep ||
3744 eOp == ocArrayColSep ||
3745 eOp == ocArrayClose) )
3747 // FIXME: should we check for known functions with optional empty
3748 // args so the correction dialog can do better?
3749 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
3751 SetError(errCodeOverflow); break;
3754 if (bOOXML)
3756 // Append a parameter for CEILING, FLOOR and WEEKNUM, all 1.0
3757 // Function is already closed, parameter count is nSep+1
3758 size_t nFunc = nFunction + 1;
3759 if (eOp == ocClose && (
3760 (pFunctionStack[ nFunc ].eOp == ocCeil && // 3rd Excel mode
3761 pFunctionStack[ nFunc ].nSep == 1) ||
3762 (pFunctionStack[ nFunc ].eOp == ocFloor && // 3rd Excel mode
3763 pFunctionStack[ nFunc ].nSep == 1) ||
3764 (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
3765 pFunctionStack[ nFunc ].nSep == 0)))
3767 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
3768 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
3770 SetError(errCodeOverflow); break;
3774 else if (bPODF)
3776 /* TODO: for now this is the only PODF adapter. If there were more,
3777 * factor this out. */
3778 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
3779 if (eOp == ocSep &&
3780 pFunctionStack[ nFunction ].eOp == ocAddress &&
3781 pFunctionStack[ nFunction ].nSep == 3)
3783 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
3784 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
3786 SetError(errCodeOverflow); break;
3788 ++pFunctionStack[ nFunction ].nSep;
3791 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( pRawToken->CreateToken());
3792 if (!pNewToken)
3794 SetError(errCodeOverflow); break;
3796 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush &&
3797 pNewToken->GetType() == svSingleRef)
3798 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
3799 eLastOp = pRawToken->GetOpCode();
3800 if ( bAutoCorrect )
3801 aCorrectedFormula += aCorrectedSymbol;
3803 if ( mbCloseBrackets )
3805 if( bInArray )
3807 FormulaByteToken aToken( ocArrayClose );
3808 if( !pArr->AddToken( aToken ) )
3810 SetError(errCodeOverflow);
3812 else if ( bAutoCorrect )
3813 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
3816 FormulaByteToken aToken( ocClose );
3817 while( nBrackets-- )
3819 if( !pArr->AddToken( aToken ) )
3821 SetError(errCodeOverflow); break;
3823 if ( bAutoCorrect )
3824 aCorrectedFormula += mxSymbols->getSymbol(ocClose);
3827 if ( nForced >= 2 )
3828 pArr->SetRecalcModeForced();
3830 if (pFunctionStack != &aFuncs[0])
3831 delete [] pFunctionStack;
3833 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3834 ScTokenArray* pNew = new ScTokenArray( aArr );
3835 pNew->GenHash();
3836 pArr = pNew;
3838 if (!maExternalFiles.empty())
3840 // Remove duplicates, and register all external files found in this cell.
3841 std::sort(maExternalFiles.begin(), maExternalFiles.end());
3842 std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
3843 std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *pDoc->GetExternalRefManager()));
3844 maExternalFiles.erase(itEnd, maExternalFiles.end());
3847 return pNew;
3850 ScTokenArray* ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
3852 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
3853 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
3854 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
3856 ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
3857 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
3858 table::CellAddress aReferencePos;
3859 ScUnoConversion::FillApiAddress( aReferencePos, aPos );
3860 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
3861 ScTokenArray aTokenArray;
3862 if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
3864 // remember pArr, in case a subsequent CompileTokenArray() is executed.
3865 ScTokenArray* pNew = new ScTokenArray( aTokenArray );
3866 pArr = pNew;
3867 return pNew;
3870 catch( uno::Exception& )
3873 // no success - fallback to some internal grammar and hope the best
3874 return CompileString( rFormula );
3877 ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const
3879 ScRangeData* pRangeData = NULL;
3880 bool bGlobal = rToken.IsGlobal();
3881 if (bGlobal)
3882 // global named range.
3883 pRangeData = pDoc->GetRangeName()->findByIndex( rToken.GetIndex());
3884 else
3886 // sheet local named range.
3887 const ScRangeName* pRN = pDoc->GetRangeName( aPos.Tab());
3888 if (pRN)
3889 pRangeData = pRN->findByIndex( rToken.GetIndex());
3891 return pRangeData;
3894 bool ScCompiler::HandleRange()
3896 const ScRangeData* pRangeData = GetRangeData( *mpToken);
3897 if (pRangeData)
3899 sal_uInt16 nErr = pRangeData->GetErrCode();
3900 if( nErr )
3901 SetError( errNoName );
3902 else if ( !bCompileForFAP )
3904 ScTokenArray* pNew;
3905 // put named formula into parentheses.
3906 // But only if there aren't any yet, parenthetical
3907 // ocSep doesn't work, e.g. SUM((...;...))
3908 // or if not directly between ocSep/parenthesis,
3909 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
3910 // in short: if it isn't a self-contained expression.
3911 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
3912 FormulaToken* p2 = pArr->PeekNextNoSpaces();
3913 OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) );
3914 OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) );
3915 bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
3916 bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
3917 bool bAddPair = !(bBorder1 && bBorder2);
3918 if ( bAddPair )
3920 pNew = new ScTokenArray();
3921 pNew->AddOpCode( ocClose );
3922 PushTokenArray( pNew, true );
3923 pNew->Reset();
3925 pNew = pRangeData->GetCode()->Clone();
3926 PushTokenArray( pNew, true );
3927 if( pRangeData->HasReferences() )
3929 SetRelNameReference();
3930 MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
3932 pNew->Reset();
3933 if ( bAddPair )
3935 pNew = new ScTokenArray();
3936 pNew->AddOpCode( ocOpen );
3937 PushTokenArray( pNew, true );
3938 pNew->Reset();
3940 return GetToken();
3943 else
3944 SetError(errNoName);
3945 return true;
3948 bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
3950 // Handle external range names.
3951 switch (_aToken.GetType())
3953 case svExternalSingleRef:
3954 case svExternalDoubleRef:
3955 pArr->IncrementRefs();
3956 break;
3957 case svExternalName:
3959 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3960 const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
3961 if (!pFile)
3963 SetError(errNoName);
3964 return true;
3967 OUString aName = _aToken.GetString().getString();
3968 ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
3969 _aToken.GetIndex(), aName, &aPos);
3971 if (!xNew)
3973 SetError(errNoName);
3974 return true;
3977 ScTokenArray* pNew = xNew->Clone();
3978 PushTokenArray( pNew, true);
3979 if (pNew->GetNextReference() != NULL)
3981 SetRelNameReference();
3982 MoveRelWrap(MAXCOL, MAXROW);
3984 pNew->Reset();
3985 return GetToken();
3987 default:
3988 OSL_FAIL("Wrong type for external reference!");
3989 return false;
3991 return true;
3994 template< typename T, typename S >
3995 static S lcl_adjval( S& n, T pos, T max, bool bRel )
3997 max++;
3998 if( bRel )
3999 n = sal::static_int_cast<S>( n + pos );
4000 if( n < 0 )
4001 n = sal::static_int_cast<S>( n + max );
4002 else if( n >= max )
4003 n = sal::static_int_cast<S>( n - max );
4004 if( bRel )
4005 n = sal::static_int_cast<S>( n - pos );
4006 return n;
4009 // reference of named range with relative references
4011 void ScCompiler::SetRelNameReference()
4013 pArr->Reset();
4014 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4015 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4017 ScSingleRefData& rRef1 = t->GetSingleRef();
4018 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
4019 rRef1.SetRelName( true );
4020 if ( t->GetType() == svDoubleRef )
4022 ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2;
4023 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
4024 rRef2.SetRelName( true );
4029 // Wrap-adjust relative references of a RangeName to current position,
4030 // don't call for other token arrays!
4031 void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow )
4033 pArr->Reset();
4034 for( ScToken* t = static_cast<ScToken*>(pArr->GetNextReference()); t;
4035 t = static_cast<ScToken*>(pArr->GetNextReference()) )
4037 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4038 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4039 else
4040 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4044 // Wrap-adjust relative references of a RangeName to current position,
4045 // don't call for other token arrays!
4046 void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos,
4047 SCCOL nMaxCol, SCROW nMaxRow )
4049 rArr.Reset();
4050 for( ScToken* t = static_cast<ScToken*>(rArr.GetNextReference()); t;
4051 t = static_cast<ScToken*>(rArr.GetNextReference()) )
4053 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4054 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( t->GetSingleRef() ).Ref() );
4055 else
4056 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, t->GetDoubleRef() );
4060 bool ScCompiler::IsCharFlagAllConventions(
4061 OUString const & rStr, xub_StrLen nPos, sal_uLong nFlags, bool bTestLetterNumeric )
4063 sal_Unicode c = rStr[ nPos ];
4064 sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
4065 if (c < 128)
4067 for ( int nConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
4068 ++nConv < formula::FormulaGrammar::CONV_LAST; )
4070 if (pConventions[nConv] &&
4071 ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
4072 return false;
4073 // convention not known => assume valid
4075 return true;
4077 else if (bTestLetterNumeric)
4078 return ScGlobal::pCharClass->isLetterNumeric( rStr, nPos );
4079 else
4080 return false;
4083 void ScCompiler::CreateStringFromExternal(OUStringBuffer& rBuffer, FormulaToken* pTokenP) const
4085 FormulaToken* t = pTokenP;
4086 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4087 const OUString* pFileName = pRefMgr->getExternalFileName(t->GetIndex());
4088 if (!pFileName)
4089 return;
4091 switch (t->GetType())
4093 case svExternalName:
4094 rBuffer.append(pConv->makeExternalNameStr(*pFileName, t->GetString().getString()));
4095 break;
4096 case svExternalSingleRef:
4097 pConv->makeExternalRefStr(
4098 rBuffer, GetPos(), *pFileName, t->GetString().getString(),
4099 static_cast<ScToken*>(t)->GetSingleRef());
4100 break;
4101 case svExternalDoubleRef:
4103 vector<OUString> aTabNames;
4104 pRefMgr->getAllCachedTableNames(t->GetIndex(), aTabNames);
4105 if (aTabNames.empty())
4106 return;
4108 pConv->makeExternalRefStr(
4109 rBuffer, GetPos(), *pFileName, aTabNames, t->GetString().getString(),
4110 static_cast<ScToken*>(t)->GetDoubleRef());
4112 break;
4113 default:
4114 // warning, not error, otherwise we may end up with a never
4115 // ending message box loop if this was the cursor cell to be redrawn.
4116 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
4120 void ScCompiler::CreateStringFromMatrix(
4121 OUStringBuffer& rBuffer, FormulaToken* pTokenP) const
4123 const ScMatrix* pMatrix = static_cast<ScToken*>(pTokenP)->GetMatrix();
4124 SCSIZE nC, nMaxC, nR, nMaxR;
4126 pMatrix->GetDimensions( nMaxC, nMaxR);
4128 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
4129 for( nR = 0 ; nR < nMaxR ; nR++)
4131 if( nR > 0)
4133 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
4136 for( nC = 0 ; nC < nMaxC ; nC++)
4138 if( nC > 0)
4140 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
4143 if( pMatrix->IsValue( nC, nR ) )
4145 if (pMatrix->IsBoolean(nC, nR))
4146 AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
4147 else
4149 sal_uInt16 nErr = pMatrix->GetError(nC, nR);
4150 if (nErr)
4151 rBuffer.append(ScGlobal::GetErrorString(nErr));
4152 else
4153 AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
4156 else if( pMatrix->IsEmpty( nC, nR ) )
4158 else if( pMatrix->IsString( nC, nR ) )
4159 AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() );
4162 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
4165 void ScCompiler::CreateStringFromSingleRef(OUStringBuffer& rBuffer,FormulaToken* _pTokenP) const
4167 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
4168 const OpCode eOp = _pTokenP->GetOpCode();
4169 const ScSingleRefData& rRef = static_cast<const ScToken*>(_pTokenP)->GetSingleRef();
4170 ScComplexRefData aRef;
4171 aRef.Ref1 = aRef.Ref2 = rRef;
4172 if ( eOp == ocColRowName )
4174 ScAddress aAbs = rRef.toAbs(aPos);
4175 if (pDoc->HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab()))
4177 OUString aStr = pDoc->GetString(aAbs);
4178 EnQuote( aStr );
4179 rBuffer.append(aStr);
4181 else
4183 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
4184 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef, maTabNames, aRef, true);
4187 else
4188 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef, maTabNames, aRef, true);
4191 void ScCompiler::CreateStringFromDoubleRef(OUStringBuffer& rBuffer,FormulaToken* _pTokenP) const
4193 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
4194 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef, maTabNames, static_cast<ScToken*>(_pTokenP)->GetDoubleRef(), false);
4197 void ScCompiler::CreateStringFromIndex(OUStringBuffer& rBuffer,FormulaToken* _pTokenP) const
4199 const OpCode eOp = _pTokenP->GetOpCode();
4200 OUStringBuffer aBuffer;
4201 switch ( eOp )
4203 case ocName:
4205 const ScRangeData* pData = GetRangeData( *_pTokenP);
4206 if (pData)
4207 aBuffer.append(pData->GetName());
4209 break;
4210 case ocDBArea:
4212 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
4213 if (pDBData)
4214 aBuffer.append(pDBData->GetName());
4216 break;
4217 default:
4218 ; // nothing
4220 if ( !aBuffer.isEmpty() )
4221 rBuffer.append(aBuffer.makeStringAndClear());
4222 else
4223 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
4226 void ScCompiler::LocalizeString( OUString& rName ) const
4228 ScGlobal::GetAddInCollection()->LocalizeString( rName );
4231 // Put quotes around string if non-alphanumeric characters are contained,
4232 // quote characters contained within are escaped by '\\'.
4233 bool ScCompiler::EnQuote( OUString& rStr )
4235 sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.getLength() );
4236 if ( !CharClass::isNumericType( nType )
4237 && CharClass::isAlphaNumericType( nType ) )
4238 return false;
4240 sal_Int32 nPos = 0;
4241 while ( (nPos = rStr.indexOf( '\'', nPos)) != -1 )
4243 rStr = rStr.replaceAt( nPos, 0, "\\" );
4244 nPos += 2;
4246 rStr = "'" + rStr + "'";
4247 return true;
4250 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
4252 return pConv->getSpecialSymbol(eType);
4255 void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
4257 // All known AddIn functions.
4258 sheet::FormulaOpCodeMapEntry aEntry;
4259 aEntry.Token.OpCode = ocExternal;
4261 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
4262 const long nCount = pColl->GetFuncCount();
4263 for (long i=0; i < nCount; ++i)
4265 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
4266 if (pFuncData)
4268 if ( _bIsEnglish )
4270 OUString aName;
4271 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
4272 aEntry.Name = aName;
4273 else
4274 aEntry.Name = pFuncData->GetUpperName();
4276 else
4277 aEntry.Name = pFuncData->GetUpperLocal();
4278 aEntry.Token.Data <<= OUString( pFuncData->GetOriginalName());
4279 _rVec.push_back( aEntry);
4282 // FIXME: what about those old non-UNO AddIns?
4285 bool ScCompiler::HandleSingleRef()
4287 ScSingleRefData& rRef = static_cast<ScToken*>(mpToken.get())->GetSingleRef();
4288 ScAddress aAbs = rRef.toAbs(aPos);
4289 if (!ValidAddress(aAbs))
4291 SetError( errNoRef );
4292 return true;
4294 SCCOL nCol = aAbs.Col();
4295 SCROW nRow = aAbs.Row();
4296 SCTAB nTab = aAbs.Tab();
4297 ScAddress aLook = aAbs;
4298 bool bColName = rRef.IsColRel();
4299 SCCOL nMyCol = aPos.Col();
4300 SCROW nMyRow = aPos.Row();
4301 bool bInList = false;
4302 bool bValidName = false;
4303 ScRangePairList* pRL = (bColName ?
4304 pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
4305 ScRange aRange;
4306 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4308 ScRangePair* pR = (*pRL)[i];
4309 if ( pR->GetRange(0).In( aLook ) )
4311 bInList = bValidName = true;
4312 aRange = pR->GetRange(1);
4313 if ( bColName )
4315 aRange.aStart.SetCol( nCol );
4316 aRange.aEnd.SetCol( nCol );
4318 else
4320 aRange.aStart.SetRow( nRow );
4321 aRange.aEnd.SetRow( nRow );
4323 break; // for
4326 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
4327 { // automagically or created by copying and NamePos isn't in list
4328 ScRefCellValue aCell;
4329 aCell.assign(*pDoc, aLook);
4330 bool bString = aCell.hasString();
4331 if (!bString && aCell.isEmpty())
4332 bString = true; // empty cell is ok
4333 if ( bString )
4334 { //! coresponds with ScInterpreter::ScColRowNameAuto()
4335 bValidName = true;
4336 if ( bColName )
4337 { // ColName
4338 SCROW nStartRow = nRow + 1;
4339 if ( nStartRow > MAXROW )
4340 nStartRow = MAXROW;
4341 SCROW nMaxRow = MAXROW;
4342 if ( nMyCol == nCol )
4343 { // formula cell in same column
4344 if ( nMyRow == nStartRow )
4345 { // take remainder under name cell
4346 nStartRow++;
4347 if ( nStartRow > MAXROW )
4348 nStartRow = MAXROW;
4350 else if ( nMyRow > nStartRow )
4351 { // from name cell down to formula cell
4352 nMaxRow = nMyRow - 1;
4355 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4356 { // next defined ColNameRange below limits row
4357 ScRangePair* pR = (*pRL)[i];
4358 const ScRange& rRange = pR->GetRange(1);
4359 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
4360 { // identical column range
4361 SCROW nTmp = rRange.aStart.Row();
4362 if ( nStartRow < nTmp && nTmp <= nMaxRow )
4363 nMaxRow = nTmp - 1;
4366 aRange.aStart.Set( nCol, nStartRow, nTab );
4367 aRange.aEnd.Set( nCol, nMaxRow, nTab );
4369 else
4370 { // RowName
4371 SCCOL nStartCol = nCol + 1;
4372 if ( nStartCol > MAXCOL )
4373 nStartCol = MAXCOL;
4374 SCCOL nMaxCol = MAXCOL;
4375 if ( nMyRow == nRow )
4376 { // formula cell in same row
4377 if ( nMyCol == nStartCol )
4378 { // take remainder right from name cell
4379 nStartCol++;
4380 if ( nStartCol > MAXCOL )
4381 nStartCol = MAXCOL;
4383 else if ( nMyCol > nStartCol )
4384 { // from name cell right to formula cell
4385 nMaxCol = nMyCol - 1;
4388 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4389 { // next defined RowNameRange to the right limits column
4390 ScRangePair* pR = (*pRL)[i];
4391 const ScRange& rRange = pR->GetRange(1);
4392 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
4393 { // identical row range
4394 SCCOL nTmp = rRange.aStart.Col();
4395 if ( nStartCol < nTmp && nTmp <= nMaxCol )
4396 nMaxCol = nTmp - 1;
4399 aRange.aStart.Set( nStartCol, nRow, nTab );
4400 aRange.aEnd.Set( nMaxCol, nRow, nTab );
4404 if ( bValidName )
4406 // And now the magic to distinguish between a range and a single
4407 // cell thereof, which is picked position-dependent of the formula
4408 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
4409 // SingleRef matching the column/row of the formula cell is
4410 // generated. A ocColRowName or ocIntersect as a neighbor results
4411 // in a range. Special case: if label is valid for a single cell, a
4412 // position independent SingleRef is generated.
4413 bool bSingle = (aRange.aStart == aRange.aEnd);
4414 bool bFound;
4415 if ( bSingle )
4416 bFound = true;
4417 else
4419 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
4420 FormulaToken* p2 = pArr->PeekNextNoSpaces();
4421 // begin/end of a formula => single
4422 OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd );
4423 OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd );
4424 if ( eOp1 != ocColRowName && eOp1 != ocIntersect
4425 && eOp2 != ocColRowName && eOp2 != ocIntersect )
4427 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
4428 (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
4429 bSingle = true;
4431 if ( bSingle )
4432 { // column and/or row must match range
4433 if ( bColName )
4435 bFound = (aRange.aStart.Row() <= nMyRow
4436 && nMyRow <= aRange.aEnd.Row());
4437 if ( bFound )
4438 aRange.aStart.SetRow( nMyRow );
4440 else
4442 bFound = (aRange.aStart.Col() <= nMyCol
4443 && nMyCol <= aRange.aEnd.Col());
4444 if ( bFound )
4445 aRange.aStart.SetCol( nMyCol );
4448 else
4449 bFound = true;
4451 if ( !bFound )
4452 SetError(errNoRef);
4453 else if ( !bCompileForFAP )
4455 ScTokenArray* pNew = new ScTokenArray();
4456 if ( bSingle )
4458 ScSingleRefData aRefData;
4459 aRefData.InitAddress( aRange.aStart );
4460 if ( bColName )
4461 aRefData.SetColRel( true );
4462 else
4463 aRefData.SetRowRel( true );
4464 aRefData.SetAddress(aRange.aStart, aPos);
4465 pNew->AddSingleReference( aRefData );
4467 else
4469 ScComplexRefData aRefData;
4470 aRefData.InitRange( aRange );
4471 if ( bColName )
4473 aRefData.Ref1.SetColRel( true );
4474 aRefData.Ref2.SetColRel( true );
4476 else
4478 aRefData.Ref1.SetRowRel( true );
4479 aRefData.Ref2.SetRowRel( true );
4481 aRefData.SetRange(aRange, aPos);
4482 if ( bInList )
4483 pNew->AddDoubleReference( aRefData );
4484 else
4485 { // automagically
4486 pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) );
4489 PushTokenArray( pNew, true );
4490 pNew->Reset();
4491 return GetToken();
4494 else
4495 SetError(errNoName);
4496 return true;
4499 bool ScCompiler::HandleDbData()
4501 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex());
4502 if ( !pDBData )
4503 SetError(errNoName);
4504 else if ( !bCompileForFAP )
4506 ScComplexRefData aRefData;
4507 aRefData.InitFlags();
4508 ScRange aRange;
4509 pDBData->GetArea(aRange);
4510 aRange.aEnd.SetTab(aRange.aStart.Tab());
4511 aRefData.SetRange(aRange, aPos);
4512 ScTokenArray* pNew = new ScTokenArray();
4513 pNew->AddDoubleReference( aRefData );
4514 PushTokenArray( pNew, true );
4515 pNew->Reset();
4516 return GetToken();
4518 return true;
4521 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
4523 return ScToken::ExtendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );
4526 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */