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 <osl/diagnose.h> 34 #include <rtl/character.hxx> 35 #include <tools/solar.h> 36 #include <unotools/charclass.hxx> 37 #include <unotools/configmgr.hxx> 38 #include <com/sun/star/lang/Locale.hpp> 39 #include <com/sun/star/sheet/FormulaOpCodeMapEntry.hpp> 40 #include <com/sun/star/sheet/FormulaLanguage.hpp> 41 #include <com/sun/star/i18n/KParseTokens.hpp> 42 #include <com/sun/star/i18n/KParseType.hpp> 43 #include <comphelper/processfactory.hxx> 44 #include <comphelper/string.hxx> 45 #include <unotools/transliterationwrapper.hxx> 46 #include <tools/urlobj.hxx> 47 #include <rtl/math.hxx> 48 #include <rtl/ustring.hxx> 49 #include <svtools/miscopt.hxx> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <math.h> 53 #include <rangenam.hxx> 54 #include <dbdata.hxx> 55 #include <document.hxx> 56 #include <callform.hxx> 57 #include <addincol.hxx> 58 #include <refupdat.hxx> 59 #include <sc.hrc> 60 #include <globstr.hrc> 61 #include <scresid.hxx> 62 #include <formulacell.hxx> 63 #include <dociter.hxx> 64 #include <docoptio.hxx> 65 #include <formula/errorcodes.hxx> 66 #include <parclass.hxx> 67 #include <autonamecache.hxx> 68 #include <externalrefmgr.hxx> 69 #include <rangeutl.hxx> 70 #include <convuno.hxx> 71 #include <tokenuno.hxx> 72 #include <formulaparserpool.hxx> 73 #include <tokenarray.hxx> 74 #include <scmatrix.hxx> 75 #include <tokenstringcontext.hxx> 76 77 using namespace formula; 78 using namespace ::com::sun::star; 79 using ::std::vector; 80 81 CharClass* ScCompiler::pCharClassEnglish = nullptr; 82 const ScCompiler::Convention* ScCompiler::pConventions[ ] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; 83 84 namespace { 85 86 enum ScanState 87 { 88 ssGetChar, 89 ssGetBool, 90 ssGetValue, 91 ssGetString, 92 ssSkipString, 93 ssGetIdent, 94 ssGetReference, 95 ssSkipReference, 96 ssGetErrorConstant, 97 ssGetTableRefItem, 98 ssGetTableRefColumn, 99 ssStop 100 }; 101 102 } 103 104 static const char* pInternal[2] = { "TTT", "__DEBUG_VAR" }; 105 106 using namespace ::com::sun::star::i18n; 107 108 void ScCompiler::fillFromAddInMap( const NonConstOpCodeMapPtr& xMap,FormulaGrammar::Grammar _eGrammar ) const 109 { 110 size_t nSymbolOffset; 111 switch( _eGrammar ) 112 { 113 case FormulaGrammar::GRAM_PODF: 114 nSymbolOffset = offsetof( AddInMap, pUpper); 115 break; 116 default: 117 case FormulaGrammar::GRAM_ODFF: 118 nSymbolOffset = offsetof( AddInMap, pODFF); 119 break; 120 case FormulaGrammar::GRAM_ENGLISH: 121 nSymbolOffset = offsetof( AddInMap, pEnglish); 122 break; 123 } 124 const AddInMap* pMap = g_aAddInMap; 125 const AddInMap* const pStop = pMap + GetAddInMapCount(); 126 for ( ; pMap < pStop; ++pMap) 127 { 128 char const * const * ppSymbol = 129 reinterpret_cast< char const * const * >( 130 reinterpret_cast< char const * >(pMap) + nSymbolOffset); 131 xMap->putExternal( OUString::createFromAscii( *ppSymbol), 132 OUString::createFromAscii( pMap->pOriginal)); 133 } 134 } 135 136 void ScCompiler::fillFromAddInCollectionUpperName( const NonConstOpCodeMapPtr& xMap ) const 137 { 138 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); 139 long nCount = pColl->GetFuncCount(); 140 for (long i=0; i < nCount; ++i) 141 { 142 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); 143 if (pFuncData) 144 xMap->putExternalSoftly( pFuncData->GetUpperName(), 145 pFuncData->GetOriginalName()); 146 } 147 } 148 149 void ScCompiler::fillFromAddInCollectionEnglishName( const NonConstOpCodeMapPtr& xMap ) const 150 { 151 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); 152 long nCount = pColl->GetFuncCount(); 153 for (long i=0; i < nCount; ++i) 154 { 155 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); 156 if (pFuncData) 157 { 158 OUString aName; 159 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName)) 160 xMap->putExternalSoftly( aName, pFuncData->GetOriginalName()); 161 else 162 xMap->putExternalSoftly( pFuncData->GetUpperName(), 163 pFuncData->GetOriginalName()); 164 } 165 } 166 } 167 168 void ScCompiler::DeInit() 169 { 170 if (pCharClassEnglish) 171 { 172 delete pCharClassEnglish; 173 pCharClassEnglish = nullptr; 174 } 175 } 176 177 bool ScCompiler::IsEnglishSymbol( const OUString& rName ) 178 { 179 // function names are always case-insensitive 180 OUString aUpper = ScGlobal::pCharClass->uppercase(rName); 181 182 // 1. built-in function name 183 OpCode eOp = ScCompiler::GetEnglishOpCode( aUpper ); 184 if ( eOp != ocNone ) 185 { 186 return true; 187 } 188 // 2. old add in functions 189 if (ScGlobal::GetLegacyFuncCollection()->findByName(aUpper)) 190 { 191 return true; 192 } 193 194 // 3. new (uno) add in functions 195 OUString aIntName = ScGlobal::GetAddInCollection()->FindFunction(aUpper, false); 196 return !aIntName.isEmpty(); // no valid function name 197 } 198 199 void ScCompiler::InitCharClassEnglish() 200 { 201 css::lang::Locale aLocale( "en", "US", ""); 202 pCharClassEnglish = new CharClass( 203 ::comphelper::getProcessComponentContext(), LanguageTag( aLocale)); 204 } 205 206 void ScCompiler::SetGrammar( const FormulaGrammar::Grammar eGrammar ) 207 { 208 assert( eGrammar != FormulaGrammar::GRAM_UNSPECIFIED && "ScCompiler::SetGrammar: don't pass FormulaGrammar::GRAM_UNSPECIFIED"); 209 if (eGrammar == GetGrammar()) 210 return; // nothing to be done 211 212 if( eGrammar == FormulaGrammar::GRAM_EXTERNAL ) 213 { 214 meGrammar = eGrammar; 215 mxSymbols = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE); 216 } 217 else 218 { 219 FormulaGrammar::Grammar eMyGrammar = eGrammar; 220 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( eMyGrammar); 221 OpCodeMapPtr xMap = GetOpCodeMap( nFormulaLanguage); 222 OSL_ENSURE( xMap, "ScCompiler::SetGrammar: unknown formula language"); 223 if (!xMap) 224 { 225 xMap = GetOpCodeMap( css::sheet::FormulaLanguage::NATIVE); 226 eMyGrammar = xMap->getGrammar(); 227 } 228 229 // Save old grammar for call to SetGrammarAndRefConvention(). 230 FormulaGrammar::Grammar eOldGrammar = GetGrammar(); 231 // This also sets the grammar associated with the map! 232 SetFormulaLanguage( xMap); 233 234 // Override if necessary. 235 if (eMyGrammar != GetGrammar()) 236 SetGrammarAndRefConvention( eMyGrammar, eOldGrammar); 237 } 238 } 239 240 // Unclear how this was intended to be refreshed when the 241 // grammar or sheet count is changed ? Ideally this would be 242 // a method on Document that would globally cache these. 243 std::vector<OUString> &ScCompiler::GetSetupTabNames() const 244 { 245 std::vector<OUString> &rTabNames = const_cast<ScCompiler *>(this)->maTabNames; 246 247 if (pDoc && rTabNames.empty()) 248 { 249 rTabNames = pDoc->GetAllTableNames(); 250 for (auto& rTabName : rTabNames) 251 ScCompiler::CheckTabQuotes(rTabName, formula::FormulaGrammar::extractRefConvention(meGrammar)); 252 } 253 254 return rTabNames; 255 } 256 257 void ScCompiler::SetNumberFormatter( SvNumberFormatter* pFormatter ) 258 { 259 mpFormatter = pFormatter; 260 } 261 262 void ScCompiler::SetFormulaLanguage( const ScCompiler::OpCodeMapPtr & xMap ) 263 { 264 if (xMap.get()) 265 { 266 mxSymbols = xMap; 267 if (mxSymbols->isEnglish()) 268 { 269 if (!pCharClassEnglish) 270 InitCharClassEnglish(); 271 pCharClass = pCharClassEnglish; 272 } 273 else 274 pCharClass = ScGlobal::pCharClass; 275 SetGrammarAndRefConvention( mxSymbols->getGrammar(), GetGrammar()); 276 } 277 } 278 279 void ScCompiler::SetGrammarAndRefConvention( 280 const FormulaGrammar::Grammar eNewGrammar, const FormulaGrammar::Grammar eOldGrammar ) 281 { 282 meGrammar = eNewGrammar; // SetRefConvention needs the new grammar set! 283 FormulaGrammar::AddressConvention eConv = FormulaGrammar::extractRefConvention( meGrammar); 284 if (eConv == FormulaGrammar::CONV_UNSPECIFIED && eOldGrammar == FormulaGrammar::GRAM_UNSPECIFIED) 285 { 286 if (pDoc) 287 SetRefConvention( pDoc->GetAddressConvention()); 288 else 289 SetRefConvention( GetRefConvention( FormulaGrammar::CONV_OOO ) ); 290 } 291 else 292 SetRefConvention( eConv ); 293 } 294 295 OUString ScCompiler::FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const 296 { 297 return ScGlobal::GetAddInCollection()->FindFunction(rUpperName, bLocalFirst); // bLocalFirst=false for english 298 } 299 300 ScCompiler::Convention::~Convention() 301 { 302 } 303 304 ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv ) 305 : 306 meConv( eConv ) 307 { 308 int i; 309 ScCharFlags *t= new ScCharFlags [128]; 310 311 ScCompiler::pConventions[ meConv ] = this; 312 mpCharTable.reset( t ); 313 314 for (i = 0; i < 128; i++) 315 t[i] = ScCharFlags::Illegal; 316 317 // tdf#56036: Allow tabs/newlines in imported formulas (for now simply treat them as (and convert to) space) 318 // TODO: tdf#76310: allow saving newlines as is (as per OpenFormula specification v.1.2, clause 5.14 "Whitespace") 319 // This is compliant with the OASIS decision (see https://issues.oasis-open.org/browse/OFFICE-701) 320 // Also, this would enable correct roundtrip from/to OOXML without losing tabs/newlines 321 // This requires saving actual space characters in ocSpaces token, using them in UI and saving 322 /* tab */ t[ 9] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep; 323 /* lf */ t[10] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep; 324 /* cr */ t[13] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep; 325 326 /* */ t[32] = ScCharFlags::CharDontCare | ScCharFlags::WordSep | ScCharFlags::ValueSep; 327 /* ! */ t[33] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 328 if (FormulaGrammar::CONV_ODF == meConv) 329 /* ! */ t[33] |= ScCharFlags::OdfLabelOp; 330 /* " */ t[34] = ScCharFlags::CharString | ScCharFlags::StringSep; 331 /* # */ t[35] = ScCharFlags::WordSep | ScCharFlags::CharErrConst; 332 /* $ */ t[36] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident; 333 if (FormulaGrammar::CONV_ODF == meConv) 334 /* $ */ t[36] |= ScCharFlags::OdfNameMarker; 335 /* % */ t[37] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 336 /* & */ t[38] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 337 /* ' */ t[39] = ScCharFlags::NameSep; 338 /* ( */ t[40] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 339 /* ) */ t[41] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 340 /* * */ t[42] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 341 /* + */ t[43] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign; 342 /* , */ t[44] = ScCharFlags::CharValue | ScCharFlags::Value; 343 /* - */ t[45] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueExp | ScCharFlags::ValueSign; 344 /* . */ t[46] = ScCharFlags::Word | ScCharFlags::CharValue | ScCharFlags::Value | ScCharFlags::Ident | ScCharFlags::Name; 345 /* / */ t[47] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 346 347 for (i = 48; i < 58; i++) 348 /* 0-9 */ t[i] = ScCharFlags::CharValue | ScCharFlags::Word | ScCharFlags::Value | ScCharFlags::ValueExp | ScCharFlags::ValueValue | ScCharFlags::Ident | ScCharFlags::Name; 349 350 /* : */ t[58] = ScCharFlags::Char | ScCharFlags::Word; 351 /* ; */ t[59] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 352 /* < */ t[60] = ScCharFlags::CharBool | ScCharFlags::WordSep | ScCharFlags::ValueSep; 353 /* = */ t[61] = ScCharFlags::Char | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep; 354 /* > */ t[62] = ScCharFlags::CharBool | ScCharFlags::Bool | ScCharFlags::WordSep | ScCharFlags::ValueSep; 355 /* ? */ t[63] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::Name; 356 /* @ */ // FREE 357 358 for (i = 65; i < 91; i++) 359 /* A-Z */ t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name; 360 361 if (FormulaGrammar::CONV_ODF == meConv) 362 { 363 /* [ */ t[91] = ScCharFlags::OdfLBracket; 364 /* \ */ // FREE 365 /* ] */ t[93] = ScCharFlags::OdfRBracket; 366 } 367 else if (FormulaGrammar::CONV_OOO == meConv) 368 { 369 /* [ */ t[91] = ScCharFlags::Char; 370 /* \ */ // FREE 371 /* ] */ t[93] = ScCharFlags::Char; 372 } 373 else if (FormulaGrammar::CONV_XL_OOX == meConv) 374 { 375 /* [ */ t[91] = ScCharFlags::Char | ScCharFlags::CharIdent; 376 /* \ */ // FREE 377 /* ] */ t[93] = ScCharFlags::Char | ScCharFlags::Ident; 378 } 379 else if (FormulaGrammar::CONV_XL_A1 == meConv) 380 { 381 /* [ */ t[91] = ScCharFlags::Char; 382 /* \ */ // FREE 383 /* ] */ t[93] = ScCharFlags::Char; 384 } 385 else if( FormulaGrammar::CONV_XL_R1C1 == meConv ) 386 { 387 /* [ */ t[91] = ScCharFlags::Ident; 388 /* \ */ // FREE 389 /* ] */ t[93] = ScCharFlags::Ident; 390 } 391 else 392 { 393 /* [ */ // FREE 394 /* \ */ // FREE 395 /* ] */ // FREE 396 } 397 398 /* ^ */ t[94] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 399 /* _ */ t[95] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name; 400 /* ` */ // FREE 401 402 for (i = 97; i < 123; i++) 403 /* a-z */ t[i] = ScCharFlags::CharWord | ScCharFlags::Word | ScCharFlags::CharIdent | ScCharFlags::Ident | ScCharFlags::CharName | ScCharFlags::Name; 404 405 /* { */ t[123] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array open 406 /* | */ t[124] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array row sep (Should be OOo specific) 407 /* } */ t[125] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; // array close 408 /* ~ */ t[126] = ScCharFlags::Char; // OOo specific 409 /* 127 */ // FREE 410 411 if( FormulaGrammar::CONV_XL_A1 == meConv || FormulaGrammar::CONV_XL_R1C1 == meConv || FormulaGrammar::CONV_XL_OOX == meConv ) 412 { 413 /* */ t[32] |= ScCharFlags::Word; 414 /* ! */ t[33] |= ScCharFlags::Ident | ScCharFlags::Word; 415 /* " */ t[34] |= ScCharFlags::Word; 416 /* # */ t[35] &= ~ScCharFlags::WordSep; 417 /* # */ t[35] |= ScCharFlags::Word; 418 /* % */ t[37] |= ScCharFlags::Word; 419 /* ' */ t[39] |= ScCharFlags::Word; 420 421 /* % */ t[37] |= ScCharFlags::Word; 422 /* & */ t[38] |= ScCharFlags::Word; 423 /* ' */ t[39] |= ScCharFlags::Word; 424 /* ( */ t[40] |= ScCharFlags::Word; 425 /* ) */ t[41] |= ScCharFlags::Word; 426 /* * */ t[42] |= ScCharFlags::Word; 427 /* + */ t[43] |= ScCharFlags::Word; 428 #if 0 /* this really needs to be locale specific. */ 429 /* , */ t[44] = ScCharFlags::Char | ScCharFlags::WordSep | ScCharFlags::ValueSep; 430 #else 431 /* , */ t[44] |= ScCharFlags::Word; 432 #endif 433 /* - */ t[45] |= ScCharFlags::Word; 434 435 /* ; */ t[59] |= ScCharFlags::Word; 436 /* < */ t[60] |= ScCharFlags::Word; 437 /* = */ t[61] |= ScCharFlags::Word; 438 /* > */ t[62] |= ScCharFlags::Word; 439 /* ? */ // question really is not permitted in sheet name 440 /* @ */ t[64] |= ScCharFlags::Word; 441 /* [ */ t[91] |= ScCharFlags::Word; 442 /* ] */ t[93] |= ScCharFlags::Word; 443 /* { */ t[123]|= ScCharFlags::Word; 444 /* | */ t[124]|= ScCharFlags::Word; 445 /* } */ t[125]|= ScCharFlags::Word; 446 /* ~ */ t[126]|= ScCharFlags::Word; 447 } 448 } 449 450 static bool lcl_isValidQuotedText( const OUString& rFormula, sal_Int32 nSrcPos, ParseResult& rRes ) 451 { 452 // Tokens that start at ' can have anything in them until a final ' 453 // but '' marks an escaped ' 454 // We've earlier guaranteed that a string containing '' will be 455 // surrounded by ' 456 if (nSrcPos < rFormula.getLength() && rFormula[nSrcPos] == '\'') 457 { 458 sal_Int32 nPos = nSrcPos+1; 459 while (nPos < rFormula.getLength()) 460 { 461 if (rFormula[nPos] == '\'') 462 { 463 if ( (nPos+1 == rFormula.getLength()) || (rFormula[nPos+1] != '\'') ) 464 { 465 rRes.TokenType = KParseType::SINGLE_QUOTE_NAME; 466 rRes.EndPos = nPos+1; 467 return true; 468 } 469 ++nPos; 470 } 471 ++nPos; 472 } 473 } 474 475 return false; 476 } 477 478 static bool lcl_parseExternalName( 479 const OUString& rSymbol, 480 OUString& rFile, 481 OUString& rName, 482 const sal_Unicode cSep, 483 const ScDocument* pDoc, 484 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) 485 { 486 /* TODO: future versions will have to support sheet-local names too, thus 487 * return a possible sheet name as well. */ 488 const sal_Unicode* const pStart = rSymbol.getStr(); 489 const sal_Unicode* p = pStart; 490 sal_Int32 nLen = rSymbol.getLength(); 491 OUString aTmpFile; 492 OUStringBuffer aTmpName; 493 sal_Int32 i = 0; 494 bool bInName = false; 495 if (cSep == '!') 496 { 497 // For XL use existing parser that resolves bracketed and quoted and 498 // indexed external document names. 499 ScRange aRange; 500 OUString aStartTabName, aEndTabName; 501 ScRefFlags nFlags = ScRefFlags::ZERO; 502 p = aRange.Parse_XL_Header( p, pDoc, aTmpFile, aStartTabName, 503 aEndTabName, nFlags, true, pExternalLinks ); 504 if (!p || p == pStart) 505 return false; 506 i = sal_Int32(p - pStart); 507 } 508 for ( ; i < nLen; ++i, ++p) 509 { 510 sal_Unicode c = *p; 511 if (i == 0) 512 { 513 if (c == '.' || c == cSep) 514 return false; 515 516 if (c == '\'') 517 { 518 // Move to the next char and loop until the second single 519 // quote. 520 sal_Unicode cPrev = c; 521 ++i; ++p; 522 for (sal_Int32 j = i; j < nLen; ++j, ++p) 523 { 524 c = *p; 525 if (c == '\'') 526 { 527 if (j == i) 528 { 529 // empty quote e.g. (=''!Name) 530 return false; 531 } 532 533 if (cPrev == '\'') 534 { 535 // two consecutive quotes equal a single quote in 536 // the file name. 537 aTmpFile += OUStringChar(c); 538 cPrev = 'a'; 539 } 540 else 541 cPrev = c; 542 543 continue; 544 } 545 546 if (cPrev == '\'' && j != i) 547 { 548 // this is not a quote but the previous one is. This 549 // ends the parsing of the quoted segment. At this 550 // point, the current char must equal the separator 551 // char. 552 553 i = j; 554 bInName = true; 555 aTmpName.append(c); // Keep the separator as part of the name. 556 break; 557 } 558 aTmpFile += OUStringChar(c); 559 cPrev = c; 560 } 561 562 if (!bInName) 563 { 564 // premature ending of the quoted segment. 565 return false; 566 } 567 568 if (c != cSep) 569 { 570 // only the separator is allowed after the closing quote. 571 return false; 572 } 573 574 continue; 575 } 576 } 577 578 if (bInName) 579 { 580 if (c == cSep) 581 { 582 // A second separator ? Not a valid external name. 583 return false; 584 } 585 aTmpName.append(c); 586 } 587 else 588 { 589 if (c == cSep) 590 { 591 bInName = true; 592 aTmpName.append(c); // Keep the separator as part of the name. 593 } 594 else 595 { 596 do 597 { 598 if (rtl::isAsciiAlphanumeric(c)) 599 // allowed. 600 break; 601 602 if (c > 128) 603 // non-ASCII character is allowed. 604 break; 605 606 bool bValid = false; 607 switch (c) 608 { 609 case '_': 610 case '-': 611 case '.': 612 // these special characters are allowed. 613 bValid = true; 614 break; 615 } 616 if (bValid) 617 break; 618 619 return false; 620 } 621 while (false); 622 aTmpFile += OUStringChar(c); 623 } 624 } 625 } 626 627 if (!bInName) 628 { 629 // No name found - most likely the symbol has no '!'s. 630 return false; 631 } 632 633 sal_Int32 nNameLen = aTmpName.getLength(); 634 if (nNameLen < 2) 635 { 636 // Name must be at least 2-char long (separator plus name). 637 return false; 638 } 639 640 if (aTmpName[0] != cSep) 641 { 642 // 1st char of the name must equal the separator. 643 return false; 644 } 645 646 if (aTmpName[nNameLen-1] == '!') 647 { 648 // Check against #REF!. 649 if (aTmpName.toString().equalsIgnoreAsciiCase("#REF!")) 650 return false; 651 } 652 653 rFile = aTmpFile; 654 rName = aTmpName.makeStringAndClear().copy(1); // Skip the first char as it is always the separator. 655 return true; 656 } 657 658 static OUString lcl_makeExternalNameStr(const OUString& rFile, const OUString& rName, 659 const sal_Unicode cSep, bool bODF ) 660 { 661 OUString aEscQuote("''"); 662 OUString aFile(rFile.replaceAll("'", aEscQuote)); 663 OUString aName(rName); 664 if (bODF) 665 aName = aName.replaceAll("'", aEscQuote); 666 OUStringBuffer aBuf(aFile.getLength() + aName.getLength() + 9); 667 if (bODF) 668 aBuf.append( '['); 669 aBuf.append( "'" ).append( aFile ).append( "'" ).append( OUStringChar(cSep) ); 670 if (bODF) 671 aBuf.append( "$$'" ); 672 aBuf.append( aName); 673 if (bODF) 674 aBuf.append( "']" ); 675 return aBuf.makeStringAndClear(); 676 } 677 678 static bool lcl_getLastTabName( OUString& rTabName2, const OUString& rTabName1, 679 const vector<OUString>& rTabNames, const ScRange& rRef ) 680 { 681 SCTAB nTabSpan = rRef.aEnd.Tab() - rRef.aStart.Tab(); 682 if (nTabSpan > 0) 683 { 684 size_t nCount = rTabNames.size(); 685 vector<OUString>::const_iterator itrBeg = rTabNames.begin(), itrEnd = rTabNames.end(); 686 vector<OUString>::const_iterator itr = ::std::find(itrBeg, itrEnd, rTabName1); 687 if (itr == rTabNames.end()) 688 { 689 rTabName2 = ScResId(STR_NO_REF_TABLE); 690 return false; 691 } 692 693 size_t nDist = ::std::distance(itrBeg, itr); 694 if (nDist + static_cast<size_t>(nTabSpan) >= nCount) 695 { 696 rTabName2 = ScResId(STR_NO_REF_TABLE); 697 return false; 698 } 699 700 rTabName2 = rTabNames[nDist+nTabSpan]; 701 } 702 else 703 rTabName2 = rTabName1; 704 705 return true; 706 } 707 708 namespace { 709 710 struct Convention_A1 : public ScCompiler::Convention 711 { 712 explicit Convention_A1( FormulaGrammar::AddressConvention eConv ) : ScCompiler::Convention( eConv ) { } 713 static void MakeColStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol ); 714 static void MakeRowStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow ); 715 716 ParseResult parseAnyToken( const OUString& rFormula, 717 sal_Int32 nSrcPos, 718 const CharClass* pCharClass, 719 bool bGroupSeparator) const override 720 { 721 ParseResult aRet; 722 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) 723 return aRet; 724 725 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | 726 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR; 727 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; 728 // '?' allowed in range names because of Xcl :-/ 729 static const OUString aAddAllowed("?#"); 730 return pCharClass->parseAnyToken( rFormula, 731 nSrcPos, nStartFlags, aAddAllowed, 732 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags), 733 aAddAllowed ); 734 } 735 736 virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode /*cLast*/ ) const override 737 { 738 return mpCharTable[static_cast<sal_uInt8>(c)]; 739 } 740 }; 741 742 } 743 744 void Convention_A1::MakeColStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCCOL nCol ) 745 { 746 if ( !rLimits.ValidCol(nCol) ) 747 rBuffer.append(ScResId(STR_NO_REF_TABLE)); 748 else 749 ::ScColToAlpha( rBuffer, nCol); 750 } 751 752 void Convention_A1::MakeRowStr( ScSheetLimits& rLimits, OUStringBuffer& rBuffer, SCROW nRow ) 753 { 754 if ( !rLimits.ValidRow(nRow) ) 755 rBuffer.append(ScResId(STR_NO_REF_TABLE)); 756 else 757 rBuffer.append(sal_Int32(nRow + 1)); 758 } 759 760 namespace { 761 762 struct ConventionOOO_A1 : public Convention_A1 763 { 764 ConventionOOO_A1() : Convention_A1 (FormulaGrammar::CONV_OOO) { } 765 explicit ConventionOOO_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1 (eConv) { } 766 767 static void MakeTabStr( OUStringBuffer &rBuf, const std::vector<OUString>& rTabNames, SCTAB nTab ) 768 { 769 if (static_cast<size_t>(nTab) >= rTabNames.size()) 770 rBuf.append(ScResId(STR_NO_REF_TABLE)); 771 else 772 rBuf.append(rTabNames[nTab]); 773 rBuf.append('.'); 774 } 775 776 enum SingletonDisplay 777 { 778 SINGLETON_NONE, 779 SINGLETON_COL, 780 SINGLETON_ROW 781 }; 782 783 static void MakeOneRefStrImpl( 784 ScSheetLimits& rLimits, OUStringBuffer& rBuffer, 785 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 786 const ScSingleRefData& rRef, const ScAddress& rAbsRef, 787 bool bForceTab, bool bODF, SingletonDisplay eSingletonDisplay ) 788 { 789 // For ODF override singleton so earlier releases still can read what 790 // we write now as of 2015-06-26. 791 /* TODO: we may want to change that in future in a few releases. */ 792 if (bODF) 793 eSingletonDisplay = SINGLETON_NONE; 794 795 if( rRef.IsFlag3D() || bForceTab ) 796 { 797 if (!ValidTab(rAbsRef.Tab()) || rRef.IsTabDeleted()) 798 { 799 if (!rRef.IsTabRel()) 800 rBuffer.append('$'); 801 rBuffer.append(rErrRef); 802 rBuffer.append('.'); 803 } 804 else 805 { 806 if (!rRef.IsTabRel()) 807 rBuffer.append('$'); 808 MakeTabStr(rBuffer, rTabNames, rAbsRef.Tab()); 809 } 810 } 811 else if (bODF) 812 rBuffer.append('.'); 813 814 if (eSingletonDisplay != SINGLETON_ROW) 815 { 816 if (!rRef.IsColRel()) 817 rBuffer.append('$'); 818 if (!rLimits.ValidCol(rAbsRef.Col()) || rRef.IsColDeleted()) 819 rBuffer.append(rErrRef); 820 else 821 MakeColStr(rLimits, rBuffer, rAbsRef.Col()); 822 } 823 824 if (eSingletonDisplay != SINGLETON_COL) 825 { 826 if (!rRef.IsRowRel()) 827 rBuffer.append('$'); 828 if (!rLimits.ValidRow(rAbsRef.Row()) || rRef.IsRowDeleted()) 829 rBuffer.append(rErrRef); 830 else 831 MakeRowStr(rLimits, rBuffer, rAbsRef.Row()); 832 } 833 } 834 835 static SingletonDisplay getSingletonDisplay( ScSheetLimits& rLimits, const ScAddress& rAbs1, const ScAddress& rAbs2, 836 const ScComplexRefData& rRef, bool bFromRangeName ) 837 { 838 // If any part is error, display as such. 839 if (!rLimits.ValidCol(rAbs1.Col()) || rRef.Ref1.IsColDeleted() || !rLimits.ValidRow(rAbs1.Row()) || rRef.Ref1.IsRowDeleted() || 840 !rLimits.ValidCol(rAbs2.Col()) || rRef.Ref2.IsColDeleted() || !rLimits.ValidRow(rAbs2.Row()) || rRef.Ref2.IsRowDeleted()) 841 return SINGLETON_NONE; 842 843 // A:A or $A:$A or A:$A or $A:A 844 if (rRef.IsEntireCol()) 845 return SINGLETON_COL; 846 847 // Same if not in named expression and both rows of entire columns are 848 // relative references. 849 if (!bFromRangeName && rAbs1.Row() == 0 && rAbs2.Row() == rLimits.mnMaxRow && 850 rRef.Ref1.IsRowRel() && rRef.Ref2.IsRowRel()) 851 return SINGLETON_COL; 852 853 // 1:1 or $1:$1 or 1:$1 or $1:1 854 if (rRef.IsEntireRow()) 855 return SINGLETON_ROW; 856 857 // Same if not in named expression and both columns of entire rows are 858 // relative references. 859 if (!bFromRangeName && rAbs1.Col() == 0 && rAbs2.Col() == rLimits.mnMaxCol && 860 rRef.Ref1.IsColRel() && rRef.Ref2.IsColRel()) 861 return SINGLETON_ROW; 862 863 return SINGLETON_NONE; 864 } 865 866 virtual void makeRefStr( 867 ScSheetLimits& rLimits, 868 OUStringBuffer& rBuffer, 869 formula::FormulaGrammar::Grammar /*eGram*/, 870 const ScAddress& rPos, 871 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 872 const ScComplexRefData& rRef, 873 bool bSingleRef, 874 bool bFromRangeName ) const override 875 { 876 // In case absolute/relative positions weren't separately available: 877 // transform relative to absolute! 878 ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2; 879 if( !bSingleRef ) 880 aAbs2 = rRef.Ref2.toAbs(rLimits, rPos); 881 882 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE : 883 getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName); 884 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, false, eSingleton); 885 if (!bSingleRef) 886 { 887 rBuffer.append(':'); 888 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), false, 889 eSingleton); 890 } 891 } 892 893 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override 894 { 895 switch (eSymType) 896 { 897 case ScCompiler::Convention::ABS_SHEET_PREFIX: 898 return '$'; 899 case ScCompiler::Convention::SHEET_SEPARATOR: 900 return '.'; 901 } 902 903 return u'\0'; 904 } 905 906 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName, 907 const ScDocument* pDoc, 908 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override 909 { 910 return lcl_parseExternalName(rSymbol, rFile, rName, '#', pDoc, pExternalLinks); 911 } 912 913 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, 914 const OUString& rName ) const override 915 { 916 return lcl_makeExternalNameStr( rFile, rName, '#', false); 917 } 918 919 static bool makeExternalSingleRefStr( 920 ScSheetLimits& rLimits, 921 OUStringBuffer& rBuffer, const OUString& rFileName, const OUString& rTabName, 922 const ScSingleRefData& rRef, const ScAddress& rPos, bool bDisplayTabName, bool bEncodeUrl ) 923 { 924 ScAddress aAbsRef = rRef.toAbs(rLimits, rPos); 925 if (bDisplayTabName) 926 { 927 OUString aFile; 928 if (bEncodeUrl) 929 aFile = rFileName; 930 else 931 aFile = INetURLObject::decode(rFileName, INetURLObject::DecodeMechanism::Unambiguous); 932 933 rBuffer.append("'").append(aFile.replaceAll("'", "''")).append("'#"); 934 935 if (!rRef.IsTabRel()) 936 rBuffer.append('$'); 937 ScRangeStringConverter::AppendTableName(rBuffer, rTabName); 938 939 rBuffer.append('.'); 940 } 941 942 if (!rRef.IsColRel()) 943 rBuffer.append('$'); 944 MakeColStr( rLimits, rBuffer, aAbsRef.Col()); 945 if (!rRef.IsRowRel()) 946 rBuffer.append('$'); 947 MakeRowStr( rLimits, rBuffer, aAbsRef.Row()); 948 949 return true; 950 } 951 952 static void makeExternalRefStrImpl( 953 ScSheetLimits& rLimits, 954 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, 955 const OUString& rTabName, const ScSingleRefData& rRef, bool bODF ) 956 { 957 if (bODF) 958 rBuffer.append( '['); 959 960 bool bEncodeUrl = bODF; 961 makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef, rPos, true, bEncodeUrl); 962 if (bODF) 963 rBuffer.append( ']'); 964 } 965 966 virtual void makeExternalRefStr( 967 ScSheetLimits& rLimits, 968 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 969 const OUString& rTabName, const ScSingleRefData& rRef ) const override 970 { 971 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, false); 972 } 973 974 static void makeExternalRefStrImpl( 975 ScSheetLimits& rLimits, 976 OUStringBuffer& rBuffer, const ScAddress& rPos, const OUString& rFileName, 977 const std::vector<OUString>& rTabNames, const OUString& rTabName, 978 const ScComplexRefData& rRef, bool bODF ) 979 { 980 ScRange aAbsRange = rRef.toAbs(rLimits, rPos); 981 982 if (bODF) 983 rBuffer.append( '['); 984 // Ensure that there's always a closing bracket, no premature returns. 985 bool bEncodeUrl = bODF; 986 987 do 988 { 989 if (!makeExternalSingleRefStr(rLimits, rBuffer, rFileName, rTabName, rRef.Ref1, rPos, true, bEncodeUrl)) 990 break; 991 992 rBuffer.append(':'); 993 994 OUString aLastTabName; 995 bool bDisplayTabName = (aAbsRange.aStart.Tab() != aAbsRange.aEnd.Tab()); 996 if (bDisplayTabName) 997 { 998 // Get the name of the last table. 999 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, aAbsRange)) 1000 { 1001 OSL_FAIL( "ConventionOOO_A1::makeExternalRefStrImpl: sheet name not found"); 1002 // aLastTabName contains #REF!, proceed. 1003 } 1004 } 1005 else if (bODF) 1006 rBuffer.append( '.'); // need at least the sheet separator in ODF 1007 makeExternalSingleRefStr(rLimits, 1008 rBuffer, rFileName, aLastTabName, rRef.Ref2, rPos, bDisplayTabName, bEncodeUrl); 1009 } while (false); 1010 1011 if (bODF) 1012 rBuffer.append( ']'); 1013 } 1014 1015 virtual void makeExternalRefStr( 1016 ScSheetLimits& rLimits, 1017 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1018 const std::vector<OUString>& rTabNames, const OUString& rTabName, 1019 const ScComplexRefData& rRef ) const override 1020 { 1021 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, false); 1022 } 1023 }; 1024 1025 struct ConventionOOO_A1_ODF : public ConventionOOO_A1 1026 { 1027 ConventionOOO_A1_ODF() : ConventionOOO_A1 (FormulaGrammar::CONV_ODF) { } 1028 1029 virtual void makeRefStr( 1030 ScSheetLimits& rLimits, 1031 OUStringBuffer& rBuffer, 1032 formula::FormulaGrammar::Grammar eGram, 1033 const ScAddress& rPos, 1034 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 1035 const ScComplexRefData& rRef, 1036 bool bSingleRef, 1037 bool bFromRangeName ) const override 1038 { 1039 rBuffer.append('['); 1040 // In case absolute/relative positions weren't separately available: 1041 // transform relative to absolute! 1042 ScAddress aAbs1 = rRef.Ref1.toAbs(rLimits, rPos), aAbs2; 1043 if( !bSingleRef ) 1044 aAbs2 = rRef.Ref2.toAbs(rLimits, rPos); 1045 1046 if (FormulaGrammar::isODFF(eGram) && (rRef.Ref1.IsDeleted() || !rLimits.ValidAddress(aAbs1) || 1047 (!bSingleRef && (rRef.Ref2.IsDeleted() || !rLimits.ValidAddress(aAbs2))))) 1048 { 1049 rBuffer.append(rErrRef); 1050 // For ODFF write [#REF!], but not for PODF so apps reading ODF 1051 // 1.0/1.1 may have a better chance if they implemented the old 1052 // form. 1053 } 1054 else 1055 { 1056 SingletonDisplay eSingleton = bSingleRef ? SINGLETON_NONE : 1057 getSingletonDisplay( rLimits, aAbs1, aAbs2, rRef, bFromRangeName); 1058 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref1, aAbs1, false, true, eSingleton); 1059 if (!bSingleRef) 1060 { 1061 rBuffer.append(':'); 1062 MakeOneRefStrImpl(rLimits, rBuffer, rErrRef, rTabNames, rRef.Ref2, aAbs2, aAbs1.Tab() != aAbs2.Tab(), true, 1063 eSingleton); 1064 } 1065 } 1066 rBuffer.append(']'); 1067 } 1068 1069 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, 1070 const OUString& rName ) const override 1071 { 1072 return lcl_makeExternalNameStr( rFile, rName, '#', true); 1073 } 1074 1075 virtual void makeExternalRefStr( 1076 ScSheetLimits& rLimits, 1077 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1078 const OUString& rTabName, const ScSingleRefData& rRef ) const override 1079 { 1080 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabName, rRef, true); 1081 } 1082 1083 virtual void makeExternalRefStr( 1084 ScSheetLimits& rLimits, 1085 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1086 const std::vector<OUString>& rTabNames, 1087 const OUString& rTabName, const ScComplexRefData& rRef ) const override 1088 { 1089 makeExternalRefStrImpl(rLimits, rBuffer, rPos, rFileName, rTabNames, rTabName, rRef, true); 1090 } 1091 }; 1092 1093 struct ConventionXL 1094 { 1095 virtual ~ConventionXL() 1096 { 1097 } 1098 1099 static void GetTab( 1100 ScSheetLimits& rLimits, 1101 const ScAddress& rPos, const std::vector<OUString>& rTabNames, 1102 const ScSingleRefData& rRef, OUString& rTabName ) 1103 { 1104 ScAddress aAbs = rRef.toAbs(rLimits, rPos); 1105 if (rRef.IsTabDeleted() || static_cast<size_t>(aAbs.Tab()) >= rTabNames.size()) 1106 { 1107 rTabName = ScResId( STR_NO_REF_TABLE ); 1108 return; 1109 } 1110 rTabName = rTabNames[aAbs.Tab()]; 1111 } 1112 1113 static void MakeTabStr( ScSheetLimits& rLimits, OUStringBuffer& rBuf, 1114 const ScAddress& rPos, 1115 const std::vector<OUString>& rTabNames, 1116 const ScComplexRefData& rRef, 1117 bool bSingleRef ) 1118 { 1119 if( rRef.Ref1.IsFlag3D() ) 1120 { 1121 OUString aStartTabName, aEndTabName; 1122 1123 GetTab(rLimits, rPos, rTabNames, rRef.Ref1, aStartTabName); 1124 1125 if( !bSingleRef && rRef.Ref2.IsFlag3D() ) 1126 { 1127 GetTab(rLimits, rPos, rTabNames, rRef.Ref2, aEndTabName); 1128 } 1129 1130 rBuf.append( aStartTabName ); 1131 if( !bSingleRef && rRef.Ref2.IsFlag3D() && aStartTabName != aEndTabName ) 1132 { 1133 rBuf.append( ':' ); 1134 rBuf.append( aEndTabName ); 1135 } 1136 1137 rBuf.append( '!' ); 1138 } 1139 } 1140 1141 static sal_Unicode getSpecialSymbol( ScCompiler::Convention::SpecialSymbolType eSymType ) 1142 { 1143 switch (eSymType) 1144 { 1145 case ScCompiler::Convention::ABS_SHEET_PREFIX: 1146 return u'\0'; 1147 case ScCompiler::Convention::SHEET_SEPARATOR: 1148 return '!'; 1149 } 1150 return u'\0'; 1151 } 1152 1153 static bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName, 1154 const ScDocument* pDoc, 1155 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) 1156 { 1157 return lcl_parseExternalName( rSymbol, rFile, rName, '!', pDoc, pExternalLinks); 1158 } 1159 1160 static OUString makeExternalNameStr( const OUString& rFile, const OUString& rName ) 1161 { 1162 return lcl_makeExternalNameStr( rFile, rName, '!', false); 1163 } 1164 1165 static void makeExternalDocStr( OUStringBuffer& rBuffer, const OUString& rFullName ) 1166 { 1167 // Format that is easier to deal with inside OOo, because we use file 1168 // URL, and all characters are allowed. Check if it makes sense to do 1169 // it the way Gnumeric does it. Gnumeric doesn't use the URL form 1170 // and allows relative file path. 1171 // 1172 // ['file:///path/to/source/filename.xls'] 1173 1174 rBuffer.append('['); 1175 rBuffer.append('\''); 1176 OUString aFullName = INetURLObject::decode(rFullName, INetURLObject::DecodeMechanism::Unambiguous); 1177 1178 const sal_Unicode* pBuf = aFullName.getStr(); 1179 sal_Int32 nLen = aFullName.getLength(); 1180 for (sal_Int32 i = 0; i < nLen; ++i) 1181 { 1182 const sal_Unicode c = pBuf[i]; 1183 if (c == '\'') 1184 rBuffer.append(c); 1185 rBuffer.append(c); 1186 } 1187 rBuffer.append('\''); 1188 rBuffer.append(']'); 1189 } 1190 1191 static void makeExternalTabNameRange( OUStringBuffer& rBuf, const OUString& rTabName, 1192 const vector<OUString>& rTabNames, 1193 const ScRange& rRef ) 1194 { 1195 OUString aLastTabName; 1196 if (!lcl_getLastTabName(aLastTabName, rTabName, rTabNames, rRef)) 1197 { 1198 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName); 1199 return; 1200 } 1201 1202 ScRangeStringConverter::AppendTableName(rBuf, rTabName); 1203 if (rTabName != aLastTabName) 1204 { 1205 rBuf.append(':'); 1206 ScRangeStringConverter::AppendTableName(rBuf, aLastTabName); 1207 } 1208 } 1209 1210 virtual void parseExternalDocName( const OUString& rFormula, sal_Int32& rSrcPos ) const 1211 { 1212 sal_Int32 nLen = rFormula.getLength(); 1213 const sal_Unicode* p = rFormula.getStr(); 1214 sal_Unicode cPrev = 0; 1215 for (sal_Int32 i = rSrcPos; i < nLen; ++i) 1216 { 1217 sal_Unicode c = p[i]; 1218 if (i == rSrcPos) 1219 { 1220 // first character must be '['. 1221 if (c != '[') 1222 return; 1223 } 1224 else if (i == rSrcPos + 1) 1225 { 1226 // second character must be a single quote. 1227 if (c != '\'') 1228 return; 1229 } 1230 else if (c == '\'') 1231 { 1232 if (cPrev == '\'') 1233 // two successive single quote is treated as a single 1234 // valid character. 1235 c = 'a'; 1236 } 1237 else if (c == ']') 1238 { 1239 if (cPrev == '\'') 1240 { 1241 // valid source document path found. Increment the 1242 // current position to skip the source path. 1243 rSrcPos = i + 1; 1244 if (rSrcPos >= nLen) 1245 rSrcPos = nLen - 1; 1246 return; 1247 } 1248 else 1249 return; 1250 } 1251 else 1252 { 1253 // any other character 1254 if (i > rSrcPos + 2 && cPrev == '\'') 1255 // unless it's the 3rd character, a normal character 1256 // following immediately a single quote is invalid. 1257 return; 1258 } 1259 cPrev = c; 1260 } 1261 } 1262 }; 1263 1264 struct ConventionXL_A1 : public Convention_A1, public ConventionXL 1265 { 1266 ConventionXL_A1() : Convention_A1( FormulaGrammar::CONV_XL_A1 ) { } 1267 explicit ConventionXL_A1( FormulaGrammar::AddressConvention eConv ) : Convention_A1( eConv ) { } 1268 1269 static void makeSingleCellStr( ScSheetLimits& rLimits, OUStringBuffer& rBuf, const ScSingleRefData& rRef, const ScAddress& rAbs ) 1270 { 1271 if (!rRef.IsColRel()) 1272 rBuf.append('$'); 1273 MakeColStr(rLimits, rBuf, rAbs.Col()); 1274 if (!rRef.IsRowRel()) 1275 rBuf.append('$'); 1276 MakeRowStr(rLimits, rBuf, rAbs.Row()); 1277 } 1278 1279 virtual void makeRefStr( 1280 ScSheetLimits& rLimits, 1281 OUStringBuffer& rBuf, 1282 formula::FormulaGrammar::Grammar /*eGram*/, 1283 const ScAddress& rPos, 1284 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 1285 const ScComplexRefData& rRef, 1286 bool bSingleRef, 1287 bool /*bFromRangeName*/ ) const override 1288 { 1289 ScComplexRefData aRef( rRef ); 1290 1291 // Play fast and loose with invalid refs. There is not much point in producing 1292 // Foo!A1:#REF! versus #REF! at this point 1293 ScAddress aAbs1 = aRef.Ref1.toAbs(rLimits, rPos), aAbs2; 1294 1295 MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef); 1296 1297 if (!rLimits.ValidAddress(aAbs1)) 1298 { 1299 rBuf.append(rErrRef); 1300 return; 1301 } 1302 1303 if( !bSingleRef ) 1304 { 1305 aAbs2 = aRef.Ref2.toAbs(rLimits, rPos); 1306 if (!rLimits.ValidAddress(aAbs2)) 1307 { 1308 rBuf.append(rErrRef); 1309 return; 1310 } 1311 1312 if (aAbs1.Col() == 0 && aAbs2.Col() >= rLimits.mnMaxCol) 1313 { 1314 if (!aRef.Ref1.IsRowRel()) 1315 rBuf.append( '$' ); 1316 MakeRowStr(rLimits, rBuf, aAbs1.Row()); 1317 rBuf.append( ':' ); 1318 if (!aRef.Ref2.IsRowRel()) 1319 rBuf.append( '$' ); 1320 MakeRowStr(rLimits, rBuf, aAbs2.Row()); 1321 return; 1322 } 1323 1324 if (aAbs1.Row() == 0 && aAbs2.Row() >= rLimits.mnMaxRow) 1325 { 1326 if (!aRef.Ref1.IsColRel()) 1327 rBuf.append( '$' ); 1328 MakeColStr(rLimits, rBuf, aAbs1.Col()); 1329 rBuf.append( ':' ); 1330 if (!aRef.Ref2.IsColRel()) 1331 rBuf.append( '$' ); 1332 MakeColStr(rLimits, rBuf, aAbs2.Col()); 1333 return; 1334 } 1335 } 1336 1337 makeSingleCellStr(rLimits, rBuf, aRef.Ref1, aAbs1); 1338 if (!bSingleRef) 1339 { 1340 rBuf.append( ':' ); 1341 makeSingleCellStr(rLimits, rBuf, aRef.Ref2, aAbs2); 1342 } 1343 } 1344 1345 virtual ParseResult parseAnyToken( const OUString& rFormula, 1346 sal_Int32 nSrcPos, 1347 const CharClass* pCharClass, 1348 bool bGroupSeparator) const override 1349 { 1350 parseExternalDocName(rFormula, nSrcPos); 1351 1352 ParseResult aRet; 1353 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) 1354 return aRet; 1355 1356 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | 1357 KParseTokens::ASC_UNDERSCORE | KParseTokens::ASC_DOLLAR; 1358 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; 1359 // '?' allowed in range names 1360 static const OUString aAddAllowed("?!"); 1361 return pCharClass->parseAnyToken( rFormula, 1362 nSrcPos, nStartFlags, aAddAllowed, 1363 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags), 1364 aAddAllowed ); 1365 } 1366 1367 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override 1368 { 1369 return ConventionXL::getSpecialSymbol(eSymType); 1370 } 1371 1372 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName, 1373 const ScDocument* pDoc, 1374 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override 1375 { 1376 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); 1377 } 1378 1379 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, 1380 const OUString& rName ) const override 1381 { 1382 return ConventionXL::makeExternalNameStr(rFile, rName); 1383 } 1384 1385 virtual void makeExternalRefStr( 1386 ScSheetLimits& rLimits, 1387 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1388 const OUString& rTabName, const ScSingleRefData& rRef ) const override 1389 { 1390 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 1391 // This is a little different from the format Excel uses, as Excel 1392 // puts [] only around the file name. But we need to enclose the 1393 // whole file path with [] because the file name can contain any 1394 // characters. 1395 1396 ConventionXL::makeExternalDocStr(rBuffer, rFileName); 1397 ScRangeStringConverter::AppendTableName(rBuffer, rTabName); 1398 rBuffer.append('!'); 1399 1400 makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos)); 1401 } 1402 1403 virtual void makeExternalRefStr( 1404 ScSheetLimits& rLimits, 1405 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1406 const std::vector<OUString>& rTabNames, const OUString& rTabName, 1407 const ScComplexRefData& rRef ) const override 1408 { 1409 ScRange aAbsRef = rRef.toAbs(rLimits, rPos); 1410 1411 ConventionXL::makeExternalDocStr(rBuffer, rFileName); 1412 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef); 1413 rBuffer.append('!'); 1414 1415 makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart); 1416 if (aAbsRef.aStart != aAbsRef.aEnd) 1417 { 1418 rBuffer.append(':'); 1419 makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd); 1420 } 1421 } 1422 }; 1423 1424 struct ConventionXL_OOX : public ConventionXL_A1 1425 { 1426 ConventionXL_OOX() : ConventionXL_A1( FormulaGrammar::CONV_XL_OOX ) { } 1427 1428 virtual void makeRefStr( ScSheetLimits& rLimits, 1429 OUStringBuffer& rBuf, 1430 formula::FormulaGrammar::Grammar eGram, 1431 const ScAddress& rPos, 1432 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 1433 const ScComplexRefData& rRef, 1434 bool bSingleRef, 1435 bool bFromRangeName ) const override 1436 { 1437 // In OOXML relative references in named expressions are relative to 1438 // column 0 and row 0. Relative sheet references don't exist. 1439 ScAddress aPos( rPos ); 1440 if (bFromRangeName) 1441 { 1442 // XXX NOTE: by decrementing the reference position we may end up 1443 // with resolved references with negative values. There's no proper 1444 // way to solve that or wrap them around without sheet dimensions 1445 // that are stored along. That, or blindly assume fixed dimensions 1446 // here and in import. 1447 /* TODO: maybe do that blind fixed dimensions wrap? */ 1448 aPos.SetCol(0); 1449 aPos.SetRow(0); 1450 } 1451 1452 if (rRef.Ref1.IsDeleted() || (!bSingleRef && rRef.Ref2.IsDeleted())) 1453 { 1454 // For OOXML write plain "#REF!" instead of detailed sheet/col/row 1455 // information. 1456 rBuf.append(rErrRef); 1457 return; 1458 } 1459 1460 ConventionXL_A1::makeRefStr( rLimits, rBuf, eGram, aPos, rErrRef, rTabNames, rRef, bSingleRef, bFromRangeName); 1461 } 1462 1463 virtual OUString makeExternalNameStr( sal_uInt16 nFileId, const OUString& /*rFile*/, 1464 const OUString& rName ) const override 1465 { 1466 // [N]!DefinedName is a workbook global name. 1467 return OUString( "[" + OUString::number(nFileId+1) + "]!" + rName ); 1468 1469 /* TODO: add support for sheet local names, would be 1470 * [N]'Sheet Name'!DefinedName 1471 * Similar to makeExternalRefStr() but with DefinedName instead of 1472 * CellStr. */ 1473 } 1474 1475 virtual void parseExternalDocName(const OUString& rFormula, sal_Int32& rSrcPos) const override 1476 { 1477 sal_Int32 nLen = rFormula.getLength(); 1478 const sal_Unicode* p = rFormula.getStr(); 1479 for (sal_Int32 i = rSrcPos; i < nLen; ++i) 1480 { 1481 sal_Unicode c = p[i]; 1482 if (i == rSrcPos) 1483 { 1484 // first character must be '['. 1485 if (c != '[') 1486 return; 1487 } 1488 else if (c == ']') 1489 { 1490 rSrcPos = i + 1; 1491 return; 1492 } 1493 } 1494 } 1495 1496 virtual void makeExternalRefStr( 1497 ScSheetLimits& rLimits, 1498 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/, 1499 const OUString& rTabName, const ScSingleRefData& rRef ) const override 1500 { 1501 // '[N]Sheet Name'!$A$1 or [N]SheetName!$A$1 1502 // Where N is a 1-based positive integer number of a file name in OOXML 1503 // xl/externalLinks/externalLinkN.xml 1504 1505 OUString aQuotedTab( rTabName); 1506 ScCompiler::CheckTabQuotes( aQuotedTab); 1507 if (!aQuotedTab.isEmpty() && aQuotedTab[0] == '\'') 1508 { 1509 rBuffer.append('\''); 1510 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId); 1511 rBuffer.append( aQuotedTab.copy(1)); 1512 } 1513 else 1514 { 1515 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId); 1516 rBuffer.append( aQuotedTab); 1517 } 1518 rBuffer.append('!'); 1519 1520 makeSingleCellStr(rLimits, rBuffer, rRef, rRef.toAbs(rLimits, rPos)); 1521 } 1522 1523 virtual void makeExternalRefStr( 1524 ScSheetLimits& rLimits, 1525 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 nFileId, const OUString& /*rFileName*/, 1526 const std::vector<OUString>& rTabNames, const OUString& rTabName, 1527 const ScComplexRefData& rRef ) const override 1528 { 1529 // '[N]Sheet One':'Sheet Two'!A1:B2 or [N]SheetOne!A1:B2 1530 // Actually Excel writes '[N]Sheet One:Sheet Two'!A1:B2 but reads the 1531 // simpler to produce and more logical form with independently quoted 1532 // sheet names as well. The [N] having to be within the quoted sheet 1533 // name is ugly enough... 1534 1535 ScRange aAbsRef = rRef.toAbs(rLimits, rPos); 1536 1537 OUStringBuffer aBuf; 1538 ConventionXL::makeExternalTabNameRange( aBuf, rTabName, rTabNames, aAbsRef); 1539 if (!aBuf.isEmpty() && aBuf[0] == '\'') 1540 { 1541 rBuffer.append('\''); 1542 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId); 1543 rBuffer.append( aBuf.copy(1)); 1544 } 1545 else 1546 { 1547 ConventionXL_OOX::makeExternalDocStr( rBuffer, nFileId); 1548 rBuffer.append( aBuf); 1549 } 1550 rBuffer.append('!'); 1551 1552 makeSingleCellStr(rLimits, rBuffer, rRef.Ref1, aAbsRef.aStart); 1553 if (aAbsRef.aStart != aAbsRef.aEnd) 1554 { 1555 rBuffer.append(':'); 1556 makeSingleCellStr(rLimits, rBuffer, rRef.Ref2, aAbsRef.aEnd); 1557 } 1558 } 1559 1560 static void makeExternalDocStr( OUStringBuffer& rBuffer, sal_uInt16 nFileId ) 1561 { 1562 rBuffer.append('[').append( OUString::number( nFileId+1)).append(']'); 1563 } 1564 }; 1565 1566 } 1567 1568 static void 1569 r1c1_add_col( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef ) 1570 { 1571 rBuf.append( 'C' ); 1572 if( rRef.IsColRel() ) 1573 { 1574 SCCOL nCol = rRef.Col(); 1575 if (nCol != 0) 1576 rBuf.append("[").append(OUString::number(nCol)).append("]"); 1577 } 1578 else 1579 rBuf.append( OUString::number( rAbsRef.Col() + 1 ) ); 1580 } 1581 static void 1582 r1c1_add_row( OUStringBuffer &rBuf, const ScSingleRefData& rRef, const ScAddress& rAbsRef ) 1583 { 1584 rBuf.append( 'R' ); 1585 if( rRef.IsRowRel() ) 1586 { 1587 if (rRef.Row() != 0) 1588 { 1589 rBuf.append("[").append( OUString::number(rRef.Row()) ).append("]"); 1590 } 1591 } 1592 else 1593 rBuf.append( OUString::number( rAbsRef.Row() + 1 ) ); 1594 } 1595 1596 namespace { 1597 1598 struct ConventionXL_R1C1 : public ScCompiler::Convention, public ConventionXL 1599 { 1600 ConventionXL_R1C1() : ScCompiler::Convention( FormulaGrammar::CONV_XL_R1C1 ) { } 1601 1602 virtual void makeRefStr( ScSheetLimits& rLimits, 1603 OUStringBuffer& rBuf, 1604 formula::FormulaGrammar::Grammar /*eGram*/, 1605 const ScAddress& rPos, 1606 const OUString& rErrRef, const std::vector<OUString>& rTabNames, 1607 const ScComplexRefData& rRef, 1608 bool bSingleRef, 1609 bool /*bFromRangeName*/ ) const override 1610 { 1611 ScRange aAbsRef = rRef.toAbs(rLimits, rPos); 1612 ScComplexRefData aRef( rRef ); 1613 1614 MakeTabStr(rLimits, rBuf, rPos, rTabNames, aRef, bSingleRef); 1615 1616 // Play fast and loose with invalid refs. There is not much point in producing 1617 // Foo!A1:#REF! versus #REF! at this point 1618 if (!rLimits.ValidCol(aAbsRef.aStart.Col()) || !rLimits.ValidRow(aAbsRef.aStart.Row())) 1619 { 1620 rBuf.append(rErrRef); 1621 return; 1622 } 1623 1624 if( !bSingleRef ) 1625 { 1626 if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row())) 1627 { 1628 rBuf.append(rErrRef); 1629 return; 1630 } 1631 1632 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol) 1633 { 1634 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart); 1635 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || 1636 rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel() ) 1637 { 1638 rBuf.append( ':' ); 1639 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd); 1640 } 1641 return; 1642 1643 } 1644 1645 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow) 1646 { 1647 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart); 1648 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || 1649 rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel()) 1650 { 1651 rBuf.append( ':' ); 1652 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd); 1653 } 1654 return; 1655 } 1656 } 1657 1658 r1c1_add_row(rBuf, rRef.Ref1, aAbsRef.aStart); 1659 r1c1_add_col(rBuf, rRef.Ref1, aAbsRef.aStart); 1660 if (!bSingleRef) 1661 { 1662 rBuf.append( ':' ); 1663 r1c1_add_row(rBuf, rRef.Ref2, aAbsRef.aEnd); 1664 r1c1_add_col(rBuf, rRef.Ref2, aAbsRef.aEnd); 1665 } 1666 } 1667 1668 ParseResult parseAnyToken( const OUString& rFormula, 1669 sal_Int32 nSrcPos, 1670 const CharClass* pCharClass, 1671 bool bGroupSeparator) const override 1672 { 1673 parseExternalDocName(rFormula, nSrcPos); 1674 1675 ParseResult aRet; 1676 if ( lcl_isValidQuotedText(rFormula, nSrcPos, aRet) ) 1677 return aRet; 1678 1679 constexpr sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | 1680 KParseTokens::ASC_UNDERSCORE ; 1681 constexpr sal_Int32 nContFlags = nStartFlags | KParseTokens::ASC_DOT; 1682 // '?' allowed in range names 1683 static const OUString aAddAllowed("?-[]!"); 1684 1685 return pCharClass->parseAnyToken( rFormula, 1686 nSrcPos, nStartFlags, aAddAllowed, 1687 (bGroupSeparator ? nContFlags | KParseTokens::GROUP_SEPARATOR_IN_NUMBER : nContFlags), 1688 aAddAllowed ); 1689 } 1690 1691 virtual sal_Unicode getSpecialSymbol( SpecialSymbolType eSymType ) const override 1692 { 1693 return ConventionXL::getSpecialSymbol(eSymType); 1694 } 1695 1696 virtual bool parseExternalName( const OUString& rSymbol, OUString& rFile, OUString& rName, 1697 const ScDocument* pDoc, 1698 const uno::Sequence<sheet::ExternalLinkInfo>* pExternalLinks ) const override 1699 { 1700 return ConventionXL::parseExternalName( rSymbol, rFile, rName, pDoc, pExternalLinks); 1701 } 1702 1703 virtual OUString makeExternalNameStr( sal_uInt16 /*nFileId*/, const OUString& rFile, 1704 const OUString& rName ) const override 1705 { 1706 return ConventionXL::makeExternalNameStr(rFile, rName); 1707 } 1708 1709 virtual void makeExternalRefStr( 1710 ScSheetLimits& rLimits, 1711 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1712 const OUString& rTabName, const ScSingleRefData& rRef ) const override 1713 { 1714 // ['file:///path/to/file/filename.xls']'Sheet Name'!$A$1 1715 // This is a little different from the format Excel uses, as Excel 1716 // puts [] only around the file name. But we need to enclose the 1717 // whole file path with [] because the file name can contain any 1718 // characters. 1719 1720 ScAddress aAbsRef = rRef.toAbs(rLimits, rPos); 1721 ConventionXL::makeExternalDocStr(rBuffer, rFileName); 1722 ScRangeStringConverter::AppendTableName(rBuffer, rTabName); 1723 rBuffer.append('!'); 1724 1725 r1c1_add_row(rBuffer, rRef, aAbsRef); 1726 r1c1_add_col(rBuffer, rRef, aAbsRef); 1727 } 1728 1729 virtual void makeExternalRefStr( 1730 ScSheetLimits& rLimits, 1731 OUStringBuffer& rBuffer, const ScAddress& rPos, sal_uInt16 /*nFileId*/, const OUString& rFileName, 1732 const std::vector<OUString>& rTabNames, const OUString& rTabName, 1733 const ScComplexRefData& rRef ) const override 1734 { 1735 ScRange aAbsRef = rRef.toAbs(rLimits, rPos); 1736 1737 ConventionXL::makeExternalDocStr(rBuffer, rFileName); 1738 ConventionXL::makeExternalTabNameRange(rBuffer, rTabName, rTabNames, aAbsRef); 1739 rBuffer.append('!'); 1740 1741 if (!rLimits.ValidCol(aAbsRef.aEnd.Col()) || !rLimits.ValidRow(aAbsRef.aEnd.Row())) 1742 { 1743 rBuffer.append(ScResId(STR_NO_REF_TABLE)); 1744 return; 1745 } 1746 1747 if (aAbsRef.aStart.Col() == 0 && aAbsRef.aEnd.Col() >= rLimits.mnMaxCol) 1748 { 1749 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart); 1750 if (aAbsRef.aStart.Row() != aAbsRef.aEnd.Row() || rRef.Ref1.IsRowRel() != rRef.Ref2.IsRowRel()) 1751 { 1752 rBuffer.append(':'); 1753 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd); 1754 } 1755 return; 1756 } 1757 1758 if (aAbsRef.aStart.Row() == 0 && aAbsRef.aEnd.Row() >= rLimits.mnMaxRow) 1759 { 1760 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart); 1761 if (aAbsRef.aStart.Col() != aAbsRef.aEnd.Col() || rRef.Ref1.IsColRel() != rRef.Ref2.IsColRel()) 1762 { 1763 rBuffer.append(':'); 1764 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd); 1765 } 1766 return; 1767 } 1768 1769 r1c1_add_row(rBuffer, rRef.Ref1, aAbsRef.aStart); 1770 r1c1_add_col(rBuffer, rRef.Ref1, aAbsRef.aStart); 1771 rBuffer.append(':'); 1772 r1c1_add_row(rBuffer, rRef.Ref2, aAbsRef.aEnd); 1773 r1c1_add_col(rBuffer, rRef.Ref2, aAbsRef.aEnd); 1774 } 1775 1776 virtual ScCharFlags getCharTableFlags( sal_Unicode c, sal_Unicode cLast ) const override 1777 { 1778 ScCharFlags nFlags = mpCharTable[static_cast<sal_uInt8>(c)]; 1779 if (c == '-' && cLast == '[') 1780 // '-' can occur within a reference string only after '[' e.g. R[-1]C. 1781 nFlags |= ScCharFlags::Ident; 1782 return nFlags; 1783 } 1784 }; 1785 1786 } 1787 1788 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, ScTokenArray& rArr, 1789 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext ) 1790 : FormulaCompiler(rArr, bComputeII, bMatrixFlag), 1791 pDoc(rCxt.getDoc()), 1792 aPos(rPos), 1793 mpFormatter(pContext? pContext->GetFormatTable() : pDoc->GetFormatTable()), 1794 mpInterpreterContext(pContext), 1795 mnCurrentSheetTab(-1), 1796 mnCurrentSheetEndPos(0), 1797 pCharClass(ScGlobal::pCharClass), 1798 mnPredetectedReference(0), 1799 mnRangeOpPosInSymbol(-1), 1800 pConv(GetRefConvention(FormulaGrammar::CONV_OOO)), 1801 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE), 1802 mbCloseBrackets(true), 1803 mbRewind(false), 1804 maTabNames(rCxt.getTabNames()) 1805 { 1806 SetGrammar(rCxt.getGrammar()); 1807 } 1808 1809 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos, ScTokenArray& rArr, 1810 formula::FormulaGrammar::Grammar eGrammar, 1811 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext ) 1812 : FormulaCompiler(rArr, bComputeII, bMatrixFlag), 1813 pDoc( pDocument ), 1814 aPos( rPos ), 1815 mpFormatter(pContext ? pContext->GetFormatTable() : pDoc->GetFormatTable()), 1816 mpInterpreterContext(pContext), 1817 mnCurrentSheetTab(-1), 1818 mnCurrentSheetEndPos(0), 1819 nSrcPos(0), 1820 pCharClass( ScGlobal::pCharClass ), 1821 mnPredetectedReference(0), 1822 mnRangeOpPosInSymbol(-1), 1823 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ), 1824 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ), 1825 mbCloseBrackets( true ), 1826 mbRewind( false ) 1827 { 1828 SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ? 1829 pDocument->GetGrammar() : 1830 eGrammar ); 1831 } 1832 1833 ScCompiler::ScCompiler( sc::CompileFormulaContext& rCxt, const ScAddress& rPos, 1834 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext ) 1835 : FormulaCompiler(bComputeII, bMatrixFlag), 1836 pDoc(rCxt.getDoc()), 1837 aPos(rPos), 1838 mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr), 1839 mpInterpreterContext(pContext), 1840 mnCurrentSheetTab(-1), 1841 mnCurrentSheetEndPos(0), 1842 pCharClass(ScGlobal::pCharClass), 1843 mnPredetectedReference(0), 1844 mnRangeOpPosInSymbol(-1), 1845 pConv(GetRefConvention(FormulaGrammar::CONV_OOO)), 1846 meExtendedErrorDetection(EXTENDED_ERROR_DETECTION_NONE), 1847 mbCloseBrackets(true), 1848 mbRewind(false), 1849 maTabNames(rCxt.getTabNames()) 1850 { 1851 SetGrammar(rCxt.getGrammar()); 1852 } 1853 1854 ScCompiler::ScCompiler( ScDocument* pDocument, const ScAddress& rPos, 1855 formula::FormulaGrammar::Grammar eGrammar, 1856 bool bComputeII, bool bMatrixFlag, const ScInterpreterContext* pContext ) 1857 : FormulaCompiler(bComputeII, bMatrixFlag), 1858 pDoc( pDocument ), 1859 aPos( rPos ), 1860 mpFormatter(pContext ? pContext->GetFormatTable() : pDoc ? pDoc->GetFormatTable() : nullptr), 1861 mpInterpreterContext(pContext), 1862 mnCurrentSheetTab(-1), 1863 mnCurrentSheetEndPos(0), 1864 nSrcPos(0), 1865 pCharClass( ScGlobal::pCharClass ), 1866 mnPredetectedReference(0), 1867 mnRangeOpPosInSymbol(-1), 1868 pConv( GetRefConvention( FormulaGrammar::CONV_OOO ) ), 1869 meExtendedErrorDetection( EXTENDED_ERROR_DETECTION_NONE ), 1870 mbCloseBrackets( true ), 1871 mbRewind( false ) 1872 { 1873 SetGrammar( (eGrammar == formula::FormulaGrammar::GRAM_UNSPECIFIED) ? 1874 (pDocument ? pDocument->GetGrammar() : formula::FormulaGrammar::GRAM_DEFAULT) : 1875 eGrammar ); 1876 } 1877 1878 ScCompiler::~ScCompiler() 1879 { 1880 } 1881 1882 void ScCompiler::CheckTabQuotes( OUString& rString, 1883 const FormulaGrammar::AddressConvention eConv ) 1884 { 1885 sal_Int32 nStartFlags = KParseTokens::ANY_LETTER_OR_NUMBER | KParseTokens::ASC_UNDERSCORE; 1886 sal_Int32 nContFlags = nStartFlags; 1887 ParseResult aRes = ScGlobal::pCharClass->parsePredefinedToken( 1888 KParseType::IDENTNAME, rString, 0, nStartFlags, EMPTY_OUSTRING, nContFlags, EMPTY_OUSTRING); 1889 bool bNeedsQuote = !((aRes.TokenType & KParseType::IDENTNAME) && aRes.EndPos == rString.getLength()); 1890 1891 switch ( eConv ) 1892 { 1893 default : 1894 case FormulaGrammar::CONV_UNSPECIFIED : 1895 break; 1896 case FormulaGrammar::CONV_OOO : 1897 case FormulaGrammar::CONV_XL_A1 : 1898 case FormulaGrammar::CONV_XL_R1C1 : 1899 case FormulaGrammar::CONV_XL_OOX : 1900 case FormulaGrammar::CONV_ODF : 1901 if( bNeedsQuote ) 1902 { 1903 const OUString one_quote('\''); 1904 const OUString two_quote("''"); 1905 // escape embedded quotes 1906 rString = rString.replaceAll( one_quote, two_quote ); 1907 } 1908 break; 1909 } 1910 1911 if ( !bNeedsQuote && CharClass::isAsciiNumeric( rString ) ) 1912 { 1913 // Prevent any possible confusion resulting from pure numeric sheet names. 1914 bNeedsQuote = true; 1915 } 1916 1917 if( bNeedsQuote ) 1918 { 1919 rString = "'" + rString + "'"; 1920 } 1921 } 1922 1923 sal_Int32 ScCompiler::GetDocTabPos( const OUString& rString ) 1924 { 1925 if (rString[0] != '\'') 1926 return -1; 1927 sal_Int32 nPos = ScGlobal::FindUnquoted( rString, SC_COMPILER_FILE_TAB_SEP); 1928 // it must be 'Doc'# 1929 if (nPos != -1 && rString[nPos-1] != '\'') 1930 nPos = -1; 1931 return nPos; 1932 } 1933 1934 void ScCompiler::SetRefConvention( FormulaGrammar::AddressConvention eConv ) 1935 { 1936 const Convention* p = GetRefConvention(eConv); 1937 if (p) 1938 SetRefConvention(p); 1939 } 1940 1941 const ScCompiler::Convention* ScCompiler::GetRefConvention( FormulaGrammar::AddressConvention eConv ) 1942 { 1943 1944 switch (eConv) 1945 { 1946 case FormulaGrammar::CONV_OOO: 1947 { 1948 static const ConventionOOO_A1 ConvOOO_A1; 1949 return &ConvOOO_A1; 1950 } 1951 case FormulaGrammar::CONV_ODF: 1952 { 1953 static const ConventionOOO_A1_ODF ConvOOO_A1_ODF; 1954 return &ConvOOO_A1_ODF; 1955 } 1956 case FormulaGrammar::CONV_XL_A1: 1957 { 1958 static const ConventionXL_A1 ConvXL_A1; 1959 return &ConvXL_A1; 1960 } 1961 case FormulaGrammar::CONV_XL_R1C1: 1962 { 1963 static const ConventionXL_R1C1 ConvXL_R1C1; 1964 return &ConvXL_R1C1; 1965 } 1966 case FormulaGrammar::CONV_XL_OOX: 1967 { 1968 static const ConventionXL_OOX ConvXL_OOX; 1969 return &ConvXL_OOX; 1970 } 1971 case FormulaGrammar::CONV_UNSPECIFIED: 1972 default: 1973 ; 1974 } 1975 1976 return nullptr; 1977 } 1978 1979 void ScCompiler::SetRefConvention( const ScCompiler::Convention *pConvP ) 1980 { 1981 pConv = pConvP; 1982 meGrammar = FormulaGrammar::mergeToGrammar( meGrammar, pConv->meConv); 1983 OSL_ENSURE( FormulaGrammar::isSupported( meGrammar), 1984 "ScCompiler::SetRefConvention: unsupported grammar resulting"); 1985 } 1986 1987 void ScCompiler::SetError(FormulaError nError) 1988 { 1989 if( pArr->GetCodeError() == FormulaError::NONE) 1990 pArr->SetCodeError( nError); 1991 } 1992 1993 static sal_Unicode* lcl_UnicodeStrNCpy( sal_Unicode* pDst, const sal_Unicode* pSrc, sal_Int32 nMax ) 1994 { 1995 const sal_Unicode* const pStop = pDst + nMax; 1996 while ( pDst < pStop ) 1997 { 1998 *pDst++ = *pSrc++; 1999 } 2000 *pDst = 0; 2001 return pDst; 2002 } 2003 2004 // p1 MUST contain at least n characters, or terminate with NIL. 2005 // p2 MUST pass upper case letters, if any. 2006 // n MUST not be greater than length of p2 2007 static bool lcl_isUnicodeIgnoreAscii( const sal_Unicode* p1, const char* p2, size_t n ) 2008 { 2009 for (size_t i=0; i<n; ++i) 2010 { 2011 if (!p1[i]) 2012 return false; 2013 if (p1[i] != p2[i]) 2014 { 2015 if (p1[i] < 'a' || 'z' < p1[i]) 2016 return false; // not a lower case letter 2017 if (p2[i] < 'A' || 'Z' < p2[i]) 2018 return false; // not a letter to match 2019 if (p1[i] != p2[i] + 0x20) 2020 return false; // lower case doesn't match either 2021 } 2022 } 2023 return true; 2024 } 2025 2026 // NextSymbol 2027 2028 // Parses the formula into separate symbols for further processing. 2029 // XXX NOTE: this is a rough sketch of the original idea, there are other 2030 // states that were added and didn't make it into this table and things are 2031 // more complicated. Use the source, Luke. 2032 2033 // initial state = GetChar 2034 2035 // old state | read character | action | new state 2036 //---------------+-------------------+-----------------------+--------------- 2037 // GetChar | ;()+-*/^=& | Symbol=char | Stop 2038 // | <> | Symbol=char | GetBool 2039 // | $ letter | Symbol=char | GetWord 2040 // | number | Symbol=char | GetValue 2041 // | " | none | GetString 2042 // | other | none | GetChar 2043 //---------------+-------------------+-----------------------+--------------- 2044 // GetBool | => | Symbol=Symbol+char | Stop 2045 // | other | Dec(CharPos) | Stop 2046 //---------------+-------------------+-----------------------+--------------- 2047 // GetWord | SepSymbol | Dec(CharPos) | Stop 2048 // | ()+-*/^=<>&~ | | 2049 // | space | Dec(CharPos) | Stop 2050 // | $_:. | | 2051 // | letter, number | Symbol=Symbol+char | GetWord 2052 // | other | error | Stop 2053 //---------------+-------------------+-----------------------+--------------- 2054 // GetValue | ;()*/^=<>& | | 2055 // | space | Dec(CharPos) | Stop 2056 // | number E+-%,. | Symbol=Symbol+char | GetValue 2057 // | other | error | Stop 2058 //---------------+-------------------+-----------------------+--------------- 2059 // GetString | " | none | Stop 2060 // | other | Symbol=Symbol+char | GetString 2061 //---------------+-------------------+-----------------------+--------------- 2062 2063 sal_Int32 ScCompiler::NextSymbol(bool bInArray) 2064 { 2065 cSymbol[MAXSTRLEN] = 0; // end 2066 sal_Unicode* pSym = cSymbol; 2067 const sal_Unicode* const pStart = aFormula.getStr(); 2068 const sal_Unicode* pSrc = pStart + nSrcPos; 2069 bool bi18n = false; 2070 sal_Unicode c = *pSrc; 2071 sal_Unicode cLast = 0; 2072 bool bQuote = false; 2073 mnRangeOpPosInSymbol = -1; 2074 ScanState eState = ssGetChar; 2075 sal_Int32 nSpaces = 0; 2076 sal_Unicode cSep = mxSymbols->getSymbolChar( ocSep); 2077 sal_Unicode cArrayColSep = mxSymbols->getSymbolChar( ocArrayColSep); 2078 sal_Unicode cArrayRowSep = mxSymbols->getSymbolChar( ocArrayRowSep); 2079 sal_Unicode cDecSep = (mxSymbols->isEnglish() ? '.' : ScGlobal::pLocaleData->getNumDecimalSep()[0]); 2080 sal_Unicode cDecSepAlt = (mxSymbols->isEnglish() ? 0 : ScGlobal::pLocaleData->getNumDecimalSepAlt().toChar()); 2081 2082 // special symbols specific to address convention used 2083 sal_Unicode cSheetPrefix = pConv->getSpecialSymbol(ScCompiler::Convention::ABS_SHEET_PREFIX); 2084 sal_Unicode cSheetSep = pConv->getSpecialSymbol(ScCompiler::Convention::SHEET_SEPARATOR); 2085 2086 int nDecSeps = 0; 2087 bool bAutoIntersection = false; 2088 int nRefInName = 0; 2089 bool bErrorConstantHadSlash = false; 2090 mnPredetectedReference = 0; 2091 // try to parse simple tokens before calling i18n parser 2092 while ((c != 0) && (eState != ssStop) ) 2093 { 2094 pSrc++; 2095 ScCharFlags nMask = GetCharTableFlags( c, cLast ); 2096 2097 // The parameter separator and the array column and row separators end 2098 // things unconditionally if not in string or reference. 2099 if (c == cSep || (bInArray && (c == cArrayColSep || c == cArrayRowSep))) 2100 { 2101 switch (eState) 2102 { 2103 // these are to be continued 2104 case ssGetString: 2105 case ssSkipString: 2106 case ssGetReference: 2107 case ssSkipReference: 2108 break; 2109 default: 2110 if (eState == ssGetChar) 2111 *pSym++ = c; 2112 else 2113 pSrc--; 2114 eState = ssStop; 2115 } 2116 } 2117 Label_MaskStateMachine: 2118 switch (eState) 2119 { 2120 case ssGetChar : 2121 { 2122 // Order is important! 2123 if (eLastOp == ocTableRefOpen && c != '[' && c != '#' && c != ']') 2124 { 2125 *pSym++ = c; 2126 eState = ssGetTableRefColumn; 2127 } 2128 else if( nMask & ScCharFlags::OdfLabelOp ) 2129 { 2130 // '!!' automatic intersection 2131 if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfLabelOp) 2132 { 2133 /* TODO: For now the UI "space operator" is used, this 2134 * could be enhanced using a specialized OpCode to get 2135 * rid of the space ambiguity, which would need some 2136 * places to be adapted though. And we would still need 2137 * to support the ambiguous space operator for UI 2138 * purposes anyway. However, we then could check for 2139 * invalid usage of '!!', which currently isn't 2140 * possible. */ 2141 if (!bAutoIntersection) 2142 { 2143 ++pSrc; 2144 nSpaces += 2; // must match the character count 2145 bAutoIntersection = true; 2146 } 2147 else 2148 { 2149 pSrc--; 2150 eState = ssStop; 2151 } 2152 } 2153 else 2154 { 2155 nMask &= ~ScCharFlags::OdfLabelOp; 2156 goto Label_MaskStateMachine; 2157 } 2158 } 2159 else if( nMask & ScCharFlags::OdfNameMarker ) 2160 { 2161 // '$$' defined name marker 2162 if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::OdfNameMarker) 2163 { 2164 // both eaten, not added to pSym 2165 ++pSrc; 2166 } 2167 else 2168 { 2169 nMask &= ~ScCharFlags::OdfNameMarker; 2170 goto Label_MaskStateMachine; 2171 } 2172 } 2173 else if( nMask & ScCharFlags::Char ) 2174 { 2175 // '[' is a special case in Excel syntax, it can start an 2176 // external reference, ID in OOXML like [1]Sheet1!A1 or 2177 // Excel_A1 [filename]Sheet!A1 or Excel_R1C1 2178 // [filename]Sheet!R1C1 that needs to be scanned 2179 // entirely, or can be ocTableRefOpen, of which the first 2180 // transforms an ocDBArea into an ocTableRef. 2181 if (c == '[' && FormulaGrammar::isExcelSyntax( meGrammar) 2182 && eLastOp != ocDBArea && maTableRefs.empty()) 2183 { 2184 nMask &= ~ScCharFlags::Char; 2185 goto Label_MaskStateMachine; 2186 } 2187 else 2188 { 2189 *pSym++ = c; 2190 eState = ssStop; 2191 } 2192 } 2193 else if( nMask & ScCharFlags::OdfLBracket ) 2194 { 2195 // eaten, not added to pSym 2196 eState = ssGetReference; 2197 mnPredetectedReference = 1; 2198 } 2199 else if( nMask & ScCharFlags::CharBool ) 2200 { 2201 *pSym++ = c; 2202 eState = ssGetBool; 2203 } 2204 else if( nMask & ScCharFlags::CharValue ) 2205 { 2206 *pSym++ = c; 2207 eState = ssGetValue; 2208 } 2209 else if( nMask & ScCharFlags::CharString ) 2210 { 2211 *pSym++ = c; 2212 eState = ssGetString; 2213 } 2214 else if( nMask & ScCharFlags::CharErrConst ) 2215 { 2216 *pSym++ = c; 2217 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2) 2218 eState = ssGetTableRefItem; 2219 else 2220 eState = ssGetErrorConstant; 2221 } 2222 else if( nMask & ScCharFlags::CharDontCare ) 2223 { 2224 nSpaces++; 2225 } 2226 else if( nMask & ScCharFlags::CharIdent ) 2227 { // try to get a simple ASCII identifier before calling 2228 // i18n, to gain performance during import 2229 *pSym++ = c; 2230 eState = ssGetIdent; 2231 } 2232 else 2233 { 2234 bi18n = true; 2235 eState = ssStop; 2236 } 2237 } 2238 break; 2239 case ssGetIdent: 2240 { 2241 if ( nMask & ScCharFlags::Ident ) 2242 { // This catches also $Sheet1.A$1, for example. 2243 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2244 { 2245 SetError(FormulaError::StringOverflow); 2246 eState = ssStop; 2247 } 2248 else 2249 *pSym++ = c; 2250 } 2251 else if (c == '#' && lcl_isUnicodeIgnoreAscii( pSrc, "REF!", 4)) 2252 { 2253 // Completely ugly means to catch broken 2254 // [$]#REF!.[$]#REF![$]#REF! (one or multiple parts) 2255 // references that were written in ODF named ranges 2256 // (without embracing [] hence no predetected reference) 2257 // and to OOXML and handle them as one symbol. 2258 // Also catches these in UI, so we can process them 2259 // further. 2260 int i = 0; 2261 for ( ; i<5; ++i) 2262 { 2263 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2264 { 2265 SetError(FormulaError::StringOverflow); 2266 eState = ssStop; 2267 break; // for 2268 } 2269 else 2270 { 2271 *pSym++ = c; 2272 c = *pSrc++; 2273 } 2274 } 2275 if (i == 5) 2276 c = *((--pSrc)-1); // position last/next character correctly 2277 } 2278 else if (c == ':' && mnRangeOpPosInSymbol < 0) 2279 { 2280 // One range operator may form Sheet1.A:A, which we need to 2281 // pass as one entity to IsReference(). 2282 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2283 { 2284 SetError(FormulaError::StringOverflow); 2285 eState = ssStop; 2286 } 2287 else 2288 { 2289 mnRangeOpPosInSymbol = pSym - &cSymbol[0]; 2290 *pSym++ = c; 2291 } 2292 } 2293 else if ( 128 <= c || '\'' == c ) 2294 { // High values need reparsing with i18n, 2295 // single quoted $'sheet' names too (otherwise we'd had to 2296 // implement everything twice). 2297 bi18n = true; 2298 eState = ssStop; 2299 } 2300 else 2301 { 2302 pSrc--; 2303 eState = ssStop; 2304 } 2305 } 2306 break; 2307 case ssGetBool : 2308 { 2309 if( nMask & ScCharFlags::Bool ) 2310 { 2311 *pSym++ = c; 2312 eState = ssStop; 2313 } 2314 else 2315 { 2316 pSrc--; 2317 eState = ssStop; 2318 } 2319 } 2320 break; 2321 case ssGetValue : 2322 { 2323 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2324 { 2325 SetError(FormulaError::StringOverflow); 2326 eState = ssStop; 2327 } 2328 else if (c == cDecSep || (cDecSepAlt && c == cDecSepAlt)) 2329 { 2330 if (++nDecSeps > 1) 2331 { 2332 // reparse with i18n, may be numeric sheet name as well 2333 bi18n = true; 2334 eState = ssStop; 2335 } 2336 else 2337 *pSym++ = c; 2338 } 2339 else if( nMask & ScCharFlags::Value ) 2340 *pSym++ = c; 2341 else if( nMask & ScCharFlags::ValueSep ) 2342 { 2343 pSrc--; 2344 eState = ssStop; 2345 } 2346 else if (c == 'E' || c == 'e') 2347 { 2348 if (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueExp) 2349 *pSym++ = c; 2350 else 2351 { 2352 // reparse with i18n 2353 bi18n = true; 2354 eState = ssStop; 2355 } 2356 } 2357 else if( nMask & ScCharFlags::ValueSign ) 2358 { 2359 if (((cLast == 'E') || (cLast == 'e')) && 2360 (GetCharTableFlags( pSrc[0], 0 ) & ScCharFlags::ValueValue)) 2361 { 2362 *pSym++ = c; 2363 } 2364 else 2365 { 2366 pSrc--; 2367 eState = ssStop; 2368 } 2369 } 2370 else 2371 { 2372 // reparse with i18n 2373 bi18n = true; 2374 eState = ssStop; 2375 } 2376 } 2377 break; 2378 case ssGetString : 2379 { 2380 if( nMask & ScCharFlags::StringSep ) 2381 { 2382 if ( !bQuote ) 2383 { 2384 if ( *pSrc == '"' ) 2385 bQuote = true; // "" => literal " 2386 else 2387 eState = ssStop; 2388 } 2389 else 2390 bQuote = false; 2391 } 2392 if ( !bQuote ) 2393 { 2394 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2395 { 2396 SetError(FormulaError::StringOverflow); 2397 eState = ssSkipString; 2398 } 2399 else 2400 *pSym++ = c; 2401 } 2402 } 2403 break; 2404 case ssSkipString: 2405 if( nMask & ScCharFlags::StringSep ) 2406 eState = ssStop; 2407 break; 2408 case ssGetErrorConstant: 2409 { 2410 // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?])))) 2411 // BUT, in UI these may have been translated! So don't 2412 // check for ASCII alnum. Note that this construct can't be 2413 // parsed with i18n. 2414 /* TODO: be strict when reading ODFF, check for ASCII alnum 2415 * and proper continuation after '/'. However, even with 2416 * the lax parsing only the error constants we have defined 2417 * as opcode symbols will be recognized and others result 2418 * in ocBad, so the result is actually conformant. */ 2419 bool bAdd = true; 2420 if ('?' == c) 2421 eState = ssStop; 2422 else if ('!' == c) 2423 { 2424 // Check if this is #REF! that starts an invalid reference. 2425 // Note we have an implicit '!' here at the end. 2426 if (pSym - &cSymbol[0] == 4 && lcl_isUnicodeIgnoreAscii( cSymbol, "#REF", 4) && 2427 (GetCharTableFlags( *pSrc, c) & ScCharFlags::Ident)) 2428 eState = ssGetIdent; 2429 else 2430 eState = ssStop; 2431 } 2432 else if ('/' == c) 2433 { 2434 if (!bErrorConstantHadSlash) 2435 bErrorConstantHadSlash = true; 2436 else 2437 { 2438 bAdd = false; 2439 eState = ssStop; 2440 } 2441 } 2442 else if ((nMask & ScCharFlags::WordSep) || 2443 (c < 128 && !rtl::isAsciiAlphanumeric( c))) 2444 { 2445 bAdd = false; 2446 eState = ssStop; 2447 } 2448 if (!bAdd) 2449 --pSrc; 2450 else 2451 { 2452 if (pSym == &cSymbol[ MAXSTRLEN ]) 2453 { 2454 SetError( FormulaError::StringOverflow); 2455 eState = ssStop; 2456 } 2457 else 2458 *pSym++ = c; 2459 } 2460 } 2461 break; 2462 case ssGetTableRefItem: 2463 { 2464 // Scan whatever up to the next ']' closer. 2465 if (c != ']') 2466 { 2467 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2468 { 2469 SetError( FormulaError::StringOverflow); 2470 eState = ssStop; 2471 } 2472 else 2473 *pSym++ = c; 2474 } 2475 else 2476 { 2477 --pSrc; 2478 eState = ssStop; 2479 } 2480 } 2481 break; 2482 case ssGetTableRefColumn: 2483 { 2484 // Scan whatever up to the next unescaped ']' closer. 2485 if (c != ']' || cLast == '\'') 2486 { 2487 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2488 { 2489 SetError( FormulaError::StringOverflow); 2490 eState = ssStop; 2491 } 2492 else 2493 *pSym++ = c; 2494 } 2495 else 2496 { 2497 --pSrc; 2498 eState = ssStop; 2499 } 2500 } 2501 break; 2502 case ssGetReference: 2503 if( pSym == &cSymbol[ MAXSTRLEN ] ) 2504 { 2505 SetError( FormulaError::StringOverflow); 2506 eState = ssSkipReference; 2507 } 2508 [[fallthrough]]; 2509 case ssSkipReference: 2510 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being 2511 // mandatory also if no sheet name. 'External'# is optional, 2512 // sheet name is optional, quotes around sheet name are 2513 // optional if no quote contained. [#REF!] is valid. 2514 // 2nd usage: ['Sheet'.$$'DefinedName'] 2515 // 3rd usage: ['External'#$$'DefinedName'] 2516 // 4th usage: ['External'#$'Sheet'.$$'DefinedName'] 2517 // Also for all these names quotes are optional if no quote 2518 // contained. 2519 { 2520 2521 // nRefInName: 0 := not in sheet name yet. 'External' 2522 // is parsed as if it was a sheet name and nRefInName 2523 // is reset when # is encountered immediately after closing 2524 // quote. Same with 'DefinedName', nRefInName is cleared 2525 // when : is encountered. 2526 2527 // Encountered leading $ before sheet name. 2528 constexpr int kDollar = (1 << 1); 2529 // Encountered ' opening quote, which may be after $ or 2530 // not. 2531 constexpr int kOpen = (1 << 2); 2532 // Somewhere in name. 2533 constexpr int kName = (1 << 3); 2534 // Encountered ' in name, will be cleared if double or 2535 // transformed to kClose if not, in which case kOpen is 2536 // cleared. 2537 constexpr int kQuote = (1 << 4); 2538 // Past ' closing quote. 2539 constexpr int kClose = (1 << 5); 2540 // Encountered # file/sheet separator. 2541 constexpr int kFileSep = (1 << 6); 2542 // Past . sheet name separator. 2543 constexpr int kPast = (1 << 7); 2544 // Marked name $$ follows sheet name separator, detected 2545 // while we're still on the separator. Will be cleared when 2546 // entering the name. 2547 constexpr int kMarkAhead = (1 << 8); 2548 // In marked defined name. 2549 constexpr int kDefName = (1 << 9); 2550 // Encountered # of #REF! 2551 constexpr int kRefErr = (1 << 10); 2552 2553 bool bAddToSymbol = true; 2554 if ((nMask & ScCharFlags::OdfRBracket) && !(nRefInName & kOpen)) 2555 { 2556 OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr), 2557 "ScCompiler::NextSymbol: reference: " 2558 "closing bracket ']' without prior sheet name separator '.' violates ODF spec"); 2559 // eaten, not added to pSym 2560 bAddToSymbol = false; 2561 eState = ssStop; 2562 } 2563 else if (cSheetSep == c && nRefInName == 0) 2564 { 2565 // eat it, no sheet name [.A1] 2566 bAddToSymbol = false; 2567 nRefInName |= kPast; 2568 if ('$' == pSrc[0] && '$' == pSrc[1]) 2569 nRefInName |= kMarkAhead; 2570 } 2571 else if (!(nRefInName & kPast) || (nRefInName & (kMarkAhead | kDefName))) 2572 { 2573 // Not in col/row yet. 2574 2575 if (SC_COMPILER_FILE_TAB_SEP == c && (nRefInName & kFileSep)) 2576 nRefInName = 0; 2577 else if ('$' == c && '$' == pSrc[0] && !(nRefInName & kOpen)) 2578 { 2579 nRefInName &= ~kMarkAhead; 2580 if (!(nRefInName & kDefName)) 2581 { 2582 // eaten, not added to pSym (2 chars) 2583 bAddToSymbol = false; 2584 ++pSrc; 2585 nRefInName &= kPast; 2586 nRefInName |= kDefName; 2587 } 2588 else 2589 { 2590 // ScAddress::Parse() will recognize this as 2591 // invalid later. 2592 if (eState != ssSkipReference) 2593 { 2594 *pSym++ = c; 2595 *pSym++ = *pSrc++; 2596 } 2597 bAddToSymbol = false; 2598 } 2599 } 2600 else if (cSheetPrefix == c && nRefInName == 0) 2601 nRefInName |= kDollar; 2602 else if ('\'' == c) 2603 { 2604 // TODO: The conventions' parseExternalName() 2605 // should handle quoted names, but as long as they 2606 // don't remove non-embedded quotes here. 2607 if (!(nRefInName & kName)) 2608 { 2609 nRefInName |= (kOpen | kName); 2610 bAddToSymbol = !(nRefInName & kDefName); 2611 } 2612 else if (!(nRefInName & kOpen)) 2613 { 2614 OSL_FAIL("ScCompiler::NextSymbol: reference: " 2615 "a ''' without the name being enclosed in '...' violates ODF spec"); 2616 } 2617 else if (nRefInName & kQuote) 2618 { 2619 // escaped embedded quote 2620 nRefInName &= ~kQuote; 2621 } 2622 else 2623 { 2624 switch (pSrc[0]) 2625 { 2626 case '\'': 2627 // escapes embedded quote 2628 nRefInName |= kQuote; 2629 break; 2630 case SC_COMPILER_FILE_TAB_SEP: 2631 // sheet name should follow 2632 nRefInName |= kFileSep; 2633 [[fallthrough]]; 2634 default: 2635 // quote not followed by quote => close 2636 nRefInName |= kClose; 2637 nRefInName &= ~kOpen; 2638 } 2639 bAddToSymbol = !(nRefInName & kDefName); 2640 } 2641 } 2642 else if ('#' == c && nRefInName == 0) 2643 nRefInName |= kRefErr; 2644 else if (cSheetSep == c && !(nRefInName & kOpen)) 2645 { 2646 // unquoted sheet name separator 2647 nRefInName |= kPast; 2648 if ('$' == pSrc[0] && '$' == pSrc[1]) 2649 nRefInName |= kMarkAhead; 2650 } 2651 else if (':' == c && !(nRefInName & kOpen)) 2652 { 2653 OSL_FAIL("ScCompiler::NextSymbol: reference: " 2654 "range operator ':' without prior sheet name separator '.' violates ODF spec"); 2655 nRefInName = 0; 2656 ++mnPredetectedReference; 2657 } 2658 else if (!(nRefInName & kName)) 2659 { 2660 // start unquoted name 2661 nRefInName |= kName; 2662 } 2663 } 2664 else if (':' == c) 2665 { 2666 // range operator 2667 nRefInName = 0; 2668 ++mnPredetectedReference; 2669 } 2670 if (bAddToSymbol && eState != ssSkipReference) 2671 *pSym++ = c; // everything is part of reference 2672 } 2673 break; 2674 case ssStop: 2675 ; // nothing, prevent warning 2676 break; 2677 } 2678 cLast = c; 2679 c = *pSrc; 2680 } 2681 if ( bi18n ) 2682 { 2683 const sal_Int32 nOldSrcPos = nSrcPos; 2684 nSrcPos = nSrcPos + nSpaces; 2685 // If group separator is not a possible operator and not one of any 2686 // separators then it may be parsed away in numbers. This is 2687 // specifically the case with NO-BREAK SPACE, which actually triggers 2688 // the bi18n case (which we don't want to include as yet another 2689 // special case above as it is rare enough and doesn't generally occur 2690 // in formulas). 2691 const sal_Unicode cGroupSep = ScGlobal::pLocaleData->getNumThousandSep()[0]; 2692 const bool bGroupSeparator = (128 <= cGroupSep && cGroupSep != cSep && 2693 cGroupSep != cArrayColSep && cGroupSep != cArrayRowSep && 2694 cGroupSep != cDecSep && cGroupSep != cDecSepAlt && 2695 cGroupSep != cSheetPrefix && cGroupSep != cSheetSep); 2696 OUStringBuffer aSymbol; 2697 mnRangeOpPosInSymbol = -1; 2698 FormulaError nErr = FormulaError::NONE; 2699 do 2700 { 2701 bi18n = false; 2702 // special case (e.g. $'sheetname' in OOO A1) 2703 if ( pStart[nSrcPos] == cSheetPrefix && pStart[nSrcPos+1] == '\'' ) 2704 aSymbol.append(pStart[nSrcPos++]); 2705 2706 ParseResult aRes = pConv->parseAnyToken( aFormula, nSrcPos, pCharClass, bGroupSeparator); 2707 2708 if ( !aRes.TokenType ) 2709 { 2710 nErr = FormulaError::IllegalChar; 2711 SetError( nErr ); // parsed chars as string 2712 } 2713 if ( aRes.EndPos <= nSrcPos ) 2714 { 2715 // Could not parse anything meaningful. 2716 assert(!aRes.TokenType); 2717 nErr = FormulaError::IllegalChar; 2718 SetError( nErr ); 2719 // Caller has to act on an empty symbol for 2720 // nSrcPos < aFormula.getLength() 2721 nSrcPos = nOldSrcPos; 2722 aSymbol.setLength(0); 2723 } 2724 else 2725 { 2726 // When having parsed a second reference part, ensure that the 2727 // i18n parser did not mistakingly parse a number that included 2728 // a separator which happened to be meant as a parameter 2729 // separator instead. 2730 if (mnRangeOpPosInSymbol >= 0 && (aRes.TokenType & KParseType::ASC_NUMBER)) 2731 { 2732 for (sal_Int32 i = nSrcPos; i < aRes.EndPos; ++i) 2733 { 2734 if (pStart[i] == cSep) 2735 aRes.EndPos = i; // also ends for 2736 } 2737 } 2738 aSymbol.append( pStart + nSrcPos, aRes.EndPos - nSrcPos); 2739 nSrcPos = aRes.EndPos; 2740 c = pStart[nSrcPos]; 2741 if ( aRes.TokenType & KParseType::SINGLE_QUOTE_NAME ) 2742 { // special cases (e.g. 'sheetname'. or 'filename'# in OOO A1) 2743 bi18n = (c == cSheetSep || c == SC_COMPILER_FILE_TAB_SEP); 2744 } 2745 // One range operator restarts parsing for second reference. 2746 if (c == ':' && mnRangeOpPosInSymbol < 0) 2747 { 2748 mnRangeOpPosInSymbol = aSymbol.getLength(); 2749 bi18n = true; 2750 } 2751 if ( bi18n ) 2752 aSymbol.append(pStart[nSrcPos++]); 2753 } 2754 } while ( bi18n && nErr == FormulaError::NONE ); 2755 sal_Int32 nLen = aSymbol.getLength(); 2756 if ( nLen > MAXSTRLEN ) 2757 { 2758 SetError( FormulaError::StringOverflow ); 2759 nLen = MAXSTRLEN; 2760 } 2761 if (mnRangeOpPosInSymbol >= nLen) 2762 mnRangeOpPosInSymbol = -1; 2763 lcl_UnicodeStrNCpy( cSymbol, aSymbol.getStr(), nLen ); 2764 pSym = &cSymbol[nLen]; 2765 } 2766 else 2767 { 2768 nSrcPos = pSrc - pStart; 2769 *pSym = 0; 2770 } 2771 if (mnRangeOpPosInSymbol >= 0 && mnRangeOpPosInSymbol == (pSym-1) - &cSymbol[0]) 2772 { 2773 // This is a trailing range operator, which is nonsense. Will be caught 2774 // in next round. 2775 mnRangeOpPosInSymbol = -1; 2776 *--pSym = 0; 2777 --nSrcPos; 2778 } 2779 if ( bAutoCorrect ) 2780 aCorrectedSymbol = OUString(cSymbol, pSym - cSymbol); 2781 if (bAutoIntersection && nSpaces > 1) 2782 --nSpaces; // replace '!!' with only one space 2783 return nSpaces; 2784 } 2785 2786 // Convert symbol to token 2787 2788 bool ScCompiler::IsOpCode( const OUString& rName, bool bInArray ) 2789 { 2790 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName)); 2791 bool bFound = (iLook != mxSymbols->getHashMap().end()); 2792 if (bFound) 2793 { 2794 OpCode eOp = iLook->second; 2795 if (bInArray) 2796 { 2797 if (rName == mxSymbols->getSymbol(ocArrayColSep)) 2798 eOp = ocArrayColSep; 2799 else if (rName == mxSymbols->getSymbol(ocArrayRowSep)) 2800 eOp = ocArrayRowSep; 2801 } 2802 else if (eOp == ocArrayColSep || eOp == ocArrayRowSep) 2803 { 2804 if (rName == mxSymbols->getSymbol(ocSep)) 2805 eOp = ocSep; 2806 else if (rName == ";") 2807 { 2808 switch (FormulaGrammar::extractFormulaLanguage( meGrammar)) 2809 { 2810 // Only for languages/grammars that actually use ';' 2811 // parameter separator. 2812 case css::sheet::FormulaLanguage::NATIVE: 2813 case css::sheet::FormulaLanguage::ENGLISH: 2814 case css::sheet::FormulaLanguage::ODFF: 2815 case css::sheet::FormulaLanguage::ODF_11: 2816 eOp = ocSep; 2817 } 2818 } 2819 } 2820 else if (eOp == ocCeil && mxSymbols->isOOXML()) 2821 { 2822 // Ensure that _xlfn.CEILING.MATH maps to ocCeil_Math. ocCeil is 2823 // unassigned for import. 2824 eOp = ocCeil_Math; 2825 } 2826 else if (eOp == ocFloor && mxSymbols->isOOXML()) 2827 { 2828 // Ensure that _xlfn.FLOOR.MATH maps to ocFloor_Math. ocFloor is 2829 // unassigned for import. 2830 eOp = ocFloor_Math; 2831 } 2832 maRawToken.SetOpCode(eOp); 2833 } 2834 else if (mxSymbols->isODFF()) 2835 { 2836 // ODFF names that are not written in the current mapping but to be 2837 // recognized. New names will be written in a future release, then 2838 // exchange (!) with the names in 2839 // formula/source/core/resource/core_resource.src to be able to still 2840 // read the old names as well. 2841 struct FunctionName 2842 { 2843 const char* pName; 2844 OpCode eOp; 2845 }; 2846 static const FunctionName aOdffAliases[] = { 2847 // Renamed old names, still accept them: 2848 { "B", ocB }, // B -> BINOM.DIST.RANGE 2849 { "TDIST", ocTDist }, // TDIST -> LEGACY.TDIST 2850 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> ORG.OPENOFFICE.EASTERSUNDAY 2851 { "ZGZ", ocRRI }, // ZGZ -> RRI 2852 { "COLOR", ocColor }, // COLOR -> ORG.LIBREOFFICE.COLOR 2853 { "GOALSEEK", ocBackSolver }, // GOALSEEK -> ORG.OPENOFFICE.GOALSEEK 2854 { "COM.MICROSOFT.F.DIST", ocFDist_LT }, // fdo#40835, -> FDIST -> COM.MICROSOFT.F.DIST 2855 { "COM.MICROSOFT.F.INV", ocFInv_LT } // tdf#94214, COM.MICROSOFT.F.INV -> FINV (ODF) 2856 // Renamed new names, prepare to read future names: 2857 //{ "ORG.OPENOFFICE.XXX", ocXXX } // XXX -> ORG.OPENOFFICE.XXX 2858 }; 2859 for (const FunctionName& rOdffAlias : aOdffAliases) 2860 { 2861 if (rName.equalsIgnoreAsciiCaseAscii( rOdffAlias.pName)) 2862 { 2863 maRawToken.SetOpCode( rOdffAlias.eOp); 2864 bFound = true; 2865 break; // for 2866 } 2867 } 2868 } 2869 else if (mxSymbols->isOOXML()) 2870 { 2871 // OOXML names that are not written in the current mapping but to be 2872 // recognized as old versions wrote them. 2873 struct FunctionName 2874 { 2875 const char* pName; 2876 OpCode eOp; 2877 }; 2878 static const FunctionName aOoxmlAliases[] = { 2879 { "EFFECTIVE", ocEffect }, // EFFECTIVE -> EFFECT 2880 { "ERRORTYPE", ocErrorType }, // ERRORTYPE -> _xlfn.ORG.OPENOFFICE.ERRORTYPE 2881 { "MULTIRANGE", ocMultiArea }, // MULTIRANGE -> _xlfn.ORG.OPENOFFICE.MULTIRANGE 2882 { "GOALSEEK", ocBackSolver }, // GOALSEEK -> _xlfn.ORG.OPENOFFICE.GOALSEEK 2883 { "EASTERSUNDAY", ocEasterSunday }, // EASTERSUNDAY -> _xlfn.ORG.OPENOFFICE.EASTERSUNDAY 2884 { "CURRENT", ocCurrent }, // CURRENT -> _xlfn.ORG.OPENOFFICE.CURRENT 2885 { "STYLE", ocStyle } // STYLE -> _xlfn.ORG.OPENOFFICE.STYLE 2886 }; 2887 for (const FunctionName& rOoxmlAlias : aOoxmlAliases) 2888 { 2889 if (rName.equalsIgnoreAsciiCaseAscii( rOoxmlAlias.pName)) 2890 { 2891 maRawToken.SetOpCode( rOoxmlAlias.eOp); 2892 bFound = true; 2893 break; // for 2894 } 2895 } 2896 } 2897 else if (mxSymbols->isPODF()) 2898 { 2899 // PODF names are ODF 1.0/1.1 and also used in API XFunctionAccess. 2900 // We can't rename them in 2901 // formula/source/core/resource/core_resource.src but can add 2902 // additional names to be recognized here so they match the UI names if 2903 // those are renamed. 2904 struct FunctionName 2905 { 2906 const char* pName; 2907 OpCode eOp; 2908 }; 2909 static const FunctionName aPodfAliases[] = { 2910 { "EFFECT", ocEffect } // EFFECTIVE -> EFFECT 2911 }; 2912 for (const FunctionName& rPodfAlias : aPodfAliases) 2913 { 2914 if (rName.equalsIgnoreAsciiCaseAscii( rPodfAlias.pName)) 2915 { 2916 maRawToken.SetOpCode( rPodfAlias.eOp); 2917 bFound = true; 2918 break; // for 2919 } 2920 } 2921 } 2922 2923 if (!bFound) 2924 { 2925 OUString aIntName; 2926 if (mxSymbols->hasExternals()) 2927 { 2928 // If symbols are set by filters get mapping to exact name. 2929 ExternalHashMap::const_iterator iExt( 2930 mxSymbols->getExternalHashMap().find( rName)); 2931 if (iExt != mxSymbols->getExternalHashMap().end()) 2932 { 2933 if (ScGlobal::GetAddInCollection()->GetFuncData( (*iExt).second)) 2934 aIntName = (*iExt).second; 2935 } 2936 if (aIntName.isEmpty()) 2937 { 2938 // If that isn't found we might continue with rName lookup as a 2939 // last resort by just falling through to FindFunction(), but 2940 // it shouldn't happen if the map was setup correctly. Don't 2941 // waste time and bail out. 2942 return false; 2943 } 2944 } 2945 if (aIntName.isEmpty()) 2946 { 2947 // Old (deprecated) addins first for legacy. 2948 if (ScGlobal::GetLegacyFuncCollection()->findByName(cSymbol)) 2949 { 2950 maRawToken.SetExternal( cSymbol ); 2951 } 2952 else 2953 // bLocalFirst=false for (English) upper full original name 2954 // (service.function) 2955 aIntName = ScGlobal::GetAddInCollection()->FindFunction( 2956 rName, !mxSymbols->isEnglish()); 2957 } 2958 if (!aIntName.isEmpty()) 2959 { 2960 maRawToken.SetExternal( aIntName ); // international name 2961 bFound = true; 2962 } 2963 } 2964 OpCode eOp; 2965 if (bFound && ((eOp = maRawToken.GetOpCode()) == ocSub || eOp == ocNegSub)) 2966 { 2967 bool bShouldBeNegSub = 2968 (eLastOp == ocOpen || eLastOp == ocSep || eLastOp == ocNegSub || 2969 (SC_OPCODE_START_BIN_OP <= eLastOp && eLastOp < SC_OPCODE_STOP_BIN_OP) || 2970 eLastOp == ocArrayOpen || 2971 eLastOp == ocArrayColSep || eLastOp == ocArrayRowSep); 2972 if (bShouldBeNegSub && eOp == ocSub) 2973 maRawToken.NewOpCode( ocNegSub ); 2974 //TODO: if ocNegSub had ForceArray we'd have to set it here 2975 else if (!bShouldBeNegSub && eOp == ocNegSub) 2976 maRawToken.NewOpCode( ocSub ); 2977 } 2978 return bFound; 2979 } 2980 2981 bool ScCompiler::IsOpCode2( const OUString& rName ) 2982 { 2983 bool bFound = false; 2984 sal_uInt16 i; 2985 2986 for( i = ocInternalBegin; i <= ocInternalEnd && !bFound; i++ ) 2987 bFound = rName.equalsAscii( pInternal[ i-ocInternalBegin ] ); 2988 2989 if (bFound) 2990 { 2991 maRawToken.SetOpCode( static_cast<OpCode>(--i) ); 2992 } 2993 return bFound; 2994 } 2995 2996 static bool lcl_ParenthesisFollows( const sal_Unicode* p ) 2997 { 2998 while (*p == ' ') 2999 p++; 3000 return *p == '('; 3001 } 3002 3003 bool ScCompiler::IsValue( const OUString& rSym ) 3004 { 3005 const sal_Int32 nFormulaLanguage = FormulaGrammar::extractFormulaLanguage( GetGrammar()); 3006 if (nFormulaLanguage == css::sheet::FormulaLanguage::ODFF || nFormulaLanguage == css::sheet::FormulaLanguage::OOXML) 3007 { 3008 // Speedup things for ODFF, only well-formed numbers, not locale 3009 // dependent nor user input. 3010 rtl_math_ConversionStatus eStatus; 3011 sal_Int32 nParseEnd; 3012 double fVal = rtl::math::stringToDouble( rSym, '.', 0, &eStatus, &nParseEnd); 3013 if (nParseEnd != rSym.getLength()) 3014 { 3015 // Not (only) a number. 3016 3017 if (nParseEnd > 0) 3018 return false; // partially a number => no such thing 3019 3020 if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos)) 3021 return false; // some function name, not a constant 3022 3023 // Could be TRUE or FALSE constant. 3024 if (rSym.equalsIgnoreAsciiCase("TRUE")) 3025 { 3026 maRawToken.SetDouble( 1.0 ); 3027 return true; 3028 } 3029 if (rSym.equalsIgnoreAsciiCase("FALSE")) 3030 { 3031 maRawToken.SetDouble( 0.0 ); 3032 return true; 3033 } 3034 return false; 3035 } 3036 if (eStatus == rtl_math_ConversionStatus_OutOfRange) 3037 SetError( FormulaError::IllegalArgument ); 3038 maRawToken.SetDouble( fVal ); 3039 return true; 3040 } 3041 3042 double fVal; 3043 sal_uInt32 nIndex = mxSymbols->isEnglish() ? mpFormatter->GetStandardIndex(LANGUAGE_ENGLISH_US) : 0; 3044 3045 if (!mpFormatter->IsNumberFormat(rSym, nIndex, fVal)) 3046 return false; 3047 3048 SvNumFormatType nType = mpFormatter->GetType(nIndex); 3049 3050 // Don't accept 3:3 as time, it is a reference to entire row 3 instead. 3051 // Dates should never be entered directly and automatically converted 3052 // to serial, because the serial would be wrong if null-date changed. 3053 // Usually it wouldn't be accepted anyway because the date separator 3054 // clashed with other separators or operators. 3055 if (nType & (SvNumFormatType::TIME | SvNumFormatType::DATE)) 3056 return false; 3057 3058 if (nType == SvNumFormatType::LOGICAL) 3059 { 3060 if (lcl_ParenthesisFollows( aFormula.getStr() + nSrcPos)) 3061 return false; // Boolean function instead. 3062 } 3063 3064 if( nType == SvNumFormatType::TEXT ) 3065 // HACK: number too big! 3066 SetError( FormulaError::IllegalArgument ); 3067 maRawToken.SetDouble( fVal ); 3068 return true; 3069 } 3070 3071 bool ScCompiler::IsString() 3072 { 3073 if ( cSymbol[0] != '"' ) 3074 return false; 3075 const sal_Unicode* p = cSymbol+1; 3076 while ( *p ) 3077 p++; 3078 sal_Int32 nLen = sal::static_int_cast<sal_Int32>( p - cSymbol - 1 ); 3079 if (!nLen || cSymbol[nLen] != '"') 3080 return false; 3081 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(OUString(cSymbol+1, nLen-1)); 3082 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase()); 3083 return true; 3084 } 3085 3086 bool ScCompiler::IsPredetectedErrRefReference( const OUString& rName, const OUString* pErrRef ) 3087 { 3088 switch (mnPredetectedReference) 3089 { 3090 case 1: 3091 return IsSingleReference( rName, pErrRef); 3092 case 2: 3093 return IsDoubleReference( rName, pErrRef); 3094 default: 3095 return false; 3096 } 3097 } 3098 3099 bool ScCompiler::IsPredetectedReference( const OUString& rName ) 3100 { 3101 // Speedup documents with lots of broken references, e.g. sheet deleted. 3102 // It could also be a broken invalidated reference that contains #REF! 3103 // (but is not equal to), which we wrote prior to ODFF and also to ODFF 3104 // between 2013 and 2016 until 5.1.4 3105 const OUString aErrRef("#REF!"); // not localized in ODFF 3106 sal_Int32 nPos = rName.indexOf( aErrRef); 3107 if (nPos != -1) 3108 { 3109 /* TODO: this may be enhanced by reusing scan information from 3110 * NextSymbol(), the positions of quotes and special characters found 3111 * there for $'sheet'.A1:... could be stored in a vector. We don't 3112 * fully rescan here whether found positions are within single quotes 3113 * for performance reasons. This code does not check for possible 3114 * occurrences of insane "valid" sheet names like 3115 * 'haha.#REF!1fooledyou' and will generate an error on such. */ 3116 if (nPos == 0) 3117 { 3118 // Per ODFF the correct string for a reference error is just #REF!, 3119 // so pass it on. 3120 if (rName.getLength() == 5) 3121 return IsErrorConstant( rName); 3122 // #REF!.AB42 or #REF!42 or #REF!#REF! 3123 return IsPredetectedErrRefReference( rName, &aErrRef); 3124 } 3125 sal_Unicode c = rName[nPos-1]; // before #REF! 3126 if ('$' == c) 3127 { 3128 if (nPos == 1) 3129 { 3130 // $#REF!.AB42 or $#REF!42 or $#REF!#REF! 3131 return IsPredetectedErrRefReference( rName, &aErrRef); 3132 } 3133 c = rName[nPos-2]; // before $#REF! 3134 } 3135 sal_Unicode c2 = nPos+5 < rName.getLength() ? rName[nPos+5] : 0; // after #REF! 3136 switch (c) 3137 { 3138 case '.': 3139 if ('$' == c2 || '#' == c2 || ('0' <= c2 && c2 <= '9')) 3140 { 3141 // sheet.#REF!42 or sheet.#REF!#REF! 3142 return IsPredetectedErrRefReference( rName, &aErrRef); 3143 } 3144 break; 3145 case ':': 3146 if (mnPredetectedReference > 1 && 3147 ('.' == c2 || '$' == c2 || '#' == c2 || 3148 ('0' <= c2 && c2 <= '9'))) 3149 { 3150 // :#REF!.AB42 or :#REF!42 or :#REF!#REF! 3151 return IsPredetectedErrRefReference( rName, &aErrRef); 3152 } 3153 break; 3154 default: 3155 if (rtl::isAsciiAlpha(c) && 3156 ((mnPredetectedReference > 1 && ':' == c2) || 0 == c2)) 3157 { 3158 // AB#REF!: or AB#REF! 3159 return IsPredetectedErrRefReference( rName, &aErrRef); 3160 } 3161 } 3162 } 3163 switch (mnPredetectedReference) 3164 { 3165 case 1: 3166 return IsSingleReference( rName); 3167 case 2: 3168 return IsDoubleReference( rName); 3169 } 3170 return false; 3171 } 3172 3173 bool ScCompiler::IsDoubleReference( const OUString& rName, const OUString* pErrRef ) 3174 { 3175 ScRange aRange( aPos, aPos ); 3176 const ScAddress::Details aDetails( pConv->meConv, aPos ); 3177 ScAddress::ExternalInfo aExtInfo; 3178 ScRefFlags nFlags = aRange.Parse( rName, pDoc, aDetails, &aExtInfo, &maExternalLinks, pErrRef ); 3179 if( nFlags & ScRefFlags::VALID ) 3180 { 3181 ScComplexRefData aRef; 3182 aRef.InitRange( aRange ); 3183 aRef.Ref1.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO ); 3184 aRef.Ref1.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO ); 3185 aRef.Ref1.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO ); 3186 if ( !(nFlags & ScRefFlags::TAB_VALID) ) 3187 aRef.Ref1.SetTabDeleted( true ); // #REF! 3188 aRef.Ref1.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO ); 3189 aRef.Ref2.SetColRel( (nFlags & ScRefFlags::COL2_ABS) == ScRefFlags::ZERO ); 3190 aRef.Ref2.SetRowRel( (nFlags & ScRefFlags::ROW2_ABS) == ScRefFlags::ZERO ); 3191 aRef.Ref2.SetTabRel( (nFlags & ScRefFlags::TAB2_ABS) == ScRefFlags::ZERO ); 3192 if ( !(nFlags & ScRefFlags::TAB2_VALID) ) 3193 aRef.Ref2.SetTabDeleted( true ); // #REF! 3194 aRef.Ref2.SetFlag3D( ( nFlags & ScRefFlags::TAB2_3D ) != ScRefFlags::ZERO ); 3195 aRef.SetRange(pDoc->GetSheetLimits(), aRange, aPos); 3196 if (aExtInfo.mbExternal) 3197 { 3198 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 3199 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName); 3200 maRawToken.SetExternalDoubleRef( 3201 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef); 3202 maExternalFiles.push_back(aExtInfo.mnFileId); 3203 } 3204 else 3205 { 3206 maRawToken.SetDoubleReference(aRef); 3207 } 3208 } 3209 3210 return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO; 3211 } 3212 3213 bool ScCompiler::IsSingleReference( const OUString& rName, const OUString* pErrRef ) 3214 { 3215 mnCurrentSheetEndPos = 0; 3216 mnCurrentSheetTab = -1; 3217 ScAddress aAddr( aPos ); 3218 const ScAddress::Details aDetails( pConv->meConv, aPos ); 3219 ScAddress::ExternalInfo aExtInfo; 3220 ScRefFlags nFlags = aAddr.Parse( rName, pDoc, aDetails, 3221 &aExtInfo, &maExternalLinks, &mnCurrentSheetEndPos, pErrRef); 3222 // Something must be valid in order to recognize Sheet1.blah or blah.a1 3223 // as a (wrong) reference. 3224 if( nFlags & ( ScRefFlags::COL_VALID|ScRefFlags::ROW_VALID|ScRefFlags::TAB_VALID ) ) 3225 { 3226 // Valid given tab and invalid col or row may indicate a sheet-local 3227 // named expression, bail out early and don't create a reference token. 3228 if (!(nFlags & ScRefFlags::VALID) && mnCurrentSheetEndPos > 0 && 3229 (nFlags & ScRefFlags::TAB_VALID) && (nFlags & ScRefFlags::TAB_3D)) 3230 { 3231 if (aExtInfo.mbExternal) 3232 { 3233 // External names are handled separately. 3234 mnCurrentSheetEndPos = 0; 3235 mnCurrentSheetTab = -1; 3236 } 3237 else 3238 { 3239 mnCurrentSheetTab = aAddr.Tab(); 3240 } 3241 return false; 3242 } 3243 3244 ScSingleRefData aRef; 3245 aRef.InitAddress( aAddr ); 3246 aRef.SetColRel( (nFlags & ScRefFlags::COL_ABS) == ScRefFlags::ZERO ); 3247 aRef.SetRowRel( (nFlags & ScRefFlags::ROW_ABS) == ScRefFlags::ZERO ); 3248 aRef.SetTabRel( (nFlags & ScRefFlags::TAB_ABS) == ScRefFlags::ZERO ); 3249 aRef.SetFlag3D( ( nFlags & ScRefFlags::TAB_3D ) != ScRefFlags::ZERO ); 3250 // the reference is really invalid 3251 if( !( nFlags & ScRefFlags::VALID ) ) 3252 { 3253 if( !( nFlags & ScRefFlags::COL_VALID ) ) 3254 aRef.SetColDeleted(true); 3255 if( !( nFlags & ScRefFlags::ROW_VALID ) ) 3256 aRef.SetRowDeleted(true); 3257 if( !( nFlags & ScRefFlags::TAB_VALID ) ) 3258 aRef.SetTabDeleted(true); 3259 nFlags |= ScRefFlags::VALID; 3260 } 3261 aRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos); 3262 3263 if (aExtInfo.mbExternal) 3264 { 3265 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 3266 const OUString* pRealTab = pRefMgr->getRealTableName(aExtInfo.mnFileId, aExtInfo.maTabName); 3267 maRawToken.SetExternalSingleRef( 3268 aExtInfo.mnFileId, pRealTab ? *pRealTab : aExtInfo.maTabName, aRef); 3269 maExternalFiles.push_back(aExtInfo.mnFileId); 3270 } 3271 else 3272 maRawToken.SetSingleReference(aRef); 3273 } 3274 3275 return ( nFlags & ScRefFlags::VALID ) != ScRefFlags::ZERO; 3276 } 3277 3278 bool ScCompiler::IsReference( const OUString& rName, const OUString* pErrRef ) 3279 { 3280 // Has to be called before IsValue 3281 sal_Unicode ch1 = rName[0]; 3282 sal_Unicode cDecSep = ( mxSymbols->isEnglish() ? '.' : ScGlobal::pLocaleData->getNumDecimalSep()[0] ); 3283 if ( ch1 == cDecSep ) 3284 return false; 3285 // Code further down checks only if cDecSep=='.' so simply obtaining the 3286 // alternative decimal separator if it's not is sufficient. 3287 if (cDecSep != '.') 3288 { 3289 cDecSep = ScGlobal::pLocaleData->getNumDecimalSepAlt().toChar(); 3290 if ( ch1 == cDecSep ) 3291 return false; 3292 } 3293 // Who was that imbecile introducing '.' as the sheet name separator!?! 3294 if ( rtl::isAsciiDigit( ch1 ) && pConv->getSpecialSymbol( Convention::SHEET_SEPARATOR) == '.' ) 3295 { 3296 // Numerical sheet name is valid. 3297 // But English 1.E2 or 1.E+2 is value 100, 1.E-2 is 0.01 3298 // Don't create a #REF! of values. But also do not bail out on 3299 // something like 3:3, meaning entire row 3. 3300 do 3301 { 3302 const sal_Int32 nPos = ScGlobal::FindUnquoted( rName, '.'); 3303 if ( nPos == -1 ) 3304 { 3305 if (ScGlobal::FindUnquoted( rName, ':') != -1) 3306 break; // may be 3:3, continue as usual 3307 return false; 3308 } 3309 sal_Unicode const * const pTabSep = rName.getStr() + nPos; 3310 sal_Unicode ch2 = pTabSep[1]; // maybe a column identifier 3311 if ( !(ch2 == '$' || rtl::isAsciiAlpha( ch2 )) ) 3312 return false; 3313 if ( cDecSep == '.' && (ch2 == 'E' || ch2 == 'e') // E + - digit 3314 && (GetCharTableFlags( pTabSep[2], pTabSep[1] ) & ScCharFlags::ValueExp) ) 3315 { 3316 // If it is an 1.E2 expression check if "1" is an existent sheet 3317 // name. If so, a desired value 1.E2 would have to be entered as 3318 // 1E2 or 1.0E2 or 1.E+2, sorry. Another possibility would be to 3319 // require numerical sheet names always being entered quoted, which 3320 // is not desirable (too many 1999, 2000, 2001 sheets in use). 3321 // Furthermore, XML files created with versions prior to SRC640e 3322 // wouldn't contain the quotes added by MakeTabStr()/CheckTabQuotes() 3323 // and would produce wrong formulas if the conditions here are met. 3324 // If you can live with these restrictions you may remove the 3325 // check and return an unconditional FALSE. 3326 OUString aTabName( rName.copy( 0, nPos ) ); 3327 SCTAB nTab; 3328 if ( !pDoc->GetTable( aTabName, nTab ) ) 3329 return false; 3330 // If sheet "1" exists and the expression is 1.E+2 continue as 3331 // usual, the ScRange/ScAddress parser will take care of it. 3332 } 3333 } while(false); 3334 } 3335 3336 if (IsSingleReference( rName, pErrRef)) 3337 return true; 3338 3339 // Though the range operator is handled explicitly, when encountering 3340 // something like Sheet1.A:A we will have to treat it as one entity if it 3341 // doesn't pass as single cell reference. 3342 if (mnRangeOpPosInSymbol > 0) // ":foo" would be nonsense 3343 { 3344 if (IsDoubleReference( rName, pErrRef)) 3345 return true; 3346 // Now try with a symbol up to the range operator, rewind source 3347 // position. 3348 assert(mnRangeOpPosInSymbol < MAXSTRLEN); // We should have caught the maldoers. 3349 if (mnRangeOpPosInSymbol >= MAXSTRLEN) // TODO: this check and return 3350 return false; // can be removed when sure. 3351 sal_Int32 nLen = mnRangeOpPosInSymbol; 3352 while (cSymbol[++nLen]) 3353 ; 3354 cSymbol[mnRangeOpPosInSymbol] = 0; 3355 nSrcPos -= (nLen - mnRangeOpPosInSymbol); 3356 mnRangeOpPosInSymbol = -1; 3357 mbRewind = true; 3358 return true; // end all checks 3359 } 3360 else 3361 { 3362 switch (pConv->meConv) 3363 { 3364 case FormulaGrammar::CONV_XL_A1: 3365 case FormulaGrammar::CONV_XL_OOX: 3366 // Special treatment for the 'E:\[doc]Sheet1:Sheet3'!D5 Excel 3367 // sickness, mnRangeOpPosInSymbol did not catch the range 3368 // operator as it is within a quoted name. 3369 if (rName[0] != '\'') 3370 return false; // Document name has to be single quoted. 3371 [[fallthrough]]; 3372 case FormulaGrammar::CONV_XL_R1C1: 3373 // C2 or C[1] are valid entire column references. 3374 if (IsDoubleReference( rName, pErrRef)) 3375 return true; 3376 break; 3377 default: 3378 ; // nothing 3379 } 3380 } 3381 return false; 3382 } 3383 3384 bool ScCompiler::IsMacro( const OUString& rName ) 3385 { 3386 #if !HAVE_FEATURE_SCRIPTING 3387 (void) rName; 3388 3389 return false; 3390 #else 3391 3392 // Calling SfxObjectShell::GetBasic() may result in all sort of things 3393 // including obtaining the model and deep down in 3394 // SfxBaseModel::getDocumentStorage() acquiring the SolarMutex, which when 3395 // formulas are compiled from a threaded import may result in a deadlock. 3396 // Check first if we actually could acquire it and if not bail out. 3397 /* FIXME: yes, but how ... */ 3398 vcl::SolarMutexTryAndBuyGuard g; 3399 if (!g.isAcquired()) 3400 { 3401 SAL_WARN( "sc.core", "ScCompiler::IsMacro - SolarMutex would deadlock, not obtaining Basic"); 3402 return false; // bad luck 3403 } 3404 3405 OUString aName( rName); 3406 StarBASIC* pObj = nullptr; 3407 SfxObjectShell* pDocSh = pDoc->GetDocumentShell(); 3408 3409 try 3410 { 3411 if( pDocSh )//XXX 3412 pObj = pDocSh->GetBasic(); 3413 else 3414 pObj = SfxApplication::GetBasic(); 3415 } 3416 catch (...) 3417 { 3418 return false; 3419 } 3420 3421 if (!pObj) 3422 return false; 3423 3424 // ODFF recommends to store user-defined functions prefixed with "USER.", 3425 // use only unprefixed name if encountered. BASIC doesn't allow '.' in a 3426 // function name so a function "USER.FOO" could not exist, and macro check 3427 // is assigned the lowest priority in function name check. 3428 if (FormulaGrammar::isODFF( GetGrammar()) && aName.startsWithIgnoreAsciiCase("USER.")) 3429 aName = aName.copy(5); 3430 3431 SbxMethod* pMeth = static_cast<SbxMethod*>(pObj->Find( aName, SbxClassType::Method )); 3432 if( !pMeth ) 3433 { 3434 return false; 3435 } 3436 // It really should be a BASIC function! 3437 if( pMeth->GetType() == SbxVOID 3438 || ( pMeth->IsFixed() && pMeth->GetType() == SbxEMPTY ) 3439 || dynamic_cast<const SbMethod*>( pMeth) == nullptr ) 3440 { 3441 return false; 3442 } 3443 maRawToken.SetExternal( aName ); 3444 maRawToken.eOp = ocMacro; 3445 return true; 3446 #endif 3447 } 3448 3449 bool ScCompiler::IsNamedRange( const OUString& rUpperName ) 3450 { 3451 // IsNamedRange is called only from NextNewToken, with an upper-case string 3452 3453 // try local names first 3454 sal_Int16 nSheet = aPos.Tab(); 3455 ScRangeName* pRangeName = pDoc->GetRangeName(nSheet); 3456 const ScRangeData* pData = nullptr; 3457 if (pRangeName) 3458 pData = pRangeName->findByUpperName(rUpperName); 3459 if (!pData) 3460 { 3461 pRangeName = pDoc->GetRangeName(); 3462 if (pRangeName) 3463 pData = pRangeName->findByUpperName(rUpperName); 3464 if (pData) 3465 nSheet = -1; 3466 } 3467 3468 if (pData) 3469 { 3470 maRawToken.SetName( nSheet, pData->GetIndex()); 3471 return true; 3472 } 3473 3474 // Sheet-local name with sheet specified. 3475 if (mnCurrentSheetEndPos > 0 && mnCurrentSheetTab >= 0) 3476 { 3477 OUString aName( rUpperName.copy( mnCurrentSheetEndPos)); 3478 pRangeName = pDoc->GetRangeName( mnCurrentSheetTab); 3479 if (pRangeName) 3480 { 3481 pData = pRangeName->findByUpperName(aName); 3482 if (pData) 3483 { 3484 maRawToken.SetName( mnCurrentSheetTab, pData->GetIndex()); 3485 return true; 3486 } 3487 } 3488 } 3489 3490 return false; 3491 } 3492 3493 bool ScCompiler::IsExternalNamedRange( const OUString& rSymbol, bool& rbInvalidExternalNameRange ) 3494 { 3495 /* FIXME: This code currently (2008-12-02T15:41+0100 in CWS mooxlsc) 3496 * correctly parses external named references in OOo, as required per RFE 3497 * #i3740#, just that we can't store them in ODF yet. We will need an OASIS 3498 * spec first. Until then don't pretend to support external names that 3499 * wouldn't survive a save and reload cycle, return false instead. */ 3500 3501 rbInvalidExternalNameRange = false; 3502 3503 if (!pConv) 3504 return false; 3505 3506 OUString aFile, aName; 3507 if (!pConv->parseExternalName( rSymbol, aFile, aName, pDoc, &maExternalLinks)) 3508 return false; 3509 3510 if (aFile.getLength() > MAXSTRLEN || aName.getLength() > MAXSTRLEN) 3511 return false; 3512 3513 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 3514 OUString aTmp = aFile; 3515 pRefMgr->convertToAbsName(aTmp); 3516 aFile = aTmp; 3517 sal_uInt16 nFileId = pRefMgr->getExternalFileId(aFile); 3518 if (!pRefMgr->isValidRangeName(nFileId, aName)) 3519 { 3520 rbInvalidExternalNameRange = true; 3521 // range name doesn't exist in the source document. 3522 return false; 3523 } 3524 3525 const OUString* pRealName = pRefMgr->getRealRangeName(nFileId, aName); 3526 maRawToken.SetExternalName(nFileId, pRealName ? *pRealName : aTmp); 3527 maExternalFiles.push_back(nFileId); 3528 return true; 3529 } 3530 3531 bool ScCompiler::IsDBRange( const OUString& rName ) 3532 { 3533 ScDBCollection::NamedDBs& rDBs = pDoc->GetDBCollection()->getNamedDBs(); 3534 const ScDBData* p = rDBs.findByUpperName(rName); 3535 if (!p) 3536 return false; 3537 3538 maRawToken.SetName( -1, p->GetIndex()); // DB range is always global. 3539 maRawToken.eOp = ocDBArea; 3540 return true; 3541 } 3542 3543 bool ScCompiler::IsColRowName( const OUString& rName ) 3544 { 3545 bool bInList = false; 3546 bool bFound = false; 3547 ScSingleRefData aRef; 3548 OUString aName( rName ); 3549 DeQuote( aName ); 3550 SCTAB nThisTab = aPos.Tab(); 3551 for ( short jThisTab = 1; jThisTab >= 0 && !bInList; jThisTab-- ) 3552 { // first check ranges on this sheet, in case of duplicated names 3553 for ( short jRow=0; jRow<2 && !bInList; jRow++ ) 3554 { 3555 ScRangePairList* pRL; 3556 if ( !jRow ) 3557 pRL = pDoc->GetColNameRanges(); 3558 else 3559 pRL = pDoc->GetRowNameRanges(); 3560 for ( size_t iPair = 0, nPairs = pRL->size(); iPair < nPairs && !bInList; ++iPair ) 3561 { 3562 const ScRangePair & rR = (*pRL)[iPair]; 3563 const ScRange& rNameRange = rR.GetRange(0); 3564 if ( jThisTab && !(rNameRange.aStart.Tab() <= nThisTab && 3565 nThisTab <= rNameRange.aEnd.Tab()) ) 3566 continue; // for 3567 ScCellIterator aIter( pDoc, rNameRange ); 3568 for (bool bHas = aIter.first(); bHas && !bInList; bHas = aIter.next()) 3569 { 3570 // Don't crash if cell (via CompileNameFormula) encounters 3571 // a formula cell without code and 3572 // HasStringData/Interpret/Compile is executed and all that 3573 // recursively... 3574 // Furthermore, *this* cell won't be touched, since no RPN exists yet. 3575 CellType eType = aIter.getType(); 3576 bool bOk = false; 3577 if (eType == CELLTYPE_FORMULA) 3578 { 3579 ScFormulaCell* pFC = aIter.getFormulaCell(); 3580 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos); 3581 } 3582 else 3583 bOk = true; 3584 3585 if (bOk && aIter.hasString()) 3586 { 3587 OUString aStr = aIter.getString(); 3588 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) ) 3589 { 3590 aRef.InitFlags(); 3591 if ( !jRow ) 3592 aRef.SetColRel( true ); // ColName 3593 else 3594 aRef.SetRowRel( true ); // RowName 3595 aRef.SetAddress(pDoc->GetSheetLimits(), aIter.GetPos(), aPos); 3596 bInList = bFound = true; 3597 } 3598 } 3599 } 3600 } 3601 } 3602 } 3603 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() ) 3604 { // search in current sheet 3605 long nDistance = 0, nMax = 0; 3606 long nMyCol = static_cast<long>(aPos.Col()); 3607 long nMyRow = static_cast<long>(aPos.Row()); 3608 bool bTwo = false; 3609 ScAddress aOne( 0, 0, aPos.Tab() ); 3610 ScAddress aTwo( pDoc->MaxCol(), pDoc->MaxRow(), aPos.Tab() ); 3611 3612 ScAutoNameCache* pNameCache = pDoc->GetAutoNameCache(); 3613 if ( pNameCache ) 3614 { 3615 // use GetNameOccurrences to collect all positions of aName on the sheet 3616 // (only once), similar to the outer part of the loop in the "else" branch. 3617 3618 const ScAutoNameAddresses& rAddresses = pNameCache->GetNameOccurrences( aName, aPos.Tab() ); 3619 3620 // Loop through the found positions, similar to the inner part of the loop in the "else" branch. 3621 // The order of addresses in the vector is the same as from ScCellIterator. 3622 3623 for ( const ScAddress& aAddress : rAddresses ) 3624 { 3625 if ( bFound ) 3626 { // stop if everything else is further away 3627 if ( nMax < static_cast<long>(aAddress.Col()) ) 3628 break; // aIter 3629 } 3630 if ( aAddress != aPos ) 3631 { 3632 // same treatment as in isEqual case below 3633 3634 SCCOL nCol = aAddress.Col(); 3635 SCROW nRow = aAddress.Row(); 3636 long nC = nMyCol - nCol; 3637 long nR = nMyRow - nRow; 3638 if ( bFound ) 3639 { 3640 long nD = nC * nC + nR * nR; 3641 if ( nD < nDistance ) 3642 { 3643 if ( nC < 0 || nR < 0 ) 3644 { // right or below 3645 bTwo = true; 3646 aTwo.Set( nCol, nRow, aAddress.Tab() ); 3647 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) ); 3648 nDistance = nD; 3649 } 3650 else if ( !(nRow < aOne.Row() && nMyRow >= static_cast<long>(aOne.Row())) ) 3651 { 3652 // upper left, only if not further up than the 3653 // current entry and nMyRow is below (CellIter 3654 // runs column-wise) 3655 bTwo = false; 3656 aOne.Set( nCol, nRow, aAddress.Tab() ); 3657 nMax = std::max( nMyCol + nC, nMyRow + nR ); 3658 nDistance = nD; 3659 } 3660 } 3661 } 3662 else 3663 { 3664 aOne.Set( nCol, nRow, aAddress.Tab() ); 3665 nDistance = nC * nC + nR * nR; 3666 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) ); 3667 3668 } 3669 bFound = true; 3670 } 3671 } 3672 } 3673 else 3674 { 3675 ScCellIterator aIter( pDoc, ScRange( aOne, aTwo ) ); 3676 for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) 3677 { 3678 if ( bFound ) 3679 { // stop if everything else is further away 3680 if ( nMax < static_cast<long>(aIter.GetPos().Col()) ) 3681 break; // aIter 3682 } 3683 CellType eType = aIter.getType(); 3684 bool bOk = false; 3685 if (eType == CELLTYPE_FORMULA) 3686 { 3687 ScFormulaCell* pFC = aIter.getFormulaCell(); 3688 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos); 3689 } 3690 else 3691 bOk = true; 3692 3693 if (bOk && aIter.hasString()) 3694 { 3695 OUString aStr = aIter.getString(); 3696 if ( ScGlobal::GetpTransliteration()->isEqual( aStr, aName ) ) 3697 { 3698 SCCOL nCol = aIter.GetPos().Col(); 3699 SCROW nRow = aIter.GetPos().Row(); 3700 long nC = nMyCol - nCol; 3701 long nR = nMyRow - nRow; 3702 if ( bFound ) 3703 { 3704 long nD = nC * nC + nR * nR; 3705 if ( nD < nDistance ) 3706 { 3707 if ( nC < 0 || nR < 0 ) 3708 { // right or below 3709 bTwo = true; 3710 aTwo.Set( nCol, nRow, aIter.GetPos().Tab() ); 3711 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) ); 3712 nDistance = nD; 3713 } 3714 else if ( !(nRow < aOne.Row() && nMyRow >= static_cast<long>(aOne.Row())) ) 3715 { 3716 // upper left, only if not further up than the 3717 // current entry and nMyRow is below (CellIter 3718 // runs column-wise) 3719 bTwo = false; 3720 aOne.Set( nCol, nRow, aIter.GetPos().Tab() ); 3721 nMax = std::max( nMyCol + nC, nMyRow + nR ); 3722 nDistance = nD; 3723 } 3724 } 3725 } 3726 else 3727 { 3728 aOne.Set( nCol, nRow, aIter.GetPos().Tab() ); 3729 nDistance = nC * nC + nR * nR; 3730 nMax = std::max( nMyCol + std::abs( nC ), nMyRow + std::abs( nR ) ); 3731 } 3732 bFound = true; 3733 } 3734 } 3735 } 3736 } 3737 3738 if ( bFound ) 3739 { 3740 ScAddress aAdr; 3741 if ( bTwo ) 3742 { 3743 if ( nMyCol >= static_cast<long>(aOne.Col()) && nMyRow >= static_cast<long>(aOne.Row()) ) 3744 aAdr = aOne; // upper left takes precedence 3745 else 3746 { 3747 if ( nMyCol < static_cast<long>(aOne.Col()) ) 3748 { // two to the right 3749 if ( nMyRow >= static_cast<long>(aTwo.Row()) ) 3750 aAdr = aTwo; // directly right 3751 else 3752 aAdr = aOne; 3753 } 3754 else 3755 { // two below or below and right, take the nearest 3756 long nC1 = nMyCol - aOne.Col(); 3757 long nR1 = nMyRow - aOne.Row(); 3758 long nC2 = nMyCol - aTwo.Col(); 3759 long nR2 = nMyRow - aTwo.Row(); 3760 if ( nC1 * nC1 + nR1 * nR1 <= nC2 * nC2 + nR2 * nR2 ) 3761 aAdr = aOne; 3762 else 3763 aAdr = aTwo; 3764 } 3765 } 3766 } 3767 else 3768 aAdr = aOne; 3769 aRef.InitAddress( aAdr ); 3770 if ( (aAdr.Row() != pDoc->MaxRow() && pDoc->HasStringData( 3771 aAdr.Col(), aAdr.Row() + 1, aAdr.Tab())) 3772 || (aAdr.Row() != 0 && pDoc->HasStringData( 3773 aAdr.Col(), aAdr.Row() - 1, aAdr.Tab()))) 3774 aRef.SetRowRel( true ); // RowName 3775 else 3776 aRef.SetColRel( true ); // ColName 3777 aRef.SetAddress(pDoc->GetSheetLimits(), aAdr, aPos); 3778 } 3779 } 3780 if ( bFound ) 3781 { 3782 maRawToken.SetSingleReference( aRef ); 3783 maRawToken.eOp = ocColRowName; 3784 return true; 3785 } 3786 else 3787 return false; 3788 } 3789 3790 bool ScCompiler::IsBoolean( const OUString& rName ) 3791 { 3792 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName ) ); 3793 if( iLook != mxSymbols->getHashMap().end() && 3794 ((*iLook).second == ocTrue || 3795 (*iLook).second == ocFalse) ) 3796 { 3797 maRawToken.SetOpCode( (*iLook).second ); 3798 return true; 3799 } 3800 else 3801 return false; 3802 } 3803 3804 bool ScCompiler::IsErrorConstant( const OUString& rName ) const 3805 { 3806 FormulaError nError = GetErrorConstant( rName); 3807 if (nError != FormulaError::NONE) 3808 { 3809 maRawToken.SetErrorConstant( nError); 3810 return true; 3811 } 3812 else 3813 return false; 3814 } 3815 3816 bool ScCompiler::IsTableRefItem( const OUString& rName ) const 3817 { 3818 bool bItem = false; 3819 OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap().find( rName)); 3820 if (iLook != mxSymbols->getHashMap().end()) 3821 { 3822 // Only called when there actually is a current TableRef, hence 3823 // accessing maTableRefs.back() is safe. 3824 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get()); 3825 assert(p); // not a ScTableRefToken can't be 3826 3827 switch ((*iLook).second) 3828 { 3829 case ocTableRefItemAll: 3830 bItem = true; 3831 p->AddItem( ScTableRefToken::ALL); 3832 break; 3833 case ocTableRefItemHeaders: 3834 bItem = true; 3835 p->AddItem( ScTableRefToken::HEADERS); 3836 break; 3837 case ocTableRefItemData: 3838 bItem = true; 3839 p->AddItem( ScTableRefToken::DATA); 3840 break; 3841 case ocTableRefItemTotals: 3842 bItem = true; 3843 p->AddItem( ScTableRefToken::TOTALS); 3844 break; 3845 case ocTableRefItemThisRow: 3846 bItem = true; 3847 p->AddItem( ScTableRefToken::THIS_ROW); 3848 break; 3849 default: 3850 ; 3851 } 3852 if (bItem) 3853 maRawToken.SetOpCode( (*iLook).second ); 3854 } 3855 return bItem; 3856 } 3857 3858 namespace { 3859 OUString unescapeTableRefColumnSpecifier( const OUString& rStr ) 3860 { 3861 // '#', '[', ']' and '\'' are escaped with '\'' 3862 3863 if (rStr.indexOf( '\'' ) < 0) 3864 return rStr; 3865 3866 const sal_Int32 n = rStr.getLength(); 3867 OUStringBuffer aBuf( n ); 3868 const sal_Unicode* p = rStr.getStr(); 3869 const sal_Unicode* const pStop = p + n; 3870 bool bEscaped = false; 3871 for ( ; p < pStop; ++p) 3872 { 3873 const sal_Unicode c = *p; 3874 if (bEscaped) 3875 { 3876 aBuf.append( c ); 3877 bEscaped = false; 3878 } 3879 else if (c == '\'') 3880 bEscaped = true; // unescaped escaping '\'' 3881 else 3882 aBuf.append( c ); 3883 } 3884 return aBuf.makeStringAndClear(); 3885 } 3886 } 3887 3888 bool ScCompiler::IsTableRefColumn( const OUString& rName ) const 3889 { 3890 // Only called when there actually is a current TableRef, hence 3891 // accessing maTableRefs.back() is safe. 3892 ScTableRefToken* p = dynamic_cast<ScTableRefToken*>(maTableRefs.back().mxToken.get()); 3893 assert(p); // not a ScTableRefToken can't be 3894 3895 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( p->GetIndex()); 3896 if (!pDBData) 3897 return false; 3898 3899 OUString aName( unescapeTableRefColumnSpecifier( rName)); 3900 3901 ScRange aRange; 3902 pDBData->GetArea( aRange); 3903 aRange.aEnd.SetTab( aRange.aStart.Tab()); 3904 aRange.aEnd.SetRow( aRange.aStart.Row()); 3905 3906 // Prefer the stored internal table column name, which is also needed for 3907 // named expressions during document load time when cell content isn't 3908 // available yet. Also, avoiding a possible calculation step in case the 3909 // header cell is a formula cell is "a good thing". 3910 sal_Int32 nOffset = pDBData->GetColumnNameOffset( aName); 3911 if (nOffset >= 0) 3912 { 3913 // This is sneaky... we always use the top row of the database range, 3914 // regardless of whether it is a header row or not. Code evaluating 3915 // this reference must take that into account and may have to act 3916 // differently if it is a header-less table. Which are two places, 3917 // HandleTableRef() (no change necessary there) and 3918 // CreateStringFromSingleRef() (must not fallback to cell lookup). 3919 ScSingleRefData aRef; 3920 ScAddress aAdr( aRange.aStart); 3921 aAdr.IncCol( nOffset); 3922 aRef.InitAddress( aAdr); 3923 maRawToken.SetSingleReference( aRef ); 3924 return true; 3925 } 3926 3927 if (pDBData->HasHeader()) 3928 { 3929 // Quite similar to IsColRowName() but limited to one row of headers. 3930 ScCellIterator aIter( pDoc, aRange); 3931 for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) 3932 { 3933 CellType eType = aIter.getType(); 3934 bool bOk = false; 3935 if (eType == CELLTYPE_FORMULA) 3936 { 3937 ScFormulaCell* pFC = aIter.getFormulaCell(); 3938 bOk = (pFC->GetCode()->GetCodeLen() > 0) && (pFC->aPos != aPos); 3939 } 3940 else 3941 bOk = true; 3942 3943 if (bOk && aIter.hasString()) 3944 { 3945 OUString aStr = aIter.getString(); 3946 if (ScGlobal::GetpTransliteration()->isEqual( aStr, aName)) 3947 { 3948 // If this is successful and the internal column name 3949 // lookup was not, it may be worth a warning. 3950 SAL_WARN("sc.core", "ScCompiler::IsTableRefColumn - falling back to cell lookup"); 3951 3952 /* XXX NOTE: we could init the column as relative so copying a 3953 * formula across columns would point to the relative column, 3954 * but do it absolute because: 3955 * a) it makes the reference work in named expressions without 3956 * having to distinguish 3957 * b) Excel does it the same. */ 3958 ScSingleRefData aRef; 3959 aRef.InitAddress( aIter.GetPos()); 3960 maRawToken.SetSingleReference( aRef ); 3961 return true; 3962 } 3963 } 3964 } 3965 } 3966 3967 return false; 3968 } 3969 3970 void ScCompiler::SetAutoCorrection( bool bVal ) 3971 { 3972 assert(mbJumpCommandReorder); 3973 bAutoCorrect = bVal; 3974 mbStopOnError = !bVal; 3975 } 3976 3977 void ScCompiler::AutoCorrectParsedSymbol() 3978 { 3979 sal_Int32 nPos = aCorrectedSymbol.getLength(); 3980 if ( nPos ) 3981 { 3982 nPos--; 3983 const sal_Unicode cQuote = '\"'; 3984 const sal_Unicode cx = 'x'; 3985 const sal_Unicode cX = 'X'; 3986 sal_Unicode c1 = aCorrectedSymbol[0]; 3987 sal_Unicode c2 = aCorrectedSymbol[nPos]; 3988 sal_Unicode c2p = nPos > 0 ? aCorrectedSymbol[nPos-1] : 0; 3989 if ( c1 == cQuote && c2 != cQuote ) 3990 { // "... 3991 // What's not a word doesn't belong to it. 3992 // Don't be pedantic: c < 128 should be sufficient here. 3993 while ( nPos && ((aCorrectedSymbol[nPos] < 128) && 3994 ((GetCharTableFlags(aCorrectedSymbol[nPos], aCorrectedSymbol[nPos-1]) & 3995 (ScCharFlags::Word | ScCharFlags::CharDontCare)) == ScCharFlags::NONE)) ) 3996 nPos--; 3997 if ( nPos == MAXSTRLEN - 1 ) 3998 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos, 1, OUString(cQuote) ); // '"' the MAXSTRLENth character 3999 else 4000 aCorrectedSymbol = aCorrectedSymbol.replaceAt( nPos + 1, 0, OUString(cQuote) ); 4001 bCorrected = true; 4002 } 4003 else if ( c1 != cQuote && c2 == cQuote ) 4004 { // ..." 4005 aCorrectedSymbol = OUStringChar(cQuote) + aCorrectedSymbol; 4006 bCorrected = true; 4007 } 4008 else if ( nPos == 0 && (c1 == cx || c1 == cX) ) 4009 { // x => * 4010 aCorrectedSymbol = mxSymbols->getSymbol(ocMul); 4011 bCorrected = true; 4012 } 4013 else if ( (GetCharTableFlags( c1, 0 ) & ScCharFlags::CharValue) 4014 && (GetCharTableFlags( c2, c2p ) & ScCharFlags::CharValue) ) 4015 { 4016 if ( aCorrectedSymbol.indexOf(cx) >= 0 ) // At least two tokens separated by cx 4017 { // x => * 4018 sal_Unicode c = mxSymbols->getSymbolChar(ocMul); 4019 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cx), OUStringChar(c)); 4020 bCorrected = true; 4021 } 4022 if ( aCorrectedSymbol.indexOf(cX) >= 0 ) // At least two tokens separated by cX 4023 { // X => * 4024 sal_Unicode c = mxSymbols->getSymbolChar(ocMul); 4025 aCorrectedSymbol = aCorrectedSymbol.replaceAll(OUStringChar(cX), OUStringChar(c)); 4026 bCorrected = true; 4027 } 4028 } 4029 else 4030 { 4031 OUString aSymbol( aCorrectedSymbol ); 4032 OUString aDoc; 4033 sal_Int32 nPosition; 4034 if ( aSymbol[0] == '\'' 4035 && ((nPosition = aSymbol.indexOf( "'#" )) != -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 sal_Int32 nRefs = comphelper::string::getTokenCount(aSymbol, ':'); 4041 bool bColons; 4042 if ( nRefs > 2 ) 4043 { // duplicated or too many ':'? B:2::C10 => B2:C10 4044 bColons = true; 4045 sal_Int32 nIndex = 0; 4046 OUString aTmp1( aSymbol.getToken( 0, ':', nIndex ) ); 4047 sal_Int32 nLen1 = aTmp1.getLength(); 4048 OUStringBuffer aSym; 4049 OUString aTmp2; 4050 bool bLastAlp, bNextNum; 4051 bLastAlp = bNextNum = 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 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; 4117 ScRefFlags nMask = ScRefFlags::VALID | ScRefFlags::COL_VALID | ScRefFlags::ROW_VALID; 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 4161 static bool lcl_UpperAsciiOrI18n( OUString& rUpper, const OUString& rOrg, FormulaGrammar::Grammar eGrammar ) 4162 { 4163 if (FormulaGrammar::isODFF( eGrammar )) 4164 { 4165 // ODFF has a defined set of English function names, avoid i18n 4166 // overhead. 4167 rUpper = rOrg.toAsciiUpperCase(); 4168 return true; 4169 } 4170 else 4171 { 4172 rUpper = ScGlobal::pCharClass->uppercase(rOrg); 4173 return false; 4174 } 4175 } 4176 4177 bool ScCompiler::NextNewToken( bool bInArray ) 4178 { 4179 bool bAllowBooleans = bInArray; 4180 sal_Int32 nSpaces = NextSymbol(bInArray); 4181 4182 if (!cSymbol[0]) 4183 { 4184 if (nSrcPos < aFormula.getLength()) 4185 { 4186 // Nothing could be parsed, remainder as bad string. 4187 // NextSymbol() must had set an error for this. 4188 assert( pArr->GetCodeError() != FormulaError::NONE); 4189 const OUString aBad( aFormula.copy( nSrcPos)); 4190 svl::SharedString aSS = pDoc->GetSharedStringPool().intern( aBad); 4191 maRawToken.SetString( aSS.getData(), aSS.getDataIgnoreCase()); 4192 maRawToken.NewOpCode( ocBad); 4193 nSrcPos = aFormula.getLength(); 4194 // Add bad string as last token. 4195 return true; 4196 } 4197 return false; 4198 } 4199 4200 if( nSpaces ) 4201 { 4202 ScRawToken aToken; 4203 aToken.SetOpCode( ocSpaces ); 4204 aToken.sbyte.cByte = static_cast<sal_uInt8>( std::min<sal_Int32>(nSpaces, 255) ); 4205 if( !static_cast<ScTokenArray*>(pArr)->AddRawToken( aToken ) ) 4206 { 4207 SetError(FormulaError::CodeOverflow); 4208 return false; 4209 } 4210 } 4211 4212 // Short cut for references when reading ODF to speedup things. 4213 if (mnPredetectedReference) 4214 { 4215 OUString aStr( cSymbol); 4216 bool bInvalidExternalNameRange; 4217 if (!IsPredetectedReference( aStr) && !IsExternalNamedRange( aStr, bInvalidExternalNameRange )) 4218 { 4219 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aStr); 4220 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase()); 4221 maRawToken.NewOpCode( ocBad ); 4222 } 4223 return true; 4224 } 4225 4226 if ( (cSymbol[0] == '#' || cSymbol[0] == '$') && cSymbol[1] == 0 && 4227 !bAutoCorrect ) 4228 { // special case to speed up broken [$]#REF documents 4229 /* FIXME: ISERROR(#REF!) would be valid and true and the formula to 4230 * be processed as usual. That would need some special treatment, 4231 * also in NextSymbol() because of possible combinations of 4232 * #REF!.#REF!#REF! parts. In case of reading ODF that is all 4233 * handled by IsPredetectedReference(), this case here remains for 4234 * manual/API input. */ 4235 OUString aBad( aFormula.copy( nSrcPos-1 ) ); 4236 eLastOp = pArr->AddBad( aBad )->GetOpCode(); 4237 return false; 4238 } 4239 4240 if( IsString() ) 4241 return true; 4242 4243 bool bMayBeFuncName; 4244 bool bAsciiNonAlnum; // operators, separators, ... 4245 if ( cSymbol[0] < 128 ) 4246 { 4247 bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] ); 4248 if (!bMayBeFuncName && (cSymbol[0] == '_' && cSymbol[1] == '_') && !utl::ConfigManager::IsFuzzing()) 4249 { 4250 SvtMiscOptions aOpt; 4251 bMayBeFuncName = aOpt.IsExperimentalMode(); 4252 } 4253 4254 bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] ); 4255 } 4256 else 4257 { 4258 OUString aTmpStr( cSymbol[0] ); 4259 bMayBeFuncName = ScGlobal::pCharClass->isLetter( aTmpStr, 0 ); 4260 bAsciiNonAlnum = false; 4261 } 4262 if (bAsciiNonAlnum && cSymbol[1] == 0) 4263 { 4264 // Shortcut for operators and separators that need no further checks or upper. 4265 if (IsOpCode( OUString( cSymbol), bInArray )) 4266 return true; 4267 } 4268 if ( bMayBeFuncName ) 4269 { 4270 // a function name must be followed by a parenthesis 4271 const sal_Unicode* p = aFormula.getStr() + nSrcPos; 4272 while( *p == ' ' ) 4273 p++; 4274 bMayBeFuncName = ( *p == '(' ); 4275 } 4276 4277 // Italian ARCTAN.2 resulted in #REF! => IsOpcode() before 4278 // IsReference(). 4279 4280 OUString aUpper; 4281 4282 do 4283 { 4284 const OUString aOrg( cSymbol ); 4285 4286 // Check for TableRef column specifier first, it may be anything. 4287 if (cSymbol[0] != '#' && !maTableRefs.empty() && maTableRefs.back().mnLevel) 4288 { 4289 if (IsTableRefColumn( aOrg )) 4290 return true; 4291 // Do not attempt to resolve as any other name. 4292 aUpper = aOrg; // for ocBad 4293 break; // do; create ocBad token or set error. 4294 } 4295 4296 mbRewind = false; 4297 aUpper.clear(); 4298 bool bAsciiUpper = false; 4299 4300 if (bAsciiNonAlnum) 4301 { 4302 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar); 4303 if (cSymbol[0] == '#') 4304 { 4305 // Check for TableRef item specifiers first. 4306 if (!maTableRefs.empty() && maTableRefs.back().mnLevel == 2) 4307 { 4308 if (IsTableRefItem( aUpper )) 4309 return true; 4310 } 4311 4312 // This can be either an error constant ... 4313 if (IsErrorConstant( aUpper)) 4314 return true; 4315 4316 // ... or some invalidated reference starting with #REF! 4317 // which is handled after the do loop. 4318 4319 break; // do; create ocBad token or set error. 4320 } 4321 if (IsOpCode( aUpper, bInArray )) 4322 return true; 4323 } 4324 4325 if (bMayBeFuncName) 4326 { 4327 if (aUpper.isEmpty()) 4328 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar); 4329 if (IsOpCode( aUpper, bInArray )) 4330 return true; 4331 } 4332 4333 // Column 'DM' ("Deutsche Mark", German currency) couldn't be 4334 // referred => IsReference() before IsValue(). 4335 // Preserve case of file names in external references. 4336 if (IsReference( aOrg )) 4337 { 4338 if (mbRewind) // Range operator, but no direct reference. 4339 continue; // do; up to range operator. 4340 // If a syntactically correct reference was recognized but invalid 4341 // e.g. because of non-existing sheet name => entire reference 4342 // ocBad to preserve input instead of #REF!.A1 4343 if (!maRawToken.IsValidReference(pDoc)) 4344 { 4345 aUpper = aOrg; // ensure for ocBad 4346 break; // do; create ocBad token or set error. 4347 } 4348 return true; 4349 } 4350 4351 if (aUpper.isEmpty()) 4352 bAsciiUpper = lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar); 4353 4354 // IsBoolean() before IsValue() to catch inline bools without the kludge 4355 // for inline arrays. 4356 if (bAllowBooleans && IsBoolean( aUpper )) 4357 return true; 4358 4359 if (IsValue( aUpper )) 4360 return true; 4361 4362 // User defined names and such do need i18n upper also in ODF. 4363 if (bAsciiUpper) 4364 aUpper = ScGlobal::pCharClass->uppercase( aOrg ); 4365 4366 if (IsNamedRange( aUpper )) 4367 return true; 4368 4369 // Compiling a named expression during collecting them in import shall 4370 // not match arbitrary names that otherwise if all named expressions 4371 // were present would be recognized as named expression. Such name will 4372 // flag an error below and will be recompiled in a second step later 4373 // with ScRangeData::CompileUnresolvedXML() 4374 if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_NO_BREAK && pDoc->IsImportingXML()) 4375 break; // while 4376 4377 // Preserve case of file names in external references. 4378 bool bInvalidExternalNameRange; 4379 if (IsExternalNamedRange( aOrg, bInvalidExternalNameRange )) 4380 return true; 4381 // Preserve case of file names in external references even when range 4382 // is not valid and previous check failed tdf#89330 4383 if (bInvalidExternalNameRange) 4384 { 4385 // add ocBad but do not lowercase 4386 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aOrg); 4387 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase()); 4388 maRawToken.NewOpCode( ocBad ); 4389 return true; 4390 } 4391 if (IsDBRange( aUpper )) 4392 return true; 4393 // If followed by '(' (with or without space inbetween) it can not be a 4394 // column/row label. Prevent arbitrary content detection. 4395 if (!bMayBeFuncName && IsColRowName( aUpper )) 4396 return true; 4397 if (bMayBeFuncName && IsMacro( aUpper )) 4398 return true; 4399 if (bMayBeFuncName && IsOpCode2( aUpper )) 4400 return true; 4401 4402 } while (mbRewind); 4403 4404 // Last chance: it could be a broken invalidated reference that contains 4405 // #REF! (but is not equal to), which we also wrote to ODFF between 2013 4406 // and 2016 until 5.1.4 4407 OUString aErrRef( mxSymbols->getSymbol( ocErrRef)); 4408 if (aUpper.indexOf( aErrRef) >= 0 && IsReference( aUpper, &aErrRef)) 4409 return true; 4410 4411 if ( meExtendedErrorDetection != EXTENDED_ERROR_DETECTION_NONE ) 4412 { 4413 // set an error 4414 SetError( FormulaError::NoName ); 4415 if (meExtendedErrorDetection == EXTENDED_ERROR_DETECTION_NAME_BREAK) 4416 return false; // end compilation 4417 } 4418 4419 // Provide single token information and continue. Do not set an error, that 4420 // would prematurely end compilation. Simple unknown names are handled by 4421 // the interpreter. 4422 aUpper = ScGlobal::pCharClass->lowercase( aUpper ); 4423 svl::SharedString aSS = pDoc->GetSharedStringPool().intern(aUpper); 4424 maRawToken.SetString(aSS.getData(), aSS.getDataIgnoreCase()); 4425 maRawToken.NewOpCode( ocBad ); 4426 if ( bAutoCorrect ) 4427 AutoCorrectParsedSymbol(); 4428 return true; 4429 } 4430 4431 void ScCompiler::CreateStringFromXMLTokenArray( OUString& rFormula, OUString& rFormulaNmsp ) 4432 { 4433 bool bExternal = GetGrammar() == FormulaGrammar::GRAM_EXTERNAL; 4434 sal_uInt16 nExpectedCount = bExternal ? 2 : 1; 4435 OSL_ENSURE( pArr->GetLen() == nExpectedCount, "ScCompiler::CreateStringFromXMLTokenArray - wrong number of tokens" ); 4436 if( pArr->GetLen() == nExpectedCount ) 4437 { 4438 FormulaToken** ppTokens = pArr->GetArray(); 4439 // string tokens expected, GetString() will assert if token type is wrong 4440 rFormula = ppTokens[0]->GetString().getString(); 4441 if( bExternal ) 4442 rFormulaNmsp = ppTokens[1]->GetString().getString(); 4443 } 4444 } 4445 4446 namespace { 4447 4448 class ExternalFileInserter 4449 { 4450 ScAddress const maPos; 4451 ScExternalRefManager& mrRefMgr; 4452 public: 4453 ExternalFileInserter(const ScAddress& rPos, ScExternalRefManager& rRefMgr) : 4454 maPos(rPos), mrRefMgr(rRefMgr) {} 4455 4456 void operator() (sal_uInt16 nFileId) const 4457 { 4458 mrRefMgr.insertRefCell(nFileId, maPos); 4459 } 4460 }; 4461 4462 } 4463 4464 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula ) 4465 { 4466 OSL_ENSURE( meGrammar != FormulaGrammar::GRAM_EXTERNAL, "ScCompiler::CompileString - unexpected grammar GRAM_EXTERNAL" ); 4467 if( meGrammar == FormulaGrammar::GRAM_EXTERNAL ) 4468 SetGrammar( FormulaGrammar::GRAM_PODF ); 4469 4470 ScTokenArray aArr(pDoc); 4471 pArr = &aArr; 4472 maArrIterator = FormulaTokenArrayPlainIterator(*pArr); 4473 aFormula = comphelper::string::strip(rFormula, ' '); 4474 4475 nSrcPos = 0; 4476 bCorrected = false; 4477 if ( bAutoCorrect ) 4478 { 4479 aCorrectedFormula.clear(); 4480 aCorrectedSymbol.clear(); 4481 } 4482 sal_uInt8 nForced = 0; // ==formula forces recalc even if cell is not visible 4483 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' ) 4484 { 4485 nSrcPos++; 4486 nForced++; 4487 if ( bAutoCorrect ) 4488 aCorrectedFormula += "="; 4489 } 4490 if( nSrcPos < aFormula.getLength() && aFormula[nSrcPos] == '=' ) 4491 { 4492 nSrcPos++; 4493 nForced++; 4494 if ( bAutoCorrect ) 4495 aCorrectedFormula += "="; 4496 } 4497 struct FunctionStack 4498 { 4499 OpCode eOp; 4500 short nSep; 4501 }; 4502 // FunctionStack only used if PODF or OOXML! 4503 bool bPODF = FormulaGrammar::isPODF( meGrammar); 4504 bool bOOXML = FormulaGrammar::isOOXML( meGrammar); 4505 bool bUseFunctionStack = (bPODF || bOOXML); 4506 const size_t nAlloc = 512; 4507 FunctionStack aFuncs[ nAlloc ]; 4508 FunctionStack* pFunctionStack = (bUseFunctionStack && static_cast<size_t>(rFormula.getLength()) > nAlloc ? 4509 new FunctionStack[rFormula.getLength()] : &aFuncs[0]); 4510 pFunctionStack[0].eOp = ocNone; 4511 pFunctionStack[0].nSep = 0; 4512 size_t nFunction = 0; 4513 short nBrackets = 0; 4514 bool bInArray = false; 4515 eLastOp = ocOpen; 4516 while( NextNewToken( bInArray ) ) 4517 { 4518 const OpCode eOp = maRawToken.GetOpCode(); 4519 if (eOp == ocSkip) 4520 continue; 4521 4522 switch (eOp) 4523 { 4524 case ocOpen: 4525 { 4526 ++nBrackets; 4527 if (bUseFunctionStack) 4528 { 4529 ++nFunction; 4530 pFunctionStack[ nFunction ].eOp = eLastOp; 4531 pFunctionStack[ nFunction ].nSep = 0; 4532 } 4533 } 4534 break; 4535 case ocClose: 4536 { 4537 if( !nBrackets ) 4538 { 4539 SetError( FormulaError::PairExpected ); 4540 if ( bAutoCorrect ) 4541 { 4542 bCorrected = true; 4543 aCorrectedSymbol.clear(); 4544 } 4545 } 4546 else 4547 nBrackets--; 4548 if (bUseFunctionStack && nFunction) 4549 --nFunction; 4550 } 4551 break; 4552 case ocSep: 4553 { 4554 if (bUseFunctionStack) 4555 ++pFunctionStack[ nFunction ].nSep; 4556 } 4557 break; 4558 case ocArrayOpen: 4559 { 4560 if( bInArray ) 4561 SetError( FormulaError::NestedArray ); 4562 else 4563 bInArray = true; 4564 // Don't count following column separator as parameter separator. 4565 if (bUseFunctionStack) 4566 { 4567 ++nFunction; 4568 pFunctionStack[ nFunction ].eOp = eOp; 4569 pFunctionStack[ nFunction ].nSep = 0; 4570 } 4571 } 4572 break; 4573 case ocArrayClose: 4574 { 4575 if( bInArray ) 4576 { 4577 bInArray = false; 4578 } 4579 else 4580 { 4581 SetError( FormulaError::PairExpected ); 4582 if ( bAutoCorrect ) 4583 { 4584 bCorrected = true; 4585 aCorrectedSymbol.clear(); 4586 } 4587 } 4588 if (bUseFunctionStack && nFunction) 4589 --nFunction; 4590 } 4591 break; 4592 case ocTableRefOpen: 4593 { 4594 // Don't count following item separator as parameter separator. 4595 if (bUseFunctionStack) 4596 { 4597 ++nFunction; 4598 pFunctionStack[ nFunction ].eOp = eOp; 4599 pFunctionStack[ nFunction ].nSep = 0; 4600 } 4601 } 4602 break; 4603 case ocTableRefClose: 4604 { 4605 if (bUseFunctionStack && nFunction) 4606 --nFunction; 4607 } 4608 break; 4609 case ocColRowName: 4610 case ocColRowNameAuto: 4611 // The current implementation of column / row labels doesn't 4612 // function correctly in grouped cells. 4613 aArr.SetShareable(false); 4614 break; 4615 default: 4616 break; 4617 } 4618 if (!(eLastOp == ocOpen && eOp == ocClose) && 4619 (eLastOp == ocOpen || 4620 eLastOp == ocSep || 4621 eLastOp == ocArrayRowSep || 4622 eLastOp == ocArrayColSep || 4623 eLastOp == ocArrayOpen) && 4624 (eOp == ocSep || 4625 eOp == ocClose || 4626 eOp == ocArrayRowSep || 4627 eOp == ocArrayColSep || 4628 eOp == ocArrayClose)) 4629 { 4630 // TODO: should we check for known functions with optional empty 4631 // args so the correction dialog can do better? 4632 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaMissingToken ) ) 4633 { 4634 SetError(FormulaError::CodeOverflow); break; 4635 } 4636 } 4637 if (bOOXML) 4638 { 4639 // Append a parameter for WEEKNUM, all 1.0 4640 // Function is already closed, parameter count is nSep+1 4641 size_t nFunc = nFunction + 1; 4642 if (eOp == ocClose && 4643 (pFunctionStack[ nFunc ].eOp == ocWeek && // 2nd week start 4644 pFunctionStack[ nFunc ].nSep == 0)) 4645 { 4646 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) || 4647 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0))) 4648 { 4649 SetError(FormulaError::CodeOverflow); break; 4650 } 4651 } 4652 } 4653 else if (bPODF) 4654 { 4655 /* TODO: for now this is the only PODF adapter. If there were more, 4656 * factor this out. */ 4657 // Insert ADDRESS() new empty parameter 4 if there is a 4th, now to be 5th. 4658 if (eOp == ocSep && 4659 pFunctionStack[ nFunction ].eOp == ocAddress && 4660 pFunctionStack[ nFunction ].nSep == 3) 4661 { 4662 if ( !static_cast<ScTokenArray*>(pArr)->Add( new FormulaToken( svSep, ocSep)) || 4663 !static_cast<ScTokenArray*>(pArr)->Add( new FormulaDoubleToken( 1.0))) 4664 { 4665 SetError(FormulaError::CodeOverflow); break; 4666 } 4667 ++pFunctionStack[ nFunction ].nSep; 4668 } 4669 } 4670 FormulaToken* pNewToken = static_cast<ScTokenArray*>(pArr)->Add( maRawToken.CreateToken(pDoc->GetSheetLimits())); 4671 if (!pNewToken && eOp == ocArrayClose && pArr->OpCodeBefore( pArr->GetLen()) == ocArrayClose) 4672 { 4673 // Nested inline array or non-value/non-string in array. The 4674 // original tokens are still in the ScTokenArray and not merged 4675 // into an ScMatrixToken. Set error but keep on tokenizing. 4676 SetError( FormulaError::BadArrayContent); 4677 } 4678 else if (!pNewToken) 4679 { 4680 SetError(FormulaError::CodeOverflow); 4681 break; 4682 } 4683 else if (eLastOp == ocRange && pNewToken->GetOpCode() == ocPush && pNewToken->GetType() == svSingleRef) 4684 { 4685 static_cast<ScTokenArray*>(pArr)->MergeRangeReference( aPos); 4686 } 4687 else if (eLastOp == ocDBArea && pNewToken->GetOpCode() == ocTableRefOpen) 4688 { 4689 sal_uInt16 nIdx = pArr->GetLen() - 1; 4690 const FormulaToken* pPrev = pArr->PeekPrev( nIdx); 4691 if (pPrev && pPrev->GetOpCode() == ocDBArea) 4692 { 4693 FormulaToken* pTableRefToken = new ScTableRefToken( pPrev->GetIndex(), ScTableRefToken::TABLE); 4694 maTableRefs.emplace_back( pTableRefToken); 4695 // pPrev may be dead hereafter. 4696 static_cast<ScTokenArray*>(pArr)->ReplaceToken( nIdx, pTableRefToken, 4697 FormulaTokenArray::ReplaceMode::CODE_ONLY); 4698 } 4699 } 4700 switch (eOp) 4701 { 4702 case ocTableRefOpen: 4703 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefOpen without TableRefEntry"); 4704 if (maTableRefs.empty()) 4705 SetError(FormulaError::Pair); 4706 else 4707 ++maTableRefs.back().mnLevel; 4708 break; 4709 case ocTableRefClose: 4710 SAL_WARN_IF( maTableRefs.empty(), "sc.core", "ocTableRefClose without TableRefEntry"); 4711 if (maTableRefs.empty()) 4712 SetError(FormulaError::Pair); 4713 else 4714 { 4715 if (--maTableRefs.back().mnLevel == 0) 4716 maTableRefs.pop_back(); 4717 } 4718 break; 4719 default: 4720 break; 4721 } 4722 eLastOp = maRawToken.GetOpCode(); 4723 if ( bAutoCorrect ) 4724 aCorrectedFormula += aCorrectedSymbol; 4725 } 4726 if ( mbCloseBrackets ) 4727 { 4728 if( bInArray ) 4729 { 4730 FormulaByteToken aToken( ocArrayClose ); 4731 if( !pArr->AddToken( aToken ) ) 4732 { 4733 SetError(FormulaError::CodeOverflow); 4734 } 4735 else if ( bAutoCorrect ) 4736 aCorrectedFormula += mxSymbols->getSymbol(ocArrayClose); 4737 } 4738 4739 if (nBrackets) 4740 { 4741 FormulaToken aToken( svSep, ocClose ); 4742 while( nBrackets-- ) 4743 { 4744 if( !pArr->AddToken( aToken ) ) 4745 { 4746 SetError(FormulaError::CodeOverflow); 4747 break; // while 4748 } 4749 if ( bAutoCorrect ) 4750 aCorrectedFormula += mxSymbols->getSymbol(ocClose); 4751 } 4752 } 4753 } 4754 if ( nForced >= 2 ) 4755 pArr->SetRecalcModeForced(); 4756 4757 if (pFunctionStack != &aFuncs[0]) 4758 delete [] pFunctionStack; 4759 4760 // remember pArr, in case a subsequent CompileTokenArray() is executed. 4761 std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aArr )); 4762 pNew->GenHash(); 4763 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid 4764 pArr = pNew.get(); 4765 maArrIterator = FormulaTokenArrayPlainIterator(*pArr); 4766 4767 if (!maExternalFiles.empty()) 4768 { 4769 // Remove duplicates, and register all external files found in this cell. 4770 std::sort(maExternalFiles.begin(), maExternalFiles.end()); 4771 std::vector<sal_uInt16>::iterator itEnd = std::unique(maExternalFiles.begin(), maExternalFiles.end()); 4772 std::for_each(maExternalFiles.begin(), itEnd, ExternalFileInserter(aPos, *pDoc->GetExternalRefManager())); 4773 maExternalFiles.erase(itEnd, maExternalFiles.end()); 4774 } 4775 4776 return pNew; 4777 } 4778 4779 std::unique_ptr<ScTokenArray> ScCompiler::CompileString( const OUString& rFormula, const OUString& rFormulaNmsp ) 4780 { 4781 OSL_ENSURE( (GetGrammar() == FormulaGrammar::GRAM_EXTERNAL) || rFormulaNmsp.isEmpty(), 4782 "ScCompiler::CompileString - unexpected formula namespace for internal grammar" ); 4783 if( GetGrammar() == FormulaGrammar::GRAM_EXTERNAL ) try 4784 { 4785 ScFormulaParserPool& rParserPool = pDoc->GetFormulaParserPool(); 4786 uno::Reference< sheet::XFormulaParser > xParser( rParserPool.getFormulaParser( rFormulaNmsp ), uno::UNO_SET_THROW ); 4787 table::CellAddress aReferencePos; 4788 ScUnoConversion::FillApiAddress( aReferencePos, aPos ); 4789 uno::Sequence< sheet::FormulaToken > aTokenSeq = xParser->parseFormula( rFormula, aReferencePos ); 4790 ScTokenArray aTokenArray(pDoc); 4791 if( ScTokenConversion::ConvertToTokenArray( *pDoc, aTokenArray, aTokenSeq ) ) 4792 { 4793 // remember pArr, in case a subsequent CompileTokenArray() is executed. 4794 std::unique_ptr<ScTokenArray> pNew(new ScTokenArray( aTokenArray )); 4795 // coverity[escape : FALSE] - ownership of pNew is retained by caller, so pArr remains valid 4796 pArr = pNew.get(); 4797 maArrIterator = FormulaTokenArrayPlainIterator(*pArr); 4798 return pNew; 4799 } 4800 } 4801 catch( uno::Exception& ) 4802 { 4803 } 4804 // no success - fallback to some internal grammar and hope the best 4805 return CompileString( rFormula ); 4806 } 4807 4808 ScRangeData* ScCompiler::GetRangeData( const FormulaToken& rToken ) const 4809 { 4810 return pDoc->FindRangeNameBySheetAndIndex( rToken.GetSheet(), rToken.GetIndex()); 4811 } 4812 4813 bool ScCompiler::HandleRange() 4814 { 4815 ScTokenArray* pNew; 4816 const ScRangeData* pRangeData = GetRangeData( *mpToken); 4817 if (pRangeData) 4818 { 4819 FormulaError nErr = pRangeData->GetErrCode(); 4820 if( nErr != FormulaError::NONE ) 4821 SetError( nErr ); 4822 else if (mbJumpCommandReorder) 4823 { 4824 // put named formula into parentheses. 4825 // But only if there aren't any yet, parenthetical 4826 // ocSep doesn't work, e.g. SUM((...;...)) 4827 // or if not directly between ocSep/parenthesis, 4828 // e.g. SUM(...;(...;...)) no, SUM(...;(...)*3) yes, 4829 // in short: if it isn't a self-contained expression. 4830 FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces(); 4831 FormulaToken* p2 = maArrIterator.PeekNextNoSpaces(); 4832 OpCode eOp1 = (p1 ? p1->GetOpCode() : ocSep); 4833 OpCode eOp2 = (p2 ? p2->GetOpCode() : ocSep); 4834 bool bBorder1 = (eOp1 == ocSep || eOp1 == ocOpen); 4835 bool bBorder2 = (eOp2 == ocSep || eOp2 == ocClose); 4836 bool bAddPair = !(bBorder1 && bBorder2); 4837 if ( bAddPair ) 4838 { 4839 pNew = new ScTokenArray(pDoc); 4840 pNew->AddOpCode( ocClose ); 4841 PushTokenArray( pNew, true ); 4842 } 4843 pNew = pRangeData->GetCode()->Clone().release(); 4844 pNew->SetFromRangeName( true ); 4845 PushTokenArray( pNew, true ); 4846 if( pRangeData->HasReferences() ) 4847 { 4848 // Relative sheet references in sheet-local named expressions 4849 // shall still point to the same sheet as if used on the 4850 // original sheet, not shifted to the current position where 4851 // they are used. 4852 SCTAB nSheetTab = mpToken->GetSheet(); 4853 if (nSheetTab >= 0 && nSheetTab != aPos.Tab()) 4854 AdjustSheetLocalNameRelReferences( nSheetTab - aPos.Tab()); 4855 4856 SetRelNameReference(); 4857 MoveRelWrap(); 4858 } 4859 maArrIterator.Reset(); 4860 if ( bAddPair ) 4861 { 4862 pNew = new ScTokenArray(pDoc); 4863 pNew->AddOpCode( ocOpen ); 4864 PushTokenArray( pNew, true ); 4865 } 4866 return GetToken(); 4867 } 4868 } 4869 else 4870 { 4871 // No ScRangeData for an already compiled token can happen in BIFF .xls 4872 // import if the original range is not present in the document. 4873 pNew = new ScTokenArray(pDoc); 4874 pNew->Add( new FormulaErrorToken( FormulaError::NoName)); 4875 PushTokenArray( pNew, true ); 4876 return GetToken(); 4877 } 4878 return true; 4879 } 4880 4881 bool ScCompiler::HandleExternalReference(const FormulaToken& _aToken) 4882 { 4883 // Handle external range names. 4884 switch (_aToken.GetType()) 4885 { 4886 case svExternalSingleRef: 4887 case svExternalDoubleRef: 4888 break; 4889 case svExternalName: 4890 { 4891 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 4892 const OUString* pFile = pRefMgr->getExternalFileName(_aToken.GetIndex()); 4893 if (!pFile) 4894 { 4895 SetError(FormulaError::NoName); 4896 return true; 4897 } 4898 4899 OUString aName = _aToken.GetString().getString(); 4900 ScExternalRefCache::TokenArrayRef xNew = pRefMgr->getRangeNameTokens( 4901 _aToken.GetIndex(), aName, &aPos); 4902 4903 if (!xNew) 4904 { 4905 SetError(FormulaError::NoName); 4906 return true; 4907 } 4908 4909 ScTokenArray* pNew = xNew->Clone().release(); 4910 PushTokenArray( pNew, true); 4911 if (FormulaTokenArrayPlainIterator(*pNew).GetNextReference() != nullptr) 4912 { 4913 SetRelNameReference(); 4914 MoveRelWrap(); 4915 } 4916 maArrIterator.Reset(); 4917 return GetToken(); 4918 } 4919 default: 4920 OSL_FAIL("Wrong type for external reference!"); 4921 return false; 4922 } 4923 return true; 4924 } 4925 4926 void ScCompiler::AdjustSheetLocalNameRelReferences( SCTAB nDelta ) 4927 { 4928 for ( auto t: pArr->References() ) 4929 { 4930 ScSingleRefData& rRef1 = *t->GetSingleRef(); 4931 if (rRef1.IsTabRel()) 4932 rRef1.IncTab( nDelta); 4933 if ( t->GetType() == svDoubleRef ) 4934 { 4935 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; 4936 if (rRef2.IsTabRel()) 4937 rRef2.IncTab( nDelta); 4938 } 4939 } 4940 } 4941 4942 // reference of named range with relative references 4943 4944 void ScCompiler::SetRelNameReference() 4945 { 4946 for ( auto t: pArr->References() ) 4947 { 4948 ScSingleRefData& rRef1 = *t->GetSingleRef(); 4949 if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() ) 4950 rRef1.SetRelName( true ); 4951 if ( t->GetType() == svDoubleRef ) 4952 { 4953 ScSingleRefData& rRef2 = t->GetDoubleRef()->Ref2; 4954 if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() ) 4955 rRef2.SetRelName( true ); 4956 } 4957 } 4958 } 4959 4960 // Wrap-adjust relative references of a RangeName to current position, 4961 // don't call for other token arrays! 4962 void ScCompiler::MoveRelWrap() 4963 { 4964 for ( auto t: pArr->References() ) 4965 { 4966 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef ) 4967 ScRefUpdate::MoveRelWrap( pDoc, aPos, pDoc->MaxCol(), pDoc->MaxRow(), SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() ); 4968 else 4969 ScRefUpdate::MoveRelWrap( pDoc, aPos, pDoc->MaxCol(), pDoc->MaxRow(), *t->GetDoubleRef() ); 4970 } 4971 } 4972 4973 // Wrap-adjust relative references of a RangeName to current position, 4974 // don't call for other token arrays! 4975 void ScCompiler::MoveRelWrap( const ScTokenArray& rArr, const ScDocument* pDoc, const ScAddress& rPos, 4976 SCCOL nMaxCol, SCROW nMaxRow ) 4977 { 4978 for ( auto t: rArr.References() ) 4979 { 4980 if ( t->GetType() == svSingleRef || t->GetType() == svExternalSingleRef ) 4981 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, SingleDoubleRefModifier( *t->GetSingleRef() ).Ref() ); 4982 else 4983 ScRefUpdate::MoveRelWrap( pDoc, rPos, nMaxCol, nMaxRow, *t->GetDoubleRef() ); 4984 } 4985 } 4986 4987 bool ScCompiler::IsCharFlagAllConventions( 4988 OUString const & rStr, sal_Int32 nPos, ScCharFlags nFlags ) 4989 { 4990 sal_Unicode c = rStr[ nPos ]; 4991 sal_Unicode cLast = nPos > 0 ? rStr[ nPos-1 ] : 0; 4992 if (c < 128) 4993 { 4994 for ( int nConv = formula::FormulaGrammar::CONV_UNSPECIFIED; 4995 ++nConv < formula::FormulaGrammar::CONV_LAST; ) 4996 { 4997 if (pConventions[nConv] && 4998 ((pConventions[nConv]->getCharTableFlags(c, cLast) & nFlags) != nFlags)) 4999 return false; 5000 // convention not known => assume valid 5001 } 5002 return true; 5003 } 5004 else 5005 return ScGlobal::pCharClass->isLetterNumeric( rStr, nPos ); 5006 } 5007 5008 void ScCompiler::CreateStringFromExternal( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const 5009 { 5010 const FormulaToken* t = pTokenP; 5011 sal_uInt16 nFileId = t->GetIndex(); 5012 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager(); 5013 const OUString* pFileName = pRefMgr->getExternalFileName(nFileId); 5014 if (!pFileName) 5015 return; 5016 5017 switch (t->GetType()) 5018 { 5019 case svExternalName: 5020 rBuffer.append(pConv->makeExternalNameStr( nFileId, *pFileName, t->GetString().getString())); 5021 break; 5022 case svExternalSingleRef: 5023 pConv->makeExternalRefStr(pDoc->GetSheetLimits(), 5024 rBuffer, GetPos(), nFileId, *pFileName, t->GetString().getString(), 5025 *t->GetSingleRef()); 5026 break; 5027 case svExternalDoubleRef: 5028 { 5029 vector<OUString> aTabNames; 5030 pRefMgr->getAllCachedTableNames(nFileId, aTabNames); 5031 // No sheet names is a valid case if external sheets were not 5032 // cached in this document and external document is not reachable, 5033 // else not and worth to be investigated. 5034 SAL_WARN_IF( aTabNames.empty(), "sc.core", "wrecked cache of external document? '" << 5035 *pFileName << "' '" << t->GetString().getString() << "'"); 5036 5037 pConv->makeExternalRefStr( 5038 pDoc->GetSheetLimits(), rBuffer, GetPos(), nFileId, *pFileName, aTabNames, t->GetString().getString(), 5039 *t->GetDoubleRef()); 5040 } 5041 break; 5042 default: 5043 // warning, not error, otherwise we may end up with a never 5044 // ending message box loop if this was the cursor cell to be redrawn. 5045 OSL_FAIL("ScCompiler::CreateStringFromToken: unknown type of ocExternalRef"); 5046 } 5047 } 5048 5049 void ScCompiler::CreateStringFromMatrix( OUStringBuffer& rBuffer, const FormulaToken* pTokenP ) const 5050 { 5051 const ScMatrix* pMatrix = pTokenP->GetMatrix(); 5052 SCSIZE nC, nMaxC, nR, nMaxR; 5053 5054 pMatrix->GetDimensions( nMaxC, nMaxR); 5055 5056 rBuffer.append( mxSymbols->getSymbol(ocArrayOpen) ); 5057 for( nR = 0 ; nR < nMaxR ; nR++) 5058 { 5059 if( nR > 0) 5060 { 5061 rBuffer.append( mxSymbols->getSymbol(ocArrayRowSep) ); 5062 } 5063 5064 for( nC = 0 ; nC < nMaxC ; nC++) 5065 { 5066 if( nC > 0) 5067 { 5068 rBuffer.append( mxSymbols->getSymbol(ocArrayColSep) ); 5069 } 5070 5071 if( pMatrix->IsValue( nC, nR ) ) 5072 { 5073 if (pMatrix->IsBoolean(nC, nR)) 5074 AppendBoolean(rBuffer, pMatrix->GetDouble(nC, nR) != 0.0); 5075 else 5076 { 5077 FormulaError nErr = pMatrix->GetError(nC, nR); 5078 if (nErr != FormulaError::NONE) 5079 rBuffer.append(ScGlobal::GetErrorString(nErr)); 5080 else 5081 AppendDouble(rBuffer, pMatrix->GetDouble(nC, nR)); 5082 } 5083 } 5084 else if( pMatrix->IsEmpty( nC, nR ) ) 5085 ; 5086 else if( pMatrix->IsStringOrEmpty( nC, nR ) ) 5087 AppendString( rBuffer, pMatrix->GetString(nC, nR).getString() ); 5088 } 5089 } 5090 rBuffer.append( mxSymbols->getSymbol(ocArrayClose) ); 5091 } 5092 5093 namespace { 5094 void escapeTableRefColumnSpecifier( OUString& rStr ) 5095 { 5096 const sal_Int32 n = rStr.getLength(); 5097 OUStringBuffer aBuf( n * 2 ); 5098 const sal_Unicode* p = rStr.getStr(); 5099 const sal_Unicode* const pStop = p + n; 5100 for ( ; p < pStop; ++p) 5101 { 5102 const sal_Unicode c = *p; 5103 switch (c) 5104 { 5105 case '\'': 5106 case '[': 5107 case '#': 5108 case ']': 5109 aBuf.append( '\'' ); 5110 break; 5111 default: 5112 ; // nothing 5113 } 5114 aBuf.append( c ); 5115 } 5116 rStr = aBuf.makeStringAndClear(); 5117 } 5118 } 5119 5120 void ScCompiler::CreateStringFromSingleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const 5121 { 5122 const FormulaToken* p; 5123 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef); 5124 const OpCode eOp = _pTokenP->GetOpCode(); 5125 const ScSingleRefData& rRef = *_pTokenP->GetSingleRef(); 5126 ScComplexRefData aRef; 5127 aRef.Ref1 = aRef.Ref2 = rRef; 5128 if ( eOp == ocColRowName ) 5129 { 5130 ScAddress aAbs = rRef.toAbs(pDoc, aPos); 5131 if (pDoc->HasStringData(aAbs.Col(), aAbs.Row(), aAbs.Tab())) 5132 { 5133 OUString aStr = pDoc->GetString(aAbs, mpInterpreterContext); 5134 EnQuote( aStr ); 5135 rBuffer.append(aStr); 5136 } 5137 else 5138 { 5139 rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName)); 5140 pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef, 5141 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName())); 5142 } 5143 } 5144 else if (pArr && (p = maArrIterator.PeekPrevNoSpaces()) && p->GetOpCode() == ocTableRefOpen) 5145 { 5146 OUString aStr; 5147 ScAddress aAbs = rRef.toAbs(pDoc, aPos); 5148 const ScDBData* pData = pDoc->GetDBAtCursor( aAbs.Col(), aAbs.Row(), aAbs.Tab(), ScDBDataPortion::AREA); 5149 SAL_WARN_IF( !pData, "sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef without ScDBData: " << 5150 aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc)); 5151 if (pData) 5152 aStr = pData->GetTableColumnName( aAbs.Col()); 5153 if (aStr.isEmpty()) 5154 { 5155 if (pData && pData->HasHeader()) 5156 { 5157 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef falling back to cell: " << 5158 aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc)); 5159 aStr = pDoc->GetString(aAbs, mpInterpreterContext); 5160 } 5161 else 5162 { 5163 SAL_WARN("sc.core", "ScCompiler::CreateStringFromSingleRef - TableRef of empty header-less: " << 5164 aAbs.Format( ScRefFlags::VALID | ScRefFlags::TAB_3D, pDoc)); 5165 aStr = aErrRef; 5166 } 5167 } 5168 escapeTableRefColumnSpecifier( aStr); 5169 rBuffer.append(aStr); 5170 } 5171 else 5172 pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef, 5173 GetSetupTabNames(), aRef, true, (pArr && pArr->IsFromRangeName())); 5174 } 5175 5176 void ScCompiler::CreateStringFromDoubleRef( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const 5177 { 5178 OUString aErrRef = GetCurrentOpCodeMap()->getSymbol(ocErrRef); 5179 pConv->makeRefStr(pDoc->GetSheetLimits(), rBuffer, meGrammar, aPos, aErrRef, GetSetupTabNames(), 5180 *_pTokenP->GetDoubleRef(), false, (pArr && pArr->IsFromRangeName())); 5181 } 5182 5183 void ScCompiler::CreateStringFromIndex( OUStringBuffer& rBuffer, const FormulaToken* _pTokenP ) const 5184 { 5185 const OpCode eOp = _pTokenP->GetOpCode(); 5186 OUStringBuffer aBuffer; 5187 switch ( eOp ) 5188 { 5189 case ocName: 5190 { 5191 const ScRangeData* pData = GetRangeData( *_pTokenP); 5192 if (pData) 5193 { 5194 SCTAB nTab = _pTokenP->GetSheet(); 5195 if (nTab >= 0 && nTab != aPos.Tab()) 5196 { 5197 // Sheet-local on other sheet. 5198 OUString aName; 5199 if (pDoc->GetName( nTab, aName)) 5200 { 5201 ScCompiler::CheckTabQuotes( aName, pConv->meConv); 5202 aBuffer.append( aName); 5203 } 5204 else 5205 aBuffer.append(ScCompiler::GetNativeSymbol(ocErrName)); 5206 aBuffer.append( pConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR)); 5207 } 5208 aBuffer.append(pData->GetName()); 5209 } 5210 } 5211 break; 5212 case ocDBArea: 5213 { 5214 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex()); 5215 if (pDBData) 5216 aBuffer.append(pDBData->GetName()); 5217 } 5218 break; 5219 case ocTableRef: 5220 { 5221 if (NeedsTableRefTransformation()) 5222 { 5223 // Write the resulting reference if TableRef is not supported. 5224 const ScTableRefToken* pTR = dynamic_cast<const ScTableRefToken*>(_pTokenP); 5225 if (!pTR) 5226 AppendErrorConstant( aBuffer, FormulaError::NoCode); 5227 else 5228 { 5229 const FormulaToken* pRef = pTR->GetAreaRefRPN(); 5230 if (!pRef) 5231 AppendErrorConstant( aBuffer, FormulaError::NoCode); 5232 else 5233 { 5234 switch (pRef->GetType()) 5235 { 5236 case svSingleRef: 5237 CreateStringFromSingleRef( aBuffer, pRef); 5238 break; 5239 case svDoubleRef: 5240 CreateStringFromDoubleRef( aBuffer, pRef); 5241 break; 5242 case svError: 5243 AppendErrorConstant( aBuffer, pRef->GetError()); 5244 break; 5245 default: 5246 AppendErrorConstant( aBuffer, FormulaError::NoCode); 5247 } 5248 } 5249 } 5250 } 5251 else 5252 { 5253 const ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(_pTokenP->GetIndex()); 5254 if (pDBData) 5255 aBuffer.append(pDBData->GetName()); 5256 } 5257 } 5258 break; 5259 default: 5260 ; // nothing 5261 } 5262 if ( !aBuffer.isEmpty() ) 5263 rBuffer.append(aBuffer.makeStringAndClear()); 5264 else 5265 rBuffer.append(ScCompiler::GetNativeSymbol(ocErrName)); 5266 } 5267 5268 void ScCompiler::LocalizeString( OUString& rName ) const 5269 { 5270 ScGlobal::GetAddInCollection()->LocalizeString( rName ); 5271 } 5272 5273 // Put quotes around string if non-alphanumeric characters are contained, 5274 // quote characters contained within are escaped by '\\'. 5275 bool ScCompiler::EnQuote( OUString& rStr ) 5276 { 5277 sal_Int32 nType = ScGlobal::pCharClass->getStringType( rStr, 0, rStr.getLength() ); 5278 if ( !CharClass::isNumericType( nType ) 5279 && CharClass::isAlphaNumericType( nType ) ) 5280 return false; 5281 5282 sal_Int32 nPos = 0; 5283 while ( (nPos = rStr.indexOf( '\'', nPos)) != -1 ) 5284 { 5285 rStr = rStr.replaceAt( nPos, 0, "\\" ); 5286 nPos += 2; 5287 } 5288 rStr = "'" + rStr + "'"; 5289 return true; 5290 } 5291 5292 sal_Unicode ScCompiler::GetNativeAddressSymbol( Convention::SpecialSymbolType eType ) const 5293 { 5294 return pConv->getSpecialSymbol(eType); 5295 } 5296 5297 FormulaTokenRef ScCompiler::ExtendRangeReference( FormulaToken & rTok1, FormulaToken & rTok2 ) 5298 { 5299 return extendRangeReference( pDoc->GetSheetLimits(), rTok1, rTok2, aPos, true/*bReuseDoubleRef*/ ); 5300 } 5301 5302 void ScCompiler::fillAddInToken(::std::vector< css::sheet::FormulaOpCodeMapEntry >& _rVec,bool _bIsEnglish) const 5303 { 5304 // All known AddIn functions. 5305 sheet::FormulaOpCodeMapEntry aEntry; 5306 aEntry.Token.OpCode = ocExternal; 5307 5308 ScUnoAddInCollection* pColl = ScGlobal::GetAddInCollection(); 5309 const long nCount = pColl->GetFuncCount(); 5310 for (long i=0; i < nCount; ++i) 5311 { 5312 const ScUnoAddInFuncData* pFuncData = pColl->GetFuncData(i); 5313 if (pFuncData) 5314 { 5315 if ( _bIsEnglish ) 5316 { 5317 OUString aName; 5318 if (pFuncData->GetExcelName( LANGUAGE_ENGLISH_US, aName)) 5319 aEntry.Name = aName; 5320 else 5321 aEntry.Name = pFuncData->GetUpperName(); 5322 } 5323 else 5324 aEntry.Name = pFuncData->GetUpperLocal(); 5325 aEntry.Token.Data <<= pFuncData->GetOriginalName(); 5326 _rVec.push_back( aEntry); 5327 } 5328 } 5329 // FIXME: what about those old non-UNO AddIns? 5330 } 5331 5332 bool ScCompiler::HandleColRowName() 5333 { 5334 ScSingleRefData& rRef = *mpToken->GetSingleRef(); 5335 const ScAddress aAbs = rRef.toAbs(pDoc, aPos); 5336 if (!pDoc->ValidAddress(aAbs)) 5337 { 5338 SetError( FormulaError::NoRef ); 5339 return true; 5340 } 5341 SCCOL nCol = aAbs.Col(); 5342 SCROW nRow = aAbs.Row(); 5343 SCTAB nTab = aAbs.Tab(); 5344 bool bColName = rRef.IsColRel(); 5345 SCCOL nMyCol = aPos.Col(); 5346 SCROW nMyRow = aPos.Row(); 5347 bool bInList = false; 5348 bool bValidName = false; 5349 ScRangePairList* pRL = (bColName ? 5350 pDoc->GetColNameRanges() : pDoc->GetRowNameRanges()); 5351 ScRange aRange; 5352 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i ) 5353 { 5354 const ScRangePair & rR = (*pRL)[i]; 5355 if ( rR.GetRange(0).In( aAbs ) ) 5356 { 5357 bInList = bValidName = true; 5358 aRange = rR.GetRange(1); 5359 if ( bColName ) 5360 { 5361 aRange.aStart.SetCol( nCol ); 5362 aRange.aEnd.SetCol( nCol ); 5363 } 5364 else 5365 { 5366 aRange.aStart.SetRow( nRow ); 5367 aRange.aEnd.SetRow( nRow ); 5368 } 5369 break; // for 5370 } 5371 } 5372 if ( !bInList && pDoc->GetDocOptions().IsLookUpColRowNames() ) 5373 { // automagically or created by copying and NamePos isn't in list 5374 ScRefCellValue aCell(*pDoc, aAbs); 5375 bool bString = aCell.hasString(); 5376 if (!bString && aCell.isEmpty()) 5377 bString = true; // empty cell is ok 5378 if ( bString ) 5379 { // corresponds with ScInterpreter::ScColRowNameAuto() 5380 bValidName = true; 5381 if ( bColName ) 5382 { // ColName 5383 SCROW nStartRow = nRow + 1; 5384 if ( nStartRow > pDoc->MaxRow() ) 5385 nStartRow = pDoc->MaxRow(); 5386 SCROW nMaxRow = pDoc->MaxRow(); 5387 if ( nMyCol == nCol ) 5388 { // formula cell in same column 5389 if ( nMyRow == nStartRow ) 5390 { // take remainder under name cell 5391 nStartRow++; 5392 if ( nStartRow > pDoc->MaxRow() ) 5393 nStartRow = pDoc->MaxRow(); 5394 } 5395 else if ( nMyRow > nStartRow ) 5396 { // from name cell down to formula cell 5397 nMaxRow = nMyRow - 1; 5398 } 5399 } 5400 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i ) 5401 { // next defined ColNameRange below limits row 5402 const ScRangePair & rR = (*pRL)[i]; 5403 const ScRange& rRange = rR.GetRange(1); 5404 if ( rRange.aStart.Col() <= nCol && nCol <= rRange.aEnd.Col() ) 5405 { // identical column range 5406 SCROW nTmp = rRange.aStart.Row(); 5407 if ( nStartRow < nTmp && nTmp <= nMaxRow ) 5408 nMaxRow = nTmp - 1; 5409 } 5410 } 5411 aRange.aStart.Set( nCol, nStartRow, nTab ); 5412 aRange.aEnd.Set( nCol, nMaxRow, nTab ); 5413 } 5414 else 5415 { // RowName 5416 SCCOL nStartCol = nCol + 1; 5417 if ( nStartCol > pDoc->MaxCol() ) 5418 nStartCol = pDoc->MaxCol(); 5419 SCCOL nMaxCol = pDoc->MaxCol(); 5420 if ( nMyRow == nRow ) 5421 { // formula cell in same row 5422 if ( nMyCol == nStartCol ) 5423 { // take remainder right from name cell 5424 nStartCol++; 5425 if ( nStartCol > pDoc->MaxCol() ) 5426 nStartCol = pDoc->MaxCol(); 5427 } 5428 else if ( nMyCol > nStartCol ) 5429 { // from name cell right to formula cell 5430 nMaxCol = nMyCol - 1; 5431 } 5432 } 5433 for ( size_t i = 0, nPairs = pRL->size(); i < nPairs; ++i ) 5434 { // next defined RowNameRange to the right limits column 5435 const ScRangePair & rR = (*pRL)[i]; 5436 const ScRange& rRange = rR.GetRange(1); 5437 if ( rRange.aStart.Row() <= nRow && nRow <= rRange.aEnd.Row() ) 5438 { // identical row range 5439 SCCOL nTmp = rRange.aStart.Col(); 5440 if ( nStartCol < nTmp && nTmp <= nMaxCol ) 5441 nMaxCol = nTmp - 1; 5442 } 5443 } 5444 aRange.aStart.Set( nStartCol, nRow, nTab ); 5445 aRange.aEnd.Set( nMaxCol, nRow, nTab ); 5446 } 5447 } 5448 } 5449 if ( bValidName ) 5450 { 5451 // And now the magic to distinguish between a range and a single 5452 // cell thereof, which is picked position-dependent of the formula 5453 // cell. If a direct neighbor is a binary operator (ocAdd, ...) a 5454 // SingleRef matching the column/row of the formula cell is 5455 // generated. A ocColRowName or ocIntersect as a neighbor results 5456 // in a range. Special case: if label is valid for a single cell, a 5457 // position independent SingleRef is generated. 5458 bool bSingle = (aRange.aStart == aRange.aEnd); 5459 bool bFound; 5460 if ( bSingle ) 5461 bFound = true; 5462 else 5463 { 5464 FormulaToken* p1 = maArrIterator.PeekPrevNoSpaces(); 5465 FormulaToken* p2 = maArrIterator.PeekNextNoSpaces(); 5466 // begin/end of a formula => single 5467 OpCode eOp1 = p1 ? p1->GetOpCode() : ocAdd; 5468 OpCode eOp2 = p2 ? p2->GetOpCode() : ocAdd; 5469 if ( eOp1 != ocColRowName && eOp1 != ocIntersect 5470 && eOp2 != ocColRowName && eOp2 != ocIntersect ) 5471 { 5472 if ( (SC_OPCODE_START_BIN_OP <= eOp1 && eOp1 < SC_OPCODE_STOP_BIN_OP) || 5473 (SC_OPCODE_START_BIN_OP <= eOp2 && eOp2 < SC_OPCODE_STOP_BIN_OP)) 5474 bSingle = true; 5475 } 5476 if ( bSingle ) 5477 { // column and/or row must match range 5478 if ( bColName ) 5479 { 5480 bFound = (aRange.aStart.Row() <= nMyRow 5481 && nMyRow <= aRange.aEnd.Row()); 5482 if ( bFound ) 5483 aRange.aStart.SetRow( nMyRow ); 5484 } 5485 else 5486 { 5487 bFound = (aRange.aStart.Col() <= nMyCol 5488 && nMyCol <= aRange.aEnd.Col()); 5489 if ( bFound ) 5490 aRange.aStart.SetCol( nMyCol ); 5491 } 5492 } 5493 else 5494 bFound = true; 5495 } 5496 if ( !bFound ) 5497 SetError(FormulaError::NoRef); 5498 else if (mbJumpCommandReorder) 5499 { 5500 ScTokenArray* pNew = new ScTokenArray(pDoc); 5501 if ( bSingle ) 5502 { 5503 ScSingleRefData aRefData; 5504 aRefData.InitAddress( aRange.aStart ); 5505 if ( bColName ) 5506 aRefData.SetColRel( true ); 5507 else 5508 aRefData.SetRowRel( true ); 5509 aRefData.SetAddress(pDoc->GetSheetLimits(), aRange.aStart, aPos); 5510 pNew->AddSingleReference( aRefData ); 5511 } 5512 else 5513 { 5514 ScComplexRefData aRefData; 5515 aRefData.InitRange( aRange ); 5516 if ( bColName ) 5517 { 5518 aRefData.Ref1.SetColRel( true ); 5519 aRefData.Ref2.SetColRel( true ); 5520 } 5521 else 5522 { 5523 aRefData.Ref1.SetRowRel( true ); 5524 aRefData.Ref2.SetRowRel( true ); 5525 } 5526 aRefData.SetRange(pDoc->GetSheetLimits(), aRange, aPos); 5527 if ( bInList ) 5528 pNew->AddDoubleReference( aRefData ); 5529 else 5530 { // automagically 5531 pNew->Add( new ScDoubleRefToken( pDoc->GetSheetLimits(), aRefData, ocColRowNameAuto ) ); 5532 } 5533 } 5534 PushTokenArray( pNew, true ); 5535 return GetToken(); 5536 } 5537 } 5538 else 5539 SetError(FormulaError::NoName); 5540 return true; 5541 } 5542 5543 bool ScCompiler::HandleDbData() 5544 { 5545 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex(mpToken->GetIndex()); 5546 if ( !pDBData ) 5547 SetError(FormulaError::NoName); 5548 else if (mbJumpCommandReorder) 5549 { 5550 ScComplexRefData aRefData; 5551 aRefData.InitFlags(); 5552 ScRange aRange; 5553 pDBData->GetArea(aRange); 5554 aRange.aEnd.SetTab(aRange.aStart.Tab()); 5555 aRefData.SetRange(pDoc->GetSheetLimits(), aRange, aPos); 5556 ScTokenArray* pNew = new ScTokenArray(pDoc); 5557 pNew->AddDoubleReference( aRefData ); 5558 PushTokenArray( pNew, true ); 5559 return GetToken(); 5560 } 5561 return true; 5562 } 5563 5564 bool ScCompiler::GetTokenIfOpCode( OpCode eOp ) 5565 { 5566 const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces(); 5567 if (p && p->GetOpCode() == eOp) 5568 return GetToken(); 5569 return false; 5570 } 5571 5572 5573 /* Documentation on MS-Excel Table structured references: 5574 * https://support.office.com/en-us/article/Use-structured-references-in-Excel-table-formulas-75fb07d3-826a-449c-b76f-363057e3d16f 5575 * * as of Excel 2013 5576 * [MS-XLSX]: Formulas https://msdn.microsoft.com/en-us/library/dd906358.aspx 5577 * * look for structure-reference 5578 */ 5579 5580 bool ScCompiler::HandleTableRef() 5581 { 5582 ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(mpToken.get()); 5583 if (!pTR) 5584 { 5585 SetError(FormulaError::UnknownToken); 5586 return true; 5587 } 5588 5589 ScDBData* pDBData = pDoc->GetDBCollection()->getNamedDBs().findByIndex( pTR->GetIndex()); 5590 if ( !pDBData ) 5591 SetError(FormulaError::NoName); 5592 else if (mbJumpCommandReorder) 5593 { 5594 ScRange aDBRange; 5595 pDBData->GetArea(aDBRange); 5596 aDBRange.aEnd.SetTab(aDBRange.aStart.Tab()); 5597 ScRange aRange( aDBRange); 5598 FormulaError nError = FormulaError::NONE; 5599 bool bForwardToClose = false; 5600 ScTableRefToken::Item eItem = pTR->GetItem(); 5601 switch (eItem) 5602 { 5603 case ScTableRefToken::TABLE: 5604 { 5605 // The table name without items references the table data, 5606 // without headers or totals. 5607 if (pDBData->HasHeader()) 5608 aRange.aStart.IncRow(); 5609 if (pDBData->HasTotals()) 5610 aRange.aEnd.IncRow(-1); 5611 if (aRange.aEnd.Row() < aRange.aStart.Row()) 5612 nError = FormulaError::NoRef; 5613 bForwardToClose = true; 5614 } 5615 break; 5616 case ScTableRefToken::ALL: 5617 { 5618 bForwardToClose = true; 5619 } 5620 break; 5621 case ScTableRefToken::HEADERS: 5622 { 5623 if (pDBData->HasHeader()) 5624 aRange.aEnd.SetRow( aRange.aStart.Row()); 5625 else 5626 nError = FormulaError::NoRef; 5627 bForwardToClose = true; 5628 } 5629 break; 5630 case ScTableRefToken::DATA: 5631 { 5632 if (pDBData->HasHeader()) 5633 aRange.aStart.IncRow(); 5634 } 5635 [[fallthrough]]; 5636 case ScTableRefToken::HEADERS_DATA: 5637 { 5638 if (pDBData->HasTotals()) 5639 aRange.aEnd.IncRow(-1); 5640 if (aRange.aEnd.Row() < aRange.aStart.Row()) 5641 nError = FormulaError::NoRef; 5642 bForwardToClose = true; 5643 } 5644 break; 5645 case ScTableRefToken::TOTALS: 5646 { 5647 if (pDBData->HasTotals()) 5648 aRange.aStart.SetRow( aRange.aEnd.Row()); 5649 else 5650 nError = FormulaError::NoRef; 5651 bForwardToClose = true; 5652 } 5653 break; 5654 case ScTableRefToken::DATA_TOTALS: 5655 { 5656 if (pDBData->HasHeader()) 5657 aRange.aStart.IncRow(); 5658 if (aRange.aEnd.Row() < aRange.aStart.Row()) 5659 nError = FormulaError::NoRef; 5660 bForwardToClose = true; 5661 } 5662 break; 5663 case ScTableRefToken::THIS_ROW: 5664 { 5665 if (aRange.aStart.Row() <= aPos.Row() && aPos.Row() <= aRange.aEnd.Row()) 5666 { 5667 aRange.aStart.SetRow( aPos.Row()); 5668 aRange.aEnd.SetRow( aPos.Row()); 5669 } 5670 else 5671 { 5672 nError = FormulaError::NoValue; 5673 // For *some* relative row reference in named 5674 // expressions' thisrow special handling below. 5675 aRange.aEnd.SetRow( aRange.aStart.Row()); 5676 } 5677 bForwardToClose = true; 5678 } 5679 break; 5680 } 5681 bool bColumnRange = false; 5682 bool bCol1Rel = false; 5683 bool bCol1RelName = false; 5684 int nLevel = 0; 5685 if (bForwardToClose && GetTokenIfOpCode( ocTableRefOpen)) 5686 { 5687 ++nLevel; 5688 enum 5689 { 5690 sOpen, 5691 sItem, 5692 sClose, 5693 sSep, 5694 sLast, 5695 sStop 5696 } eState = sOpen; 5697 do 5698 { 5699 const formula::FormulaToken* p = maArrIterator.PeekNextNoSpaces(); 5700 if (!p) 5701 eState = sStop; 5702 else 5703 { 5704 switch (p->GetOpCode()) 5705 { 5706 case ocTableRefOpen: 5707 eState = ((eState == sOpen || eState == sSep) ? sOpen : sStop); 5708 if (++nLevel > 2) 5709 { 5710 SetError( FormulaError::Pair); 5711 eState = sStop; 5712 } 5713 break; 5714 case ocTableRefItemAll: 5715 case ocTableRefItemHeaders: 5716 case ocTableRefItemData: 5717 case ocTableRefItemTotals: 5718 case ocTableRefItemThisRow: 5719 eState = ((eState == sOpen) ? sItem : sStop); 5720 break; 5721 case ocTableRefClose: 5722 eState = ((eState == sItem || eState == sClose) ? sClose : sStop); 5723 if (eState != sStop && --nLevel == 0) 5724 eState = sLast; 5725 break; 5726 case ocSep: 5727 eState = ((eState == sClose) ? sSep : sStop); 5728 break; 5729 case ocPush: 5730 if (eState == sOpen && p->GetType() == svSingleRef) 5731 { 5732 bColumnRange = true; 5733 bCol1Rel = p->GetSingleRef()->IsColRel(); 5734 bCol1RelName = p->GetSingleRef()->IsRelName(); 5735 eState = sLast; 5736 } 5737 else 5738 { 5739 eState = sStop; 5740 } 5741 break; 5742 case ocBad: 5743 eState = sLast; 5744 if (nError == FormulaError::NONE) 5745 nError = FormulaError::NoName; 5746 break; 5747 default: 5748 eState = sStop; 5749 } 5750 if (eState != sStop) 5751 GetToken(); 5752 if (eState == sLast) 5753 eState = sStop; 5754 } 5755 } while (eState != sStop); 5756 } 5757 ScTokenArray* pNew = new ScTokenArray(pDoc); 5758 if (nError == FormulaError::NONE || nError == FormulaError::NoValue) 5759 { 5760 bool bCol2Rel = false; 5761 bool bCol2RelName = false; 5762 // The FormulaError::NoValue case generates a thisrow reference that can be 5763 // used to save named expressions in A1 syntax notation. 5764 if (bColumnRange) 5765 { 5766 // Limit range to specified columns. 5767 ScRange aColRange( ScAddress::INITIALIZE_INVALID ); 5768 switch (mpToken->GetType()) 5769 { 5770 case svSingleRef: 5771 { 5772 aColRange.aStart = aColRange.aEnd = mpToken->GetSingleRef()->toAbs(pDoc, aPos); 5773 if ( GetTokenIfOpCode( ocTableRefClose) && (nLevel--) && 5774 GetTokenIfOpCode( ocRange) && 5775 GetTokenIfOpCode( ocTableRefOpen) && (++nLevel) && 5776 GetTokenIfOpCode( ocPush)) 5777 { 5778 if (mpToken->GetType() != svSingleRef) 5779 aColRange = ScRange( ScAddress::INITIALIZE_INVALID); 5780 else 5781 { 5782 aColRange.aEnd = mpToken->GetSingleRef()->toAbs(pDoc, aPos); 5783 aColRange.PutInOrder(); 5784 bCol2Rel = mpToken->GetSingleRef()->IsColRel(); 5785 bCol2RelName = mpToken->GetSingleRef()->IsRelName(); 5786 } 5787 } 5788 } 5789 break; 5790 default: 5791 ; // nothing 5792 } 5793 // coverity[copy_paste_error : FALSE] - this is correct, aStart in both aDBRange uses 5794 if (aColRange.aStart.Row() != aDBRange.aStart.Row() || aColRange.aEnd.Row() != aDBRange.aStart.Row()) 5795 aRange = ScRange( ScAddress::INITIALIZE_INVALID); 5796 else 5797 { 5798 aColRange.aEnd.SetRow( aRange.aEnd.Row()); 5799 aRange = aRange.Intersection( aColRange); 5800 } 5801 } 5802 if (aRange.IsValid()) 5803 { 5804 if (aRange.aStart == aRange.aEnd) 5805 { 5806 ScSingleRefData aRefData; 5807 aRefData.InitFlags(); 5808 aRefData.SetColRel( bCol1Rel); 5809 if (eItem == ScTableRefToken::THIS_ROW) 5810 { 5811 aRefData.SetRowRel( true); 5812 if (!bCol1RelName) 5813 bCol1RelName = pArr->IsFromRangeName(); 5814 } 5815 aRefData.SetRelName( bCol1RelName); 5816 aRefData.SetFlag3D( true); 5817 if (nError != FormulaError::NONE) 5818 { 5819 aRefData.SetAddress( pDoc->GetSheetLimits(), aRange.aStart, aRange.aStart); 5820 pTR->SetAreaRefRPN( new ScSingleRefToken(pDoc->GetSheetLimits(), aRefData)); // set reference at TableRef 5821 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN 5822 } 5823 else 5824 { 5825 aRefData.SetAddress( pDoc->GetSheetLimits(), aRange.aStart, aPos); 5826 pTR->SetAreaRefRPN( pNew->AddSingleReference( aRefData)); 5827 } 5828 } 5829 else 5830 { 5831 ScComplexRefData aRefData; 5832 aRefData.InitFlags(); 5833 aRefData.Ref1.SetColRel( bCol1Rel); 5834 aRefData.Ref2.SetColRel( bCol2Rel); 5835 bool bRelName = bCol1RelName || bCol2RelName; 5836 if (eItem == ScTableRefToken::THIS_ROW) 5837 { 5838 aRefData.Ref1.SetRowRel( true); 5839 aRefData.Ref2.SetRowRel( true); 5840 if (!bRelName) 5841 bRelName = pArr->IsFromRangeName(); 5842 } 5843 aRefData.Ref1.SetRelName( bRelName); 5844 aRefData.Ref2.SetRelName( bRelName); 5845 aRefData.Ref1.SetFlag3D( true); 5846 if (nError != FormulaError::NONE) 5847 { 5848 aRefData.SetRange( pDoc->GetSheetLimits(), aRange, aRange.aStart); 5849 pTR->SetAreaRefRPN( new ScDoubleRefToken(pDoc->GetSheetLimits(), aRefData)); // set reference at TableRef 5850 pNew->Add( new FormulaErrorToken( nError)); // set error in RPN 5851 } 5852 else 5853 { 5854 aRefData.SetRange( pDoc->GetSheetLimits(), aRange, aPos); 5855 pTR->SetAreaRefRPN( pNew->AddDoubleReference( aRefData)); 5856 } 5857 } 5858 } 5859 else 5860 { 5861 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( FormulaError::NoRef))); 5862 } 5863 } 5864 else 5865 { 5866 pTR->SetAreaRefRPN( pNew->Add( new FormulaErrorToken( nError))); 5867 } 5868 while (nLevel-- > 0) 5869 { 5870 if (!GetTokenIfOpCode( ocTableRefClose)) 5871 SetError( FormulaError::Pair); 5872 } 5873 PushTokenArray( pNew, true ); 5874 return GetToken(); 5875 } 5876 return true; 5877 } 5878 5879 formula::ParamClass ScCompiler::GetForceArrayParameter( const formula::FormulaToken* pToken, sal_uInt16 nParam ) const 5880 { 5881 return ScParameterClassification::GetParameterType( pToken, nParam); 5882 } 5883 5884 bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken* token, int parameter) 5885 { 5886 formula::ParamClass param = ScParameterClassification::GetParameterType( token, parameter ); 5887 return param == Value || param == Array; 5888 } 5889 5890 bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* token) const 5891 { 5892 if (mbMatrixFlag) 5893 return true; 5894 formula::ParamClass paramClass = token->GetInForceArray(); 5895 if (paramClass == formula::ForceArray 5896 || paramClass == formula::ReferenceOrForceArray 5897 || paramClass == formula::SuppressedReferenceOrForceArray 5898 || paramClass == formula::ReferenceOrRefArray) 5899 { 5900 return true; 5901 } 5902 formula::ParamClass returnType = ScParameterClassification::GetParameterType( token, SAL_MAX_UINT16 ); 5903 return returnType == formula::Reference; 5904 } 5905 5906 void ScCompiler::HandleIIOpCode(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams) 5907 { 5908 if (!mbComputeII) 5909 return; 5910 #ifdef DBG_UTIL 5911 if(!HandleIIOpCodeInternal(token, pppToken, nNumParams)) 5912 mUnhandledPossibleImplicitIntersectionsOpCodes.insert(token->GetOpCode()); 5913 #else 5914 HandleIIOpCodeInternal(token, pppToken, nNumParams); 5915 #endif 5916 } 5917 5918 // return true if opcode is handled 5919 bool ScCompiler::HandleIIOpCodeInternal(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams) 5920 { 5921 if (nNumParams > 0 && *pppToken[0] == nullptr) 5922 return false; // Bad expression (see the dummy creation in FormulaCompiler::CompileTokenArray()) 5923 5924 const OpCode nOpCode = token->GetOpCode(); 5925 5926 if (nOpCode == ocPush) 5927 { 5928 if(token->GetType() == svDoubleRef) 5929 mUnhandledPossibleImplicitIntersections.insert( token ); 5930 return true; 5931 } 5932 else if (nOpCode == ocSumIf || nOpCode == ocAverageIf) 5933 { 5934 if (nNumParams != 3) 5935 return false; 5936 5937 if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2])) 5938 return false; 5939 5940 if ((*pppToken[0])->GetType() != svDoubleRef) 5941 return false; 5942 5943 const StackVar eSumRangeType = (*pppToken[2])->GetType(); 5944 5945 if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef ) 5946 return false; 5947 5948 const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef(); 5949 5950 ScComplexRefData aSumRange; 5951 if (eSumRangeType == svSingleRef) 5952 { 5953 aSumRange.Ref1 = *(*pppToken[2])->GetSingleRef(); 5954 aSumRange.Ref2 = aSumRange.Ref1; 5955 } 5956 else 5957 aSumRange = *(*pppToken[2])->GetDoubleRef(); 5958 5959 CorrectSumRange(rBaseRange, aSumRange, pppToken[2]); 5960 // TODO mark parameters as handled 5961 return true; 5962 } 5963 else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR) 5964 { 5965 if (nNumParams != 1) 5966 return false; 5967 5968 if( !ParameterMayBeImplicitIntersection( token, 0 )) 5969 return false; 5970 if (SkipImplicitIntersectionOptimization(token)) 5971 return false; 5972 5973 if ((*pppToken[0])->GetType() != svDoubleRef) 5974 return false; 5975 5976 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token ); 5977 return true; 5978 } 5979 else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP) 5980 || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == ocRoundDown) 5981 { 5982 if (nNumParams != 2) 5983 return false; 5984 5985 if( !ParameterMayBeImplicitIntersection( token, 0 ) || !ParameterMayBeImplicitIntersection( token, 1 )) 5986 return false; 5987 if (SkipImplicitIntersectionOptimization(token)) 5988 return false; 5989 5990 // Convert only if the other parameter is not a matrix (which would force the result to be a matrix). 5991 if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix) 5992 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token ); 5993 if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix) 5994 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[1], token ); 5995 return true; 5996 } 5997 else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < SC_OPCODE_STOP_UN_OP) 5998 || nOpCode == ocPercentSign) 5999 { 6000 if (nNumParams != 1) 6001 return false; 6002 6003 if( !ParameterMayBeImplicitIntersection( token, 0 )) 6004 return false; 6005 if (SkipImplicitIntersectionOptimization(token)) 6006 return false; 6007 6008 if ((*pppToken[0])->GetType() == svDoubleRef) 6009 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token ); 6010 return true; 6011 } 6012 else if (nOpCode == ocVLookup) 6013 { 6014 if (nNumParams != 3 && nNumParams != 4) 6015 return false; 6016 6017 if (SkipImplicitIntersectionOptimization(token)) 6018 return false; 6019 6020 assert( ParameterMayBeImplicitIntersection( token, 0 )); 6021 assert( !ParameterMayBeImplicitIntersection( token, 1 )); 6022 assert( ParameterMayBeImplicitIntersection( token, 2 )); 6023 assert( ParameterMayBeImplicitIntersection( token, 3 )); 6024 if ((*pppToken[2])->GetType() == svDoubleRef) 6025 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[2], token ); 6026 if ((*pppToken[0])->GetType() == svDoubleRef) 6027 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token ); 6028 if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef) 6029 mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[3], token ); 6030 // a range for the second parameters is not an implicit intersection 6031 mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] ); 6032 return true; 6033 } 6034 else 6035 { 6036 bool possibleII = false; 6037 for( int i = 0; i < nNumParams; ++i ) 6038 { 6039 if( ParameterMayBeImplicitIntersection( token, i )) 6040 { 6041 possibleII = true; 6042 break; 6043 } 6044 } 6045 if( !possibleII ) 6046 { 6047 // all parameters have been handled, they are not implicit intersections 6048 for( int i = 0; i < nNumParams; ++i ) 6049 mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] ); 6050 return true; 6051 } 6052 } 6053 6054 return false; 6055 } 6056 6057 void ScCompiler::PostProcessCode() 6058 { 6059 for( const PendingImplicitIntersectionOptimization& item : mPendingImplicitIntersectionOptimizations ) 6060 { 6061 if( *item.parameterLocation != item.parameter ) // the parameter has been changed somehow 6062 continue; 6063 if( item.parameterLocation >= pCode ) // the location is not inside the code (pCode points after the end) 6064 continue; 6065 // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection. 6066 if( item.operation->IsInForceArray()) 6067 continue; 6068 ReplaceDoubleRefII( item.parameterLocation ); 6069 } 6070 mPendingImplicitIntersectionOptimizations.clear(); 6071 } 6072 6073 void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok) 6074 { 6075 const ScComplexRefData* pRange = (*ppDoubleRefTok)->GetDoubleRef(); 6076 if (!pRange) 6077 return; 6078 6079 const ScComplexRefData& rRange = *pRange; 6080 6081 // Can't do optimization reliably in this case (when row references are absolute). 6082 // Example : =SIN(A$1:A$10) filled in a formula group starting at B5 and of length 100. 6083 // If we just optimize the argument $A$1:$A$10 to singleref "A5" for the top cell in the fg, then 6084 // the results in cells B11:B104 will be incorrect (sin(0) = 0, assuming empty cells in A11:A104) 6085 // instead of the #VALUE! errors we would expect. We need to know the formula-group length to 6086 // fix this, but that is unknown at this stage, so skip such cases. 6087 if (!rRange.Ref1.IsRowRel() && !rRange.Ref2.IsRowRel()) 6088 return; 6089 6090 ScRange aAbsRange = rRange.toAbs(pDoc, aPos); 6091 if (aAbsRange.aStart == aAbsRange.aEnd) 6092 return; // Nothing to do (trivial case). 6093 6094 ScAddress aAddr; 6095 6096 if (!DoubleRefToPosSingleRefScalarCase(aAbsRange, aAddr, aPos)) 6097 return; 6098 6099 ScSingleRefData aSingleRef; 6100 aSingleRef.InitFlags(); 6101 aSingleRef.SetColRel(rRange.Ref1.IsColRel()); 6102 aSingleRef.SetRowRel(true); 6103 aSingleRef.SetTabRel(rRange.Ref1.IsTabRel()); 6104 aSingleRef.SetAddress(pDoc->GetSheetLimits(), aAddr, aPos); 6105 6106 // Replace the original doubleref token with computed singleref token 6107 FormulaToken* pNewSingleRefTok = new ScSingleRefToken(pDoc->GetSheetLimits(), aSingleRef); 6108 (*ppDoubleRefTok)->DecRef(); 6109 *ppDoubleRefTok = pNewSingleRefTok; 6110 pNewSingleRefTok->IncRef(); 6111 } 6112 6113 bool ScCompiler::DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr, const ScAddress& rFormulaPos) 6114 { 6115 assert(rRange.aStart != rRange.aEnd); 6116 6117 bool bOk = false; 6118 SCCOL nMyCol = rFormulaPos.Col(); 6119 SCROW nMyRow = rFormulaPos.Row(); 6120 SCTAB nMyTab = rFormulaPos.Tab(); 6121 SCCOL nCol = 0; 6122 SCROW nRow = 0; 6123 SCTAB nTab; 6124 nTab = rRange.aStart.Tab(); 6125 if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() ) 6126 { 6127 nRow = rRange.aStart.Row(); 6128 if ( nRow == rRange.aEnd.Row() ) 6129 { 6130 bOk = true; 6131 nCol = nMyCol; 6132 } 6133 else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab() 6134 && rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() ) 6135 { 6136 bOk = true; 6137 nCol = nMyCol; 6138 nRow = nMyRow; 6139 } 6140 } 6141 else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() ) 6142 { 6143 nCol = rRange.aStart.Col(); 6144 if ( nCol == rRange.aEnd.Col() ) 6145 { 6146 bOk = true; 6147 nRow = nMyRow; 6148 } 6149 else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab() 6150 && rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() ) 6151 { 6152 bOk = true; 6153 nCol = nMyCol; 6154 nRow = nMyRow; 6155 } 6156 } 6157 if ( bOk ) 6158 { 6159 if ( nTab == rRange.aEnd.Tab() ) 6160 ; // all done 6161 else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() ) 6162 nTab = nMyTab; 6163 else 6164 bOk = false; 6165 if ( bOk ) 6166 rAdr.Set( nCol, nRow, nTab ); 6167 } 6168 6169 return bOk; 6170 } 6171 6172 static void lcl_GetColRowDeltas(const ScRange& rRange, SCCOL& rXDelta, SCROW& rYDelta) 6173 { 6174 rXDelta = rRange.aEnd.Col() - rRange.aStart.Col(); 6175 rYDelta = rRange.aEnd.Row() - rRange.aStart.Row(); 6176 } 6177 6178 bool ScCompiler::AdjustSumRangeShape(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange) 6179 { 6180 ScRange aAbs = rSumRange.toAbs(pDoc, aPos); 6181 6182 // Current sum-range end col/row 6183 SCCOL nEndCol = aAbs.aEnd.Col(); 6184 SCROW nEndRow = aAbs.aEnd.Row(); 6185 6186 // Current behaviour is, we will get a #NAME? for the below case, so bail out. 6187 // Note that sum-range's End[Col,Row] are same as Start[Col,Row] if the original formula 6188 // has a single-ref as the sum-range. 6189 if (!pDoc->ValidCol(nEndCol) || !pDoc->ValidRow(nEndRow)) 6190 return false; 6191 6192 SCCOL nXDeltaSum = 0; 6193 SCROW nYDeltaSum = 0; 6194 6195 lcl_GetColRowDeltas(aAbs, nXDeltaSum, nYDeltaSum); 6196 6197 aAbs = rBaseRange.toAbs(pDoc, aPos); 6198 SCCOL nXDelta = 0; 6199 SCROW nYDelta = 0; 6200 6201 lcl_GetColRowDeltas(aAbs, nXDelta, nYDelta); 6202 6203 if (nXDelta == nXDeltaSum && 6204 nYDelta == nYDeltaSum) 6205 return false; // shapes of base-range match current sum-range 6206 6207 // Try to make the sum-range to take the same shape as base-range, 6208 // by adjusting Ref2 member of rSumRange if the resultant sum-range don't 6209 // go out-of-bounds. 6210 6211 SCCOL nXInc = nXDelta - nXDeltaSum; 6212 SCROW nYInc = nYDelta - nYDeltaSum; 6213 6214 // Don't let a valid End[Col,Row] go beyond (pDoc->MaxCol(),pDoc->MaxRow()) to match 6215 // what happens in ScInterpreter::IterateParametersIf(), but there it also shrinks 6216 // the base-range by the (out-of-bound)amount clipped off the sum-range. 6217 // TODO: Probably we can optimize (from threading perspective) rBaseRange 6218 // by shrinking it here correspondingly (?) 6219 if (nEndCol + nXInc > pDoc->MaxCol()) 6220 nXInc = pDoc->MaxCol() - nEndCol; 6221 if (nEndRow + nYInc > pDoc->MaxRow()) 6222 nYInc = pDoc->MaxRow() - nEndRow; 6223 6224 rSumRange.Ref2.IncCol(nXInc); 6225 rSumRange.Ref2.IncRow(nYInc); 6226 6227 return true; 6228 } 6229 6230 void ScCompiler::CorrectSumRange(const ScComplexRefData& rBaseRange, 6231 ScComplexRefData& rSumRange, 6232 FormulaToken** ppSumRangeToken) 6233 { 6234 if (!AdjustSumRangeShape(rBaseRange, rSumRange)) 6235 return; 6236 6237 // Replace sum-range token 6238 FormulaToken* pNewSumRangeTok = new ScDoubleRefToken(pDoc->GetSheetLimits(), rSumRange); 6239 (*ppSumRangeToken)->DecRef(); 6240 *ppSumRangeToken = pNewSumRangeTok; 6241 pNewSumRangeTok->IncRef(); 6242 } 6243 6244 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 6245
