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