1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_formula.hxx" 26 27 28 29 // INCLUDE --------------------------------------------------------------- 30 31 #include <cstddef> 32 #include <cstdio> 33 34 #include <string.h> 35 #include <limits.h> 36 #include <tools/debug.hxx> 37 38 #include "formula/token.hxx" 39 #include "formula/tokenarray.hxx" 40 #include "formula/FormulaCompiler.hxx" 41 #include <formula/compiler.hrc> 42 //#include "rechead.hxx" 43 //#include "parclass.hxx" 44 //#include "jumpmatrix.hxx" 45 #define MAXJUMPCOUNT 32 /* maximum number of jumps (ocChose) */ 46 47 namespace formula 48 { 49 using namespace com::sun::star; 50 // ImpTokenIterator wird je Interpreter angelegt, mehrfache auch durch 51 // SubCode via FormulaTokenIterator Push/Pop moeglich 52 IMPL_FIXEDMEMPOOL_NEWDEL( ImpTokenIterator, 32, 16 ) 53 54 // Align MemPools on 4k boundaries - 64 bytes (4k is a MUST for OS/2) 55 56 // Need a lot of FormulaDoubleToken 57 const sal_uInt16 nMemPoolDoubleToken = (0x3000 - 64) / sizeof(FormulaDoubleToken); 58 IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaDoubleToken, nMemPoolDoubleToken, nMemPoolDoubleToken ) 59 // Need a lot of FormulaByteToken 60 const sal_uInt16 nMemPoolByteToken = (0x3000 - 64) / sizeof(FormulaByteToken); 61 IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaByteToken, nMemPoolByteToken, nMemPoolByteToken ) 62 // Need several FormulaStringToken 63 const sal_uInt16 nMemPoolStringToken = (0x1000 - 64) / sizeof(FormulaStringToken); 64 IMPL_FIXEDMEMPOOL_NEWDEL_DLL( FormulaStringToken, nMemPoolStringToken, nMemPoolStringToken ) 65 66 67 // --- helpers -------------------------------------------------------------- 68 69 inline sal_Bool lcl_IsReference( OpCode eOp, StackVar eType ) 70 { 71 return 72 (eOp == ocPush && (eType == svSingleRef || eType == svDoubleRef)) 73 || (eOp == ocColRowNameAuto && eType == svDoubleRef) 74 || (eOp == ocColRowName && eType == svSingleRef) 75 || (eOp == ocMatRef && eType == svSingleRef) 76 ; 77 } 78 79 // --- class FormulaToken -------------------------------------------------------- 80 FormulaToken::~FormulaToken() 81 { 82 } 83 84 sal_Bool FormulaToken::Is3DRef() const 85 { 86 return sal_False; 87 } 88 89 sal_Bool FormulaToken::IsFunction() const 90 { 91 // OpCode eOp = GetOpCode(); 92 return (eOp != ocPush && eOp != ocBad && eOp != ocColRowName && 93 eOp != ocColRowNameAuto && eOp != ocName && eOp != ocDBArea && 94 (GetByte() != 0 // x parameters 95 || (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) // no parameter 96 || (ocIf == eOp || ocChose == eOp ) // @ jump commands 97 || (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) // one parameter 98 || (SC_OPCODE_START_2_PAR <= eOp && eOp < SC_OPCODE_STOP_2_PAR) // x parameters (cByte==0 in 99 // FuncAutoPilot) 100 || eOp == ocMacro || eOp == ocExternal // macros, AddIns 101 || eOp == ocAnd || eOp == ocOr // former binary, now x parameters 102 || eOp == ocNot || eOp == ocNeg // unary but function 103 || (eOp >= ocInternalBegin && eOp <= ocInternalEnd) // internal 104 )); 105 } 106 107 108 sal_uInt8 FormulaToken::GetParamCount() const 109 { 110 // OpCode eOp = GetOpCode(); 111 if ( eOp < SC_OPCODE_STOP_DIV && eOp != ocExternal && eOp != ocMacro && 112 eOp != ocIf && eOp != ocChose && eOp != ocPercentSign ) 113 return 0; // parameters and specials 114 // ocIf and ocChose not for FAP, have cByte then 115 //2do: sal_Bool parameter whether FAP or not? 116 else if ( GetByte() ) 117 return GetByte(); // all functions, also ocExternal and ocMacro 118 else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP) 119 return 2; // binary 120 else if ((SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP) 121 || eOp == ocPercentSign) 122 return 1; // unary 123 else if (SC_OPCODE_START_NO_PAR <= eOp && eOp < SC_OPCODE_STOP_NO_PAR) 124 return 0; // no parameter 125 else if (SC_OPCODE_START_1_PAR <= eOp && eOp < SC_OPCODE_STOP_1_PAR) 126 return 1; // one parameter 127 else if ( eOp == ocIf || eOp == ocChose ) 128 return 1; // only the condition counts as parameter 129 else 130 return 0; // all the rest, no Parameter, or 131 // if so then it should be in cByte 132 } 133 134 135 sal_Bool FormulaToken::IsMatrixFunction() const 136 { 137 return formula::FormulaCompiler::IsMatrixFunction(GetOpCode()); 138 } 139 140 sal_Bool FormulaToken::operator==( const FormulaToken& rToken ) const 141 { 142 // don't compare reference count! 143 return eType == rToken.eType && GetOpCode() == rToken.GetOpCode(); 144 } 145 146 147 // --- virtual dummy methods ------------------------------------------------- 148 149 sal_uInt8 FormulaToken::GetByte() const 150 { 151 // ok to be called for any derived class 152 return 0; 153 } 154 155 void FormulaToken::SetByte( sal_uInt8 ) 156 { 157 DBG_ERRORFILE( "FormulaToken::SetByte: virtual dummy called" ); 158 } 159 160 bool FormulaToken::HasForceArray() const 161 { 162 // ok to be called for any derived class 163 return false; 164 } 165 166 void FormulaToken::SetForceArray( bool ) 167 { 168 DBG_ERRORFILE( "FormulaToken::SetForceArray: virtual dummy called" ); 169 } 170 171 double FormulaToken::GetDouble() const 172 { 173 DBG_ERRORFILE( "FormulaToken::GetDouble: virtual dummy called" ); 174 return 0.0; 175 } 176 177 double & FormulaToken::GetDoubleAsReference() 178 { 179 DBG_ERRORFILE( "FormulaToken::GetDouble: virtual dummy called" ); 180 static double fVal = 0.0; 181 return fVal; 182 } 183 184 const String& FormulaToken::GetString() const 185 { 186 DBG_ERRORFILE( "FormulaToken::GetString: virtual dummy called" ); 187 static String aDummyString; 188 return aDummyString; 189 } 190 191 sal_uInt16 FormulaToken::GetIndex() const 192 { 193 DBG_ERRORFILE( "FormulaToken::GetIndex: virtual dummy called" ); 194 return 0; 195 } 196 197 void FormulaToken::SetIndex( sal_uInt16 ) 198 { 199 DBG_ERRORFILE( "FormulaToken::SetIndex: virtual dummy called" ); 200 } 201 202 short* FormulaToken::GetJump() const 203 { 204 DBG_ERRORFILE( "FormulaToken::GetJump: virtual dummy called" ); 205 return NULL; 206 } 207 208 209 const String& FormulaToken::GetExternal() const 210 { 211 DBG_ERRORFILE( "FormulaToken::GetExternal: virtual dummy called" ); 212 static String aDummyString; 213 return aDummyString; 214 } 215 216 FormulaToken* FormulaToken::GetFAPOrigToken() const 217 { 218 DBG_ERRORFILE( "FormulaToken::GetFAPOrigToken: virtual dummy called" ); 219 return NULL; 220 } 221 222 sal_uInt16 FormulaToken::GetError() const 223 { 224 DBG_ERRORFILE( "FormulaToken::GetError: virtual dummy called" ); 225 return 0; 226 } 227 228 void FormulaToken::SetError( sal_uInt16 ) 229 { 230 DBG_ERRORFILE( "FormulaToken::SetError: virtual dummy called" ); 231 } 232 sal_Bool FormulaToken::TextEqual( const FormulaToken& rToken ) const 233 { 234 return *this == rToken; 235 } 236 // ========================================================================== 237 // real implementations of virtual functions 238 // -------------------------------------------------------------------------- 239 240 241 sal_uInt8 FormulaByteToken::GetByte() const { return nByte; } 242 void FormulaByteToken::SetByte( sal_uInt8 n ) { nByte = n; } 243 bool FormulaByteToken::HasForceArray() const { return bHasForceArray; } 244 void FormulaByteToken::SetForceArray( bool b ) { bHasForceArray = b; } 245 sal_Bool FormulaByteToken::operator==( const FormulaToken& r ) const 246 { 247 return FormulaToken::operator==( r ) && nByte == r.GetByte() && 248 bHasForceArray == r.HasForceArray(); 249 } 250 251 252 FormulaToken* FormulaFAPToken::GetFAPOrigToken() const { return pOrigToken; } 253 sal_Bool FormulaFAPToken::operator==( const FormulaToken& r ) const 254 { 255 return FormulaByteToken::operator==( r ) && pOrigToken == r.GetFAPOrigToken(); 256 } 257 short* FormulaJumpToken::GetJump() const { return pJump; } 258 sal_Bool FormulaJumpToken::operator==( const FormulaToken& r ) const 259 { 260 return FormulaToken::operator==( r ) && pJump[0] == r.GetJump()[0] && 261 memcmp( pJump+1, r.GetJump()+1, pJump[0] * sizeof(short) ) == 0; 262 } 263 FormulaJumpToken::~FormulaJumpToken() 264 { 265 delete [] pJump; 266 } 267 268 269 bool FormulaTokenArray::AddFormulaToken(const sheet::FormulaToken& _aToken,ExternalReferenceHelper* /*_pRef*/) 270 { 271 bool bError = false; 272 const OpCode eOpCode = static_cast<OpCode>(_aToken.OpCode); //! assuming equal values for the moment 273 274 const uno::TypeClass eClass = _aToken.Data.getValueTypeClass(); 275 switch ( eClass ) 276 { 277 case uno::TypeClass_VOID: 278 // empty data -> use AddOpCode (does some special cases) 279 AddOpCode( eOpCode ); 280 break; 281 case uno::TypeClass_DOUBLE: 282 // double is only used for "push" 283 if ( eOpCode == ocPush ) 284 AddDouble( _aToken.Data.get<double>() ); 285 else 286 bError = true; 287 break; 288 case uno::TypeClass_LONG: 289 { 290 // long is svIndex, used for name / database area, or "byte" for spaces 291 sal_Int32 nValue = _aToken.Data.get<sal_Int32>(); 292 if ( eOpCode == ocName || eOpCode == ocDBArea ) 293 AddToken( formula::FormulaIndexToken( eOpCode, static_cast<sal_uInt16>(nValue) ) ); 294 else if ( eOpCode == ocSpaces ) 295 AddToken( formula::FormulaByteToken( ocSpaces, static_cast<sal_uInt8>(nValue) ) ); 296 else 297 bError = true; 298 } 299 break; 300 case uno::TypeClass_STRING: 301 { 302 String aStrVal( _aToken.Data.get<rtl::OUString>() ); 303 if ( eOpCode == ocPush ) 304 AddString( aStrVal ); 305 else if ( eOpCode == ocBad ) 306 AddBad( aStrVal ); 307 else if ( eOpCode == ocExternal || eOpCode == ocMacro ) 308 AddToken( formula::FormulaExternalToken( eOpCode, aStrVal ) ); 309 else 310 bError = true; // unexpected string: don't know what to do with it 311 } 312 break; 313 default: 314 bError = true; 315 } // switch ( eClass ) 316 return bError; 317 } 318 bool FormulaTokenArray::Fill(const uno::Sequence< sheet::FormulaToken >& _aSequence,ExternalReferenceHelper* _pRef) 319 { 320 bool bError = false; 321 const sal_Int32 nCount = _aSequence.getLength(); 322 for (sal_Int32 nPos=0; nPos<nCount; nPos++) 323 { 324 bError |= AddFormulaToken( _aSequence[nPos] ,_pRef); 325 } 326 return bError; 327 } 328 ////////////////////////////////////////////////////////////////////////// 329 FormulaToken* FormulaTokenArray::GetNextReference() 330 { 331 while( nIndex < nLen ) 332 { 333 FormulaToken* t = pCode[ nIndex++ ]; 334 switch( t->GetType() ) 335 { 336 case svSingleRef: 337 case svDoubleRef: 338 case svExternalSingleRef: 339 case svExternalDoubleRef: 340 return t; 341 default: 342 { 343 // added to avoid warnings 344 } 345 } 346 } 347 return NULL; 348 } 349 350 FormulaToken* FormulaTokenArray::GetNextColRowName() 351 { 352 while( nIndex < nLen ) 353 { 354 FormulaToken* t = pCode[ nIndex++ ]; 355 if ( t->GetOpCode() == ocColRowName ) 356 return t; 357 } 358 return NULL; 359 } 360 361 FormulaToken* FormulaTokenArray::GetNextReferenceRPN() 362 { 363 while( nIndex < nRPN ) 364 { 365 FormulaToken* t = pRPN[ nIndex++ ]; 366 switch( t->GetType() ) 367 { 368 case svSingleRef: 369 case svDoubleRef: 370 case svExternalSingleRef: 371 case svExternalDoubleRef: 372 return t; 373 default: 374 { 375 // added to avoid warnings 376 } 377 } 378 } 379 return NULL; 380 } 381 382 FormulaToken* FormulaTokenArray::GetNextReferenceOrName() 383 { 384 if( pCode ) 385 { 386 while ( nIndex < nLen ) 387 { 388 FormulaToken* t = pCode[ nIndex++ ]; 389 switch( t->GetType() ) 390 { 391 case svSingleRef: 392 case svDoubleRef: 393 case svIndex: 394 case svExternalSingleRef: 395 case svExternalDoubleRef: 396 case svExternalName: 397 return t; 398 default: 399 { 400 // added to avoid warnings 401 } 402 } 403 } 404 } 405 return NULL; 406 } 407 408 FormulaToken* FormulaTokenArray::GetNextName() 409 { 410 if( pCode ) 411 { 412 while ( nIndex < nLen ) 413 { 414 FormulaToken* t = pCode[ nIndex++ ]; 415 if( t->GetType() == svIndex ) 416 return t; 417 } 418 } // if( pCode ) 419 return NULL; 420 } 421 422 FormulaToken* FormulaTokenArray::GetNextDBArea() 423 { 424 if( pCode ) 425 { 426 while ( nIndex < nLen ) 427 { 428 FormulaToken* t = pCode[ nIndex++ ]; 429 if( t->GetOpCode() == ocDBArea ) 430 return t; 431 } // while ( nIndex < nLen )+ 432 } 433 return NULL; 434 } 435 436 FormulaToken* FormulaTokenArray::GetNextOpCodeRPN( OpCode eOp ) 437 { 438 while( nIndex < nRPN ) 439 { 440 FormulaToken* t = pRPN[ nIndex++ ]; 441 if ( t->GetOpCode() == eOp ) 442 return t; 443 } 444 return NULL; 445 } 446 447 FormulaToken* FormulaTokenArray::Next() 448 { 449 if( pCode && nIndex < nLen ) 450 return pCode[ nIndex++ ]; 451 else 452 return NULL; 453 } 454 455 FormulaToken* FormulaTokenArray::NextNoSpaces() 456 { 457 if( pCode ) 458 { 459 while( (nIndex < nLen) && (pCode[ nIndex ]->GetOpCode() == ocSpaces) ) 460 ++nIndex; 461 if( nIndex < nLen ) 462 return pCode[ nIndex++ ]; 463 } 464 return NULL; 465 } 466 467 FormulaToken* FormulaTokenArray::NextRPN() 468 { 469 if( pRPN && nIndex < nRPN ) 470 return pRPN[ nIndex++ ]; 471 else 472 return NULL; 473 } 474 475 FormulaToken* FormulaTokenArray::PrevRPN() 476 { 477 if( pRPN && nIndex ) 478 return pRPN[ --nIndex ]; 479 else 480 return NULL; 481 } 482 483 void FormulaTokenArray::DelRPN() 484 { 485 if( nRPN ) 486 { 487 FormulaToken** p = pRPN; 488 for( sal_uInt16 i = 0; i < nRPN; i++ ) 489 { 490 (*p++)->DecRef(); 491 } 492 delete [] pRPN; 493 } 494 pRPN = NULL; 495 nRPN = nIndex = 0; 496 } 497 498 FormulaToken* FormulaTokenArray::PeekPrev( sal_uInt16 & nIdx ) 499 { 500 if (0 < nIdx && nIdx <= nLen) 501 return pCode[--nIdx]; 502 return NULL; 503 } 504 505 FormulaToken* FormulaTokenArray::PeekNext() 506 { 507 if( pCode && nIndex < nLen ) 508 return pCode[ nIndex ]; 509 else 510 return NULL; 511 } 512 513 FormulaToken* FormulaTokenArray::PeekNextNoSpaces() 514 { 515 if( pCode && nIndex < nLen ) 516 { 517 sal_uInt16 j = nIndex; 518 while ( pCode[j]->GetOpCode() == ocSpaces && j < nLen ) 519 j++; 520 if ( j < nLen ) 521 return pCode[ j ]; 522 else 523 return NULL; 524 } 525 else 526 return NULL; 527 } 528 529 FormulaToken* FormulaTokenArray::PeekPrevNoSpaces() 530 { 531 if( pCode && nIndex > 1 ) 532 { 533 sal_uInt16 j = nIndex - 2; 534 while ( pCode[j]->GetOpCode() == ocSpaces && j > 0 ) 535 j--; 536 if ( j > 0 || pCode[j]->GetOpCode() != ocSpaces ) 537 return pCode[ j ]; 538 else 539 return NULL; 540 } 541 else 542 return NULL; 543 } 544 545 sal_Bool FormulaTokenArray::HasOpCode( OpCode eOp ) const 546 { 547 for ( sal_uInt16 j=0; j < nLen; j++ ) 548 { 549 if ( pCode[j]->GetOpCode() == eOp ) 550 return sal_True; 551 } 552 return sal_False; 553 } 554 555 sal_Bool FormulaTokenArray::HasOpCodeRPN( OpCode eOp ) const 556 { 557 for ( sal_uInt16 j=0; j < nRPN; j++ ) 558 { 559 if ( pRPN[j]->GetOpCode() == eOp ) 560 return sal_True; 561 } 562 return sal_False; 563 } 564 565 sal_Bool FormulaTokenArray::HasNameOrColRowName() const 566 { 567 for ( sal_uInt16 j=0; j < nLen; j++ ) 568 { 569 if( pCode[j]->GetType() == svIndex || pCode[j]->GetOpCode() == ocColRowName ) 570 return sal_True; 571 } 572 return sal_False; 573 } 574 575 //////////////////////////////////////////////////////////////////////////// 576 577 FormulaTokenArray::FormulaTokenArray() 578 { 579 pCode = NULL; pRPN = NULL; 580 nError = nLen = nIndex = nRPN = nRefs = 0; 581 bHyperLink = sal_False; 582 ClearRecalcMode(); 583 } 584 585 FormulaTokenArray::FormulaTokenArray( const FormulaTokenArray& rArr ) 586 { 587 Assign( rArr ); 588 } 589 590 FormulaTokenArray::~FormulaTokenArray() 591 { 592 Clear(); 593 } 594 595 void FormulaTokenArray::Assign( const FormulaTokenArray& r ) 596 { 597 nLen = r.nLen; 598 nRPN = r.nRPN; 599 nIndex = r.nIndex; 600 nError = r.nError; 601 nRefs = r.nRefs; 602 nMode = r.nMode; 603 bHyperLink = r.bHyperLink; 604 pCode = NULL; 605 pRPN = NULL; 606 FormulaToken** pp; 607 if( nLen ) 608 { 609 pp = pCode = new FormulaToken*[ nLen ]; 610 memcpy( pp, r.pCode, nLen * sizeof( FormulaToken* ) ); 611 for( sal_uInt16 i = 0; i < nLen; i++ ) 612 (*pp++)->IncRef(); 613 } 614 if( nRPN ) 615 { 616 pp = pRPN = new FormulaToken*[ nRPN ]; 617 memcpy( pp, r.pRPN, nRPN * sizeof( FormulaToken* ) ); 618 for( sal_uInt16 i = 0; i < nRPN; i++ ) 619 (*pp++)->IncRef(); 620 } 621 } 622 623 FormulaTokenArray& FormulaTokenArray::operator=( const FormulaTokenArray& rArr ) 624 { 625 Clear(); 626 Assign( rArr ); 627 return *this; 628 } 629 630 FormulaTokenArray* FormulaTokenArray::Clone() const 631 { 632 FormulaTokenArray* p = new FormulaTokenArray; 633 p->nLen = nLen; 634 p->nRPN = nRPN; 635 p->nRefs = nRefs; 636 p->nMode = nMode; 637 p->nError = nError; 638 p->bHyperLink = bHyperLink; 639 FormulaToken** pp; 640 if( nLen ) 641 { 642 pp = p->pCode = new FormulaToken*[ nLen ]; 643 memcpy( pp, pCode, nLen * sizeof( FormulaToken* ) ); 644 for( sal_uInt16 i = 0; i < nLen; i++, pp++ ) 645 { 646 *pp = (*pp)->Clone(); 647 (*pp)->IncRef(); 648 } 649 } 650 if( nRPN ) 651 { 652 pp = p->pRPN = new FormulaToken*[ nRPN ]; 653 memcpy( pp, pRPN, nRPN * sizeof( FormulaToken* ) ); 654 for( sal_uInt16 i = 0; i < nRPN; i++, pp++ ) 655 { 656 FormulaToken* t = *pp; 657 if( t->GetRef() > 1 ) 658 { 659 FormulaToken** p2 = pCode; 660 sal_uInt16 nIdx = 0xFFFF; 661 for( sal_uInt16 j = 0; j < nLen; j++, p2++ ) 662 { 663 if( *p2 == t ) 664 { 665 nIdx = j; break; 666 } 667 } 668 if( nIdx == 0xFFFF ) 669 *pp = t->Clone(); 670 else 671 *pp = p->pCode[ nIdx ]; 672 } 673 else 674 *pp = t->Clone(); 675 (*pp)->IncRef(); 676 } 677 } 678 return p; 679 } 680 681 void FormulaTokenArray::Clear() 682 { 683 if( nRPN ) DelRPN(); 684 if( pCode ) 685 { 686 FormulaToken** p = pCode; 687 for( sal_uInt16 i = 0; i < nLen; i++ ) 688 { 689 (*p++)->DecRef(); 690 } 691 delete [] pCode; 692 } 693 pCode = NULL; pRPN = NULL; 694 nError = nLen = nIndex = nRPN = nRefs = 0; 695 bHyperLink = sal_False; 696 ClearRecalcMode(); 697 } 698 699 FormulaToken* FormulaTokenArray::AddToken( const FormulaToken& r ) 700 { 701 return Add( r.Clone() ); 702 } 703 704 FormulaToken* FormulaTokenArray::MergeArray( ) 705 { 706 return NULL; 707 } 708 709 FormulaToken* FormulaTokenArray::Add( FormulaToken* t ) 710 { 711 if( !pCode ) 712 pCode = new FormulaToken*[ MAXCODE ]; 713 if( nLen < MAXCODE-1 ) 714 { 715 // fprintf (stderr, "Add : %d\n", t->GetOpCode()); 716 pCode[ nLen++ ] = t; 717 if( t->GetOpCode() == ocPush 718 && ( t->GetType() == svSingleRef || t->GetType() == svDoubleRef ) ) 719 nRefs++; 720 t->IncRef(); 721 if( t->GetOpCode() == ocArrayClose ) 722 return MergeArray(); 723 return t; 724 } 725 else 726 { 727 t->Delete(); 728 if ( nLen == MAXCODE-1 ) 729 { 730 t = new FormulaByteToken( ocStop ); 731 pCode[ nLen++ ] = t; 732 t->IncRef(); 733 } 734 return NULL; 735 } 736 } 737 738 FormulaToken* FormulaTokenArray::AddString( const sal_Unicode* pStr ) 739 { 740 return AddString( String( pStr ) ); 741 } 742 743 FormulaToken* FormulaTokenArray::AddString( const String& rStr ) 744 { 745 return Add( new FormulaStringToken( rStr ) ); 746 } 747 748 FormulaToken* FormulaTokenArray::AddDouble( double fVal ) 749 { 750 return Add( new FormulaDoubleToken( fVal ) ); 751 } 752 753 FormulaToken* FormulaTokenArray::AddName( sal_uInt16 n ) 754 { 755 return Add( new FormulaIndexToken( ocName, n ) ); 756 } 757 758 FormulaToken* FormulaTokenArray::AddExternal( const sal_Unicode* pStr ) 759 { 760 return AddExternal( String( pStr ) ); 761 } 762 763 FormulaToken* FormulaTokenArray::AddExternal( const String& rStr, 764 OpCode eOp /* = ocExternal */ ) 765 { 766 return Add( new FormulaExternalToken( eOp, rStr ) ); 767 } 768 769 FormulaToken* FormulaTokenArray::AddBad( const sal_Unicode* pStr ) 770 { 771 return AddBad( String( pStr ) ); 772 } 773 774 FormulaToken* FormulaTokenArray::AddBad( const String& rStr ) 775 { 776 return Add( new FormulaStringOpToken( ocBad, rStr ) ); 777 } 778 779 780 781 void FormulaTokenArray::AddRecalcMode( ScRecalcMode nBits ) 782 { 783 //! Reihenfolge ist wichtig 784 if ( nBits & RECALCMODE_ALWAYS ) 785 SetRecalcModeAlways(); 786 else if ( !IsRecalcModeAlways() ) 787 { 788 if ( nBits & RECALCMODE_ONLOAD ) 789 SetRecalcModeOnLoad(); 790 else if ( nBits & RECALCMODE_ONLOAD_ONCE && !IsRecalcModeOnLoad() ) 791 SetRecalcModeOnLoadOnce(); 792 } 793 SetCombinedBitsRecalcMode( nBits ); 794 } 795 796 797 sal_Bool FormulaTokenArray::HasMatrixDoubleRefOps() 798 { 799 if ( pRPN && nRPN ) 800 { 801 // RPN-Interpreter Simulation 802 // als Ergebnis jeder Funktion wird einfach ein Double angenommen 803 FormulaToken** pStack = new FormulaToken* [nRPN]; 804 FormulaToken* pResult = new FormulaDoubleToken( 0.0 ); 805 short sp = 0; 806 for ( sal_uInt16 j = 0; j < nRPN; j++ ) 807 { 808 FormulaToken* t = pRPN[j]; 809 OpCode eOp = t->GetOpCode(); 810 sal_uInt8 nParams = t->GetParamCount(); 811 switch ( eOp ) 812 { 813 case ocAdd : 814 case ocSub : 815 case ocMul : 816 case ocDiv : 817 case ocPow : 818 case ocPower : 819 case ocAmpersand : 820 case ocEqual : 821 case ocNotEqual : 822 case ocLess : 823 case ocGreater : 824 case ocLessEqual : 825 case ocGreaterEqual : 826 { 827 for ( sal_uInt8 k = nParams; k; k-- ) 828 { 829 if ( sp >= k && pStack[sp-k]->GetType() == svDoubleRef ) 830 { 831 pResult->Delete(); 832 delete [] pStack; 833 return sal_True; 834 } 835 } 836 } 837 break; 838 default: 839 { 840 // added to avoid warnings 841 } 842 } 843 if ( eOp == ocPush || lcl_IsReference( eOp, t->GetType() ) ) 844 pStack[sp++] = t; 845 else if ( eOp == ocIf || eOp == ocChose ) 846 { // Jumps ignorieren, vorheriges Result (Condition) poppen 847 if ( sp ) 848 --sp; 849 } 850 else 851 { // pop parameters, push result 852 sp = sal::static_int_cast<short>( sp - nParams ); 853 if ( sp < 0 ) 854 { 855 DBG_ERROR( "FormulaTokenArray::HasMatrixDoubleRefOps: sp < 0" ); 856 sp = 0; 857 } 858 pStack[sp++] = pResult; 859 } 860 } 861 pResult->Delete(); 862 delete [] pStack; 863 } 864 865 return sal_False; 866 } 867 868 869 870 // --- POF (plain old formula) rewrite of a token array --------------------- 871 872 #if 0 873 // static function can't be compiled if not used (warning) 874 //#if OSL_DEBUG_LEVEL > 0 875 static void DumpTokArr( FormulaTokenArray *pCode ) 876 { 877 fprintf (stderr, "TokenArr: "); 878 for ( FormulaToken *pCur = pCode->First(); pCur; pCur = pCode->Next() ) 879 fprintf( stderr, "t%d,o%d ", 880 pCur->GetType(), pCur->GetOpCode() ); 881 fprintf (stderr, "\n"); 882 } 883 #endif 884 885 inline bool MissingConvention::isRewriteNeeded( OpCode eOp ) const 886 { 887 switch (eOp) 888 { 889 case ocGammaDist: 890 case ocPoissonDist: 891 case ocAddress: 892 case ocLogNormDist: 893 case ocNormDist: 894 return true; 895 case ocMissing: 896 case ocLog: 897 return !isODFF(); // rewrite only for PODF 898 default: 899 return false; 900 } 901 } 902 903 class FormulaMissingContext 904 { 905 public: 906 const FormulaToken* mpFunc; 907 int mnCurArg; 908 909 void Clear() { mpFunc = NULL; mnCurArg = 0; } 910 inline bool AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const; 911 bool AddMissingExternal( FormulaTokenArray* pNewArr ) const; 912 bool AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const; 913 void AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const; 914 }; 915 916 void FormulaMissingContext::AddMoreArgs( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const 917 { 918 if ( !mpFunc ) 919 return; 920 921 switch (mpFunc->GetOpCode()) 922 { 923 case ocGammaDist: 924 if (mnCurArg == 2) 925 { 926 pNewArr->AddOpCode( ocSep ); 927 pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=sal_True() 928 } 929 break; 930 case ocPoissonDist: 931 if (mnCurArg == 1) 932 { 933 pNewArr->AddOpCode( ocSep ); 934 pNewArr->AddDouble( 1.0 ); // 3rd, Cumulative=sal_True() 935 } 936 break; 937 case ocNormDist: 938 if ( mnCurArg == 2 ) 939 { 940 pNewArr->AddOpCode( ocSep ); 941 pNewArr->AddDouble( 1.0 ); // 4th, Cumulative=sal_True() 942 } 943 break; 944 case ocLogNormDist: 945 if ( mnCurArg == 0 ) 946 { 947 pNewArr->AddOpCode( ocSep ); 948 pNewArr->AddDouble( 0.0 ); // 2nd, mean = 0.0 949 } 950 if ( mnCurArg <= 1 ) 951 { 952 pNewArr->AddOpCode( ocSep ); 953 pNewArr->AddDouble( 1.0 ); // 3rd, standard deviation = 1.0 954 } 955 break; 956 case ocLog: 957 if ( !rConv.isODFF() && mnCurArg == 0 ) 958 { 959 pNewArr->AddOpCode( ocSep ); 960 pNewArr->AddDouble( 10.0 ); // 2nd, basis 10 961 } 962 break; 963 default: 964 break; 965 } 966 } 967 968 inline bool FormulaMissingContext::AddDefaultArg( FormulaTokenArray* pNewArr, int nArg, double f ) const 969 { 970 if (mnCurArg == nArg) 971 { 972 pNewArr->AddDouble( f ); 973 return true; 974 } 975 return false; 976 } 977 978 bool FormulaMissingContext::AddMissingExternal( FormulaTokenArray *pNewArr ) const 979 { 980 // Only called for PODF, not ODFF. No need to distinguish. 981 982 const String &rName = mpFunc->GetExternal(); 983 984 // initial (fast) check: 985 sal_Unicode nLastChar = rName.GetChar( rName.Len() - 1); 986 if ( nLastChar != 't' && nLastChar != 'm' ) 987 return false; 988 989 if (rName.EqualsIgnoreCaseAscii( 990 "com.sun.star.sheet.addin.Analysis.getAccrint" )) 991 { 992 return AddDefaultArg( pNewArr, 4, 1000.0 ); 993 } 994 if (rName.EqualsIgnoreCaseAscii( 995 "com.sun.star.sheet.addin.Analysis.getAccrintm" )) 996 { 997 return AddDefaultArg( pNewArr, 3, 1000.0 ); 998 } 999 return false; 1000 } 1001 1002 bool FormulaMissingContext::AddMissing( FormulaTokenArray *pNewArr, const MissingConvention & rConv ) const 1003 { 1004 if ( !mpFunc ) 1005 return false; 1006 1007 bool bRet = false; 1008 const OpCode eOp = mpFunc->GetOpCode(); 1009 1010 // Add for both, PODF and ODFF 1011 switch (eOp) 1012 { 1013 case ocAddress: 1014 return AddDefaultArg( pNewArr, 2, 1.0 ); // abs 1015 default: 1016 break; 1017 } 1018 1019 if (rConv.isODFF()) 1020 { 1021 // Add for ODFF 1022 } 1023 else 1024 { 1025 // Add for PODF 1026 switch (eOp) 1027 { 1028 case ocFixed: 1029 return AddDefaultArg( pNewArr, 1, 2.0 ); 1030 case ocBetaDist: 1031 case ocBetaInv: 1032 case ocRMZ: // PMT 1033 return AddDefaultArg( pNewArr, 3, 0.0 ); 1034 case ocZinsZ: // IPMT 1035 case ocKapz: // PPMT 1036 return AddDefaultArg( pNewArr, 4, 0.0 ); 1037 case ocBW: // PV 1038 case ocZW: // FV 1039 bRet |= AddDefaultArg( pNewArr, 2, 0.0 ); // pmt 1040 bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // [fp]v 1041 break; 1042 case ocZins: // RATE 1043 bRet |= AddDefaultArg( pNewArr, 1, 0.0 ); // pmt 1044 bRet |= AddDefaultArg( pNewArr, 3, 0.0 ); // fv 1045 bRet |= AddDefaultArg( pNewArr, 4, 0.0 ); // type 1046 break; 1047 case ocExternal: 1048 return AddMissingExternal( pNewArr ); 1049 1050 // --- more complex cases --- 1051 1052 case ocOffset: 1053 // FIXME: rather tough. 1054 // if arg 3 (height) ommitted, export arg1 (rows) 1055 break; 1056 default: 1057 break; 1058 } 1059 } 1060 1061 return bRet; 1062 } 1063 1064 bool FormulaTokenArray::NeedsPofRewrite( const MissingConvention & rConv ) 1065 { 1066 for ( FormulaToken *pCur = First(); pCur; pCur = Next() ) 1067 { 1068 if ( rConv.isRewriteNeeded( pCur->GetOpCode())) 1069 return true; 1070 } 1071 return false; 1072 } 1073 1074 1075 FormulaTokenArray * FormulaTokenArray::RewriteMissingToPof( const MissingConvention & rConv ) 1076 { 1077 const size_t nAlloc = 256; 1078 FormulaMissingContext aCtx[ nAlloc ]; 1079 int aOpCodeAddressStack[ nAlloc ]; // use of ADDRESS() function 1080 const int nOmitAddressArg = 3; // ADDRESS() 4th parameter A1/R1C1 1081 sal_uInt16 nTokens = GetLen() + 1; 1082 FormulaMissingContext* pCtx = (nAlloc < nTokens ? new FormulaMissingContext[nTokens] : &aCtx[0]); 1083 int* pOcas = (nAlloc < nTokens ? new int[nTokens] : &aOpCodeAddressStack[0]); 1084 // Never go below 0, never use 0, mpFunc always NULL. 1085 pCtx[0].Clear(); 1086 int nFn = 0; 1087 int nOcas = 0; 1088 1089 FormulaTokenArray *pNewArr = new FormulaTokenArray; 1090 // At least RECALCMODE_ALWAYS needs to be set. 1091 pNewArr->AddRecalcMode( GetRecalcMode()); 1092 1093 for ( FormulaToken *pCur = First(); pCur; pCur = Next() ) 1094 { 1095 bool bAdd = true; 1096 // Don't write the expression of the new inserted ADDRESS() parameter. 1097 // Do NOT omit the new second parameter of INDIRECT() though. If that 1098 // was done for both, INDIRECT() actually could calculate different and 1099 // valid (but wrong) results with the then changed return value of 1100 // ADDRESS(). Better let it generate an error instead. 1101 for (int i = nOcas; i-- > 0 && bAdd; ) 1102 { 1103 if (pCtx[ pOcas[ i ] ].mnCurArg == nOmitAddressArg) 1104 { 1105 // Omit erverything except a trailing separator, the leading 1106 // separator is omitted below. The other way around would leave 1107 // an extraneous separator if no parameter followed. 1108 if (!(pOcas[ i ] == nFn && pCur->GetOpCode() == ocSep)) 1109 bAdd = false; 1110 } 1111 //fprintf( stderr, "ocAddress %d arg %d%s\n", (int)i, (int)pCtx[ pOcas[ i ] ].mnCurArg, (bAdd ? "" : " omitted")); 1112 } 1113 switch ( pCur->GetOpCode() ) 1114 { 1115 case ocOpen: 1116 ++nFn; // all following operations on _that_ function 1117 pCtx[ nFn ].mpFunc = PeekPrevNoSpaces(); 1118 pCtx[ nFn ].mnCurArg = 0; 1119 if (pCtx[ nFn ].mpFunc && pCtx[ nFn ].mpFunc->GetOpCode() == ocAddress && !rConv.isODFF()) 1120 pOcas[ nOcas++ ] = nFn; // entering ADDRESS() if PODF 1121 break; 1122 case ocClose: 1123 pCtx[ nFn ].AddMoreArgs( pNewArr, rConv ); 1124 DBG_ASSERT( nFn > 0, "FormulaTokenArray::RewriteMissingToPof: underflow"); 1125 if (nOcas > 0 && pOcas[ nOcas-1 ] == nFn) 1126 --nOcas; // leaving ADDRESS() 1127 if (nFn > 0) 1128 --nFn; 1129 break; 1130 case ocSep: 1131 pCtx[ nFn ].mnCurArg++; 1132 // Omit leading separator of ADDRESS() parameter. 1133 if (nOcas && pOcas[ nOcas-1 ] == nFn && pCtx[ nFn ].mnCurArg == nOmitAddressArg) 1134 { 1135 bAdd = false; 1136 //fprintf( stderr, "ocAddress %d sep %d omitted\n", (int)nOcas-1, nOmitAddressArg); 1137 } 1138 break; 1139 case ocMissing: 1140 if (bAdd) 1141 bAdd = !pCtx[ nFn ].AddMissing( pNewArr, rConv ); 1142 break; 1143 default: 1144 break; 1145 } 1146 if (bAdd) 1147 pNewArr->AddToken( *pCur ); 1148 } 1149 1150 if (pOcas != &aOpCodeAddressStack[0]) 1151 delete [] pOcas; 1152 if (pCtx != &aCtx[0]) 1153 delete [] pCtx; 1154 1155 return pNewArr; 1156 } 1157 1158 bool FormulaTokenArray::MayReferenceFollow() 1159 { 1160 if ( pCode && nLen > 0 ) 1161 { 1162 // ignore trailing spaces 1163 sal_uInt16 i = nLen - 1; 1164 while ( i > 0 && pCode[i]->GetOpCode() == SC_OPCODE_SPACES ) 1165 { 1166 --i; 1167 } 1168 if ( i > 0 || pCode[i]->GetOpCode() != SC_OPCODE_SPACES ) 1169 { 1170 OpCode eOp = pCode[i]->GetOpCode(); 1171 if ( (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_BIN_OP ) || 1172 (SC_OPCODE_START_UN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP ) || 1173 eOp == SC_OPCODE_OPEN || eOp == SC_OPCODE_SEP ) 1174 { 1175 return true; 1176 } 1177 } 1178 } 1179 return false; 1180 } 1181 FormulaToken* FormulaTokenArray::AddOpCode( OpCode eOp ) 1182 { 1183 FormulaToken* pRet = NULL; 1184 switch ( eOp ) 1185 { 1186 case ocOpen: 1187 case ocClose: 1188 case ocSep: 1189 case ocArrayOpen: 1190 case ocArrayClose: 1191 case ocArrayRowSep: 1192 case ocArrayColSep: 1193 pRet = new FormulaToken( svSep,eOp ); 1194 break; 1195 case ocIf: 1196 case ocChose: 1197 { 1198 short nJump[MAXJUMPCOUNT + 1]; 1199 nJump[ 0 ] = ocIf == eOp ? 3 : MAXJUMPCOUNT+1; 1200 pRet = new FormulaJumpToken( eOp, (short*)nJump ); 1201 } 1202 break; 1203 default: 1204 pRet = new FormulaByteToken( eOp, 0, sal_False ); 1205 break; 1206 } 1207 return AddToken( *pRet ); 1208 } 1209 1210 1211 /*----------------------------------------------------------------------*/ 1212 1213 FormulaTokenIterator::FormulaTokenIterator( const FormulaTokenArray& rArr ) 1214 { 1215 pCur = NULL; 1216 Push( &rArr ); 1217 } 1218 1219 FormulaTokenIterator::~FormulaTokenIterator() 1220 { 1221 while( pCur ) 1222 Pop(); 1223 } 1224 1225 void FormulaTokenIterator::Push( const FormulaTokenArray* pArr ) 1226 { 1227 ImpTokenIterator* p = new ImpTokenIterator; 1228 p->pArr = pArr; 1229 p->nPC = -1; 1230 p->nStop = SHRT_MAX; 1231 p->pNext = pCur; 1232 pCur = p; 1233 } 1234 1235 void FormulaTokenIterator::Pop() 1236 { 1237 ImpTokenIterator* p = pCur; 1238 if( p ) 1239 { 1240 pCur = p->pNext; 1241 delete p; 1242 } 1243 } 1244 1245 void FormulaTokenIterator::Reset() 1246 { 1247 while( pCur->pNext ) 1248 Pop(); 1249 pCur->nPC = -1; 1250 } 1251 1252 const FormulaToken* FormulaTokenIterator::First() 1253 { 1254 Reset(); 1255 return Next(); 1256 } 1257 1258 const FormulaToken* FormulaTokenIterator::Next() 1259 { 1260 const FormulaToken* t = GetNonEndOfPathToken( ++pCur->nPC ); 1261 if( !t && pCur->pNext ) 1262 { 1263 Pop(); 1264 t = Next(); 1265 } 1266 return t; 1267 } 1268 1269 const FormulaToken* FormulaTokenIterator::PeekNextOperator() 1270 { 1271 const FormulaToken* t = NULL; 1272 short nIdx = pCur->nPC; 1273 while (!t && ((t = GetNonEndOfPathToken( ++nIdx)) != NULL)) 1274 { 1275 if (t->GetOpCode() == ocPush) 1276 t = NULL; // ignore operands 1277 } 1278 if (!t && pCur->pNext) 1279 { 1280 ImpTokenIterator* pHere = pCur; 1281 pCur = pCur->pNext; 1282 t = PeekNextOperator(); 1283 pCur = pHere; 1284 } 1285 return t; 1286 } 1287 1288 //! The nPC counts after a Push() are -1 1289 1290 void FormulaTokenIterator::Jump( short nStart, short nNext, short nStop ) 1291 { 1292 pCur->nPC = nNext; 1293 if( nStart != nNext ) 1294 { 1295 Push( pCur->pArr ); 1296 pCur->nPC = nStart; 1297 pCur->nStop = nStop; 1298 } 1299 } 1300 1301 const FormulaToken* FormulaTokenIterator::GetNonEndOfPathToken( short nIdx ) const 1302 { 1303 if (nIdx < pCur->pArr->nRPN && nIdx < pCur->nStop) 1304 { 1305 const FormulaToken* t = pCur->pArr->pRPN[ nIdx ]; 1306 // such an OpCode ends an IF() or CHOOSE() path 1307 return (t->GetOpCode() == ocSep || t->GetOpCode() == ocClose) ? NULL : t; 1308 } 1309 return NULL; 1310 } 1311 1312 bool FormulaTokenIterator::IsEndOfPath() const 1313 { 1314 return GetNonEndOfPathToken( pCur->nPC + 1) == NULL; 1315 } 1316 1317 // ----------------------------------------------------------------------------- 1318 // ========================================================================== 1319 // real implementations of virtual functions 1320 // -------------------------------------------------------------------------- 1321 1322 double FormulaDoubleToken::GetDouble() const { return fDouble; } 1323 double & FormulaDoubleToken::GetDoubleAsReference() { return fDouble; } 1324 sal_Bool FormulaDoubleToken::operator==( const FormulaToken& r ) const 1325 { 1326 return FormulaToken::operator==( r ) && fDouble == r.GetDouble(); 1327 } 1328 1329 1330 const String& FormulaStringToken::GetString() const { return aString; } 1331 sal_Bool FormulaStringToken::operator==( const FormulaToken& r ) const 1332 { 1333 return FormulaToken::operator==( r ) && aString == r.GetString(); 1334 } 1335 1336 1337 const String& FormulaStringOpToken::GetString() const { return aString; } 1338 sal_Bool FormulaStringOpToken::operator==( const FormulaToken& r ) const 1339 { 1340 return FormulaByteToken::operator==( r ) && aString == r.GetString(); 1341 } 1342 1343 sal_uInt16 FormulaIndexToken::GetIndex() const { return nIndex; } 1344 void FormulaIndexToken::SetIndex( sal_uInt16 n ) { nIndex = n; } 1345 sal_Bool FormulaIndexToken::operator==( const FormulaToken& r ) const 1346 { 1347 return FormulaToken::operator==( r ) && nIndex == r.GetIndex(); 1348 } 1349 const String& FormulaExternalToken::GetExternal() const { return aExternal; } 1350 sal_uInt8 FormulaExternalToken::GetByte() const { return nByte; } 1351 void FormulaExternalToken::SetByte( sal_uInt8 n ) { nByte = n; } 1352 sal_Bool FormulaExternalToken::operator==( const FormulaToken& r ) const 1353 { 1354 return FormulaToken::operator==( r ) && nByte == r.GetByte() && 1355 aExternal == r.GetExternal(); 1356 } 1357 1358 1359 sal_uInt16 FormulaErrorToken::GetError() const { return nError; } 1360 void FormulaErrorToken::SetError( sal_uInt16 nErr ) { nError = nErr; } 1361 sal_Bool FormulaErrorToken::operator==( const FormulaToken& r ) const 1362 { 1363 return FormulaToken::operator==( r ) && 1364 nError == static_cast< const FormulaErrorToken & >(r).GetError(); 1365 } 1366 double FormulaMissingToken::GetDouble() const { return 0.0; } 1367 const String& FormulaMissingToken::GetString() const 1368 { 1369 static String aDummyString; 1370 return aDummyString; 1371 } 1372 sal_Bool FormulaMissingToken::operator==( const FormulaToken& r ) const 1373 { 1374 return FormulaToken::operator==( r ); 1375 } 1376 1377 1378 FormulaSubroutineToken::FormulaSubroutineToken( const FormulaSubroutineToken& r ) : 1379 FormulaToken( r ), 1380 mpArray( r.mpArray->Clone()) 1381 { 1382 } 1383 FormulaSubroutineToken::~FormulaSubroutineToken() 1384 { 1385 delete mpArray; 1386 } 1387 const FormulaTokenArray* FormulaSubroutineToken::GetTokenArray() const 1388 { 1389 return mpArray; 1390 } 1391 sal_Bool FormulaSubroutineToken::operator==( const FormulaToken& r ) const 1392 { 1393 // Arrays don't equal.. 1394 return FormulaToken::operator==( r ) && 1395 (mpArray == static_cast<const FormulaSubroutineToken&>(r).mpArray); 1396 } 1397 1398 1399 sal_Bool FormulaUnknownToken::operator==( const FormulaToken& r ) const 1400 { 1401 return FormulaToken::operator==( r ); 1402 } 1403 1404 // ----------------------------------------------------------------------------- 1405 } // formula 1406 // ----------------------------------------------------------------------------- 1407 1408