1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
50 #include "rangenam.hxx"
52 #include "document.hxx"
53 #include "callform.hxx"
54 #include "addincol.hxx"
55 #include "refupdat.hxx"
56 #include "scresid.hxx"
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
;
78 CharClass
* ScCompiler::pCharClassEnglish
= NULL
;
79 const ScCompiler::Convention
* ScCompiler::pConventions
[ ] = { NULL
, NULL
, NULL
, NULL
, NULL
, NULL
};
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
;
106 case FormulaGrammar::GRAM_PODF
:
107 nSymbolOffset
= offsetof( AddInMap
, pUpper
);
110 case FormulaGrammar::GRAM_ODFF
:
111 nSymbolOffset
= offsetof( AddInMap
, pODFF
);
113 case FormulaGrammar::GRAM_ENGLISH
:
114 nSymbolOffset
= offsetof( AddInMap
, pEnglish
);
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
);
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
);
152 if (pFuncData
->GetExcelName( LANGUAGE_ENGLISH_US
, aName
))
153 xMap
->putExternalSoftly( aName
, pFuncData
->GetOriginalName());
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
);
181 // 2. old add in functions
182 if (ScGlobal::GetFuncCollection()->findByName(aUpper
))
187 // 3. new (uno) add in functions
188 OUString aIntName
= ScGlobal::GetAddInCollection()->FindFunction(aUpper
, false);
189 if (!aIntName
.isEmpty())
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
);
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");
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
));
255 void ScCompiler::SetNumberFormatter( SvNumberFormatter
* pFormatter
)
257 mpFormatter
= pFormatter
;
260 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr
& xMap
)
265 if (mxSymbols
->isEnglish())
267 if (!pCharClassEnglish
)
268 InitCharClassEnglish();
269 pCharClass
= pCharClassEnglish
;
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
)
285 SetRefConvention( pDoc
->GetAddressConvention());
287 SetRefConvention( GetRefConvention( FormulaGrammar::CONV_OOO
) );
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
;
304 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv
)
309 sal_uLong
*t
= new sal_uLong
[128];
311 ScCompiler::pConventions
[ meConv
] = this;
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
;
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
;
365 /* ] */ t
[93] = SC_COMPILER_C_ODF_RBRACKET
;
367 else if (FormulaGrammar::CONV_OOO
== meConv
)
369 /* [ */ t
[91] = SC_COMPILER_C_CHAR
;
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
;
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
;
383 /* ] */ t
[93] = SC_COMPILER_C_CHAR
;
385 else if( FormulaGrammar::CONV_XL_R1C1
== meConv
)
387 /* [ */ t
[91] = SC_COMPILER_C_IDENT
;
389 /* ] */ t
[93] = SC_COMPILER_C_IDENT
;
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
;
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
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
;
431 /* , */ t
[44] |= SC_COMPILER_C_WORD
;
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
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;
478 static bool lcl_parseExternalName(
479 const OUString
& rSymbol
,
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
;
493 bool bInName
= false;
496 // For XL use existing parser that resolves bracketed and quoted and
497 // indexed external document names.
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
)
505 i
= sal_Int32(p
- pStart
);
507 for ( ; i
< nLen
; ++i
, ++p
)
512 if (c
== '.' || c
== cSep
)
517 // Move to the next char and loop until the second single
519 sal_Unicode cPrev
= c
;
521 for (sal_Int32 j
= i
; j
< nLen
; ++j
, ++p
)
528 // empty quote e.g. (=''!Name)
534 // two consecutive quotes equal a single quote in
536 aTmpFile
+= OUString(c
);
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
554 aTmpName
+= OUString(c
); // Keep the separator as part of the name.
557 aTmpFile
+= OUString(c
);
563 // premature ending of the quoted segment.
569 // only the separator is allowed after the closing quote.
581 // A second separator ? Not a valid external name.
584 aTmpName
+= OUString(c
);
591 aTmpName
+= OUString(c
); // Keep the separator as part of the name.
597 if (rtl::isAsciiAlphanumeric(c
))
602 // non-ASCII character is allowed.
611 // these special characters are allowed.
621 aTmpFile
+= OUString(c
);
628 // No name found - most likely the symbol has no '!'s.
632 sal_Int32 nNameLen
= aTmpName
.getLength();
635 // Name must be at least 2-char long (separator plus name).
639 if (aTmpName
[0] != cSep
)
641 // 1st char of the name must equal the separator.
645 if (aTmpName
[nNameLen
-1] == '!')
647 // Check against #REF!.
648 if (aTmpName
== "#REF!")
653 rName
= aTmpName
.copy(1); // Skip the first char as it is always the separator.
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
);
664 aName
= aName
.replaceAll("'", aEscQuote
);
665 OUStringBuffer
aBuf(aFile
.getLength() + aName
.getLength() + 9);
668 aBuf
.append( "'" + aFile
+ "'" + OUString(cSep
));
670 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();
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
);
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
);
699 rTabName2
= rTabNames
[nDist
+nTabSpan
];
702 rTabName2
= rTabName1
;
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
,
715 const CharClass
* pCharClass
) const SAL_OVERRIDE
718 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, 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
));
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
));
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
));
762 rBuf
.append(rTabNames
[nTab
]);
766 enum SingletonDisplay
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. */
783 eSingletonDisplay
= SINGLETON_NONE
;
785 if( rRef
.IsFlag3D() || bForceTab
)
787 if (!ValidTab(rAbsRef
.Tab()) || rRef
.IsTabDeleted())
789 if (!rRef
.IsTabRel())
791 rBuffer
.append(rErrRef
);
796 if (!rRef
.IsTabRel())
798 MakeTabStr(rBuffer
, rTabNames
, rAbsRef
.Tab());
804 if (eSingletonDisplay
!= SINGLETON_ROW
)
806 if (!rRef
.IsColRel())
808 if (!ValidCol(rAbsRef
.Col()) || rRef
.IsColDeleted())
809 rBuffer
.append(rErrRef
);
811 MakeColStr(rBuffer
, rAbsRef
.Col());
814 if (eSingletonDisplay
!= SINGLETON_COL
)
816 if (!rRef
.IsRowRel())
818 if (!ValidRow(rAbsRef
.Row()) || rRef
.IsRowDeleted())
819 rBuffer
.append(rErrRef
);
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
,
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
;
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
);
863 MakeOneRefStrImpl(rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), false,
868 virtual sal_Unicode
getSpecialSymbol( SpecialSymbolType eSymType
) const SAL_OVERRIDE
872 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
874 case ScCompiler::Convention::SHEET_SEPARATOR
:
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
);
905 aFile
= INetURLObject::decode(rFileName
, INetURLObject::DECODE_UNAMBIGUOUS
);
907 rBuffer
.append("'" + aFile
.replaceAll("'", "''") + "'#");
909 if (!rRef
.IsTabRel())
911 ScRangeStringConverter::AppendTableName(rBuffer
, rTabName
);
916 if (!rRef
.IsColRel())
918 MakeColStr( rBuffer
, aAbsRef
.Col());
919 if (!rRef
.IsRowRel())
921 MakeRowStr( rBuffer
, aAbsRef
.Row());
926 static void makeExternalRefStrImpl(
927 OUStringBuffer
& rBuffer
, const ScAddress
& rPos
, const OUString
& rFileName
,
928 const OUString
& rTabName
, const ScSingleRefData
& rRef
, bool bODF
)
931 rBuffer
.append( '[');
933 bool bEncodeUrl
= bODF
;
934 makeExternalSingleRefStr(rBuffer
, rFileName
, rTabName
, rRef
, rPos
, true, bEncodeUrl
);
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
);
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
))
965 OUString aLastTabName
;
966 bool bDisplayTabName
= (aAbsRange
.aStart
.Tab() != aAbsRange
.aEnd
.Tab());
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.
977 rBuffer
.append( '.'); // need at least the sheet separator in ODF
978 makeExternalSingleRefStr(
979 rBuffer
, rFileName
, aLastTabName
, rRef
.Ref2
, rPos
, bDisplayTabName
, bEncodeUrl
);
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
,
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
;
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
1023 SingletonDisplay eSingleton
= bSingleRef
? SINGLETON_NONE
: getSingletonDisplay( aAbs1
, aAbs2
, rRef
);
1024 MakeOneRefStrImpl(rBuffer
, rErrRef
, rTabNames
, rRef
.Ref1
, aAbs1
, false, true, eSingleton
);
1027 rBuffer
.append(':');
1028 MakeOneRefStrImpl(rBuffer
, rErrRef
, rTabNames
, rRef
.Ref2
, aAbs2
, aAbs1
.Tab() != aAbs2
.Tab(), true,
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);
1059 virtual ~ConventionXL()
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
);
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
,
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
)
1097 rBuf
.append( aEndTabName
);
1104 static sal_Unicode
getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType
)
1108 case ScCompiler::Convention::ABS_SHEET_PREFIX
:
1109 return sal_Unicode(0);
1110 case ScCompiler::Convention::SHEET_SEPARATOR
:
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('\'');
1141 aFullName
= rFullName
;
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
];
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
);
1169 ScRangeStringConverter::AppendTableName(rBuf
, rTabName
);
1170 if (rTabName
!= aLastTabName
)
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
];
1187 // first character must be '['.
1191 else if (i
== rSrcPos
+ 1)
1193 // second character must be a single quote.
1200 // two successive single quote is treated as a single
1208 // valid source document path found. Increment the
1209 // current position to skip the source path.
1211 if (rSrcPos
>= nLen
)
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.
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())
1240 MakeColStr(rBuf
, rAbs
.Col());
1241 if (!rRef
.IsRowRel())
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
,
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
));
1270 aAbs2
= aRef
.Ref2
.toAbs(rPos
);
1271 if (!ValidAddress(aAbs2
))
1273 rBuf
.append(ScGlobal::GetRscString(STR_NO_REF_TABLE
));
1277 if (aAbs1
.Col() == 0 && aAbs2
.Col() >= MAXCOL
)
1279 if (!aRef
.Ref1
.IsRowRel())
1281 MakeRowStr(rBuf
, aAbs1
.Row());
1283 if (!aRef
.Ref2
.IsRowRel())
1285 MakeRowStr(rBuf
, aAbs2
.Row());
1289 if (aAbs1
.Row() == 0 && aAbs2
.Row() >= MAXROW
)
1291 if (!aRef
.Ref1
.IsColRel())
1293 MakeColStr(rBuf
, aAbs1
.Col());
1295 if (!aRef
.Ref2
.IsColRel())
1297 MakeColStr(rBuf
, aAbs2
.Col());
1302 makeSingleCellStr(rBuf
, aRef
.Ref1
, aAbs1
);
1306 makeSingleCellStr(rBuf
, aRef
.Ref2
, aAbs2
);
1310 virtual ParseResult
parseAnyToken( const OUString
& rFormula
,
1312 const CharClass
* pCharClass
) const SAL_OVERRIDE
1314 parseExternalDocName(rFormula
, nSrcPos
);
1317 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, 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
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
,
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
);
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? */
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
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
];
1435 // first character must be '['.
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(']');
1488 r1c1_add_col( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1491 if( rRef
.IsColRel() )
1493 SCCOL nCol
= rRef
.Col();
1495 rBuf
.append("[").append(OUString::number(nCol
)).append("]");
1498 rBuf
.append( OUString::number( rAbsRef
.Col() + 1 ) );
1501 r1c1_add_row( OUStringBuffer
&rBuf
, const ScSingleRefData
& rRef
, const ScAddress
& rAbsRef
)
1504 if( rRef
.IsRowRel() )
1506 if (rRef
.Row() != 0)
1508 rBuf
.append("[").append( OUString::number(rRef
.Row()) ).append("]");
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
,
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
));
1542 if (!ValidCol(aAbsRef
.aEnd
.Col()) || !ValidRow(aAbsRef
.aEnd
.Row()))
1544 rBuf
.append(ScGlobal::GetRscString(STR_NO_REF_TABLE
));
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() )
1555 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
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())
1568 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1574 r1c1_add_row(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1575 r1c1_add_col(rBuf
, rRef
.Ref1
, aAbsRef
.aStart
);
1579 r1c1_add_row(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1580 r1c1_add_col(rBuf
, rRef
.Ref2
, aAbsRef
.aEnd
);
1584 ParseResult
parseAnyToken( const OUString
& rFormula
,
1586 const CharClass
* pCharClass
) const SAL_OVERRIDE
1588 parseExternalDocName(rFormula
, nSrcPos
);
1591 if ( lcl_isValidQuotedText(rFormula
, nSrcPos
, 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
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
));
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
);
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
);
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
;
1697 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
, ScTokenArray
& rArr
) :
1698 FormulaCompiler(rArr
),
1699 pDoc(rCxt
.getDoc()),
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),
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
),
1719 mpFormatter(pDoc
->GetFormatTable()),
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 ),
1729 nMaxTab
= pDoc
->GetTableCount() - 1;
1732 ScCompiler::ScCompiler( sc::CompileFormulaContext
& rCxt
, const ScAddress
& rPos
) :
1733 pDoc(rCxt
.getDoc()),
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),
1743 maTabNames(rCxt
.getTabNames())
1745 nMaxTab
= pDoc
? pDoc
->GetTableCount() - 1 : 0;
1746 SetGrammar(rCxt
.getGrammar());
1749 ScCompiler::ScCompiler( ScDocument
* pDocument
, const ScAddress
& rPos
)
1753 mpFormatter(pDoc
? pDoc
->GetFormatTable() : NULL
),
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 ),
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());
1783 case FormulaGrammar::CONV_UNSPECIFIED
:
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
:
1792 const OUString
one_quote('\'');
1793 const OUString
two_quote("''");
1794 // escape embedded quotes
1795 rString
= rString
.replaceAll( one_quote
, two_quote
);
1800 if ( !bNeedsQuote
&& CharClass::isAsciiNumeric( rString
) )
1802 // Prevent any possible confusion resulting from pure numeric sheet names.
1808 rString
= "'" + rString
+ "'";
1812 sal_Int32
ScCompiler::GetDocTabPos( const OUString
& rString
)
1814 if (rString
[0] != '\'')
1816 sal_Int32 nPos
= ScGlobal::FindUnquoted( rString
, SC_COMPILER_FILE_TAB_SEP
);
1817 // it must be 'Doc'#
1818 if (nPos
!= -1 && rString
[nPos
-1] != '\'')
1823 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv
)
1825 const Convention
* p
= GetRefConvention(eConv
);
1827 SetRefConvention(p
);
1830 const ScCompiler::Convention
* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv
)
1835 case FormulaGrammar::CONV_OOO
:
1837 static const ConventionOOO_A1 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
;
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
;
1860 case FormulaGrammar::CONV_UNSPECIFIED
:
1868 void ScCompiler::SetRefConvention( const ScCompiler::Convention
*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
)
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
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
;
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
);
1952 bool bAutoIntersection
= false;
1954 bool bErrorConstantHadSlash
= false;
1955 mnPredetectedReference
= 0;
1956 // try to parse simple tokens before calling i18n parser
1957 while ((c
!= 0) && (eState
!= ssStop
) )
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
)))
1968 // these are to be continued
1971 case ssGetReference
:
1972 case ssSkipReference
:
1975 if (eState
== ssGetChar
)
1982 Label_MaskStateMachine
:
1987 // Order is important!
1988 if (eLastOp
== ocTableRefOpen
&& c
!= '[' && c
!= '#' && 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
2006 if (!bAutoIntersection
)
2009 nSpaces
+= 2; // must match the character count
2010 bAutoIntersection
= true;
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
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
;
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
)
2066 else if( nMask
& SC_COMPILER_C_CHAR_VALUE
)
2069 eState
= ssGetValue
;
2071 else if( nMask
& SC_COMPILER_C_CHAR_STRING
)
2074 eState
= ssGetString
;
2076 else if( nMask
& SC_COMPILER_C_CHAR_ERRCONST
)
2079 if (!maTableRefs
.empty() && maTableRefs
.back().mnLevel
== 2)
2080 eState
= ssGetTableRefItem
;
2082 eState
= ssGetErrorConstant
;
2084 else if( nMask
& SC_COMPILER_C_CHAR_DONTCARE
)
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
2092 eState
= 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
);
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
);
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).
2142 if( nMask
& SC_COMPILER_C_BOOL
)
2156 if( pSym
== &cSymbol
[ MAXSTRLEN
-1 ] )
2158 SetError(errStringOverflow
);
2161 else if (c
== cDecSep
)
2165 // reparse with i18n, may be numeric sheet name as well
2172 else if( nMask
& SC_COMPILER_C_VALUE
)
2174 else if( nMask
& SC_COMPILER_C_VALUE_SEP
)
2179 else if (c
== 'E' || c
== 'e')
2181 if (GetCharTableFlags( pSrc
[0], 0 ) & SC_COMPILER_C_VALUE_EXP
)
2185 // reparse with i18n
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
))
2205 // reparse with i18n
2213 if( nMask
& SC_COMPILER_C_STRING_SEP
)
2218 bQuote
= true; // "" => literal "
2227 if( pSym
== &cSymbol
[ MAXSTRLEN
-1 ] )
2229 SetError(errStringOverflow
);
2230 eState
= ssSkipString
;
2238 if( nMask
& SC_COMPILER_C_STRING_SEP
)
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. */
2253 if ('!' == c
|| '?' == c
)
2257 if (!bErrorConstantHadSlash
)
2258 bErrorConstantHadSlash
= true;
2265 else if ((nMask
& SC_COMPILER_C_WORD_SEP
) ||
2266 (c
< 128 && !rtl::isAsciiAlphanumeric( c
)))
2275 if (pSym
== &cSymbol
[ MAXSTRLEN
-1 ])
2277 SetError( errStringOverflow
);
2285 case ssGetTableRefItem
:
2287 // Scan whatever up to the next ']' closer.
2290 if( pSym
== &cSymbol
[ MAXSTRLEN
-1 ] )
2292 SetError( errStringOverflow
);
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
);
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
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
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
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;
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
))
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;
2408 nRefInName
&= kPast
;
2409 nRefInName
|= kDefName
;
2413 // ScAddress::Parse() will recognize this as
2415 if (eState
!= ssSkipReference
)
2420 bAddToSymbol
= false;
2423 else if (cSheetPrefix
== c
&& nRefInName
== 0)
2424 nRefInName
|= kDollar
;
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
;
2450 // escapes embedded quote
2451 nRefInName
|= kQuote
;
2453 case SC_COMPILER_FILE_TAB_SEP
:
2454 // sheet name should follow
2455 nRefInName
|= kFileSep
;
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");
2479 ++mnPredetectedReference
;
2481 else if (!(nRefInName
& kName
))
2483 // start unquoted name
2484 nRefInName
|= kName
;
2491 ++mnPredetectedReference
;
2493 if (bAddToSymbol
&& eState
!= ssSkipReference
)
2494 *pSym
++ = c
; // everything is part of reference
2498 ; // nothing, prevent warning
2506 nSrcPos
= nSrcPos
+ nSpaces
;
2507 OUStringBuffer aSymbol
;
2508 mnRangeOpPosInSymbol
= -1;
2509 sal_uInt16 nErr
= 0;
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
)
2523 SetError( nErr
= errIllegalChar
);
2524 nSrcPos
= aFormula
.getLength();
2525 aSymbol
.truncate(0);
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();
2555 aSymbol
.append(pStart
[nSrcPos
++]);
2557 } while ( bi18n
&& !nErr
);
2558 sal_Int32 nLen
= aSymbol
.getLength();
2559 if ( nLen
>= MAXSTRLEN
)
2561 SetError( errStringOverflow
);
2564 lcl_UnicodeStrNCpy( cSymbol
, aSymbol
.getStr(), nLen
);
2565 pSym
= &cSymbol
[nLen
];
2569 nSrcPos
= pSrc
- pStart
;
2572 if (mnRangeOpPosInSymbol
>= 0 && mnRangeOpPosInSymbol
== (pSym
-1) - &cSymbol
[0])
2574 // This is a trailing range operator, which is nonsense. Will be caught
2576 mnRangeOpPosInSymbol
= -1;
2581 aCorrectedSymbol
= cSymbol
;
2582 if (bAutoIntersection
&& nSpaces
> 1)
2583 --nSpaces
; // replace '!!' with only one space
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());
2595 OpCode eOp
= iLook
->second
;
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.
2609 else if (eOp
== ocFloor
&& mxSymbols
->isOOXML())
2611 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2612 // unassigned for import.
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.
2626 const sal_Char
* pName
;
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
);
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.
2675 if (aIntName
.isEmpty())
2677 // Old (deprecated) addins first for legacy.
2678 if (ScGlobal::GetFuncCollection()->findByName(cSymbol
))
2680 maRawToken
.SetExternal( cSymbol
);
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
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
);
2711 bool ScCompiler::IsOpCode2( const OUString
& rName
)
2713 bool bFound
= false;
2716 for( i
= ocInternalBegin
; i
<= ocInternalEnd
&& !bFound
; i
++ )
2717 bFound
= rName
.equalsAscii( pInternal
[ i
-ocInternalBegin
] );
2721 maRawToken
.SetOpCode( (OpCode
) --i
);
2726 bool ScCompiler::IsValue( const OUString
& rSym
)
2729 sal_uInt32 nIndex
= mxSymbols
->isEnglish() ? mpFormatter
->GetStandardIndex(LANGUAGE_ENGLISH_US
) : 0;
2731 if (!mpFormatter
->IsNumberFormat(rSym
, nIndex
, fVal
))
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
))
2744 if (nType
== css::util::NumberFormat::LOGICAL
)
2746 const sal_Unicode
* p
= aFormula
.getStr() + nSrcPos
;
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
);
2760 bool ScCompiler::IsString()
2762 const sal_Unicode
* p
= cSymbol
;
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
);
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());
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!");
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. */
2798 // Per ODFF the correct string for a reference error is just #REF!,
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!
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!
2815 if ('$' == c2
|| '#' == c2
|| ('0' <= c2
&& c2
<= '9'))
2816 return false; // sheet.#REF!42 or sheet.#REF!#REF!
2819 if (mnPredetectedReference
> 1 &&
2820 ('.' == c2
|| '$' == c2
|| '#' == c2
||
2821 ('0' <= c2
&& c2
<= '9')))
2822 return false; // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
2825 if (comphelper::string::isalphaAscii(c
) &&
2826 ((mnPredetectedReference
> 1 && ':' == c2
) || 0 == c2
))
2827 return false; // AB#REF!: or AB#REF!
2830 switch (mnPredetectedReference
)
2833 return IsSingleReference( rName
);
2835 return IsDoubleReference( rName
);
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
);
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
);
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
)
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
, '.');
2944 if (ScGlobal::FindUnquoted( rName
, ':') != -1)
2945 break; // may be 3:3, continue as usual
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
)) )
2952 if ( cDecSep
== '.' && (ch2
== 'E' || ch2
== 'e') // E + - digit
2953 && (GetCharTableFlags( pTabSep
[2], pTabSep
[1] ) & SC_COMPILER_C_VALUE_EXP
) )
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
) );
2967 if ( !pDoc
->GetTable( aTabName
, nTab
) )
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.
2975 if (IsSingleReference( rName
))
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
))
2985 // Now try with a symbol up to the range operator, rewind source
2987 sal_Int32 nLen
= mnRangeOpPosInSymbol
;
2988 while (cSymbol
[++nLen
])
2990 cSymbol
[mnRangeOpPosInSymbol
] = 0;
2991 nSrcPos
-= (nLen
- mnRangeOpPosInSymbol
);
2992 mnRangeOpPosInSymbol
= -1;
2994 return true; // end all checks
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
))
3016 bool ScCompiler::IsMacro( const OUString
& rName
)
3018 #if !HAVE_FEATURE_SCRIPTING
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();
3044 pObj
= pDocSh
->GetBasic();
3046 pObj
= SfxApplication::GetBasic();
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
));
3065 // It really should be a BASIC function!
3066 if( pMeth
->GetType() == SbxVOID
3067 || ( pMeth
->IsFixed() && pMeth
->GetType() == SbxEMPTY
)
3068 || !pMeth
->ISA(SbMethod
) )
3072 maRawToken
.SetExternal( aName
.getStr() );
3073 maRawToken
.eOp
= ocMacro
;
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
;
3087 pData
= pRangeName
->findByUpperName(rUpperName
);
3090 pRangeName
= pDoc
->GetRangeName();
3092 pData
= pRangeName
->findByUpperName(rUpperName
);
3099 maRawToken
.SetName(bGlobal
, pData
->GetIndex());
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;
3119 OUString aFile
, aName
;
3120 if (!pConv
->parseExternalName( rSymbol
, aFile
, aName
, pDoc
, &maExternalLinks
))
3123 if (aFile
.getLength() > MAXSTRLEN
|| aName
.getLength() > MAXSTRLEN
)
3126 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
3127 OUString aTmp
= aFile
;
3128 pRefMgr
->convertToAbsName(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.
3138 const OUString
* pRealName
= pRefMgr
->getRealRangeName(nFileId
, aName
);
3139 maRawToken
.SetExternalName(nFileId
, pRealName
? *pRealName
: OUString(aTmp
));
3140 maExternalFiles
.push_back(nFileId
);
3144 bool ScCompiler::IsDBRange( const OUString
& rName
)
3146 ScDBCollection::NamedDBs
& rDBs
= pDoc
->GetDBCollection()->getNamedDBs();
3147 const ScDBData
* p
= rDBs
.findByUpperName(rName
);
3151 maRawToken
.SetName(true, p
->GetIndex()); // DB range is always global.
3152 maRawToken
.eOp
= ocDBArea
;
3156 bool ScCompiler::IsColRowName( const OUString
& rName
)
3158 bool bInList
= false;
3159 bool bFound
= false;
3160 ScSingleRefData aRef
;
3161 OUString
aName( rName
);
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
;
3170 pRL
= pDoc
->GetColNameRanges();
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()) )
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
3187 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3188 CellType eType
= aIter
.getType();
3190 if (eType
== CELLTYPE_FORMULA
)
3192 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3193 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
3198 if (bOk
&& aIter
.hasString())
3200 OUString aStr
= aIter
.getString();
3201 if ( ScGlobal::GetpTransliteration()->isEqual( aStr
, aName
) )
3205 aRef
.SetColRel( true ); // ColName
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();
3222 ScAddress
aOne( 0, 0, aPos
.Tab() );
3223 ScAddress
aTwo( MAXCOL
, MAXROW
, aPos
.Tab() );
3225 ScAutoNameCache
* pNameCache
= pDoc
->GetAutoNameCache();
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
3242 { // stop if everything else is further away
3243 if ( nMax
< (long)aAddress
.Col() )
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
;
3256 long nD
= nC
* nC
+ nR
* nR
;
3257 if ( nD
< nDistance
)
3259 if ( nC
< 0 || nR
< 0 )
3262 aTwo
.Set( nCol
, nRow
, aAddress
.Tab() );
3263 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
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)
3272 aOne
.Set( nCol
, nRow
, aAddress
.Tab() );
3273 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
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
) );
3291 ScCellIterator
aIter( pDoc
, ScRange( aOne
, aTwo
) );
3292 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
3295 { // stop if everything else is further away
3296 if ( nMax
< (long)aIter
.GetPos().Col() )
3299 CellType eType
= aIter
.getType();
3301 if (eType
== CELLTYPE_FORMULA
)
3303 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3304 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
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
;
3320 long nD
= nC
* nC
+ nR
* nR
;
3321 if ( nD
< nDistance
)
3323 if ( nC
< 0 || nR
< 0 )
3326 aTwo
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3327 nMax
= std::max( nMyCol
+ std::abs( nC
), nMyRow
+ std::abs( nR
) );
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)
3336 aOne
.Set( nCol
, nRow
, aIter
.GetPos().Tab() );
3337 nMax
= std::max( nMyCol
+ nC
, nMyRow
+ nR
);
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
) );
3359 if ( nMyCol
>= (long)aOne
.Col() && nMyRow
>= (long)aOne
.Row() )
3360 aAdr
= aOne
; // upper left takes precedence
3363 if ( nMyCol
< (long)aOne
.Col() )
3364 { // two to the right
3365 if ( nMyRow
>= (long)aTwo
.Row() )
3366 aAdr
= aTwo
; // directly right
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
)
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
3392 aRef
.SetColRel( true ); // ColName
3393 aRef
.SetAddress(aAdr
, aPos
);
3398 maRawToken
.SetSingleReference( aRef
);
3399 maRawToken
.eOp
= ocColRowName
;
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
);
3420 bool ScCompiler::IsErrorConstant( const OUString
& rName
) const
3422 sal_uInt16 nError
= GetErrorConstant( rName
);
3425 maRawToken
.SetErrorConstant( nError
);
3432 bool ScCompiler::IsTableRefItem( const OUString
& rName
) const
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
:
3447 p
->AddItem( ScTableRefToken::ALL
);
3449 case ocTableRefItemHeaders
:
3451 p
->AddItem( ScTableRefToken::HEADERS
);
3453 case ocTableRefItemData
:
3455 p
->AddItem( ScTableRefToken::DATA
);
3457 case ocTableRefItemTotals
:
3459 p
->AddItem( ScTableRefToken::TOTALS
);
3461 case ocTableRefItemThisRow
:
3463 p
->AddItem( ScTableRefToken::THIS_ROW
);
3469 maRawToken
.SetOpCode( (*iLook
).second
);
3475 OUString
unescapeTableRefColumnSpecifier( const OUString
& rStr
)
3477 // '#', '[', ']' and '\'' are escaped with '\''
3479 if (rStr
.indexOf( '\'' ) < 0)
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
;
3496 bEscaped
= true; // unescaped escaping '\''
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());
3515 OUString
aName( unescapeTableRefColumnSpecifier( rName
));
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();
3530 if (eType
== CELLTYPE_FORMULA
)
3532 ScFormulaCell
* pFC
= aIter
.getFormulaCell();
3533 bOk
= (pFC
->GetCode()->GetCodeLen() > 0) && (pFC
->aPos
!= aPos
);
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
);
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
);
3565 if (pDBData
->HasHeader())
3567 ScSingleRefData aRef
;
3568 ScAddress
aAdr( aRange
.aStart
);
3569 aAdr
.IncCol( nOffset
);
3570 aRef
.InitAddress( aAdr
);
3571 maRawToken
.SetSingleReference( aRef
);
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. */
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();
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
)
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)) )
3618 if ( nPos
== MAXSTRLEN
- 2 )
3619 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
, 1, OUString(cQuote
) ); // '"' the 255th character
3621 aCorrectedSymbol
= aCorrectedSymbol
.replaceAt( nPos
+ 1, 0, OUString(cQuote
) );
3624 else if ( c1
!= cQuote
&& c2
== cQuote
)
3626 aCorrectedSymbol
= OUStringLiteral1
<cQuote
>() + aCorrectedSymbol
;
3629 else if ( nPos
== 0 && (c1
== cx
|| c1
== cX
) )
3631 aCorrectedSymbol
= mxSymbols
->getSymbol(ocMul
);
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 )
3639 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
3640 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUString(cx
), OUString(c
));
3643 if ( comphelper::string::getTokenCount(aCorrectedSymbol
, cX
) > 1 )
3645 sal_Unicode c
= mxSymbols
->getSymbolChar(ocMul
);
3646 aCorrectedSymbol
= aCorrectedSymbol
.replaceAll(OUString(cX
), OUString(c
));
3652 OUString
aSymbol( aCorrectedSymbol
);
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
, ':');
3664 { // duplicated or too many ':'? B:2::C10 => B2:C10
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
)
3683 bLastAlp
= CharClass::isAsciiAlpha( aTmp1
);
3687 bNextNum
= CharClass::isAsciiNumeric( aTmp2
);
3688 if ( bLastAlp
== bNextNum
&& nStrip
< 1 )
3690 // Must be alternating number/string, only
3691 // strip within a reference.
3697 if ( !aSym
.isEmpty() && !aSym
.endsWith(":"))
3701 bLastAlp
= !bNextNum
;
3707 { // B10::C10 ? append ':' on next round
3708 if ( !bLastAlp
&& !CharClass::isAsciiNumeric( aTmp1
) )
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
);
3729 aRef
[0] = aSymbol
.getToken( 0, ':' );
3730 aRef
[1] = aSymbol
.getToken( 1, ':' );
3735 bool bChanged
= false;
3737 sal_uInt16 nMask
= SCA_VALID
| SCA_VALID_COL
| SCA_VALID_ROW
;
3738 for ( int j
=0; j
<nRefs
; j
++ )
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
] );
3751 const sal_Unicode
* p
= aRef
[j
].getStr();
3752 while ( *p
&& CharClass::isAsciiNumeric( OUString(*p
) ) )
3753 aStr2
+= OUString(*p
++);
3754 aRef
[j
] = OUString( p
);
3756 if ( bColons
|| aRef
[j
] != aOld
)
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];
3770 aCorrectedSymbol
+= ":";
3771 aCorrectedSymbol
+= aTab
[1];
3772 aCorrectedSymbol
+= aRef
[1];
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
3787 rUpper
= rOrg
.toAsciiUpperCase();
3792 rUpper
= ScGlobal::pCharClass
->uppercase(rOrg
);
3797 bool ScCompiler::NextNewToken( bool bInArray
)
3799 bool bAllowBooleans
= bInArray
;
3800 sal_Int32 nSpaces
= NextSymbol(bInArray
);
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
);
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).
3830 svl::SharedString aSS
= pDoc
->GetSharedStringPool().intern(aStr
);
3831 maRawToken
.SetString(aSS
.getData(), aSS
.getDataIgnoreCase());
3832 maRawToken
.NewOpCode( ocBad
);
3837 if ( (cSymbol
[0] == '#' || cSymbol
[0] == '$') && cSymbol
[1] == 0 &&
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();
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] );
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
))
3879 if ( bMayBeFuncName
)
3881 // a function name must be followed by a parenthesis
3882 const sal_Unicode
* p
= aFormula
.getStr() + nSrcPos
;
3885 bMayBeFuncName
= ( *p
== '(' );
3888 // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before
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
))
3902 // Do not attempt to resolve as any other name.
3903 aUpper
= aOrg
; // for ocBad
3904 break; // do; create ocBad token or set error.
3909 bool bAsciiUpper
= false;
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
))
3923 // This can be only an error constant, if any.
3924 if (IsErrorConstant( aUpper
))
3927 break; // do; create ocBad token or set error.
3929 if (IsOpCode( aUpper
, bInArray
))
3935 if (aUpper
.isEmpty())
3936 bAsciiUpper
= lcl_UpperAsciiOrI18n( aUpper
, aOrg
, meGrammar
);
3937 if (IsOpCode( aUpper
, bInArray
))
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.
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
))
3967 if (IsValue( aUpper
))
3970 // User defined names and such do need i18n upper also in ODF.
3972 aUpper
= ScGlobal::pCharClass
->uppercase( aOrg
);
3974 if (IsNamedRange( aUpper
))
3976 // Preserve case of file names in external references.
3977 bool bInvalidExternalNameRange
;
3978 if (IsExternalNamedRange( aOrg
, bInvalidExternalNameRange
))
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
);
3990 if (IsDBRange( aUpper
))
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
))
3996 if (bMayBeFuncName
&& IsMacro( aUpper
))
3998 if (bMayBeFuncName
&& IsOpCode2( aUpper
))
4003 if ( meExtendedErrorDetection
!= EXTENDED_ERROR_DETECTION_NONE
)
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
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
);
4019 AutoCorrectParsedSymbol();
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();
4034 rFormulaNmsp
= ppTokens
[1]->GetString().getString();
4040 class ExternalFileInserter
: std::unary_function
<sal_uInt16
, void>
4043 ScExternalRefManager
& mrRefMgr
;
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
);
4064 aFormula
= comphelper::string::strip(rFormula
, ' ');
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
] == '=' )
4079 aCorrectedFormula
+= "=";
4081 if( nSrcPos
< aFormula
.getLength() && aFormula
[nSrcPos
] == '=' )
4086 aCorrectedFormula
+= "=";
4088 struct FunctionStack
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;
4107 while( NextNewToken( bInArray
) )
4109 const OpCode eOp
= maRawToken
.GetOpCode();
4118 if (bUseFunctionStack
)
4121 pFunctionStack
[ nFunction
].eOp
= eLastOp
;
4122 pFunctionStack
[ nFunction
].nSep
= 0;
4130 SetError( errPairExpected
);
4134 aCorrectedSymbol
.clear();
4139 if (bUseFunctionStack
&& nFunction
)
4145 if (bUseFunctionStack
)
4146 ++pFunctionStack
[ nFunction
].nSep
;
4152 SetError( errNestedArray
);
4155 // Don't count following column separator as parameter separator.
4156 if (bUseFunctionStack
)
4159 pFunctionStack
[ nFunction
].eOp
= eOp
;
4160 pFunctionStack
[ nFunction
].nSep
= 0;
4172 SetError( errPairExpected
);
4176 aCorrectedSymbol
.clear();
4179 if (bUseFunctionStack
&& nFunction
)
4183 case ocTableRefOpen
:
4185 // Don't count following item separator as parameter separator.
4186 if (bUseFunctionStack
)
4189 pFunctionStack
[ nFunction
].eOp
= eOp
;
4190 pFunctionStack
[ nFunction
].nSep
= 0;
4194 case ocTableRefClose
:
4196 if (bUseFunctionStack
&& nFunction
)
4203 if( (eLastOp
== ocSep
||
4204 eLastOp
== ocArrayRowSep
||
4205 eLastOp
== ocArrayColSep
||
4206 eLastOp
== ocArrayOpen
) &&
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;
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;
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.
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());
4256 SetError(errCodeOverflow
);
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
);
4278 case ocTableRefOpen
:
4279 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4280 if (maTableRefs
.empty())
4283 ++maTableRefs
.back().mnLevel
;
4285 case ocTableRefClose
:
4286 SAL_WARN_IF( maTableRefs
.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4287 if (maTableRefs
.empty())
4291 if (--maTableRefs
.back().mnLevel
== 0)
4292 maTableRefs
.pop_back();
4298 eLastOp
= maRawToken
.GetOpCode();
4300 aCorrectedFormula
+= aCorrectedSymbol
;
4302 if ( mbCloseBrackets
)
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;
4323 aCorrectedFormula
+= mxSymbols
->getSymbol(ocClose
);
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
);
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());
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
);
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();
4381 // global named range.
4382 pRangeData
= pDoc
->GetRangeName()->findByIndex( rToken
.GetIndex());
4385 // sheet local named range.
4386 const ScRangeName
* pRN
= pDoc
->GetRangeName( aPos
.Tab());
4388 pRangeData
= pRN
->findByIndex( rToken
.GetIndex());
4393 bool ScCompiler::HandleRange()
4395 const ScRangeData
* pRangeData
= GetRangeData( *mpToken
);
4398 sal_uInt16 nErr
= pRangeData
->GetErrCode();
4400 SetError( errNoName
);
4401 else if (mbJumpCommandReorder
)
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
);
4419 pNew
= new ScTokenArray();
4420 pNew
->AddOpCode( ocClose
);
4421 PushTokenArray( pNew
, true );
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());
4435 pNew
= new ScTokenArray();
4436 pNew
->AddOpCode( ocOpen
);
4437 PushTokenArray( pNew
, true );
4444 SetError(errNoName
);
4448 bool ScCompiler::HandleExternalReference(const FormulaToken
& _aToken
)
4450 // Handle external range names.
4451 switch (_aToken
.GetType())
4453 case svExternalSingleRef
:
4454 case svExternalDoubleRef
:
4456 case svExternalName
:
4458 ScExternalRefManager
* pRefMgr
= pDoc
->GetExternalRefManager();
4459 const OUString
* pFile
= pRefMgr
->getExternalFileName(_aToken
.GetIndex());
4462 SetError(errNoName
);
4466 OUString aName
= _aToken
.GetString().getString();
4467 ScExternalRefCache::TokenArrayRef xNew
= pRefMgr
->getRangeNameTokens(
4468 _aToken
.GetIndex(), aName
, &aPos
);
4472 SetError(errNoName
);
4476 ScTokenArray
* pNew
= xNew
->Clone();
4477 PushTokenArray( pNew
, true);
4478 if (pNew
->GetNextReference() != NULL
)
4480 SetRelNameReference();
4481 MoveRelWrap(MAXCOL
, MAXROW
);
4487 OSL_FAIL("Wrong type for external reference!");
4493 // reference of named range with relative references
4495 void ScCompiler::SetRelNameReference()
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
)
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() );
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
)
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() );
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;
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
))
4557 // convention not known => assume valid
4561 else if (bTestLetterNumeric
)
4562 return ScGlobal::pCharClass
->isLetterNumeric( rStr
, nPos
);
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
);
4576 switch (t
->GetType())
4578 case svExternalName
:
4579 rBuffer
.append(pConv
->makeExternalNameStr( nFileId
, *pFileName
, t
->GetString().getString()));
4581 case svExternalSingleRef
:
4582 pConv
->makeExternalRefStr(
4583 rBuffer
, GetPos(), nFileId
, *pFileName
, t
->GetString().getString(),
4584 *t
->GetSingleRef());
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());
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
++)
4620 rBuffer
.append( mxSymbols
->getSymbol(ocArrayRowSep
) );
4623 for( nC
= 0 ; nC
< nMaxC
; nC
++)
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);
4636 sal_uInt16 nErr
= pMatrix
->GetError(nC
, nR
);
4638 rBuffer
.append(ScGlobal::GetErrorString(nErr
));
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
) );
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
;
4668 aBuf
.append( '\'' );
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
);
4694 rBuffer
.append(aStr
);
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
);
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
;
4730 const ScRangeData
* pData
= GetRangeData( *_pTokenP
);
4732 aBuffer
.append(pData
->GetName());
4737 const ScDBData
* pDBData
= pDoc
->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
4739 aBuffer
.append(pDBData
->GetName());
4744 if (NeedsTableRefTransformation())
4746 // Write the resulting reference if TableRef is not supported.
4747 const ScTableRefToken
* pTR
= dynamic_cast<const ScTableRefToken
*>(_pTokenP
);
4749 AppendErrorConstant( aBuffer
, errNoCode
);
4752 const FormulaToken
* pRef
= pTR
->GetAreaRefRPN();
4754 AppendErrorConstant( aBuffer
, errNoCode
);
4757 switch (pRef
->GetType())
4760 CreateStringFromSingleRef( aBuffer
, pRef
);
4763 CreateStringFromDoubleRef( aBuffer
, pRef
);
4766 AppendErrorConstant( aBuffer
, pRef
->GetError());
4769 AppendErrorConstant( aBuffer
, errNoCode
);
4776 const ScDBData
* pDBData
= pDoc
->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP
->GetIndex());
4778 aBuffer
.append(pDBData
->GetName());
4785 if ( !aBuffer
.isEmpty() )
4786 rBuffer
.append(aBuffer
.makeStringAndClear());
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
) )
4806 while ( (nPos
= rStr
.indexOf( '\'', nPos
)) != -1 )
4808 rStr
= rStr
.replaceAt( nPos
, 0, "\\" );
4811 rStr
= "'" + rStr
+ "'";
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
);
4841 if (pFuncData
->GetExcelName( LANGUAGE_ENGLISH_US
, aName
))
4842 aEntry
.Name
= aName
;
4844 aEntry
.Name
= pFuncData
->GetUpperName();
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
);
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());
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);
4885 aRange
.aStart
.SetCol( nCol
);
4886 aRange
.aEnd
.SetCol( nCol
);
4890 aRange
.aStart
.SetRow( nRow
);
4891 aRange
.aEnd
.SetRow( nRow
);
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
4904 { // coresponds with ScInterpreter::ScColRowNameAuto()
4908 SCROW nStartRow
= nRow
+ 1;
4909 if ( 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
4917 if ( 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
)
4936 aRange
.aStart
.Set( nCol
, nStartRow
, nTab
);
4937 aRange
.aEnd
.Set( nCol
, nMaxRow
, nTab
);
4941 SCCOL nStartCol
= nCol
+ 1;
4942 if ( 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
4950 if ( 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
)
4969 aRange
.aStart
.Set( nStartCol
, nRow
, nTab
);
4970 aRange
.aEnd
.Set( nMaxCol
, nRow
, nTab
);
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
);
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
))
5002 { // column and/or row must match range
5005 bFound
= (aRange
.aStart
.Row() <= nMyRow
5006 && nMyRow
<= aRange
.aEnd
.Row());
5008 aRange
.aStart
.SetRow( nMyRow
);
5012 bFound
= (aRange
.aStart
.Col() <= nMyCol
5013 && nMyCol
<= aRange
.aEnd
.Col());
5015 aRange
.aStart
.SetCol( nMyCol
);
5023 else if (mbJumpCommandReorder
)
5025 ScTokenArray
* pNew
= new ScTokenArray();
5028 ScSingleRefData aRefData
;
5029 aRefData
.InitAddress( aRange
.aStart
);
5031 aRefData
.SetColRel( true );
5033 aRefData
.SetRowRel( true );
5034 aRefData
.SetAddress(aRange
.aStart
, aPos
);
5035 pNew
->AddSingleReference( aRefData
);
5039 ScComplexRefData aRefData
;
5040 aRefData
.InitRange( aRange
);
5043 aRefData
.Ref1
.SetColRel( true );
5044 aRefData
.Ref2
.SetColRel( true );
5048 aRefData
.Ref1
.SetRowRel( true );
5049 aRefData
.Ref2
.SetRowRel( true );
5051 aRefData
.SetRange(aRange
, aPos
);
5053 pNew
->AddDoubleReference( aRefData
);
5056 pNew
->Add( new ScDoubleRefToken( aRefData
, ocColRowNameAuto
) );
5059 PushTokenArray( pNew
, true );
5065 SetError(errNoName
);
5069 bool ScCompiler::HandleDbData()
5071 ScDBData
* pDBData
= pDoc
->GetDBCollection()->getNamedDBs().findByIndex(mpToken
->GetIndex());
5073 SetError(errNoName
);
5074 else if (mbJumpCommandReorder
)
5076 ScComplexRefData aRefData
;
5077 aRefData
.InitFlags();
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 );
5091 bool ScCompiler::GetTokenIfOpCode( OpCode eOp
)
5093 const formula::FormulaToken
* p
= pArr
->PeekNextNoSpaces();
5094 if (p
&& p
->GetOpCode() == eOp
)
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());
5112 SetError(errUnknownToken
);
5116 ScDBData
* pDBData
= pDoc
->GetDBCollection()->getNamedDBs().findByIndex( pTR
->GetIndex());
5118 SetError(errNoName
);
5119 else if (mbJumpCommandReorder
)
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();
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())
5140 bForwardToClose
= true;
5143 case ScTableRefToken::ALL
:
5145 bForwardToClose
= true;
5148 case ScTableRefToken::HEADERS
:
5150 if (pDBData
->HasHeader())
5151 aRange
.aEnd
.SetRow( aRange
.aStart
.Row());
5154 bForwardToClose
= true;
5157 case ScTableRefToken::DATA
:
5159 if (pDBData
->HasHeader())
5160 aRange
.aStart
.IncRow();
5163 case ScTableRefToken::HEADERS_DATA
:
5165 if (pDBData
->HasTotals())
5166 aRange
.aEnd
.IncRow(-1);
5167 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5169 bForwardToClose
= true;
5172 case ScTableRefToken::TOTALS
:
5174 if (pDBData
->HasTotals())
5175 aRange
.aStart
.SetRow( aRange
.aEnd
.Row());
5178 bForwardToClose
= true;
5181 case ScTableRefToken::DATA_TOTALS
:
5183 if (pDBData
->HasHeader())
5184 aRange
.aStart
.IncRow();
5185 if (aRange
.aEnd
.Row() < aRange
.aStart
.Row())
5187 bForwardToClose
= true;
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());
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;
5208 bool bColumnRange
= false;
5209 bool bCol1Rel
= false;
5210 bool bCol2Rel
= false;
5211 bool bCol1RelName
= false;
5212 bool bCol2RelName
= false;
5214 if (bForwardToClose
&& GetTokenIfOpCode( ocTableRefOpen
))
5228 const formula::FormulaToken
* p
= pArr
->PeekNextNoSpaces();
5233 switch (p
->GetOpCode())
5235 case ocTableRefOpen
:
5236 eState
= ((eState
== sOpen
|| eState
== sSep
) ? sOpen
: sStop
);
5243 case ocTableRefItemAll
:
5244 case ocTableRefItemHeaders
:
5245 case ocTableRefItemData
:
5246 case ocTableRefItemTotals
:
5247 case ocTableRefItemThisRow
:
5248 eState
= ((eState
== sOpen
) ? sItem
: sStop
);
5250 case ocTableRefClose
:
5251 eState
= ((eState
== sItem
|| eState
== sClose
) ? sClose
: sStop
);
5252 if (eState
!= sStop
&& --nLevel
== 0)
5256 eState
= ((eState
== sClose
) ? sSep
: sStop
);
5259 if (eState
== sOpen
&& p
->GetType() == svSingleRef
)
5261 bColumnRange
= true;
5262 bCol1Rel
= p
->GetSingleRef()->IsColRel();
5263 bCol1RelName
= p
->GetSingleRef()->IsRelName();
5279 if (eState
!= sStop
)
5281 if (eState
== sLast
)
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.
5293 // Limit range to specified columns.
5294 ScRange
aColRange( ScAddress::INITIALIZE_INVALID
);
5295 switch (mpToken
->GetType())
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
);
5309 aColRange
.aEnd
= mpToken
->GetSingleRef()->toAbs( aPos
);
5310 aColRange
.Justify();
5311 bCol2Rel
= mpToken
->GetSingleRef()->IsColRel();
5312 bCol2RelName
= mpToken
->GetSingleRef()->IsRelName();
5320 if (aColRange
.aStart
.Row() != aDBRange
.aStart
.Row() || aColRange
.aEnd
.Row() != aDBRange
.aStart
.Row())
5321 aRange
= ScRange( ScAddress::INITIALIZE_INVALID
);
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);
5339 bCol1RelName
= pArr
->IsFromRangeName();
5341 aRefData
.SetRelName( bCol1RelName
);
5342 aRefData
.SetFlag3D( true);
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
5351 aRefData
.SetAddress( aRange
.aStart
, aPos
);
5352 pTR
->SetAreaRefRPN( pNew
->AddSingleReference( aRefData
));
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);
5367 bRelName
= pArr
->IsFromRangeName();
5369 aRefData
.Ref1
.SetRelName( bRelName
);
5370 aRefData
.Ref2
.SetRelName( bRelName
);
5371 aRefData
.Ref1
.SetFlag3D( true);
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
5380 aRefData
.SetRange( aRange
, aPos
);
5381 pTR
->SetAreaRefRPN( pNew
->AddDoubleReference( aRefData
));
5387 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( errNoRef
)));
5392 pTR
->SetAreaRefRPN( pNew
->Add( new FormulaErrorToken( nError
)));
5394 while (nLevel
-- > 0)
5396 if (!GetTokenIfOpCode( ocTableRefClose
))
5399 PushTokenArray( pNew
, true );
5406 bool ScCompiler::IsForceArrayParameter( const formula::FormulaToken
* pToken
, sal_uInt16 nParam
) const
5408 ScParameterClassification::Type eType
= ScParameterClassification::GetParameterType( pToken
, nParam
);
5410 eType
== ScParameterClassification::ForceArray
||
5411 eType
== ScParameterClassification::ReferenceOrForceArray
;
5414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */