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