xref: /trunk/main/l10ntools/source/xrmmerge.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_l10ntools.hxx"
30 #include <stdio.h>
31 #include <tools/string.hxx>
32 #include <tools/fsys.hxx>
33 
34 // local includes
35 #include "export.hxx"
36 #include "xrmmerge.hxx"
37 #include "utf8conv.hxx"
38 #include "tokens.h"
39 #include <iostream>
40 #include <vector>
41 
42 using namespace std;
43 
44 extern "C" { int yyerror( char * ); }
45 extern "C" { int YYWarning( char * ); }
46 
47 // defines to parse command line
48 #define STATE_NON       0x0001
49 #define STATE_INPUT     0x0002
50 #define STATE_OUTPUT    0x0003
51 #define STATE_PRJ       0x0004
52 #define STATE_ROOT      0x0005
53 #define STATE_MERGESRC  0x0006
54 #define STATE_ERRORLOG  0x0007
55 #define STATE_UTF8      0x000B
56 #define STATE_LANGUAGES 0x000C
57 #define STATE_ISOCODE99 0x000D
58 
59 // set of global variables
60 sal_Bool bEnableExport;
61 sal_Bool bMergeMode;
62 sal_Bool bErrorLog;
63 sal_Bool bUTF8;
64 ByteString sPrj;
65 ByteString sPrjRoot;
66 ByteString sInputFileName;
67 ByteString sActFileName;
68 ByteString sOutputFile;
69 ByteString sMergeSrc;
70 String sUsedTempFile;
71 XRMResParser *pParser = NULL;
72 
73 extern "C" {
74 // the whole interface to lexer is in this extern "C" section
75 
76 /*****************************************************************************/
77 extern char *GetOutputFile( int argc, char* argv[])
78 /*****************************************************************************/
79 {
80     bEnableExport = sal_False;
81     bMergeMode = sal_False;
82     bErrorLog = sal_True;
83     bUTF8 = sal_True;
84     sPrj = "";
85     sPrjRoot = "";
86     sInputFileName = "";
87     sActFileName = "";
88     Export::sLanguages = "";
89     sal_uInt16 nState = STATE_NON;
90     sal_Bool bInput = sal_False;
91 
92     // parse command line
93     for( int i = 1; i < argc; i++ ) {
94         if ( ByteString( argv[ i ] ).ToUpperAscii() == "-I" ) {
95             nState = STATE_INPUT; // next token specifies source file
96         }
97         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-O" ) {
98             nState = STATE_OUTPUT; // next token specifies the dest file
99         }
100         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-P" ) {
101             nState = STATE_PRJ; // next token specifies the cur. project
102         }
103         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-R" ) {
104             nState = STATE_ROOT; // next token specifies path to project root
105         }
106         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-M" ) {
107             nState = STATE_MERGESRC; // next token specifies the merge database
108         }
109         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-E" ) {
110             nState = STATE_ERRORLOG;
111             bErrorLog = sal_False;
112         }
113         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-UTF8" ) {
114             nState = STATE_UTF8;
115             bUTF8 = sal_True;
116         }
117         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-NOUTF8" ) {
118             nState = STATE_UTF8;
119             bUTF8 = sal_False;
120         }
121         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-L" ) {
122             nState = STATE_LANGUAGES;
123         }
124         else if ( ByteString( argv[ i ] ).ToUpperAscii() == "-ISO99" ) {
125             nState = STATE_ISOCODE99;
126         }
127         else {
128             switch ( nState ) {
129                 case STATE_NON: {
130                     return NULL;    // no valid command line
131                 }
132                 case STATE_INPUT: {
133                     sInputFileName = argv[ i ];
134                     bInput = sal_True; // source file found
135                 }
136                 break;
137                 case STATE_OUTPUT: {
138                     sOutputFile = argv[ i ]; // the dest. file
139                 }
140                 break;
141                 case STATE_PRJ: {
142                     sPrj = ByteString( argv[ i ]);
143                 }
144                 break;
145                 case STATE_ROOT: {
146                     sPrjRoot = ByteString( argv[ i ]); // path to project root
147                 }
148                 break;
149                 case STATE_MERGESRC: {
150                     sMergeSrc = ByteString( argv[ i ]);
151                     bMergeMode = sal_True; // activate merge mode, cause merge database found
152                 }
153                 break;
154                 case STATE_LANGUAGES: {
155                     Export::sLanguages = ByteString( argv[ i ]);
156                 }
157                 break;
158             }
159         }
160     }
161 
162     if ( bInput ) {
163         // command line is valid
164         bEnableExport = sal_True;
165         char *pReturn = new char[ sOutputFile.Len() + 1 ];
166         strcpy( pReturn, sOutputFile.GetBuffer());  // #100211# - checked
167         return pReturn;
168     }
169 
170     // command line is not valid
171     return NULL;
172 }
173 void removeTempFile(){
174     if( !sUsedTempFile.EqualsIgnoreCaseAscii( "" ) ){
175         DirEntry aTempFile( sUsedTempFile );
176         aTempFile.Kill();
177     }
178 }
179 /*****************************************************************************/
180 int InitXrmExport( char *pOutput , char* pFilename)
181 /*****************************************************************************/
182 {
183     // instanciate Export
184     ByteString sOutput( pOutput );
185     ByteString sFilename( pFilename );
186     Export::InitLanguages( false );
187 
188     if ( bMergeMode )
189         pParser = new XRMResMerge( sMergeSrc, sOutputFile, sFilename );
190     else if ( sOutputFile.Len()) {
191         pParser = new XRMResExport( sOutputFile, sPrj, sActFileName );
192     }
193 
194     return 1;
195 }
196 
197 /*****************************************************************************/
198 int EndXrmExport()
199 /*****************************************************************************/
200 {
201     delete pParser;
202     return 1;
203 }
204 extern const char* getFilename()
205 {
206     return sInputFileName.GetBuffer();
207 }
208 /*****************************************************************************/
209 extern FILE *GetXrmFile()
210 /*****************************************************************************/
211 {
212     FILE *pFile = 0;
213     // look for valid filename
214     if ( sInputFileName.Len()) {
215         if( Export::fileHasUTF8ByteOrderMarker( sInputFileName ) ){
216             DirEntry aTempFile = Export::GetTempFile();
217             DirEntry aSourceFile( String( sInputFileName , RTL_TEXTENCODING_ASCII_US ) );
218             aSourceFile.CopyTo( aTempFile , FSYS_ACTION_COPYFILE );
219             String sTempFile = aTempFile.GetFull();
220             Export::RemoveUTF8ByteOrderMarkerFromFile( ByteString( sTempFile , RTL_TEXTENCODING_ASCII_US ) );
221             pFile = fopen( ByteString( sTempFile , RTL_TEXTENCODING_ASCII_US ).GetBuffer(), "r" );
222             sUsedTempFile = sTempFile;
223         }else{
224             // able to open file?
225             pFile = fopen( sInputFileName.GetBuffer(), "r" );
226             sUsedTempFile = String::CreateFromAscii("");
227         }
228         if ( !pFile ){
229             fprintf( stderr, "Error: Could not open file %s\n",
230                 sInputFileName.GetBuffer());
231         }
232         else {
233             // this is a valid file which can be opened, so
234             // create path to project root
235             DirEntry aEntry( String( sInputFileName, RTL_TEXTENCODING_ASCII_US ));
236             aEntry.ToAbs();
237             ByteString sFullEntry( aEntry.GetFull(), RTL_TEXTENCODING_ASCII_US );
238             aEntry += DirEntry( String( "..", RTL_TEXTENCODING_ASCII_US ));
239             aEntry += DirEntry( sPrjRoot );
240             ByteString sPrjEntry( aEntry.GetFull(), RTL_TEXTENCODING_ASCII_US );
241 
242             // create file name, beginnig with project root
243             // (e.g.: source\ui\src\menue.src)
244             sActFileName = sFullEntry.Copy( sPrjEntry.Len() + 1 );
245 
246 
247             sActFileName.SearchAndReplaceAll( "/", "\\" );
248 
249             return pFile;
250         }
251     }
252     // this means the file could not be opened
253     return NULL;
254 }
255 
256 /*****************************************************************************/
257 int WorkOnTokenSet( int nTyp, char *pTokenText )
258 /*****************************************************************************/
259 {
260     //printf("Typ = %d , text = '%s'\n",nTyp , pTokenText );
261     pParser->Execute( nTyp, pTokenText );
262 
263     return 1;
264 }
265 
266 /*****************************************************************************/
267 int SetError()
268 /*****************************************************************************/
269 {
270     pParser->SetError();
271     return 1;
272 }
273 }
274 
275 extern "C" {
276 /*****************************************************************************/
277 int GetError()
278 /*****************************************************************************/
279 {
280     return pParser->GetError();
281 }
282 }
283 
284 //
285 // class XRMResParser
286 //
287 
288 
289 /*****************************************************************************/
290 XRMResParser::XRMResParser()
291 /*****************************************************************************/
292                 : bError( sal_False ),
293                 bText( sal_False )
294 {
295     aLanguages = Export::GetLanguages();
296 }
297 
298 /*****************************************************************************/
299 XRMResParser::~XRMResParser()
300 /*****************************************************************************/
301 {
302 }
303 
304 /*****************************************************************************/
305 int XRMResParser::Execute( int nToken, char * pToken )
306 /*****************************************************************************/
307 {
308     ByteString rToken( pToken );
309 
310     switch ( nToken ) {
311         case XRM_README_START:
312             sLID = "";
313             sGID = GetAttribute( rToken, "name" );
314         break;
315 
316         case XRM_README_END:
317             sGID = "";
318         break;
319 
320         case XRM_SECTION_START:
321             sLID = "";
322             sGID += ".";
323             sGID += GetAttribute( rToken, "id" );
324             //sLocalized = "1";
325 
326             //sLocalized = "X:";
327             sLocalized = true;
328         break;
329 
330         case XRM_SECTION_END:
331             sGID = sGID.GetToken( 0, '.' );
332             break;
333 
334         case XRM_PARAGRAPH_START:
335             sLID = "";
336             sGID += ".";
337             sGID += GetAttribute( rToken, "id" );
338 //          if ( GetAttribute( rToken, "localized" ) == "false" )
339 //              sLocalized += "0";
340 //                sLocalized = false;
341 //          else
342 //              sLocalized += "1";
343                 sLocalized = true;
344         break;
345 
346         case XRM_PARAGRAPH_END: {
347             if ( sLID.Len())
348                 EndOfText( sCurrentOpenTag, sCurrentCloseTag );
349             ByteString sTmp = sGID;
350             sGID = "";
351             for ( sal_uInt16 i = 0; i + 1 < sTmp.GetTokenCount( '.' ); i++ ) {
352                 if ( sGID.Len())
353                     sGID += ".";
354                 sGID += sTmp.GetToken( i, '.' );
355             }
356             //sLocalized = sLocalized.Copy( 0, sLocalized.Len() - 1 );
357         }
358         break;
359 
360         case XRM_TEXT_START:{
361                 //printf("->XRM_TEXT_START\n");
362                 ByteString sNewLID = GetAttribute( rToken, "id" );
363                 if ( sNewLID != sLID ) {
364                     //EndOfText( sCurrentOpenTag, sCurrentCloseTag );
365                     sLID = sNewLID;
366                 }
367                 bText = sal_True;
368                 sCurrentText = "";
369                 sCurrentOpenTag = rToken;
370                 Output( rToken );
371                 //printf("<-XRM_TEXT_START\n");
372             }
373         break;
374 
375         case XRM_TEXT_END: {
376                 sCurrentCloseTag = rToken;
377                 //printf("->XRM_TEXT_END\n");
378                 ByteString sLang = GetAttribute( sCurrentOpenTag, "xml:lang" );
379                 WorkOnText( sCurrentOpenTag, sCurrentText );
380                 Output( sCurrentText );
381                 EndOfText( sCurrentOpenTag, sCurrentCloseTag );// <---
382                 bText = sal_False;
383                 rToken = ByteString("");
384                 sCurrentText  = ByteString("");
385                 //printf("<-XRM_TEXT_END");
386         }
387         break;
388 
389         case XRM_LIST_START:
390             sLID = "";
391         break;
392 
393         case XRM_LIST_END:
394             if ( sLID.Len())
395                 EndOfText( sCurrentOpenTag, sCurrentCloseTag );
396         break;
397 
398         default:
399             if ( bText ) {
400                 sCurrentText += rToken;
401             }
402         break;
403     }
404 
405     if ( !bText )
406     {
407         Output( rToken );
408     }
409     return 0;
410 }
411 
412 /*****************************************************************************/
413 ByteString XRMResParser::GetAttribute( const ByteString &rToken, const ByteString &rAttribute )
414 /*****************************************************************************/
415 {
416     ByteString sTmp( rToken );
417     sTmp.SearchAndReplaceAll( "\t", " " );
418 
419     ByteString sSearch( " " );
420     sSearch += rAttribute;
421     sSearch += "=";
422     sal_uInt16 nPos = sTmp.Search( sSearch );
423 
424     if ( nPos != STRING_NOTFOUND ) {
425         sTmp = sTmp.Copy( nPos );
426         ByteString sId = sTmp.GetToken( 1, '\"' );
427         return sId;
428     }
429     return "";
430 }
431 
432 
433 /*****************************************************************************/
434 void XRMResParser::Error( const ByteString &rError )
435 /*****************************************************************************/
436 {
437     yyerror(( char * ) rError.GetBuffer());
438 }
439 
440 /*****************************************************************************/
441 void XRMResParser::ConvertStringToDBFormat( ByteString &rString )
442 /*****************************************************************************/
443 {
444     ByteString sResult;
445     do {
446         sResult = rString;
447         rString.EraseLeadingChars( _LF );
448     //  rString.EraseLeadingChars( ' ' );
449         rString.EraseLeadingChars( '\t' );
450     //  rString.EraseTrailingChars( ' ' );
451         rString.EraseTrailingChars( '\t' );
452     } while ( sResult != rString );
453 
454     rString.SearchAndReplaceAll( "\t", "\\t" );
455 }
456 
457 /*****************************************************************************/
458 void XRMResParser::ConvertStringToXMLFormat( ByteString &rString )
459 /*****************************************************************************/
460 {
461     rString.SearchAndReplaceAll( "\\t", "\t" );
462 }
463 
464 
465 
466 //
467 // class XRMResOutputParser
468 //
469 
470 /*****************************************************************************/
471 XRMResOutputParser::XRMResOutputParser ( const ByteString &rOutputFile )
472 /*****************************************************************************/
473 {
474     aLanguages = Export::GetLanguages();
475     pOutputStream =
476         new SvFileStream(
477             String( rOutputFile, RTL_TEXTENCODING_ASCII_US ),
478             STREAM_STD_WRITE | STREAM_TRUNC
479         );
480     pOutputStream->SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
481     if ( !pOutputStream->IsOpen()) {
482         ByteString sError( "Unable to open output file: " );
483         sError += rOutputFile;
484         Error( sError );
485         delete pOutputStream;
486         pOutputStream = NULL;
487     }
488 }
489 
490 /*****************************************************************************/
491 XRMResOutputParser::~XRMResOutputParser()
492 /*****************************************************************************/
493 {
494     if ( pOutputStream ) {
495         pOutputStream->Close();
496         delete pOutputStream;
497     }
498 }
499 
500 //
501 // class XMLResExport
502 //
503 
504 /*****************************************************************************/
505 XRMResExport::XRMResExport(
506     const ByteString &rOutputFile, const ByteString &rProject,
507     const ByteString &rFilePath )
508 /*****************************************************************************/
509                 : XRMResOutputParser( rOutputFile ),
510                 pResData( NULL ),
511                 sPrj( rProject ),
512                 sPath( rFilePath )
513 {
514     aLanguages = Export::GetLanguages();
515 }
516 
517 /*****************************************************************************/
518 XRMResExport::~XRMResExport()
519 /*****************************************************************************/
520 {
521     delete pResData;
522 }
523 
524 void XRMResExport::Output( const ByteString& rOutput )
525 {
526     // Dummy to suppress warnings caused by poor class design
527     (void) rOutput;
528 }
529 
530 /*****************************************************************************/
531 void XRMResExport::WorkOnText(
532     const ByteString &rOpenTag,
533     ByteString &rText
534 )
535 /*****************************************************************************/
536 {
537     ByteString sLang( GetAttribute( rOpenTag, "xml:lang" ));
538 
539         if ( !pResData ) {
540             ByteString sPlatform( "" );
541             pResData = new ResData( sPlatform, GetGID() );
542             pResData->sId = GetLID();
543         }
544 
545         pResData->sText[ sLang ] = rText;
546         ConvertStringToDBFormat( pResData->sText[ sLang ] );
547 }
548 
549 /*****************************************************************************/
550 void XRMResExport::EndOfText(
551     const ByteString &rOpenTag,
552     const ByteString &rCloseTag
553 )
554 /*****************************************************************************/
555 {
556 
557     (void) rOpenTag;        // FIXME
558     (void) rCloseTag;       // FIXME
559 
560     if ( pResData && pOutputStream ) {
561 
562         char cSearch = 0x00;
563         ByteString sSearch( cSearch );
564 
565     //  if ( !pResData->sText[ ByteString("en-US") ].Len() )
566     //        pResData->sText[ ByteString("en-US") ] = pResData->sText[ ByteString("de") ];
567 
568         Export::FillInFallbacks( pResData );
569 
570         ByteString sTimeStamp( Export::GetTimeStamp());
571         ByteString sCur;
572         for( unsigned int n = 0; n < aLanguages.size(); n++ ){
573             sCur = aLanguages[ n ];
574 
575             ByteString sAct = pResData->sText[ sCur ];
576                 //Export::UnquotHTML( sAct );
577                 sAct.EraseAllChars( 0x0A );
578 
579                 ByteString sOutput( sPrj ); sOutput += "\t";
580                 sOutput += sPath;
581                 sOutput += "\t0\t";
582                 sOutput += "readmeitem\t";
583                 sOutput += pResData->sId;
584                 // USE LID AS GID OR MERGE DON'T WORK
585                 //sOutput += pResData->sGId;
586                 sOutput += "\t";
587                 sOutput += pResData->sId;
588                 sOutput += "\t\t\t0\t";
589                 sOutput += sCur;
590                 sOutput += "\t";
591 
592                 sOutput += sAct; sOutput += "\t\t\t\t";
593                 sOutput += sTimeStamp;
594 
595                 sOutput.SearchAndReplaceAll( sSearch, "_" );
596                 //if( !sCur.EqualsIgnoreCaseAscii("de") ||( sCur.EqualsIgnoreCaseAscii("de") && !Export::isMergingGermanAllowed( sPrj ) ) )
597                 if( sAct.Len() > 1 )
598                     pOutputStream->WriteLine( sOutput );
599             }
600     }
601     delete pResData;
602     pResData = NULL;
603 }
604 
605 //
606 // class XRMResMerge
607 //
608 
609 /*****************************************************************************/
610 XRMResMerge::XRMResMerge(
611     const ByteString &rMergeSource, const ByteString &rOutputFile,
612     ByteString &rFilename)
613 /*****************************************************************************/
614                 : XRMResOutputParser( rOutputFile ),
615                 pMergeDataFile( NULL ),
616                 sFilename( rFilename ) ,
617                 pResData( NULL )
618 {
619     if ( rMergeSource.Len())
620         pMergeDataFile = new MergeDataFile(
621             rMergeSource, sInputFileName , bErrorLog, RTL_TEXTENCODING_MS_1252);//, bUTF8 );
622     if( Export::sLanguages.EqualsIgnoreCaseAscii("ALL") ){
623         Export::SetLanguages( pMergeDataFile->GetLanguages() );
624         aLanguages = pMergeDataFile->GetLanguages();
625     }
626     else aLanguages = Export::GetLanguages();
627 }
628 
629 /*****************************************************************************/
630 XRMResMerge::~XRMResMerge()
631 /*****************************************************************************/
632 {
633     delete pMergeDataFile;
634     delete pResData;
635 }
636 
637 /*****************************************************************************/
638 void XRMResMerge::WorkOnText(
639     const ByteString &rOpenTag,
640     ByteString &rText
641 )
642 /*****************************************************************************/
643 {
644     ByteString sLang( GetAttribute( rOpenTag, "xml:lang" ));
645 
646     if ( pMergeDataFile ) {
647         if ( !pResData ) {
648             ByteString sPlatform( "" );
649 //          pResData = new ResData( sPlatform, GetGID() , sFilename );
650             pResData = new ResData( sPlatform, GetLID() , sFilename );
651             pResData->sId = GetLID();
652 
653             pResData->sResTyp = "readmeitem";
654         }
655 
656         PFormEntrys *pEntrys = pMergeDataFile->GetPFormEntrys( pResData );
657             if ( pEntrys ) {
658                 ByteString sContent;
659                 if ( Export::isAllowed( sLang ) &&
660                     ( pEntrys->GetText(
661                         sContent, STRING_TYP_TEXT, sLang )) &&
662                     ( sContent != "-" ) && ( sContent.Len()))
663 
664                 {
665                     rText = sContent;
666                     ConvertStringToXMLFormat( rText );
667                     //Export::QuotHTMLXRM( rText );
668                 }
669             }
670     }
671 }
672 
673 /*****************************************************************************/
674 void XRMResMerge::Output( const ByteString& rOutput )
675 /*****************************************************************************/
676 {
677     //printf("W: %s\n",rOutput.GetBuffer());
678     if ( pOutputStream && rOutput.Len() > 0 )
679         pOutputStream->Write( rOutput.GetBuffer(), rOutput.Len());
680 }
681 
682 /*****************************************************************************/
683 void XRMResMerge::EndOfText(
684     const ByteString &rOpenTag,
685     const ByteString &rCloseTag
686 )
687 /*****************************************************************************/
688 {
689 
690     Output( rCloseTag );
691     if ( pMergeDataFile && pResData ) {
692         PFormEntrys *pEntrys = pMergeDataFile->GetPFormEntrys( pResData );
693         if ( pEntrys ) {
694             ByteString sCur;
695             for( unsigned int n = 0; n < aLanguages.size(); n++ ){
696                 sCur = aLanguages[ n ];
697                 ByteString sContent;
698                 if ( !sCur.EqualsIgnoreCaseAscii("en-US")  &&
699                     ( pEntrys->GetText(
700                         sContent, STRING_TYP_TEXT, sCur, sal_True )) &&
701                     ( sContent != "-" ) && ( sContent.Len()))
702                 {
703                     ByteString sText( sContent );
704                     //Export::QuotHTMLXRM( sText );
705 
706                     ByteString sAdditionalLine( "\t" );
707                     sAdditionalLine += rOpenTag;
708                     ByteString sSearch = "xml:lang=\"";
709                     ByteString sReplace( sSearch );
710 
711                     sSearch += GetAttribute( rOpenTag, "xml:lang" );
712                     sReplace += sCur;
713 
714                     sAdditionalLine.SearchAndReplace( sSearch, sReplace );
715 
716                     sAdditionalLine += sText;
717                     sAdditionalLine += rCloseTag;
718                     sAdditionalLine += "\n";
719 
720                     for ( sal_uInt16 i = 0; i + 1 < GetGID().GetTokenCount( '.' ); i++ )
721                         sAdditionalLine += "\t";
722 
723                     Output( sAdditionalLine );
724                 }
725             }
726         }
727     }
728     delete pResData;
729     pResData = NULL;
730 }
731 
732