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