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