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