fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / compiler.cxx
blob2bec06721c863e5acd52c5335d7fd8d4e03e932f
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 <config_features.h>
22 #include "compiler.hxx"
24 #include <sfx2/app.hxx>
25 #include <sfx2/objsh.hxx>
26 #include <basic/sbmeth.hxx>
27 #include <basic/sbstar.hxx>
28 #include <svl/zforlist.hxx>
29 #include <svl/sharedstringpool.hxx>
30 #include <sal/macros.h>
31 #include <tools/rcid.h>
32 #include <tools/solar.h>
33 #include <unotools/charclass.hxx>
34 #include <com/sun/star/lang/Locale.hpp>
35 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
36 #include <com/sun/star/sheet/FormulaLanguage.hpp>
37 #include <com/sun/star/sheet/FormulaMapGroup.hpp>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/string.hxx>
40 #include <unotools/transliterationwrapper.hxx>
41 #include <tools/urlobj.hxx>
42 #include <rtl/math.hxx>
43 #include <rtl/ustring.hxx>
44 #include <svtools/miscopt.hxx>
45 #include <ctype.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <math.h>
50 #include "rangenam.hxx"
51 #include "dbdata.hxx"
52 #include "document.hxx"
53 #include "callform.hxx"
54 #include "addincol.hxx"
55 #include "refupdat.hxx"
56 #include "scresid.hxx"
57 #include "sc.hrc"
58 #include "globstr.hrc"
59 #include "formulacell.hxx"
60 #include "dociter.hxx"
61 #include "docoptio.hxx"
62 #include <formula/errorcodes.hxx>
63 #include "parclass.hxx"
64 #include "autonamecache.hxx"
65 #include "externalrefmgr.hxx"
66 #include "rangeutl.hxx"
67 #include "convuno.hxx"
68 #include "tokenuno.hxx"
69 #include "formulaparserpool.hxx"
70 #include "tokenarray.hxx"
71 #include "scmatrix.hxx"
72 #include <tokenstringcontext.hxx>
74 using namespace formula;
75 using namespace ::com::sun::star;
76 using ::std::vector;
78 CharClass* ScCompiler::pCharClassEnglish = NULL;
79 const ScCompiler::Convention* ScCompiler::pConventions[ ] = { NULL, NULL, NULL, NULL, NULL, NULL };
81 enum ScanState
83 ssGetChar,
84 ssGetBool,
85 ssGetValue,
86 ssGetString,
87 ssSkipString,
88 ssGetIdent,
89 ssGetReference,
90 ssSkipReference,
91 ssGetErrorConstant,
92 ssGetTableRefItem,
93 ssGetTableRefColumn,
94 ssStop
97 static const sal_Char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
99 using namespace ::com::sun::star::i18n;
101 void ScCompiler::fillFromAddInMap( NonConstOpCodeMapPtr xMap,FormulaGrammar::Grammar _eGrammar ) const
103 size_t nSymbolOffset;
104 switch( _eGrammar )
106 case FormulaGrammar::GRAM_PODF:
107 nSymbolOffset = offsetof( AddInMap, pUpper);
108 break;
109 default:
110 case FormulaGrammar::GRAM_ODFF:
111 nSymbolOffset = offsetof( AddInMap, pODFF);
112 break;
113 case FormulaGrammar::GRAM_ENGLISH:
114 nSymbolOffset = offsetof( AddInMap, pEnglish);
115 break;
117 const AddInMap* pMap = GetAddInMap();
118 const AddInMap* const pStop = pMap + GetAddInMapCount();
119 for ( ; pMap < pStop; ++pMap)
121 char const * const * ppSymbol =
122 reinterpret_cast< char const * const * >(
123 reinterpret_cast< char const * >(pMap) + nSymbolOffset);
124 xMap->putExternal( OUString::createFromAscii( *ppSymbol),
125 OUString::createFromAscii( pMap->pOriginal));
129 void ScCompiler::fillFromAddInCollectionUpperName( NonConstOpCodeMapPtr xMap ) const
131 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
132 long nCount = pColl->GetFuncCount();
133 for (long i=0; i < nCount; ++i)
135 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
136 if (pFuncData)
137 xMap->putExternalSoftly( pFuncData->GetUpperName(),
138 pFuncData->GetOriginalName());
142 void ScCompiler::fillFromAddInCollectionEnglishName( NonConstOpCodeMapPtr xMap ) const
144 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
145 long nCount = pColl->GetFuncCount();
146 for (long i=0; i < nCount; ++i)
148 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
149 if (pFuncData)
151 OUString aName;
152 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
153 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
154 else
155 xMap->putExternalSoftly( pFuncData->GetUpperName(),
156 pFuncData->GetOriginalName());
161 void ScCompiler::DeInit()
163 if (pCharClassEnglish)
165 delete pCharClassEnglish;
166 pCharClassEnglish = NULL;
170 bool ScCompiler::IsEnglishSymbol( const OUString& rName )
172 // function names are always case-insensitive
173 OUString aUpper = ScGlobal::pCharClass->uppercase(rName);
175 // 1. built-in function name
176 OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper );
177 if ( eOp != ocNone )
179 return true;
181 // 2. old add in functions
182 if (ScGlobal::GetFuncCollection()->findByName(aUpper))
184 return true;
187 // 3. new (uno) add in functions
188 OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
189 if (!aIntName.isEmpty())
191 return true;
193 return false; // no valid function name
196 void ScCompiler::InitCharClassEnglish()
198 ::com::sun::star::lang::Locale aLocale( "en", "US", "");
199 pCharClassEnglish = new CharClass(
200 ::comphelper::getProcessComponentContext(), LanguageTag( aLocale));
203 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar )
205 OSL_ENSURE( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED, "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
206 if (eGrammar == GetGrammar())
207 return; // nothing to be done
209 if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
211 meGrammar = eGrammar;
212 mxSymbols = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
214 else
216 FormulaGrammar::Grammar eMyGrammar = eGrammar;
217 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
218 OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage);
219 OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
220 if (!xMap)
222 xMap = GetOpCodeMap( ::com::sun::star::sheet::FormulaLanguage::NATIVE);
223 eMyGrammar = xMap->getGrammar();
226 // Save old grammar for call to SetGrammarAndRefConvention().
227 FormulaGrammar::Grammar eOldGrammar = GetGrammar();
228 // This also sets the grammar associated with the map!
229 SetFormulaLanguage( xMap);
231 // Override if necessary.
232 if (eMyGrammar != GetGrammar())
233 SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
237 // Unclear how this was intended to be refreshed when the
238 // grammar or sheet count is changed ? Ideally this would be
239 // a method on Document that would globally cache these.
240 std::vector<OUString> &ScCompiler::GetSetupTabNames() const
242 std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;
244 if (pDoc && rTabNames.empty())
246 rTabNames = pDoc->GetAllTableNames();
247 std::vector<OUString>::iterator it = rTabNames.begin(), itEnd = rTabNames.end();
248 for (; it != itEnd; ++it)
249 ScCompiler::CheckTabQuotes(*it, formula::FormulaGrammar::extractRefConvention(meGrammar));
252 return rTabNames;
255 void ScCompiler::SetNumberFormatter( SvNumberFormatter* pFormatter )
257 mpFormatter = pFormatter;
260 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap )
262 if (xMap.get())
264 mxSymbols = xMap;
265 if (mxSymbols->isEnglish())
267 if (!pCharClassEnglish)
268 InitCharClassEnglish();
269 pCharClass = pCharClassEnglish;
271 else
272 pCharClass = ScGlobal::pCharClass;
273 SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar());
277 void ScCompiler::SetGrammarAndRefConvention(
278 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
280 meGrammar = eNewGrammar; // SetRefConvention needs the new grammar set!
281 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
282 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
284 if (pDoc)
285 SetRefConvention( pDoc->GetAddressConvention());
286 else
287 SetRefConvention( GetRefConvention( FormulaGrammar::CONV_OOO ) );
289 else
290 SetRefConvention( eConv );
293 OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
295 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english
298 ScCompiler::Convention::~Convention()
300 delete [] mpCharTable;
301 mpCharTable = NULL;
304 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
306 meConv( eConv )
308 int i;
309 sal_uLong *t= new sal_uLong [128];
311 ScCompiler::pConventions[ meConv ] = this;
312 mpCharTable = t;
314 for (i = 0; i < 128; i++)
315 t[i] = SC_COMPILER_C_ILLEGAL;
317 // tdf#56036: Allow tabs/newlines in imported formulas (for now simply treat them as (and convert to) space)
318 // TODO: tdf#76310: allow saving newlines as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace")
319 // This is compliant with the OASIS decision (see https://issues.oasis-open.org/browse/OFFICE-701)
320 // Also, this would enable correct roundtrip from/to OOXML without loosing tabs/newlines
321 // This requires saving actual space characters in ocSpaces token, using them in UI and saving
322 /* tab */ t[ 9] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
323 /* lf */ t[10] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
324 /* cr */ t[13] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
326 /* */ t[32] = SC_COMPILER_C_CHAR_DONTCARE | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
327 /* ! */ t[33] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
328 if (FormulaGrammar::CONV_ODF == meConv)
329 /* ! */ t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
330 /* " */ t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
331 /* # */ t[35] = SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_CHAR_ERRCONST;
332 /* $ */ t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
333 if (FormulaGrammar::CONV_ODF == meConv)
334 /* $ */ t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
335 /* % */ t[37] = SC_COMPILER_C_VALUE;
336 /* & */ t[38] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
337 /* ' */ t[39] = SC_COMPILER_C_NAME_SEP;
338 /* ( */ t[40] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
339 /* ) */ t[41] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
340 /* * */ t[42] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
341 /* + */ t[43] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
342 /* , */ t[44] = SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE;
343 /* - */ t[45] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_EXP | SC_COMPILER_C_VALUE_SIGN;
344 /* . */ t[46] = SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_VALUE | SC_COMPILER_C_VALUE | SC_COMPILER_C_IDENT | SC_COMPILER_C_NAME;
345 /* / */ t[47] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
347 for (i = 48; i < 58; i++)
348 /* 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;
350 /* : */ t[58] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD;
351 /* ; */ t[59] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
352 /* < */ t[60] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
353 /* = */ t[61] = SC_COMPILER_C_CHAR | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
354 /* > */ t[62] = SC_COMPILER_C_CHAR_BOOL | SC_COMPILER_C_BOOL | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
355 /* ? */ t[63] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_NAME;
356 /* @ */ // FREE
358 for (i = 65; i < 91; i++)
359 /* 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;
361 if (FormulaGrammar::CONV_ODF == meConv)
363 /* [ */ t[91] = SC_COMPILER_C_ODF_LBRACKET;
364 /* \ */ // FREE
365 /* ] */ t[93] = SC_COMPILER_C_ODF_RBRACKET;
367 else if (FormulaGrammar::CONV_OOO == meConv)
369 /* [ */ t[91] = SC_COMPILER_C_CHAR;
370 /* \ */ // FREE
371 /* ] */ t[93] = SC_COMPILER_C_CHAR;
373 else if (FormulaGrammar::CONV_XL_OOX == meConv)
375 /* [ */ t[91] = SC_COMPILER_C_CHAR | SC_COMPILER_C_CHAR_IDENT;
376 /* \ */ // FREE
377 /* ] */ t[93] = SC_COMPILER_C_CHAR | SC_COMPILER_C_IDENT;
379 else if (FormulaGrammar::CONV_XL_A1 == meConv)
381 /* [ */ t[91] = SC_COMPILER_C_CHAR;
382 /* \ */ // FREE
383 /* ] */ t[93] = SC_COMPILER_C_CHAR;
385 else if( FormulaGrammar::CONV_XL_R1C1 == meConv )
387 /* [ */ t[91] = SC_COMPILER_C_IDENT;
388 /* \ */ // FREE
389 /* ] */ t[93] = SC_COMPILER_C_IDENT;
391 else
393 /* [ */ // FREE
394 /* \ */ // FREE
395 /* ] */ // FREE
398 /* ^ */ t[94] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
399 /* _ */ 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;
400 /* ` */ // FREE
402 for (i = 97; i < 123; i++)
403 /* 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;
405 /* { */ t[123] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array open
406 /* | */ t[124] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array row sep (Should be OOo specific)
407 /* } */ t[125] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP; // array close
408 /* ~ */ t[126] = SC_COMPILER_C_CHAR; // OOo specific
409 /* 127 */ // FREE
411 if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv )
413 /* */ t[32] |= SC_COMPILER_C_WORD;
414 /* ! */ t[33] |= SC_COMPILER_C_IDENT | SC_COMPILER_C_WORD;
415 /* " */ t[34] |= SC_COMPILER_C_WORD;
416 /* # */ t[35] &= (~SC_COMPILER_C_WORD_SEP);
417 /* # */ t[35] |= SC_COMPILER_C_WORD;
418 /* % */ t[37] |= SC_COMPILER_C_WORD;
419 /* ' */ t[39] |= SC_COMPILER_C_WORD;
421 /* % */ t[37] |= SC_COMPILER_C_WORD;
422 /* & */ t[38] |= SC_COMPILER_C_WORD;
423 /* ' */ t[39] |= SC_COMPILER_C_WORD;
424 /* ( */ t[40] |= SC_COMPILER_C_WORD;
425 /* ) */ t[41] |= SC_COMPILER_C_WORD;
426 /* * */ t[42] |= SC_COMPILER_C_WORD;
427 /* + */ t[43] |= SC_COMPILER_C_WORD;
428 #if 0 /* this really needs to be locale specific. */
429 /* , */ t[44] = SC_COMPILER_C_CHAR | SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_VALUE_SEP;
430 #else
431 /* , */ t[44] |= SC_COMPILER_C_WORD;
432 #endif
433 /* - */ t[45] |= SC_COMPILER_C_WORD;
435 /* ; */ t[59] |= SC_COMPILER_C_WORD;
436 /* < */ t[60] |= SC_COMPILER_C_WORD;
437 /* = */ t[61] |= SC_COMPILER_C_WORD;
438 /* > */ t[62] |= SC_COMPILER_C_WORD;
439 /* ? */ // question really is not permitted in sheet name
440 /* @ */ t[64] |= SC_COMPILER_C_WORD;
441 /* [ */ t[91] |= SC_COMPILER_C_WORD;
442 /* ] */ t[93] |= SC_COMPILER_C_WORD;
443 /* { */ t[123]|= SC_COMPILER_C_WORD;
444 /* | */ t[124]|= SC_COMPILER_C_WORD;
445 /* } */ t[125]|= SC_COMPILER_C_WORD;
446 /* ~ */ t[126]|= SC_COMPILER_C_WORD;
450 static bool lcl_isValidQuotedText( const OUString& rFormula, sal_Int32 nSrcPos, ParseResult& rRes )
452 // Tokens that start at ' can have anything in them until a final '
453 // but '' marks an escaped '
454 // We've earlier guaranteed that a string containing '' will be
455 // surrounded by '
456 if (nSrcPos < rFormula.getLength() && rFormula[nSrcPos] == '\'')
458 sal_Int32 nPos = nSrcPos+1;
459 while (nPos < rFormula.getLength())
461 if (rFormula[nPos] == '\'')
463 if ( (nPos+1 == rFormula.getLength()) || (rFormula[nPos+1] != '\'') )
465 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
466 rRes.EndPos = nPos+1;
467 return true;
469 ++nPos;
471 ++nPos;
475 return false;
478 static bool lcl_parseExternalName(
479 const OUString& rSymbol,
480 OUString& rFile,
481 OUString& rName,
482 const sal_Unicode cSep,
483 const ScDocument* pDoc = NULL,
484 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks = NULL )
486 /* TODO: future versions will have to support sheet-local names too, thus
487 * return a possible sheet name as well. */
488 const sal_Unicode* const pStart = rSymbol.getStr();
489 const sal_Unicode* p = pStart;
490 sal_Int32 nLen = rSymbol.getLength();
491 OUString aTmpFile, aTmpName;
492 sal_Int32 i = 0;
493 bool bInName = false;
494 if (cSep == '!')
496 // For XL use existing parser that resolves bracketed and quoted and
497 // indexed external document names.
498 ScRange aRange;
499 OUString aStartTabName, aEndTabName;
500 sal_uInt16 nFlags = 0;
501 p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName,
502 aEndTabName, nFlags, true, pExternalLinks );
503 if (!p || p == pStart)
504 return false;
505 i = sal_Int32(p - pStart);
507 for ( ; i < nLen; ++i, ++p)
509 sal_Unicode c = *p;
510 if (i == 0)
512 if (c == '.' || c == cSep)
513 return false;
515 if (c == '\'')
517 // Move to the next char and loop until the second single
518 // quote.
519 sal_Unicode cPrev = c;
520 ++i; ++p;
521 for (sal_Int32 j = i; j < nLen; ++j, ++p)
523 c = *p;
524 if (c == '\'')
526 if (j == i)
528 // empty quote e.g. (=''!Name)
529 return false;
532 if (cPrev == '\'')
534 // two consecutive quotes equal a single quote in
535 // the file name.
536 aTmpFile += OUString(c);
537 cPrev = 'a';
539 else
540 cPrev = c;
542 continue;
545 if (cPrev == '\'' && j != i)
547 // this is not a quote but the previous one is. This
548 // ends the parsing of the quoted segment. At this
549 // point, the current char must equal the separator
550 // char.
552 i = j;
553 bInName = true;
554 aTmpName += OUString(c); // Keep the separator as part of the name.
555 break;
557 aTmpFile += OUString(c);
558 cPrev = c;
561 if (!bInName)
563 // premature ending of the quoted segment.
564 return false;
567 if (c != cSep)
569 // only the separator is allowed after the closing quote.
570 return false;
573 continue;
577 if (bInName)
579 if (c == cSep)
581 // A second separator ? Not a valid external name.
582 return false;
584 aTmpName += OUString(c);
586 else
588 if (c == cSep)
590 bInName = true;
591 aTmpName += OUString(c); // Keep the separator as part of the name.
593 else
597 if (rtl::isAsciiAlphanumeric(c))
598 // allowed.
599 break;
601 if (c > 128)
602 // non-ASCII character is allowed.
603 break;
605 bool bValid = false;
606 switch (c)
608 case '_':
609 case '-':
610 case '.':
611 // these special characters are allowed.
612 bValid = true;
613 break;
615 if (bValid)
616 break;
618 return false;
620 while (false);
621 aTmpFile += OUString(c);
626 if (!bInName)
628 // No name found - most likely the symbol has no '!'s.
629 return false;
632 sal_Int32 nNameLen = aTmpName.getLength();
633 if (nNameLen < 2)
635 // Name must be at least 2-char long (separator plus name).
636 return false;
639 if (aTmpName[0] != cSep)
641 // 1st char of the name must equal the separator.
642 return false;
645 if (aTmpName[nNameLen-1] == '!')
647 // Check against #REF!.
648 if (aTmpName == "#REF!")
649 return false;
652 rFile = aTmpFile;
653 rName = aTmpName.copy(1); // Skip the first char as it is always the separator.
654 return true;
657 static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
658 const sal_Unicode cSep, bool bODF )
660 OUString aEscQuote("''");
661 OUString aFile(rFile.replaceAll("'", aEscQuote));
662 OUString aName(rName);
663 if (bODF)
664 aName = aName.replaceAll("'", aEscQuote);
665 OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
666 if (bODF)
667 aBuf.append( '[');
668 aBuf.append( "'" + aFile + "'" + OUString(cSep));
669 if (bODF)
670 aBuf.append( "$$'" );
671 aBuf.append( aName);
672 if (bODF)
673 aBuf.append( "']" );
674 return aBuf.makeStringAndClear();
677 static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
678 const vector<OUString>& rTabNames, const ScRange& rRef )
680 SCsTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
681 if (nTabSpan > 0)
683 size_t nCount = rTabNames.size();
684 vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
685 vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
686 if (itr == rTabNames.end())
688 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
689 return false;
692 size_t nDist = ::std::distance(itrBeg, itr);
693 if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
695 rTabName2 = ScGlobal::GetRscString(STR_NO_REF_TABLE);
696 return false;
699 rTabName2 = rTabNames[nDist+nTabSpan];
701 else
702 rTabName2 = rTabName1;
704 return true;
707 struct Convention_A1 : public ScCompiler::Convention
709 Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
710 static void MakeColStr( OUStringBuffer& rBuffer, SCCOL nCol );
711 static void MakeRowStr( OUStringBuffer& rBuffer, SCROW nRow );
713 ParseResult parseAnyToken( const OUString& rFormula,
714 sal_Int32 nSrcPos,
715 const CharClass* pCharClass) const SAL_OVERRIDE
717 ParseResult aRet;
718 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
719 return aRet;
721 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
722 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
723 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
724 // '?' allowed in range names because of Xcl :-/
725 static const char aAddAllowed[] = "?#";
726 return pCharClass->parseAnyToken( rFormula,
727 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
730 virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const SAL_OVERRIDE
732 return mpCharTable[static_cast<sal_uInt8>(c)];
736 void Convention_A1::MakeColStr( OUStringBuffer& rBuffer, SCCOL nCol )
738 if ( !ValidCol( nCol) )
739 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
740 else
741 ::ScColToAlpha( rBuffer, nCol);
744 void Convention_A1::MakeRowStr( OUStringBuffer& rBuffer, SCROW nRow )
746 if ( !ValidRow(nRow) )
747 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
748 else
749 rBuffer.append(sal_Int32(nRow + 1));
752 struct ConventionOOO_A1 : public Convention_A1
754 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
755 ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
757 static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
759 if (static_cast<size_t>(nTab) >= rTabNames.size())
760 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
761 else
762 rBuf.append(rTabNames[nTab]);
763 rBuf.append('.');
766 enum SingletonDisplay
768 SINGLETON_NONE,
769 SINGLETON_COL,
770 SINGLETON_ROW
773 static void MakeOneRefStrImpl(
774 OUStringBuffer& rBuffer,
775 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
776 const ScSingleRefData& rRef, const ScAddress& rAbsRef,
777 bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
779 // For ODF override singleton so earlier releases still can read what
780 // we write now as of 2015-06-26.
781 /* TODO: we may want to change that in future in a few releases. */
782 if (bODF)
783 eSingletonDisplay = SINGLETON_NONE;
785 if( rRef.IsFlag3D() || bForceTab )
787 if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
789 if (!rRef.IsTabRel())
790 rBuffer.append('$');
791 rBuffer.append(rErrRef);
792 rBuffer.append('.');
794 else
796 if (!rRef.IsTabRel())
797 rBuffer.append('$');
798 MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
801 else if (bODF)
802 rBuffer.append('.');
804 if (eSingletonDisplay != SINGLETON_ROW)
806 if (!rRef.IsColRel())
807 rBuffer.append('$');
808 if (!ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
809 rBuffer.append(rErrRef);
810 else
811 MakeColStr(rBuffer, rAbsRef.Col());
814 if (eSingletonDisplay != SINGLETON_COL)
816 if (!rRef.IsRowRel())
817 rBuffer.append('$');
818 if (!ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
819 rBuffer.append(rErrRef);
820 else
821 MakeRowStr(rBuffer, rAbsRef.Row());
825 static SingletonDisplay getSingletonDisplay( const ScAddress& rAbs1, const ScAddress& rAbs2,
826 const ScComplexRefData& rRef )
828 // If any part is error, display as such.
829 if (!ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
830 !ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
831 return SINGLETON_NONE;
833 // A:A or $A:$A or A:$A or $A:A
834 if (rRef.IsEntireCol())
835 return SINGLETON_COL;
837 // 1:1 or $1:$1 or 1:$1 or $1:1
838 if (rRef.IsEntireRow())
839 return SINGLETON_ROW;
841 return SINGLETON_NONE;
844 virtual void makeRefStr( OUStringBuffer& rBuffer,
845 formula::FormulaGrammar::Grammar /*eGram*/,
846 const ScAddress& rPos,
847 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
848 const ScComplexRefData& rRef,
849 bool bSingleRef,
850 bool /*bFromRangeName*/ ) const SAL_OVERRIDE
852 // In case absolute/relative positions weren't separately available:
853 // transform relative to absolute!
854 ScAddress aAbs1 = rRef.Ref1.toAbs(rPos), aAbs2;
855 if( !bSingleRef )
856 aAbs2 = rRef.Ref2.toAbs(rPos);
858 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE : getSingletonDisplay( aAbs1, aAbs2, rRef);
859 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton);
860 if (!bSingleRef)
862 rBuffer.append(':');
863 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
864 eSingleton);
868 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const SAL_OVERRIDE
870 switch (eSymType)
872 case ScCompiler::Convention::ABS_SHEET_PREFIX:
873 return '$';
874 case ScCompiler::Convention::SHEET_SEPARATOR:
875 return '.';
878 return sal_Unicode(0);
881 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
882 const ScDocument* pDoc,
883 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const SAL_OVERRIDE
885 return lcl_parseExternalName(rSymbol, rFile, rName, '#', pDoc, pExternalLinks);
888 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
889 const OUString& rName ) const SAL_OVERRIDE
891 return lcl_makeExternalNameStr( rFile, rName, '#', false);
894 static bool makeExternalSingleRefStr(
895 OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
896 const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
898 ScAddress aAbsRef = rRef.toAbs(rPos);
899 if (bDisplayTabName)
901 OUString aFile;
902 if (bEncodeUrl)
903 aFile = rFileName;
904 else
905 aFile = INetURLObject::decode(rFileName, INetURLObject::DECODE_UNAMBIGUOUS);
907 rBuffer.append("'" + aFile.replaceAll("'", "''") + "'#");
909 if (!rRef.IsTabRel())
910 rBuffer.append('$');
911 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
913 rBuffer.append('.');
916 if (!rRef.IsColRel())
917 rBuffer.append('$');
918 MakeColStr( rBuffer, aAbsRef.Col());
919 if (!rRef.IsRowRel())
920 rBuffer.append('$');
921 MakeRowStr( rBuffer, aAbsRef.Row());
923 return true;
926 static void makeExternalRefStrImpl(
927 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
928 const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
930 if (bODF)
931 rBuffer.append( '[');
933 bool bEncodeUrl = bODF;
934 makeExternalSingleRefStr(rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
935 if (bODF)
936 rBuffer.append( ']');
939 virtual void makeExternalRefStr(
940 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
941 const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE
943 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, false);
946 static void makeExternalRefStrImpl(
947 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
948 const std::vector<OUString>& rTabNames, const OUString& rTabName,
949 const ScComplexRefData& rRef, bool bODF )
951 ScRange aAbsRange = rRef.toAbs(rPos);
953 if (bODF)
954 rBuffer.append( '[');
955 // Ensure that there's always a closing bracket, no premature returns.
956 bool bEncodeUrl = bODF;
960 if (!makeExternalSingleRefStr(rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
961 break;
963 rBuffer.append(':');
965 OUString aLastTabName;
966 bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
967 if (bDisplayTabName)
969 // Get the name of the last table.
970 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
972 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
973 // aLastTabName contains #REF!, proceed.
976 else if (bODF)
977 rBuffer.append( '.'); // need at least the sheet separator in ODF
978 makeExternalSingleRefStr(
979 rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
980 } while (false);
982 if (bODF)
983 rBuffer.append( ']');
986 virtual void makeExternalRefStr(
987 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
988 const std::vector<OUString>& rTabNames, const OUString& rTabName,
989 const ScComplexRefData& rRef ) const SAL_OVERRIDE
991 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
995 struct ConventionOOO_A1_ODF : public ConventionOOO_A1
997 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
999 virtual void makeRefStr( OUStringBuffer& rBuffer,
1000 formula::FormulaGrammar::Grammar eGram,
1001 const ScAddress& rPos,
1002 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1003 const ScComplexRefData& rRef,
1004 bool bSingleRef,
1005 bool /*bFromRangeName*/ ) const SAL_OVERRIDE
1007 rBuffer.append('[');
1008 // In case absolute/relative positions weren't separately available:
1009 // transform relative to absolute!
1010 ScAddress aAbs1 = rRef.Ref1.toAbs(rPos), aAbs2;
1011 if( !bSingleRef )
1012 aAbs2 = rRef.Ref2.toAbs(rPos);
1014 if (FormulaGrammar::isODFF(eGram) && (!ValidAddress(aAbs1) || !ValidAddress(aAbs2)))
1016 rBuffer.append(rErrRef);
1017 // For ODFF write [#REF!], but not for PODF so apps reading ODF
1018 // 1.0/1.1 may have a better chance if they implemented the old
1019 // form.
1021 else
1023 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE : getSingletonDisplay( aAbs1, aAbs2, rRef);
1024 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton);
1025 if (!bSingleRef)
1027 rBuffer.append(':');
1028 MakeOneRefStrImpl(rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
1029 eSingleton);
1032 rBuffer.append(']');
1035 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1036 const OUString& rName ) const SAL_OVERRIDE
1038 return lcl_makeExternalNameStr( rFile, rName, '#', true);
1041 virtual void makeExternalRefStr(
1042 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1043 const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE
1045 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabName, rRef, true);
1048 virtual void makeExternalRefStr(
1049 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1050 const std::vector<OUString>& rTabNames,
1051 const OUString& rTabName, const ScComplexRefData& rRef ) const SAL_OVERRIDE
1053 makeExternalRefStrImpl(rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
1057 struct ConventionXL
1059 virtual ~ConventionXL()
1063 static void GetTab(
1064 const ScAddress& rPos, const std::vector<OUString>& rTabNames,
1065 const ScSingleRefData& rRef, OUString& rTabName )
1067 ScAddress aAbs = rRef.toAbs(rPos);
1068 if (rRef.IsTabDeleted() || static_cast<size_t>(aAbs.Tab()) >= rTabNames.size())
1070 rTabName = ScGlobal::GetRscString( STR_NO_REF_TABLE );
1071 return;
1073 rTabName = rTabNames[aAbs.Tab()];
1076 static void MakeTabStr( OUStringBuffer& rBuf,
1077 const ScAddress& rPos,
1078 const std::vector<OUString>& rTabNames,
1079 const ScComplexRefData& rRef,
1080 bool bSingleRef )
1082 if( rRef.Ref1.IsFlag3D() )
1084 OUString aStartTabName, aEndTabName;
1086 GetTab(rPos, rTabNames, rRef.Ref1, aStartTabName);
1088 if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1090 GetTab(rPos, rTabNames, rRef.Ref2, aEndTabName);
1093 rBuf.append( aStartTabName );
1094 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1096 rBuf.append( ':' );
1097 rBuf.append( aEndTabName );
1100 rBuf.append( '!' );
1104 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1106 switch (eSymType)
1108 case ScCompiler::Convention::ABS_SHEET_PREFIX:
1109 return sal_Unicode(0);
1110 case ScCompiler::Convention::SHEET_SEPARATOR:
1111 return '!';
1113 return sal_Unicode(0);
1116 static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1117 const ScDocument* pDoc,
1118 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1120 return lcl_parseExternalName( rSymbol, rFile, rName, '!', pDoc, pExternalLinks);
1123 static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
1125 return lcl_makeExternalNameStr( rFile, rName, '!', false);
1128 static void makeExternalDocStr( OUStringBuffer& rBuffer, const OUString& rFullName, bool bEncodeUrl )
1130 // Format that is easier to deal with inside OOo, because we use file
1131 // URL, and all characetrs are allowed. Check if it makes sense to do
1132 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1133 // and allows relative file path.
1135 // ['file:///path/to/source/filename.xls']
1137 rBuffer.append('[');
1138 rBuffer.append('\'');
1139 OUString aFullName;
1140 if (bEncodeUrl)
1141 aFullName = rFullName;
1142 else
1143 aFullName = INetURLObject::decode(rFullName, INetURLObject::DECODE_UNAMBIGUOUS);
1145 const sal_Unicode* pBuf = aFullName.getStr();
1146 sal_Int32 nLen = aFullName.getLength();
1147 for (sal_Int32 i = 0; i < nLen; ++i)
1149 const sal_Unicode c = pBuf[i];
1150 if (c == '\'')
1151 rBuffer.append(c);
1152 rBuffer.append(c);
1154 rBuffer.append('\'');
1155 rBuffer.append(']');
1158 static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
1159 const vector<OUString>& rTabNames,
1160 const ScRange& rRef )
1162 OUString aLastTabName;
1163 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1165 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1166 return;
1169 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1170 if (rTabName != aLastTabName)
1172 rBuf.append(':');
1173 ScRangeStringConverter::AppendTableName(rBuf, rTabName);
1177 virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const
1179 sal_Int32 nLen = rFormula.getLength();
1180 const sal_Unicode* p = rFormula.getStr();
1181 sal_Unicode cPrev = 0;
1182 for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1184 sal_Unicode c = p[i];
1185 if (i == rSrcPos)
1187 // first character must be '['.
1188 if (c != '[')
1189 return;
1191 else if (i == rSrcPos + 1)
1193 // second character must be a single quote.
1194 if (c != '\'')
1195 return;
1197 else if (c == '\'')
1199 if (cPrev == '\'')
1200 // two successive single quote is treated as a single
1201 // valid character.
1202 c = 'a';
1204 else if (c == ']')
1206 if (cPrev == '\'')
1208 // valid source document path found. Increment the
1209 // current position to skip the source path.
1210 rSrcPos = i + 1;
1211 if (rSrcPos >= nLen)
1212 rSrcPos = nLen - 1;
1213 return;
1215 else
1216 return;
1218 else
1220 // any other character
1221 if (i > rSrcPos + 2 && cPrev == '\'')
1222 // unless it's the 3rd character, a normal character
1223 // following immediately a single quote is invalid.
1224 return;
1226 cPrev = c;
1231 struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1233 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1234 ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1236 static void makeSingleCellStr( OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
1238 if (!rRef.IsColRel())
1239 rBuf.append('$');
1240 MakeColStr(rBuf, rAbs.Col());
1241 if (!rRef.IsRowRel())
1242 rBuf.append('$');
1243 MakeRowStr(rBuf, rAbs.Row());
1246 virtual void makeRefStr( OUStringBuffer& rBuf,
1247 formula::FormulaGrammar::Grammar /*eGram*/,
1248 const ScAddress& rPos,
1249 const OUString& /*rErrRef*/, const std::vector<OUString>& rTabNames,
1250 const ScComplexRefData& rRef,
1251 bool bSingleRef,
1252 bool /*bFromRangeName*/ ) const SAL_OVERRIDE
1254 ScComplexRefData aRef( rRef );
1256 // Play fast and loose with invalid refs. There is not much point in producing
1257 // Foo!A1:#REF! versus #REF! at this point
1258 ScAddress aAbs1 = aRef.Ref1.toAbs(rPos), aAbs2;
1260 MakeTabStr(rBuf, rPos, rTabNames, aRef, bSingleRef);
1262 if (!ValidAddress(aAbs1))
1264 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1265 return;
1268 if( !bSingleRef )
1270 aAbs2 = aRef.Ref2.toAbs(rPos);
1271 if (!ValidAddress(aAbs2))
1273 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1274 return;
1277 if (aAbs1.Col() == 0 && aAbs2.Col() >= MAXCOL)
1279 if (!aRef.Ref1.IsRowRel())
1280 rBuf.append( '$' );
1281 MakeRowStr(rBuf, aAbs1.Row());
1282 rBuf.append( ':' );
1283 if (!aRef.Ref2.IsRowRel())
1284 rBuf.append( '$' );
1285 MakeRowStr(rBuf, aAbs2.Row());
1286 return;
1289 if (aAbs1.Row() == 0 && aAbs2.Row() >= MAXROW)
1291 if (!aRef.Ref1.IsColRel())
1292 rBuf.append( '$' );
1293 MakeColStr(rBuf, aAbs1.Col());
1294 rBuf.append( ':' );
1295 if (!aRef.Ref2.IsColRel())
1296 rBuf.append( '$' );
1297 MakeColStr(rBuf, aAbs2.Col());
1298 return;
1302 makeSingleCellStr(rBuf, aRef.Ref1, aAbs1);
1303 if (!bSingleRef)
1305 rBuf.append( ':' );
1306 makeSingleCellStr(rBuf, aRef.Ref2, aAbs2);
1310 virtual ParseResult parseAnyToken( const OUString& rFormula,
1311 sal_Int32 nSrcPos,
1312 const CharClass* pCharClass) const SAL_OVERRIDE
1314 parseExternalDocName(rFormula, nSrcPos);
1316 ParseResult aRet;
1317 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1318 return aRet;
1320 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1321 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1322 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1323 // '?' allowed in range names
1324 const OUString aAddAllowed("?!");
1325 return pCharClass->parseAnyToken( rFormula,
1326 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1329 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const SAL_OVERRIDE
1331 return ConventionXL::getSpecialSymbol(eSymType);
1334 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1335 const ScDocument* pDoc,
1336 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const SAL_OVERRIDE
1338 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1341 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1342 const OUString& rName ) const SAL_OVERRIDE
1344 return ConventionXL::makeExternalNameStr(rFile, rName);
1347 virtual void makeExternalRefStr(
1348 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1349 const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE
1351 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1352 // This is a little different from the format Excel uses, as Excel
1353 // puts [] only around the file name. But we need to enclose the
1354 // whole file path with [] because the file name can contain any
1355 // characters.
1357 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1358 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1359 rBuffer.append('!');
1361 makeSingleCellStr(rBuffer, rRef, rRef.toAbs(rPos));
1364 virtual void makeExternalRefStr(
1365 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1366 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1367 const ScComplexRefData& rRef ) const SAL_OVERRIDE
1369 ScRange aAbsRef = rRef.toAbs(rPos);
1371 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1372 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1373 rBuffer.append('!');
1375 makeSingleCellStr(rBuffer, rRef.Ref1, aAbsRef.aStart);
1376 if (aAbsRef.aStart != aAbsRef.aEnd)
1378 rBuffer.append(':');
1379 makeSingleCellStr(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1384 struct ConventionXL_OOX : public ConventionXL_A1
1386 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1388 virtual void makeRefStr( OUStringBuffer& rBuf,
1389 formula::FormulaGrammar::Grammar eGram,
1390 const ScAddress& rPos,
1391 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1392 const ScComplexRefData& rRef,
1393 bool bSingleRef,
1394 bool bFromRangeName ) const SAL_OVERRIDE
1396 // In OOXML relative references in named expressions are relative to
1397 // column 0 and row 0. Relative sheet references don't exist.
1398 ScAddress aPos( rPos );
1399 if (bFromRangeName)
1401 // XXX NOTE: by decrementing the reference position we may end up
1402 // with resolved references with negative values. There's no proper
1403 // way to solve that or wrap them around without sheet dimensions
1404 // that are stored along. That, or blindly assume fixed dimensions
1405 // here and in import.
1406 /* TODO: maybe do that blind fixed dimensions wrap? */
1407 aPos.SetCol(0);
1408 aPos.SetRow(0);
1411 ConventionXL_A1::makeRefStr( rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
1414 virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
1415 const OUString& rName ) const SAL_OVERRIDE
1417 // [N]!DefinedName is a workbook global name.
1418 return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );
1420 /* TODO: add support for sheet local names, would be
1421 * [N]'Sheet Name'!DefinedName
1422 * Similar to makeExternalRefStr() but with DefinedName instead of
1423 * CellStr. */
1426 virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const SAL_OVERRIDE
1428 sal_Int32 nLen = rFormula.getLength();
1429 const sal_Unicode* p = rFormula.getStr();
1430 for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1432 sal_Unicode c = p[i];
1433 if (i == rSrcPos)
1435 // first character must be '['.
1436 if (c != '[')
1437 return;
1439 else if (c == ']')
1441 rSrcPos = i + 1;
1442 return;
1447 virtual void makeExternalRefStr(
1448 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1449 const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE
1451 // [N]'Sheet Name'!$A$1
1452 // Where N is a 1-based positive integer number of a file name in OOXML
1453 // xl/externalLinks/externalLinkN.xml
1455 ConventionXL_OOX::makeExternalDocStr(rBuffer, nFileId);
1456 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1457 rBuffer.append('!');
1459 makeSingleCellStr(rBuffer, rRef, rRef.toAbs(rPos));
1462 virtual void makeExternalRefStr(
1463 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1464 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1465 const ScComplexRefData& rRef ) const SAL_OVERRIDE
1467 ScRange aAbsRef = rRef.toAbs(rPos);
1469 ConventionXL_OOX::makeExternalDocStr(rBuffer, nFileId);
1470 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1471 rBuffer.append('!');
1473 makeSingleCellStr(rBuffer, rRef.Ref1, aAbsRef.aStart);
1474 if (aAbsRef.aStart != aAbsRef.aEnd)
1476 rBuffer.append(':');
1477 makeSingleCellStr(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1481 static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
1483 rBuffer.append('[').append( OUString::number( nFileId+1)).append(']');
1487 static void
1488 r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1490 rBuf.append( 'C' );
1491 if( rRef.IsColRel() )
1493 SCCOL nCol = rRef.Col();
1494 if (nCol != 0)
1495 rBuf.append("[").append(OUString::number(nCol)).append("]");
1497 else
1498 rBuf.append( OUString::number( rAbsRef.Col() + 1 ) );
1500 static void
1501 r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1503 rBuf.append( 'R' );
1504 if( rRef.IsRowRel() )
1506 if (rRef.Row() != 0)
1508 rBuf.append("[").append( OUString::number(rRef.Row()) ).append("]");
1511 else
1512 rBuf.append( OUString::number( rAbsRef.Row() + 1 ) );
1515 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1517 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1519 virtual void makeRefStr( OUStringBuffer& rBuf,
1520 formula::FormulaGrammar::Grammar /*eGram*/,
1521 const ScAddress& rPos,
1522 const OUString& /*rErrRef*/, const std::vector<OUString>& rTabNames,
1523 const ScComplexRefData& rRef,
1524 bool bSingleRef,
1525 bool /*bFromRangeName*/ ) const SAL_OVERRIDE
1527 ScRange aAbsRef = rRef.toAbs(rPos);
1528 ScComplexRefData aRef( rRef );
1530 MakeTabStr(rBuf, rPos, rTabNames, aRef, bSingleRef);
1532 // Play fast and loose with invalid refs. There is not much point in producing
1533 // Foo!A1:#REF! versus #REF! at this point
1534 if (!ValidCol(aAbsRef.aStart.Col()) || !ValidRow(aAbsRef.aStart.Row()))
1536 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1537 return;
1540 if( !bSingleRef )
1542 if (!ValidCol(aAbsRef.aEnd.Col()) || !ValidRow(aAbsRef.aEnd.Row()))
1544 rBuf.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1545 return;
1548 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= MAXCOL)
1550 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1551 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
1552 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
1554 rBuf.append( ':' );
1555 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1557 return;
1561 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= MAXROW)
1563 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1564 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
1565 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1567 rBuf.append( ':' );
1568 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1570 return;
1574 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1575 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1576 if (!bSingleRef)
1578 rBuf.append( ':' );
1579 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1580 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1584 ParseResult parseAnyToken( const OUString& rFormula,
1585 sal_Int32 nSrcPos,
1586 const CharClass* pCharClass) const SAL_OVERRIDE
1588 parseExternalDocName(rFormula, nSrcPos);
1590 ParseResult aRet;
1591 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1592 return aRet;
1594 static const sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1595 KParseTokens::ASC_UNDERSCORE ;
1596 static const sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1597 // '?' allowed in range names
1598 const OUString aAddAllowed("?-[]!");
1600 return pCharClass->parseAnyToken( rFormula,
1601 nSrcPos, nStartFlags, aAddAllowed, nContFlags, aAddAllowed );
1604 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const SAL_OVERRIDE
1606 return ConventionXL::getSpecialSymbol(eSymType);
1609 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1610 const ScDocument* pDoc,
1611 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const SAL_OVERRIDE
1613 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks);
1616 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1617 const OUString& rName ) const SAL_OVERRIDE
1619 return ConventionXL::makeExternalNameStr(rFile, rName);
1622 virtual void makeExternalRefStr(
1623 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1624 const OUString& rTabName, const ScSingleRefData& rRef ) const SAL_OVERRIDE
1626 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1627 // This is a little different from the format Excel uses, as Excel
1628 // puts [] only around the file name. But we need to enclose the
1629 // whole file path with [] because the file name can contain any
1630 // characters.
1632 ScAddress aAbsRef = rRef.toAbs(rPos);
1633 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1634 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1635 rBuffer.append('!');
1637 r1c1_add_row(rBuffer, rRef, aAbsRef);
1638 r1c1_add_col(rBuffer, rRef, aAbsRef);
1641 virtual void makeExternalRefStr(
1642 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1643 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1644 const ScComplexRefData& rRef ) const SAL_OVERRIDE
1646 ScRange aAbsRef = rRef.toAbs(rPos);
1648 ConventionXL::makeExternalDocStr(rBuffer, rFileName, false);
1649 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1650 rBuffer.append('!');
1652 if (!ValidCol(aAbsRef.aEnd.Col()) || !ValidRow(aAbsRef.aEnd.Row()))
1654 rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
1655 return;
1658 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= MAXCOL)
1660 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1661 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1663 rBuffer.append(':');
1664 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1666 return;
1669 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= MAXROW)
1671 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1672 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1674 rBuffer.append(':');
1675 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1677 return;
1680 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1681 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1682 rBuffer.append(':');
1683 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1684 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1687 virtual sal_uLong getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const SAL_OVERRIDE
1689 sal_uLong nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
1690 if (c == '-' && cLast == '[')
1691 // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1692 nFlags |= SC_COMPILER_C_IDENT;
1693 return nFlags;
1697 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr ) :
1698 FormulaCompiler(rArr),
1699 pDoc(rCxt.getDoc()),
1700 aPos(rPos),
1701 mpFormatter(pDoc->GetFormatTable()),
1702 pCharClass(ScGlobal::pCharClass),
1703 mnPredetectedReference(0),
1704 mnRangeOpPosInSymbol(-1),
1705 pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1706 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1707 mbCloseBrackets(true),
1708 mbRewind(false),
1709 maTabNames(rCxt.getTabNames())
1711 nMaxTab = pDoc->GetTableCount() - 1;
1712 SetGrammar(rCxt.getGrammar());
1715 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos,ScTokenArray& rArr)
1716 : FormulaCompiler(rArr),
1717 pDoc( pDocument ),
1718 aPos( rPos ),
1719 mpFormatter(pDoc->GetFormatTable()),
1720 nSrcPos(0),
1721 pCharClass( ScGlobal::pCharClass ),
1722 mnPredetectedReference(0),
1723 mnRangeOpPosInSymbol(-1),
1724 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1725 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1726 mbCloseBrackets( true ),
1727 mbRewind( false )
1729 nMaxTab = pDoc->GetTableCount() - 1;
1732 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos ) :
1733 pDoc(rCxt.getDoc()),
1734 aPos(rPos),
1735 mpFormatter(pDoc ? pDoc->GetFormatTable() : NULL),
1736 pCharClass(ScGlobal::pCharClass),
1737 mnPredetectedReference(0),
1738 mnRangeOpPosInSymbol(-1),
1739 pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1740 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1741 mbCloseBrackets(true),
1742 mbRewind(false),
1743 maTabNames(rCxt.getTabNames())
1745 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1746 SetGrammar(rCxt.getGrammar());
1749 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos)
1751 pDoc( pDocument ),
1752 aPos( rPos ),
1753 mpFormatter(pDoc ? pDoc->GetFormatTable() : NULL),
1754 nSrcPos(0),
1755 pCharClass( ScGlobal::pCharClass ),
1756 mnPredetectedReference(0),
1757 mnRangeOpPosInSymbol(-1),
1758 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1759 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1760 mbCloseBrackets( true ),
1761 mbRewind( false )
1763 nMaxTab = pDoc ? pDoc->GetTableCount() - 1 : 0;
1766 ScCompiler::~ScCompiler()
1770 void ScCompiler::CheckTabQuotes( OUString& rString,
1771 const FormulaGrammar::AddressConvention eConv )
1773 using namespace ::com::sun::star::i18n;
1774 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1775 sal_Int32 nContFlags = nStartFlags;
1776 ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken(
1777 KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_OUSTRING, nContFlags, EMPTY_OUSTRING);
1778 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
1780 switch ( eConv )
1782 default :
1783 case FormulaGrammar::CONV_UNSPECIFIED :
1784 break;
1785 case FormulaGrammar::CONV_OOO :
1786 case FormulaGrammar::CONV_XL_A1 :
1787 case FormulaGrammar::CONV_XL_R1C1 :
1788 case FormulaGrammar::CONV_XL_OOX :
1789 case FormulaGrammar::CONV_ODF :
1790 if( bNeedsQuote )
1792 const OUString one_quote('\'');
1793 const OUString two_quote("''");
1794 // escape embedded quotes
1795 rString = rString.replaceAll( one_quote, two_quote );
1797 break;
1800 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1802 // Prevent any possible confusion resulting from pure numeric sheet names.
1803 bNeedsQuote = true;
1806 if( bNeedsQuote )
1808 rString = "'" + rString + "'";
1812 sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
1814 if (rString[0] != '\'')
1815 return -1;
1816 sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP);
1817 // it must be 'Doc'#
1818 if (nPos != -1 && rString[nPos-1] != '\'')
1819 nPos = -1;
1820 return nPos;
1823 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv )
1825 const Convention* p = GetRefConvention(eConv);
1826 if (p)
1827 SetRefConvention(p);
1830 const ScCompiler::Convention* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv )
1833 switch (eConv)
1835 case FormulaGrammar::CONV_OOO:
1837 static const ConventionOOO_A1 ConvOOO_A1;
1838 return &ConvOOO_A1;
1840 case FormulaGrammar::CONV_ODF:
1842 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
1843 return &ConvOOO_A1_ODF;
1845 case FormulaGrammar::CONV_XL_A1:
1847 static const ConventionXL_A1 ConvXL_A1;
1848 return &ConvXL_A1;
1850 case FormulaGrammar::CONV_XL_R1C1:
1852 static const ConventionXL_R1C1 ConvXL_R1C1;
1853 return &ConvXL_R1C1;
1855 case FormulaGrammar::CONV_XL_OOX:
1857 static const ConventionXL_OOX ConvXL_OOX;
1858 return &ConvXL_OOX;
1860 case FormulaGrammar::CONV_UNSPECIFIED:
1861 default:
1865 return NULL;
1868 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP )
1870 pConv = pConvP;
1871 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
1872 OSL_ENSURE( FormulaGrammar::isSupported( meGrammar),
1873 "ScCompiler::SetRefConvention: unsupported grammar resulting");
1876 void ScCompiler::SetError(sal_uInt16 nError)
1878 if( !pArr->GetCodeError() )
1879 pArr->SetCodeError( nError);
1882 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
1884 const sal_Unicode* const pStop = pDst + nMax;
1885 while ( *pSrc && pDst < pStop )
1887 *pDst++ = *pSrc++;
1889 *pDst = 0;
1890 return pDst;
1893 // NextSymbol
1895 // Zerlegt die Formel in einzelne Symbole fuer die weitere
1896 // Verarbeitung (Turing-Maschine).
1898 // Ausgangs Zustand = GetChar
1900 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1901 //---------------+-------------------+-----------------------+---------------
1902 // GetChar | ;()+-*/^=& | Symbol=Zeichen | Stop
1903 // | <> | Symbol=Zeichen | GetBool
1904 // | $ Buchstabe | Symbol=Zeichen | GetWord
1905 // | Ziffer | Symbol=Zeichen | GetValue
1906 // | " | Keine | GetString
1907 // | Sonst | Keine | GetChar
1908 //---------------+-------------------+-----------------------+---------------
1909 // GetBool | => | Symbol=Symbol+Zeichen | Stop
1910 // | Sonst | Dec(CharPos) | Stop
1911 //---------------+-------------------+-----------------------+---------------
1912 // GetWord | SepSymbol | Dec(CharPos) | Stop
1913 // | ()+-*/^=<>&~ | |
1914 // | Leerzeichen | Dec(CharPos) | Stop
1915 // | $_:. | |
1916 // | Buchstabe,Ziffer | Symbol=Symbol+Zeichen | GetWord
1917 // | Sonst | Fehler | Stop
1918 //---------------+-------------------+-----------------------+---------------
1919 // GetValue | ;()*/^=<>& | |
1920 // | Leerzeichen | Dec(CharPos) | Stop
1921 // | Ziffer E+-%,. | Symbol=Symbol+Zeichen | GetValue
1922 // | Sonst | Fehler | Stop
1923 //---------------+-------------------+-----------------------+---------------
1924 // GetString | " | Keine | Stop
1925 // | Sonst | Symbol=Symbol+Zeichen | GetString
1926 //---------------+-------------------+-----------------------+---------------
1928 sal_Int32 ScCompiler::NextSymbol(bool bInArray)
1930 cSymbol[MAXSTRLEN-1] = 0; // Stopper
1931 sal_Unicode* pSym = cSymbol;
1932 const sal_Unicode* const pStart = aFormula.getStr();
1933 const sal_Unicode* pSrc = pStart + nSrcPos;
1934 bool bi18n = false;
1935 sal_Unicode c = *pSrc;
1936 sal_Unicode cLast = 0;
1937 bool bQuote = false;
1938 mnRangeOpPosInSymbol = -1;
1939 ScanState eState = ssGetChar;
1940 sal_Int32 nSpaces = 0;
1941 sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
1942 sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
1943 sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
1944 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' :
1945 ScGlobal::pLocaleData->getNumDecimalSep()[0]);
1947 // special symbols specific to address convention used
1948 sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX);
1949 sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR);
1951 int nDecSeps = 0;
1952 bool bAutoIntersection = false;
1953 int nRefInName = 0;
1954 bool bErrorConstantHadSlash = false;
1955 mnPredetectedReference = 0;
1956 // try to parse simple tokens before calling i18n parser
1957 while ((c != 0) && (eState != ssStop) )
1959 pSrc++;
1960 sal_uLong nMask = GetCharTableFlags( c, cLast );
1962 // The parameter separator and the array column and row separators end
1963 // things unconditionally if not in string or reference.
1964 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
1966 switch (eState)
1968 // these are to be continued
1969 case ssGetString:
1970 case ssSkipString:
1971 case ssGetReference:
1972 case ssSkipReference:
1973 break;
1974 default:
1975 if (eState == ssGetChar)
1976 *pSym++ = c;
1977 else
1978 pSrc--;
1979 eState = ssStop;
1982 Label_MaskStateMachine:
1983 switch (eState)
1985 case ssGetChar :
1987 // Order is important!
1988 if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
1990 *pSym++ = c;
1991 eState = ssGetTableRefColumn;
1993 else if( nMask & SC_COMPILER_C_ODF_LABEL_OP )
1995 // '!!' automatic intersection
1996 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_ODF_LABEL_OP)
1998 /* TODO: For now the UI "space operator" is used, this
1999 * could be enhanced using a specialized OpCode to get
2000 * rid of the space ambiguity, which would need some
2001 * places to be adapted though. And we would still need
2002 * to support the ambiguous space operator for UI
2003 * purposes anyway. However, we then could check for
2004 * invalid usage of '!!', which currently isn't
2005 * possible. */
2006 if (!bAutoIntersection)
2008 ++pSrc;
2009 nSpaces += 2; // must match the character count
2010 bAutoIntersection = true;
2012 else
2014 pSrc--;
2015 eState = ssStop;
2018 else
2020 nMask &= ~SC_COMPILER_C_ODF_LABEL_OP;
2021 goto Label_MaskStateMachine;
2024 else if( nMask & SC_COMPILER_C_ODF_NAME_MARKER )
2026 // '$$' defined name marker
2027 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_ODF_NAME_MARKER)
2029 // both eaten, not added to pSym
2030 ++pSrc;
2032 else
2034 nMask &= ~SC_COMPILER_C_ODF_NAME_MARKER;
2035 goto Label_MaskStateMachine;
2038 else if( nMask & SC_COMPILER_C_CHAR )
2040 // '[' is a special case in OOXML, it can start an external
2041 // reference ID like [1]Sheet1!A1 that needs to be scanned
2042 // entirely, or can be ocTableRefOpen, of which the first
2043 // transforms an ocDBArea into an ocTableRef.
2044 if (c == '[' && FormulaGrammar::isOOXML( meGrammar) && eLastOp != ocDBArea && maTableRefs.empty())
2046 nMask &= ~SC_COMPILER_C_CHAR;
2047 goto Label_MaskStateMachine;
2049 else
2051 *pSym++ = c;
2052 eState = ssStop;
2055 else if( nMask & SC_COMPILER_C_ODF_LBRACKET )
2057 // eaten, not added to pSym
2058 eState = ssGetReference;
2059 mnPredetectedReference = 1;
2061 else if( nMask & SC_COMPILER_C_CHAR_BOOL )
2063 *pSym++ = c;
2064 eState = ssGetBool;
2066 else if( nMask & SC_COMPILER_C_CHAR_VALUE )
2068 *pSym++ = c;
2069 eState = ssGetValue;
2071 else if( nMask & SC_COMPILER_C_CHAR_STRING )
2073 *pSym++ = c;
2074 eState = ssGetString;
2076 else if( nMask & SC_COMPILER_C_CHAR_ERRCONST )
2078 *pSym++ = c;
2079 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
2080 eState = ssGetTableRefItem;
2081 else
2082 eState = ssGetErrorConstant;
2084 else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
2086 nSpaces++;
2088 else if( nMask & SC_COMPILER_C_CHAR_IDENT )
2089 { // try to get a simple ASCII identifier before calling
2090 // i18n, to gain performance during import
2091 *pSym++ = c;
2092 eState = ssGetIdent;
2094 else
2096 bi18n = true;
2097 eState = ssStop;
2100 break;
2101 case ssGetIdent:
2103 if ( nMask & SC_COMPILER_C_IDENT )
2104 { // This catches also $Sheet1.A$1, for example.
2105 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2107 SetError(errStringOverflow);
2108 eState = ssStop;
2110 else
2111 *pSym++ = c;
2113 else if (c == ':' && mnRangeOpPosInSymbol < 0)
2115 // One range operator may form Sheet1.A:A, which we need to
2116 // pass as one entity to IsReference().
2117 mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2118 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2120 SetError(errStringOverflow);
2121 eState = ssStop;
2123 else
2124 *pSym++ = c;
2126 else if ( 128 <= c || '\'' == c )
2127 { // High values need reparsing with i18n,
2128 // single quoted $'sheet' names too (otherwise we'd had to
2129 // implement everything twice).
2130 bi18n = true;
2131 eState = ssStop;
2133 else
2135 pSrc--;
2136 eState = ssStop;
2139 break;
2140 case ssGetBool :
2142 if( nMask & SC_COMPILER_C_BOOL )
2144 *pSym++ = c;
2145 eState = ssStop;
2147 else
2149 pSrc--;
2150 eState = ssStop;
2153 break;
2154 case ssGetValue :
2156 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2158 SetError(errStringOverflow);
2159 eState = ssStop;
2161 else if (c == cDecSep)
2163 if (++nDecSeps > 1)
2165 // reparse with i18n, may be numeric sheet name as well
2166 bi18n = true;
2167 eState = ssStop;
2169 else
2170 *pSym++ = c;
2172 else if( nMask & SC_COMPILER_C_VALUE )
2173 *pSym++ = c;
2174 else if( nMask & SC_COMPILER_C_VALUE_SEP )
2176 pSrc--;
2177 eState = ssStop;
2179 else if (c == 'E' || c == 'e')
2181 if (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_VALUE_EXP)
2182 *pSym++ = c;
2183 else
2185 // reparse with i18n
2186 bi18n = true;
2187 eState = ssStop;
2190 else if( nMask & SC_COMPILER_C_VALUE_SIGN )
2192 if (((cLast == 'E') || (cLast == 'e')) &&
2193 (GetCharTableFlags( pSrc[0], 0 ) & SC_COMPILER_C_VALUE_VALUE))
2195 *pSym++ = c;
2197 else
2199 pSrc--;
2200 eState = ssStop;
2203 else
2205 // reparse with i18n
2206 bi18n = true;
2207 eState = ssStop;
2210 break;
2211 case ssGetString :
2213 if( nMask & SC_COMPILER_C_STRING_SEP )
2215 if ( !bQuote )
2217 if ( *pSrc == '"' )
2218 bQuote = true; // "" => literal "
2219 else
2220 eState = ssStop;
2222 else
2223 bQuote = false;
2225 if ( !bQuote )
2227 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2229 SetError(errStringOverflow);
2230 eState = ssSkipString;
2232 else
2233 *pSym++ = c;
2236 break;
2237 case ssSkipString:
2238 if( nMask & SC_COMPILER_C_STRING_SEP )
2239 eState = ssStop;
2240 break;
2241 case ssGetErrorConstant:
2243 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2244 // BUT, in UI these may have been translated! So don't
2245 // check for ASCII alnum. Note that this construct can't be
2246 // parsed with i18n.
2247 /* TODO: be strict when reading ODFF, check for ASCII alnum
2248 * and proper continuation after '/'. However, even with
2249 * the lax parsing only the error constants we have defined
2250 * as opcode symbols will be recognized and others result
2251 * in ocBad, so the result is actually conformant. */
2252 bool bAdd = true;
2253 if ('!' == c || '?' == c)
2254 eState = ssStop;
2255 else if ('/' == c)
2257 if (!bErrorConstantHadSlash)
2258 bErrorConstantHadSlash = true;
2259 else
2261 bAdd = false;
2262 eState = ssStop;
2265 else if ((nMask & SC_COMPILER_C_WORD_SEP) ||
2266 (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2268 bAdd = false;
2269 eState = ssStop;
2271 if (!bAdd)
2272 --pSrc;
2273 else
2275 if (pSym == &cSymbol[ MAXSTRLEN-1 ])
2277 SetError( errStringOverflow);
2278 eState = ssStop;
2280 else
2281 *pSym++ = c;
2284 break;
2285 case ssGetTableRefItem:
2287 // Scan whatever up to the next ']' closer.
2288 if (c != ']')
2290 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2292 SetError( errStringOverflow);
2293 eState = ssStop;
2295 else
2296 *pSym++ = c;
2298 else
2300 --pSrc;
2301 eState = ssStop;
2304 break;
2305 case ssGetTableRefColumn:
2307 // Scan whatever up to the next unescaped ']' closer.
2308 if (c != ']' || cLast == '\'')
2310 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2312 SetError( errStringOverflow);
2313 eState = ssStop;
2315 else
2316 *pSym++ = c;
2318 else
2320 --pSrc;
2321 eState = ssStop;
2324 break;
2325 case ssGetReference:
2326 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
2328 SetError( errStringOverflow);
2329 eState = ssSkipReference;
2331 // fall through and follow logic
2332 case ssSkipReference:
2333 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2334 // mandatory also if no sheet name. 'External'# is optional,
2335 // sheet name is optional, quotes around sheet name are
2336 // optional if no quote contained. [#REF!] is valid.
2337 // 2nd usage: ['Sheet'.$$'DefinedName']
2338 // 3rd usage: ['External'#$$'DefinedName']
2339 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2340 // Also for all these names quotes are optional if no quote
2341 // contained.
2344 // nRefInName: 0 := not in sheet name yet. 'External'
2345 // is parsed as if it was a sheet name and nRefInName
2346 // is reset when # is encountered immediately after closing
2347 // quote. Same with 'DefinedName', nRefInName is cleared
2348 // when : is encountered.
2350 // Encountered leading $ before sheet name.
2351 static const int kDollar = (1 << 1);
2352 // Encountered ' opening quote, which may be after $ or
2353 // not.
2354 static const int kOpen = (1 << 2);
2355 // Somewhere in name.
2356 static const int kName = (1 << 3);
2357 // Encountered ' in name, will be cleared if double or
2358 // transformed to kClose if not, in which case kOpen is
2359 // cleared.
2360 static const int kQuote = (1 << 4);
2361 // Past ' closing quote.
2362 static const int kClose = (1 << 5);
2363 // Encountered # file/sheet separator.
2364 static const int kFileSep = (1 << 6);
2365 // Past . sheet name separator.
2366 static const int kPast = (1 << 7);
2367 // Marked name $$ follows sheet name separator, detected
2368 // while we're still on the separator. Will be cleared when
2369 // entering the name.
2370 static const int kMarkAhead = (1 << 8);
2371 // In marked defined name.
2372 static const int kDefName = (1 << 9);
2373 // Encountered # of #REF!
2374 static const int kRefErr = (1 << 10);
2376 bool bAddToSymbol = true;
2377 if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
2379 OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2380 "ScCompiler::NextSymbol: reference: "
2381 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2382 // eaten, not added to pSym
2383 bAddToSymbol = false;
2384 eState = ssStop;
2386 else if (cSheetSep == c && nRefInName == 0)
2388 // eat it, no sheet name [.A1]
2389 bAddToSymbol = false;
2390 nRefInName |= kPast;
2391 if ('$' == pSrc[0] && '$' == pSrc[1])
2392 nRefInName |= kMarkAhead;
2394 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2396 // Not in col/row yet.
2398 if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2399 nRefInName = 0;
2400 else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2402 nRefInName &= ~kMarkAhead;
2403 if (!(nRefInName & kDefName))
2405 // eaten, not added to pSym (2 chars)
2406 bAddToSymbol = false;
2407 ++pSrc;
2408 nRefInName &= kPast;
2409 nRefInName |= kDefName;
2411 else
2413 // ScAddress::Parse() will recognize this as
2414 // invalid later.
2415 if (eState != ssSkipReference)
2417 *pSym++ = c;
2418 *pSym++ = *pSrc++;
2420 bAddToSymbol = false;
2423 else if (cSheetPrefix == c && nRefInName == 0)
2424 nRefInName |= kDollar;
2425 else if ('\'' == c)
2427 // TODO: The conventions' parseExternalName()
2428 // should handle quoted names, but as long as they
2429 // don't remove non-embedded quotes here.
2430 if (!(nRefInName & kName))
2432 nRefInName |= (kOpen | kName);
2433 bAddToSymbol = !(nRefInName & kDefName);
2435 else if (!(nRefInName & kOpen))
2437 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2438 "a ''' without the name being enclosed in '...' violates ODF spec");
2440 else if (nRefInName & kQuote)
2442 // escaped embedded quote
2443 nRefInName &= ~kQuote;
2445 else
2447 switch (pSrc[0])
2449 case '\'':
2450 // escapes embedded quote
2451 nRefInName |= kQuote;
2452 break;
2453 case SC_COMPILER_FILE_TAB_SEP:
2454 // sheet name should follow
2455 nRefInName |= kFileSep;
2456 // fallthru
2457 default:
2458 // quote not followed by quote => close
2459 nRefInName |= kClose;
2460 nRefInName &= ~kOpen;
2462 bAddToSymbol = !(nRefInName & kDefName);
2465 else if ('#' == c && nRefInName == 0)
2466 nRefInName |= kRefErr;
2467 else if (cSheetSep == c && !(nRefInName & kOpen))
2469 // unquoted sheet name separator
2470 nRefInName |= kPast;
2471 if ('$' == pSrc[0] && '$' == pSrc[1])
2472 nRefInName |= kMarkAhead;
2474 else if (':' == c && !(nRefInName & kOpen))
2476 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2477 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2478 nRefInName = 0;
2479 ++mnPredetectedReference;
2481 else if (!(nRefInName & kName))
2483 // start unquoted name
2484 nRefInName |= kName;
2487 else if (':' == c)
2489 // range operator
2490 nRefInName = 0;
2491 ++mnPredetectedReference;
2493 if (bAddToSymbol && eState != ssSkipReference)
2494 *pSym++ = c; // everything is part of reference
2496 break;
2497 case ssStop:
2498 ; // nothing, prevent warning
2499 break;
2501 cLast = c;
2502 c = *pSrc;
2504 if ( bi18n )
2506 nSrcPos = nSrcPos + nSpaces;
2507 OUStringBuffer aSymbol;
2508 mnRangeOpPosInSymbol = -1;
2509 sal_uInt16 nErr = 0;
2512 bi18n = false;
2513 // special case (e.g. $'sheetname' in OOO A1)
2514 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2515 aSymbol.append(pStart[nSrcPos++]);
2517 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass );
2519 if ( !aRes.TokenType )
2520 SetError( nErr = errIllegalChar ); // parsed chars as string
2521 if ( aRes.EndPos <= nSrcPos )
2522 { // ?!?
2523 SetError( nErr = errIllegalChar );
2524 nSrcPos = aFormula.getLength();
2525 aSymbol.truncate(0);
2527 else
2529 // When having parsed a second reference part, ensure that the
2530 // i18n parser did not mistakingly parse a number that included
2531 // a separator which happened to be meant as a parameter
2532 // separator instead.
2533 if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
2535 for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
2537 if (pStart[i] == cSep)
2538 aRes.EndPos = i; // also ends for
2541 aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2542 nSrcPos = aRes.EndPos;
2543 c = pStart[nSrcPos];
2544 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2545 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2546 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2548 // One range operator restarts parsing for second reference.
2549 if (c == ':' && mnRangeOpPosInSymbol < 0)
2551 mnRangeOpPosInSymbol = aSymbol.getLength();
2552 bi18n = true;
2554 if ( bi18n )
2555 aSymbol.append(pStart[nSrcPos++]);
2557 } while ( bi18n && !nErr );
2558 sal_Int32 nLen = aSymbol.getLength();
2559 if ( nLen >= MAXSTRLEN )
2561 SetError( errStringOverflow );
2562 nLen = MAXSTRLEN-1;
2564 lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2565 pSym = &cSymbol[nLen];
2567 else
2569 nSrcPos = pSrc - pStart;
2570 *pSym = 0;
2572 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2574 // This is a trailing range operator, which is nonsense. Will be caught
2575 // in next round.
2576 mnRangeOpPosInSymbol = -1;
2577 *--pSym = 0;
2578 --nSrcPos;
2580 if ( bAutoCorrect )
2581 aCorrectedSymbol = cSymbol;
2582 if (bAutoIntersection && nSpaces > 1)
2583 --nSpaces; // replace '!!' with only one space
2584 return nSpaces;
2587 // Convert symbol to token
2589 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray )
2591 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
2592 bool bFound = (iLook != mxSymbols->getHashMap()->end());
2593 if (bFound)
2595 OpCode eOp = iLook->second;
2596 if (bInArray)
2598 if (rName.equals(mxSymbols->getSymbol(ocArrayColSep)))
2599 eOp = ocArrayColSep;
2600 else if (rName.equals(mxSymbols->getSymbol(ocArrayRowSep)))
2601 eOp = ocArrayRowSep;
2603 else if (eOp == ocCeil && mxSymbols->isOOXML())
2605 // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2606 // unassigned for import.
2607 eOp = ocCeil_Math;
2609 else if (eOp == ocFloor && mxSymbols->isOOXML())
2611 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2612 // unassigned for import.
2613 eOp = ocFloor_Math;
2615 maRawToken.SetOpCode(eOp);
2617 else if (mxSymbols->isODFF())
2619 // ODFF names that are not written in the current mapping but to be
2620 // recognized. New names will be written in a future relase, then
2621 // exchange (!) with the names in
2622 // formula/source/core/resource/core_resource.src to be able to still
2623 // read the old names as well.
2624 struct FunctionName
2626 const sal_Char* pName;
2627 OpCode eOp;
2629 static const FunctionName aOdffAliases[] = {
2630 // Renamed old names, still accept them:
2631 { "B", ocB }, // B -> BINOM.DIST.RANGE
2632 { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
2633 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2634 { "ZGZ", ocRRI }, // ZGZ -> RRI
2635 { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR
2636 { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2637 { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2638 { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2639 // Renamed new names, prepare to read future names:
2640 //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
2642 static const size_t nOdffAliases = sizeof(aOdffAliases) / sizeof(aOdffAliases[0]);
2643 for (size_t i=0; i<nOdffAliases; ++i)
2645 if (rName.equalsIgnoreAsciiCaseAscii( aOdffAliases[i].pName))
2647 maRawToken.SetOpCode( aOdffAliases[i].eOp);
2648 bFound = true;
2649 break; // for
2653 if (!bFound)
2655 OUString aIntName;
2656 if (mxSymbols->hasExternals())
2658 // If symbols are set by filters get mapping to exact name.
2659 ExternalHashMap::const_iterator iExt(
2660 mxSymbols->getExternalHashMap()->find( rName));
2661 if (iExt != mxSymbols->getExternalHashMap()->end())
2663 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
2664 aIntName = (*iExt).second;
2666 if (aIntName.isEmpty())
2668 // If that isn't found we might continue with rName lookup as a
2669 // last resort by just falling through to FindFunction(), but
2670 // it shouldn't happen if the map was setup correctly. Don't
2671 // waste time and bail out.
2672 return false;
2675 if (aIntName.isEmpty())
2677 // Old (deprecated) addins first for legacy.
2678 if (ScGlobal::GetFuncCollection()->findByName(cSymbol))
2680 maRawToken.SetExternal( cSymbol );
2682 else
2683 // bLocalFirst=false for (English) upper full original name
2684 // (service.function)
2685 aIntName = ScGlobal::GetAddInCollection()->FindFunction(
2686 rName, !mxSymbols->isEnglish());
2688 if (!aIntName.isEmpty())
2690 maRawToken.SetExternal( aIntName.getStr() ); // international name
2691 bFound = true;
2694 OpCode eOp;
2695 if (bFound && ((eOp = maRawToken.GetOpCode()) == ocSub || eOp == ocNegSub))
2697 bool bShouldBeNegSub =
2698 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
2699 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) ||
2700 eLastOp == ocArrayOpen ||
2701 eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep);
2702 if (bShouldBeNegSub && eOp == ocSub)
2703 maRawToken.NewOpCode( ocNegSub );
2704 //TODO: if ocNegSub had ForceArray we'd have to set it here
2705 else if (!bShouldBeNegSub && eOp == ocNegSub)
2706 maRawToken.NewOpCode( ocSub );
2708 return bFound;
2711 bool ScCompiler::IsOpCode2( const OUString& rName )
2713 bool bFound = false;
2714 sal_uInt16 i;
2716 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
2717 bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] );
2719 if (bFound)
2721 maRawToken.SetOpCode( (OpCode) --i );
2723 return bFound;
2726 bool ScCompiler::IsValue( const OUString& rSym )
2728 double fVal;
2729 sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
2731 if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
2732 return false;
2734 sal_uInt16 nType = mpFormatter->GetType(nIndex);
2736 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
2737 // Dates should never be entered directly and automatically converted
2738 // to serial, because the serial would be wrong if null-date changed.
2739 // Usually it wouldn't be accepted anyway because the date separator
2740 // clashed with other separators or operators.
2741 if (nType & (css::util::NumberFormat::TIME | css::util::NumberFormat::DATE))
2742 return false;
2744 if (nType == css::util::NumberFormat::LOGICAL)
2746 const sal_Unicode* p = aFormula.getStr() + nSrcPos;
2747 while( *p == ' ' )
2748 p++;
2749 if (*p == '(')
2750 return false; // Boolean function instead.
2753 if( nType == css::util::NumberFormat::TEXT )
2754 // HACK: number too big!
2755 SetError( errIllegalArgument );
2756 maRawToken.SetDouble( fVal );
2757 return true;
2760 bool ScCompiler::IsString()
2762 const sal_Unicode* p = cSymbol;
2763 while ( *p )
2764 p++;
2765 sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
2766 bool bQuote = ((cSymbol[0] == '"') && (cSymbol[nLen] == '"'));
2767 if ((bQuote ? nLen-2 : nLen) > MAXSTRLEN-1)
2769 SetError(errStringOverflow);
2770 return false;
2772 if ( bQuote )
2774 cSymbol[nLen] = '\0';
2775 const sal_Unicode* pStr = cSymbol+1;
2776 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(OUString(pStr));
2777 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
2778 return true;
2780 return false;
2783 bool ScCompiler::IsPredetectedReference(const OUString& rName)
2785 // Speedup documents with lots of broken references, e.g. sheet deleted.
2786 sal_Int32 nPos = rName.indexOf("#REF!");
2787 if (nPos != -1)
2789 /* TODO: this may be enhanced by reusing scan information from
2790 * NextSymbol(), the positions of quotes and special characters found
2791 * there for $'sheet'.A1:... could be stored in a vector. We don't
2792 * fully rescan here whether found positions are within single quotes
2793 * for performance reasons. This code does not check for possible
2794 * occurrences of insane "valid" sheet names like
2795 * 'haha.#REF!1fooledyou' and will generate an error on such. */
2796 if (nPos == 0)
2798 // Per ODFF the correct string for a reference error is just #REF!,
2799 // so pass it on.
2800 if (rName.getLength() == 5)
2801 return IsErrorConstant( rName);
2802 return false; // #REF!.AB42 or #REF!42 or #REF!#REF!
2804 sal_Unicode c = rName[nPos-1]; // before #REF!
2805 if ('$' == c)
2807 if (nPos == 1)
2808 return false; // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
2809 c = rName[nPos-2]; // before $#REF!
2811 sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
2812 switch (c)
2814 case '.':
2815 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
2816 return false; // sheet.#REF!42 or sheet.#REF!#REF!
2817 break;
2818 case ':':
2819 if (mnPredetectedReference > 1 &&
2820 ('.' == c2 || '$' == c2 || '#' == c2 ||
2821 ('0' <= c2 && c2 <= '9')))
2822 return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
2823 break;
2824 default:
2825 if (comphelper::string::isalphaAscii(c) &&
2826 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
2827 return false; // AB#REF!: or AB#REF!
2830 switch (mnPredetectedReference)
2832 case 1:
2833 return IsSingleReference( rName);
2834 case 2:
2835 return IsDoubleReference( rName);
2837 return false;
2840 bool ScCompiler::IsDoubleReference( const OUString& rName )
2842 ScRange aRange( aPos, aPos );
2843 const ScAddress::Details aDetails( pConv->meConv, aPos );
2844 ScAddress::ExternalInfo aExtInfo;
2845 sal_uInt16 nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2846 if( nFlags & SCA_VALID )
2848 ScComplexRefData aRef;
2849 aRef.InitRange( aRange );
2850 aRef.Ref1.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2851 aRef.Ref1.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2852 aRef.Ref1.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2853 if ( !(nFlags & SCA_VALID_TAB) )
2854 aRef.Ref1.SetTabDeleted( true ); // #REF!
2855 aRef.Ref1.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2856 aRef.Ref2.SetColRel( (nFlags & SCA_COL2_ABSOLUTE) == 0 );
2857 aRef.Ref2.SetRowRel( (nFlags & SCA_ROW2_ABSOLUTE) == 0 );
2858 aRef.Ref2.SetTabRel( (nFlags & SCA_TAB2_ABSOLUTE) == 0 );
2859 if ( !(nFlags & SCA_VALID_TAB2) )
2860 aRef.Ref2.SetTabDeleted( true ); // #REF!
2861 aRef.Ref2.SetFlag3D( ( nFlags & SCA_TAB2_3D ) != 0 );
2862 aRef.SetRange(aRange, aPos);
2863 if (aExtInfo.mbExternal)
2865 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2866 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2867 maRawToken.SetExternalDoubleRef(
2868 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2869 maExternalFiles.push_back(aExtInfo.mnFileId);
2871 else
2873 maRawToken.SetDoubleReference(aRef);
2877 return ( nFlags & SCA_VALID ) != 0;
2880 bool ScCompiler::IsSingleReference( const OUString& rName )
2882 ScAddress aAddr( aPos );
2883 const ScAddress::Details aDetails( pConv->meConv, aPos );
2884 ScAddress::ExternalInfo aExtInfo;
2885 sal_uInt16 nFlags = aAddr.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks );
2886 // Something must be valid in order to recognize Sheet1.blah or blah.a1
2887 // as a (wrong) reference.
2888 if( nFlags & ( SCA_VALID_COL|SCA_VALID_ROW|SCA_VALID_TAB ) )
2890 ScSingleRefData aRef;
2891 aRef.InitAddress( aAddr );
2892 aRef.SetColRel( (nFlags & SCA_COL_ABSOLUTE) == 0 );
2893 aRef.SetRowRel( (nFlags & SCA_ROW_ABSOLUTE) == 0 );
2894 aRef.SetTabRel( (nFlags & SCA_TAB_ABSOLUTE) == 0 );
2895 aRef.SetFlag3D( ( nFlags & SCA_TAB_3D ) != 0 );
2896 // the reference is really invalid
2897 if( !( nFlags & SCA_VALID ) )
2899 if( !( nFlags & SCA_VALID_COL ) )
2900 aRef.SetColDeleted(true);
2901 if( !( nFlags & SCA_VALID_ROW ) )
2902 aRef.SetRowDeleted(true);
2903 if( !( nFlags & SCA_VALID_TAB ) )
2904 aRef.SetTabDeleted(true);
2905 nFlags |= SCA_VALID;
2907 aRef.SetAddress(aAddr, aPos);
2909 if (aExtInfo.mbExternal)
2911 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
2912 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
2913 maRawToken.SetExternalSingleRef(
2914 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
2915 maExternalFiles.push_back(aExtInfo.mnFileId);
2917 else
2918 maRawToken.SetSingleReference(aRef);
2921 return ( nFlags & SCA_VALID ) != 0;
2924 bool ScCompiler::IsReference( const OUString& rName )
2926 // Has to be called before IsValue
2927 sal_Unicode ch1 = rName[0];
2928 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' :
2929 ScGlobal::pLocaleData->getNumDecimalSep()[0] );
2930 if ( ch1 == cDecSep )
2931 return false;
2932 // Who was that imbecile introducing '.' as the sheet name separator!?!
2933 if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
2935 // Numerical sheet name is valid.
2936 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
2937 // Don't create a #REF! of values. But also do not bail out on
2938 // something like 3:3, meaning entire row 3.
2941 const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
2942 if ( nPos == -1 )
2944 if (ScGlobal::FindUnquoted( rName, ':') != -1)
2945 break; // may be 3:3, continue as usual
2946 return false;
2948 sal_Unicode const * const pTabSep = rName.getStr() + nPos;
2949 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
2950 if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
2951 return false;
2952 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
2953 && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & SC_COMPILER_C_VALUE_EXP) )
2954 { // #91053#
2955 // If it is an 1.E2 expression check if "1" is an existent sheet
2956 // name. If so, a desired value 1.E2 would have to be entered as
2957 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
2958 // require numerical sheet names always being entered quoted, which
2959 // is not desirable (too many 1999, 2000, 2001 sheets in use).
2960 // Furthermore, XML files created with versions prior to SRC640e
2961 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
2962 // and would produce wrong formulas if the conditions here are met.
2963 // If you can live with these restrictions you may remove the
2964 // check and return an unconditional FALSE.
2965 OUString aTabName( rName.copy( 0, nPos ) );
2966 SCTAB nTab;
2967 if ( !pDoc->GetTable( aTabName, nTab ) )
2968 return false;
2969 // If sheet "1" exists and the expression is 1.E+2 continue as
2970 // usual, the ScRange/ScAddress parser will take care of it.
2972 } while(false);
2975 if (IsSingleReference( rName))
2976 return true;
2978 // Though the range operator is handled explicitly, when encountering
2979 // something like Sheet1.A:A we will have to treat it as one entity if it
2980 // doesn't pass as single cell reference.
2981 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
2983 if (IsDoubleReference( rName))
2984 return true;
2985 // Now try with a symbol up to the range operator, rewind source
2986 // position.
2987 sal_Int32 nLen = mnRangeOpPosInSymbol;
2988 while (cSymbol[++nLen])
2990 cSymbol[mnRangeOpPosInSymbol] = 0;
2991 nSrcPos -= (nLen - mnRangeOpPosInSymbol);
2992 mnRangeOpPosInSymbol = -1;
2993 mbRewind = true;
2994 return true; // end all checks
2996 else
2998 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel sickness,
2999 // mnRangeOpPosInSymbol did not catch the range operator as it is
3000 // within a quoted name.
3001 switch (pConv->meConv)
3003 case FormulaGrammar::CONV_XL_A1:
3004 case FormulaGrammar::CONV_XL_R1C1:
3005 case FormulaGrammar::CONV_XL_OOX:
3006 if (rName[0] == '\'' && IsDoubleReference( rName))
3007 return true;
3008 break;
3009 default:
3010 ; // nothing
3013 return false;
3016 bool ScCompiler::IsMacro( const OUString& rName )
3018 #if !HAVE_FEATURE_SCRIPTING
3019 (void) rName;
3021 return false;
3022 #else
3024 // Calling SfxObjectShell::GetBasic() may result in all sort of things
3025 // including obtaining the model and deep down in
3026 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3027 // formulas are compiled from a threaded import may result in a deadlock.
3028 // Check first if we actually could acquire it and if not bail out.
3029 /* FIXME: yes, but how ... */
3030 vcl::SolarMutexTryAndBuyGuard g;
3031 if (!g.isAcquired())
3033 SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic");
3034 return false; // bad luck
3037 OUString aName( rName);
3038 StarBASIC* pObj = 0;
3039 SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
3043 if( pDocSh )//XXX
3044 pObj = pDocSh->GetBasic();
3045 else
3046 pObj = SfxApplication::GetBasic();
3048 catch (...)
3050 return false;
3053 // ODFF recommends to store user-defined functions prefixed with "USER.",
3054 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3055 // function name so a function "USER.FOO" could not exist, and macro check
3056 // is assigned the lowest priority in function name check.
3057 if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
3058 aName = aName.copy(5);
3060 SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxCLASS_METHOD ));
3061 if( !pMeth )
3063 return false;
3065 // It really should be a BASIC function!
3066 if( pMeth->GetType() == SbxVOID
3067 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
3068 || !pMeth->ISA(SbMethod) )
3070 return false;
3072 maRawToken.SetExternal( aName.getStr() );
3073 maRawToken.eOp = ocMacro;
3074 return true;
3075 #endif
3078 bool ScCompiler::IsNamedRange( const OUString& rUpperName )
3080 // IsNamedRange is called only from NextNewToken, with an upper-case string
3082 // try local names first
3083 bool bGlobal = false;
3084 ScRangeName* pRangeName = pDoc->GetRangeName(aPos.Tab());
3085 const ScRangeData* pData = NULL;
3086 if (pRangeName)
3087 pData = pRangeName->findByUpperName(rUpperName);
3088 if (!pData)
3090 pRangeName = pDoc->GetRangeName();
3091 if (pRangeName)
3092 pData = pRangeName->findByUpperName(rUpperName);
3093 if (pData)
3094 bGlobal = true;
3097 if (pData)
3099 maRawToken.SetName(bGlobal, pData->GetIndex());
3100 return true;
3102 else
3103 return false;
3106 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
3108 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3109 * correctly parses external named references in OOo, as required per RFE
3110 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3111 * spec first. Until then don't pretend to support external names that
3112 * wouldn't survive a save and reload cycle, return false instead. */
3114 rbInvalidExternalNameRange = false;
3116 if (!pConv)
3117 return false;
3119 OUString aFile, aName;
3120 if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks))
3121 return false;
3123 if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
3124 return false;
3126 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
3127 OUString aTmp = aFile;
3128 pRefMgr->convertToAbsName(aTmp);
3129 aFile = aTmp;
3130 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
3131 if (!pRefMgr->isValidRangeName(nFileId, aName))
3133 rbInvalidExternalNameRange = true;
3134 // range name doesn't exist in the source document.
3135 return false;
3138 const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
3139 maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : OUString(aTmp));
3140 maExternalFiles.push_back(nFileId);
3141 return true;
3144 bool ScCompiler::IsDBRange( const OUString& rName )
3146 ScDBCollection::NamedDBs& rDBs = pDoc->GetDBCollection()->getNamedDBs();
3147 const ScDBData* p = rDBs.findByUpperName(rName);
3148 if (!p)
3149 return false;
3151 maRawToken.SetName(true, p->GetIndex()); // DB range is always global.
3152 maRawToken.eOp = ocDBArea;
3153 return true;
3156 bool ScCompiler::IsColRowName( const OUString& rName )
3158 bool bInList = false;
3159 bool bFound = false;
3160 ScSingleRefData aRef;
3161 OUString aName( rName );
3162 DeQuote( aName );
3163 SCTAB nThisTab = aPos.Tab();
3164 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
3165 { // first check ranges on this sheet, in case of duplicated names
3166 for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3168 ScRangePairList* pRL;
3169 if ( !jRow )
3170 pRL = pDoc->GetColNameRanges();
3171 else
3172 pRL = pDoc->GetRowNameRanges();
3173 for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
3175 ScRangePair* pR = (*pRL)[iPair];
3176 const ScRange& rNameRange = pR->GetRange(0);
3177 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab &&
3178 nThisTab <= rNameRange.aEnd.Tab()) )
3179 continue; // for
3180 ScCellIterator aIter( pDoc, rNameRange );
3181 for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
3183 // Don't crash if cell (via CompileNameFormula) encounters
3184 // a formula cell without code and
3185 // HasStringData/Interpret/Compile is executed and all that
3186 // recursive..
3187 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3188 CellType eType = aIter.getType();
3189 bool bOk = false;
3190 if (eType == CELLTYPE_FORMULA)
3192 ScFormulaCell* pFC = aIter.getFormulaCell();
3193 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3195 else
3196 bOk = true;
3198 if (bOk && aIter.hasString())
3200 OUString aStr = aIter.getString();
3201 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3203 aRef.InitFlags();
3204 if ( !jRow )
3205 aRef.SetColRel( true ); // ColName
3206 else
3207 aRef.SetRowRel( true ); // RowName
3208 aRef.SetAddress(aIter.GetPos(), aPos);
3209 bInList = bFound = true;
3216 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
3217 { // search in current sheet
3218 long nDistance = 0, nMax = 0;
3219 long nMyCol = (long) aPos.Col();
3220 long nMyRow = (long) aPos.Row();
3221 bool bTwo = false;
3222 ScAddress aOne( 0, 0, aPos.Tab() );
3223 ScAddress aTwo( MAXCOL, MAXROW, aPos.Tab() );
3225 ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache();
3226 if ( pNameCache )
3228 // use GetNameOccurrences to collect all positions of aName on the sheet
3229 // (only once), similar to the outer part of the loop in the "else" branch.
3231 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
3233 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3234 // The order of addresses in the vector is the same as from ScCellIterator.
3236 ScAutoNameAddresses::const_iterator aEnd(rAddresses.end());
3237 for ( ScAutoNameAddresses::const_iterator aAdrIter(rAddresses.begin()); aAdrIter != aEnd; ++aAdrIter )
3239 ScAddress aAddress( *aAdrIter ); // cell address with an equal string
3241 if ( bFound )
3242 { // stop if everything else is further away
3243 if ( nMax < (long)aAddress.Col() )
3244 break; // aIter
3246 if ( aAddress != aPos )
3248 // same treatment as in isEqual case below
3250 SCCOL nCol = aAddress.Col();
3251 SCROW nRow = aAddress.Row();
3252 long nC = nMyCol - nCol;
3253 long nR = nMyRow - nRow;
3254 if ( bFound )
3256 long nD = nC * nC + nR * nR;
3257 if ( nD < nDistance )
3259 if ( nC < 0 || nR < 0 )
3260 { // right or below
3261 bTwo = true;
3262 aTwo.Set( nCol, nRow, aAddress.Tab() );
3263 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3264 nDistance = nD;
3266 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3268 // upper left, only if not further up than the
3269 // current entry and nMyRow is below (CellIter
3270 // runs column-wise)
3271 bTwo = false;
3272 aOne.Set( nCol, nRow, aAddress.Tab() );
3273 nMax = std::max( nMyCol + nC, nMyRow + nR );
3274 nDistance = nD;
3278 else
3280 aOne.Set( nCol, nRow, aAddress.Tab() );
3281 nDistance = nC * nC + nR * nR;
3282 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3285 bFound = true;
3289 else
3291 ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) );
3292 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3294 if ( bFound )
3295 { // stop if everything else is further away
3296 if ( nMax < (long)aIter.GetPos().Col() )
3297 break; // aIter
3299 CellType eType = aIter.getType();
3300 bool bOk = false;
3301 if (eType == CELLTYPE_FORMULA)
3303 ScFormulaCell* pFC = aIter.getFormulaCell();
3304 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3306 else
3307 bOk = true;
3309 if (bOk && aIter.hasString())
3311 OUString aStr = aIter.getString();
3312 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) )
3314 SCCOL nCol = aIter.GetPos().Col();
3315 SCROW nRow = aIter.GetPos().Row();
3316 long nC = nMyCol - nCol;
3317 long nR = nMyRow - nRow;
3318 if ( bFound )
3320 long nD = nC * nC + nR * nR;
3321 if ( nD < nDistance )
3323 if ( nC < 0 || nR < 0 )
3324 { // right or below
3325 bTwo = true;
3326 aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3327 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3328 nDistance = nD;
3330 else if ( !(nRow < aOne.Row() && nMyRow >= (long)aOne.Row()) )
3332 // upper left, only if not further up than the
3333 // current entry and nMyRow is below (CellIter
3334 // runs column-wise)
3335 bTwo = false;
3336 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3337 nMax = std::max( nMyCol + nC, nMyRow + nR );
3338 nDistance = nD;
3342 else
3344 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3345 nDistance = nC * nC + nR * nR;
3346 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3348 bFound = true;
3354 if ( bFound )
3356 ScAddress aAdr;
3357 if ( bTwo )
3359 if ( nMyCol >= (long)aOne.Col() && nMyRow >= (long)aOne.Row() )
3360 aAdr = aOne; // upper left takes precedence
3361 else
3363 if ( nMyCol < (long)aOne.Col() )
3364 { // two to the right
3365 if ( nMyRow >= (long)aTwo.Row() )
3366 aAdr = aTwo; // directly right
3367 else
3368 aAdr = aOne;
3370 else
3371 { // two below or below and right, take the nearest
3372 long nC1 = nMyCol - aOne.Col();
3373 long nR1 = nMyRow - aOne.Row();
3374 long nC2 = nMyCol - aTwo.Col();
3375 long nR2 = nMyRow - aTwo.Row();
3376 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3377 aAdr = aOne;
3378 else
3379 aAdr = aTwo;
3383 else
3384 aAdr = aOne;
3385 aRef.InitAddress( aAdr );
3386 if ( (aAdr.Row() != MAXROW && pDoc->HasStringData(
3387 aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3388 || (aAdr.Row() != 0 && pDoc->HasStringData(
3389 aAdr.Col(), aAdr.Row() - 1, aAdr.Tab())))
3390 aRef.SetRowRel( true ); // RowName
3391 else
3392 aRef.SetColRel( true ); // ColName
3393 aRef.SetAddress(aAdr, aPos);
3396 if ( bFound )
3398 maRawToken.SetSingleReference( aRef );
3399 maRawToken.eOp = ocColRowName;
3400 return true;
3402 else
3403 return false;
3406 bool ScCompiler::IsBoolean( const OUString& rName )
3408 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName ) );
3409 if( iLook != mxSymbols->getHashMap()->end() &&
3410 ((*iLook).second == ocTrue ||
3411 (*iLook).second == ocFalse) )
3413 maRawToken.SetOpCode( (*iLook).second );
3414 return true;
3416 else
3417 return false;
3420 bool ScCompiler::IsErrorConstant( const OUString& rName ) const
3422 sal_uInt16 nError = GetErrorConstant( rName);
3423 if (nError)
3425 maRawToken.SetErrorConstant( nError);
3426 return true;
3428 else
3429 return false;
3432 bool ScCompiler::IsTableRefItem( const OUString& rName ) const
3434 bool bItem = false;
3435 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
3436 if (iLook != mxSymbols->getHashMap()->end())
3438 // Only called when there actually is a current TableRef, hence
3439 // accessing maTableRefs.back() is safe.
3440 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3441 assert(p); // not a ScTableRefToken can't be
3443 switch ((*iLook).second)
3445 case ocTableRefItemAll:
3446 bItem = true;
3447 p->AddItem( ScTableRefToken::ALL);
3448 break;
3449 case ocTableRefItemHeaders:
3450 bItem = true;
3451 p->AddItem( ScTableRefToken::HEADERS);
3452 break;
3453 case ocTableRefItemData:
3454 bItem = true;
3455 p->AddItem( ScTableRefToken::DATA);
3456 break;
3457 case ocTableRefItemTotals:
3458 bItem = true;
3459 p->AddItem( ScTableRefToken::TOTALS);
3460 break;
3461 case ocTableRefItemThisRow:
3462 bItem = true;
3463 p->AddItem( ScTableRefToken::THIS_ROW);
3464 break;
3465 default:
3468 if (bItem)
3469 maRawToken.SetOpCode( (*iLook).second );
3471 return bItem;
3474 namespace {
3475 OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
3477 // '#', '[', ']' and '\'' are escaped with '\''
3479 if (rStr.indexOf( '\'' ) < 0)
3480 return rStr;
3482 const sal_Int32 n = rStr.getLength();
3483 OUStringBuffer aBuf( n );
3484 const sal_Unicode* p = rStr.getStr();
3485 const sal_Unicode* const pStop = p + n;
3486 bool bEscaped = false;
3487 for ( ; p < pStop; ++p)
3489 const sal_Unicode c = *p;
3490 if (bEscaped)
3492 aBuf.append( c );
3493 bEscaped = false;
3495 else if (c == '\'')
3496 bEscaped = true; // unescaped escaping '\''
3497 else
3498 aBuf.append( c );
3500 return aBuf.makeStringAndClear();
3504 bool ScCompiler::IsTableRefColumn( const OUString& rName ) const
3506 // Only called when there actually is a current TableRef, hence
3507 // accessing maTableRefs.back() is safe.
3508 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3509 assert(p); // not a ScTableRefToken can't be
3511 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( p->GetIndex());
3512 if (!pDBData)
3513 return false;
3515 OUString aName( unescapeTableRefColumnSpecifier( rName));
3517 ScRange aRange;
3518 pDBData->GetArea( aRange);
3519 aRange.aEnd.SetTab( aRange.aStart.Tab());
3520 aRange.aEnd.SetRow( aRange.aStart.Row());
3522 if (pDBData->HasHeader())
3524 // Quite similar to IsColRowName() but limited to one row of headers.
3525 ScCellIterator aIter( pDoc, aRange);
3526 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3528 CellType eType = aIter.getType();
3529 bool bOk = false;
3530 if (eType == CELLTYPE_FORMULA)
3532 ScFormulaCell* pFC = aIter.getFormulaCell();
3533 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3535 else
3536 bOk = true;
3538 if (bOk && aIter.hasString())
3540 OUString aStr = aIter.getString();
3541 if (ScGlobal::GetpTransliteration()->isEqual( aStr, aName))
3543 /* XXX NOTE: we could init the column as relative so copying a
3544 * formula across columns would point to the relative column,
3545 * but do it absolute because:
3546 * a) it makes the reference work in named expressions without
3547 * having to distinguish
3548 * b) Excel does it the same. */
3549 ScSingleRefData aRef;
3550 aRef.InitAddress( aIter.GetPos());
3551 maRawToken.SetSingleReference( aRef );
3552 return true;
3558 // And now a fallback for named expressions during document load time when
3559 // cell content isn't available yet. This could be the preferred method IF
3560 // the ScDBData column names were maintained and refreshed on ALL sheet
3561 // operations, including cell content changes.
3562 sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
3563 if (nOffset >= 0)
3565 if (pDBData->HasHeader())
3567 ScSingleRefData aRef;
3568 ScAddress aAdr( aRange.aStart);
3569 aAdr.IncCol( nOffset);
3570 aRef.InitAddress( aAdr);
3571 maRawToken.SetSingleReference( aRef );
3573 else
3575 /* TODO: this probably needs a new token type to hold offset and
3576 * name, to be handled in HandleTableRef(). We can't use a
3577 * reference token here because any reference would be wrong as
3578 * there are no header cells to be referenced. However, it would
3579 * only work as long as the ScDBData column names are not
3580 * invalidated during document structure changes, otherwise
3581 * recompiling the same formula could not resolve the name again.
3582 * As long as this doesn't work generate an error. */
3583 return false;
3585 return true;
3588 return false;
3591 void ScCompiler::SetAutoCorrection( bool bVal )
3593 assert(mbJumpCommandReorder);
3594 bAutoCorrect = bVal;
3595 mbStopOnError = !bVal;
3598 void ScCompiler::AutoCorrectParsedSymbol()
3600 sal_Int32 nPos = aCorrectedSymbol.getLength();
3601 if ( nPos )
3603 nPos--;
3604 const sal_Unicode cQuote = '\"';
3605 const sal_Unicode cx = 'x';
3606 const sal_Unicode cX = 'X';
3607 sal_Unicode c1 = aCorrectedSymbol[0];
3608 sal_Unicode c2 = aCorrectedSymbol[nPos];
3609 sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
3610 if ( c1 == cQuote && c2 != cQuote )
3611 { // "...
3612 // What's not a word doesn't belong to it.
3613 // Don't be pedantic: c < 128 should be sufficient here.
3614 while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
3615 ((GetCharTableFlags(aCorrectedSymbol[nPos], aCorrectedSymbol[nPos-1]) &
3616 (SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_DONTCARE)) == 0)) )
3617 nPos--;
3618 if ( nPos == MAXSTRLEN - 2 )
3619 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) ); // '"' the 255th character
3620 else
3621 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) );
3622 bCorrected = true;
3624 else if ( c1 != cQuote && c2 == cQuote )
3625 { // ..."
3626 aCorrectedSymbol = OUStringLiteral1<cQuote>() + aCorrectedSymbol;
3627 bCorrected = true;
3629 else if ( nPos == 0 && (c1 == cx || c1 == cX) )
3630 { // x => *
3631 aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
3632 bCorrected = true;
3634 else if ( (GetCharTableFlags( c1, 0 ) & SC_COMPILER_C_CHAR_VALUE)
3635 && (GetCharTableFlags( c2, c2p ) & SC_COMPILER_C_CHAR_VALUE) )
3637 if ( comphelper::string::getTokenCount(aCorrectedSymbol, cx) > 1 )
3638 { // x => *
3639 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
3640 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUString(cx), OUString(c));
3641 bCorrected = true;
3643 if ( comphelper::string::getTokenCount(aCorrectedSymbol, cX) > 1 )
3644 { // X => *
3645 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
3646 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUString(cX), OUString(c));
3647 bCorrected = true;
3650 else
3652 OUString aSymbol( aCorrectedSymbol );
3653 OUString aDoc;
3654 sal_Int32 nPosition;
3655 if ( aSymbol[0] == '\''
3656 && ((nPosition = aSymbol.indexOf( "'#" )) != -1) )
3657 { // Split off 'Doc'#, may be d:\... or whatever
3658 aDoc = aSymbol.copy(0, nPosition + 2);
3659 aSymbol = aSymbol.copy(nPosition + 2);
3661 sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
3662 bool bColons;
3663 if ( nRefs > 2 )
3664 { // duplicated or too many ':'? B:2::C10 => B2:C10
3665 bColons = true;
3666 sal_Int32 nIndex = 0;
3667 OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
3668 sal_Int32 nLen1 = aTmp1.getLength();
3669 OUString aSym, aTmp2;
3670 bool bLastAlp, bNextNum;
3671 bLastAlp = bNextNum = true;
3672 sal_Int32 nStrip = 0;
3673 sal_Int32 nCount = nRefs;
3674 for ( sal_Int32 j=1; j<nCount; j++ )
3676 aTmp2 = aSymbol.getToken( 0, ':', nIndex );
3677 sal_Int32 nLen2 = aTmp2.getLength();
3678 if ( nLen1 || nLen2 )
3680 if ( nLen1 )
3682 aSym += aTmp1;
3683 bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
3685 if ( nLen2 )
3687 bNextNum = CharClass::isAsciiNumeric( aTmp2 );
3688 if ( bLastAlp == bNextNum && nStrip < 1 )
3690 // Must be alternating number/string, only
3691 // strip within a reference.
3692 nRefs--;
3693 nStrip++;
3695 else
3697 if ( !aSym.isEmpty() && !aSym.endsWith(":"))
3698 aSym += ":";
3699 nStrip = 0;
3701 bLastAlp = !bNextNum;
3703 else
3704 { // ::
3705 nRefs--;
3706 if ( nLen1 )
3707 { // B10::C10 ? append ':' on next round
3708 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
3709 nStrip++;
3712 aTmp1 = aTmp2;
3713 nLen1 = nLen2;
3715 else
3716 nRefs--;
3718 aSymbol = aSym;
3719 aSymbol += aTmp1;
3721 else
3722 bColons = false;
3723 if ( nRefs && nRefs <= 2 )
3724 { // reference twisted? 4A => A4 etc.
3725 OUString aTab[2], aRef[2];
3726 const ScAddress::Details aDetails( pConv->meConv, aPos );
3727 if ( nRefs == 2 )
3729 aRef[0] = aSymbol.getToken( 0, ':' );
3730 aRef[1] = aSymbol.getToken( 1, ':' );
3732 else
3733 aRef[0] = aSymbol;
3735 bool bChanged = false;
3736 bool bOk = true;
3737 sal_uInt16 nMask = SCA_VALID | SCA_VALID_COL | SCA_VALID_ROW;
3738 for ( int j=0; j<nRefs; j++ )
3740 sal_Int32 nTmp = 0;
3741 sal_Int32 nDotPos = -1;
3742 while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
3743 nDotPos = nTmp++; // the last one counts
3744 if ( nDotPos != -1 )
3746 aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
3747 aRef[j] = aRef[j].copy( nDotPos + 1 );
3749 OUString aOld( aRef[j] );
3750 OUString aStr2;
3751 const sal_Unicode* p = aRef[j].getStr();
3752 while ( *p && CharClass::isAsciiNumeric( OUString(*p) ) )
3753 aStr2 += OUString(*p++);
3754 aRef[j] = OUString( p );
3755 aRef[j] += aStr2;
3756 if ( bColons || aRef[j] != aOld )
3758 bChanged = true;
3759 ScAddress aAdr;
3760 bOk &= ((aAdr.Parse( aRef[j], pDoc, aDetails ) & nMask) == nMask);
3763 if ( bChanged && bOk )
3765 aCorrectedSymbol = aDoc;
3766 aCorrectedSymbol += aTab[0];
3767 aCorrectedSymbol += aRef[0];
3768 if ( nRefs == 2 )
3770 aCorrectedSymbol += ":";
3771 aCorrectedSymbol += aTab[1];
3772 aCorrectedSymbol += aRef[1];
3774 bCorrected = true;
3781 static inline bool lcl_UpperAsciiOrI18n( OUString& rUpper, const OUString& rOrg, FormulaGrammar::Grammar eGrammar )
3783 if (FormulaGrammar::isODFF( eGrammar ))
3785 // ODFF has a defined set of English function names, avoid i18n
3786 // overhead.
3787 rUpper = rOrg.toAsciiUpperCase();
3788 return true;
3790 else
3792 rUpper = ScGlobal::pCharClass->uppercase(rOrg);
3793 return false;
3797 bool ScCompiler::NextNewToken( bool bInArray )
3799 bool bAllowBooleans = bInArray;
3800 sal_Int32 nSpaces = NextSymbol(bInArray);
3802 if (!cSymbol[0])
3803 return false;
3805 if( nSpaces )
3807 ScRawToken aToken;
3808 aToken.SetOpCode( ocSpaces );
3809 aToken.sbyte.cByte = (sal_uInt8) ( nSpaces > 255 ? 255 : nSpaces );
3810 if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) )
3812 SetError(errCodeOverflow);
3813 return false;
3817 // Short cut for references when reading ODF to speedup things.
3818 if (mnPredetectedReference)
3820 OUString aStr( cSymbol);
3821 bool bInvalidExternalNameRange;
3822 if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange ))
3824 /* TODO: it would be nice to generate a #REF! error here, which
3825 * would need an ocBad token with additional error value.
3826 * FormulaErrorToken wouldn't do because we want to preserve the
3827 * original string containing partial valid address
3828 * information if not ODFF (in that case it was already handled).
3829 * */
3830 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aStr);
3831 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
3832 maRawToken.NewOpCode( ocBad );
3834 return true;
3837 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
3838 !bAutoCorrect )
3839 { // special case to speed up broken [$]#REF documents
3840 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
3841 * be processed as usual. That would need some special treatment,
3842 * also in NextSymbol() because of possible combinations of
3843 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
3844 * handled by IsPredetectedReference(), this case here remains for
3845 * manual/API input. */
3846 OUString aBad( aFormula.copy( nSrcPos-1 ) );
3847 eLastOp = pArr->AddBad( aBad )->GetOpCode();
3848 return false;
3851 if( IsString() )
3852 return true;
3854 bool bMayBeFuncName;
3855 bool bAsciiNonAlnum; // operators, separators, ...
3856 if ( cSymbol[0] < 128 )
3858 bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
3859 if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') )
3861 SvtMiscOptions aOpt;
3862 bMayBeFuncName = aOpt.IsExperimentalMode();
3865 bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
3867 else
3869 OUString aTmpStr( cSymbol[0] );
3870 bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 );
3871 bAsciiNonAlnum = false;
3873 if (bAsciiNonAlnum && cSymbol[1] == 0)
3875 // Shortcut for operators and separators that need no further checks or upper.
3876 if (IsOpCode( OUString( cSymbol), bInArray ))
3877 return true;
3879 if ( bMayBeFuncName )
3881 // a function name must be followed by a parenthesis
3882 const sal_Unicode* p = aFormula.getStr() + nSrcPos;
3883 while( *p == ' ' )
3884 p++;
3885 bMayBeFuncName = ( *p == '(' );
3888 // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
3889 // IsReference().
3891 OUString aUpper;
3895 const OUString aOrg( cSymbol );
3897 // Check for TableRef column specifier first, it may be anything.
3898 if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
3900 if (IsTableRefColumn( aOrg ))
3901 return true;
3902 // Do not attempt to resolve as any other name.
3903 aUpper = aOrg; // for ocBad
3904 break; // do; create ocBad token or set error.
3907 mbRewind = false;
3908 aUpper.clear();
3909 bool bAsciiUpper = false;
3911 if (bAsciiNonAlnum)
3913 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3914 if (cSymbol[0] == '#')
3916 // Check for TableRef item specifiers first.
3917 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
3919 if (IsTableRefItem( aUpper ))
3920 return true;
3923 // This can be only an error constant, if any.
3924 if (IsErrorConstant( aUpper))
3925 return true;
3927 break; // do; create ocBad token or set error.
3929 if (IsOpCode( aUpper, bInArray ))
3930 return true;
3933 if (bMayBeFuncName)
3935 if (aUpper.isEmpty())
3936 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3937 if (IsOpCode( aUpper, bInArray ))
3938 return true;
3941 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
3942 // referred => IsReference() before IsValue().
3943 // Preserve case of file names in external references.
3944 if (IsReference( aOrg ))
3946 if (mbRewind) // Range operator, but no direct reference.
3947 continue; // do; up to range operator.
3948 // If a syntactically correct reference was recognized but invalid
3949 // e.g. because of non-existing sheet name => entire reference
3950 // ocBad to preserve input instead of #REF!.A1
3951 if (!maRawToken.IsValidReference())
3953 aUpper = aOrg; // ensure for ocBad
3954 break; // do; create ocBad token or set error.
3956 return true;
3959 if (aUpper.isEmpty())
3960 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
3962 // IsBoolean() before IsValue() to catch inline bools without the kludge
3963 // for inline arrays.
3964 if (bAllowBooleans && IsBoolean( aUpper ))
3965 return true;
3967 if (IsValue( aUpper ))
3968 return true;
3970 // User defined names and such do need i18n upper also in ODF.
3971 if (bAsciiUpper)
3972 aUpper = ScGlobal::pCharClass->uppercase( aOrg );
3974 if (IsNamedRange( aUpper ))
3975 return true;
3976 // Preserve case of file names in external references.
3977 bool bInvalidExternalNameRange;
3978 if (IsExternalNamedRange( aOrg, bInvalidExternalNameRange ))
3979 return true;
3980 // Preserve case of file names in external references even when range
3981 // is not valid and previous check failed tdf#89330
3982 if (bInvalidExternalNameRange)
3984 // add ocBad but do not lowercase
3985 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aOrg);
3986 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
3987 maRawToken.NewOpCode( ocBad );
3988 return true;
3990 if (IsDBRange( aUpper ))
3991 return true;
3992 // If followed by '(' (with or without space inbetween) it can not be a
3993 // column/row label. Prevent arbitrary content detection.
3994 if (!bMayBeFuncName && IsColRowName( aUpper ))
3995 return true;
3996 if (bMayBeFuncName && IsMacro( aUpper ))
3997 return true;
3998 if (bMayBeFuncName && IsOpCode2( aUpper ))
3999 return true;
4001 } while (mbRewind);
4003 if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE )
4005 // set an error
4006 SetError( errNoName );
4007 if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_BREAK)
4008 return false; // end compilation
4011 // Provide single token information and continue. Do not set an error, that
4012 // would prematurely end compilation. Simple unknown names are handled by
4013 // the interpreter.
4014 aUpper = ScGlobal::pCharClass->lowercase( aUpper );
4015 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aUpper);
4016 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase());
4017 maRawToken.NewOpCode( ocBad );
4018 if ( bAutoCorrect )
4019 AutoCorrectParsedSymbol();
4020 return true;
4023 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
4025 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
4026 sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
4027 OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4028 if( pArr->GetLen() == nExpectedCount )
4030 FormulaToken** ppTokens = pArr->GetArray();
4031 // string tokens expected, GetString() will assert if token type is wrong
4032 rFormula = ppTokens[0]->GetString().getString();
4033 if( bExternal )
4034 rFormulaNmsp = ppTokens[1]->GetString().getString();
4038 namespace {
4040 class ExternalFileInserter : std::unary_function<sal_uInt16, void>
4042 ScAddress maPos;
4043 ScExternalRefManager& mrRefMgr;
4044 public:
4045 ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
4046 maPos(rPos), mrRefMgr(rRefMgr) {}
4048 void operator() (sal_uInt16 nFileId) const
4050 mrRefMgr.insertRefCell(nFileId, maPos);
4056 ScTokenArray* ScCompiler::CompileString( const OUString& rFormula )
4058 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4059 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
4060 SetGrammar( FormulaGrammar::GRAM_PODF );
4062 ScTokenArray aArr;
4063 pArr = &aArr;
4064 aFormula = comphelper::string::strip(rFormula, ' ');
4066 nSrcPos = 0;
4067 bCorrected = false;
4068 if ( bAutoCorrect )
4070 aCorrectedFormula.clear();
4071 aCorrectedSymbol.clear();
4073 sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
4074 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4076 nSrcPos++;
4077 nForced++;
4078 if ( bAutoCorrect )
4079 aCorrectedFormula += "=";
4081 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4083 nSrcPos++;
4084 nForced++;
4085 if ( bAutoCorrect )
4086 aCorrectedFormula += "=";
4088 struct FunctionStack
4090 OpCode eOp;
4091 short nSep;
4093 // FunctionStack only used if PODF or OOXML!
4094 bool bPODF = FormulaGrammar::isPODF( meGrammar);
4095 bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
4096 bool bUseFunctionStack = (bPODF || bOOXML);
4097 const size_t nAlloc = 512;
4098 FunctionStack aFuncs[ nAlloc ];
4099 FunctionStack* pFunctionStack = (bUseFunctionStack && static_cast<size_t>(rFormula.getLength()) > nAlloc ?
4100 new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
4101 pFunctionStack[0].eOp = ocNone;
4102 pFunctionStack[0].nSep = 0;
4103 size_t nFunction = 0;
4104 short nBrackets = 0;
4105 bool bInArray = false;
4106 eLastOp = ocOpen;
4107 while( NextNewToken( bInArray ) )
4109 const OpCode eOp = maRawToken.GetOpCode();
4110 if (eOp == ocSkip)
4111 continue;
4113 switch (eOp)
4115 case ocOpen:
4117 ++nBrackets;
4118 if (bUseFunctionStack)
4120 ++nFunction;
4121 pFunctionStack[ nFunction ].eOp = eLastOp;
4122 pFunctionStack[ nFunction ].nSep = 0;
4125 break;
4126 case ocClose:
4128 if( !nBrackets )
4130 SetError( errPairExpected );
4131 if ( bAutoCorrect )
4133 bCorrected = true;
4134 aCorrectedSymbol.clear();
4137 else
4138 nBrackets--;
4139 if (bUseFunctionStack && nFunction)
4140 --nFunction;
4142 break;
4143 case ocSep:
4145 if (bUseFunctionStack)
4146 ++pFunctionStack[ nFunction ].nSep;
4148 break;
4149 case ocArrayOpen:
4151 if( bInArray )
4152 SetError( errNestedArray );
4153 else
4154 bInArray = true;
4155 // Don't count following column separator as parameter separator.
4156 if (bUseFunctionStack)
4158 ++nFunction;
4159 pFunctionStack[ nFunction ].eOp = eOp;
4160 pFunctionStack[ nFunction ].nSep = 0;
4163 break;
4164 case ocArrayClose:
4166 if( bInArray )
4168 bInArray = false;
4170 else
4172 SetError( errPairExpected );
4173 if ( bAutoCorrect )
4175 bCorrected = true;
4176 aCorrectedSymbol.clear();
4179 if (bUseFunctionStack && nFunction)
4180 --nFunction;
4182 break;
4183 case ocTableRefOpen:
4185 // Don't count following item separator as parameter separator.
4186 if (bUseFunctionStack)
4188 ++nFunction;
4189 pFunctionStack[ nFunction ].eOp = eOp;
4190 pFunctionStack[ nFunction ].nSep = 0;
4193 break;
4194 case ocTableRefClose:
4196 if (bUseFunctionStack && nFunction)
4197 --nFunction;
4199 break;
4200 default:
4201 break;
4203 if( (eLastOp == ocSep ||
4204 eLastOp == ocArrayRowSep ||
4205 eLastOp == ocArrayColSep ||
4206 eLastOp == ocArrayOpen) &&
4207 (eOp == ocSep ||
4208 eOp == ocClose ||
4209 eOp == ocArrayRowSep ||
4210 eOp == ocArrayColSep ||
4211 eOp == ocArrayClose) )
4213 // FIXME: should we check for known functions with optional empty
4214 // args so the correction dialog can do better?
4215 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
4217 SetError(errCodeOverflow); break;
4220 if (bOOXML)
4222 // Append a parameter for WEEKNUM, all 1.0
4223 // Function is already closed, parameter count is nSep+1
4224 size_t nFunc = nFunction + 1;
4225 if (eOp == ocClose &&
4226 (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
4227 pFunctionStack[ nFunc ].nSep == 0))
4229 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4230 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4232 SetError(errCodeOverflow); break;
4236 else if (bPODF)
4238 /* TODO: for now this is the only PODF adapter. If there were more,
4239 * factor this out. */
4240 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4241 if (eOp == ocSep &&
4242 pFunctionStack[ nFunction ].eOp == ocAddress &&
4243 pFunctionStack[ nFunction ].nSep == 3)
4245 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4246 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4248 SetError(errCodeOverflow); break;
4250 ++pFunctionStack[ nFunction ].nSep;
4253 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken());
4254 if (!pNewToken)
4256 SetError(errCodeOverflow);
4257 break;
4259 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
4261 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
4263 else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
4265 sal_uInt16 nIdx = pArr->GetLen() - 1;
4266 const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
4267 if (pPrev && pPrev->GetOpCode() == ocDBArea)
4269 FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
4270 maTableRefs.push_back( TableRefEntry( pTableRefToken));
4271 // pPrev may be dead hereafter.
4272 static_cast<ScTokenArray*>(pArr)->ReplaceToken( 1, pTableRefToken,
4273 FormulaTokenArray::ReplaceMode::BACKWARD_CODE_ONLY);
4276 switch (eOp)
4278 case ocTableRefOpen:
4279 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4280 if (maTableRefs.empty())
4281 SetError(errPair);
4282 else
4283 ++maTableRefs.back().mnLevel;
4284 break;
4285 case ocTableRefClose:
4286 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4287 if (maTableRefs.empty())
4288 SetError(errPair);
4289 else
4291 if (--maTableRefs.back().mnLevel == 0)
4292 maTableRefs.pop_back();
4294 break;
4295 default:
4296 break;
4298 eLastOp = maRawToken.GetOpCode();
4299 if ( bAutoCorrect )
4300 aCorrectedFormula += aCorrectedSymbol;
4302 if ( mbCloseBrackets )
4304 if( bInArray )
4306 FormulaByteToken aToken( ocArrayClose );
4307 if( !pArr->AddToken( aToken ) )
4309 SetError(errCodeOverflow);
4311 else if ( bAutoCorrect )
4312 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose);
4315 FormulaByteToken aToken( ocClose );
4316 while( nBrackets-- )
4318 if( !pArr->AddToken( aToken ) )
4320 SetError(errCodeOverflow); break;
4322 if ( bAutoCorrect )
4323 aCorrectedFormula += mxSymbols->getSymbol(ocClose);
4326 if ( nForced >= 2 )
4327 pArr->SetRecalcModeForced();
4329 if (pFunctionStack != &aFuncs[0])
4330 delete [] pFunctionStack;
4332 // remember pArr, in case a subsequent CompileTokenArray() is executed.
4333 ScTokenArray* pNew = new ScTokenArray( aArr );
4334 pNew->GenHash();
4335 pArr = pNew;
4337 if (!maExternalFiles.empty())
4339 // Remove duplicates, and register all external files found in this cell.
4340 std::sort(maExternalFiles.begin(), maExternalFiles.end());
4341 std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
4342 std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *pDoc->GetExternalRefManager()));
4343 maExternalFiles.erase(itEnd, maExternalFiles.end());
4346 return pNew;
4349 ScTokenArray* ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
4351 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
4352 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
4353 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
4355 ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool();
4356 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
4357 table::CellAddress aReferencePos;
4358 ScUnoConversion::FillApiAddress( aReferencePos, aPos );
4359 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
4360 ScTokenArray aTokenArray;
4361 if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) )
4363 // remember pArr, in case a subsequent CompileTokenArray() is executed.
4364 ScTokenArray* pNew = new ScTokenArray( aTokenArray );
4365 pArr = pNew;
4366 return pNew;
4369 catch( uno::Exception& )
4372 // no success - fallback to some internal grammar and hope the best
4373 return CompileString( rFormula );
4376 ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const
4378 ScRangeData* pRangeData = NULL;
4379 bool bGlobal = rToken.IsGlobal();
4380 if (bGlobal)
4381 // global named range.
4382 pRangeData = pDoc->GetRangeName()->findByIndex( rToken.GetIndex());
4383 else
4385 // sheet local named range.
4386 const ScRangeName* pRN = pDoc->GetRangeName( aPos.Tab());
4387 if (pRN)
4388 pRangeData = pRN->findByIndex( rToken.GetIndex());
4390 return pRangeData;
4393 bool ScCompiler::HandleRange()
4395 const ScRangeData* pRangeData = GetRangeData( *mpToken);
4396 if (pRangeData)
4398 sal_uInt16 nErr = pRangeData->GetErrCode();
4399 if( nErr )
4400 SetError( errNoName );
4401 else if (mbJumpCommandReorder)
4403 ScTokenArray* pNew;
4404 // put named formula into parentheses.
4405 // But only if there aren't any yet, parenthetical
4406 // ocSep doesn't work, e.g. SUM((...;...))
4407 // or if not directly between ocSep/parenthesis,
4408 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
4409 // in short: if it isn't a self-contained expression.
4410 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
4411 FormulaToken* p2 = pArr->PeekNextNoSpaces();
4412 OpCode eOp1 = (p1 ? p1->GetOpCode() : static_cast<OpCode>( ocSep ) );
4413 OpCode eOp2 = (p2 ? p2->GetOpCode() : static_cast<OpCode>( ocSep ) );
4414 bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
4415 bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
4416 bool bAddPair = !(bBorder1 && bBorder2);
4417 if ( bAddPair )
4419 pNew = new ScTokenArray();
4420 pNew->AddOpCode( ocClose );
4421 PushTokenArray( pNew, true );
4422 pNew->Reset();
4424 pNew = pRangeData->GetCode()->Clone();
4425 pNew->SetFromRangeName( true );
4426 PushTokenArray( pNew, true );
4427 if( pRangeData->HasReferences() )
4429 SetRelNameReference();
4430 MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow());
4432 pNew->Reset();
4433 if ( bAddPair )
4435 pNew = new ScTokenArray();
4436 pNew->AddOpCode( ocOpen );
4437 PushTokenArray( pNew, true );
4438 pNew->Reset();
4440 return GetToken();
4443 else
4444 SetError(errNoName);
4445 return true;
4448 bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken)
4450 // Handle external range names.
4451 switch (_aToken.GetType())
4453 case svExternalSingleRef:
4454 case svExternalDoubleRef:
4455 break;
4456 case svExternalName:
4458 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4459 const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
4460 if (!pFile)
4462 SetError(errNoName);
4463 return true;
4466 OUString aName = _aToken.GetString().getString();
4467 ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens(
4468 _aToken.GetIndex(), aName, &aPos);
4470 if (!xNew)
4472 SetError(errNoName);
4473 return true;
4476 ScTokenArray* pNew = xNew->Clone();
4477 PushTokenArray( pNew, true);
4478 if (pNew->GetNextReference() != NULL)
4480 SetRelNameReference();
4481 MoveRelWrap(MAXCOL, MAXROW);
4483 pNew->Reset();
4484 return GetToken();
4486 default:
4487 OSL_FAIL("Wrong type for external reference!");
4488 return false;
4490 return true;
4493 // reference of named range with relative references
4495 void ScCompiler::SetRelNameReference()
4497 pArr->Reset();
4498 for( formula::FormulaToken* t = pArr->GetNextReference(); t;
4499 t = pArr->GetNextReference() )
4501 ScSingleRefData& rRef1 = *t->GetSingleRef();
4502 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
4503 rRef1.SetRelName( true );
4504 if ( t->GetType() == svDoubleRef )
4506 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
4507 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
4508 rRef2.SetRelName( true );
4513 // Wrap-adjust relative references of a RangeName to current position,
4514 // don't call for other token arrays!
4515 void ScCompiler::MoveRelWrap( SCCOL nMaxCol, SCROW nMaxRow )
4517 pArr->Reset();
4518 for( formula::FormulaToken* t = pArr->GetNextReference(); t;
4519 t = pArr->GetNextReference() )
4521 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4522 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
4523 else
4524 ScRefUpdate::MoveRelWrap( pDoc, aPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
4528 // Wrap-adjust relative references of a RangeName to current position,
4529 // don't call for other token arrays!
4530 void ScCompiler::MoveRelWrap( ScTokenArray& rArr, ScDocument* pDoc, const ScAddress& rPos,
4531 SCCOL nMaxCol, SCROW nMaxRow )
4533 rArr.Reset();
4534 for( formula::FormulaToken* t = rArr.GetNextReference(); t;
4535 t = rArr.GetNextReference() )
4537 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
4538 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
4539 else
4540 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
4544 bool ScCompiler::IsCharFlagAllConventions(
4545 OUString const & rStr, sal_Int32 nPos, sal_uLong nFlags, bool bTestLetterNumeric )
4547 sal_Unicode c = rStr[ nPos ];
4548 sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
4549 if (c < 128)
4551 for ( int nConv = formula::FormulaGrammar::CONV_UNSPECIFIED;
4552 ++nConv < formula::FormulaGrammar::CONV_LAST; )
4554 if (pConventions[nConv] &&
4555 ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
4556 return false;
4557 // convention not known => assume valid
4559 return true;
4561 else if (bTestLetterNumeric)
4562 return ScGlobal::pCharClass->isLetterNumeric( rStr, nPos );
4563 else
4564 return false;
4567 void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
4569 const FormulaToken* t = pTokenP;
4570 sal_uInt16 nFileId = t->GetIndex();
4571 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
4572 const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
4573 if (!pFileName)
4574 return;
4576 switch (t->GetType())
4578 case svExternalName:
4579 rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString()));
4580 break;
4581 case svExternalSingleRef:
4582 pConv->makeExternalRefStr(
4583 rBuffer, GetPos(), nFileId, *pFileName, t->GetString().getString(),
4584 *t->GetSingleRef());
4585 break;
4586 case svExternalDoubleRef:
4588 vector<OUString> aTabNames;
4589 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
4590 // No sheet names is a valid case if external sheets were not
4591 // cached in this document and external document is not reachable,
4592 // else not and worth to be investigated.
4593 SAL_WARN_IF( aTabNames.empty(), "sc.core", "wrecked cache of external document? '" <<
4594 *pFileName << "' '" << t->GetString().getString() << "'");
4596 pConv->makeExternalRefStr(
4597 rBuffer, GetPos(), nFileId, *pFileName, aTabNames, t->GetString().getString(),
4598 *t->GetDoubleRef());
4600 break;
4601 default:
4602 // warning, not error, otherwise we may end up with a never
4603 // ending message box loop if this was the cursor cell to be redrawn.
4604 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
4608 void ScCompiler::CreateStringFromMatrix( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
4610 const ScMatrix* pMatrix = pTokenP->GetMatrix();
4611 SCSIZE nC, nMaxC, nR, nMaxR;
4613 pMatrix->GetDimensions( nMaxC, nMaxR);
4615 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
4616 for( nR = 0 ; nR < nMaxR ; nR++)
4618 if( nR > 0)
4620 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
4623 for( nC = 0 ; nC < nMaxC ; nC++)
4625 if( nC > 0)
4627 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
4630 if( pMatrix->IsValue( nC, nR ) )
4632 if (pMatrix->IsBoolean(nC, nR))
4633 AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
4634 else
4636 sal_uInt16 nErr = pMatrix->GetError(nC, nR);
4637 if (nErr)
4638 rBuffer.append(ScGlobal::GetErrorString(nErr));
4639 else
4640 AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
4643 else if( pMatrix->IsEmpty( nC, nR ) )
4645 else if( pMatrix->IsString( nC, nR ) )
4646 AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() );
4649 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
4652 namespace {
4653 void escapeTableRefColumnSpecifier( OUString& rStr )
4655 const sal_Int32 n = rStr.getLength();
4656 OUStringBuffer aBuf( n * 2 );
4657 const sal_Unicode* p = rStr.getStr();
4658 const sal_Unicode* const pStop = p + n;
4659 for ( ; p < pStop; ++p)
4661 const sal_Unicode c = *p;
4662 switch (c)
4664 case '\'':
4665 case '[':
4666 case '#':
4667 case ']':
4668 aBuf.append( '\'' );
4669 break;
4670 default:
4671 ; // nothing
4673 aBuf.append( c );
4675 rStr = aBuf.makeStringAndClear();
4679 void ScCompiler::CreateStringFromSingleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
4681 const FormulaToken* p;
4682 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
4683 const OpCode eOp = _pTokenP->GetOpCode();
4684 const ScSingleRefData& rRef = *_pTokenP->GetSingleRef();
4685 ScComplexRefData aRef;
4686 aRef.Ref1 = aRef.Ref2 = rRef;
4687 if ( eOp == ocColRowName )
4689 ScAddress aAbs = rRef.toAbs(aPos);
4690 if (pDoc->HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab()))
4692 OUString aStr = pDoc->GetString(aAbs);
4693 EnQuote( aStr );
4694 rBuffer.append(aStr);
4696 else
4698 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
4699 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef,
4700 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
4703 else if (pArr && (p = pArr->PeekPrevNoSpaces()) && p->GetOpCode() == ocTableRefOpen)
4705 ScAddress aAbs = rRef.toAbs(aPos);
4706 OUString aStr = pDoc->GetString(aAbs);
4707 escapeTableRefColumnSpecifier( aStr);
4708 rBuffer.append(aStr);
4710 else
4711 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef,
4712 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
4715 void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
4717 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
4718 pConv->makeRefStr(rBuffer, meGrammar, aPos, aErrRef, GetSetupTabNames(),
4719 *_pTokenP->GetDoubleRef(), false, (pArr && pArr->IsFromRangeName()));
4722 void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
4724 const OpCode eOp = _pTokenP->GetOpCode();
4725 OUStringBuffer aBuffer;
4726 switch ( eOp )
4728 case ocName:
4730 const ScRangeData* pData = GetRangeData( *_pTokenP);
4731 if (pData)
4732 aBuffer.append(pData->GetName());
4734 break;
4735 case ocDBArea:
4737 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
4738 if (pDBData)
4739 aBuffer.append(pDBData->GetName());
4741 break;
4742 case ocTableRef:
4744 if (NeedsTableRefTransformation())
4746 // Write the resulting reference if TableRef is not supported.
4747 const ScTableRefToken* pTR = dynamic_cast<const ScTableRefToken*>(_pTokenP);
4748 if (!pTR)
4749 AppendErrorConstant( aBuffer, errNoCode);
4750 else
4752 const FormulaToken* pRef = pTR->GetAreaRefRPN();
4753 if (!pRef)
4754 AppendErrorConstant( aBuffer, errNoCode);
4755 else
4757 switch (pRef->GetType())
4759 case svSingleRef:
4760 CreateStringFromSingleRef( aBuffer, pRef);
4761 break;
4762 case svDoubleRef:
4763 CreateStringFromDoubleRef( aBuffer, pRef);
4764 break;
4765 case svError:
4766 AppendErrorConstant( aBuffer, pRef->GetError());
4767 break;
4768 default:
4769 AppendErrorConstant( aBuffer, errNoCode);
4774 else
4776 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
4777 if (pDBData)
4778 aBuffer.append(pDBData->GetName());
4781 break;
4782 default:
4783 ; // nothing
4785 if ( !aBuffer.isEmpty() )
4786 rBuffer.append(aBuffer.makeStringAndClear());
4787 else
4788 rBuffer.append(ScGlobal::GetRscString(STR_NO_NAME_REF));
4791 void ScCompiler::LocalizeString( OUString& rName ) const
4793 ScGlobal::GetAddInCollection()->LocalizeString( rName );
4796 // Put quotes around string if non-alphanumeric characters are contained,
4797 // quote characters contained within are escaped by '\\'.
4798 bool ScCompiler::EnQuote( OUString& rStr )
4800 sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.getLength() );
4801 if ( !CharClass::isNumericType( nType )
4802 && CharClass::isAlphaNumericType( nType ) )
4803 return false;
4805 sal_Int32 nPos = 0;
4806 while ( (nPos = rStr.indexOf( '\'', nPos)) != -1 )
4808 rStr = rStr.replaceAt( nPos, 0, "\\" );
4809 nPos += 2;
4811 rStr = "'" + rStr + "'";
4812 return true;
4815 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const
4817 return pConv->getSpecialSymbol(eType);
4820 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2, bool bReuseDoubleRef )
4822 return extendRangeReference( rTok1, rTok2, aPos,bReuseDoubleRef );
4825 void ScCompiler::fillAddInToken(::std::vector< ::com::sun::star::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
4827 // All known AddIn functions.
4828 sheet::FormulaOpCodeMapEntry aEntry;
4829 aEntry.Token.OpCode = ocExternal;
4831 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection();
4832 const long nCount = pColl->GetFuncCount();
4833 for (long i=0; i < nCount; ++i)
4835 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
4836 if (pFuncData)
4838 if ( _bIsEnglish )
4840 OUString aName;
4841 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName))
4842 aEntry.Name = aName;
4843 else
4844 aEntry.Name = pFuncData->GetUpperName();
4846 else
4847 aEntry.Name = pFuncData->GetUpperLocal();
4848 aEntry.Token.Data <<= OUString( pFuncData->GetOriginalName());
4849 _rVec.push_back( aEntry);
4852 // FIXME: what about those old non-UNO AddIns?
4855 bool ScCompiler::HandleColRowName()
4857 ScSingleRefData& rRef = *mpToken.get()->GetSingleRef();
4858 ScAddress aAbs = rRef.toAbs(aPos);
4859 if (!ValidAddress(aAbs))
4861 SetError( errNoRef );
4862 return true;
4864 SCCOL nCol = aAbs.Col();
4865 SCROW nRow = aAbs.Row();
4866 SCTAB nTab = aAbs.Tab();
4867 ScAddress aLook = aAbs;
4868 bool bColName = rRef.IsColRel();
4869 SCCOL nMyCol = aPos.Col();
4870 SCROW nMyRow = aPos.Row();
4871 bool bInList = false;
4872 bool bValidName = false;
4873 ScRangePairList* pRL = (bColName ?
4874 pDoc->GetColNameRanges() : pDoc->GetRowNameRanges());
4875 ScRange aRange;
4876 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4878 ScRangePair* pR = (*pRL)[i];
4879 if ( pR->GetRange(0).In( aLook ) )
4881 bInList = bValidName = true;
4882 aRange = pR->GetRange(1);
4883 if ( bColName )
4885 aRange.aStart.SetCol( nCol );
4886 aRange.aEnd.SetCol( nCol );
4888 else
4890 aRange.aStart.SetRow( nRow );
4891 aRange.aEnd.SetRow( nRow );
4893 break; // for
4896 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() )
4897 { // automagically or created by copying and NamePos isn't in list
4898 ScRefCellValue aCell;
4899 aCell.assign(*pDoc, aLook);
4900 bool bString = aCell.hasString();
4901 if (!bString && aCell.isEmpty())
4902 bString = true; // empty cell is ok
4903 if ( bString )
4904 { // coresponds with ScInterpreter::ScColRowNameAuto()
4905 bValidName = true;
4906 if ( bColName )
4907 { // ColName
4908 SCROW nStartRow = nRow + 1;
4909 if ( nStartRow > MAXROW )
4910 nStartRow = MAXROW;
4911 SCROW nMaxRow = MAXROW;
4912 if ( nMyCol == nCol )
4913 { // formula cell in same column
4914 if ( nMyRow == nStartRow )
4915 { // take remainder under name cell
4916 nStartRow++;
4917 if ( nStartRow > MAXROW )
4918 nStartRow = MAXROW;
4920 else if ( nMyRow > nStartRow )
4921 { // from name cell down to formula cell
4922 nMaxRow = nMyRow - 1;
4925 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4926 { // next defined ColNameRange below limits row
4927 ScRangePair* pR = (*pRL)[i];
4928 const ScRange& rRange = pR->GetRange(1);
4929 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
4930 { // identical column range
4931 SCROW nTmp = rRange.aStart.Row();
4932 if ( nStartRow < nTmp && nTmp <= nMaxRow )
4933 nMaxRow = nTmp - 1;
4936 aRange.aStart.Set( nCol, nStartRow, nTab );
4937 aRange.aEnd.Set( nCol, nMaxRow, nTab );
4939 else
4940 { // RowName
4941 SCCOL nStartCol = nCol + 1;
4942 if ( nStartCol > MAXCOL )
4943 nStartCol = MAXCOL;
4944 SCCOL nMaxCol = MAXCOL;
4945 if ( nMyRow == nRow )
4946 { // formula cell in same row
4947 if ( nMyCol == nStartCol )
4948 { // take remainder right from name cell
4949 nStartCol++;
4950 if ( nStartCol > MAXCOL )
4951 nStartCol = MAXCOL;
4953 else if ( nMyCol > nStartCol )
4954 { // from name cell right to formula cell
4955 nMaxCol = nMyCol - 1;
4958 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
4959 { // next defined RowNameRange to the right limits column
4960 ScRangePair* pR = (*pRL)[i];
4961 const ScRange& rRange = pR->GetRange(1);
4962 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
4963 { // identical row range
4964 SCCOL nTmp = rRange.aStart.Col();
4965 if ( nStartCol < nTmp && nTmp <= nMaxCol )
4966 nMaxCol = nTmp - 1;
4969 aRange.aStart.Set( nStartCol, nRow, nTab );
4970 aRange.aEnd.Set( nMaxCol, nRow, nTab );
4974 if ( bValidName )
4976 // And now the magic to distinguish between a range and a single
4977 // cell thereof, which is picked position-dependent of the formula
4978 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
4979 // SingleRef matching the column/row of the formula cell is
4980 // generated. A ocColRowName or ocIntersect as a neighbor results
4981 // in a range. Special case: if label is valid for a single cell, a
4982 // position independent SingleRef is generated.
4983 bool bSingle = (aRange.aStart == aRange.aEnd);
4984 bool bFound;
4985 if ( bSingle )
4986 bFound = true;
4987 else
4989 FormulaToken* p1 = pArr->PeekPrevNoSpaces();
4990 FormulaToken* p2 = pArr->PeekNextNoSpaces();
4991 // begin/end of a formula => single
4992 OpCode eOp1 = p1 ? p1->GetOpCode() : static_cast<OpCode>( ocAdd );
4993 OpCode eOp2 = p2 ? p2->GetOpCode() : static_cast<OpCode>( ocAdd );
4994 if ( eOp1 != ocColRowName && eOp1 != ocIntersect
4995 && eOp2 != ocColRowName && eOp2 != ocIntersect )
4997 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
4998 (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP))
4999 bSingle = true;
5001 if ( bSingle )
5002 { // column and/or row must match range
5003 if ( bColName )
5005 bFound = (aRange.aStart.Row() <= nMyRow
5006 && nMyRow <= aRange.aEnd.Row());
5007 if ( bFound )
5008 aRange.aStart.SetRow( nMyRow );
5010 else
5012 bFound = (aRange.aStart.Col() <= nMyCol
5013 && nMyCol <= aRange.aEnd.Col());
5014 if ( bFound )
5015 aRange.aStart.SetCol( nMyCol );
5018 else
5019 bFound = true;
5021 if ( !bFound )
5022 SetError(errNoRef);
5023 else if (mbJumpCommandReorder)
5025 ScTokenArray* pNew = new ScTokenArray();
5026 if ( bSingle )
5028 ScSingleRefData aRefData;
5029 aRefData.InitAddress( aRange.aStart );
5030 if ( bColName )
5031 aRefData.SetColRel( true );
5032 else
5033 aRefData.SetRowRel( true );
5034 aRefData.SetAddress(aRange.aStart, aPos);
5035 pNew->AddSingleReference( aRefData );
5037 else
5039 ScComplexRefData aRefData;
5040 aRefData.InitRange( aRange );
5041 if ( bColName )
5043 aRefData.Ref1.SetColRel( true );
5044 aRefData.Ref2.SetColRel( true );
5046 else
5048 aRefData.Ref1.SetRowRel( true );
5049 aRefData.Ref2.SetRowRel( true );
5051 aRefData.SetRange(aRange, aPos);
5052 if ( bInList )
5053 pNew->AddDoubleReference( aRefData );
5054 else
5055 { // automagically
5056 pNew->Add( new ScDoubleRefToken( aRefData, ocColRowNameAuto ) );
5059 PushTokenArray( pNew, true );
5060 pNew->Reset();
5061 return GetToken();
5064 else
5065 SetError(errNoName);
5066 return true;
5069 bool ScCompiler::HandleDbData()
5071 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex());
5072 if ( !pDBData )
5073 SetError(errNoName);
5074 else if (mbJumpCommandReorder)
5076 ScComplexRefData aRefData;
5077 aRefData.InitFlags();
5078 ScRange aRange;
5079 pDBData->GetArea(aRange);
5080 aRange.aEnd.SetTab(aRange.aStart.Tab());
5081 aRefData.SetRange(aRange, aPos);
5082 ScTokenArray* pNew = new ScTokenArray();
5083 pNew->AddDoubleReference( aRefData );
5084 PushTokenArray( pNew, true );
5085 pNew->Reset();
5086 return GetToken();
5088 return true;
5091 bool ScCompiler::GetTokenIfOpCode( OpCode eOp )
5093 const formula::FormulaToken* p = pArr->PeekNextNoSpaces();
5094 if (p && p->GetOpCode() == eOp)
5095 return GetToken();
5096 return false;
5100 /* Documentation on MS-Excel Table structured references:
5101 * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
5102 * * as of Excel 2013
5103 * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
5104 * * look for structure-reference
5107 bool ScCompiler::HandleTableRef()
5109 ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(mpToken.get());
5110 if (!pTR)
5112 SetError(errUnknownToken);
5113 return true;
5116 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( pTR->GetIndex());
5117 if ( !pDBData )
5118 SetError(errNoName);
5119 else if (mbJumpCommandReorder)
5121 ScRange aDBRange;
5122 pDBData->GetArea(aDBRange);
5123 aDBRange.aEnd.SetTab(aDBRange.aStart.Tab());
5124 ScRange aRange( aDBRange);
5125 sal_uInt16 nError = 0;
5126 bool bForwardToClose = false;
5127 ScTableRefToken::Item eItem = pTR->GetItem();
5128 switch (eItem)
5130 case ScTableRefToken::TABLE:
5132 // The table name without items references the table data,
5133 // without headers or totals.
5134 if (pDBData->HasHeader())
5135 aRange.aStart.IncRow();
5136 if (pDBData->HasTotals())
5137 aRange.aEnd.IncRow(-1);
5138 if (aRange.aEnd.Row() < aRange.aStart.Row())
5139 nError = errNoRef;
5140 bForwardToClose = true;
5142 break;
5143 case ScTableRefToken::ALL:
5145 bForwardToClose = true;
5147 break;
5148 case ScTableRefToken::HEADERS:
5150 if (pDBData->HasHeader())
5151 aRange.aEnd.SetRow( aRange.aStart.Row());
5152 else
5153 nError = errNoRef;
5154 bForwardToClose = true;
5156 break;
5157 case ScTableRefToken::DATA:
5159 if (pDBData->HasHeader())
5160 aRange.aStart.IncRow();
5162 // fallthru
5163 case ScTableRefToken::HEADERS_DATA:
5165 if (pDBData->HasTotals())
5166 aRange.aEnd.IncRow(-1);
5167 if (aRange.aEnd.Row() < aRange.aStart.Row())
5168 nError = errNoRef;
5169 bForwardToClose = true;
5171 break;
5172 case ScTableRefToken::TOTALS:
5174 if (pDBData->HasTotals())
5175 aRange.aStart.SetRow( aRange.aEnd.Row());
5176 else
5177 nError = errNoRef;
5178 bForwardToClose = true;
5180 break;
5181 case ScTableRefToken::DATA_TOTALS:
5183 if (pDBData->HasHeader())
5184 aRange.aStart.IncRow();
5185 if (aRange.aEnd.Row() < aRange.aStart.Row())
5186 nError = errNoRef;
5187 bForwardToClose = true;
5189 break;
5190 case ScTableRefToken::THIS_ROW:
5192 if (aRange.aStart.Row() <= aPos.Row() && aPos.Row() <= aRange.aEnd.Row())
5194 aRange.aStart.SetRow( aPos.Row());
5195 aRange.aEnd.SetRow( aPos.Row());
5197 else
5199 nError = errNoValue;
5200 // For *some* relative row reference in named
5201 // expressions' thisrow special handling below.
5202 aRange.aEnd.SetRow( aRange.aStart.Row());
5204 bForwardToClose = true;
5206 break;
5208 bool bColumnRange = false;
5209 bool bCol1Rel = false;
5210 bool bCol2Rel = false;
5211 bool bCol1RelName = false;
5212 bool bCol2RelName = false;
5213 int nLevel = 0;
5214 if (bForwardToClose && GetTokenIfOpCode( ocTableRefOpen))
5216 ++nLevel;
5217 enum
5219 sOpen,
5220 sItem,
5221 sClose,
5222 sSep,
5223 sLast,
5224 sStop
5225 } eState = sOpen;
5228 const formula::FormulaToken* p = pArr->PeekNextNoSpaces();
5229 if (!p)
5230 eState = sStop;
5231 else
5233 switch (p->GetOpCode())
5235 case ocTableRefOpen:
5236 eState = ((eState == sOpen || eState == sSep) ? sOpen : sStop);
5237 if (++nLevel > 2)
5239 SetError( errPair);
5240 eState = sStop;
5242 break;
5243 case ocTableRefItemAll:
5244 case ocTableRefItemHeaders:
5245 case ocTableRefItemData:
5246 case ocTableRefItemTotals:
5247 case ocTableRefItemThisRow:
5248 eState = ((eState == sOpen) ? sItem : sStop);
5249 break;
5250 case ocTableRefClose:
5251 eState = ((eState == sItem || eState == sClose) ? sClose : sStop);
5252 if (eState != sStop && --nLevel == 0)
5253 eState = sLast;
5254 break;
5255 case ocSep:
5256 eState = ((eState == sClose) ? sSep : sStop);
5257 break;
5258 case ocPush:
5259 if (eState == sOpen && p->GetType() == svSingleRef)
5261 bColumnRange = true;
5262 bCol1Rel = p->GetSingleRef()->IsColRel();
5263 bCol1RelName = p->GetSingleRef()->IsRelName();
5264 eState = sLast;
5266 else
5268 eState = sStop;
5270 break;
5271 case ocBad:
5272 eState = sLast;
5273 if (!nError)
5274 nError = errNoName;
5275 break;
5276 default:
5277 eState = sStop;
5279 if (eState != sStop)
5280 GetToken();
5281 if (eState == sLast)
5282 eState = sStop;
5284 } while (eState != sStop);
5286 ScTokenArray* pNew = new ScTokenArray();
5287 if (!nError || nError == errNoValue)
5289 // The errNoValue case generates a thisrow reference that can be
5290 // used to save named expressions in A1 syntax notation.
5291 if (bColumnRange)
5293 // Limit range to specified columns.
5294 ScRange aColRange( ScAddress::INITIALIZE_INVALID );
5295 switch (mpToken->GetType())
5297 case svSingleRef:
5299 aColRange.aStart = aColRange.aEnd = mpToken->GetSingleRef()->toAbs( aPos);
5300 if ( GetTokenIfOpCode( ocTableRefClose) && (nLevel--) &&
5301 GetTokenIfOpCode( ocRange) &&
5302 GetTokenIfOpCode( ocTableRefOpen) && (++nLevel) &&
5303 GetTokenIfOpCode( ocPush))
5305 if (mpToken->GetType() != svSingleRef)
5306 aColRange = ScRange( ScAddress::INITIALIZE_INVALID);
5307 else
5309 aColRange.aEnd = mpToken->GetSingleRef()->toAbs( aPos);
5310 aColRange.Justify();
5311 bCol2Rel = mpToken->GetSingleRef()->IsColRel();
5312 bCol2RelName = mpToken->GetSingleRef()->IsRelName();
5316 break;
5317 default:
5318 ; // nothing
5320 if (aColRange.aStart.Row() != aDBRange.aStart.Row() || aColRange.aEnd.Row() != aDBRange.aStart.Row())
5321 aRange = ScRange( ScAddress::INITIALIZE_INVALID);
5322 else
5324 aColRange.aEnd.SetRow( aRange.aEnd.Row());
5325 aRange = aRange.Intersection( aColRange);
5328 if (aRange.IsValid())
5330 if (aRange.aStart == aRange.aEnd)
5332 ScSingleRefData aRefData;
5333 aRefData.InitFlags();
5334 aRefData.SetColRel( bCol1Rel);
5335 if (eItem == ScTableRefToken::THIS_ROW)
5337 aRefData.SetRowRel( true);
5338 if (!bCol1RelName)
5339 bCol1RelName = pArr->IsFromRangeName();
5341 aRefData.SetRelName( bCol1RelName);
5342 aRefData.SetFlag3D( true);
5343 if (nError)
5345 aRefData.SetAddress( aRange.aStart, aRange.aStart);
5346 pTR->SetAreaRefRPN( new ScSingleRefToken( aRefData)); // set reference at TableRef
5347 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
5349 else
5351 aRefData.SetAddress( aRange.aStart, aPos);
5352 pTR->SetAreaRefRPN( pNew->AddSingleReference( aRefData));
5355 else
5357 ScComplexRefData aRefData;
5358 aRefData.InitFlags();
5359 aRefData.Ref1.SetColRel( bCol1Rel);
5360 aRefData.Ref2.SetColRel( bCol2Rel);
5361 bool bRelName = bCol1RelName || bCol2RelName;
5362 if (eItem == ScTableRefToken::THIS_ROW)
5364 aRefData.Ref1.SetRowRel( true);
5365 aRefData.Ref2.SetRowRel( true);
5366 if (!bRelName)
5367 bRelName = pArr->IsFromRangeName();
5369 aRefData.Ref1.SetRelName( bRelName);
5370 aRefData.Ref2.SetRelName( bRelName);
5371 aRefData.Ref1.SetFlag3D( true);
5372 if (nError)
5374 aRefData.SetRange( aRange, aRange.aStart);
5375 pTR->SetAreaRefRPN( new ScDoubleRefToken( aRefData)); // set reference at TableRef
5376 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
5378 else
5380 aRefData.SetRange( aRange, aPos);
5381 pTR->SetAreaRefRPN( pNew->AddDoubleReference( aRefData));
5385 else
5387 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( errNoRef)));
5390 else
5392 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( nError)));
5394 while (nLevel-- > 0)
5396 if (!GetTokenIfOpCode( ocTableRefClose))
5397 SetError( errPair);
5399 PushTokenArray( pNew, true );
5400 pNew->Reset();
5401 return GetToken();
5403 return true;
5406 bool ScCompiler::IsForceArrayParameter( const formula::FormulaToken* pToken, sal_uInt16 nParam ) const
5408 ScParameterClassification::Type eType = ScParameterClassification::GetParameterType( pToken, nParam);
5409 return
5410 eType == ScParameterClassification::ForceArray ||
5411 eType == ScParameterClassification::ReferenceOrForceArray;
5414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */