LibreOffice Module sc (master) 1
compiler.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <config_features.h>
21
22#include <compiler.hxx>
23
24#include <vcl/svapp.hxx>
25#include <vcl/settings.hxx>
26#include <sfx2/app.hxx>
27#include <sfx2/objsh.hxx>
28#include <basic/sbmeth.hxx>
29#include <basic/sbstar.hxx>
30#include <svl/numformat.hxx>
31#include <svl/zforlist.hxx>
33#include <sal/log.hxx>
34#include <o3tl/safeint.hxx>
35#include <o3tl/string_view.hxx>
36#include <osl/diagnose.h>
37#include <rtl/character.hxx>
40#include <com/sun/star/lang/Locale.hpp>
41#include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp>
42#include <com/sun/star/sheet/FormulaLanguage.hpp>
43#include <com/sun/star/i18n/KParseTokens.hpp>
44#include <com/sun/star/i18n/KParseType.hpp>
46#include <comphelper/string.hxx>
48#include <tools/urlobj.hxx>
49#include <rtl/math.hxx>
50#include <rtl/ustring.hxx>
51#include <stdlib.h>
52#include <rangenam.hxx>
53#include <dbdata.hxx>
54#include <document.hxx>
55#include <callform.hxx>
56#include <addincol.hxx>
57#include <refupdat.hxx>
58#include <globstr.hrc>
59#include <scresid.hxx>
60#include <formulacell.hxx>
61#include <dociter.hxx>
62#include <docoptio.hxx>
64#include <parclass.hxx>
65#include <autonamecache.hxx>
66#include <externalrefmgr.hxx>
67#include <rangeutl.hxx>
68#include <convuno.hxx>
69#include <tokenuno.hxx>
70#include <formulaparserpool.hxx>
71#include <tokenarray.hxx>
72#include <scmatrix.hxx>
74#include <officecfg/Office/Common.hxx>
75
76using namespace formula;
77using namespace ::com::sun::star;
78using ::std::vector;
79
80osl::Mutex ScCompiler::maMutex;
83const ScCompiler::Convention* ScCompiler::pConventions[ ] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
84
85namespace {
86
87enum ScanState
88{
89 ssGetChar,
90 ssGetBool,
91 ssGetValue,
92 ssGetString,
93 ssSkipString,
94 ssGetIdent,
95 ssGetReference,
96 ssSkipReference,
97 ssGetErrorConstant,
98 ssGetTableRefItem,
99 ssGetTableRefColumn,
100 ssStop
101};
102
103}
104
105static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
106
107using namespace ::com::sun::star::i18n;
108
110{
111 size_t nSymbolOffset;
112 switch( _eGrammar )
113 {
114 // XFunctionAccess and XCell::setFormula()/getFormula() API always used
115 // PODF grammar symbols, keep it.
116 case FormulaGrammar::GRAM_API:
117 case FormulaGrammar::GRAM_PODF:
118 nSymbolOffset = offsetof( AddInMap, pUpper);
119 break;
120 default:
121 case FormulaGrammar::GRAM_ODFF:
122 nSymbolOffset = offsetof( AddInMap, pODFF);
123 break;
124 case FormulaGrammar::GRAM_ENGLISH:
125 nSymbolOffset = offsetof( AddInMap, pEnglish);
126 break;
127 }
128 const AddInMap* const pStop = g_aAddInMap + GetAddInMapCount();
129 for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
130 {
131 char const * const * ppSymbol =
132 reinterpret_cast< char const * const * >(
133 reinterpret_cast< char const * >(pMap) + nSymbolOffset);
134 xMap->putExternal( OUString::createFromAscii( *ppSymbol),
135 OUString::createFromAscii( pMap->pOriginal));
136 }
137 if (_eGrammar == FormulaGrammar::GRAM_API)
138 {
139 // Add English names additionally to programmatic names, so they
140 // can be used in XCell::setFormula() non-localized API calls.
141 // Note the reverse map will still deliver programmatic names for
142 // XCell::getFormula().
143 nSymbolOffset = offsetof( AddInMap, pEnglish);
144 for (const AddInMap* pMap = g_aAddInMap; pMap < pStop; ++pMap)
145 {
146 char const * const * ppSymbol =
147 reinterpret_cast< char const * const * >(
148 reinterpret_cast< char const * >(pMap) + nSymbolOffset);
149 xMap->putExternal( OUString::createFromAscii( *ppSymbol),
150 OUString::createFromAscii( pMap->pOriginal));
151 }
152 }
153}
154
156{
159 for (tools::Long i=0; i < nCount; ++i)
160 {
161 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
162 if (pFuncData)
163 xMap->putExternalSoftly( pFuncData->GetUpperName(),
164 pFuncData->GetOriginalName());
165 }
166}
167
169{
170 const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
173 for (tools::Long i=0; i < nCount; ++i)
174 {
175 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
176 if (pFuncData)
177 {
178 OUString aName;
179 if (pFuncData->GetExcelName( aEnglishLanguageTag, aName))
180 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName());
181 else
182 xMap->putExternalSoftly( pFuncData->GetUpperName(),
183 pFuncData->GetOriginalName());
184 }
185 }
186}
187
189{
191 {
192 delete pCharClassEnglish;
193 pCharClassEnglish = nullptr;
194 }
196 {
197 delete pCharClassLocalized;
198 pCharClassLocalized = nullptr;
199 }
200}
201
202bool ScCompiler::IsEnglishSymbol( const OUString& rName )
203{
204 // function names are always case-insensitive
205 OUString aUpper = GetCharClassEnglish()->uppercase(rName);
206
207 // 1. built-in function name
208 formula::FormulaCompiler aCompiler;
209 OpCode eOp = aCompiler.GetEnglishOpCode( aUpper );
210 if ( eOp != ocNone )
211 {
212 return true;
213 }
214 // 2. old add in functions
215 if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper))
216 {
217 return true;
218 }
219
220 // 3. new (uno) add in functions
221 OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false);
222 return !aIntName.isEmpty(); // no valid function name
223}
224
226{
228 {
229 osl::MutexGuard aGuard(maMutex);
231 {
232 pCharClassEnglish = new CharClass( ::comphelper::getProcessComponentContext(),
234 }
235 }
236 return pCharClassEnglish;
237}
238
240{
242 {
243 // Switching UI language requires restart; if not, we would have to
244 // keep track of that.
245 osl::MutexGuard aGuard(maMutex);
247 {
248 pCharClassLocalized = new CharClass( ::comphelper::getProcessComponentContext(),
249 Application::GetSettings().GetUILanguageTag());
250 }
251 }
252 return pCharClassLocalized;
253}
254
256{
257 assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED");
258 if (eGrammar == GetGrammar())
259 return; // nothing to be done
260
261 if( eGrammar == FormulaGrammar::GRAM_EXTERNAL )
262 {
263 meGrammar = eGrammar;
264 mxSymbols = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
265 }
266 else
267 {
268 FormulaGrammar::Grammar eMyGrammar = eGrammar;
269 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar);
270 OpCodeMapPtr xMap = GetFinalOpCodeMap( nFormulaLanguage);
271 OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language");
272 if (!xMap)
273 {
274 xMap = GetFinalOpCodeMap( css::sheet::FormulaLanguage::NATIVE);
275 eMyGrammar = xMap->getGrammar();
276 }
277
278 // Save old grammar for call to SetGrammarAndRefConvention().
279 FormulaGrammar::Grammar eOldGrammar = GetGrammar();
280 // This also sets the grammar associated with the map!
281 SetFormulaLanguage( xMap);
282
283 // Override if necessary.
284 if (eMyGrammar != GetGrammar())
285 SetGrammarAndRefConvention( eMyGrammar, eOldGrammar);
286 }
287}
288
289// Unclear how this was intended to be refreshed when the
290// grammar or sheet count is changed ? Ideally this would be
291// a method on Document that would globally cache these.
292std::vector<OUString> &ScCompiler::GetSetupTabNames() const
293{
294 std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames;
295
296 if (rTabNames.empty())
297 {
298 rTabNames = rDoc.GetAllTableNames();
299 for (auto& rTabName : rTabNames)
301 }
302
303 return rTabNames;
304}
305
307{
308 mpFormatter = pFormatter;
309}
310
312{
313 if (!xMap)
314 return;
315
316 mxSymbols = xMap;
317 if (mxSymbols->isEnglish())
319 else
321
322 // The difference is needed for an uppercase() call that usually does not
323 // result in different strings but for a few languages like Turkish;
324 // though even de-DE and de-CH may differ in ß/SS handling..
325 // At least don't care if both are English.
326 // The current locale is more likely to not be "en" so check first.
328 const LanguageTag& rLT2 = pCharClass->getLanguageTag();
329 mbCharClassesDiffer = (rLT1 != rLT2 && (rLT1.getLanguage() != "en" || rLT2.getLanguage() != "en"));
330
332}
333
335 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar )
336{
337 meGrammar = eNewGrammar; // SetRefConvention needs the new grammar set!
338 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar);
339 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED)
341 else
342 SetRefConvention( eConv );
343}
344
345OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const
346{
347 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english
348}
349
351{
352}
353
355 :
356 meConv( eConv )
357{
358 int i;
359 ScCharFlags *t= new ScCharFlags [128];
360
362 mpCharTable.reset( t );
363
364 for (i = 0; i < 128; i++)
366
367// Allow tabs/newlines.
368// Allow saving whitespace as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace").
372
375 if (FormulaGrammar::CONV_ODF == meConv)
376/* ! */ t[33] |= ScCharFlags::OdfLabelOp;
380 if (FormulaGrammar::CONV_ODF == meConv)
381/* $ */ t[36] |= ScCharFlags::OdfNameMarker;
384/* ' */ t[39] = ScCharFlags::NameSep;
393
394 for (i = 48; i < 58; i++)
396
403/* @ */ // FREE
404
405 for (i = 65; i < 91; i++)
407
408 if (FormulaGrammar::CONV_ODF == meConv)
409 {
410/* [ */ t[91] = ScCharFlags::OdfLBracket;
411/* \ */ // FREE
412/* ] */ t[93] = ScCharFlags::OdfRBracket;
413 }
414 else if (FormulaGrammar::CONV_OOO == meConv)
415 {
416/* [ */ t[91] = ScCharFlags::Char;
417/* \ */ // FREE
418/* ] */ t[93] = ScCharFlags::Char;
419 }
420 else if (FormulaGrammar::CONV_XL_OOX == meConv)
421 {
423/* \ */ // FREE
425 }
426 else if (FormulaGrammar::CONV_XL_A1 == meConv)
427 {
428/* [ */ t[91] = ScCharFlags::Char;
429/* \ */ // FREE
430/* ] */ t[93] = ScCharFlags::Char;
431 }
432 else if( FormulaGrammar::CONV_XL_R1C1 == meConv )
433 {
434/* [ */ t[91] = ScCharFlags::Ident;
435/* \ */ // FREE
436/* ] */ t[93] = ScCharFlags::Ident;
437 }
438 else
439 {
440/* [ */ // FREE
441/* \ */ // FREE
442/* ] */ // FREE
443 }
444
447/* ` */ // FREE
448
449 for (i = 97; i < 123; i++)
451
452/* { */ t[123] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open
453/* | */ t[124] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific)
454/* } */ t[125] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close
455/* ~ */ t[126] = ScCharFlags::Char; // OOo specific
456/* 127 */ // FREE
457
458 if( !(FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv) )
459return;
460
461/* */ t[32] |= ScCharFlags::Word;
462/* ! */ t[33] |= ScCharFlags::Ident | ScCharFlags::Word;
463/* " */ t[34] |= ScCharFlags::Word;
464/* # */ t[35] &= ~ScCharFlags::WordSep;
465/* # */ t[35] |= ScCharFlags::Word;
466/* % */ t[37] |= ScCharFlags::Word;
467/* & */ t[38] |= ScCharFlags::Word;
468/* ' */ t[39] |= ScCharFlags::Word;
469/* ( */ t[40] |= ScCharFlags::Word;
470/* ) */ t[41] |= ScCharFlags::Word;
471/* * */ t[42] |= ScCharFlags::Word;
472/* + */ t[43] |= ScCharFlags::Word;
473#if 0 /* this really needs to be locale specific. */
475#else
476/* , */ t[44] |= ScCharFlags::Word;
477#endif
478/* - */ t[45] |= ScCharFlags::Word;
479
480/* ; */ t[59] |= ScCharFlags::Word;
481/* < */ t[60] |= ScCharFlags::Word;
482/* = */ t[61] |= ScCharFlags::Word;
483/* > */ t[62] |= ScCharFlags::Word;
484/* ? */ // question really is not permitted in sheet name
485/* @ */ t[64] |= ScCharFlags::Word;
486/* [ */ t[91] |= ScCharFlags::Word;
487/* ] */ t[93] |= ScCharFlags::Word;
488/* { */ t[123]|= ScCharFlags::Word;
489/* | */ t[124]|= ScCharFlags::Word;
490/* } */ t[125]|= ScCharFlags::Word;
491/* ~ */ t[126]|= ScCharFlags::Word;
492}
493
494static bool lcl_isValidQuotedText( std::u16string_view rFormula, size_t nSrcPos, ParseResult& rRes )
495{
496 // Tokens that start at ' can have anything in them until a final '
497 // but '' marks an escaped '
498 // We've earlier guaranteed that a string containing '' will be
499 // surrounded by '
500 if (nSrcPos < rFormula.size() && rFormula[nSrcPos] == '\'')
501 {
502 size_t nPos = nSrcPos+1;
503 while (nPos < rFormula.size())
504 {
505 if (rFormula[nPos] == '\'')
506 {
507 if ( (nPos+1 == rFormula.size()) || (rFormula[nPos+1] != '\'') )
508 {
509 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME;
510 rRes.EndPos = nPos+1;
511 return true;
512 }
513 ++nPos;
514 }
515 ++nPos;
516 }
517 }
518
519 return false;
520}
521
523 const OUString& rSymbol,
524 OUString& rFile,
525 OUString& rName,
526 const sal_Unicode cSep,
527 const ScDocument& rDoc,
528 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
529{
530 /* TODO: future versions will have to support sheet-local names too, thus
531 * return a possible sheet name as well. */
532 const sal_Unicode* const pStart = rSymbol.getStr();
533 const sal_Unicode* p = pStart;
534 sal_Int32 nLen = rSymbol.getLength();
535 OUString aTmpFile;
536 OUStringBuffer aTmpName;
537 sal_Int32 i = 0;
538 bool bInName = false;
539 if (cSep == '!')
540 {
541 // For XL use existing parser that resolves bracketed and quoted and
542 // indexed external document names.
543 ScRange aRange;
544 OUString aStartTabName, aEndTabName;
546 p = aRange.Parse_XL_Header( p, rDoc, aTmpFile, aStartTabName,
547 aEndTabName, nFlags, true, pExternalLinks );
548 if (!p || p == pStart)
549 return false;
550 i = sal_Int32(p - pStart);
551 }
552 for ( ; i < nLen; ++i, ++p)
553 {
554 sal_Unicode c = *p;
555 if (i == 0)
556 {
557 if (c == '.' || c == cSep)
558 return false;
559
560 if (c == '\'')
561 {
562 // Move to the next char and loop until the second single
563 // quote.
564 sal_Unicode cPrev = c;
565 ++i; ++p;
566 for (sal_Int32 j = i; j < nLen; ++j, ++p)
567 {
568 c = *p;
569 if (c == '\'')
570 {
571 if (j == i)
572 {
573 // empty quote e.g. (=''!Name)
574 return false;
575 }
576
577 if (cPrev == '\'')
578 {
579 // two consecutive quotes equal a single quote in
580 // the file name.
581 aTmpFile += OUStringChar(c);
582 cPrev = 'a';
583 }
584 else
585 cPrev = c;
586
587 continue;
588 }
589
590 if (cPrev == '\'' && j != i)
591 {
592 // this is not a quote but the previous one is. This
593 // ends the parsing of the quoted segment. At this
594 // point, the current char must equal the separator
595 // char.
596
597 i = j;
598 bInName = true;
599 aTmpName.append(c); // Keep the separator as part of the name.
600 break;
601 }
602 aTmpFile += OUStringChar(c);
603 cPrev = c;
604 }
605
606 if (!bInName)
607 {
608 // premature ending of the quoted segment.
609 return false;
610 }
611
612 if (c != cSep)
613 {
614 // only the separator is allowed after the closing quote.
615 return false;
616 }
617
618 continue;
619 }
620 }
621
622 if (bInName)
623 {
624 if (c == cSep)
625 {
626 // A second separator ? Not a valid external name.
627 return false;
628 }
629 aTmpName.append(c);
630 }
631 else
632 {
633 if (c == cSep)
634 {
635 bInName = true;
636 aTmpName.append(c); // Keep the separator as part of the name.
637 }
638 else
639 {
640 do
641 {
642 if (rtl::isAsciiAlphanumeric(c))
643 // allowed.
644 break;
645
646 if (c > 128)
647 // non-ASCII character is allowed.
648 break;
649
650 bool bValid = false;
651 switch (c)
652 {
653 case '_':
654 case '-':
655 case '.':
656 // these special characters are allowed.
657 bValid = true;
658 break;
659 }
660 if (bValid)
661 break;
662
663 return false;
664 }
665 while (false);
666 aTmpFile += OUStringChar(c);
667 }
668 }
669 }
670
671 if (!bInName)
672 {
673 // No name found - most likely the symbol has no '!'s.
674 return false;
675 }
676
677 sal_Int32 nNameLen = aTmpName.getLength();
678 if (nNameLen < 2)
679 {
680 // Name must be at least 2-char long (separator plus name).
681 return false;
682 }
683
684 if (aTmpName[0] != cSep)
685 {
686 // 1st char of the name must equal the separator.
687 return false;
688 }
689
690 if (aTmpName[nNameLen-1] == '!')
691 {
692 // Check against #REF!.
693 if (OUString::unacquired(aTmpName).equalsIgnoreAsciiCase("#REF!"))
694 return false;
695 }
696
697 rFile = aTmpFile;
698 rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator.
699 return true;
700}
701
702static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName,
703 const sal_Unicode cSep, bool bODF )
704{
705 OUString aEscQuote("''");
706 OUString aFile(rFile.replaceAll("'", aEscQuote));
707 OUString aName(rName);
708 if (bODF)
709 aName = aName.replaceAll("'", aEscQuote);
710 OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9);
711 if (bODF)
712 aBuf.append( '[');
713 aBuf.append( "'" + aFile + "'" + OUStringChar(cSep) );
714 if (bODF)
715 aBuf.append( "$$'" );
716 aBuf.append( aName);
717 if (bODF)
718 aBuf.append( "']" );
719 return aBuf.makeStringAndClear();
720}
721
722static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1,
723 const vector<OUString>& rTabNames, const ScRange& rRef )
724{
725 SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab();
726 if (nTabSpan > 0)
727 {
728 size_t nCount = rTabNames.size();
729 vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end();
730 vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1);
731 if (itr == rTabNames.end())
732 {
733 rTabName2 = ScResId(STR_NO_REF_TABLE);
734 return false;
735 }
736
737 size_t nDist = ::std::distance(itrBeg, itr);
738 if (nDist + static_cast<size_t>(nTabSpan) >= nCount)
739 {
740 rTabName2 = ScResId(STR_NO_REF_TABLE);
741 return false;
742 }
743
744 rTabName2 = rTabNames[nDist+nTabSpan];
745 }
746 else
747 rTabName2 = rTabName1;
748
749 return true;
750}
751
752namespace {
753
754struct Convention_A1 : public ScCompiler::Convention
755{
756 explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { }
757 static void MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol );
758 static void MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow );
759
760 ParseResult parseAnyToken( const OUString& rFormula,
761 sal_Int32 nSrcPos,
762 const CharClass* pCharClass,
763 bool bGroupSeparator) const override
764 {
765 ParseResult aRet;
766 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
767 return aRet;
768
769 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
770 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
771 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
772 // '?' allowed in range names because of Xcl :-/
773 static constexpr OUStringLiteral aAddAllowed(u"?#");
774 return pCharClass->parseAnyToken( rFormula,
775 nSrcPos, nStartFlags, aAddAllowed,
776 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
777 aAddAllowed );
778 }
779
780 virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override
781 {
782 return mpCharTable[static_cast<sal_uInt8>(c)];
783 }
784};
785
786}
787
788void Convention_A1::MakeColStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol )
789{
790 if ( !rLimits.ValidCol(nCol) )
791 rBuffer.append(ScResId(STR_NO_REF_TABLE));
792 else
793 ::ScColToAlpha( rBuffer, nCol);
794}
795
796void Convention_A1::MakeRowStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow )
797{
798 if ( !rLimits.ValidRow(nRow) )
799 rBuffer.append(ScResId(STR_NO_REF_TABLE));
800 else
801 rBuffer.append(sal_Int32(nRow + 1));
802}
803
804namespace {
805
806struct ConventionOOO_A1 : public Convention_A1
807{
808 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { }
809 explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { }
810
811 static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab )
812 {
813 if (o3tl::make_unsigned(nTab) >= rTabNames.size())
814 rBuf.append(ScResId(STR_NO_REF_TABLE));
815 else
816 rBuf.append(rTabNames[nTab]);
817 rBuf.append('.');
818 }
819
820 enum SingletonDisplay
821 {
822 SINGLETON_NONE,
823 SINGLETON_COL,
824 SINGLETON_ROW
825 };
826
827 static void MakeOneRefStrImpl(
828 const ScSheetLimits& rLimits, OUStringBuffer& rBuffer,
829 std::u16string_view rErrRef, const std::vector<OUString>& rTabNames,
830 const ScSingleRefData& rRef, const ScAddress& rAbsRef,
831 bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay )
832 {
833 if( rRef.IsFlag3D() || bForceTab )
834 {
835 if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted())
836 {
837 if (!rRef.IsTabRel())
838 rBuffer.append('$');
839 rBuffer.append(rErrRef);
840 rBuffer.append('.');
841 }
842 else
843 {
844 if (!rRef.IsTabRel())
845 rBuffer.append('$');
846 MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab());
847 }
848 }
849 else if (bODF)
850 rBuffer.append('.');
851
852 if (eSingletonDisplay != SINGLETON_ROW)
853 {
854 if (!rRef.IsColRel())
855 rBuffer.append('$');
856 if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted())
857 rBuffer.append(rErrRef);
858 else
859 MakeColStr(rLimits, rBuffer, rAbsRef.Col());
860 }
861
862 if (eSingletonDisplay != SINGLETON_COL)
863 {
864 if (!rRef.IsRowRel())
865 rBuffer.append('$');
866 if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted())
867 rBuffer.append(rErrRef);
868 else
869 MakeRowStr(rLimits, rBuffer, rAbsRef.Row());
870 }
871 }
872
873 static SingletonDisplay getSingletonDisplay( const ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2,
874 const ScComplexRefData& rRef, bool bFromRangeName )
875 {
876 // If any part is error, display as such.
877 if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() ||
878 !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted())
879 return SINGLETON_NONE;
880
881 // A:A or $A:$A or A:$A or $A:A
882 if (rRef.IsEntireCol(rLimits))
883 return SINGLETON_COL;
884
885 // Same if not in named expression and both rows of entire columns are
886 // relative references.
887 if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow &&
888 rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel())
889 return SINGLETON_COL;
890
891 // 1:1 or $1:$1 or 1:$1 or $1:1
892 if (rRef.IsEntireRow(rLimits))
893 return SINGLETON_ROW;
894
895 // Same if not in named expression and both columns of entire rows are
896 // relative references.
897 if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol &&
898 rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel())
899 return SINGLETON_ROW;
900
901 return SINGLETON_NONE;
902 }
903
904 virtual void makeRefStr(
905 ScSheetLimits& rLimits,
906 OUStringBuffer& rBuffer,
908 const ScAddress& rPos,
909 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
910 const ScComplexRefData& rRef,
911 bool bSingleRef,
912 bool bFromRangeName ) const override
913 {
914 // In case absolute/relative positions weren't separately available:
915 // transform relative to absolute!
916 ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
917 if( !bSingleRef )
918 aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
919
920 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
921 getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
922 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton);
923 if (!bSingleRef)
924 {
925 rBuffer.append(':');
926 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false,
927 eSingleton);
928 }
929 }
930
931 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
932 {
933 switch (eSymType)
934 {
936 return '$';
938 return '.';
939 }
940
941 return u'\0';
942 }
943
944 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
945 const ScDocument& rDoc,
946 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
947 {
948 return lcl_parseExternalName(rSymbol, rFile, rName, '#', rDoc, pExternalLinks);
949 }
950
951 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
952 const OUString& rName ) const override
953 {
954 return lcl_makeExternalNameStr( rFile, rName, '#', false);
955 }
956
957 static bool makeExternalSingleRefStr(
958 const ScSheetLimits& rLimits,
959 OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName,
960 const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl )
961 {
962 ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
963 if (bDisplayTabName)
964 {
965 OUString aFile;
966 if (bEncodeUrl)
967 aFile = rFileName;
968 else
970
971 rBuffer.append("'" + aFile.replaceAll("'", "''") + "'#");
972
973 if (!rRef.IsTabRel())
974 rBuffer.append('$');
976
977 rBuffer.append('.');
978 }
979
980 if (!rRef.IsColRel())
981 rBuffer.append('$');
982 MakeColStr( rLimits, rBuffer, aAbsRef.Col());
983 if (!rRef.IsRowRel())
984 rBuffer.append('$');
985 MakeRowStr( rLimits, rBuffer, aAbsRef.Row());
986
987 return true;
988 }
989
990 static void makeExternalRefStrImpl(
991 const ScSheetLimits& rLimits,
992 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
993 const OUString& rTabName, const ScSingleRefData& rRef, bool bODF )
994 {
995 if (bODF)
996 rBuffer.append( '[');
997
998 bool bEncodeUrl = bODF;
999 makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl);
1000 if (bODF)
1001 rBuffer.append( ']');
1002 }
1003
1004 virtual void makeExternalRefStr(
1005 ScSheetLimits& rLimits,
1006 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1007 const OUString& rTabName, const ScSingleRefData& rRef ) const override
1008 {
1009 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false);
1010 }
1011
1012 static void makeExternalRefStrImpl(
1013 const ScSheetLimits& rLimits,
1014 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName,
1015 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1016 const ScComplexRefData& rRef, bool bODF )
1017 {
1018 ScRange aAbsRange = rRef.toAbs(rLimits, rPos);
1019
1020 if (bODF)
1021 rBuffer.append( '[');
1022 // Ensure that there's always a closing bracket, no premature returns.
1023 bool bEncodeUrl = bODF;
1024
1025 do
1026 {
1027 if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl))
1028 break;
1029
1030 rBuffer.append(':');
1031
1032 OUString aLastTabName;
1033 bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab());
1034 if (bDisplayTabName)
1035 {
1036 // Get the name of the last table.
1037 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange))
1038 {
1039 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found");
1040 // aLastTabName contains #REF!, proceed.
1041 }
1042 }
1043 else if (bODF)
1044 rBuffer.append( '.'); // need at least the sheet separator in ODF
1045 makeExternalSingleRefStr(rLimits,
1046 rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl);
1047 } while (false);
1048
1049 if (bODF)
1050 rBuffer.append( ']');
1051 }
1052
1053 virtual void makeExternalRefStr(
1054 ScSheetLimits& rLimits,
1055 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1056 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1057 const ScComplexRefData& rRef ) const override
1058 {
1059 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false);
1060 }
1061};
1062
1063struct ConventionOOO_A1_ODF : public ConventionOOO_A1
1064{
1065 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { }
1066
1067 virtual void makeRefStr(
1068 ScSheetLimits& rLimits,
1069 OUStringBuffer& rBuffer,
1071 const ScAddress& rPos,
1072 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1073 const ScComplexRefData& rRef,
1074 bool bSingleRef,
1075 bool bFromRangeName ) const override
1076 {
1077 rBuffer.append('[');
1078 // In case absolute/relative positions weren't separately available:
1079 // transform relative to absolute!
1080 ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1081 if( !bSingleRef )
1082 aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
1083
1084 if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) ||
1085 (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2)))))
1086 {
1087 rBuffer.append(rErrRef);
1088 // For ODFF write [#REF!], but not for PODF so apps reading ODF
1089 // 1.0/1.1 may have a better chance if they implemented the old
1090 // form.
1091 }
1092 else
1093 {
1094 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE :
1095 getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName);
1096 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton);
1097 if (!bSingleRef)
1098 {
1099 rBuffer.append(':');
1100 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true,
1101 eSingleton);
1102 }
1103 }
1104 rBuffer.append(']');
1105 }
1106
1107 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1108 const OUString& rName ) const override
1109 {
1110 return lcl_makeExternalNameStr( rFile, rName, '#', true);
1111 }
1112
1113 virtual void makeExternalRefStr(
1114 ScSheetLimits& rLimits,
1115 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1116 const OUString& rTabName, const ScSingleRefData& rRef ) const override
1117 {
1118 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true);
1119 }
1120
1121 virtual void makeExternalRefStr(
1122 ScSheetLimits& rLimits,
1123 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1124 const std::vector<OUString>& rTabNames,
1125 const OUString& rTabName, const ScComplexRefData& rRef ) const override
1126 {
1127 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true);
1128 }
1129};
1130
1131struct ConventionXL
1132{
1133 virtual ~ConventionXL()
1134 {
1135 }
1136
1137 static void GetTab(
1138 const ScSheetLimits& rLimits,
1139 const ScAddress& rPos, const std::vector<OUString>& rTabNames,
1140 const ScSingleRefData& rRef, OUString& rTabName )
1141 {
1142 ScAddress aAbs = rRef.toAbs(rLimits, rPos);
1143 if (rRef.IsTabDeleted() || o3tl::make_unsigned(aAbs.Tab()) >= rTabNames.size())
1144 {
1145 rTabName = ScResId( STR_NO_REF_TABLE );
1146 return;
1147 }
1148 rTabName = rTabNames[aAbs.Tab()];
1149 }
1150
1151 static void MakeTabStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf,
1152 const ScAddress& rPos,
1153 const std::vector<OUString>& rTabNames,
1154 const ScComplexRefData& rRef,
1155 bool bSingleRef )
1156 {
1157 if( !rRef.Ref1.IsFlag3D() )
1158 return;
1159
1160 OUString aStartTabName, aEndTabName;
1161
1162 GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName);
1163
1164 if( !bSingleRef && rRef.Ref2.IsFlag3D() )
1165 {
1166 GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName);
1167 }
1168
1169 rBuf.append( aStartTabName );
1170 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName )
1171 {
1172 rBuf.append( ':' );
1173 rBuf.append( aEndTabName );
1174 }
1175
1176 rBuf.append( '!' );
1177 }
1178
1179 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType )
1180 {
1181 switch (eSymType)
1182 {
1184 return u'\0';
1186 return '!';
1187 }
1188 return u'\0';
1189 }
1190
1191 static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1192 const ScDocument& rDoc,
1193 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks )
1194 {
1195 return lcl_parseExternalName( rSymbol, rFile, rName, '!', rDoc, pExternalLinks);
1196 }
1197
1198 static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName )
1199 {
1200 return lcl_makeExternalNameStr( rFile, rName, '!', false);
1201 }
1202
1203 static void makeExternalDocStr( OUStringBuffer& rBuffer, std::u16string_view rFullName )
1204 {
1205 // Format that is easier to deal with inside OOo, because we use file
1206 // URL, and all characters are allowed. Check if it makes sense to do
1207 // it the way Gnumeric does it. Gnumeric doesn't use the URL form
1208 // and allows relative file path.
1209 //
1210 // ['file:///path/to/source/filename.xls']
1211
1212 rBuffer.append('[');
1213 rBuffer.append('\'');
1215
1216 const sal_Unicode* pBuf = aFullName.getStr();
1217 sal_Int32 nLen = aFullName.getLength();
1218 for (sal_Int32 i = 0; i < nLen; ++i)
1219 {
1220 const sal_Unicode c = pBuf[i];
1221 if (c == '\'')
1222 rBuffer.append(c);
1223 rBuffer.append(c);
1224 }
1225 rBuffer.append('\'');
1226 rBuffer.append(']');
1227 }
1228
1229 static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName,
1230 const vector<OUString>& rTabNames,
1231 const ScRange& rRef )
1232 {
1233 OUString aLastTabName;
1234 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef))
1235 {
1236 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1237 return;
1238 }
1239
1241 if (rTabName != aLastTabName)
1242 {
1243 rBuf.append(':');
1244 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName);
1245 }
1246 }
1247
1248 virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const
1249 {
1250 sal_Int32 nLen = rFormula.getLength();
1251 const sal_Unicode* p = rFormula.getStr();
1252 sal_Unicode cPrev = 0;
1253 for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1254 {
1255 sal_Unicode c = p[i];
1256 if (i == rSrcPos)
1257 {
1258 // first character must be '['.
1259 if (c != '[')
1260 return;
1261 }
1262 else if (i == rSrcPos + 1)
1263 {
1264 // second character must be a single quote.
1265 if (c != '\'')
1266 return;
1267 }
1268 else if (c == '\'')
1269 {
1270 if (cPrev == '\'')
1271 // two successive single quote is treated as a single
1272 // valid character.
1273 c = 'a';
1274 }
1275 else if (c == ']')
1276 {
1277 if (cPrev == '\'')
1278 {
1279 // valid source document path found. Increment the
1280 // current position to skip the source path.
1281 rSrcPos = i + 1;
1282 if (rSrcPos >= nLen)
1283 rSrcPos = nLen - 1;
1284 return;
1285 }
1286 else
1287 return;
1288 }
1289 else
1290 {
1291 // any other character
1292 if (i > rSrcPos + 2 && cPrev == '\'')
1293 // unless it's the 3rd character, a normal character
1294 // following immediately a single quote is invalid.
1295 return;
1296 }
1297 cPrev = c;
1298 }
1299 }
1300};
1301
1302struct ConventionXL_A1 : public Convention_A1, public ConventionXL
1303{
1304 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { }
1305 explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { }
1306
1307 static void makeSingleCellStr( const ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs )
1308 {
1309 if (!rRef.IsColRel())
1310 rBuf.append('$');
1311 MakeColStr(rLimits, rBuf, rAbs.Col());
1312 if (!rRef.IsRowRel())
1313 rBuf.append('$');
1314 MakeRowStr(rLimits, rBuf, rAbs.Row());
1315 }
1316
1317 virtual void makeRefStr(
1318 ScSheetLimits& rLimits,
1319 OUStringBuffer& rBuf,
1321 const ScAddress& rPos,
1322 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1323 const ScComplexRefData& rRef,
1324 bool bSingleRef,
1325 bool /*bFromRangeName*/ ) const override
1326 {
1327 ScComplexRefData aRef( rRef );
1328
1329 // Play fast and loose with invalid refs. There is not much point in producing
1330 // Foo!A1:#REF! versus #REF! at this point
1331 ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2;
1332
1333 MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1334
1335 if (!rLimits.ValidAddress(aAbs1))
1336 {
1337 rBuf.append(rErrRef);
1338 return;
1339 }
1340
1341 if( !bSingleRef )
1342 {
1343 aAbs2 = aRef.Ref2.toAbs(rLimits, rPos);
1344 if (!rLimits.ValidAddress(aAbs2))
1345 {
1346 rBuf.append(rErrRef);
1347 return;
1348 }
1349
1350 if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol)
1351 {
1352 if (!aRef.Ref1.IsRowRel())
1353 rBuf.append( '$' );
1354 MakeRowStr(rLimits, rBuf, aAbs1.Row());
1355 rBuf.append( ':' );
1356 if (!aRef.Ref2.IsRowRel())
1357 rBuf.append( '$' );
1358 MakeRowStr(rLimits, rBuf, aAbs2.Row());
1359 return;
1360 }
1361
1362 if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow)
1363 {
1364 if (!aRef.Ref1.IsColRel())
1365 rBuf.append( '$' );
1366 MakeColStr(rLimits, rBuf, aAbs1.Col());
1367 rBuf.append( ':' );
1368 if (!aRef.Ref2.IsColRel())
1369 rBuf.append( '$' );
1370 MakeColStr(rLimits, rBuf, aAbs2.Col());
1371 return;
1372 }
1373 }
1374
1375 makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1);
1376 if (!bSingleRef)
1377 {
1378 rBuf.append( ':' );
1379 makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2);
1380 }
1381 }
1382
1383 virtual ParseResult parseAnyToken( const OUString& rFormula,
1384 sal_Int32 nSrcPos,
1385 const CharClass* pCharClass,
1386 bool bGroupSeparator) const override
1387 {
1388 parseExternalDocName(rFormula, nSrcPos);
1389
1390 ParseResult aRet;
1391 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1392 return aRet;
1393
1394 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1395 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR;
1396 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1397 // '?' allowed in range names
1398 static constexpr OUStringLiteral aAddAllowed(u"?!");
1399 return pCharClass->parseAnyToken( rFormula,
1400 nSrcPos, nStartFlags, aAddAllowed,
1401 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1402 aAddAllowed );
1403 }
1404
1405 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1406 {
1407 return ConventionXL::getSpecialSymbol(eSymType);
1408 }
1409
1410 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1411 const ScDocument& rDoc,
1412 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1413 {
1414 return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
1415 }
1416
1417 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1418 const OUString& rName ) const override
1419 {
1420 return ConventionXL::makeExternalNameStr(rFile, rName);
1421 }
1422
1423 virtual void makeExternalRefStr(
1424 ScSheetLimits& rLimits,
1425 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1426 const OUString& rTabName, const ScSingleRefData& rRef ) const override
1427 {
1428 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1429 // This is a little different from the format Excel uses, as Excel
1430 // puts [] only around the file name. But we need to enclose the
1431 // whole file path with [] because the file name can contain any
1432 // characters.
1433
1434 ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1435 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1436 rBuffer.append('!');
1437
1438 makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1439 }
1440
1441 virtual void makeExternalRefStr(
1442 ScSheetLimits& rLimits,
1443 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1444 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1445 const ScComplexRefData& rRef ) const override
1446 {
1447 ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1448
1449 ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1450 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1451 rBuffer.append('!');
1452
1453 makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1454 if (aAbsRef.aStart != aAbsRef.aEnd)
1455 {
1456 rBuffer.append(':');
1457 makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1458 }
1459 }
1460};
1461
1462struct ConventionXL_OOX : public ConventionXL_A1
1463{
1464 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { }
1465
1466 virtual void makeRefStr( ScSheetLimits& rLimits,
1467 OUStringBuffer& rBuf,
1469 const ScAddress& rPos,
1470 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1471 const ScComplexRefData& rRef,
1472 bool bSingleRef,
1473 bool bFromRangeName ) const override
1474 {
1475 // In OOXML relative references in named expressions are relative to
1476 // column 0 and row 0. Relative sheet references don't exist.
1477 ScAddress aPos( rPos );
1478 if (bFromRangeName)
1479 {
1480 // XXX NOTE: by decrementing the reference position we may end up
1481 // with resolved references with negative values. There's no proper
1482 // way to solve that or wrap them around without sheet dimensions
1483 // that are stored along. That, or blindly assume fixed dimensions
1484 // here and in import.
1485 /* TODO: maybe do that blind fixed dimensions wrap? */
1486 aPos.SetCol(0);
1487 aPos.SetRow(0);
1488 }
1489
1490 if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted()))
1491 {
1492 // For OOXML write plain "#REF!" instead of detailed sheet/col/row
1493 // information.
1494 rBuf.append(rErrRef);
1495 return;
1496 }
1497
1498 {
1499 ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos);
1500 if (!rLimits.ValidAddress(aAbs1)
1501 || o3tl::make_unsigned(aAbs1.Tab()) >= rTabNames.size())
1502 {
1503 rBuf.append(rErrRef);
1504 return;
1505 }
1506 }
1507
1508 if (!bSingleRef)
1509 {
1510 ScAddress aAbs2 = rRef.Ref2.toAbs(rLimits, rPos);
1511 if (!rLimits.ValidAddress(aAbs2)
1512 || o3tl::make_unsigned(aAbs2.Tab()) >= rTabNames.size())
1513 {
1514 rBuf.append(rErrRef);
1515 return;
1516 }
1517 }
1518
1519 ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName);
1520 }
1521
1522 virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/,
1523 const OUString& rName ) const override
1524 {
1525 // [N]!DefinedName is a workbook global name.
1526 return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName );
1527
1528 /* TODO: add support for sheet local names, would be
1529 * [N]'Sheet Name'!DefinedName
1530 * Similar to makeExternalRefStr() but with DefinedName instead of
1531 * CellStr. */
1532 }
1533
1534 virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const override
1535 {
1536 sal_Int32 nLen = rFormula.getLength();
1537 const sal_Unicode* p = rFormula.getStr();
1538 for (sal_Int32 i = rSrcPos; i < nLen; ++i)
1539 {
1540 sal_Unicode c = p[i];
1541 if (i == rSrcPos)
1542 {
1543 // first character must be '['.
1544 if (c != '[')
1545 return;
1546 }
1547 else if (c == ']')
1548 {
1549 rSrcPos = i + 1;
1550 return;
1551 }
1552 }
1553 }
1554
1555 virtual void makeExternalRefStr(
1556 ScSheetLimits& rLimits,
1557 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1558 const OUString& rTabName, const ScSingleRefData& rRef ) const override
1559 {
1560 // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1
1561 // Where N is a 1-based positive integer number of a file name in OOXML
1562 // xl/externalLinks/externalLinkN.xml
1563
1564 OUString aQuotedTab( rTabName);
1565 ScCompiler::CheckTabQuotes( aQuotedTab);
1566 if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'')
1567 {
1568 rBuffer.append('\'');
1569 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1570 rBuffer.append( aQuotedTab.subView(1));
1571 }
1572 else
1573 {
1574 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1575 rBuffer.append( aQuotedTab);
1576 }
1577 rBuffer.append('!');
1578
1579 makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos));
1580 }
1581
1582 virtual void makeExternalRefStr(
1583 ScSheetLimits& rLimits,
1584 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/,
1585 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1586 const ScComplexRefData& rRef ) const override
1587 {
1588 // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2
1589 // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the
1590 // simpler to produce and more logical form with independently quoted
1591 // sheet names as well. The [N] having to be within the quoted sheet
1592 // name is ugly enough...
1593
1594 ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1595
1596 OUStringBuffer aBuf;
1597 ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef);
1598 if (!aBuf.isEmpty() && aBuf[0] == '\'')
1599 {
1600 rBuffer.append('\'');
1601 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1602 rBuffer.append( aBuf.subView(1));
1603 }
1604 else
1605 {
1606 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId);
1607 rBuffer.append( aBuf);
1608 }
1609 rBuffer.append('!');
1610
1611 makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart);
1612 if (aAbsRef.aStart != aAbsRef.aEnd)
1613 {
1614 rBuffer.append(':');
1615 makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd);
1616 }
1617 }
1618
1619 static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId )
1620 {
1621 rBuffer.append('[').append( static_cast<sal_Int32>(nFileId+1) ).append(']');
1622 }
1623};
1624
1625}
1626
1627static void
1628r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1629{
1630 rBuf.append( 'C' );
1631 if( rRef.IsColRel() )
1632 {
1633 SCCOL nCol = rRef.Col();
1634 if (nCol != 0)
1635 rBuf.append("[" + OUString::number(nCol) + "]");
1636 }
1637 else
1638 rBuf.append( static_cast<sal_Int32>(rAbsRef.Col() + 1) );
1639}
1640static void
1641r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef )
1642{
1643 rBuf.append( 'R' );
1644 if( rRef.IsRowRel() )
1645 {
1646 if (rRef.Row() != 0)
1647 {
1648 rBuf.append("[" + OUString::number(rRef.Row()) + "]");
1649 }
1650 }
1651 else
1652 rBuf.append( rAbsRef.Row() + 1 );
1653}
1654
1655namespace {
1656
1657struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL
1658{
1659 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { }
1660
1661 virtual void makeRefStr( ScSheetLimits& rLimits,
1662 OUStringBuffer& rBuf,
1664 const ScAddress& rPos,
1665 const OUString& rErrRef, const std::vector<OUString>& rTabNames,
1666 const ScComplexRefData& rRef,
1667 bool bSingleRef,
1668 bool /*bFromRangeName*/ ) const override
1669 {
1670 ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1671 ScComplexRefData aRef( rRef );
1672
1673 MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef);
1674
1675 // Play fast and loose with invalid refs. There is not much point in producing
1676 // Foo!A1:#REF! versus #REF! at this point
1677 if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row()))
1678 {
1679 rBuf.append(rErrRef);
1680 return;
1681 }
1682
1683 if( !bSingleRef )
1684 {
1685 if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1686 {
1687 rBuf.append(rErrRef);
1688 return;
1689 }
1690
1691 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1692 {
1693 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1694 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() ||
1695 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() )
1696 {
1697 rBuf.append( ':' );
1698 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1699 }
1700 return;
1701
1702 }
1703
1704 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1705 {
1706 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1707 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() ||
1708 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1709 {
1710 rBuf.append( ':' );
1711 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1712 }
1713 return;
1714 }
1715 }
1716
1717 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart);
1718 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart);
1719 if (!bSingleRef)
1720 {
1721 rBuf.append( ':' );
1722 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd);
1723 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd);
1724 }
1725 }
1726
1727 ParseResult parseAnyToken( const OUString& rFormula,
1728 sal_Int32 nSrcPos,
1729 const CharClass* pCharClass,
1730 bool bGroupSeparator) const override
1731 {
1732 parseExternalDocName(rFormula, nSrcPos);
1733
1734 ParseResult aRet;
1735 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) )
1736 return aRet;
1737
1738 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER |
1739 KParseTokens::ASC_UNDERSCORE ;
1740 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT;
1741 // '?' allowed in range names
1742 static constexpr OUStringLiteral aAddAllowed(u"?-[]!");
1743
1744 return pCharClass->parseAnyToken( rFormula,
1745 nSrcPos, nStartFlags, aAddAllowed,
1746 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags),
1747 aAddAllowed );
1748 }
1749
1750 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override
1751 {
1752 return ConventionXL::getSpecialSymbol(eSymType);
1753 }
1754
1755 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName,
1756 const ScDocument& rDoc,
1757 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override
1758 {
1759 return ConventionXL::parseExternalName( rSymbol, rFile, rName, rDoc, pExternalLinks);
1760 }
1761
1762 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile,
1763 const OUString& rName ) const override
1764 {
1765 return ConventionXL::makeExternalNameStr(rFile, rName);
1766 }
1767
1768 virtual void makeExternalRefStr(
1769 ScSheetLimits& rLimits,
1770 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1771 const OUString& rTabName, const ScSingleRefData& rRef ) const override
1772 {
1773 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1
1774 // This is a little different from the format Excel uses, as Excel
1775 // puts [] only around the file name. But we need to enclose the
1776 // whole file path with [] because the file name can contain any
1777 // characters.
1778
1779 ScAddress aAbsRef = rRef.toAbs(rLimits, rPos);
1780 ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1781 ScRangeStringConverter::AppendTableName(rBuffer, rTabName);
1782 rBuffer.append('!');
1783
1784 r1c1_add_row(rBuffer, rRef, aAbsRef);
1785 r1c1_add_col(rBuffer, rRef, aAbsRef);
1786 }
1787
1788 virtual void makeExternalRefStr(
1789 ScSheetLimits& rLimits,
1790 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName,
1791 const std::vector<OUString>& rTabNames, const OUString& rTabName,
1792 const ScComplexRefData& rRef ) const override
1793 {
1794 ScRange aAbsRef = rRef.toAbs(rLimits, rPos);
1795
1796 ConventionXL::makeExternalDocStr(rBuffer, rFileName);
1797 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef);
1798 rBuffer.append('!');
1799
1800 if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row()))
1801 {
1802 rBuffer.append(ScResId(STR_NO_REF_TABLE));
1803 return;
1804 }
1805
1806 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol)
1807 {
1808 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1809 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel())
1810 {
1811 rBuffer.append(':');
1812 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1813 }
1814 return;
1815 }
1816
1817 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow)
1818 {
1819 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1820 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel())
1821 {
1822 rBuffer.append(':');
1823 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1824 }
1825 return;
1826 }
1827
1828 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart);
1829 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart);
1830 rBuffer.append(':');
1831 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1832 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd);
1833 }
1834
1835 virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const override
1836 {
1837 ScCharFlags nFlags = mpCharTable[static_cast<sal_uInt8>(c)];
1838 if (c == '-' && cLast == '[')
1839 // '-' can occur within a reference string only after '[' e.g. R[-1]C.
1840 nFlags |= ScCharFlags::Ident;
1841 return nFlags;
1842 }
1843};
1844
1845}
1846
1848 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1849 : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1850 rDoc(rCxt.getDoc()),
1851 aPos(rPos),
1852 mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1853 mpInterpreterContext(pContext),
1857 mbCharClassesDiffer(false),
1862 mbCloseBrackets(true),
1863 mbRewind(false),
1865 maTabNames(rCxt.getTabNames())
1866{
1867 SetGrammar(rCxt.getGrammar());
1868}
1869
1872 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1873 : FormulaCompiler(rArr, bComputeII, bMatrixFlag),
1874 rDoc( rDocument ),
1875 aPos( rPos ),
1876 mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1877 mpInterpreterContext(pContext),
1878 mnCurrentSheetTab(-1),
1879 mnCurrentSheetEndPos(0),
1880 nSrcPos(0),
1881 pCharClass( &ScGlobal::getCharClass() ),
1882 mbCharClassesDiffer(false),
1883 mnPredetectedReference(0),
1884 mnRangeOpPosInSymbol(-1),
1885 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1886 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1887 mbCloseBrackets( true ),
1888 mbRewind( false ),
1889 mbRefConventionChartOOXML( false )
1890{
1892 rDocument.GetGrammar() :
1893 eGrammar );
1894}
1895
1897 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1898 : FormulaCompiler(bComputeII, bMatrixFlag),
1899 rDoc(rCxt.getDoc()),
1900 aPos(rPos),
1901 mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1902 mpInterpreterContext(pContext),
1903 mnCurrentSheetTab(-1),
1904 mnCurrentSheetEndPos(0),
1905 pCharClass(&ScGlobal::getCharClass()),
1906 mbCharClassesDiffer(false),
1907 mnPredetectedReference(0),
1908 mnRangeOpPosInSymbol(-1),
1909 pConv(GetRefConvention(FormulaGrammar::CONV_OOO)),
1910 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE),
1911 mbCloseBrackets(true),
1912 mbRewind(false),
1913 mbRefConventionChartOOXML(false),
1914 maTabNames(rCxt.getTabNames())
1915{
1916 SetGrammar(rCxt.getGrammar());
1917}
1918
1921 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext )
1922 : FormulaCompiler(bComputeII, bMatrixFlag),
1923 rDoc( rDocument ),
1924 aPos( rPos ),
1925 mpFormatter(pContext ? pContext->GetFormatTable() : rDoc.GetFormatTable()),
1926 mpInterpreterContext(pContext),
1927 mnCurrentSheetTab(-1),
1928 mnCurrentSheetEndPos(0),
1929 nSrcPos(0),
1930 pCharClass( &ScGlobal::getCharClass() ),
1931 mbCharClassesDiffer(false),
1932 mnPredetectedReference(0),
1933 mnRangeOpPosInSymbol(-1),
1934 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ),
1935 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ),
1936 mbCloseBrackets( true ),
1937 mbRewind( false ),
1938 mbRefConventionChartOOXML( false )
1939{
1941 rDocument.GetGrammar() :
1942 eGrammar );
1943}
1944
1946{
1947}
1948
1949void ScCompiler::CheckTabQuotes( OUString& rString,
1951{
1952 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE;
1953 sal_Int32 nContFlags = nStartFlags;
1954 ParseResult aRes = ScGlobal::getCharClass().parsePredefinedToken(
1955 KParseType::IDENTNAME, rString, 0, nStartFlags, OUString(), nContFlags, OUString());
1956 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength());
1957
1958 switch ( eConv )
1959 {
1960 default :
1961 case FormulaGrammar::CONV_UNSPECIFIED :
1962 break;
1963 case FormulaGrammar::CONV_OOO :
1964 case FormulaGrammar::CONV_XL_A1 :
1965 case FormulaGrammar::CONV_XL_R1C1 :
1966 case FormulaGrammar::CONV_XL_OOX :
1967 case FormulaGrammar::CONV_ODF :
1968 if( bNeedsQuote )
1969 {
1970 // escape embedded quotes
1971 rString = rString.replaceAll( "'", "''" );
1972 }
1973 break;
1974 }
1975
1976 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) )
1977 {
1978 // Prevent any possible confusion resulting from pure numeric sheet names.
1979 bNeedsQuote = true;
1980 }
1981
1982 if( bNeedsQuote )
1983 {
1984 rString = "'" + rString + "'";
1985 }
1986}
1987
1988sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString )
1989{
1990 if (rString[0] != '\'')
1991 return -1;
1993 // it must be 'Doc'#
1994 if (nPos != -1 && rString[nPos-1] != '\'')
1995 nPos = -1;
1996 return nPos;
1997}
1998
2000{
2001 const Convention* p = GetRefConvention(eConv);
2002 if (p)
2004}
2005
2007{
2008
2009 switch (eConv)
2010 {
2011 case FormulaGrammar::CONV_OOO:
2012 {
2013 static const ConventionOOO_A1 ConvOOO_A1;
2014 return &ConvOOO_A1;
2015 }
2016 case FormulaGrammar::CONV_ODF:
2017 {
2018 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF;
2019 return &ConvOOO_A1_ODF;
2020 }
2021 case FormulaGrammar::CONV_XL_A1:
2022 {
2023 static const ConventionXL_A1 ConvXL_A1;
2024 return &ConvXL_A1;
2025 }
2026 case FormulaGrammar::CONV_XL_R1C1:
2027 {
2028 static const ConventionXL_R1C1 ConvXL_R1C1;
2029 return &ConvXL_R1C1;
2030 }
2031 case FormulaGrammar::CONV_XL_OOX:
2032 {
2033 static const ConventionXL_OOX ConvXL_OOX;
2034 return &ConvXL_OOX;
2035 }
2036 case FormulaGrammar::CONV_UNSPECIFIED:
2037 default:
2038 ;
2039 }
2040
2041 return nullptr;
2042}
2043
2045{
2046 pConv = pConvP;
2047 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv);
2048 assert( FormulaGrammar::isSupported( meGrammar));
2049}
2050
2052{
2053 if( pArr->GetCodeError() == FormulaError::NONE)
2054 pArr->SetCodeError( nError);
2055}
2056
2057static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax )
2058{
2059 const sal_Unicode* const pStop = pDst + nMax;
2060 while ( pDst < pStop )
2061 {
2062 *pDst++ = *pSrc++;
2063 }
2064 *pDst = 0;
2065 return pDst;
2066}
2067
2068// p1 MUST contain at least n characters, or terminate with NIL.
2069// p2 MUST pass upper case letters, if any.
2070// n MUST not be greater than length of p2
2071static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n )
2072{
2073 for (size_t i=0; i<n; ++i)
2074 {
2075 if (!p1[i])
2076 return false;
2077 if (p1[i] != p2[i])
2078 {
2079 if (p1[i] < 'a' || 'z' < p1[i])
2080 return false; // not a lower case letter
2081 if (p2[i] < 'A' || 'Z' < p2[i])
2082 return false; // not a letter to match
2083 if (p1[i] != p2[i] + 0x20)
2084 return false; // lower case doesn't match either
2085 }
2086 }
2087 return true;
2088}
2089
2090// static
2091void ScCompiler::addWhitespace( std::vector<ScCompiler::Whitespace> & rvSpaces,
2092 ScCompiler::Whitespace & rSpace, sal_Unicode c, sal_Int32 n )
2093{
2094 if (rSpace.cChar != c)
2095 {
2096 if (rSpace.cChar && rSpace.nCount > 0)
2097 rvSpaces.emplace_back(rSpace);
2098 rSpace.reset(c);
2099 }
2100 rSpace.nCount += n;
2101}
2102
2103// NextSymbol
2104
2105// Parses the formula into separate symbols for further processing.
2106// XXX NOTE: this is a rough sketch of the original idea, there are other
2107// states that were added and didn't make it into this table and things are
2108// more complicated. Use the source, Luke.
2109
2110// initial state = GetChar
2111
2112// old state | read character | action | new state
2113//---------------+-------------------+-----------------------+---------------
2114// GetChar | ;()+-*/^=& | Symbol=char | Stop
2115// | <> | Symbol=char | GetBool
2116// | $ letter | Symbol=char | GetWord
2117// | number | Symbol=char | GetValue
2118// | " | none | GetString
2119// | other | none | GetChar
2120//---------------+-------------------+-----------------------+---------------
2121// GetBool | => | Symbol=Symbol+char | Stop
2122// | other | Dec(CharPos) | Stop
2123//---------------+-------------------+-----------------------+---------------
2124// GetWord | SepSymbol | Dec(CharPos) | Stop
2125// | ()+-*/^=<>&~ | |
2126// | space | Dec(CharPos) | Stop
2127// | $_:. | |
2128// | letter, number | Symbol=Symbol+char | GetWord
2129// | other | error | Stop
2130//---------------+-------------------+-----------------------+---------------
2131// GetValue | ;()*/^=<>& | |
2132// | space | Dec(CharPos) | Stop
2133// | number E+-%,. | Symbol=Symbol+char | GetValue
2134// | other | error | Stop
2135//---------------+-------------------+-----------------------+---------------
2136// GetString | " | none | Stop
2137// | other | Symbol=Symbol+char | GetString
2138//---------------+-------------------+-----------------------+---------------
2139
2140std::vector<ScCompiler::Whitespace> ScCompiler::NextSymbol(bool bInArray)
2141{
2142 std::vector<Whitespace> vSpaces;
2143 cSymbol[MAXSTRLEN] = 0; // end
2144 sal_Unicode* pSym = cSymbol;
2145 const sal_Unicode* const pStart = aFormula.getStr();
2146 const sal_Unicode* pSrc = pStart + nSrcPos;
2147 bool bi18n = false;
2148 sal_Unicode c = *pSrc;
2149 sal_Unicode cLast = 0;
2150 bool bQuote = false;
2152 ScanState eState = ssGetChar;
2153 Whitespace aSpace;
2154 sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep);
2155 sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep);
2156 sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep);
2157 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0]);
2158 sal_Unicode cDecSepAlt = (mxSymbols->isEnglish() ? 0 : ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar());
2159
2160 // special symbols specific to address convention used
2163
2164 int nDecSeps = 0;
2165 bool bAutoIntersection = false;
2166 size_t nAutoIntersectionSpacesPos = 0;
2167 int nRefInName = 0;
2168 bool bErrorConstantHadSlash = false;
2170 // try to parse simple tokens before calling i18n parser
2171 while ((c != 0) && (eState != ssStop) )
2172 {
2173 pSrc++;
2174 ScCharFlags nMask = GetCharTableFlags( c, cLast );
2175
2176 // The parameter separator and the array column and row separators end
2177 // things unconditionally if not in string or reference.
2178 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep)))
2179 {
2180 switch (eState)
2181 {
2182 // these are to be continued
2183 case ssGetString:
2184 case ssSkipString:
2185 case ssGetReference:
2186 case ssSkipReference:
2187 case ssGetTableRefItem:
2188 case ssGetTableRefColumn:
2189 break;
2190 default:
2191 if (eState == ssGetChar)
2192 *pSym++ = c;
2193 else
2194 pSrc--;
2195 eState = ssStop;
2196 }
2197 }
2198Label_MaskStateMachine:
2199 switch (eState)
2200 {
2201 case ssGetChar :
2202 {
2203 // Order is important!
2204 if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']')
2205 {
2206 *pSym++ = c;
2207 eState = ssGetTableRefColumn;
2208 }
2209 else if( nMask & ScCharFlags::OdfLabelOp )
2210 {
2211 // '!!' automatic intersection
2212 if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp)
2213 {
2214 /* TODO: For now the UI "space operator" is used, this
2215 * could be enhanced using a specialized OpCode to get
2216 * rid of the space ambiguity, which would need some
2217 * places to be adapted though. And we would still need
2218 * to support the ambiguous space operator for UI
2219 * purposes anyway. However, we then could check for
2220 * invalid usage of '!!', which currently isn't
2221 * possible. */
2222 if (!bAutoIntersection)
2223 {
2224 ++pSrc;
2225 // Add 2 because it must match the character count
2226 // for bi18n.
2227 addWhitespace( vSpaces, aSpace, 0x20, 2);
2228 // Position of Whitespace where it will be added to
2229 // vector.
2230 nAutoIntersectionSpacesPos = vSpaces.size();
2231 bAutoIntersection = true;
2232 }
2233 else
2234 {
2235 pSrc--;
2236 eState = ssStop;
2237 }
2238 }
2239 else
2240 {
2241 nMask &= ~ScCharFlags::OdfLabelOp;
2242 goto Label_MaskStateMachine;
2243 }
2244 }
2245 else if( nMask & ScCharFlags::OdfNameMarker )
2246 {
2247 // '$$' defined name marker
2249 {
2250 // both eaten, not added to pSym
2251 ++pSrc;
2252 }
2253 else
2254 {
2256 goto Label_MaskStateMachine;
2257 }
2258 }
2259 else if( nMask & ScCharFlags::Char )
2260 {
2261 // '[' is a special case in Excel syntax, it can start an
2262 // external reference, ID in OOXML like [1]Sheet1!A1 or
2263 // Excel_A1 [filename]Sheet!A1 or Excel_R1C1
2264 // [filename]Sheet!R1C1 that needs to be scanned
2265 // entirely, or can be ocTableRefOpen, of which the first
2266 // transforms an ocDBArea into an ocTableRef.
2267 if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar)
2268 && eLastOp != ocDBArea && maTableRefs.empty())
2269 {
2270 // [0]!Global_Range_Name, is a special case in OOXML
2271 // syntax, where the '0' is referencing to self and we
2272 // do not need it, so we should skip it, in order to
2273 // later it will be more recognisable for IsNamedRange.
2274 if (FormulaGrammar::isRefConventionOOXML(meGrammar) &&
2275 pSrc[0] == '0' && pSrc[1] == ']' && pSrc[2] == '!')
2276 {
2277 pSrc += 3;
2278 c = *pSrc;
2279 continue;
2280 }
2281
2282 nMask &= ~ScCharFlags::Char;
2283 goto Label_MaskStateMachine;
2284 }
2285 else
2286 {
2287 *pSym++ = c;
2288 eState = ssStop;
2289 }
2290 }
2291 else if( nMask & ScCharFlags::OdfLBracket )
2292 {
2293 // eaten, not added to pSym
2294 eState = ssGetReference;
2296 }
2297 else if( nMask & ScCharFlags::CharBool )
2298 {
2299 *pSym++ = c;
2300 eState = ssGetBool;
2301 }
2302 else if( nMask & ScCharFlags::CharValue )
2303 {
2304 *pSym++ = c;
2305 eState = ssGetValue;
2306 }
2307 else if( nMask & ScCharFlags::CharString )
2308 {
2309 *pSym++ = c;
2310 eState = ssGetString;
2311 }
2312 else if( nMask & ScCharFlags::CharErrConst )
2313 {
2314 *pSym++ = c;
2315 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
2316 eState = ssGetTableRefItem;
2317 else
2318 eState = ssGetErrorConstant;
2319 }
2320 else if( nMask & ScCharFlags::CharDontCare )
2321 {
2322 addWhitespace( vSpaces, aSpace, c);
2323 }
2324 else if( nMask & ScCharFlags::CharIdent )
2325 { // try to get a simple ASCII identifier before calling
2326 // i18n, to gain performance during import
2327 *pSym++ = c;
2328 eState = ssGetIdent;
2329 }
2330 else
2331 {
2332 bi18n = true;
2333 eState = ssStop;
2334 }
2335 }
2336 break;
2337 case ssGetIdent:
2338 {
2339 if ( nMask & ScCharFlags::Ident )
2340 { // This catches also $Sheet1.A$1, for example.
2341 if( pSym == &cSymbol[ MAXSTRLEN ] )
2342 {
2343 SetError(FormulaError::StringOverflow);
2344 eState = ssStop;
2345 }
2346 else
2347 *pSym++ = c;
2348 }
2349 else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4))
2350 {
2351 // Completely ugly means to catch broken
2352 // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts)
2353 // references that were written in ODF named ranges
2354 // (without embracing [] hence no predetected reference)
2355 // and to OOXML and handle them as one symbol.
2356 // Also catches these in UI, so we can process them
2357 // further.
2358 int i = 0;
2359 for ( ; i<5; ++i)
2360 {
2361 if( pSym == &cSymbol[ MAXSTRLEN ] )
2362 {
2363 SetError(FormulaError::StringOverflow);
2364 eState = ssStop;
2365 break; // for
2366 }
2367 else
2368 {
2369 *pSym++ = c;
2370 c = *pSrc++;
2371 }
2372 }
2373 if (i == 5)
2374 c = *((--pSrc)-1); // position last/next character correctly
2375 }
2376 else if (c == ':' && mnRangeOpPosInSymbol < 0)
2377 {
2378 // One range operator may form Sheet1.A:A, which we need to
2379 // pass as one entity to IsReference().
2380 if( pSym == &cSymbol[ MAXSTRLEN ] )
2381 {
2382 SetError(FormulaError::StringOverflow);
2383 eState = ssStop;
2384 }
2385 else
2386 {
2387 mnRangeOpPosInSymbol = pSym - &cSymbol[0];
2388 *pSym++ = c;
2389 }
2390 }
2391 else if ( 128 <= c || '\'' == c )
2392 { // High values need reparsing with i18n,
2393 // single quoted $'sheet' names too (otherwise we'd had to
2394 // implement everything twice).
2395 bi18n = true;
2396 eState = ssStop;
2397 }
2398 else
2399 {
2400 pSrc--;
2401 eState = ssStop;
2402 }
2403 }
2404 break;
2405 case ssGetBool :
2406 {
2407 if( nMask & ScCharFlags::Bool )
2408 {
2409 *pSym++ = c;
2410 eState = ssStop;
2411 }
2412 else
2413 {
2414 pSrc--;
2415 eState = ssStop;
2416 }
2417 }
2418 break;
2419 case ssGetValue :
2420 {
2421 if( pSym == &cSymbol[ MAXSTRLEN ] )
2422 {
2423 SetError(FormulaError::StringOverflow);
2424 eState = ssStop;
2425 }
2426 else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt))
2427 {
2428 if (++nDecSeps > 1)
2429 {
2430 // reparse with i18n, may be numeric sheet name as well
2431 bi18n = true;
2432 eState = ssStop;
2433 }
2434 else
2435 *pSym++ = c;
2436 }
2437 else if( nMask & ScCharFlags::Value )
2438 *pSym++ = c;
2439 else if( nMask & ScCharFlags::ValueSep )
2440 {
2441 pSrc--;
2442 eState = ssStop;
2443 }
2444 else if (c == 'E' || c == 'e')
2445 {
2446 if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp)
2447 *pSym++ = c;
2448 else
2449 {
2450 // reparse with i18n
2451 bi18n = true;
2452 eState = ssStop;
2453 }
2454 }
2455 else if( nMask & ScCharFlags::ValueSign )
2456 {
2457 if (((cLast == 'E') || (cLast == 'e')) &&
2459 {
2460 *pSym++ = c;
2461 }
2462 else
2463 {
2464 pSrc--;
2465 eState = ssStop;
2466 }
2467 }
2468 else
2469 {
2470 // reparse with i18n
2471 bi18n = true;
2472 eState = ssStop;
2473 }
2474 }
2475 break;
2476 case ssGetString :
2477 {
2478 if( nMask & ScCharFlags::StringSep )
2479 {
2480 if ( !bQuote )
2481 {
2482 if ( *pSrc == '"' )
2483 bQuote = true; // "" => literal "
2484 else
2485 eState = ssStop;
2486 }
2487 else
2488 bQuote = false;
2489 }
2490 if ( !bQuote )
2491 {
2492 if( pSym == &cSymbol[ MAXSTRLEN ] )
2493 {
2494 SetError(FormulaError::StringOverflow);
2495 eState = ssSkipString;
2496 }
2497 else
2498 *pSym++ = c;
2499 }
2500 }
2501 break;
2502 case ssSkipString:
2503 if( nMask & ScCharFlags::StringSep )
2504 eState = ssStop;
2505 break;
2506 case ssGetErrorConstant:
2507 {
2508 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
2509 // BUT, in UI these may have been translated! So don't
2510 // check for ASCII alnum. Note that this construct can't be
2511 // parsed with i18n.
2512 /* TODO: be strict when reading ODFF, check for ASCII alnum
2513 * and proper continuation after '/'. However, even with
2514 * the lax parsing only the error constants we have defined
2515 * as opcode symbols will be recognized and others result
2516 * in ocBad, so the result is actually conformant. */
2517 bool bAdd = true;
2518 if ('?' == c)
2519 eState = ssStop;
2520 else if ('!' == c)
2521 {
2522 // Check if this is #REF! that starts an invalid reference.
2523 // Note we have an implicit '!' here at the end.
2524 if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) &&
2526 eState = ssGetIdent;
2527 else
2528 eState = ssStop;
2529 }
2530 else if ('/' == c)
2531 {
2532 if (!bErrorConstantHadSlash)
2533 bErrorConstantHadSlash = true;
2534 else
2535 {
2536 bAdd = false;
2537 eState = ssStop;
2538 }
2539 }
2540 else if ((nMask & ScCharFlags::WordSep) ||
2541 (c < 128 && !rtl::isAsciiAlphanumeric( c)))
2542 {
2543 bAdd = false;
2544 eState = ssStop;
2545 }
2546 if (!bAdd)
2547 --pSrc;
2548 else
2549 {
2550 if (pSym == &cSymbol[ MAXSTRLEN ])
2551 {
2552 SetError( FormulaError::StringOverflow);
2553 eState = ssStop;
2554 }
2555 else
2556 *pSym++ = c;
2557 }
2558 }
2559 break;
2560 case ssGetTableRefItem:
2561 {
2562 // Scan whatever up to the next ']' closer.
2563 if (c != ']')
2564 {
2565 if( pSym == &cSymbol[ MAXSTRLEN ] )
2566 {
2567 SetError( FormulaError::StringOverflow);
2568 eState = ssStop;
2569 }
2570 else
2571 *pSym++ = c;
2572 }
2573 else
2574 {
2575 --pSrc;
2576 eState = ssStop;
2577 }
2578 }
2579 break;
2580 case ssGetTableRefColumn:
2581 {
2582 // Scan whatever up to the next unescaped ']' closer.
2583 if (c != ']' || cLast == '\'')
2584 {
2585 if( pSym == &cSymbol[ MAXSTRLEN ] )
2586 {
2587 SetError( FormulaError::StringOverflow);
2588 eState = ssStop;
2589 }
2590 else
2591 *pSym++ = c;
2592 }
2593 else
2594 {
2595 --pSrc;
2596 eState = ssStop;
2597 }
2598 }
2599 break;
2600 case ssGetReference:
2601 if( pSym == &cSymbol[ MAXSTRLEN ] )
2602 {
2603 SetError( FormulaError::StringOverflow);
2604 eState = ssSkipReference;
2605 }
2606 [[fallthrough]];
2607 case ssSkipReference:
2608 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
2609 // mandatory also if no sheet name. 'External'# is optional,
2610 // sheet name is optional, quotes around sheet name are
2611 // optional if no quote contained. [#REF!] is valid.
2612 // 2nd usage: ['Sheet'.$$'DefinedName']
2613 // 3rd usage: ['External'#$$'DefinedName']
2614 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
2615 // Also for all these names quotes are optional if no quote
2616 // contained.
2617 {
2618
2619 // nRefInName: 0 := not in sheet name yet. 'External'
2620 // is parsed as if it was a sheet name and nRefInName
2621 // is reset when # is encountered immediately after closing
2622 // quote. Same with 'DefinedName', nRefInName is cleared
2623 // when : is encountered.
2624
2625 // Encountered leading $ before sheet name.
2626 constexpr int kDollar = (1 << 1);
2627 // Encountered ' opening quote, which may be after $ or
2628 // not.
2629 constexpr int kOpen = (1 << 2);
2630 // Somewhere in name.
2631 constexpr int kName = (1 << 3);
2632 // Encountered ' in name, will be cleared if double or
2633 // transformed to kClose if not, in which case kOpen is
2634 // cleared.
2635 constexpr int kQuote = (1 << 4);
2636 // Past ' closing quote.
2637 constexpr int kClose = (1 << 5);
2638 // Encountered # file/sheet separator.
2639 constexpr int kFileSep = (1 << 6);
2640 // Past . sheet name separator.
2641 constexpr int kPast = (1 << 7);
2642 // Marked name $$ follows sheet name separator, detected
2643 // while we're still on the separator. Will be cleared when
2644 // entering the name.
2645 constexpr int kMarkAhead = (1 << 8);
2646 // In marked defined name.
2647 constexpr int kDefName = (1 << 9);
2648 // Encountered # of #REF!
2649 constexpr int kRefErr = (1 << 10);
2650
2651 bool bAddToSymbol = true;
2652 if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen))
2653 {
2654 OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
2655 "ScCompiler::NextSymbol: reference: "
2656 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
2657 // eaten, not added to pSym
2658 bAddToSymbol = false;
2659 eState = ssStop;
2660 }
2661 else if (cSheetSep == c && nRefInName == 0)
2662 {
2663 // eat it, no sheet name [.A1]
2664 bAddToSymbol = false;
2665 nRefInName |= kPast;
2666 if ('$' == pSrc[0] && '$' == pSrc[1])
2667 nRefInName |= kMarkAhead;
2668 }
2669 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName)))
2670 {
2671 // Not in col/row yet.
2672
2673 if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep))
2674 nRefInName = 0;
2675 else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen))
2676 {
2677 nRefInName &= ~kMarkAhead;
2678 if (!(nRefInName & kDefName))
2679 {
2680 // eaten, not added to pSym (2 chars)
2681 bAddToSymbol = false;
2682 ++pSrc;
2683 nRefInName &= kPast;
2684 nRefInName |= kDefName;
2685 }
2686 else
2687 {
2688 // ScAddress::Parse() will recognize this as
2689 // invalid later.
2690 if (eState != ssSkipReference)
2691 {
2692 *pSym++ = c;
2693
2694 if( pSym == &cSymbol[ MAXSTRLEN ] )
2695 {
2696 SetError( FormulaError::StringOverflow);
2697 eState = ssStop;
2698 }
2699 else
2700 *pSym++ = *pSrc++;
2701 }
2702 bAddToSymbol = false;
2703 }
2704 }
2705 else if (cSheetPrefix == c && nRefInName == 0)
2706 nRefInName |= kDollar;
2707 else if ('\'' == c)
2708 {
2709 // TODO: The conventions' parseExternalName()
2710 // should handle quoted names, but as long as they
2711 // don't remove non-embedded quotes here.
2712 if (!(nRefInName & kName))
2713 {
2714 nRefInName |= (kOpen | kName);
2715 bAddToSymbol = !(nRefInName & kDefName);
2716 }
2717 else if (!(nRefInName & kOpen))
2718 {
2719 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2720 "a ''' without the name being enclosed in '...' violates ODF spec");
2721 }
2722 else if (nRefInName & kQuote)
2723 {
2724 // escaped embedded quote
2725 nRefInName &= ~kQuote;
2726 }
2727 else
2728 {
2729 switch (pSrc[0])
2730 {
2731 case '\'':
2732 // escapes embedded quote
2733 nRefInName |= kQuote;
2734 break;
2736 // sheet name should follow
2737 nRefInName |= kFileSep;
2738 [[fallthrough]];
2739 default:
2740 // quote not followed by quote => close
2741 nRefInName |= kClose;
2742 nRefInName &= ~kOpen;
2743 }
2744 bAddToSymbol = !(nRefInName & kDefName);
2745 }
2746 }
2747 else if ('#' == c && nRefInName == 0)
2748 nRefInName |= kRefErr;
2749 else if (cSheetSep == c && !(nRefInName & kOpen))
2750 {
2751 // unquoted sheet name separator
2752 nRefInName |= kPast;
2753 if ('$' == pSrc[0] && '$' == pSrc[1])
2754 nRefInName |= kMarkAhead;
2755 }
2756 else if (':' == c && !(nRefInName & kOpen))
2757 {
2758 OSL_FAIL("ScCompiler::NextSymbol: reference: "
2759 "range operator ':' without prior sheet name separator '.' violates ODF spec");
2760 nRefInName = 0;
2762 }
2763 else if (!(nRefInName & kName))
2764 {
2765 // start unquoted name
2766 nRefInName |= kName;
2767 }
2768 }
2769 else if (':' == c)
2770 {
2771 // range operator
2772 nRefInName = 0;
2774 }
2775 if (bAddToSymbol && eState != ssSkipReference)
2776 *pSym++ = c; // everything is part of reference
2777 }
2778 break;
2779 case ssStop:
2780 ; // nothing, prevent warning
2781 break;
2782 }
2783 cLast = c;
2784 c = *pSrc;
2785 }
2786
2787 if (aSpace.nCount && aSpace.cChar)
2788 vSpaces.emplace_back(aSpace);
2789
2790 if ( bi18n )
2791 {
2792 const sal_Int32 nOldSrcPos = nSrcPos;
2793 for (const auto& r : vSpaces)
2794 nSrcPos += r.nCount;
2795 // If group separator is not a possible operator and not one of any
2796 // separators then it may be parsed away in numbers. This is
2797 // specifically the case with NO-BREAK SPACE, which actually triggers
2798 // the bi18n case (which we don't want to include as yet another
2799 // special case above as it is rare enough and doesn't generally occur
2800 // in formulas).
2801 const sal_Unicode cGroupSep = ScGlobal::getLocaleData().getNumThousandSep()[0];
2802 const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep &&
2803 cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep &&
2804 cGroupSep != cDecSep && cGroupSep != cDecSepAlt &&
2805 cGroupSep != cSheetPrefix && cGroupSep != cSheetSep);
2806 // If a numeric context triggered bi18n then use the default locale's
2807 // CharClass, this may accept group separator as well.
2808 const CharClass* pMyCharClass = (ScGlobal::getCharClass().isDigit( OUString(pStart[nSrcPos]), 0) ?
2810 OUStringBuffer aSymbol;
2812 FormulaError nErr = FormulaError::NONE;
2813 do
2814 {
2815 bi18n = false;
2816 // special case (e.g. $'sheetname' in OOO A1)
2817 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' )
2818 aSymbol.append(pStart[nSrcPos++]);
2819
2820 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pMyCharClass, bGroupSeparator);
2821
2822 if ( !aRes.TokenType )
2823 {
2824 nErr = FormulaError::IllegalChar;
2825 SetError( nErr ); // parsed chars as string
2826 }
2827 if ( aRes.EndPos <= nSrcPos )
2828 {
2829 // Could not parse anything meaningful.
2830 assert(!aRes.TokenType);
2831 nErr = FormulaError::IllegalChar;
2832 SetError( nErr );
2833 // Caller has to act on an empty symbol for
2834 // nSrcPos < aFormula.getLength()
2835 nSrcPos = nOldSrcPos;
2836 aSymbol.setLength(0);
2837 }
2838 else
2839 {
2840 // When having parsed a second reference part, ensure that the
2841 // i18n parser did not mistakingly parse a number that included
2842 // a separator which happened to be meant as a parameter
2843 // separator instead.
2844 if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER))
2845 {
2846 for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i)
2847 {
2848 if (pStart[i] == cSep)
2849 aRes.EndPos = i; // also ends for
2850 }
2851 }
2852 aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos);
2853 nSrcPos = aRes.EndPos;
2854 c = pStart[nSrcPos];
2855 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME )
2856 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1)
2857 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP);
2858 }
2859 // One range operator restarts parsing for second reference.
2860 if (c == ':' && mnRangeOpPosInSymbol < 0)
2861 {
2862 mnRangeOpPosInSymbol = aSymbol.getLength();
2863 bi18n = true;
2864 }
2865 if ( bi18n )
2866 aSymbol.append(pStart[nSrcPos++]);
2867 }
2868 } while ( bi18n && nErr == FormulaError::NONE );
2869 sal_Int32 nLen = aSymbol.getLength();
2870 if ( nLen > MAXSTRLEN )
2871 {
2872 SetError( FormulaError::StringOverflow );
2873 nLen = MAXSTRLEN;
2874 }
2875 if (mnRangeOpPosInSymbol >= nLen)
2877 lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen );
2878 pSym = &cSymbol[nLen];
2879 }
2880 else
2881 {
2882 nSrcPos = pSrc - pStart;
2883 *pSym = 0;
2884 }
2885 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0])
2886 {
2887 // This is a trailing range operator, which is nonsense. Will be caught
2888 // in next round.
2890 *--pSym = 0;
2891 --nSrcPos;
2892 }
2893 if ( bAutoCorrect )
2894 aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol);
2895 if (bAutoIntersection && vSpaces[nAutoIntersectionSpacesPos].nCount > 1)
2896 --vSpaces[nAutoIntersectionSpacesPos].nCount; // replace '!!' with only one space
2897 return vSpaces;
2898}
2899
2900// Convert symbol to token
2901
2902bool ScCompiler::ParseOpCode( const OUString& rName, bool bInArray )
2903{
2904 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
2905 bool bFound = (iLook != mxSymbols->getHashMap().end());
2906 if (bFound)
2907 {
2908 OpCode eOp = iLook->second;
2909 if (bInArray)
2910 {
2911 if (rName == mxSymbols->getSymbol(ocArrayColSep))
2912 eOp = ocArrayColSep;
2913 else if (rName == mxSymbols->getSymbol(ocArrayRowSep))
2914 eOp = ocArrayRowSep;
2915 }
2916 else if (eOp == ocArrayColSep || eOp == ocArrayRowSep)
2917 {
2918 if (rName == mxSymbols->getSymbol(ocSep))
2919 eOp = ocSep;
2920 else if (rName == ";")
2921 {
2922 switch (FormulaGrammar::extractFormulaLanguage( meGrammar))
2923 {
2924 // Only for languages/grammars that actually use ';'
2925 // parameter separator.
2926 case css::sheet::FormulaLanguage::NATIVE:
2927 case css::sheet::FormulaLanguage::ENGLISH:
2928 case css::sheet::FormulaLanguage::ODFF:
2929 case css::sheet::FormulaLanguage::ODF_11:
2930 eOp = ocSep;
2931 }
2932 }
2933 }
2934 else if (eOp == ocCeil && mxSymbols->isOOXML())
2935 {
2936 // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is
2937 // unassigned for import.
2938 eOp = ocCeil_Math;
2939 }
2940 else if (eOp == ocFloor && mxSymbols->isOOXML())
2941 {
2942 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is
2943 // unassigned for import.
2944 eOp = ocFloor_Math;
2945 }
2946 maRawToken.SetOpCode(eOp);
2947 }
2948 else if (mxSymbols->isODFF())
2949 {
2950 // ODFF names that are not written in the current mapping but to be
2951 // recognized. New names will be written in a future release, then
2952 // exchange (!) with the names in
2953 // formula/source/core/resource/core_resource.src to be able to still
2954 // read the old names as well.
2955 struct FunctionName
2956 {
2957 const char* pName;
2958 OpCode eOp;
2959 };
2960 static const FunctionName aOdffAliases[] = {
2961 // Renamed old names, still accept them:
2962 { "B", ocB }, // B -> BINOM.DIST.RANGE
2963 { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST
2964 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY
2965 { "ZGZ", ocRRI }, // ZGZ -> RRI
2966 { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR
2967 { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK
2968 { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST
2969 { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF)
2970 // Renamed new names, prepare to read future names:
2971 //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX
2972 };
2973 for (const FunctionName& rOdffAlias : aOdffAliases)
2974 {
2975 if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName))
2976 {
2977 maRawToken.SetOpCode( rOdffAlias.eOp);
2978 bFound = true;
2979 break; // for
2980 }
2981 }
2982 }
2983 else if (mxSymbols->isOOXML())
2984 {
2985 // OOXML names that are not written in the current mapping but to be
2986 // recognized as old versions wrote them.
2987 struct FunctionName
2988 {
2989 const char* pName;
2990 OpCode eOp;
2991 };
2992 static const FunctionName aOoxmlAliases[] = {
2993 { "EFFECTIVE", ocEffect }, // EFFECTIVE -> EFFECT
2994 { "ERRORTYPE", ocErrorType }, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE
2995 { "MULTIRANGE", ocMultiArea }, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE
2996 { "GOALSEEK", ocBackSolver }, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK
2997 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY
2998 { "CURRENT", ocCurrent }, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT
2999 { "STYLE", ocStyle } // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE
3000 };
3001 for (const FunctionName& rOoxmlAlias : aOoxmlAliases)
3002 {
3003 if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName))
3004 {
3005 maRawToken.SetOpCode( rOoxmlAlias.eOp);
3006 bFound = true;
3007 break; // for
3008 }
3009 }
3010 }
3011 else if (mxSymbols->isPODF())
3012 {
3013 // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess.
3014 // We can't rename them in
3015 // formula/source/core/resource/core_resource.src but can add
3016 // additional names to be recognized here so they match the UI names if
3017 // those are renamed.
3018 struct FunctionName
3019 {
3020 const char* pName;
3021 OpCode eOp;
3022 };
3023 static const FunctionName aPodfAliases[] = {
3024 { "EFFECT", ocEffect } // EFFECTIVE -> EFFECT
3025 };
3026 for (const FunctionName& rPodfAlias : aPodfAliases)
3027 {
3028 if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName))
3029 {
3030 maRawToken.SetOpCode( rPodfAlias.eOp);
3031 bFound = true;
3032 break; // for
3033 }
3034 }
3035 }
3036
3037 if (!bFound)
3038 {
3039 OUString aIntName;
3040 if (mxSymbols->hasExternals())
3041 {
3042 // If symbols are set by filters get mapping to exact name.
3043 ExternalHashMap::const_iterator iExt(
3044 mxSymbols->getExternalHashMap().find( rName));
3045 if (iExt != mxSymbols->getExternalHashMap().end())
3046 {
3047 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second))
3048 aIntName = (*iExt).second;
3049 }
3050 }
3051 else
3052 {
3053 // Old (deprecated) addins first for legacy.
3054 if (ScGlobal::GetLegacyFuncCollection()->findByName(OUString(cSymbol)))
3055 {
3056 aIntName = cSymbol;
3057 }
3058 else
3059 // bLocalFirst=false for (English) upper full original name
3060 // (service.function)
3062 rName, !mxSymbols->isEnglish());
3063 }
3064 if (!aIntName.isEmpty())
3065 {
3066 maRawToken.SetExternal( aIntName ); // international name
3067 bFound = true;
3068 }
3069 }
3070 if (!bFound)
3071 return false;
3072 OpCode eOp = maRawToken.GetOpCode();
3073 if (eOp == ocSub || eOp == ocNegSub)
3074 {
3075 bool bShouldBeNegSub =
3076 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub ||
3078 eLastOp == ocArrayOpen ||
3080 if (bShouldBeNegSub && eOp == ocSub)
3082 //TODO: if ocNegSub had ForceArray we'd have to set it here
3083 else if (!bShouldBeNegSub && eOp == ocNegSub)
3085 }
3086 return bFound;
3087}
3088
3089bool ScCompiler::ParseOpCode2( std::u16string_view rName )
3090{
3091 bool bFound = false;
3092 sal_uInt16 i;
3093
3094 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ )
3095 bFound = o3tl::equalsAscii( rName, pInternal[ i-ocInternalBegin ] );
3096
3097 if (bFound)
3098 {
3099 maRawToken.SetOpCode( static_cast<OpCode>(--i) );
3100 }
3101 return bFound;
3102}
3103
3105{
3106 while (*p == ' ')
3107 p++;
3108 return *p == '(';
3109}
3110
3111bool ScCompiler::ParseValue( const OUString& rSym )
3112{
3113 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar());
3114 if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML)
3115 {
3116 // Speedup things for ODFF, only well-formed numbers, not locale
3117 // dependent nor user input.
3118 rtl_math_ConversionStatus eStatus;
3119 sal_Int32 nParseEnd;
3120 double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd);
3121 if (nParseEnd != rSym.getLength())
3122 {
3123 // Not (only) a number.
3124
3125 if (nParseEnd > 0)
3126 return false; // partially a number => no such thing
3127
3128 if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3129 return false; // some function name, not a constant
3130
3131 // Could be TRUE or FALSE constant.
3132 OpCode eOpFunc = ocNone;
3133 if (rSym.equalsIgnoreAsciiCase("TRUE"))
3134 eOpFunc = ocTrue;
3135 else if (rSym.equalsIgnoreAsciiCase("FALSE"))
3136 eOpFunc = ocFalse;
3137 if (eOpFunc != ocNone)
3138 {
3139 maRawToken.SetOpCode(eOpFunc);
3140 // add missing trailing parentheses
3143 return true;
3144 }
3145 return false;
3146 }
3147 if (eStatus == rtl_math_ConversionStatus_OutOfRange)
3148 {
3149 // rtl::math::stringToDouble() recognizes XMLSchema-2 "INF" and
3150 // "NaN" (case sensitive) that could be named expressions or DB
3151 // areas as well.
3152 // rSym is already upper so "NaN" is not possible here.
3153 if (!std::isfinite(fVal) && rSym == "INF")
3154 {
3155 SCTAB nSheet = -1;
3156 if (GetRangeData( nSheet, rSym))
3157 return false;
3159 return false;
3160 }
3161 /* TODO: is there a specific reason why we don't accept an infinity
3162 * value that would raise an error in the interpreter, instead of
3163 * setting the hard error at the token array already? */
3164 SetError( FormulaError::IllegalArgument );
3165 }
3166 maRawToken.SetDouble( fVal );
3167 return true;
3168 }
3169
3170 double fVal;
3171 sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0;
3172
3173 if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal))
3174 return false;
3175
3177
3178 // Don't accept 3:3 as time, it is a reference to entire row 3 instead.
3179 // Dates should never be entered directly and automatically converted
3180 // to serial, because the serial would be wrong if null-date changed.
3181 // Usually it wouldn't be accepted anyway because the date separator
3182 // clashed with other separators or operators.
3183 if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE))
3184 return false;
3185
3186 if (nType == SvNumFormatType::LOGICAL)
3187 {
3188 if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos))
3189 return false; // Boolean function instead.
3190 }
3191
3192 if( nType == SvNumFormatType::TEXT )
3193 // HACK: number too big!
3194 SetError( FormulaError::IllegalArgument );
3195 maRawToken.SetDouble( fVal );
3196 return true;
3197}
3198
3200{
3201 if ( cSymbol[0] != '"' )
3202 return false;
3203 const sal_Unicode* p = cSymbol+1;
3204 while ( *p )
3205 p++;
3206 sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 );
3207 if (!nLen || cSymbol[nLen] != '"')
3208 return false;
3209 svl::SharedString aSS = rDoc.GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1));
3211 return true;
3212}
3213
3214bool ScCompiler::ParsePredetectedErrRefReference( const OUString& rName, const OUString* pErrRef )
3215{
3216 switch (mnPredetectedReference)
3217 {
3218 case 1:
3219 return ParseSingleReference( rName, pErrRef);
3220 case 2:
3221 return ParseDoubleReference( rName, pErrRef);
3222 default:
3223 return false;
3224 }
3225}
3226
3227bool ScCompiler::ParsePredetectedReference( const OUString& rName )
3228{
3229 // Speedup documents with lots of broken references, e.g. sheet deleted.
3230 // It could also be a broken invalidated reference that contains #REF!
3231 // (but is not equal to), which we wrote prior to ODFF and also to ODFF
3232 // between 2013 and 2016 until 5.1.4
3233 const OUString aErrRef("#REF!"); // not localized in ODFF
3234 sal_Int32 nPos = rName.indexOf( aErrRef);
3235 if (nPos != -1)
3236 {
3237 /* TODO: this may be enhanced by reusing scan information from
3238 * NextSymbol(), the positions of quotes and special characters found
3239 * there for $'sheet'.A1:... could be stored in a vector. We don't
3240 * fully rescan here whether found positions are within single quotes
3241 * for performance reasons. This code does not check for possible
3242 * occurrences of insane "valid" sheet names like
3243 * 'haha.#REF!1fooledyou' and will generate an error on such. */
3244 if (nPos == 0)
3245 {
3246 // Per ODFF the correct string for a reference error is just #REF!,
3247 // so pass it on.
3248 if (rName.getLength() == 5)
3249 return ParseErrorConstant( rName);
3250 // #REF!.AB42 or #REF!42 or #REF!#REF!
3251 return ParsePredetectedErrRefReference( rName, &aErrRef);
3252 }
3253 sal_Unicode c = rName[nPos-1]; // before #REF!
3254 if ('$' == c)
3255 {
3256 if (nPos == 1)
3257 {
3258 // $#REF!.AB42 or $#REF!42 or $#REF!#REF!
3259 return ParsePredetectedErrRefReference( rName, &aErrRef);
3260 }
3261 c = rName[nPos-2]; // before $#REF!
3262 }
3263 sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF!
3264 switch (c)
3265 {
3266 case '.':
3267 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9'))
3268 {
3269 // sheet.#REF!42 or sheet.#REF!#REF!
3270 return ParsePredetectedErrRefReference( rName, &aErrRef);
3271 }
3272 break;
3273 case ':':
3274 if (mnPredetectedReference > 1 &&
3275 ('.' == c2 || '$' == c2 || '#' == c2 ||
3276 ('0' <= c2 && c2 <= '9')))
3277 {
3278 // :#REF!.AB42 or :#REF!42 or :#REF!#REF!
3279 return ParsePredetectedErrRefReference( rName, &aErrRef);
3280 }
3281 break;
3282 default:
3283 if (rtl::isAsciiAlpha(c) &&
3284 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2))
3285 {
3286 // AB#REF!: or AB#REF!
3287 return ParsePredetectedErrRefReference( rName, &aErrRef);
3288 }
3289 }
3290 }
3291 switch (mnPredetectedReference)
3292 {
3293 case 1:
3294 return ParseSingleReference( rName);
3295 case 2:
3296 return ParseDoubleReference( rName);
3297 }
3298 return false;
3299}
3300
3301bool ScCompiler::ParseDoubleReference( const OUString& rName, const OUString* pErrRef )
3302{
3303 ScRange aRange( aPos, aPos );
3304 const ScAddress::Details aDetails( pConv->meConv, aPos );
3305 ScAddress::ExternalInfo aExtInfo;
3306 ScRefFlags nFlags = aRange.Parse( rName, rDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef );
3307 if( nFlags & ScRefFlags::VALID )
3308 {
3309 ScComplexRefData aRef;
3310 aRef.InitRange( aRange );
3311 aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3312 aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3313 aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3314 if ( !(nFlags & ScRefFlags::TAB_VALID) )
3315 aRef.Ref1.SetTabDeleted( true ); // #REF!
3316 aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3320 if ( !(nFlags & ScRefFlags::TAB2_VALID) )
3321 aRef.Ref2.SetTabDeleted( true ); // #REF!
3322 aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO );
3323 aRef.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
3324 if (aExtInfo.mbExternal)
3325 {
3327 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3329 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3330 maExternalFiles.push_back(aExtInfo.mnFileId);
3331 }
3332 else
3333 {
3335 }
3336 }
3337
3338 return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3339}
3340
3341bool ScCompiler::ParseSingleReference( const OUString& rName, const OUString* pErrRef )
3342{
3344 mnCurrentSheetTab = -1;
3345 ScAddress aAddr( aPos );
3346 const ScAddress::Details aDetails( pConv->meConv, aPos );
3347 ScAddress::ExternalInfo aExtInfo;
3348 ScRefFlags nFlags = aAddr.Parse( rName, rDoc, aDetails,
3349 &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef);
3350 // Something must be valid in order to recognize Sheet1.blah or blah.a1
3351 // as a (wrong) reference.
3353 {
3354 // Valid given tab and invalid col or row may indicate a sheet-local
3355 // named expression, bail out early and don't create a reference token.
3356 if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 &&
3357 (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D))
3358 {
3359 if (aExtInfo.mbExternal)
3360 {
3361 // External names are handled separately.
3363 mnCurrentSheetTab = -1;
3364 }
3365 else
3366 {
3367 mnCurrentSheetTab = aAddr.Tab();
3368 }
3369 return false;
3370 }
3371
3372 if( HasPossibleNamedRangeConflict( aAddr.Tab()))
3373 {
3374 // A named range named e.g. 'num1' is valid with 1k columns, but would become a reference
3375 // when the document is opened later with 16k columns. Resolve the conflict by not
3376 // considering it a reference.
3377 OUString aUpper( ScGlobal::getCharClass().uppercase( rName ));
3378 mnCurrentSheetTab = aAddr.Tab(); // temporarily set for ParseNamedRange()
3379 if(ParseNamedRange( aUpper, true )) // only check
3380 return false;
3381 mnCurrentSheetTab = -1;
3382 }
3383
3384 ScSingleRefData aRef;
3385 aRef.InitAddress( aAddr );
3386 aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO );
3387 aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO );
3388 aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO );
3389 aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO );
3390 // the reference is really invalid
3391 if( !( nFlags & ScRefFlags::VALID ) )
3392 {
3393 if( !( nFlags & ScRefFlags::COL_VALID ) )
3394 aRef.SetColDeleted(true);
3395 if( !( nFlags & ScRefFlags::ROW_VALID ) )
3396 aRef.SetRowDeleted(true);
3397 if( !( nFlags & ScRefFlags::TAB_VALID ) )
3398 aRef.SetTabDeleted(true);
3399 nFlags |= ScRefFlags::VALID;
3400 }
3401 aRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
3402
3403 if (aExtInfo.mbExternal)
3404 {
3406 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName);
3408 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef);
3409 maExternalFiles.push_back(aExtInfo.mnFileId);
3410 }
3411 else
3413 }
3414
3415 return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO;
3416}
3417
3418bool ScCompiler::ParseReference( const OUString& rName, const OUString* pErrRef )
3419{
3420 // Has to be called before ParseValue
3421
3422 // A later ParseNamedRange() relies on these, being set in ParseSingleReference()
3423 // if so, reset in all cases.
3425 mnCurrentSheetTab = -1;
3426
3427 sal_Unicode ch1 = rName[0];
3428 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : ScGlobal::getLocaleData().getNumDecimalSep()[0] );
3429 if ( ch1 == cDecSep )
3430 return false;
3431 // Code further down checks only if cDecSep=='.' so simply obtaining the
3432 // alternative decimal separator if it's not is sufficient.
3433 if (cDecSep != '.')
3434 {
3435 cDecSep = ScGlobal::getLocaleData().getNumDecimalSepAlt().toChar();
3436 if ( ch1 == cDecSep )
3437 return false;
3438 }
3439 // Who was that imbecile introducing '.' as the sheet name separator!?!
3440 if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' )
3441 {
3442 // Numerical sheet name is valid.
3443 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01
3444 // Don't create a #REF! of values. But also do not bail out on
3445 // something like 3:3, meaning entire row 3.
3446 do
3447 {
3448 const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.');
3449 if ( nPos == -1 )
3450 {
3451 if (ScGlobal::FindUnquoted( rName, ':') != -1)
3452 break; // may be 3:3, continue as usual
3453 return false;
3454 }
3455 sal_Unicode const * const pTabSep = rName.getStr() + nPos;
3456 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier
3457 if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) )
3458 return false;
3459 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit
3460 && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) )
3461 {
3462 // If it is an 1.E2 expression check if "1" is an existent sheet
3463 // name. If so, a desired value 1.E2 would have to be entered as
3464 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to
3465 // require numerical sheet names always being entered quoted, which
3466 // is not desirable (too many 1999, 2000, 2001 sheets in use).
3467 // Furthermore, XML files created with versions prior to SRC640e
3468 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes()
3469 // and would produce wrong formulas if the conditions here are met.
3470 // If you can live with these restrictions you may remove the
3471 // check and return an unconditional FALSE.
3472 OUString aTabName( rName.copy( 0, nPos ) );
3473 SCTAB nTab;
3474 if ( !rDoc.GetTable( aTabName, nTab ) )
3475 return false;
3476 // If sheet "1" exists and the expression is 1.E+2 continue as
3477 // usual, the ScRange/ScAddress parser will take care of it.
3478 }
3479 } while(false);
3480 }
3481
3482 if (ParseSingleReference( rName, pErrRef))
3483 return true;
3484
3485 // Though the range operator is handled explicitly, when encountering
3486 // something like Sheet1.A:A we will have to treat it as one entity if it
3487 // doesn't pass as single cell reference.
3488 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense
3489 {
3490 if (ParseDoubleReference( rName, pErrRef))
3491 return true;
3492 // Now try with a symbol up to the range operator, rewind source
3493 // position.
3494 assert(mnRangeOpPosInSymbol < MAXSTRLEN); // We should have caught the maldoers.
3495 if (mnRangeOpPosInSymbol >= MAXSTRLEN) // TODO: this check and return
3496 return false; // can be removed when sure.
3497 sal_Int32 nLen = mnRangeOpPosInSymbol;
3498 while (cSymbol[++nLen])
3499 ;
3501 nSrcPos -= (nLen - mnRangeOpPosInSymbol);
3503 mbRewind = true;
3504 return true; // end all checks
3505 }
3506 else
3507 {
3508 switch (pConv->meConv)
3509 {
3510 case FormulaGrammar::CONV_XL_A1:
3511 case FormulaGrammar::CONV_XL_OOX:
3512 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel
3513 // sickness, mnRangeOpPosInSymbol did not catch the range
3514 // operator as it is within a quoted name.
3515 if (rName[0] != '\'')
3516 return false; // Document name has to be single quoted.
3517 [[fallthrough]];
3518 case FormulaGrammar::CONV_XL_R1C1:
3519 // C2 or C[1] are valid entire column references.
3520 if (ParseDoubleReference( rName, pErrRef))
3521 return true;
3522 break;
3523 default:
3524 ; // nothing
3525 }
3526 }
3527 return false;
3528}
3529
3530bool ScCompiler::ParseMacro( const OUString& rName )
3531{
3532#if !HAVE_FEATURE_SCRIPTING
3533 (void) rName;
3534
3535 return false;
3536#else
3537
3538 // Calling SfxObjectShell::GetBasic() may result in all sort of things
3539 // including obtaining the model and deep down in
3540 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when
3541 // formulas are compiled from a threaded import may result in a deadlock.
3542 // Check first if we actually could acquire it and if not bail out.
3543 /* FIXME: yes, but how ... */
3545 if (!g.isAcquired())
3546 {
3547 SAL_WARN( "sc.core", "ScCompiler::ParseMacro - SolarMutex would deadlock, not obtaining Basic");
3548 return false; // bad luck
3549 }
3550
3551 OUString aName( rName);
3552 StarBASIC* pObj = nullptr;
3554
3555 try
3556 {
3557 if( pDocSh )//XXX
3558 pObj = pDocSh->GetBasic();
3559 else
3560 pObj = SfxApplication::GetBasic();
3561 }
3562 catch (...)
3563 {
3564 return false;
3565 }
3566
3567 if (!pObj)
3568 return false;
3569
3570 // ODFF recommends to store user-defined functions prefixed with "USER.",
3571 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a
3572 // function name so a function "USER.FOO" could not exist, and macro check
3573 // is assigned the lowest priority in function name check.
3574 if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER."))
3575 aName = aName.copy(5);
3576
3577 SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method ));
3578 if( !pMeth )
3579 {
3580 return false;
3581 }
3582 // It really should be a BASIC function!
3583 if( pMeth->GetType() == SbxVOID
3584 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY )
3585 || dynamic_cast<const SbMethod*>( pMeth) == nullptr )
3586 {
3587 return false;
3588 }
3591 return true;
3592#endif
3593}
3594
3595const ScRangeData* ScCompiler::GetRangeData( SCTAB& rSheet, const OUString& rUpperName ) const
3596{
3597 // try local names first
3598 rSheet = aPos.Tab();
3599 const ScRangeName* pRangeName = rDoc.GetRangeName(rSheet);
3600 const ScRangeData* pData = nullptr;
3601 if (pRangeName)
3602 pData = pRangeName->findByUpperName(rUpperName);
3603 if (!pData)
3604 {
3605 pRangeName = rDoc.GetRangeName();
3606 if (pRangeName)
3607 pData = pRangeName->findByUpperName(rUpperName);
3608 if (pData)
3609 rSheet = -1;
3610 }
3611 return pData;
3612}
3613
3615{
3616 const ScRangeName* pRangeName = rDoc.GetRangeName();
3617 if (pRangeName && pRangeName->hasPossibleAddressConflict())
3618 return true;
3619 pRangeName = rDoc.GetRangeName(nTab);
3620 if (pRangeName && pRangeName->hasPossibleAddressConflict())
3621 return true;
3622 return false;
3623}
3624
3625bool ScCompiler::ParseNamedRange( const OUString& rUpperName, bool onlyCheck )
3626{
3627 // ParseNamedRange is called only from NextNewToken, with an upper-case string
3628
3629 SCTAB nSheet = -1;
3630 const ScRangeData* pData = GetRangeData( nSheet, rUpperName);
3631 if (pData)
3632 {
3633 if (!onlyCheck)
3634 maRawToken.SetName( nSheet, pData->GetIndex());
3635 return true;
3636 }
3637
3638 // Sheet-local name with sheet specified.
3640 {
3641 OUString aName( rUpperName.copy( mnCurrentSheetEndPos));
3642 const ScRangeName* pRangeName = rDoc.GetRangeName( mnCurrentSheetTab);
3643 if (pRangeName)
3644 {
3645 pData = pRangeName->findByUpperName(aName);
3646 if (pData)
3647 {
3648 if (!onlyCheck)
3650 return true;
3651 }
3652 }
3653 }
3654
3655 return false;
3656}
3657
3658bool ScCompiler::ParseExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange )
3659{
3660 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc)
3661 * correctly parses external named references in OOo, as required per RFE
3662 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS
3663 * spec first. Until then don't pretend to support external names that
3664 * wouldn't survive a save and reload cycle, return false instead. */
3665
3666 rbInvalidExternalNameRange = false;
3667
3668 if (!pConv)
3669 return false;
3670
3671 OUString aFile, aName;
3672 if (!pConv->parseExternalName( rSymbol, aFile, aName, rDoc, &maExternalLinks))
3673 return false;
3674
3675 if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN)
3676 return false;
3677
3679 OUString aTmp = aFile;
3680 pRefMgr->convertToAbsName(aTmp);
3681 aFile = aTmp;
3682 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile);
3683 if (!pRefMgr->isValidRangeName(nFileId, aName))
3684 {
3685 rbInvalidExternalNameRange = true;
3686 // range name doesn't exist in the source document.
3687 return false;
3688 }
3689
3690 const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName);
3691 maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp);
3692 maExternalFiles.push_back(nFileId);
3693 return true;
3694}
3695
3696bool ScCompiler::ParseDBRange( const OUString& rName )
3697{
3699 const ScDBData* p = rDBs.findByUpperName(rName);
3700 if (!p)
3701 return false;
3702
3703 maRawToken.SetName( -1, p->GetIndex()); // DB range is always global.
3705 return true;
3706}
3707
3708bool ScCompiler::ParseColRowName( const OUString& rName )
3709{
3710 bool bInList = false;
3711 bool bFound = false;
3712 ScSingleRefData aRef;
3713 OUString aName( rName );
3714 DeQuote( aName );
3715 SCTAB nThisTab = aPos.Tab();
3716 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- )
3717 { // first check ranges on this sheet, in case of duplicated names
3718 for ( short jRow=0; jRow<2 && !bInList; jRow++ )
3719 {
3720 ScRangePairList* pRL;
3721 if ( !jRow )
3722 pRL = rDoc.GetColNameRanges();
3723 else
3724 pRL = rDoc.GetRowNameRanges();
3725 for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair )
3726 {
3727 const ScRangePair & rR = (*pRL)[iPair];
3728 const ScRange& rNameRange = rR.GetRange(0);
3729 if ( jThisTab && (rNameRange.aStart.Tab() > nThisTab ||
3730 nThisTab > rNameRange.aEnd.Tab()) )
3731 continue; // for
3732 ScCellIterator aIter( rDoc, rNameRange );
3733 for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next())
3734 {
3735 // Don't crash if cell (via CompileNameFormula) encounters
3736 // a formula cell without code and
3737 // HasStringData/Interpret/Compile is executed and all that
3738 // recursively...
3739 // Furthermore, *this* cell won't be touched, since no RPN exists yet.
3740 CellType eType = aIter.getType();
3741 bool bOk = false;
3742 if (eType == CELLTYPE_FORMULA)
3743 {
3744 ScFormulaCell* pFC = aIter.getFormulaCell();
3745 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3746 }
3747 else
3748 bOk = true;
3749
3750 if (bOk && aIter.hasString())
3751 {
3752 OUString aStr = aIter.getString();
3754 {
3755 aRef.InitFlags();
3756 if ( !jRow )
3757 aRef.SetColRel( true ); // ColName
3758 else
3759 aRef.SetRowRel( true ); // RowName
3760 aRef.SetAddress(rDoc.GetSheetLimits(), aIter.GetPos(), aPos);
3761 bInList = bFound = true;
3762 }
3763 }
3764 }
3765 }
3766 }
3767 }
3768 if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
3769 { // search in current sheet
3770 tools::Long nDistance = 0, nMax = 0;
3771 tools::Long nMyCol = static_cast<tools::Long>(aPos.Col());
3772 tools::Long nMyRow = static_cast<tools::Long>(aPos.Row());
3773 bool bTwo = false;
3774 ScAddress aOne( 0, 0, aPos.Tab() );
3775 ScAddress aTwo( rDoc.MaxCol(), rDoc.MaxRow(), aPos.Tab() );
3776
3777 ScAutoNameCache* pNameCache = rDoc.GetAutoNameCache();
3778 if ( pNameCache )
3779 {
3780 // use GetNameOccurrences to collect all positions of aName on the sheet
3781 // (only once), similar to the outer part of the loop in the "else" branch.
3782
3783 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() );
3784
3785 // Loop through the found positions, similar to the inner part of the loop in the "else" branch.
3786 // The order of addresses in the vector is the same as from ScCellIterator.
3787
3788 for ( const ScAddress& aAddress : rAddresses )
3789 {
3790 if ( bFound )
3791 { // stop if everything else is further away
3792 if ( nMax < static_cast<tools::Long>(aAddress.Col()) )
3793 break; // aIter
3794 }
3795 if ( aAddress != aPos )
3796 {
3797 // same treatment as in isEqual case below
3798
3799 SCCOL nCol = aAddress.Col();
3800 SCROW nRow = aAddress.Row();
3801 tools::Long nC = nMyCol - nCol;
3802 tools::Long nR = nMyRow - nRow;
3803 if ( bFound )
3804 {
3805 tools::Long nD = nC * nC + nR * nR;
3806 if ( nD < nDistance )
3807 {
3808 if ( nC < 0 || nR < 0 )
3809 { // right or below
3810 bTwo = true;
3811 aTwo.Set( nCol, nRow, aAddress.Tab() );
3812 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3813 nDistance = nD;
3814 }
3815 else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3816 {
3817 // upper left, only if not further up than the
3818 // current entry and nMyRow is below (CellIter
3819 // runs column-wise)
3820 bTwo = false;
3821 aOne.Set( nCol, nRow, aAddress.Tab() );
3822 nMax = std::max( nMyCol + nC, nMyRow + nR );
3823 nDistance = nD;
3824 }
3825 }
3826 }
3827 else
3828 {
3829 aOne.Set( nCol, nRow, aAddress.Tab() );
3830 nDistance = nC * nC + nR * nR;
3831 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3832
3833 }
3834 bFound = true;
3835 }
3836 }
3837 }
3838 else
3839 {
3840 ScCellIterator aIter( rDoc, ScRange( aOne, aTwo ) );
3841 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
3842 {
3843 if ( bFound )
3844 { // stop if everything else is further away
3845 if ( nMax < static_cast<tools::Long>(aIter.GetPos().Col()) )
3846 break; // aIter
3847 }
3848 CellType eType = aIter.getType();
3849 bool bOk = false;
3850 if (eType == CELLTYPE_FORMULA)
3851 {
3852 ScFormulaCell* pFC = aIter.getFormulaCell();
3853 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
3854 }
3855 else
3856 bOk = true;
3857
3858 if (bOk && aIter.hasString())
3859 {
3860 OUString aStr = aIter.getString();
3862 {
3863 SCCOL nCol = aIter.GetPos().Col();
3864 SCROW nRow = aIter.GetPos().Row();
3865 tools::Long nC = nMyCol - nCol;
3866 tools::Long nR = nMyRow - nRow;
3867 if ( bFound )
3868 {
3869 tools::Long nD = nC * nC + nR * nR;
3870 if ( nD < nDistance )
3871 {
3872 if ( nC < 0 || nR < 0 )
3873 { // right or below
3874 bTwo = true;
3875 aTwo.Set( nCol, nRow, aIter.GetPos().Tab() );
3876 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3877 nDistance = nD;
3878 }
3879 else if ( nRow >= aOne.Row() || nMyRow < static_cast<tools::Long>(aOne.Row()) )
3880 {
3881 // upper left, only if not further up than the
3882 // current entry and nMyRow is below (CellIter
3883 // runs column-wise)
3884 bTwo = false;
3885 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3886 nMax = std::max( nMyCol + nC, nMyRow + nR );
3887 nDistance = nD;
3888 }
3889 }
3890 }
3891 else
3892 {
3893 aOne.Set( nCol, nRow, aIter.GetPos().Tab() );
3894 nDistance = nC * nC + nR * nR;
3895 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) );
3896 }
3897 bFound = true;
3898 }
3899 }
3900 }
3901 }
3902
3903 if ( bFound )
3904 {
3905 ScAddress aAdr;
3906 if ( bTwo )
3907 {
3908 if ( nMyCol >= static_cast<tools::Long>(aOne.Col()) && nMyRow >= static_cast<tools::Long>(aOne.Row()) )
3909 aAdr = aOne; // upper left takes precedence
3910 else
3911 {
3912 if ( nMyCol < static_cast<tools::Long>(aOne.Col()) )
3913 { // two to the right
3914 if ( nMyRow >= static_cast<tools::Long>(aTwo.Row()) )
3915 aAdr = aTwo; // directly right
3916 else
3917 aAdr = aOne;
3918 }
3919 else
3920 { // two below or below and right, take the nearest
3921 tools::Long nC1 = nMyCol - aOne.Col();
3922 tools::Long nR1 = nMyRow - aOne.Row();
3923 tools::Long nC2 = nMyCol - aTwo.Col();
3924 tools::Long nR2 = nMyRow - aTwo.Row();
3925 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 )
3926 aAdr = aOne;
3927 else
3928 aAdr = aTwo;
3929 }
3930 }
3931 }
3932 else
3933 aAdr = aOne;
3934 aRef.InitAddress( aAdr );
3935 // Prioritize on column label; row label only if the next cell
3936 // above/below the found label cell is text, or if both are not and
3937 // the cell below is empty and the next cell to the right is
3938 // numeric.
3939 if ((aAdr.Row() < rDoc.MaxRow() && rDoc.HasStringData(
3940 aAdr.Col(), aAdr.Row() + 1, aAdr.Tab()))
3941 || (aAdr.Row() > 0 && rDoc.HasStringData(
3942 aAdr.Col(), aAdr.Row() - 1, aAdr.Tab()))
3943 || (aAdr.Row() < rDoc.MaxRow() && rDoc.GetRefCellValue(
3944 ScAddress( aAdr.Col(), aAdr.Row() + 1, aAdr.Tab())).isEmpty()
3945 && aAdr.Col() < rDoc.MaxCol() && rDoc.GetRefCellValue(
3946 ScAddress( aAdr.Col() + 1, aAdr.Row(), aAdr.Tab())).hasNumeric()))
3947 aRef.SetRowRel( true ); // RowName
3948 else
3949 aRef.SetColRel( true ); // ColName
3950 aRef.SetAddress(rDoc.GetSheetLimits(), aAdr, aPos);
3951 }
3952 }
3953 if ( bFound )
3954 {
3957 return true;
3958 }
3959 else
3960 return false;
3961}
3962
3963bool ScCompiler::ParseBoolean( const OUString& rName )
3964{
3965 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) );
3966 if( iLook != mxSymbols->getHashMap().end() &&
3967 ((*iLook).second == ocTrue ||
3968 (*iLook).second == ocFalse) )
3969 {
3970 maRawToken.SetOpCode( (*iLook).second );
3971 return true;
3972 }
3973 else
3974 return false;
3975}
3976
3977bool ScCompiler::ParseErrorConstant( const OUString& rName )
3978{
3979 FormulaError nError = GetErrorConstant( rName);
3980 if (nError != FormulaError::NONE)
3981 {
3983 return true;
3984 }
3985 else
3986 return false;
3987}
3988
3989bool ScCompiler::ParseTableRefItem( const OUString& rName )
3990{
3991 bool bItem = false;
3992 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName));
3993 if (iLook != mxSymbols->getHashMap().end())
3994 {
3995 // Only called when there actually is a current TableRef, hence
3996 // accessing maTableRefs.back() is safe.
3997 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
3998 assert(p); // not a ScTableRefToken can't be
3999
4000 switch ((*iLook).second)
4001 {
4002 case ocTableRefItemAll:
4003 bItem = true;
4004 p->AddItem( ScTableRefToken::ALL);
4005 break;
4007 bItem = true;
4008 p->AddItem( ScTableRefToken::HEADERS);
4009 break;
4010 case ocTableRefItemData:
4011 bItem = true;
4012 p->AddItem( ScTableRefToken::DATA);
4013 break;
4015 bItem = true;
4016 p->AddItem( ScTableRefToken::TOTALS);
4017 break;
4019 bItem = true;
4020 p->AddItem( ScTableRefToken::THIS_ROW);
4021 break;
4022 default:
4023 ;
4024 }
4025 if (bItem)
4026 maRawToken.SetOpCode( (*iLook).second );
4027 }
4028 return bItem;
4029}
4030
4031namespace {
4032OUString unescapeTableRefColumnSpecifier( const OUString& rStr )
4033{
4034 // '#', '[', ']' and '\'' are escaped with '\''
4035
4036 if (rStr.indexOf( '\'' ) < 0)
4037 return rStr;
4038
4039 const sal_Int32 n = rStr.getLength();
4040 OUStringBuffer aBuf( n );
4041 const sal_Unicode* p = rStr.getStr();
4042 const sal_Unicode* const pStop = p + n;
4043 bool bEscaped = false;
4044 for ( ; p < pStop; ++p)
4045 {
4046 const sal_Unicode c = *p;
4047 if (bEscaped)
4048 {
4049 aBuf.append( c );
4050 bEscaped = false;
4051 }
4052 else if (c == '\'')
4053 bEscaped = true; // unescaped escaping '\''
4054 else
4055 aBuf.append( c );
4056 }
4057 return aBuf.makeStringAndClear();
4058}
4059}
4060
4061bool ScCompiler::ParseTableRefColumn( const OUString& rName )
4062{
4063 // Only called when there actually is a current TableRef, hence
4064 // accessing maTableRefs.back() is safe.
4065 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get());
4066 assert(p); // not a ScTableRefToken can't be
4067
4068 ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( p->GetIndex());
4069 if (!pDBData)
4070 return false;
4071
4072 OUString aName( unescapeTableRefColumnSpecifier( rName));
4073
4074 ScRange aRange;
4075 pDBData->GetArea( aRange);
4076 aRange.aEnd.SetTab( aRange.aStart.Tab());
4077 aRange.aEnd.SetRow( aRange.aStart.Row());
4078
4079 // Prefer the stored internal table column name, which is also needed for
4080 // named expressions during document load time when cell content isn't
4081 // available yet. Also, avoiding a possible calculation step in case the
4082 // header cell is a formula cell is "a good thing".
4083 sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName);
4084 if (nOffset >= 0)
4085 {
4086 // This is sneaky... we always use the top row of the database range,
4087 // regardless of whether it is a header row or not. Code evaluating
4088 // this reference must take that into account and may have to act
4089 // differently if it is a header-less table. Which are two places,
4090 // HandleTableRef() (no change necessary there) and
4091 // CreateStringFromSingleRef() (must not fallback to cell lookup).
4092 ScSingleRefData aRef;
4093 ScAddress aAdr( aRange.aStart);
4094 aAdr.IncCol( nOffset);
4095 aRef.InitAddress( aAdr);
4097 return true;
4098 }
4099
4100 if (pDBData->HasHeader())
4101 {
4102 // Quite similar to IsColRowName() but limited to one row of headers.
4103 ScCellIterator aIter( rDoc, aRange);
4104 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
4105 {
4106 CellType eType = aIter.getType();
4107 bool bOk = false;
4108 if (eType == CELLTYPE_FORMULA)
4109 {
4110 ScFormulaCell* pFC = aIter.getFormulaCell();
4111 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos);
4112 }
4113 else
4114 bOk = true;
4115
4116 if (bOk && aIter.hasString())
4117 {
4118 OUString aStr = aIter.getString();
4120 {
4121 // If this is successful and the internal column name
4122 // lookup was not, it may be worth a warning.
4123 SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup");
4124
4125 /* XXX NOTE: we could init the column as relative so copying a
4126 * formula across columns would point to the relative column,
4127 * but do it absolute because:
4128 * a) it makes the reference work in named expressions without
4129 * having to distinguish
4130 * b) Excel does it the same. */
4131 ScSingleRefData aRef;
4132 aRef.InitAddress( aIter.GetPos());
4134 return true;
4135 }
4136 }
4137 }
4138 }
4139
4140 return false;
4141}
4142
4144{
4145 assert(mbJumpCommandReorder);
4146 bAutoCorrect = bVal;
4147 mbStopOnError = !bVal;
4148}
4149
4151{
4152 sal_Int32 nPos = aCorrectedSymbol.getLength();
4153 if ( !nPos )
4154 return;
4155
4156 nPos--;
4157 const sal_Unicode cQuote = '\"';
4158 const sal_Unicode cx = 'x';
4159 const sal_Unicode cX = 'X';
4162 sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0;
4163 if ( c1 == cQuote && c2 != cQuote )
4164 { // "...
4165 // What's not a word doesn't belong to it.
4166 // Don't be pedantic: c < 128 should be sufficient here.
4167 while ( nPos && ((aCorrectedSymbol[nPos] < 128) &&
4170 nPos--;
4171 if ( nPos == MAXSTRLEN - 1 )
4172 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, rtl::OUStringChar(cQuote) ); // '"' the MAXSTRLENth character
4173 else
4174 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, rtl::OUStringChar(cQuote) );
4175 bCorrected = true;
4176 }
4177 else if ( c1 != cQuote && c2 == cQuote )
4178 { // ..."
4179 aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol;
4180 bCorrected = true;
4181 }
4182 else if ( nPos == 0 && (c1 == cx || c1 == cX) )
4183 { // x => *
4184 aCorrectedSymbol = mxSymbols->getSymbol(ocMul);
4185 bCorrected = true;
4186 }
4187 else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue)
4188 && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) )
4189 {
4190 if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx
4191 { // x => *
4192 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4193 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c));
4194 bCorrected = true;
4195 }
4196 if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX
4197 { // X => *
4198 sal_Unicode c = mxSymbols->getSymbolChar(ocMul);
4199 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c));
4200 bCorrected = true;
4201 }
4202 }
4203 else
4204 {
4205 OUString aSymbol( aCorrectedSymbol );
4206 OUString aDoc;
4207 if ( aSymbol[0] == '\'' )
4208 {
4209 sal_Int32 nPosition = aSymbol.indexOf( "'#" );
4210 if (nPosition != -1)
4211 { // Split off 'Doc'#, may be d:\... or whatever
4212 aDoc = aSymbol.copy(0, nPosition + 2);
4213 aSymbol = aSymbol.copy(nPosition + 2);
4214 }
4215 }
4216 sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':');
4217 bool bColons;
4218 if ( nRefs > 2 )
4219 { // duplicated or too many ':'? B:2::C10 => B2:C10
4220 bColons = true;
4221 sal_Int32 nIndex = 0;
4222 OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) );
4223 sal_Int32 nLen1 = aTmp1.getLength();
4224 OUStringBuffer aSym;
4225 OUString aTmp2;
4226 bool bLastAlp = true;
4227 sal_Int32 nStrip = 0;
4228 sal_Int32 nCount = nRefs;
4229 for ( sal_Int32 j=1; j<nCount; j++ )
4230 {
4231 aTmp2 = aSymbol.getToken( 0, ':', nIndex );
4232 sal_Int32 nLen2 = aTmp2.getLength();
4233 if ( nLen1 || nLen2 )
4234 {
4235 if ( nLen1 )
4236 {
4237 aSym.append(aTmp1);
4238 bLastAlp = CharClass::isAsciiAlpha( aTmp1 );
4239 }
4240 if ( nLen2 )
4241 {
4242 bool bNextNum = CharClass::isAsciiNumeric( aTmp2 );
4243 if ( bLastAlp == bNextNum && nStrip < 1 )
4244 {
4245 // Must be alternating number/string, only
4246 // strip within a reference.
4247 nRefs--;
4248 nStrip++;
4249 }
4250 else
4251 {
4252 if ( !aSym.isEmpty() && aSym[aSym.getLength()-1] != ':')
4253 aSym.append(":");
4254 nStrip = 0;
4255 }
4256 bLastAlp = !bNextNum;
4257 }
4258 else
4259 { // ::
4260 nRefs--;
4261 if ( nLen1 )
4262 { // B10::C10 ? append ':' on next round
4263 if ( !bLastAlp && !CharClass::isAsciiNumeric( aTmp1 ) )
4264 nStrip++;
4265 }
4266 }
4267 aTmp1 = aTmp2;
4268 nLen1 = nLen2;
4269 }
4270 else
4271 nRefs--;
4272 }
4273 aSymbol = aSym + aTmp1;
4274 aSym.setLength(0);
4275 }
4276 else
4277 bColons = false;
4278 if ( nRefs && nRefs <= 2 )
4279 { // reference twisted? 4A => A4 etc.
4280 OUString aTab[2], aRef[2];
4281 const ScAddress::Details aDetails( pConv->meConv, aPos );
4282 if ( nRefs == 2 )
4283 {
4284 sal_Int32 nIdx{ 0 };
4285 aRef[0] = aSymbol.getToken( 0, ':', nIdx );
4286 aRef[1] = aSymbol.getToken( 0, ':', nIdx );
4287 }
4288 else
4289 aRef[0] = aSymbol;
4290
4291 bool bChanged = false;
4292 bool bOk = true;
4294 for ( int j=0; j<nRefs; j++ )
4295 {
4296 sal_Int32 nTmp = 0;
4297 sal_Int32 nDotPos = -1;
4298 while ( (nTmp = aRef[j].indexOf( '.', nTmp )) != -1 )
4299 nDotPos = nTmp++; // the last one counts
4300 if ( nDotPos != -1 )
4301 {
4302 aTab[j] = aRef[j].copy( 0, nDotPos + 1 ); // with '.'
4303 aRef[j] = aRef[j].copy( nDotPos + 1 );
4304 }
4305 OUString aOld( aRef[j] );
4306 OUStringBuffer aStr2;
4307 const sal_Unicode* p = aRef[j].getStr();
4308 while ( *p && rtl::isAsciiDigit( *p ) )
4309 aStr2.append(*p++);
4310 aRef[j] = OUString( p );
4311 aRef[j] += aStr2;
4312 if ( bColons || aRef[j] != aOld )
4313 {
4314 bChanged = true;
4315 ScAddress aAdr;
4316 bOk &= ((aAdr.Parse( aRef[j], rDoc, aDetails ) & nMask) == nMask);
4317 }
4318 }
4319 if ( bChanged && bOk )
4320 {
4321 aCorrectedSymbol = aDoc;
4322 aCorrectedSymbol += aTab[0];
4323 aCorrectedSymbol += aRef[0];
4324 if ( nRefs == 2 )
4325 {
4326 aCorrectedSymbol += ":";
4327 aCorrectedSymbol += aTab[1];
4328 aCorrectedSymbol += aRef[1];
4329 }
4330 bCorrected = true;
4331 }
4332 }
4333 }
4334}
4335
4336bool ScCompiler::ToUpperAsciiOrI18nIsAscii( OUString& rUpper, const OUString& rOrg ) const
4337{
4338 if (FormulaGrammar::isODFF( meGrammar) || FormulaGrammar::isOOXML( meGrammar))
4339 {
4340 // ODFF and OOXML have defined sets of English function names, avoid
4341 // i18n overhead.
4342 rUpper = rOrg.toAsciiUpperCase();
4343 return true;
4344 }
4345 else
4346 {
4347 // One of localized or English.
4348 rUpper = pCharClass->uppercase(rOrg);
4349 return false;
4350 }
4351}
4352
4353bool ScCompiler::NextNewToken( bool bInArray )
4354{
4355 if (!maPendingOpCodes.empty())
4356 {
4358 maPendingOpCodes.pop();
4359 return true;
4360 }
4361
4362 bool bAllowBooleans = bInArray;
4363 const std::vector<Whitespace> & vSpaces = NextSymbol(bInArray);
4364
4365 if (!cSymbol[0])
4366 {
4367 if (nSrcPos < aFormula.getLength())
4368 {
4369 // Nothing could be parsed, remainder as bad string.
4370 // NextSymbol() must had set an error for this.
4371 assert( pArr->GetCodeError() != FormulaError::NONE);
4372 const OUString aBad( aFormula.copy( nSrcPos));
4376 nSrcPos = aFormula.getLength();
4377 // Add bad string as last token.
4378 return true;
4379 }
4380 return false;
4381 }
4382
4383 if (!vSpaces.empty())
4384 {
4385 ScRawToken aToken;
4386 for (const auto& rSpace : vSpaces)
4387 {
4388 if (rSpace.cChar == 0x20)
4389 {
4390 // For now keep this a FormulaByteToken for the nasty
4391 // significant whitespace intersection. This probably can be
4392 // changed to a FormulaSpaceToken but then other places may
4393 // need to be adapted.
4394 aToken.SetOpCode( ocSpaces );
4395 aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
4396 }
4397 else
4398 {
4399 aToken.SetOpCode( ocWhitespace );
4400 aToken.whitespace.nCount = static_cast<sal_uInt8>( std::min<sal_Int32>(rSpace.nCount, 255) );
4401 aToken.whitespace.cChar = rSpace.cChar;
4402 }
4403 if (!static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ))
4404 {
4405 SetError(FormulaError::CodeOverflow);
4406 return false;
4407 }
4408 }
4409 }
4410
4411 // Short cut for references when reading ODF to speedup things.
4413 {
4414 OUString aStr( cSymbol);
4415 bool bInvalidExternalNameRange;
4416 if (!ParsePredetectedReference( aStr) && !ParseExternalNamedRange( aStr, bInvalidExternalNameRange ))
4417 {
4421 }
4422 return true;
4423 }
4424
4425 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 &&
4426 !bAutoCorrect )
4427 { // special case to speed up broken [$]#REF documents
4428 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to
4429 * be processed as usual. That would need some special treatment,
4430 * also in NextSymbol() because of possible combinations of
4431 * #REF!.#REF!#REF! parts. In case of reading ODF that is all
4432 * handled by IsPredetectedReference(), this case here remains for
4433 * manual/API input. */
4434 OUString aBad( aFormula.copy( nSrcPos-1 ) );
4435 const FormulaToken* pBadToken = pArr->AddBad(aBad);
4436 eLastOp = pBadToken ? pBadToken->GetOpCode() : ocNone;
4437 return false;
4438 }
4439
4440 if( ParseString() )
4441 return true;
4442
4443 bool bMayBeFuncName;
4444 bool bAsciiNonAlnum; // operators, separators, ...
4445 if ( cSymbol[0] < 128 )
4446 {
4447 bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
4448 if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing())
4449 {
4450 bMayBeFuncName = officecfg::Office::Common::Misc::ExperimentalMode::get();
4451 }
4452
4453 bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
4454 }
4455 else
4456 {
4457 OUString aTmpStr( cSymbol[0] );
4458 bMayBeFuncName = pCharClass->isLetter( aTmpStr, 0 );
4459 bAsciiNonAlnum = false;
4460 }
4461
4462 // Within a TableRef anything except an unescaped '[' or ']' is an item
4463 // or a column specifier, do not attempt to recognize any other single
4464 // operator there so even [,] or [+] for a single character column
4465 // specifier works. Note that space between two ocTableRefOpen is not
4466 // supported (Table[ [ColumnSpec]]), not only here. Note also that Table[]
4467 // without any item or column specifier is valid.
4468 if (bAsciiNonAlnum && cSymbol[1] == 0 && (eLastOp != ocTableRefOpen || cSymbol[0] == '[' || cSymbol[0] == ']'))
4469 {
4470 // Shortcut for operators and separators that need no further checks or upper.
4471 if (ParseOpCode( OUString( cSymbol), bInArray ))
4472 return true;
4473 }
4474
4475 if ( bMayBeFuncName )
4476 {
4477 // a function name must be followed by a parenthesis
4478 const sal_Unicode* p = aFormula.getStr() + nSrcPos;
4479 while( *p == ' ' )
4480 p++;
4481 bMayBeFuncName = ( *p == '(' );
4482 }
4483
4484 // Italian ARCTAN.2 resulted in #REF! => ParseOpcode() before
4485 // ParseReference().
4486
4487 OUString aUpper;
4488 bool bAsciiUpper = false;
4489
4490Label_Rewind:
4491
4492 do
4493 {
4494 const OUString aOrg( cSymbol );
4495
4496 // Check for TableRef column specifier first, it may be anything.
4497 if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel)
4498 {
4499 if (ParseTableRefColumn( aOrg ))
4500 return true;
4501 // Do not attempt to resolve as any other name.
4502 aUpper = aOrg; // for ocBad
4503 break; // do; create ocBad token or set error.
4504 }
4505
4506 mbRewind = false;
4507 aUpper.clear();
4508 bAsciiUpper = false;
4509
4510 if (bAsciiNonAlnum)
4511 {
4512 bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4513 if (cSymbol[0] == '#')
4514 {
4515 // Check for TableRef item specifiers first.
4516 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2)
4517 {
4518 if (ParseTableRefItem( aUpper ))
4519 return true;
4520 }
4521
4522 // This can be either an error constant ...
4523 if (ParseErrorConstant( aUpper))
4524 return true;
4525
4526 // ... or some invalidated reference starting with #REF!
4527 // which is handled after the do loop.
4528
4529 break; // do; create ocBad token or set error.
4530 }
4531 if (ParseOpCode( aUpper, bInArray ))
4532 return true;
4533 }
4534
4535 if (bMayBeFuncName)
4536 {
4537 if (aUpper.isEmpty())
4538 bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4539 if (ParseOpCode( aUpper, bInArray ))
4540 return true;
4541 }
4542
4543 // Column 'DM' ("Deutsche Mark", German currency) couldn't be
4544 // referred => ParseReference() before ParseValue().
4545 // Preserve case of file names in external references.
4546 if (ParseReference( aOrg ))
4547 {
4548 if (mbRewind) // Range operator, but no direct reference.
4549 continue; // do; up to range operator.
4550 // If a syntactically correct reference was recognized but invalid
4551 // e.g. because of non-existing sheet name => entire reference
4552 // ocBad to preserve input instead of #REF!.A1
4554 {
4555 aUpper = aOrg; // ensure for ocBad
4556 break; // do; create ocBad token or set error.
4557 }
4558 return true;
4559 }
4560
4561 if (aUpper.isEmpty())
4562 bAsciiUpper = ToUpperAsciiOrI18nIsAscii( aUpper, aOrg);
4563
4564 // ParseBoolean() before ParseValue() to catch inline bools without the kludge
4565 // for inline arrays.
4566 if (bAllowBooleans && ParseBoolean( aUpper ))
4567 return true;
4568
4569 if (ParseValue( aUpper ))
4570 return true;
4571
4572 // User defined names and such do need i18n upper also in ODF.
4573 if (bAsciiUpper || mbCharClassesDiffer)
4574 {
4575 // Use current system locale here because user defined symbols are
4576 // more likely in that localized language than in the formula
4577 // language. This in corner cases needs to continue to work for
4578 // existing documents and environments.
4579 // Do not change bAsciiUpper from here on for the lowercase() call
4580 // below in the ocBad case to use the correct CharClass.
4581 aUpper = ScGlobal::getCharClass().uppercase( aOrg );
4582 }
4583
4584 if (ParseNamedRange( aUpper ))
4585 return true;
4586
4587 // Compiling a named expression during collecting them in import shall
4588 // not match arbitrary names that otherwise if all named expressions
4589 // were present would be recognized as named expression. Such name will
4590 // flag an error below and will be recompiled in a second step later
4591 // with ScRangeData::CompileUnresolvedXML()
4593 break; // while
4594
4595 // Preserve case of file names in external references.
4596 bool bInvalidExternalNameRange;
4597 if (ParseExternalNamedRange( aOrg, bInvalidExternalNameRange ))
4598 return true;
4599 // Preserve case of file names in external references even when range
4600 // is not valid and previous check failed tdf#89330
4601 if (bInvalidExternalNameRange)
4602 {
4603 // add ocBad but do not lowercase
4607 return true;
4608 }
4609 if (ParseDBRange( aUpper ))
4610 return true;
4611 // If followed by '(' (with or without space inbetween) it can not be a
4612 // column/row label. Prevent arbitrary content detection.
4613 if (!bMayBeFuncName && ParseColRowName( aUpper ))
4614 return true;
4615 if (bMayBeFuncName && ParseMacro( aUpper ))
4616 return true;
4617 if (bMayBeFuncName && ParseOpCode2( aUpper ))
4618 return true;
4619
4620 } while (mbRewind);
4621
4622 // Last chance: it could be a broken invalidated reference that contains
4623 // #REF! (but is not equal to), which we also wrote to ODFF between 2013
4624 // and 2016 until 5.1.4
4625 OUString aErrRef( mxSymbols->getSymbol( ocErrRef));
4626 if (aUpper.indexOf( aErrRef) >= 0 && ParseReference( aUpper, &aErrRef))
4627 {
4628 if (mbRewind)
4629 goto Label_Rewind;
4630 return true;
4631 }
4632
4634 {
4635 // set an error
4636 SetError( FormulaError::NoName );
4638 return false; // end compilation
4639 }
4640
4641 // Provide single token information and continue. Do not set an error, that
4642 // would prematurely end compilation. Simple unknown names are handled by
4643 // the interpreter.
4644 // Use the same CharClass that was used for uppercase.
4645 aUpper = ((bAsciiUpper || mbCharClassesDiffer) ? ScGlobal::getCharClass() : *pCharClass).lowercase( aUpper );
4649 if ( bAutoCorrect )
4651 return true;
4652}
4653
4654void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp )
4655{
4656 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL;
4657 sal_uInt16 nExpectedCount = bExternal ? 2 : 1;
4658 OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" );
4659 if( pArr->GetLen() == nExpectedCount )
4660 {
4661 FormulaToken** ppTokens = pArr->GetArray();
4662 // string tokens expected, GetString() will assert if token type is wrong
4663 rFormula = ppTokens[0]->GetString().getString();
4664 if( bExternal )
4665 rFormulaNmsp = ppTokens[1]->GetString().getString();
4666 }
4667}
4668
4669namespace {
4670
4671class ExternalFileInserter
4672{
4673 ScAddress maPos;
4674 ScExternalRefManager& mrRefMgr;
4675public:
4676 ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) :
4677 maPos(rPos), mrRefMgr(rRefMgr) {}
4678
4679 void operator() (sal_uInt16 nFileId) const
4680 {
4681 mrRefMgr.insertRefCell(nFileId, maPos);
4682 }
4683};
4684
4685}
4686
4687std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula )
4688{
4689 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" );
4690 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL )
4691 SetGrammar( FormulaGrammar::GRAM_PODF );
4692
4694 pArr = &aArr;
4696 aFormula = comphelper::string::strip(rFormula, ' ');
4697
4698 nSrcPos = 0;
4699 bCorrected = false;
4700 if ( bAutoCorrect )
4701 {
4702 aCorrectedFormula.clear();
4703 aCorrectedSymbol.clear();
4704 }
4705 sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible
4706 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4707 {
4708 nSrcPos++;
4709 nForced++;
4710 if ( bAutoCorrect )
4711 aCorrectedFormula += "=";
4712 }
4713 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' )
4714 {
4715 nSrcPos++;
4716 nForced++;
4717 if ( bAutoCorrect )
4718 aCorrectedFormula += "=";
4719 }
4720 struct FunctionStack
4721 {
4722 OpCode eOp;
4723 short nSep;
4724 };
4725 // FunctionStack only used if PODF or OOXML!
4726 bool bPODF = FormulaGrammar::isPODF( meGrammar);
4727 bool bOOXML = FormulaGrammar::isOOXML( meGrammar);
4728 bool bUseFunctionStack = (bPODF || bOOXML);
4729 const size_t nAlloc = 512;
4730 FunctionStack aFuncs[ nAlloc ];
4731 FunctionStack* pFunctionStack = (bUseFunctionStack && o3tl::make_unsigned(rFormula.getLength()) > nAlloc ?
4732 new FunctionStack[rFormula.getLength()] : &aFuncs[0]);
4733 pFunctionStack[0].eOp = ocNone;
4734 pFunctionStack[0].nSep = 0;
4735 size_t nFunction = 0;
4736 short nBrackets = 0;
4737 bool bInArray = false;
4738 eLastOp = ocOpen;
4739 while( NextNewToken( bInArray ) )
4740 {
4741 const OpCode eOp = maRawToken.GetOpCode();
4742 if (eOp == ocSkip)
4743 continue;
4744
4745 switch (eOp)
4746 {
4747 case ocOpen:
4748 {
4749 ++nBrackets;
4750 if (bUseFunctionStack)
4751 {
4752 ++nFunction;
4753 pFunctionStack[ nFunction ].eOp = eLastOp;
4754 pFunctionStack[ nFunction ].nSep = 0;
4755 }
4756 }
4757 break;
4758 case ocClose:
4759 {
4760 if( !nBrackets )
4761 {
4762 SetError( FormulaError::PairExpected );
4763 if ( bAutoCorrect )
4764 {
4765 bCorrected = true;
4766 aCorrectedSymbol.clear();
4767 }
4768 }
4769 else
4770 nBrackets--;
4771 if (bUseFunctionStack && nFunction)
4772 --nFunction;
4773 }
4774 break;
4775 case ocSep:
4776 {
4777 if (bUseFunctionStack)
4778 ++pFunctionStack[ nFunction ].nSep;
4779 }
4780 break;
4781 case ocArrayOpen:
4782 {
4783 if( bInArray )
4784 SetError( FormulaError::NestedArray );
4785 else
4786 bInArray = true;
4787 // Don't count following column separator as parameter separator.
4788 if (bUseFunctionStack)
4789 {
4790 ++nFunction;
4791 pFunctionStack[ nFunction ].eOp = eOp;
4792 pFunctionStack[ nFunction ].nSep = 0;
4793 }
4794 }
4795 break;
4796 case ocArrayClose:
4797 {
4798 if( bInArray )
4799 {
4800 bInArray = false;
4801 }
4802 else
4803 {
4804 SetError( FormulaError::PairExpected );
4805 if ( bAutoCorrect )
4806 {
4807 bCorrected = true;
4808 aCorrectedSymbol.clear();
4809 }
4810 }
4811 if (bUseFunctionStack && nFunction)
4812 --nFunction;
4813 }
4814 break;
4815 case ocTableRefOpen:
4816 {
4817 // Don't count following item separator as parameter separator.
4818 if (bUseFunctionStack)
4819 {
4820 ++nFunction;
4821 pFunctionStack[ nFunction ].eOp = eOp;
4822 pFunctionStack[ nFunction ].nSep = 0;
4823 }
4824 }
4825 break;
4826 case ocTableRefClose:
4827 {
4828 if (bUseFunctionStack && nFunction)
4829 --nFunction;
4830 }
4831 break;
4832 case ocColRowName:
4833 case ocColRowNameAuto:
4834 // The current implementation of column / row labels doesn't
4835 // function correctly in grouped cells.
4836 aArr.SetShareable(false);
4837 break;
4838 default:
4839 break;
4840 }
4841 if ((eLastOp != ocOpen || eOp != ocClose) &&
4842 (eLastOp == ocOpen ||
4843 eLastOp == ocSep ||
4846 eLastOp == ocArrayOpen) &&
4847 (eOp == ocSep ||
4848 eOp == ocClose ||
4849 eOp == ocArrayRowSep ||
4850 eOp == ocArrayColSep ||
4851 eOp == ocArrayClose))
4852 {
4853 // TODO: should we check for known functions with optional empty
4854 // args so the correction dialog can do better?
4855 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) )
4856 {
4857 SetError(FormulaError::CodeOverflow); break;
4858 }
4859 }
4860 if (bOOXML)
4861 {
4862 // Append a parameter for WEEKNUM, all 1.0
4863 // Function is already closed, parameter count is nSep+1
4864 size_t nFunc = nFunction + 1;
4865 if (eOp == ocClose &&
4866 (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start
4867 pFunctionStack[ nFunc ].nSep == 0))
4868 {
4869 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4870 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4871 {
4872 SetError(FormulaError::CodeOverflow); break;
4873 }
4874 }
4875 }
4876 else if (bPODF)
4877 {
4878 /* TODO: for now this is the only PODF adapter. If there were more,
4879 * factor this out. */
4880 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th.
4881 if (eOp == ocSep &&
4882 pFunctionStack[ nFunction ].eOp == ocAddress &&
4883 pFunctionStack[ nFunction ].nSep == 3)
4884 {
4885 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) ||
4886 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0)))
4887 {
4888 SetError(FormulaError::CodeOverflow); break;
4889 }
4890 ++pFunctionStack[ nFunction ].nSep;
4891 }
4892 }
4893 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(rDoc.GetSheetLimits()));
4894 if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose)
4895 {
4896 // Nested inline array or non-value/non-string in array. The
4897 // original tokens are still in the ScTokenArray and not merged
4898 // into an ScMatrixToken. Set error but keep on tokenizing.
4899 SetError( FormulaError::BadArrayContent);
4900 }
4901 else if (!pNewToken)
4902 {
4903 SetError(FormulaError::CodeOverflow);
4904 break;
4905 }
4906 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef)
4907 {
4908 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos);
4909 }
4910 else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen)
4911 {
4912 sal_uInt16 nIdx = pArr->GetLen() - 1;
4913 const FormulaToken* pPrev = pArr->PeekPrev( nIdx);
4914 if (pPrev && pPrev->GetOpCode() == ocDBArea)
4915 {
4916 FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE);
4917 maTableRefs.emplace_back( pTableRefToken);
4918 // pPrev may be dead hereafter.
4919 static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken,
4920 FormulaTokenArray::ReplaceMode::CODE_ONLY);
4921 }
4922 }
4923 switch (eOp)
4924 {
4925 case ocTableRefOpen:
4926 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry");
4927 if (maTableRefs.empty())
4928 SetError(FormulaError::Pair);
4929 else
4930 ++maTableRefs.back().mnLevel;
4931 break;
4932 case ocTableRefClose:
4933 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry");
4934 if (maTableRefs.empty())
4935 SetError(FormulaError::Pair);
4936 else
4937 {
4938 if (--maTableRefs.back().mnLevel == 0)
4939 maTableRefs.pop_back();
4940 }
4941 break;
4942 default:
4943 break;
4944 }
4946 if ( bAutoCorrect )
4948 }
4949 if ( mbCloseBrackets )
4950 {
4951 if( bInArray )
4952 {
4954 if( !pArr->AddToken( aToken ) )
4955 {
4956 SetError(FormulaError::CodeOverflow);
4957 }
4958 else if ( bAutoCorrect )
4960 }
4961
4962 if (nBrackets)
4963 {
4964 FormulaToken aToken( svSep, ocClose );
4965 while( nBrackets-- )
4966 {
4967 if( !pArr->AddToken( aToken ) )
4968 {
4969 SetError(FormulaError::CodeOverflow);
4970 break; // while
4971 }
4972 if ( bAutoCorrect )
4973 aCorrectedFormula += mxSymbols->getSymbol(ocClose);
4974 }
4975 }
4976 }
4977 if ( nForced >= 2 )
4979
4980 if (pFunctionStack != &aFuncs[0])
4981 delete [] pFunctionStack;
4982
4983 // remember pArr, in case a subsequent CompileTokenArray() is executed.
4984 std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr ));
4985 pNew->GenHash();
4986 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
4987 pArr = pNew.get();
4989
4990 if (!maExternalFiles.empty())
4991 {
4992 // Remove duplicates, and register all external files found in this cell.
4993 std::sort(maExternalFiles.begin(), maExternalFiles.end());
4994 std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end());
4995 std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *rDoc.GetExternalRefManager()));
4996 maExternalFiles.erase(itEnd, maExternalFiles.end());
4997 }
4998
4999 return pNew;
5000}
5001
5002std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp )
5003{
5004 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(),
5005 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" );
5006 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try
5007 {
5009 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW );
5010 table::CellAddress aReferencePos;
5011 ScUnoConversion::FillApiAddress( aReferencePos, aPos );
5012 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos );
5013 ScTokenArray aTokenArray(rDoc);
5014 if( ScTokenConversion::ConvertToTokenArray( rDoc, aTokenArray, aTokenSeq ) )
5015 {
5016 // remember pArr, in case a subsequent CompileTokenArray() is executed.
5017 std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray ));
5018 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid
5019 pArr = pNew.get();
5021 return pNew;
5022 }
5023 }
5024 catch( uno::Exception& )
5025 {
5026 }
5027 // no success - fallback to some internal grammar and hope the best
5028 return CompileString( rFormula );
5029}
5030
5032{
5033 return rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
5034}