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_svtools.hxx" 26 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ 27 28 #include <stdio.h> 29 #include <svtools/svparser.hxx> 30 #include <tools/stream.hxx> 31 #include <tools/debug.hxx> 32 #define _SVSTDARR_USHORTS 33 #include <svl/svstdarr.hxx> 34 #include <rtl/textcvt.h> 35 #include <rtl/tencinfo.h> 36 37 #define SVPAR_CSM_ 38 39 #define SVPAR_CSM_ANSI 0x0001U 40 #define SVPAR_CSM_UTF8 0x0002U 41 #define SVPAR_CSM_UCS2B 0x0004U 42 #define SVPAR_CSM_UCS2L 0x0008U 43 #define SVPAR_CSM_SWITCH 0x8000U 44 45 // Struktur, um sich die akt. Daten zumerken 46 struct SvParser_Impl 47 { 48 String aToken; // gescanntes Token 49 sal_uLong nFilePos; // akt. Position im Stream 50 sal_uLong nlLineNr; // akt. Zeilen Nummer 51 sal_uLong nlLinePos; // akt. Spalten Nummer 52 long nTokenValue; // zusaetzlicher Wert (RTF) 53 sal_Bool bTokenHasValue; // indicates whether nTokenValue is valid 54 int nToken; // akt. Token 55 sal_Unicode nNextCh; // akt. Zeichen 56 57 int nSaveToken; // das Token vom Continue 58 59 rtl_TextToUnicodeConverter hConv; 60 rtl_TextToUnicodeContext hContext; 61 62 #ifdef DBG_UTIL 63 SvFileStream aOut; 64 #endif 65 66 SvParser_Impl() : 67 nSaveToken(0), hConv( 0 ), hContext( (rtl_TextToUnicodeContext)1 ) 68 { 69 } 70 71 }; 72 73 74 75 // Konstruktor 76 SvParser::SvParser( SvStream& rIn, sal_uInt8 nStackSize ) 77 : rInput( rIn ) 78 , nlLineNr( 1 ) 79 , nlLinePos( 1 ) 80 , pImplData( 0 ) 81 , nTokenValue( 0 ) 82 , bTokenHasValue( false ) 83 , eState( SVPAR_NOTSTARTED ) 84 , eSrcEnc( RTL_TEXTENCODING_DONTKNOW ) 85 , bDownloadingFile( sal_False ) 86 , nTokenStackSize( nStackSize ) 87 , nTokenStackPos( 0 ) 88 { 89 bUCS2BSrcEnc = bSwitchToUCS2 = sal_False; 90 eState = SVPAR_NOTSTARTED; 91 if( nTokenStackSize < 3 ) 92 nTokenStackSize = 3; 93 pTokenStack = new TokenStackType[ nTokenStackSize ]; 94 pTokenStackPos = pTokenStack; 95 96 #ifdef DBG_UTIL 97 98 // wenn die Datei schon existiert, dann Anhaengen: 99 if( !pImplData ) 100 pImplData = new SvParser_Impl; 101 pImplData->aOut.Open( String::CreateFromAscii( "\\parser.dmp" ), 102 STREAM_STD_WRITE | STREAM_NOCREATE ); 103 if( pImplData->aOut.GetError() || !pImplData->aOut.IsOpen() ) 104 pImplData->aOut.Close(); 105 else 106 { 107 pImplData->aOut.Seek( STREAM_SEEK_TO_END ); 108 pImplData->aOut << "\x0c\n\n >>>>>>>>>>>>>>> Dump Start <<<<<<<<<<<<<<<\n"; 109 } 110 #endif 111 } 112 113 SvParser::~SvParser() 114 { 115 #ifdef DBG_UTIL 116 if( pImplData->aOut.IsOpen() ) 117 pImplData->aOut << "\n\n >>>>>>>>>>>>>>> Dump Ende <<<<<<<<<<<<<<<\n"; 118 pImplData->aOut.Close(); 119 #endif 120 121 if( pImplData && pImplData->hConv ) 122 { 123 rtl_destroyTextToUnicodeContext( pImplData->hConv, 124 pImplData->hContext ); 125 rtl_destroyTextToUnicodeConverter( pImplData->hConv ); 126 } 127 128 delete pImplData; 129 130 delete [] pTokenStack; 131 } 132 133 void SvParser::ClearTxtConvContext() 134 { 135 if( pImplData && pImplData->hConv ) 136 rtl_resetTextToUnicodeContext( pImplData->hConv, pImplData->hContext ); 137 } 138 139 void SvParser::SetSrcEncoding( rtl_TextEncoding eEnc ) 140 { 141 142 if( eEnc != eSrcEnc ) 143 { 144 if( pImplData && pImplData->hConv ) 145 { 146 rtl_destroyTextToUnicodeContext( pImplData->hConv, 147 pImplData->hContext ); 148 rtl_destroyTextToUnicodeConverter( pImplData->hConv ); 149 pImplData->hConv = 0; 150 pImplData->hContext = (rtl_TextToUnicodeContext )1; 151 } 152 153 if( rtl_isOctetTextEncoding(eEnc) || 154 RTL_TEXTENCODING_UCS2 == eEnc ) 155 { 156 eSrcEnc = eEnc; 157 if( !pImplData ) 158 pImplData = new SvParser_Impl; 159 pImplData->hConv = rtl_createTextToUnicodeConverter( eSrcEnc ); 160 DBG_ASSERT( pImplData->hConv, 161 "SvParser::SetSrcEncoding: no converter for source encoding" ); 162 if( !pImplData->hConv ) 163 eSrcEnc = RTL_TEXTENCODING_DONTKNOW; 164 else 165 pImplData->hContext = 166 rtl_createTextToUnicodeContext( pImplData->hConv ); 167 } 168 else 169 { 170 DBG_ASSERT( !this, 171 "SvParser::SetSrcEncoding: invalid source encoding" ); 172 eSrcEnc = RTL_TEXTENCODING_DONTKNOW; 173 } 174 } 175 } 176 177 void SvParser::RereadLookahead() 178 { 179 rInput.Seek(nNextChPos); 180 nNextCh = GetNextChar(); 181 } 182 183 sal_Unicode SvParser::GetNextChar() 184 { 185 sal_Unicode c = 0U; 186 187 // When reading muliple bytes, we don't have to care about the file 188 // position when we run inti the pending state. The file position is 189 // maintained by SaveState/RestoreState. 190 sal_Bool bErr; 191 if( bSwitchToUCS2 && 0 == rInput.Tell() ) 192 { 193 sal_uChar c1, c2; 194 sal_Bool bSeekBack = sal_True; 195 196 rInput >> c1; 197 bErr = rInput.IsEof() || rInput.GetError(); 198 if( !bErr ) 199 { 200 if( 0xff == c1 || 0xfe == c1 ) 201 { 202 rInput >> c2; 203 bErr = rInput.IsEof() || rInput.GetError(); 204 if( !bErr ) 205 { 206 if( 0xfe == c1 && 0xff == c2 ) 207 { 208 eSrcEnc = RTL_TEXTENCODING_UCS2; 209 bUCS2BSrcEnc = sal_True; 210 bSeekBack = sal_False; 211 } 212 else if( 0xff == c1 && 0xfe == c2 ) 213 { 214 eSrcEnc = RTL_TEXTENCODING_UCS2; 215 bUCS2BSrcEnc = sal_False; 216 bSeekBack = sal_False; 217 } 218 } 219 } 220 } 221 if( bSeekBack ) 222 rInput.Seek( 0 ); 223 224 bSwitchToUCS2 = sal_False; 225 } 226 227 nNextChPos = rInput.Tell(); 228 229 if( RTL_TEXTENCODING_UCS2 == eSrcEnc ) 230 { 231 sal_Unicode cUC = USHRT_MAX; 232 sal_uChar c1, c2; 233 234 rInput >> c1 >> c2; 235 if( 2 == rInput.Tell() && 236 !(rInput.IsEof() || rInput.GetError()) && 237 ( (bUCS2BSrcEnc && 0xfe == c1 && 0xff == c2) || 238 (!bUCS2BSrcEnc && 0xff == c1 && 0xfe == c2) ) ) 239 rInput >> c1 >> c2; 240 241 bErr = rInput.IsEof() || rInput.GetError(); 242 if( !bErr ) 243 { 244 if( bUCS2BSrcEnc ) 245 cUC = (sal_Unicode(c1) << 8) | c2; 246 else 247 cUC = (sal_Unicode(c2) << 8) | c1; 248 } 249 250 if( !bErr ) 251 { 252 c = cUC; 253 } 254 } 255 else 256 { 257 sal_Size nChars = 0; 258 do 259 { 260 sal_Char c1; // signed, that's the text converter expects 261 rInput >> c1; 262 bErr = rInput.IsEof() || rInput.GetError(); 263 if( !bErr ) 264 { 265 if ( 266 RTL_TEXTENCODING_DONTKNOW == eSrcEnc || 267 RTL_TEXTENCODING_SYMBOL == eSrcEnc 268 ) 269 { 270 // no convserion shall take place 271 c = (sal_Unicode)c1; 272 nChars = 1; 273 } 274 else 275 { 276 DBG_ASSERT( pImplData && pImplData->hConv, 277 "no text converter!" ); 278 279 sal_Unicode cUC; 280 sal_uInt32 nInfo = 0; 281 sal_Size nCvtBytes; 282 nChars = rtl_convertTextToUnicode( 283 pImplData->hConv, pImplData->hContext, 284 &c1, 1, &cUC, 1, 285 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR| 286 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR| 287 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR, 288 &nInfo, &nCvtBytes); 289 if( (nInfo&RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL) != 0 ) 290 { 291 // The conversion wasn't successfull because we haven't 292 // read enough characters. 293 if( pImplData->hContext != (rtl_TextToUnicodeContext)1 ) 294 { 295 while( (nInfo&RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL) != 0 ) 296 { 297 rInput >> c1; 298 bErr = rInput.IsEof() || rInput.GetError(); 299 if( bErr ) 300 break; 301 302 nChars = rtl_convertTextToUnicode( 303 pImplData->hConv, pImplData->hContext, 304 &c1, 1, &cUC, 1, 305 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR| 306 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR| 307 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR, 308 &nInfo, &nCvtBytes); 309 } 310 if( !bErr ) 311 { 312 if( 1 == nChars && 0 == nInfo ) 313 { 314 c = cUC; 315 } 316 else if( 0 != nChars || 0 != nInfo ) 317 { 318 DBG_ASSERT( (nInfo&RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL) == 0, 319 "source buffer is to small" ); 320 DBG_ASSERT( (nInfo&~(RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL)) == 0, 321 "there is a conversion error" ); 322 DBG_ASSERT( 0 == nChars, 323 "there is a converted character, but an error" ); 324 // There are still errors, but nothing we can 325 // do 326 c = (sal_Unicode)'?'; 327 nChars = 1; 328 } 329 } 330 } 331 else 332 { 333 sal_Char sBuffer[10]; 334 sBuffer[0] = c1; 335 sal_uInt16 nLen = 1; 336 while( (nInfo&RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL) != 0 && 337 nLen < 10 ) 338 { 339 rInput >> c1; 340 bErr = rInput.IsEof() || rInput.GetError(); 341 if( bErr ) 342 break; 343 344 sBuffer[nLen++] = c1; 345 nChars = rtl_convertTextToUnicode( 346 pImplData->hConv, 0, sBuffer, nLen, &cUC, 1, 347 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR| 348 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR| 349 RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR, 350 &nInfo, &nCvtBytes); 351 } 352 if( !bErr ) 353 { 354 if( 1 == nChars && 0 == nInfo ) 355 { 356 DBG_ASSERT( nCvtBytes == nLen, 357 "no all bytes have been converted!" ); 358 c = cUC; 359 } 360 else 361 { 362 DBG_ASSERT( (nInfo&RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL) == 0, 363 "source buffer is to small" ); 364 DBG_ASSERT( (nInfo&~(RTL_TEXTTOUNICODE_INFO_SRCBUFFERTOSMALL)) == 0, 365 "there is a conversion error" ); 366 DBG_ASSERT( 0 == nChars, 367 "there is a converted character, but an error" ); 368 369 // There are still errors, so we use the first 370 // character and restart after that. 371 c = (sal_Unicode)sBuffer[0]; 372 rInput.SeekRel( -(nLen-1) ); 373 nChars = 1; 374 } 375 } 376 } 377 } 378 else if( 1 == nChars && 0 == nInfo ) 379 { 380 // The conversion was successfull 381 DBG_ASSERT( nCvtBytes == 1, 382 "no all bytes have been converted!" ); 383 c = cUC; 384 } 385 else if( 0 != nChars || 0 != nInfo ) 386 { 387 DBG_ASSERT( 0 == nChars, 388 "there is a converted character, but an error" ); 389 DBG_ASSERT( 0 != nInfo, 390 "there is no converted character and no error" ); 391 // #73398#: If the character could not be converted, 392 // because a conversion is not available, do no conversion at all. 393 c = (sal_Unicode)c1; 394 nChars = 1; 395 396 } 397 } 398 } 399 } 400 while( 0 == nChars && !bErr ); 401 } 402 if( bErr ) 403 { 404 if( ERRCODE_IO_PENDING == rInput.GetError() ) 405 { 406 eState = SVPAR_PENDING; 407 return c; 408 } 409 else 410 return sal_Unicode(EOF); 411 } 412 413 #ifdef DBG_UTIL 414 if( pImplData->aOut.IsOpen() ) 415 pImplData->aOut << ByteString::ConvertFromUnicode( c, 416 RTL_TEXTENCODING_MS_1251 ); 417 #endif 418 419 if( c == '\n' ) 420 { 421 IncLineNr(); 422 SetLinePos( 1L ); 423 } 424 else 425 IncLinePos(); 426 return c; 427 } 428 429 int SvParser::GetNextToken() 430 { 431 int nRet = 0; 432 433 if( !nTokenStackPos ) 434 { 435 aToken.Erase(); // Token-Buffer loeschen 436 nTokenValue = -1; // Kennzeichen fuer kein Value gelesen 437 bTokenHasValue = false; 438 439 nRet = _GetNextToken(); 440 if( SVPAR_PENDING == eState ) 441 return nRet; 442 } 443 444 ++pTokenStackPos; 445 if( pTokenStackPos == pTokenStack + nTokenStackSize ) 446 pTokenStackPos = pTokenStack; 447 448 // vom Stack holen ?? 449 if( nTokenStackPos ) 450 { 451 --nTokenStackPos; 452 nTokenValue = pTokenStackPos->nTokenValue; 453 bTokenHasValue = pTokenStackPos->bTokenHasValue; 454 aToken = pTokenStackPos->sToken; 455 nRet = pTokenStackPos->nTokenId; 456 } 457 // nein, dann das aktuelle auf den Stack 458 else if( SVPAR_WORKING == eState ) 459 { 460 pTokenStackPos->sToken = aToken; 461 pTokenStackPos->nTokenValue = nTokenValue; 462 pTokenStackPos->bTokenHasValue = bTokenHasValue; 463 pTokenStackPos->nTokenId = nRet; 464 } 465 else if( SVPAR_ACCEPTED != eState && SVPAR_PENDING != eState ) 466 eState = SVPAR_ERROR; // irgend ein Fehler 467 468 return nRet; 469 } 470 471 int SvParser::SkipToken( short nCnt ) // n Tokens zurueck "skippen" 472 { 473 pTokenStackPos = GetStackPtr( nCnt ); 474 short nTmp = nTokenStackPos - nCnt; 475 if( nTmp < 0 ) 476 nTmp = 0; 477 else if( nTmp > nTokenStackSize ) 478 nTmp = nTokenStackSize; 479 nTokenStackPos = sal_uInt8(nTmp); 480 481 // und die Werte zurueck 482 aToken = pTokenStackPos->sToken; 483 nTokenValue = pTokenStackPos->nTokenValue; 484 bTokenHasValue = pTokenStackPos->bTokenHasValue; 485 486 return pTokenStackPos->nTokenId; 487 } 488 489 SvParser::TokenStackType* SvParser::GetStackPtr( short nCnt ) 490 { 491 sal_uInt8 nAktPos = sal_uInt8(pTokenStackPos - pTokenStack ); 492 if( nCnt > 0 ) 493 { 494 if( nCnt >= nTokenStackSize ) 495 nCnt = (nTokenStackSize-1); 496 if( nAktPos + nCnt < nTokenStackSize ) 497 nAktPos = sal::static_int_cast< sal_uInt8 >(nAktPos + nCnt); 498 else 499 nAktPos = sal::static_int_cast< sal_uInt8 >( 500 nAktPos + (nCnt - nTokenStackSize)); 501 } 502 else if( nCnt < 0 ) 503 { 504 if( -nCnt >= nTokenStackSize ) 505 nCnt = -nTokenStackSize+1; 506 if( -nCnt <= nAktPos ) 507 nAktPos = sal::static_int_cast< sal_uInt8 >(nAktPos + nCnt); 508 else 509 nAktPos = sal::static_int_cast< sal_uInt8 >( 510 nAktPos + (nCnt + nTokenStackSize)); 511 } 512 return pTokenStack + nAktPos; 513 } 514 515 // wird fuer jedes Token gerufen, das in CallParser erkannt wird 516 void SvParser::NextToken( int ) 517 { 518 } 519 520 521 // fuers asynchrone lesen aus dem SvStream 522 523 int SvParser::GetSaveToken() const 524 { 525 return pImplData ? pImplData->nSaveToken : 0; 526 } 527 528 void SvParser::SaveState( int nToken ) 529 { 530 // aktuellen Status merken 531 if( !pImplData ) 532 { 533 pImplData = new SvParser_Impl; 534 pImplData->nSaveToken = 0; 535 } 536 537 pImplData->nFilePos = rInput.Tell(); 538 pImplData->nToken = nToken; 539 540 pImplData->aToken = aToken; 541 pImplData->nlLineNr = nlLineNr; 542 pImplData->nlLinePos = nlLinePos; 543 pImplData->nTokenValue= nTokenValue; 544 pImplData->bTokenHasValue = bTokenHasValue; 545 pImplData->nNextCh = nNextCh; 546 } 547 548 void SvParser::RestoreState() 549 { 550 // alten Status wieder zurueck setzen 551 if( pImplData ) 552 { 553 if( ERRCODE_IO_PENDING == rInput.GetError() ) 554 rInput.ResetError(); 555 aToken = pImplData->aToken; 556 nlLineNr = pImplData->nlLineNr; 557 nlLinePos = pImplData->nlLinePos; 558 nTokenValue= pImplData->nTokenValue; 559 bTokenHasValue=pImplData->bTokenHasValue; 560 nNextCh = pImplData->nNextCh; 561 562 pImplData->nSaveToken = pImplData->nToken; 563 564 rInput.Seek( pImplData->nFilePos ); 565 } 566 } 567 568 void SvParser::Continue( int ) 569 { 570 } 571 572 void SvParser::BuildWhichTbl( SvUShorts &rWhichMap, 573 sal_uInt16 *pWhichIds, 574 sal_uInt16 nWhichIds ) 575 { 576 sal_uInt16 aNewRange[2]; 577 578 for( sal_uInt16 nCnt = 0; nCnt < nWhichIds; ++nCnt, ++pWhichIds ) 579 if( *pWhichIds ) 580 { 581 aNewRange[0] = aNewRange[1] = *pWhichIds; 582 sal_Bool bIns = sal_True; 583 584 // Position suchen 585 for ( sal_uInt16 nOfs = 0; rWhichMap[nOfs]; nOfs += 2 ) 586 { 587 if( *pWhichIds < rWhichMap[nOfs] - 1 ) 588 { 589 // neuen Range davor 590 rWhichMap.Insert( aNewRange, 2, nOfs ); 591 bIns = sal_False; 592 break; 593 } 594 else if( *pWhichIds == rWhichMap[nOfs] - 1 ) 595 { 596 // diesen Range nach unten erweitern 597 rWhichMap[nOfs] = *pWhichIds; 598 bIns = sal_False; 599 break; 600 } 601 else if( *pWhichIds == rWhichMap[nOfs+1] + 1 ) 602 { 603 if( rWhichMap[nOfs+2] != 0 && rWhichMap[nOfs+2] == *pWhichIds + 1 ) 604 { 605 // mit dem naechsten Bereich mergen 606 rWhichMap[nOfs+1] = rWhichMap[nOfs+3]; 607 rWhichMap.Remove( nOfs+2, 2 ); 608 } 609 else 610 // diesen Range nach oben erweitern 611 rWhichMap[nOfs+1] = *pWhichIds; 612 bIns = sal_False; 613 break; 614 } 615 } 616 617 // einen Range hinten anhaengen 618 if( bIns ) 619 rWhichMap.Insert( aNewRange, 2, rWhichMap.Count()-1 ); 620 } 621 } 622 623 624 IMPL_STATIC_LINK( SvParser, NewDataRead, void*, EMPTYARG ) 625 { 626 switch( pThis->eState ) 627 { 628 case SVPAR_PENDING: 629 // Wenn gerade ein File geladen wird duerfen wir nicht weiterlaufen, 630 // sondern muessen den Aufruf ignorieren. 631 if( pThis->IsDownloadingFile() ) 632 break; 633 634 pThis->eState = SVPAR_WORKING; 635 pThis->RestoreState(); 636 637 pThis->Continue( pThis->pImplData->nToken ); 638 639 if( ERRCODE_IO_PENDING == pThis->rInput.GetError() ) 640 pThis->rInput.ResetError(); 641 642 if( SVPAR_PENDING != pThis->eState ) 643 pThis->ReleaseRef(); // ansonsten sind wir fertig! 644 break; 645 646 case SVPAR_WAITFORDATA: 647 pThis->eState = SVPAR_WORKING; 648 break; 649 650 case SVPAR_NOTSTARTED: 651 case SVPAR_WORKING: 652 break; 653 654 default: 655 pThis->ReleaseRef(); // ansonsten sind wir fertig! 656 break; 657 } 658 659 return 0; 660 } 661 662 /*======================================================================== 663 * 664 * SvKeyValueIterator. 665 * 666 *======================================================================*/ 667 SV_DECL_PTRARR_DEL(SvKeyValueList_Impl, SvKeyValue*, 0, 4) 668 SV_IMPL_PTRARR(SvKeyValueList_Impl, SvKeyValue*); 669 670 /* 671 * SvKeyValueIterator. 672 */ 673 SvKeyValueIterator::SvKeyValueIterator (void) 674 : m_pList (new SvKeyValueList_Impl), 675 m_nPos (0) 676 { 677 } 678 679 /* 680 * ~SvKeyValueIterator. 681 */ 682 SvKeyValueIterator::~SvKeyValueIterator (void) 683 { 684 delete m_pList; 685 } 686 687 /* 688 * GetFirst. 689 */ 690 sal_Bool SvKeyValueIterator::GetFirst (SvKeyValue &rKeyVal) 691 { 692 m_nPos = m_pList->Count(); 693 return GetNext (rKeyVal); 694 } 695 696 /* 697 * GetNext. 698 */ 699 sal_Bool SvKeyValueIterator::GetNext (SvKeyValue &rKeyVal) 700 { 701 if (m_nPos > 0) 702 { 703 rKeyVal = *m_pList->GetObject(--m_nPos); 704 return sal_True; 705 } 706 else 707 { 708 // Nothing to do. 709 return sal_False; 710 } 711 } 712 713 /* 714 * Append. 715 */ 716 void SvKeyValueIterator::Append (const SvKeyValue &rKeyVal) 717 { 718 SvKeyValue *pKeyVal = new SvKeyValue (rKeyVal); 719 m_pList->C40_INSERT(SvKeyValue, pKeyVal, m_pList->Count()); 720 } 721 722 /* vi:set tabstop=4 shiftwidth=4 expandtab: */ 723