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 rDoc.FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex());
5038}
5039
5041{
5042 ScTokenArray* pNew;
5043 const ScRangeData* pRangeData = GetRangeData( *mpToken);
5044 if (pRangeData)
5045 {
5046 FormulaError nErr = pRangeData->GetErrCode();
5047 if( nErr != FormulaError::NONE )
5048 SetError( nErr );
5049 else if (mbJumpCommandReorder)
5050 {
5051 // put named formula into parentheses.
5052 // But only if there aren't any yet, parenthetical
5053 // ocSep doesn't work, e.g. SUM((...;...))
5054 // or if not directly between ocSep/parenthesis,
5055 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes,
5056 // in short: if it isn't a self-contained expression.
5059 OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep);
5060 OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep);
5061 bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen);
5062 bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose);
5063 bool bAddPair = !(bBorder1 && bBorder2);
5064 if ( bAddPair )
5065 {
5066 pNew = new ScTokenArray(rDoc);
5067 pNew->AddOpCode( ocClose );
5068 PushTokenArray( pNew, true );
5069 }
5070 pNew = pRangeData->GetCode()->Clone().release();
5071 pNew->SetFromRangeName( true );
5072 PushTokenArray( pNew, true );
5073 if( pRangeData->HasReferences() )
5074 {
5075 // Relative sheet references in sheet-local named expressions
5076 // shall still point to the same sheet as if used on the
5077 // original sheet, not shifted to the current position where
5078 // they are used.
5079 SCTAB nSheetTab = mpToken->GetSheet();
5080 if (nSheetTab >= 0 && nSheetTab != aPos.Tab())
5082
5084 MoveRelWrap();
5085 }
5087 if ( bAddPair )
5088 {
5089 pNew = new ScTokenArray(rDoc);
5090 pNew->AddOpCode( ocOpen );
5091 PushTokenArray( pNew, true );
5092 }
5093 return GetToken();
5094 }
5095 }
5096 else
5097 {
5098 // No ScRangeData for an already compiled token can happen in BIFF .xls
5099 // import if the original range is not present in the document.
5100 pNew = new ScTokenArray(rDoc);
5101 pNew->Add( new FormulaErrorToken( FormulaError::NoName));
5102 PushTokenArray( pNew, true );
5103 return GetToken();
5104 }
5105 return true;
5106}
5107
5109{
5110 // Handle external range names.
5111 switch (_aToken.GetType())
5112 {
5115 break;
5116 case svExternalName:
5117 {
5119 const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex());
5120 if (!pFile)
5121 {
5122 SetError(FormulaError::NoName);
5123 return true;
5124 }
5125
5126 OUString aName = _aToken.GetString().getString();
5128 _aToken.GetIndex(), aName, &aPos);
5129
5130 if (!xNew)
5131 {
5132 SetError(FormulaError::NoName);
5133 return true;
5134 }
5135
5136 ScTokenArray* pNew = xNew->Clone().release();
5137 PushTokenArray( pNew, true);
5138 if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr)
5139 {
5141 MoveRelWrap();
5142 }
5144 return GetToken();
5145 }
5146 default:
5147 OSL_FAIL("Wrong type for external reference!");
5148 return false;
5149 }
5150 return true;
5151}
5152
5154{
5155 for ( auto t: pArr->References() )
5156 {
5157 ScSingleRefData& rRef1 = *t->GetSingleRef();
5158 if (rRef1.IsTabRel())
5159 rRef1.IncTab( nDelta);
5160 if ( t->GetType() == svDoubleRef )
5161 {
5162 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5163 if (rRef2.IsTabRel())
5164 rRef2.IncTab( nDelta);
5165 }
5166 }
5167}
5168
5169// reference of named range with relative references
5170
5172{
5173 for ( auto t: pArr->References() )
5174 {
5175 ScSingleRefData& rRef1 = *t->GetSingleRef();
5176 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() )
5177 rRef1.SetRelName( true );
5178 if ( t->GetType() == svDoubleRef )
5179 {
5180 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2;
5181 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() )
5182 rRef2.SetRelName( true );
5183 }
5184 }
5185}
5186
5187// Wrap-adjust relative references of a RangeName to current position,
5188// don't call for other token arrays!
5190{
5191 for ( auto t: pArr->References() )
5192 {
5193 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
5195 else
5196 ScRefUpdate::MoveRelWrap( rDoc, aPos, rDoc.MaxCol(), rDoc.MaxRow(), *t->GetDoubleRef() );
5197 }
5198}
5199
5200// Wrap-adjust relative references of a RangeName to current position,
5201// don't call for other token arrays!
5202void ScCompiler::MoveRelWrap( const ScTokenArray& rArr, const ScDocument& rDoc, const ScAddress& rPos,
5203 SCCOL nMaxCol, SCROW nMaxRow )
5204{
5205 for ( auto t: rArr.References() )
5206 {
5207 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef )
5208 ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() );
5209 else
5210 ScRefUpdate::MoveRelWrap( rDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() );
5211 }
5212}
5213
5215 OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags )
5216{
5217 sal_Unicode c = rStr[ nPos ];
5218 sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0;
5219 if (c < 128)
5220 {
5223 {
5224 if (pConventions[nConv] &&
5225 ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags))
5226 return false;
5227 // convention not known => assume valid
5228 }
5229 return true;
5230 }
5231 else
5233}
5234
5235void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
5236{
5237 const FormulaToken* t = pTokenP;
5238 sal_uInt16 nFileId = t->GetIndex();
5240 sal_uInt16 nUsedFileId = pRefMgr->convertFileIdToUsedFileId(nFileId);
5241 const OUString* pFileName = pRefMgr->getExternalFileName(nFileId);
5242 if (!pFileName)
5243 return;
5244
5245 switch (t->GetType())
5246 {
5247 case svExternalName:
5248 rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString()));
5249 break;
5252 rBuffer, GetPos(), nUsedFileId, *pFileName, t->GetString().getString(),
5253 *t->GetSingleRef());
5254 break;
5256 {
5257 vector<OUString> aTabNames;
5258 pRefMgr->getAllCachedTableNames(nFileId, aTabNames);
5259 // No sheet names is a valid case if external sheets were not
5260 // cached in this document and external document is not reachable,
5261 // else not and worth to be investigated.
5262 SAL_WARN_IF( aTabNames.empty(), "sc.core", "wrecked cache of external document? '" <<
5263 *pFileName << "' '" << t->GetString().getString() << "'");
5264
5266 rDoc.GetSheetLimits(), rBuffer, GetPos(), nUsedFileId, *pFileName, aTabNames, t->GetString().getString(),
5267 *t->GetDoubleRef());
5268 }
5269 break;
5270 default:
5271 // warning, not error, otherwise we may end up with a never
5272 // ending message box loop if this was the cursor cell to be redrawn.
5273 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef");
5274 }
5275}
5276
5277void ScCompiler::CreateStringFromMatrix( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const
5278{
5279 const ScMatrix* pMatrix = pTokenP->GetMatrix();
5280 SCSIZE nC, nMaxC, nR, nMaxR;
5281
5282 pMatrix->GetDimensions( nMaxC, nMaxR);
5283
5284 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) );
5285 for( nR = 0 ; nR < nMaxR ; nR++)
5286 {
5287 if( nR > 0)
5288 {
5289 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) );
5290 }
5291
5292 for( nC = 0 ; nC < nMaxC ; nC++)
5293 {
5294 if( nC > 0)
5295 {
5296 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) );
5297 }
5298
5299 if( pMatrix->IsValue( nC, nR ) )
5300 {
5301 if (pMatrix->IsBoolean(nC, nR))
5302 AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0);
5303 else
5304 {
5305 FormulaError nErr = pMatrix->GetError(nC, nR);
5306 if (nErr != FormulaError::NONE)
5307 rBuffer.append(ScGlobal::GetErrorString(nErr));
5308 else
5309 AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR));
5310 }
5311 }
5312 else if( pMatrix->IsEmpty( nC, nR ) )
5313 ;
5314 else if( pMatrix->IsStringOrEmpty( nC, nR ) )
5315 AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() );
5316 }
5317 }
5318 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) );
5319}
5320
5321namespace {
5322void escapeTableRefColumnSpecifier( OUString& rStr )
5323{
5324 const sal_Int32 n = rStr.getLength();
5325 OUStringBuffer aBuf( n * 2 );
5326 const sal_Unicode* p = rStr.getStr();
5327 const sal_Unicode* const pStop = p + n;
5328 for ( ; p < pStop; ++p)
5329 {
5330 const sal_Unicode c = *p;
5331 switch (c)
5332 {
5333 case '\'':
5334 case '[':
5335 case '#':
5336 case ']':
5337 aBuf.append( '\'' );
5338 break;
5339 default:
5340 ; // nothing
5341 }
5342 aBuf.append( c );
5343 }
5344 rStr = aBuf.makeStringAndClear();
5345}
5346}
5347
5348void ScCompiler::CreateStringFromSingleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5349{
5350 const FormulaToken* p;
5351 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
5352 const OpCode eOp = _pTokenP->GetOpCode();
5353 const ScSingleRefData& rRef = *_pTokenP->GetSingleRef();
5354 ScComplexRefData aRef;
5355 aRef.Ref1 = aRef.Ref2 = rRef;
5356 if ( eOp == ocColRowName )
5357 {
5358 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
5359 if (rDoc.HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab()))
5360 {
5361 OUString aStr = rDoc.GetString(aAbs, mpInterpreterContext);
5362 // Enquote to SingleQuoted.
5363 aStr = aStr.replaceAll(u"'", u"''");
5364 rBuffer.append('\'');
5365 rBuffer.append(aStr);
5366 rBuffer.append('\'');
5367 }
5368 else
5369 {
5370 rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
5371 pConv->makeRefStr(rDoc.GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
5372 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
5373 }
5374 }
5375 else if (pArr && (p = maArrIterator.PeekPrevNoSpaces()) && p->GetOpCode() == ocTableRefOpen)
5376 {
5377 OUString aStr;
5378 ScAddress aAbs = rRef.toAbs(rDoc, aPos);
5379 const ScDBData* pData = rDoc.GetDBAtCursor( aAbs.Col(), aAbs.Row(), aAbs.Tab(), ScDBDataPortion::AREA);
5380 SAL_WARN_IF( !pData, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " <<
5382 if (pData)
5383 aStr = pData->GetTableColumnName( aAbs.Col());
5384 if (aStr.isEmpty())
5385 {
5386 if (pData && pData->HasHeader())
5387 {
5388 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " <<
5391 }
5392 else
5393 {
5394 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " <<
5396 aStr = aErrRef;
5397 }
5398 }
5399 escapeTableRefColumnSpecifier( aStr);
5400 rBuffer.append(aStr);
5401 }
5402 else
5403 pConv->makeRefStr(rDoc.GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef,
5404 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName()));
5405}
5406
5407void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5408{
5409 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef);
5411 *_pTokenP->GetDoubleRef(), false, (pArr && pArr->IsFromRangeName()));
5412}
5413
5414void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const
5415{
5416 const OpCode eOp = _pTokenP->GetOpCode();
5417 OUStringBuffer aBuffer;
5418 switch ( eOp )
5419 {
5420 case ocName:
5421 {
5422 const ScRangeData* pData = GetRangeData( *_pTokenP);
5423 if (pData)
5424 {
5425 SCTAB nTab = _pTokenP->GetSheet();
5426 if (nTab >= 0 && (nTab != aPos.Tab() || mbRefConventionChartOOXML))
5427 {
5428 // Sheet-local on other sheet.
5429 OUString aName;
5430 if (rDoc.GetName( nTab, aName))
5431 {
5433 aBuffer.append( aName);
5434 }
5435 else
5438 }
5440 {
5441 aBuffer.append("[0]"
5443 }
5444 aBuffer.append(pData->GetName());
5445 }
5446 }
5447 break;
5448 case ocDBArea:
5449 {
5450 const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
5451 if (pDBData)
5452 aBuffer.append(pDBData->GetName());
5453 }
5454 break;
5455 case ocTableRef:
5456 {
5458 {
5459 // Write the resulting reference if TableRef is not supported.
5460 const ScTableRefToken* pTR = dynamic_cast<const ScTableRefToken*>(_pTokenP);
5461 if (!pTR)
5462 AppendErrorConstant( aBuffer, FormulaError::NoCode);
5463 else
5464 {
5465 const FormulaToken* pRef = pTR->GetAreaRefRPN();
5466 if (!pRef)
5467 AppendErrorConstant( aBuffer, FormulaError::NoCode);
5468 else
5469 {
5470 switch (pRef->GetType())
5471 {
5472 case svSingleRef:
5474 break;
5475 case svDoubleRef:
5477 break;
5478 case svError:
5480 break;
5481 default:
5482 AppendErrorConstant( aBuffer, FormulaError::NoCode);
5483 }
5484 }
5485 }
5486 }
5487 else
5488 {
5489 const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex());
5490 if (pDBData)
5491 aBuffer.append(pDBData->GetName());
5492 }
5493 }
5494 break;
5495 default:
5496 ; // nothing
5497 }
5498 if ( !aBuffer.isEmpty() )
5499 rBuffer.append(aBuffer);
5500 else
5501 rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName));
5502}
5503
5504void ScCompiler::LocalizeString( OUString& rName ) const
5505{
5507}
5508
5510{
5511 return extendRangeReference( rDoc.GetSheetLimits(), rTok1, rTok2, aPos, true/*bReuseDoubleRef*/ );
5512}
5513
5514void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const
5515{
5516 // All known AddIn functions.
5517 sheet::FormulaOpCodeMapEntry aEntry;
5518 aEntry.Token.OpCode = ocExternal;
5519
5520 const LanguageTag aEnglishLanguageTag(LANGUAGE_ENGLISH_US);
5522 const tools::Long nCount = pColl->GetFuncCount();
5523 for (tools::Long i=0; i < nCount; ++i)
5524 {
5525 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i);
5526 if (pFuncData)
5527 {
5528 if ( _bIsEnglish )
5529 {
5530 // This is used with OOXML import, so GetExcelName() is really
5531 // wanted here until we'll have a parameter to differentiate
5532 // from the general css::sheet::XFormulaOpCodeMapper case and
5533 // use pFuncData->GetUpperEnglish().
5534 OUString aName;
5535 if (pFuncData->GetExcelName( aEnglishLanguageTag, aName))
5536 aEntry.Name = aName;
5537 else
5538 aEntry.Name = pFuncData->GetUpperName();
5539 }
5540 else
5541 aEntry.Name = pFuncData->GetUpperLocal();
5542 aEntry.Token.Data <<= pFuncData->GetOriginalName();
5543 _rVec.push_back( aEntry);
5544 }
5545 }
5546 // FIXME: what about those old non-UNO AddIns?
5547}
5548
5550{
5551 ScSingleRefData& rRef = *mpToken->GetSingleRef();
5552 const ScAddress aAbs = rRef.toAbs(rDoc, aPos);
5553 if (!rDoc.ValidAddress(aAbs))
5554 {
5555 SetError( FormulaError::NoRef );
5556 return true;
5557 }
5558 SCCOL nCol = aAbs.Col();
5559 SCROW nRow = aAbs.Row();
5560 SCTAB nTab = aAbs.Tab();
5561 bool bColName = rRef.IsColRel();
5562 SCCOL nMyCol = aPos.Col();
5563 SCROW nMyRow = aPos.Row();
5564 bool bInList = false;
5565 bool bValidName = false;
5566 ScRangePairList* pRL = (bColName ?
5568 ScRange aRange;
5569 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5570 {
5571 const ScRangePair & rR = (*pRL)[i];
5572 if ( rR.GetRange(0).Contains( aAbs ) )
5573 {
5574 bInList = bValidName = true;
5575 aRange = rR.GetRange(1);
5576 if ( bColName )
5577 {
5578 aRange.aStart.SetCol( nCol );
5579 aRange.aEnd.SetCol( nCol );
5580 }
5581 else
5582 {
5583 aRange.aStart.SetRow( nRow );
5584 aRange.aEnd.SetRow( nRow );
5585 }
5586 break; // for
5587 }
5588 }
5589 if ( !bInList && rDoc.GetDocOptions().IsLookUpColRowNames() )
5590 { // automagically or created by copying and NamePos isn't in list
5591 ScRefCellValue aCell(rDoc, aAbs);
5592 bool bString = aCell.hasString();
5593 if (!bString && aCell.isEmpty())
5594 bString = true; // empty cell is ok
5595 if ( bString )
5596 { // corresponds with ScInterpreter::ScColRowNameAuto()
5597 bValidName = true;
5598 if ( bColName )
5599 { // ColName
5600 SCROW nStartRow = nRow + 1;
5601 if ( nStartRow > rDoc.MaxRow() )
5602 nStartRow = rDoc.MaxRow();
5603 SCROW nMaxRow = rDoc.MaxRow();
5604 if ( nMyCol == nCol )
5605 { // formula cell in same column
5606 if ( nMyRow == nStartRow )
5607 { // take remainder under name cell
5608 nStartRow++;
5609 if ( nStartRow > rDoc.MaxRow() )
5610 nStartRow = rDoc.MaxRow();
5611 }
5612 else if ( nMyRow > nStartRow )
5613 { // from name cell down to formula cell
5614 nMaxRow = nMyRow - 1;
5615 }
5616 }
5617 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5618 { // next defined ColNameRange below limits row
5619 const ScRangePair & rR = (*pRL)[i];
5620 const ScRange& rRange = rR.GetRange(1);
5621 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() )
5622 { // identical column range
5623 SCROW nTmp = rRange.aStart.Row();
5624 if ( nStartRow < nTmp && nTmp <= nMaxRow )
5625 nMaxRow = nTmp - 1;
5626 }
5627 }
5628 aRange.aStart.Set( nCol, nStartRow, nTab );
5629 aRange.aEnd.Set( nCol, nMaxRow, nTab );
5630 }
5631 else
5632 { // RowName
5633 SCCOL nStartCol = nCol + 1;
5634 if ( nStartCol > rDoc.MaxCol() )
5635 nStartCol = rDoc.MaxCol();
5636 SCCOL nMaxCol = rDoc.MaxCol();
5637 if ( nMyRow == nRow )
5638 { // formula cell in same row
5639 if ( nMyCol == nStartCol )
5640 { // take remainder right from name cell
5641 nStartCol++;
5642 if ( nStartCol > rDoc.MaxCol() )
5643 nStartCol = rDoc.MaxCol();
5644 }
5645 else if ( nMyCol > nStartCol )
5646 { // from name cell right to formula cell
5647 nMaxCol = nMyCol - 1;
5648 }
5649 }
5650 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i )
5651 { // next defined RowNameRange to the right limits column
5652 const ScRangePair & rR = (*pRL)[i];
5653 const ScRange& rRange = rR.GetRange(1);
5654 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() )
5655 { // identical row range
5656 SCCOL nTmp = rRange.aStart.Col();
5657 if ( nStartCol < nTmp && nTmp <= nMaxCol )
5658 nMaxCol = nTmp - 1;
5659 }
5660 }
5661 aRange.aStart.Set( nStartCol, nRow, nTab );
5662 aRange.aEnd.Set( nMaxCol, nRow, nTab );
5663 }
5664 }
5665 }
5666 if ( bValidName )
5667 {
5668 // And now the magic to distinguish between a range and a single
5669 // cell thereof, which is picked position-dependent of the formula
5670 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a
5671 // SingleRef matching the column/row of the formula cell is
5672 // generated. A ocColRowName or ocIntersect as a neighbor results
5673 // in a range. Special case: if label is valid for a single cell, a
5674 // position independent SingleRef is generated.
5675 bool bSingle = (aRange.aStart == aRange.aEnd);
5676 bool bFound;
5677 if ( bSingle )
5678 bFound = true;
5679 else
5680 {
5683 // begin/end of a formula => single
5684 OpCode eOp1 = p1 ? p1->GetOpCode() : ocAdd;
5685 OpCode eOp2 = p2 ? p2->GetOpCode() : ocAdd;
5686 if ( eOp1 != ocColRowName && eOp1 != ocIntersect
5687 && eOp2 != ocColRowName && eOp2 != ocIntersect )
5688 {
5689 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) ||
5691 bSingle = true;
5692 }
5693 if ( bSingle )
5694 { // column and/or row must match range
5695 if ( bColName )
5696 {
5697 bFound = (aRange.aStart.Row() <= nMyRow
5698 && nMyRow <= aRange.aEnd.Row());
5699 if ( bFound )
5700 aRange.aStart.SetRow( nMyRow );
5701 }
5702 else
5703 {
5704 bFound = (aRange.aStart.Col() <= nMyCol
5705 && nMyCol <= aRange.aEnd.Col());
5706 if ( bFound )
5707 aRange.aStart.SetCol( nMyCol );
5708 }
5709 }
5710 else
5711 bFound = true;
5712 }
5713 if ( !bFound )
5714 SetError(FormulaError::NoRef);
5715 else if (mbJumpCommandReorder)
5716 {
5717 ScTokenArray* pNew = new ScTokenArray(rDoc);
5718 if ( bSingle )
5719 {
5720 ScSingleRefData aRefData;
5721 aRefData.InitAddress( aRange.aStart );
5722 if ( bColName )
5723 aRefData.SetColRel( true );
5724 else
5725 aRefData.SetRowRel( true );
5726 aRefData.SetAddress(rDoc.GetSheetLimits(), aRange.aStart, aPos);
5727 pNew->AddSingleReference( aRefData );
5728 }
5729 else
5730 {
5731 ScComplexRefData aRefData;
5732 aRefData.InitRange( aRange );
5733 if ( bColName )
5734 {
5735 aRefData.Ref1.SetColRel( true );
5736 aRefData.Ref2.SetColRel( true );
5737 }
5738 else
5739 {
5740 aRefData.Ref1.SetRowRel( true );
5741 aRefData.Ref2.SetRowRel( true );
5742 }
5743 aRefData.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
5744 if ( bInList )
5745 pNew->AddDoubleReference( aRefData );
5746 else
5747 { // automagically
5748 pNew->Add( new ScDoubleRefToken( rDoc.GetSheetLimits(), aRefData, ocColRowNameAuto ) );
5749 }
5750 }
5751 PushTokenArray( pNew, true );
5752 return GetToken();
5753 }
5754 }
5755 else
5756 SetError(FormulaError::NoName);
5757 return true;
5758}
5759
5761{
5762 ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex());
5763 if ( !pDBData )
5764 SetError(FormulaError::NoName);
5765 else if (mbJumpCommandReorder)
5766 {
5767 ScComplexRefData aRefData;
5768 aRefData.InitFlags();
5769 ScRange aRange;
5770 pDBData->GetArea(aRange);
5771 aRange.aEnd.SetTab(aRange.aStart.Tab());
5772 aRefData.SetRange(rDoc.GetSheetLimits(), aRange, aPos);
5773 ScTokenArray* pNew = new ScTokenArray(rDoc);
5774 pNew->AddDoubleReference( aRefData );
5775 PushTokenArray( pNew, true );
5776 return GetToken();
5777 }
5778 return true;
5779}
5780
5782{
5784 if (p && p->GetOpCode() == eOp)
5785 return GetToken();
5786 return false;
5787}
5788
5789
5790/* Documentation on MS-Excel Table structured references:
5791 * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f
5792 * * as of Excel 2013
5793 * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx
5794 * * look for structure-reference
5795 */
5796
5798{
5799 ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(mpToken.get());
5800 if (!pTR)
5801 {
5802 SetError(FormulaError::UnknownToken);
5803 return true;
5804 }
5805
5807 if ( !pDBData )
5808 SetError(FormulaError::NoName);
5809 else if (mbJumpCommandReorder)
5810 {
5811 ScRange aDBRange;
5812 pDBData->GetArea(aDBRange);
5813 aDBRange.aEnd.SetTab(aDBRange.aStart.Tab());
5814 ScRange aRange( aDBRange);
5815 FormulaError nError = FormulaError::NONE;
5816 bool bForwardToClose = false;
5817 ScTableRefToken::Item eItem = pTR->GetItem();
5818 switch (eItem)
5819 {
5821 {
5822 // The table name without items references the table data,
5823 // without headers or totals.
5824 if (pDBData->HasHeader())
5825 aRange.aStart.IncRow();
5826 if (pDBData->HasTotals())
5827 aRange.aEnd.IncRow(-1);
5828 if (aRange.aEnd.Row() < aRange.aStart.Row())
5829 nError = FormulaError::NoRef;
5830 bForwardToClose = true;
5831 }
5832 break;
5834 {
5835 bForwardToClose = true;
5836 }
5837 break;
5839 {
5840 if (pDBData->HasHeader())
5841 aRange.aEnd.SetRow( aRange.aStart.Row());
5842 else
5843 nError = FormulaError::NoRef;
5844 bForwardToClose = true;
5845 }
5846 break;
5848 {
5849 if (pDBData->HasHeader())
5850 aRange.aStart.IncRow();
5851 }
5852 [[fallthrough]];
5854 {
5855 if (pDBData->HasTotals())
5856 aRange.aEnd.IncRow(-1);
5857 if (aRange.aEnd.Row() < aRange.aStart.Row())
5858 nError = FormulaError::NoRef;
5859 bForwardToClose = true;
5860 }
5861 break;
5863 {
5864 if (pDBData->HasTotals())
5865 aRange.aStart.SetRow( aRange.aEnd.Row());
5866 else
5867 nError = FormulaError::NoRef;
5868 bForwardToClose = true;
5869 }
5870 break;
5872 {
5873 if (pDBData->HasHeader())
5874 aRange.aStart.IncRow();
5875 if (aRange.aEnd.Row() < aRange.aStart.Row())
5876 nError = FormulaError::NoRef;
5877 bForwardToClose = true;
5878 }
5879 break;
5881 {
5882 if (aRange.aStart.Row() <= aPos.Row() && aPos.Row() <= aRange.aEnd.Row())
5883 {
5884 aRange.aStart.SetRow( aPos.Row());
5885 aRange.aEnd.SetRow( aPos.Row());
5886 }
5887 else
5888 {
5889 nError = FormulaError::NoValue;
5890 // For *some* relative row reference in named
5891 // expressions' thisrow special handling below.
5892 aRange.aEnd.SetRow( aRange.aStart.Row());
5893 }
5894 bForwardToClose = true;
5895 }
5896 break;
5897 }
5898 bool bColumnRange = false;
5899 bool bCol1Rel = false;
5900 bool bCol1RelName = false;
5901 int nLevel = 0;
5902 if (bForwardToClose && GetTokenIfOpCode( ocTableRefOpen))
5903 {
5904 ++nLevel;
5905 enum
5906 {
5907 sOpen,
5908 sItem,
5909 sClose,
5910 sSep,
5911 sLast,
5912 sStop
5913 } eState = sOpen;
5914 do
5915 {
5917 if (!p)
5918 eState = sStop;
5919 else
5920 {
5921 switch (p->GetOpCode())
5922 {
5923 case ocTableRefOpen:
5924 eState = ((eState == sOpen || eState == sSep) ? sOpen : sStop);
5925 if (++nLevel > 2)
5926 {
5927 SetError( FormulaError::Pair);
5928 eState = sStop;
5929 }
5930 break;
5931 case ocTableRefItemAll:
5933 case ocTableRefItemData:
5936 eState = ((eState == sOpen) ? sItem : sStop);
5937 break;
5938 case ocTableRefClose:
5939 eState = ((eState == sItem || eState == sClose) ? sClose : sStop);
5940 if (eState != sStop && --nLevel == 0)
5941 eState = sLast;
5942 break;
5943 case ocSep:
5944 eState = ((eState == sClose) ? sSep : sStop);
5945 break;
5946 case ocPush:
5947 if (eState == sOpen && p->GetType() == svSingleRef)
5948 {
5949 bColumnRange = true;
5950 bCol1Rel = p->GetSingleRef()->IsColRel();
5951 bCol1RelName = p->GetSingleRef()->IsRelName();
5952 eState = sLast;
5953 }
5954 else
5955 {
5956 eState = sStop;
5957 }
5958 break;
5959 case ocBad:
5960 eState = sLast;
5961 if (nError == FormulaError::NONE)
5962 nError = FormulaError::NoName;
5963 break;
5964 default:
5965 eState = sStop;
5966 }
5967 if (eState != sStop)
5968 GetToken();
5969 if (eState == sLast)
5970 eState = sStop;
5971 }
5972 } while (eState != sStop);
5973 }
5974 ScTokenArray* pNew = new ScTokenArray(rDoc);
5975 if (nError == FormulaError::NONE || nError == FormulaError::NoValue)
5976 {
5977 bool bCol2Rel = false;
5978 bool bCol2RelName = false;
5979 // The FormulaError::NoValue case generates a thisrow reference that can be
5980 // used to save named expressions in A1 syntax notation.
5981 if (bColumnRange)
5982 {
5983 // Limit range to specified columns.
5985 switch (mpToken->GetType())
5986 {
5987 case svSingleRef:
5988 {
5989 aColRange.aStart = aColRange.aEnd = mpToken->GetSingleRef()->toAbs(rDoc, aPos);
5990 if ( GetTokenIfOpCode( ocTableRefClose) && (nLevel--) &&
5992 GetTokenIfOpCode( ocTableRefOpen) && (++nLevel) &&
5994 {
5995 if (mpToken->GetType() != svSingleRef)
5997 else
5998 {
5999 aColRange.aEnd = mpToken->GetSingleRef()->toAbs(rDoc, aPos);
6000 aColRange.PutInOrder();
6001 bCol2Rel = mpToken->GetSingleRef()->IsColRel();
6002 bCol2RelName = mpToken->GetSingleRef()->IsRelName();
6003 }
6004 }
6005 }
6006 break;
6007 default:
6008 ; // nothing
6009 }
6010 // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses
6011 if (aColRange.aStart.Row() != aDBRange.aStart.Row() || aColRange.aEnd.Row() != aDBRange.aStart.Row())
6013 else
6014 {
6015 aColRange.aEnd.SetRow( aRange.aEnd.Row());
6016 aRange = aRange.Intersection( aColRange);
6017 }
6018 }
6019 if (aRange.IsValid())
6020 {
6021 if (aRange.aStart == aRange.aEnd)
6022 {
6023 ScSingleRefData aRefData;
6024 aRefData.InitFlags();
6025 aRefData.SetColRel( bCol1Rel);
6026 if (eItem == ScTableRefToken::THIS_ROW)
6027 {
6028 aRefData.SetRowRel( true);
6029 if (!bCol1RelName)
6030 bCol1RelName = pArr->IsFromRangeName();
6031 }
6032 aRefData.SetRelName( bCol1RelName);
6033 aRefData.SetFlag3D( true);
6034 if (nError != FormulaError::NONE)
6035 {
6036 aRefData.SetAddress( rDoc.GetSheetLimits(), aRange.aStart, aRange.aStart);
6037 pTR->SetAreaRefRPN( new ScSingleRefToken(rDoc.GetSheetLimits(), aRefData)); // set reference at TableRef
6038 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
6039 }
6040 else
6041 {
6042 aRefData.SetAddress( rDoc.GetSheetLimits(), aRange.aStart, aPos);
6043 pTR->SetAreaRefRPN( pNew->AddSingleReference( aRefData));
6044 }
6045 }
6046 else
6047 {
6048 ScComplexRefData aRefData;
6049 aRefData.InitFlags();
6050 aRefData.Ref1.SetColRel( bCol1Rel);
6051 aRefData.Ref2.SetColRel( bCol2Rel);
6052 bool bRelName = bCol1RelName || bCol2RelName;
6053 if (eItem == ScTableRefToken::THIS_ROW)
6054 {
6055 aRefData.Ref1.SetRowRel( true);
6056 aRefData.Ref2.SetRowRel( true);
6057 if (!bRelName)
6058 bRelName = pArr->IsFromRangeName();
6059 }
6060 aRefData.Ref1.SetRelName( bRelName);
6061 aRefData.Ref2.SetRelName( bRelName);
6062 aRefData.Ref1.SetFlag3D( true);
6063 if (nError != FormulaError::NONE)
6064 {
6065 aRefData.SetRange( rDoc.GetSheetLimits(), aRange, aRange.aStart);
6066 pTR->SetAreaRefRPN( new ScDoubleRefToken(rDoc.GetSheetLimits(), aRefData)); // set reference at TableRef
6067 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN
6068 }
6069 else
6070 {
6071 aRefData.SetRange( rDoc.GetSheetLimits(), aRange, aPos);
6072 pTR->SetAreaRefRPN( pNew->AddDoubleReference( aRefData));
6073 }
6074 }
6075 }
6076 else
6077 {
6078 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( FormulaError::NoRef)));
6079 }
6080 }
6081 else
6082 {
6083 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( nError)));
6084 }
6085 while (nLevel-- > 0)
6086 {
6088 SetError( FormulaError::Pair);
6089 }
6090 PushTokenArray( pNew, true );
6091 return GetToken();
6092 }
6093 return true;
6094}
6095
6097{
6098 return ScParameterClassification::GetParameterType( pToken, nParam);
6099}
6100
6102{
6104 return param == Value || param == Array;
6105}
6106
6108{
6109 if (mbMatrixFlag)
6110 return true;
6111 formula::ParamClass paramClass = token->GetInForceArray();
6112 if (paramClass == formula::ForceArray
6113 || paramClass == formula::ReferenceOrForceArray
6115 || paramClass == formula::ReferenceOrRefArray)
6116 {
6117 return true;
6118 }
6120 return returnType == formula::Reference;
6121}
6122
6124{
6125 if (!mbComputeII)
6126 return;
6127#ifdef DBG_UTIL
6128 if(!HandleIIOpCodeInternal(token, pppToken, nNumParams))
6130#else
6131 HandleIIOpCodeInternal(token, pppToken, nNumParams);
6132#endif
6133}
6134
6135// return true if opcode is handled
6137{
6138 if (nNumParams > 0 && *pppToken[0] == nullptr)
6139 return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray())
6140
6141 const OpCode nOpCode = token->GetOpCode();
6142
6143 if (nOpCode == ocPush)
6144 {
6145 if(token->GetType() == svDoubleRef)
6147 return true;
6148 }
6149 else if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
6150 {
6151 if (nNumParams != 3)
6152 return false;
6153
6154 if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
6155 return false;
6156
6157 if ((*pppToken[0])->GetType() != svDoubleRef)
6158 return false;
6159
6160 const StackVar eSumRangeType = (*pppToken[2])->GetType();
6161
6162 if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
6163 return false;
6164
6165 const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
6166
6167 ScComplexRefData aSumRange;
6168 if (eSumRangeType == svSingleRef)
6169 {
6170 aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef();
6171 aSumRange.Ref2 = aSumRange.Ref1;
6172 }
6173 else
6174 aSumRange = *(*pppToken[2])->GetDoubleRef();
6175
6176 CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
6177 // TODO mark parameters as handled
6178 return true;
6179 }
6180 else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
6181 {
6182 if (nNumParams != 1)
6183 return false;
6184
6185 if( !ParameterMayBeImplicitIntersection( token, 0 ))
6186 return false;
6188 return false;
6189
6190 if ((*pppToken[0])->GetType() != svDoubleRef)
6191 return false;
6192
6193 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6194 return true;
6195 }
6196 else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP
6197 && nOpCode != ocAnd && nOpCode != ocOr)
6198 || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == ocRoundDown)
6199 {
6200 if (nNumParams != 2)
6201 return false;
6202
6204 return false;
6206 return false;
6207
6208 // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
6209 if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix)
6210 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6211 if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix)
6212 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[1], token );
6213 return true;
6214 }
6215 else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < SC_OPCODE_STOP_UN_OP)
6216 || nOpCode == ocPercentSign)
6217 {
6218 if (nNumParams != 1)
6219 return false;
6220
6221 if( !ParameterMayBeImplicitIntersection( token, 0 ))
6222 return false;
6224 return false;
6225
6226 if ((*pppToken[0])->GetType() == svDoubleRef)
6227 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6228 return true;
6229 }
6230 else if (nOpCode == ocVLookup)
6231 {
6232 if (nNumParams != 3 && nNumParams != 4)
6233 return false;
6234
6236 return false;
6237
6238 assert( ParameterMayBeImplicitIntersection( token, 0 ));
6239 assert( !ParameterMayBeImplicitIntersection( token, 1 ));
6240 assert( ParameterMayBeImplicitIntersection( token, 2 ));
6241 assert( ParameterMayBeImplicitIntersection( token, 3 ));
6242 if ((*pppToken[2])->GetType() == svDoubleRef)
6243 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[2], token );
6244 if ((*pppToken[0])->GetType() == svDoubleRef)
6245 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
6246 if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef)
6247 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[3], token );
6248 // a range for the second parameters is not an implicit intersection
6249 mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] );
6250 return true;
6251 }
6252 else
6253 {
6254 bool possibleII = false;
6255 for( int i = 0; i < nNumParams; ++i )
6256 {
6258 && (*pppToken[i])->GetType() == svDoubleRef)
6259 {
6260 possibleII = true;
6261 break;
6262 }
6263 }
6264 if( !possibleII )
6265 {
6266 // all parameters have been handled, they are not implicit intersections
6267 for( int i = 0; i < nNumParams; ++i )
6268 mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] );
6269 return true;
6270 }
6271 }
6272
6273 return false;
6274}
6275
6277{
6279 {
6280 if( *item.parameterLocation != item.parameter ) // the parameter has been changed somehow
6281 continue;
6282 if( item.parameterLocation >= pCode ) // the location is not inside the code (pCode points after the end)
6283 continue;
6284 // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
6285 if( item.operation->IsInForceArray())
6286 continue;
6287 ReplaceDoubleRefII( item.parameterLocation );
6288 }
6290}
6291
6293{
6295}
6296
6298{
6299 const ScComplexRefData* pRange = (*ppDoubleRefTok)->GetDoubleRef();
6300 if (!pRange)
6301 return;
6302
6303 const ScComplexRefData& rRange = *pRange;
6304
6305 // Can't do optimization reliably in this case (when row references are absolute).
6306 // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100.
6307 // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then
6308 // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104)
6309 // instead of the #VALUE! errors we would expect. We need to know the formula-group length to
6310 // fix this, but that is unknown at this stage, so skip such cases.
6311 if (!rRange.Ref1.IsRowRel() && !rRange.Ref2.IsRowRel())
6312 return;
6313
6314 ScRange aAbsRange = rRange.toAbs(rDoc, aPos);
6315 if (aAbsRange.aStart == aAbsRange.aEnd)
6316 return; // Nothing to do (trivial case).
6317
6318 ScAddress aAddr;
6319
6320 if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos))
6321 return;
6322
6323 ScSingleRefData aSingleRef;
6324 aSingleRef.InitFlags();
6325 aSingleRef.SetColRel(rRange.Ref1.IsColRel());
6326 aSingleRef.SetRowRel(true);
6327 aSingleRef.SetTabRel(rRange.Ref1.IsTabRel());
6328 aSingleRef.SetAddress(rDoc.GetSheetLimits(), aAddr, aPos);
6329
6330 // Replace the original doubleref token with computed singleref token
6331 FormulaToken* pNewSingleRefTok = new ScSingleRefToken(rDoc.GetSheetLimits(), aSingleRef);
6332 (*ppDoubleRefTok)->DecRef();
6333 *ppDoubleRefTok = pNewSingleRefTok;
6334 pNewSingleRefTok->IncRef();
6335}
6336
6337bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr, const ScAddress& rFormulaPos)
6338{
6339 assert(rRange.aStart != rRange.aEnd);
6340
6341 bool bOk = false;
6342 SCCOL nMyCol = rFormulaPos.Col();
6343 SCROW nMyRow = rFormulaPos.Row();
6344 SCTAB nMyTab = rFormulaPos.Tab();
6345 SCCOL nCol = 0;
6346 SCROW nRow = 0;
6347 SCTAB nTab;
6348 nTab = rRange.aStart.Tab();
6349 if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
6350 {
6351 nRow = rRange.aStart.Row();
6352 if ( nRow == rRange.aEnd.Row() )
6353 {
6354 bOk = true;
6355 nCol = nMyCol;
6356 }
6357 else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
6358 && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
6359 {
6360 bOk = true;
6361 nCol = nMyCol;
6362 nRow = nMyRow;
6363 }
6364 }
6365 else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
6366 {
6367 nCol = rRange.aStart.Col();
6368 if ( nCol == rRange.aEnd.Col() )
6369 {
6370 bOk = true;
6371 nRow = nMyRow;
6372 }
6373 else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
6374 && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
6375 {
6376 bOk = true;
6377 nCol = nMyCol;
6378 nRow = nMyRow;
6379 }
6380 }
6381 if ( bOk )
6382 {
6383 if ( nTab == rRange.aEnd.Tab() )
6384 ; // all done
6385 else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
6386 nTab = nMyTab;
6387 else
6388 bOk = false;
6389 if ( bOk )
6390 rAdr.Set( nCol, nRow, nTab );
6391 }
6392
6393 return bOk;
6394}
6395
6396static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta)
6397{
6398 rXDelta = rRange.aEnd.Col() - rRange.aStart.Col();
6399 rYDelta = rRange.aEnd.Row() - rRange.aStart.Row();
6400}
6401
6403{
6404 ScRange aAbs = rSumRange.toAbs(rDoc, aPos);
6405
6406 // Current sum-range end col/row
6407 SCCOL nEndCol = aAbs.aEnd.Col();
6408 SCROW nEndRow = aAbs.aEnd.Row();
6409
6410 // Current behaviour is, we will get a #NAME? for the below case, so bail out.
6411 // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula
6412 // has a single-ref as the sum-range.
6413 if (!rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow))
6414 return false;
6415
6416 SCCOL nXDeltaSum = 0;
6417 SCROW nYDeltaSum = 0;
6418
6419 lcl_GetColRowDeltas(aAbs, nXDeltaSum, nYDeltaSum);
6420
6421 aAbs = rBaseRange.toAbs(rDoc, aPos);
6422 SCCOL nXDelta = 0;
6423 SCROW nYDelta = 0;
6424
6425 lcl_GetColRowDeltas(aAbs, nXDelta, nYDelta);
6426
6427 if (nXDelta == nXDeltaSum &&
6428 nYDelta == nYDeltaSum)
6429 return false; // shapes of base-range match current sum-range
6430
6431 // Try to make the sum-range to take the same shape as base-range,
6432 // by adjusting Ref2 member of rSumRange if the resultant sum-range don't
6433 // go out-of-bounds.
6434
6435 SCCOL nXInc = nXDelta - nXDeltaSum;
6436 SCROW nYInc = nYDelta - nYDeltaSum;
6437
6438 // Don't let a valid End[Col,Row] go beyond (rDoc.MaxCol(),rDoc.MaxRow()) to match
6439 // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks
6440 // the base-range by the (out-of-bound)amount clipped off the sum-range.
6441 // TODO: Probably we can optimize (from threading perspective) rBaseRange
6442 // by shrinking it here correspondingly (?)
6443 if (nEndCol + nXInc > rDoc.MaxCol())
6444 nXInc = rDoc.MaxCol() - nEndCol;
6445 if (nEndRow + nYInc > rDoc.MaxRow())
6446 nYInc = rDoc.MaxRow() - nEndRow;
6447
6448 rSumRange.Ref2.IncCol(nXInc);
6449 rSumRange.Ref2.IncRow(nYInc);
6450
6451 return true;
6452}
6453
6455 ScComplexRefData& rSumRange,
6456 FormulaToken** ppSumRangeToken)
6457{
6458 if (!AdjustSumRangeShape(rBaseRange, rSumRange))
6459 return;
6460
6461 // Replace sum-range token
6462 FormulaToken* pNewSumRangeTok = new ScDoubleRefToken(rDoc.GetSheetLimits(), rSumRange);
6463 (*ppSumRangeToken)->DecRef();
6464 *ppSumRangeToken = pNewSumRangeTok;
6465 pNewSumRangeTok->IncRef();
6466}
6467
6469{
6470 if (!pCode || !(*(pCode - 1)))
6471 return;
6472
6473 // OpCode of the "root" operator (which is already in RPN array).
6474 OpCode eOpCode = (*(pCode - 1))->GetOpCode();
6475 // eOpCode can be some operator which does not change with operands with or contains zero values.
6476 if (eOpCode == ocSum)
6477 {
6478 FormulaToken** ppTok = pCode - 2; // exclude the root operator.
6479 // The following loop runs till a "pattern" is found or there is a mismatch
6480 // and marks the push DoubleRef arguments as trimmable when there is a match.
6481 // The pattern is
6482 // SUM(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6483 // such that one of the operands of ocEqual is a double-ref.
6484 // Examples of formula that matches this are:
6485 // SUM(IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)
6486 // SUM((IF(D:D=$A$1,F:F)*$H$1*2.3/$G$2)*$H$2*5/$G$3)
6487 // SUM(IF(E:E=16,F:F)*$H$1*100)
6488 bool bTillClose = true;
6489 bool bCloseTillIf = false;
6490 sal_Int16 nToksTillIf = 0;
6491 constexpr sal_Int16 MAXDIST_IF = 15;
6492 while (*ppTok)
6493 {
6494 FormulaToken* pTok = *ppTok;
6495 OpCode eCurrOp = pTok->GetOpCode();
6496 ++nToksTillIf;
6497
6498 // TODO : Is there a better way to handle this ?
6499 // ocIf is too far off from the sum opcode.
6500 if (nToksTillIf > MAXDIST_IF)
6501 return;
6502
6503 switch (eCurrOp)
6504 {
6505 case ocDiv:
6506 case ocMul:
6507 if (!bTillClose)
6508 return;
6509 break;
6510 case ocPush:
6511
6512 break;
6513 case ocClose:
6514 if (bTillClose)
6515 {
6516 bTillClose = false;
6517 bCloseTillIf = true;
6518 }
6519 else
6520 return;
6521 break;
6522 case ocIf:
6523 {
6524 if (!bCloseTillIf)
6525 return;
6526
6527 if (!pTok->IsInForceArray())
6528 return;
6529
6530 const short nJumpCount = pTok->GetJump()[0];
6531 if (nJumpCount != 2) // Should have THEN but no ELSE.
6532 return;
6533
6534 OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
6535 if (eCompOp != ocEqual)
6536 return;
6537
6538 FormulaToken* pLHS = *(ppTok - 2);
6539 FormulaToken* pRHS = *(ppTok - 3);
6540 if (((pLHS->GetType() == svSingleRef || pLHS->GetType() == svDouble) && pRHS->GetType() == svDoubleRef) ||
6541 ((pRHS->GetType() == svSingleRef || pRHS->GetType() == svDouble) && pLHS->GetType() == svDoubleRef))
6542 {
6543 if (pLHS->GetType() == svDoubleRef)
6544 pLHS->GetDoubleRef()->SetTrimToData(true);
6545 else
6546 pRHS->GetDoubleRef()->SetTrimToData(true);
6547 return;
6548 }
6549 }
6550 break;
6551 default:
6552 return;
6553 }
6554 --ppTok;
6555 }
6556 }
6557 else if (eOpCode == ocSumProduct)
6558 {
6559 FormulaToken** ppTok = pCode - 2; // exclude the root operator.
6560 // The following loop runs till a "pattern" is found or there is a mismatch
6561 // and marks the push DoubleRef arguments as trimmable when there is a match.
6562 // The pattern is
6563 // SUMPRODUCT(IF(<reference|double>=<reference|double>, <then-clause>)<a some operands with operators / or *>)
6564 // such that one of the operands of ocEqual is a double-ref.
6565 // Examples of formula that matches this are:
6566 // SUMPRODUCT(IF($A:$A=$L12;$D:$D*G:G))
6567 bool bTillClose = true;
6568 bool bCloseTillIf = false;
6569 sal_Int16 nToksTillIf = 0;
6570 constexpr sal_Int16 MAXDIST_IF = 15;
6571 while (*ppTok)
6572 {
6573 FormulaToken* pTok = *ppTok;
6574 OpCode eCurrOp = pTok->GetOpCode();
6575 ++nToksTillIf;
6576
6577 // TODO : Is there a better way to handle this ?
6578 // ocIf is too far off from the sum opcode.
6579 if (nToksTillIf > MAXDIST_IF)
6580 return;
6581
6582 switch (eCurrOp)
6583 {
6584 case ocDiv:
6585 case ocMul:
6586 {
6587 if (!pTok->IsInForceArray())
6588 break;
6589 FormulaToken* pLHS = *(ppTok - 1);
6590 FormulaToken* pRHS = *(ppTok - 2);
6591 StackVar lhsType = pLHS->GetType();
6592 StackVar rhsType = pRHS->GetType();
6593 if (lhsType == svDoubleRef && rhsType == svDoubleRef)
6594 {
6595 pLHS->GetDoubleRef()->SetTrimToData(true);
6596 pRHS->GetDoubleRef()->SetTrimToData(true);
6597 }
6598 }
6599 break;
6600 case ocPush:
6601 break;
6602 case ocClose:
6603 if (bTillClose)
6604 {
6605 bTillClose = false;
6606 bCloseTillIf = true;
6607 }
6608 else
6609 return;
6610 break;
6611 case ocIf:
6612 {
6613 if (!bCloseTillIf)
6614 return;
6615
6616 if (!pTok->IsInForceArray())
6617 return;
6618
6619 const short nJumpCount = pTok->GetJump()[0];
6620 if (nJumpCount != 2) // Should have THEN but no ELSE.
6621 return;
6622
6623 OpCode eCompOp = (*(ppTok - 1))->GetOpCode();
6624 if (eCompOp != ocEqual)
6625 return;
6626
6627 FormulaToken* pLHS = *(ppTok - 2);
6628 FormulaToken* pRHS = *(ppTok - 3);
6629 StackVar lhsType = pLHS->GetType();
6630 StackVar rhsType = pRHS->GetType();
6631 if (lhsType == svDoubleRef && (rhsType == svSingleRef || rhsType == svDouble))
6632 {
6633 pLHS->GetDoubleRef()->SetTrimToData(true);
6634 }
6635 if ((lhsType == svSingleRef || lhsType == svDouble) && rhsType == svDoubleRef)
6636 {
6637 pRHS->GetDoubleRef()->SetTrimToData(true);
6638 }
6639 return;
6640 }
6641 break;
6642 default:
6643 return;
6644 }
6645 --ppTok;
6646 }
6647 }
6648}
6649
6650/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
XPropertyListType t
const char * pName
void ScColToAlpha(OUStringBuffer &rBuf, SCCOL nCol)
append alpha representation of column to buffer
Definition: address.cxx:1884
bool ValidTab(SCTAB nTab)
Definition: address.hxx:111
ScRefFlags
Definition: address.hxx:158
size_t SCSIZE
size_t typedef to be able to find places where code was changed from USHORT to size_t and is used to ...
Definition: address.hxx:44
std::vector< ScAddress > ScAutoNameAddresses
static const AllSettings & GetSettings()
static bool isAsciiNumeric(std::u16string_view rStr)
static bool isAsciiAlpha(std::u16string_view rStr)
OUString uppercase(const OUString &rStr, sal_Int32 nPos, sal_Int32 nCount) const
css::i18n::ParseResult parseAnyToken(const OUString &rStr, sal_Int32 nPos, sal_Int32 nStartCharFlags, const OUString &userDefinedCharactersStart, sal_Int32 nContCharFlags, const OUString &userDefinedCharactersCont) const
const LanguageTag & getLanguageTag() const
bool isLetter(const OUString &rStr, sal_Int32 nPos) const
bool isLetterNumeric(const OUString &rStr, sal_Int32 nPos) const
css::i18n::ParseResult parsePredefinedToken(sal_Int32 nTokenType, const OUString &rStr, sal_Int32 nPos, sal_Int32 nStartCharFlags, const OUString &userDefinedCharactersStart, sal_Int32 nContCharFlags, const OUString &userDefinedCharactersCont) const
bool isDigit(const OUString &rStr, sal_Int32 nPos) const
static OUString decode(std::u16string_view rText, DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
OUString getLanguage() const
const OUString & getNumThousandSep() const
const OUString & getNumDecimalSepAlt() const
const OUString & getNumDecimalSep() const
virtual bool IsFixed() const override
virtual SbxDataType GetType() const override
SCTAB Tab() const
Definition: address.hxx:283
void Set(SCCOL nCol, SCROW nRow, SCTAB nTab)
Definition: address.hxx:403
void SetCol(SCCOL nColP)
Definition: address.hxx:291
SC_DLLPUBLIC void Format(OStringBuffer &r, ScRefFlags nFlags, const ScDocument *pDocument=nullptr, const Details &rDetails=detailsOOOa1) const
Definition: address.cxx:2074
void IncCol(SCCOL nDelta=1)
Definition: address.hxx:316
SC_DLLPUBLIC ScRefFlags Parse(const OUString &, const ScDocument &, const Details &rDetails=detailsOOOa1, ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, sal_Int32 *pSheetEndPos=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1537
SCROW Row() const
Definition: address.hxx:274
void SetRow(SCROW nRowP)
Definition: address.hxx:287
void SetTab(SCTAB nTabP)
Definition: address.hxx:295
void IncRow(SCROW nDelta=1)
Definition: address.hxx:312
@ INITIALIZE_INVALID
Definition: address.hxx:221
SCCOL Col() const
Definition: address.hxx:279
Cache for faster lookup of automatic names during CompileXML (during CompileXML, no document content ...
const ScAutoNameAddresses & GetNameOccurrences(const OUString &rName, SCTAB nTab)
Walk through all cells in an area.
Definition: dociter.hxx:206
CellType getType() const
Definition: dociter.hxx:233
ScFormulaCell * getFormulaCell()
Definition: dociter.hxx:236
bool hasString() const
Definition: dociter.cxx:994
const ScAddress & GetPos() const
Definition: dociter.hxx:231
OUString getString() const
Definition: dociter.cxx:968
OUString aFormula
Definition: compiler.hxx:287
virtual void CreateStringFromDoubleRef(OUStringBuffer &rBuffer, const formula::FormulaToken *pToken) const override
Definition: compiler.cxx:5407
static sal_Int32 GetDocTabPos(const OUString &rString)
Analyzes a string for a 'Doc'Tab construct, or 'Do''c'Tab etc...
Definition: compiler.cxx:1993
css::uno::Sequence< css::sheet::ExternalLinkInfo > maExternalLinks
Definition: compiler.hxx:284
static bool DoubleRefToPosSingleRefScalarCase(const ScRange &rRange, ScAddress &rAdr, const ScAddress &rFormulaPos)
TODO : Move this to somewhere appropriate.
Definition: compiler.cxx:6337
const ScRangeData * GetRangeData(SCTAB &rSheet, const OUString &rUpperName) const
Definition: compiler.cxx:3599
bool ParseDBRange(const OUString &)
Definition: compiler.cxx:3700
static const struct ScCompiler::AddInMap g_aAddInMap[]
Definition: odffmap.cxx:24
virtual void HandleIIOpCode(formula::FormulaToken *token, formula::FormulaToken ***pppToken, sal_uInt8 nNumParams) override
Definition: compiler.cxx:6123
bool mbCharClassesDiffer
Definition: compiler.hxx:294
const ScAddress & GetPos() const
Definition: compiler.hxx:492
void AutoCorrectParsedSymbol()
Definition: compiler.cxx:4154
virtual void AnnotateOperands() override
Definition: compiler.cxx:6292
bool HasPossibleNamedRangeConflict(SCTAB nTab) const
Definition: compiler.cxx:3618
void SetFormulaLanguage(const OpCodeMapPtr &xMap)
Set symbol map if not empty.
Definition: compiler.cxx:316
virtual void CreateStringFromExternal(OUStringBuffer &rBuffer, const formula::FormulaToken *pToken) const override
Definition: compiler.cxx:5235
bool ParseOpCode(const OUString &, bool bInArray)
Definition: compiler.cxx:2907
virtual void fillFromAddInMap(const NonConstOpCodeMapPtr &xMap, formula::FormulaGrammar::Grammar _eGrammar) const override
Definition: compiler.cxx:109
void SetGrammarAndRefConvention(const formula::FormulaGrammar::Grammar eNewGrammar, const formula::FormulaGrammar::Grammar eOldGrammar)
Set grammar and reference convention from within SetFormulaLanguage() or SetGrammar().
Definition: compiler.cxx:339
std::unordered_set< formula::FormulaTokenRef > mUnhandledPossibleImplicitIntersections
Definition: compiler.hxx:327
static bool IsEnglishSymbol(const OUString &rName)
Definition: compiler.cxx:201
bool ParseSingleReference(const OUString &rSymbol, const OUString *pErrRef=nullptr)
Definition: compiler.cxx:3345
void CreateStringFromXMLTokenArray(OUString &rFormula, OUString &rFormulaNmsp)
Definition: compiler.cxx:4658
bool ParseOpCode2(std::u16string_view)
Definition: compiler.cxx:3094
const ScInterpreterContext * mpInterpreterContext
Definition: compiler.hxx:278
static const Convention * pConventions[formula::FormulaGrammar::CONV_LAST]
Definition: compiler.hxx:263
virtual bool HandleRange() override
Definition: compiler.cxx:5040
bool SkipImplicitIntersectionOptimization(const formula::FormulaToken *token) const
Definition: compiler.cxx:6107
std::vector< TableRefEntry > maTableRefs
Definition: compiler.hxx:314
virtual void SetError(FormulaError nError) override
Definition: compiler.cxx:2056
friend struct Convention
Definition: compiler.hxx:257
std::vector< PendingImplicitIntersectionOptimization > mPendingImplicitIntersectionOptimizations
Definition: compiler.hxx:326
ExtendedErrorDetection meExtendedErrorDetection
Definition: compiler.hxx:298
bool ToUpperAsciiOrI18nIsAscii(OUString &rUpper, const OUString &rOrg) const
Definition: compiler.cxx:4340
std::vector< Whitespace > NextSymbol(bool bInArray)
Definition: compiler.cxx:2145
const CharClass * pCharClass
Definition: compiler.hxx:293
static const CharClass * pCharClassEnglish
Definition: compiler.hxx:261
const Convention * pConv
Definition: compiler.hxx:297
virtual void fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry > &_rVec, bool _bIsEnglish) const override
Definition: compiler.cxx:5514
void AnnotateTrimOnDoubleRefs()
Definition: compiler.cxx:6468
sal_Int32 mnRangeOpPosInSymbol
Definition: compiler.hxx:296
void SetAutoCorrection(bool bVal)
When auto correction is set, the jump command reorder must be enabled.
Definition: compiler.cxx:4147
virtual void LocalizeString(OUString &rName) const override
Definition: compiler.cxx:5504
ScDocument & rDoc
Definition: compiler.hxx:274
virtual bool HandleExternalReference(const formula::FormulaToken &_aToken) override
Definition: compiler.cxx:5108
bool AdjustSumRangeShape(const ScComplexRefData &rBaseRange, ScComplexRefData &rSumRange)
Definition: compiler.cxx:6402
SCTAB mnCurrentSheetTab
Definition: compiler.hxx:280
bool ParseBoolean(const OUString &)
Definition: compiler.cxx:3967
static const CharClass * pCharClassLocalized
Definition: compiler.hxx:262
void SetNumberFormatter(SvNumberFormatter *pFormatter)
Definition: compiler.cxx:311
bool mbRefConventionChartOOXML
Definition: compiler.hxx:301
virtual ~ScCompiler() override
Definition: compiler.cxx:1950
std::vector< OUString > maTabNames
Definition: compiler.hxx:305
virtual bool HandleTableRef() override
Definition: compiler.cxx:5797
virtual formula::ParamClass GetForceArrayParameter(const formula::FormulaToken *pToken, sal_uInt16 nParam) const override
Definition: compiler.cxx:6096
bool ParseNamedRange(const OUString &, bool onlyCheck=false)
Definition: compiler.cxx:3629
static bool ParameterMayBeImplicitIntersection(const formula::FormulaToken *token, int parameter)
Definition: compiler.cxx:6101
static const CharClass * GetCharClassEnglish()
Definition: compiler.cxx:230
@ EXTENDED_ERROR_DETECTION_NAME_BREAK
Definition: compiler.hxx:187
@ EXTENDED_ERROR_DETECTION_NONE
Definition: compiler.hxx:186
@ EXTENDED_ERROR_DETECTION_NAME_NO_BREAK
Definition: compiler.hxx:188
virtual bool HandleDbData() override
Definition: compiler.cxx:5760
bool ParseErrorConstant(const OUString &)
Definition: compiler.cxx:3981
static void DeInit()
Definition: compiler.cxx:187
bool ParseString()
Definition: compiler.cxx:3203
static bool IsCharFlagAllConventions(OUString const &rStr, sal_Int32 nPos, ScCharFlags nFlags)
If the character is allowed as tested by nFlags (SC_COMPILER_C_... bits) for all known address conven...
Definition: compiler.cxx:5214
sal_Int32 mnCurrentSheetEndPos
Definition: compiler.hxx:281
std::set< OpCode > mUnhandledPossibleImplicitIntersectionsOpCodes
Definition: compiler.hxx:329
std::vector< sal_uInt16 > maExternalFiles
Definition: compiler.hxx:303
sal_uInt16 mnPredetectedReference
Definition: compiler.hxx:295
static void addWhitespace(std::vector< ScCompiler::Whitespace > &rvSpaces, ScCompiler::Whitespace &rSpace, sal_Unicode c, sal_Int32 n=1)
Definition: compiler.cxx:2096
std::vector< OUString > & GetSetupTabNames() const
sheet names mangled for the current grammar for output
Definition: compiler.cxx:297
ScCompiler(sc::CompileFormulaContext &rCxt, const ScAddress &rPos, bool bComputeII=false, bool bMatrixFlag=false, const ScInterpreterContext *pContext=nullptr)
Definition: compiler.cxx:1901
virtual formula::FormulaTokenRef ExtendRangeReference(formula::FormulaToken &rTok1, formula::FormulaToken &rTok2) override
Definition: compiler.cxx:5509
virtual void fillFromAddInCollectionUpperName(const NonConstOpCodeMapPtr &xMap) const override
Definition: compiler.cxx:155
static size_t GetAddInMapCount()
Definition: odffmap.cxx:137
bool ParseTableRefColumn(const OUString &)
Definition: compiler.cxx:4065
bool ParseTableRefItem(const OUString &)
Definition: compiler.cxx:3993
void SetRelNameReference()
Definition: compiler.cxx:5171
bool mbCloseBrackets
Definition: compiler.hxx:299
ScCharFlags GetCharTableFlags(sal_Unicode c, sal_Unicode cLast)
Access the CharTable flags.
Definition: compiler.hxx:538
bool GetTokenIfOpCode(OpCode eOp)
Calls GetToken() if PeekNextNoSpaces() is of given OpCode.
Definition: compiler.cxx:5781
static void CheckTabQuotes(OUString &aTabName, const formula::FormulaGrammar::AddressConvention eConv=formula::FormulaGrammar::CONV_OOO)
all
Definition: compiler.cxx:1954
bool ParseColRowName(const OUString &)
Definition: compiler.cxx:3712
ScAddress aPos
Definition: compiler.hxx:275
bool ParseExternalNamedRange(const OUString &rSymbol, bool &rbInvalidExternalNameRange)
Definition: compiler.cxx:3662
virtual void CreateStringFromMatrix(OUStringBuffer &rBuffer, const formula::FormulaToken *pToken) const override
Definition: compiler.cxx:5277
virtual void CreateStringFromIndex(OUStringBuffer &rBuffer, const formula::FormulaToken *pToken) const override
Definition: compiler.cxx:5414
bool ParsePredetectedErrRefReference(const OUString &rName, const OUString *pErrRef)
Definition: compiler.cxx:3218
void MoveRelWrap()
Definition: compiler.cxx:5189
bool ParseValue(const OUString &)
Definition: compiler.cxx:3115
void SetGrammar(const formula::FormulaGrammar::Grammar eGrammar)
Definition: compiler.cxx:260
sal_Int32 nSrcPos
Definition: compiler.hxx:288
virtual OUString FindAddInFunction(const OUString &rUpperName, bool bLocalFirst) const override
Definition: compiler.cxx:350
static const CharClass * GetCharClassLocalized()
Definition: compiler.cxx:244
virtual void CreateStringFromSingleRef(OUStringBuffer &rBuffer, const formula::FormulaToken *pToken) const override
Definition: compiler.cxx:5348
static const Convention * GetRefConvention(formula::FormulaGrammar::AddressConvention eConv)
Definition: compiler.cxx:2011
void SetRefConvention(const Convention *pConvP)
Definition: compiler.cxx:2049
bool ParseDoubleReference(const OUString &rSymbol, const OUString *pErrRef=nullptr)
Definition: compiler.cxx:3305
bool mbRewind
Definition: compiler.hxx:300
bool ParsePredetectedReference(const OUString &rSymbol)
Definition: compiler.cxx:3231
bool NextNewToken(bool bInArray)
Definition: compiler.cxx:4357
std::queue< OpCode > maPendingOpCodes
Definition: compiler.hxx:291
bool ParseReference(const OUString &rSymbol, const OUString *pErrRef=nullptr)
Definition: compiler.cxx:3422
virtual void fillFromAddInCollectionEnglishName(const NonConstOpCodeMapPtr &xMap) const override
Definition: compiler.cxx:168
ScRawToken maRawToken
Definition: compiler.hxx:289
void ReplaceDoubleRefII(formula::FormulaToken **ppDoubleRefTok)
Definition: compiler.cxx:6297
void AdjustSheetLocalNameRelReferences(SCTAB nDelta)
Definition: compiler.cxx:5153
bool HandleIIOpCodeInternal(formula::FormulaToken *token, formula::FormulaToken ***pppToken, sal_uInt8 nNumParams)
Definition: compiler.cxx:6136
sal_Unicode cSymbol[MAXSTRLEN+1]
Definition: compiler.hxx:286
bool ParseMacro(const OUString &)
Definition: compiler.cxx:3534
virtual void PostProcessCode() override
Definition: compiler.cxx:6276
SvNumberFormatter * mpFormatter
Definition: compiler.hxx:277
void CorrectSumRange(const ScComplexRefData &rBaseRange, ScComplexRefData &rSumRange, formula::FormulaToken **ppSumRangeToken)
Definition: compiler.cxx:6454
virtual bool HandleColRowName() override
Definition: compiler.cxx:5549
std::unique_ptr< ScTokenArray > CompileString(const OUString &rFormula)
Tokenize formula expression string into an array of tokens.
Definition: compiler.cxx:4691
Stores global named database ranges.
Definition: dbdata.hxx:243
ScDBData * findByIndex(sal_uInt16 nIndex)
Definition: dbdata.cxx:1214
ScDBData * findByUpperName(const OUString &rName)
Definition: dbdata.cxx:1221
NamedDBs & getNamedDBs()
Definition: dbdata.hxx:324
bool HasHeader() const
Definition: dbdata.hxx:136
const OUString & GetName() const
Definition: dbdata.hxx:127
bool HasTotals() const
Definition: dbdata.hxx:138
sal_Int32 GetColumnNameOffset(const OUString &rName) const
Finds the column named rName and returns the corresponding offset within the table.
Definition: dbdata.cxx:903
void GetArea(SCTAB &rTab, SCCOL &rCol1, SCROW &rRow1, SCCOL &rCol2, SCROW &rRow2) const
Definition: dbdata.cxx:298
bool IsLookUpColRowNames() const
Definition: docoptio.hxx:51
ScSheetLimits & GetSheetLimits() const
Definition: document.hxx:898
bool ValidRow(SCROW nRow) const
Definition: document.hxx:900
ScFormulaParserPool & GetFormulaParserPool() const
Returns the pool containing external formula parsers.
Definition: documen3.cxx:654
SC_DLLPUBLIC bool GetTable(const OUString &rName, SCTAB &rTab) const
Definition: document.cxx:244
ScRangePairList * GetRowNameRanges()
Definition: document.hxx:820
SC_DLLPUBLIC SCCOL MaxCol() const
Definition: document.hxx:892
const ScDBData * GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
Definition: documen3.cxx:322
bool ValidAddress(const ScAddress &rAddress) const
Definition: document.hxx:904
SC_DLLPUBLIC formula::FormulaGrammar::AddressConvention GetAddressConvention() const
Definition: documen3.cxx:492
ScRangeData * FindRangeNameBySheetAndIndex(SCTAB nTab, sal_uInt16 nIndex) const
Find a named expression / range name in either global or a local scope.
Definition: documen3.cxx:269
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:893
SC_DLLPUBLIC std::vector< OUString > GetAllTableNames() const
Definition: document.cxx:270
ScAutoNameCache * GetAutoNameCache()
Definition: document.hxx:1388
SC_DLLPUBLIC ScExternalRefManager * GetExternalRefManager() const
Definition: documen3.cxx:625
SC_DLLPUBLIC bool HasStringData(SCCOL nCol, SCROW nRow, SCTAB nTab) const
Definition: document.cxx:3743
ScRangePairList * GetColNameRanges()
Definition: document.hxx:819
SC_DLLPUBLIC formula::FormulaGrammar::Grammar GetGrammar() const
Definition: document.hxx:1010
SC_DLLPUBLIC ScDBCollection * GetDBCollection() const
Definition: document.hxx:827
SfxObjectShell * GetDocumentShell() const
Definition: document.hxx:1083
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:899
SC_DLLPUBLIC svl::SharedStringPool & GetSharedStringPool()
Definition: documen2.cxx:601
SC_DLLPUBLIC ScRangeName * GetRangeName(SCTAB nTab) const
Definition: documen3.cxx:171
SC_DLLPUBLIC OUString GetString(SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext *pContext=nullptr) const
Definition: document.cxx:3505
SC_DLLPUBLIC bool GetName(SCTAB nTab, OUString &rName) const
Definition: document.cxx:204
SC_DLLPUBLIC const ScDocOptions & GetDocOptions() const
Definition: documen3.cxx:1936
ScRefCellValue GetRefCellValue(const ScAddress &rPos)
Definition: documen2.cxx:587
bool IsImportingXML() const
Definition: document.hxx:2227
std::shared_ptr< ScTokenArray > TokenArrayRef
sal_uInt16 getExternalFileId(const OUString &rFile)
const OUString * getRealTableName(sal_uInt16 nFileId, const OUString &rTabName) const
bool isValidRangeName(sal_uInt16 nFileId, const OUString &rName)
sal_uInt16 convertFileIdToUsedFileId(sal_uInt16 nFileId)
Reindex external file references to skip unused files, if skipping is enabled.
const OUString * getRealRangeName(sal_uInt16 nFileId, const OUString &rRangeName) const
const OUString * getExternalFileName(sal_uInt16 nFileId, bool bForceOriginal=false)
It returns a pointer to the name of the URI associated with a given external file ID.
void convertToAbsName(OUString &rFile) const
Takes a flat file name, and convert it to an absolute URL path.
void insertRefCell(sal_uInt16 nFileId, const ScAddress &rCell)
void getAllCachedTableNames(sal_uInt16 nFileId, ::std::vector< OUString > &rTabNames) const
Returns a vector containing all (real) table names and cache tables of the specified file.
ScExternalRefCache::TokenArrayRef getRangeNameTokens(sal_uInt16 nFileId, const OUString &rName, const ScAddress *pCurPos=nullptr)
Get an array of tokens corresponding with a specified name in a specified file.
ScTokenArray * GetCode()
ScAddress aPos
Stores the used instances of the FilterFormulaParser service implementations, mapped by the formula n...
css::uno::Reference< css::sheet::XFormulaParser > getFormulaParser(const OUString &rNamespace)
Returns the formula parser that is registered for the passed namespace.
static LegacyFuncCollection * GetLegacyFuncCollection()
Definition: global.cxx:278
static SC_DLLPUBLIC ::utl::TransliterationWrapper & GetTransliteration()
Definition: global.cxx:1026
static SC_DLLPUBLIC ScUnoAddInCollection * GetAddInCollection()
Definition: global.cxx:283
static SC_DLLPUBLIC sal_Int32 FindUnquoted(const OUString &rString, sal_Unicode cChar, sal_Int32 nStart=0)
Finds an unquoted instance of cChar in rString, starting at offset nStart.
Definition: global.cxx:749
static SC_DLLPUBLIC const LocaleDataWrapper & getLocaleData()
Definition: global.cxx:1055
static OUString GetErrorString(FormulaError nErrNumber)
Definition: global.cxx:315
static SC_DLLPUBLIC const CharClass & getCharClass()
Definition: global.cxx:1064
Matrix data type that can store values of mixed types.
Definition: scmatrix.hxx:101
svl::SharedString GetString(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3238
bool IsStringOrEmpty(SCSIZE nIndex) const
Definition: scmatrix.cxx:3258
bool IsEmpty(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3268
bool IsBoolean(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3303
double GetDouble(SCSIZE nC, SCSIZE nR) const
Definition: scmatrix.cxx:3223
FormulaError GetError(SCSIZE nC, SCSIZE nR) const
May be used before obtaining the double value of an element to avoid passing its NAN around.
Definition: scmatrix.cxx:3218
void GetDimensions(SCSIZE &rC, SCSIZE &rR) const
Definition: scmatrix.cxx:3143
bool IsValue(SCSIZE nIndex) const
Definition: scmatrix.cxx:3288
static formula::ParamClass GetParameterType(const formula::FormulaToken *pToken, sal_uInt16 nParameter)
Get one parameter type for function eOp.
Definition: parclass.cxx:362
ScTokenArray * GetCode()
Definition: rangenam.hxx:119
SC_DLLPUBLIC FormulaError GetErrCode() const
Definition: rangenam.cxx:515
bool HasReferences() const
Definition: rangenam.cxx:520
SC_DLLPUBLIC ScRangeData * findByUpperName(const OUString &rName)
Definition: rangenam.cxx:704
bool hasPossibleAddressConflict() const
Definition: rangenam.hxx:278
size_t size() const
Definition: rangelst.cxx:1224
const ScRange & GetRange(sal_uInt16 n) const
Definition: address.hxx:810
static void AppendTableName(OUStringBuffer &rBuf, const OUString &rTabName)
Definition: rangeutl.cxx:439
ScRange Intersection(const ScRange &rOther) const
Definition: address.cxx:1547
void PutInOrder()
Definition: address.hxx:622
ScAddress aEnd
Definition: address.hxx:498
const sal_Unicode * Parse_XL_Header(const sal_Unicode *pString, const ScDocument &rDocument, OUString &rExternDocName, OUString &rStartTabName, OUString &rEndTabName, ScRefFlags &nFlags, bool bOnlyAcceptSingle, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Parse an Excel style reference up to and including the sheet name separator '!', including detection ...
Definition: address.cxx:473
bool Contains(const ScAddress &) const
is Address& fully in Range?
Definition: address.hxx:718
bool IsValid() const
Definition: address.hxx:544
ScRefFlags Parse(const OUString &, const ScDocument &, const ScAddress::Details &rDetails=ScAddress::detailsOOOa1, ScAddress::ExternalInfo *pExtInfo=nullptr, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks=nullptr, const OUString *pErrRef=nullptr)
Definition: address.cxx:1700
ScAddress aStart
Definition: address.hxx:497
static void MoveRelWrap(const ScDocument &rDoc, const ScAddress &rPos, SCCOL nMaxCol, SCROW nMaxRow, ScComplexRefData &rRef)
Definition: refupdat.cxx:461
Special token to remember details of ocTableRef "structured references".
Definition: token.hxx:204
virtual sal_uInt16 GetIndex() const override
Definition: token.cxx:881
void SetAreaRefRPN(formula::FormulaToken *pToken)
Definition: token.cxx:910
formula::FormulaToken * GetAreaRefRPN() const
Definition: token.cxx:915
Item GetItem() const
Definition: token.cxx:900
virtual formula::FormulaToken * AddOpCode(OpCode eCode) override
Definition: token.cxx:2265
std::unique_ptr< ScTokenArray > Clone() const
Definition: token.cxx:1931
formula::FormulaToken * AddRawToken(const ScRawToken &)
Definition: token.cxx:2047
formula::FormulaToken * AddDoubleReference(const ScComplexRefData &rRef)
Definition: token.cxx:2282
formula::FormulaToken * AddSingleReference(const ScSingleRefData &rRef)
ScSingleRefToken with ocPush.
Definition: token.cxx:2272
static SC_DLLPUBLIC bool ConvertToTokenArray(ScDocument &rDoc, ScTokenArray &rTokenArray, const css::uno::Sequence< css::sheet::FormulaToken > &rSequence)
Definition: tokenuno.cxx:374
void LocalizeString(OUString &rName)
modify rName - input: exact name
Definition: addincol.cxx:1255
const ScUnoAddInFuncData * GetFuncData(const OUString &rName, bool bComplete=false)
Only if bComplete is set, the function reference and argument types are initialized (component may ha...
Definition: addincol.cxx:1224
tools::Long GetFuncCount()
Definition: addincol.cxx:1267
OUString FindFunction(const OUString &rUpperName, bool bLocalFirst)
User entered name. rUpperName MUST already be upper case!
Definition: addincol.cxx:1181
const OUString & GetUpperLocal() const
Definition: addincol.hxx:118
const OUString & GetUpperEnglish() const
Definition: addincol.hxx:119
bool GetExcelName(const LanguageTag &rDestLang, OUString &rRetExcelName, bool bFallbackToAny=true) const
Definition: addincol.cxx:157
const OUString & GetUpperName() const
Definition: addincol.hxx:117
const OUString & GetOriginalName() const
Definition: addincol.hxx:115
static void FillApiAddress(css::table::CellAddress &rApiAddress, const ScAddress &rScAddress)
Definition: convuno.hxx:70
static StarBASIC * GetBasic()
StarBASIC * GetBasic() const
ScComplexRefData & Ref()
Definition: token.hxx:437
virtual SbxVariable * Find(const OUString &, SbxClassType) override
sal_uInt32 GetStandardIndex(LanguageType eLnge=LANGUAGE_DONTKNOW)
SvNumFormatType GetType(sal_uInt32 nFIndex) const
bool IsNumberFormat(const OUString &sString, sal_uInt32 &F_Index, double &fOutNumber, SvNumInputOptions eInputOptions=SvNumInputOptions::NONE)
OpCodeMapPtr GetFinalOpCodeMap(const sal_Int32 nLanguage) const
OpCode GetEnglishOpCode(const OUString &rName) const
FormulaTokenArray * pArr
FormulaError GetErrorConstant(const OUString &rName) const
FormulaTokenRef mpToken
bool NeedsTableRefTransformation() const
bool MergeRangeReference(FormulaToken **const pCode1, FormulaToken *const *const pCode2)
void PushTokenArray(FormulaTokenArray *, bool)
static bool DeQuote(OUString &rStr)
static void AppendString(OUStringBuffer &rBuffer, const OUString &rStr)
void AppendErrorConstant(OUStringBuffer &rBuffer, FormulaError nError) const
void AppendBoolean(OUStringBuffer &rBuffer, bool bVal) const
static const OUString & GetNativeSymbol(OpCode eOp)
void AppendDouble(OUStringBuffer &rBuffer, double fVal) const
FormulaTokenArrayPlainIterator maArrIterator
std::shared_ptr< OpCodeMap > NonConstOpCodeMapPtr
FormulaGrammar::Grammar meGrammar
FormulaToken ** pCode
std::shared_ptr< const OpCodeMap > OpCodeMapPtr
FormulaGrammar::Grammar GetGrammar() const
const OpCodeMapPtr & GetCurrentOpCodeMap() const
static AddressConvention extractRefConvention(const Grammar eGrammar)
FormulaToken * PeekPrevNoSpaces() const
FormulaToken * PeekNextNoSpaces() const
FormulaToken * PeekPrev(sal_uInt16 &nIdx) const
FormulaToken * AddToken(const FormulaToken &)
FormulaTokenArrayReferencesRange References() const
sal_uInt16 GetLen() const
FormulaToken * Add(FormulaToken *)
OpCode OpCodeBefore(sal_uInt16 nIdx) const
FormulaToken ** GetArray() const
void SetCodeError(FormulaError n)
FormulaError GetCodeError() const
sal_uInt16 GetCodeLen() const
FormulaToken * AddBad(const OUString &rStr)
virtual const ScSingleRefData * GetSingleRef() const
bool IsInForceArray() const
virtual sal_uInt16 GetIndex() const
virtual ParamClass GetInForceArray() const
virtual sal_Int16 GetSheet() const
OpCode GetOpCode() const
StackVar GetType() const
virtual const ScComplexRefData * GetDoubleRef() const
virtual const svl::SharedString & GetString() const
virtual const ScMatrix * GetMatrix() const
virtual short * GetJump() const
virtual FormulaError GetError() const
formula::FormulaGrammar::Grammar getGrammar() const
SharedString intern(const OUString &rStr)
const OUString & getString() const
rtl_uString * getDataIgnoreCase()
rtl_uString * getData()
static bool IsFuzzing()
bool isEqual(const OUString &rStr1, const OUString &rStr2) const
static sal_Unicode * lcl_UnicodeStrNCpy(sal_Unicode *pDst, const sal_Unicode *pSrc, sal_Int32 nMax)
Definition: compiler.cxx:2062
static void r1c1_add_col(OUStringBuffer &rBuf, const ScSingleRefData &rRef, const ScAddress &rAbsRef)
Definition: compiler.cxx:1633
static bool lcl_ParenthesisFollows(const sal_Unicode *p)
Definition: compiler.cxx:3108
static std::mutex & getCharClassMutex()
Definition: compiler.cxx:224
static OUString lcl_makeExternalNameStr(const OUString &rFile, const OUString &rName, const sal_Unicode cSep, bool bODF)
Definition: compiler.cxx:707
static void r1c1_add_row(OUStringBuffer &rBuf, const ScSingleRefData &rRef, const ScAddress &rAbsRef)
Definition: compiler.cxx:1646
static bool lcl_isValidQuotedText(std::u16string_view rFormula, size_t nSrcPos, ParseResult &rRes)
Definition: compiler.cxx:499
static bool lcl_getLastTabName(OUString &rTabName2, const OUString &rTabName1, const vector< OUString > &rTabNames, const ScRange &rRef)
Definition: compiler.cxx:727
static bool lcl_isUnicodeIgnoreAscii(const sal_Unicode *p1, const char *p2, size_t n)
Definition: compiler.cxx:2076
static const char * pInternal[2]
Definition: compiler.cxx:105
static void lcl_GetColRowDeltas(const ScRange &rRange, SCCOL &rXDelta, SCROW &rYDelta)
Definition: compiler.cxx:6396
static bool lcl_parseExternalName(const OUString &rSymbol, OUString &rFile, OUString &rName, const sal_Unicode cSep, const ScDocument &rDoc, const uno::Sequence< sheet::ExternalLinkInfo > *pExternalLinks)
Definition: compiler.cxx:527
#define SC_OPCODE_STOP_BIN_OP
#define SC_OPCODE_STOP_1_PAR
ScCharFlags
Definition: compiler.hxx:50
#define SC_OPCODE_STOP_UN_OP
#define SC_OPCODE_START_UN_OP
#define SC_COMPILER_FILE_TAB_SEP
Definition: compiler.hxx:83
#define SC_OPCODE_START_1_PAR
#define MAXSTRLEN
Definition: compiler.hxx:47
#define SC_OPCODE_START_BIN_OP
int nCount
@ AREA
entire area
float u
FormulaError
DocumentType eType
CellType
Definition: global.hxx:272
@ CELLTYPE_FORMULA
Definition: global.hxx:276
sal_Int32 nIndex
OUString aName
void * p
sal_Int64 n
#define LANGUAGE_ENGLISH_US
sal_uInt16 nPos
#define SAL_WARN_IF(condition, area, stream)
#define SAL_WARN(area, stream)
aStr
aBuf
std::unique_ptr< sal_Int32[]> pData
OString strip(const OString &rIn, char c)
sal_Int32 getTokenCount(std::string_view rIn, char cTok)
Value
SuppressedReferenceOrForceArray
ReferenceOrRefArray
ReferenceOrForceArray
Array
::boost::intrusive_ptr< FormulaToken > FormulaTokenRef
StackVar
svExternalDoubleRef
svExternalName
svDouble
svSep
svError
svDoubleRef
svExternalSingleRef
svMatrix
svSingleRef
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
bool equalsAscii(std::u16string_view s1, std::string_view s2)
PyRef getCharClass(const Runtime &)
const SvxPageUsage aArr[]
long Long
std::mutex aMutex
OpCode
ocSumProduct
ocCurrent
ocArrayOpen
ocNegSub
ocEasterSunday
ocTableRefItemThisRow
ocInternalBegin
ocB
ocBackSolver
ocDiv
ocTableRef
ocSumIf
ocTableRefItemHeaders
ocFalse
ocRoundUp
ocStyle
ocFInv_LT
ocMultiArea
ocNone
ocOr
ocAddress
ocClose
ocColRowName
ocFloor
ocRound
ocArrayRowSep
ocFDist_LT
ocMacro
ocAdd
ocTableRefItemTotals
ocSkip
ocRange
ocColor
ocWeek
ocCeil_Math
ocFloor_Math
ocErrName
ocEqual
ocErrorType
ocRRI
ocOpen
ocSub
ocAverageIf
ocInternalEnd
ocCeil
ocColRowNameAuto
ocTrue
ocTableRefItemData
ocExternal
ocSpaces
ocAnd
ocPercentSign
ocTDist
ocSep
ocTableRefItemAll
ocIntersect
ocPush
ocBad
ocDBArea
ocWhitespace
ocTableRefClose
ocRoundDown
ocArrayClose
ocErrRef
ocArrayColSep
ocMul
ocVLookup
ocIf
ocEffect
ocTableRefOpen
ocSum
ocName
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:398
SbxEMPTY
SbxVOID
OUString ScResId(TranslateId aId)
Definition: scdll.cxx:90
virtual css::i18n::ParseResult parseAnyToken(const OUString &rFormula, sal_Int32 nSrcPos, const CharClass *pCharClass, bool bGroupSeparator) const =0
@ ABS_SHEET_PREFIX
In OOO A1, a sheet name may be prefixed with '$' to indicate an absolute sheet position.
Definition: compiler.hxx:248
@ SHEET_SEPARATOR
Character between sheet name and address.
Definition: compiler.hxx:242
const formula::FormulaGrammar::AddressConvention meConv
Definition: compiler.hxx:193
virtual sal_Unicode getSpecialSymbol(SpecialSymbolType eSymType) const =0
std::unique_ptr< ScCharFlags[]> mpCharTable
Definition: compiler.hxx:255
virtual OUString makeExternalNameStr(sal_uInt16 nFileId, const OUString &rFile, const OUString &rName) const =0
virtual bool parseExternalName(const OUString &rSymbol, OUString &rFile, OUString &rName, const ScDocument &rDoc, const css::uno::Sequence< css::sheet::ExternalLinkInfo > *pExternalLinks) const =0
Parse the symbol string and pick up the file name and the external range name.
virtual void makeExternalRefStr(ScSheetLimits &rLimits, OUStringBuffer &rBuffer, const ScAddress &rPos, sal_uInt16 nFileId, const OUString &rFileName, const OUString &rTabName, const ScSingleRefData &rRef) const =0
virtual void makeRefStr(ScSheetLimits &rLimits, OUStringBuffer &rBuffer, formula::FormulaGrammar::Grammar eGram, const ScAddress &rPos, const OUString &rErrRef, const std::vector< OUString > &rTabNames, const ScComplexRefData &rRef, bool bSingleRef, bool bFromRangeName) const =0
Convention(formula::FormulaGrammar::AddressConvention eConvP)
Definition: compiler.cxx:359
"stack" of currently active ocTableRef tokens
Definition: compiler.hxx:319
void reset(sal_Unicode c)
Definition: compiler.hxx:343
Complex reference (a range) into the sheet.
Definition: refdata.hxx:123
SC_DLLPUBLIC ScRange toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:493
void InitRange(const ScRange &rRange)
Definition: refdata.hxx:130
void InitFlags()
Definition: refdata.hxx:128
ScSingleRefData Ref2
Definition: refdata.hxx:125
bool IsEntireRow(const ScSheetLimits &rLimits) const
Whether this references entire rows, 1:1.
Definition: refdata.cxx:516
void SetRange(const ScSheetLimits &rLimits, const ScRange &rRange, const ScAddress &rPos)
Set a new range, assuming that the ordering of the range matches the ordering of the reference data f...
Definition: refdata.cxx:498
void SetTrimToData(bool bSet)
Definition: refdata.hxx:194
bool IsEntireCol(const ScSheetLimits &rLimits) const
Whether this references entire columns, A:A.
Definition: refdata.cxx:509
ScSingleRefData Ref1
Definition: refdata.hxx:124
void SetExternalDoubleRef(sal_uInt16 nFileId, const OUString &rTabName, const ScComplexRefData &rRef)
Definition: token.cxx:309
void SetDouble(double fVal)
Definition: token.cxx:275
void SetString(rtl_uString *pData, rtl_uString *pDataIgnoreCase)
Definition: token.cxx:251
OpCode GetOpCode() const
Definition: compiler.hxx:151
void SetDoubleReference(const ScComplexRefData &rRef)
Definition: token.cxx:268
void NewOpCode(OpCode e)
Definition: compiler.hxx:152
OpCode eOp
Definition: compiler.hxx:106
void SetExternalSingleRef(sal_uInt16 nFileId, const OUString &rTabName, const ScSingleRefData &rRef)
Definition: token.cxx:298
void SetOpCode(OpCode eCode)
Definition: token.cxx:207
struct ScRawToken::@17::@19 whitespace
struct ScRawToken::@17::@20 sbyte
void SetName(sal_Int16 nSheet, sal_uInt16 nIndex)
Definition: token.cxx:289
sal_uInt8 cByte
Definition: compiler.hxx:116
bool IsValidReference(const ScDocument &rDoc) const
If the token is a non-external reference, determine if the reference is valid.
Definition: token.cxx:335
void SetSingleReference(const ScSingleRefData &rRef)
Definition: token.cxx:260
void SetErrorConstant(FormulaError nErr)
Definition: token.cxx:282
sal_Unicode cChar
Definition: compiler.hxx:113
void SetExternalName(sal_uInt16 nFileId, const OUString &rName)
Definition: token.cxx:319
sal_uInt8 nCount
Definition: compiler.hxx:112
void SetExternal(const OUString &rStr)
Definition: token.cxx:328
formula::FormulaToken * CreateToken(ScSheetLimits &rLimits) const
Definition: token.cxx:352
This is very similar to ScCellValue, except that it references the original value instead of copying ...
Definition: cellvalue.hxx:108
bool isEmpty() const
Definition: cellvalue.cxx:667
bool hasNumeric() const
Definition: cellvalue.cxx:619
bool hasString() const
Definition: cellvalue.cxx:614
const SCCOL mnMaxCol
Definition: sheetlimits.hxx:29
bool ValidAddress(const ScAddress &rAddress) const
Definition: sheetlimits.hxx:54
bool ValidRow(SCROW nRow) const
Definition: sheetlimits.hxx:41
const SCROW mnMaxRow
Maximum addressable column.
Definition: sheetlimits.hxx:30
bool ValidCol(SCCOL nCol) const
Definition: sheetlimits.hxx:40
Single reference (one address) into the sheet.
Definition: refdata.hxx:30
SCCOL Col() const
Definition: refdata.cxx:247
void SetAddress(const ScSheetLimits &rLimits, const ScAddress &rAddr, const ScAddress &rPos)
Definition: refdata.cxx:213
void InitAddress(const ScAddress &rAdr)
InitAddress: InitFlags and set address.
Definition: refdata.cxx:27
bool IsDeleted() const
Definition: refdata.cxx:125
bool IsRowDeleted() const
Definition: refdata.hxx:84
void IncRow(SCROW nInc)
Definition: refdata.cxx:88
void SetRowRel(bool bVal)
Definition: refdata.hxx:66
void SetTabRel(bool bVal)
Definition: refdata.hxx:68
bool IsTabRel() const
Definition: refdata.hxx:69
SCROW Row() const
Definition: refdata.cxx:240
void SetColDeleted(bool bVal)
Definition: refdata.cxx:110
bool IsRowRel() const
Definition: refdata.hxx:67
ScAddress toAbs(const ScSheetLimits &rLimits, const ScAddress &rPos) const
Definition: refdata.cxx:193
bool IsColRel() const
Definition: refdata.hxx:65
void IncCol(SCCOL nInc)
Definition: refdata.cxx:71
bool IsTabDeleted() const
Definition: refdata.hxx:86
void SetFlag3D(bool bVal)
Definition: refdata.hxx:89
void IncTab(SCTAB nInc)
Definition: refdata.cxx:105
bool IsFlag3D() const
Definition: refdata.hxx:90
void InitFlags()
No default ctor, because used in ScRawToken union, set InitFlags!
Definition: refdata.hxx:54
void SetRelName(bool bVal)
Definition: refdata.hxx:91
void SetRowDeleted(bool bVal)
Definition: refdata.cxx:115
bool IsColDeleted() const
Definition: refdata.hxx:82
void SetColRel(bool bVal)
Definition: refdata.hxx:64
void SetTabDeleted(bool bVal)
Definition: refdata.cxx:120
FormulaTokenRef extendRangeReference(ScSheetLimits &rLimits, FormulaToken &rTok1, FormulaToken &rTok2, const ScAddress &rPos, bool bReuseDoubleRef)
If rTok1 and rTok2 both are SingleRef or DoubleRef tokens, extend/merge ranges as needed for ocRange.
Definition: token.cxx:504
unsigned char sal_uInt8
#define SAL_MAX_UINT16
sal_uInt16 sal_Unicode
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17
std::unique_ptr< char[]> aBuffer
SvNumFormatType