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