001
002package gudusoft.gsqlparser.pp.utils;
003
004import gudusoft.gsqlparser.ETokenStatus;
005import gudusoft.gsqlparser.ETokenType;
006import gudusoft.gsqlparser.TSourceToken;
007import gudusoft.gsqlparser.TSourceTokenList;
008import gudusoft.gsqlparser.pp.logger.PPLogger;
009import gudusoft.gsqlparser.pp.para.GFmtOpt;
010
011/**
012 * the helper class used for operating the source token
013 * 
014 * @author zhoujun
015 * 
016 */
017public class SourceTokenOperator
018{
019
020        public static int removeWhitespaceBackward( GFmtOpt opt,
021                        TSourceTokenList list, int end )
022        {
023                if ( end == 0 )
024                {
025                        return 0;
026                }
027
028                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
029                if ( buf != null )
030                {
031                        buf.append( "removeWhitespaceAndReturnFromEnd:\ntargetToken:" )
032                                        .append(list.get(end).getAstext())
033                                        .append( " newToken:" )
034                                        .append( escapeNewlines( list.get(end).getAstext() ) )
035                                        .append( "(type:" )
036                                        .append( list.get( end ).tokentype )
037                                        .append( ")" );
038                }
039                int deleteWhitespaceCount = 0;
040                // int deleteReturn = 0;
041
042                int pos = end - 1;
043
044                // remove all white space before end item
045                TSourceToken token = list.get( pos );
046                while ( token.tokentype == ETokenType.ttwhitespace )
047                {
048
049                        if ( token.tokentype == ETokenType.ttwhitespace )
050                        {
051                                deleteWhitespaceCount++;
052                        }
053
054                        if ( opt.opearateSourceToken )
055                        {
056                                token.setReplaceToken( SourceTokenConstant.EMPTY );
057                        }
058
059                        pos--;
060                        if ( pos >= 0 )
061                        {
062                                token = token.container.get( pos );
063                        }
064                        else
065                        {
066                                break;
067                        }
068                }
069
070                if ( token.tokentype == ETokenType.ttreturn )
071                {
072                        StringBuilder b = new StringBuilder( );
073                        for (int i = 0; i < token.getAstext().length( ); i++ )
074                        {
075                                char c = token.getAstext().charAt( i );
076                                if ( c != ' ' )
077                                {
078                                        b.append( c );
079                                }
080                        }
081                        token.setAstext(b.toString( ));
082                }
083
084                if ( buf != null )
085                {
086                        buf.append( "\nDeleteWhiteSpace:" ).append( deleteWhitespaceCount );
087                        PPLogger.info( 2, buf.toString( ), null );
088                }
089
090                return pos;
091        }
092
093        /**
094         * remove the white space and the return token from the end
095         * 
096         * @param list
097         * @param end
098         * @return the pos which is not white space and return token in the source
099         *         token list
100         */
101        public static int removeWhitespaceAndReturnFromEnd( GFmtOpt opt,
102                        TSourceTokenList list, int end )
103        {
104                if ( end == 0 )
105                {
106                        return 0;
107                }
108
109                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
110                if ( buf != null )
111                {
112                        buf.append( "removeWhitespaceAndReturnFromEnd:\ntargetToken:" )
113                                        .append(list.get(end).getAstext())
114                                        .append( " newToken:" )
115                                        .append( escapeNewlines( list.get(end).getAstext() ) )
116                                        .append( "(type:" )
117                                        .append( list.get( end ).tokentype )
118                                        .append( ")" );
119                }
120                int deleteWhitespaceCount = 0;
121                int deleteReturn = 0;
122
123                int pos = end - 1;
124
125                // remove all white space before end item
126                TSourceToken token = list.get( pos );
127                while ( token.tokentype == ETokenType.ttwhitespace
128                                || token.tokentype == ETokenType.ttreturn )
129                {
130                        if ( token.tokentype == ETokenType.ttwhitespace )
131                        {
132                                deleteWhitespaceCount++;
133                        }
134
135                        if ( token.tokentype == ETokenType.ttreturn )
136                        {
137                                deleteReturn++;
138                        }
139
140                        if ( opt.opearateSourceToken )
141                        {
142                                token.setReplaceToken( SourceTokenConstant.EMPTY );
143                        }
144
145                        pos--;
146                        if ( pos >= 0 )
147                        {
148                                token = token.container.get( pos );
149                        }
150                        else
151                        {
152                                break;
153                        }
154                }
155
156                if ( buf != null )
157                {
158                        buf.append( "\nDeleteWhiteSpace:" )
159                                        .append( deleteWhitespaceCount )
160                                        .append( ", DeleteReturn:" )
161                                        .append( deleteReturn );
162                        PPLogger.info( 2, buf.toString( ), null );
163                }
164
165                return pos;
166        }
167
168        /**
169         * remove the white space and the return token from the end
170         * 
171         * @param token
172         * @return the pos which is not whitespace and return token in the source
173         *         token list
174         */
175        public static int removeWhitespaceAndReturnFromEnd( GFmtOpt opt,
176                        TSourceToken token )
177        {
178                if ( token == null )
179                {
180                        return -1;
181                }
182                return removeWhitespaceAndReturnFromEnd( opt,
183                                token.container,
184                                token.posinlist );
185        }
186
187        /**
188         * remove the white space and the return token before this token and after
189         * this token
190         * 
191         * @param opt
192         * @param token
193         */
194        public static void removeWhitespaceAndReturnFormBeforeAndAfter(
195                        GFmtOpt opt, TSourceToken token )
196        {
197                if ( token == null )
198                {
199                        return;
200                }
201                removeWhitespaceAndReturnFromEnd( opt, token );
202                removeWhitespaceAndReturnFromStart( opt,
203                                token.container,
204                                token.posinlist + 1 );
205
206        }
207
208        /**
209         * remove the white space and the return token
210         * 
211         * @param list
212         * @param start
213         * @param end
214         */
215        public static void removeWhitespaceAndReturn( GFmtOpt opt,
216                        TSourceTokenList list, int start, int end )
217        {
218                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
219                if ( buf != null )
220                {
221                        buf.append( "removeWhitespaceAndReturn:\ntargetToken:" )
222                                        .append(list.get(end).getAstext())
223                                        .append( " newToken:" )
224                                        .append( escapeNewlines( list.get(end).getAstext() ) )
225                                        .append( "(type:" )
226                                        .append( list.get( end ).tokentype )
227                                        .append( ")" );
228                }
229                int deleteWhitespaceCount = 0;
230                int deleteReturn = 0;
231
232                for ( int i = start; i < end; i++ )
233                {
234                        if ( i < 0 || i > list.size( ) )
235                        {
236                                return;
237                        }
238                        TSourceToken token = list.get( i );
239                        if ( token.tokentype == ETokenType.ttwhitespace
240                                        || token.tokentype == ETokenType.ttreturn )
241                        {
242
243                                if ( token.tokentype == ETokenType.ttwhitespace )
244                                {
245                                        deleteWhitespaceCount++;
246                                }
247
248                                if ( token.tokentype == ETokenType.ttreturn )
249                                {
250                                        deleteReturn++;
251                                }
252                                if ( opt.opearateSourceToken )
253                                {
254                                        token.setReplaceToken( SourceTokenConstant.EMPTY );
255                                }
256                        }
257                }
258
259                if ( buf != null )
260                {
261                        buf.append( "\nDeleteWhiteSpace:" )
262                                        .append( deleteWhitespaceCount )
263                                        .append( ", DeleteReturn:" )
264                                        .append( deleteReturn );
265                        PPLogger.info( 2, buf.toString( ), null );
266                }
267
268        }
269
270        /**
271         * remove the white space and the return token from the end
272         * 
273         * @param list
274         */
275        public static int removeWhitespaceAndReturnFromStart( GFmtOpt opt,
276                        TSourceTokenList list, int startPos )
277        {
278                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
279                if ( list.size( ) <= startPos )
280                {
281                        return list.size( ) - 1;
282                }
283                if ( buf != null )
284                {
285                        buf.append( "removeWhitespaceAndReturnFromStart:\ntargetToken:" )
286                                        .append(list.get(startPos).getAstext())
287                                        .append( " newToken:" )
288                                        .append( escapeNewlines( list.get(startPos).getAstext() ) )
289                                        .append( "(type:" )
290                                        .append( list.get( startPos ).tokentype )
291                                        .append( ")" );
292                }
293                int deleteWhitespaceCount = 0;
294                int deleteReturn = 0;
295
296                int pos = startPos;
297                // remove all white space before end item
298                TSourceToken token = list.get( pos );
299                while ( token.tokentype == ETokenType.ttwhitespace
300                                || token.tokentype == ETokenType.ttreturn )
301                {
302                        if ( token.tokentype == ETokenType.ttwhitespace )
303                        {
304                                deleteWhitespaceCount++;
305                        }
306
307                        if ( token.tokentype == ETokenType.ttreturn )
308                        {
309                                deleteReturn++;
310                        }
311
312                        if ( opt.opearateSourceToken )
313                        {
314                                token.setReplaceToken( SourceTokenConstant.EMPTY );
315                        }
316
317                        pos++;
318                        if ( pos < list.size( ) )
319                        {
320                                token = token.container.get( pos );
321                        }
322                        else
323                        {
324                                break;
325                        }
326                }
327
328                if ( buf != null )
329                {
330                        buf.append( "\nDeleteWhiteSpace:" )
331                                        .append( deleteWhitespaceCount )
332                                        .append( ", DeleteReturn:" )
333                                        .append( deleteReturn );
334                        PPLogger.info( 2, buf.toString( ), null );
335                }
336
337                return pos;
338        }
339
340        /**
341         * calculate the text length for all virtual token (which are call before
342         * tokens, after tokens and replace token)
343         * 
344         * @param list
345         * @param start
346         * @param end
347         * @return
348         */
349        public static int textLengthVT( TSourceTokenList list, int start, int end )
350        {
351                int total = 0;
352                for ( int i = start; i < end; i++ )
353                {
354                        TSourceToken token = list.get( i );
355
356                        if ( token.getTokensBefore( ).size( ) > 0 )
357                        {
358                                total += textLengthVT( token.getTokensBefore( ),
359                                                0,
360                                                token.getTokensBefore( ).size( ) );
361                        }
362                        if ( token.getReplaceToken( ) != null )
363                        {
364                                total += ( token.getReplaceToken().getAstext() != null ? token.getReplaceToken().getAstext().length( )
365                                                : 0 );
366                        }
367                        else
368                        {
369                                total += ( token.getAstext() != null ? token.getAstext().length( ) : 0 );
370                        }
371                        if ( token.getTokensAfter( ).size( ) > 0 )
372                        {
373                                total += textLengthVT( token.getTokensAfter( ),
374                                                0,
375                                                token.getTokensAfter( ).size( ) );
376                        }
377                }
378
379                return total;
380        }
381
382        /**
383         * calcaulate the text length from a given position to the line start. VT is
384         * the abbr for "virtual token" (which contains before tokens, after tokens
385         * and replace token)
386         * 
387         * @param list
388         * @param pos
389         * @return
390         */
391        public static int curColumnNumberVT( TSourceTokenList list, int pos )
392        {
393
394                int total = 0;
395
396                for ( int i = pos; i >= 0; i-- )
397                {
398                        TSourceToken token = list.get( i );
399                        
400                        if(token.tokenstatus == ETokenStatus.tsdeleted) {
401                                continue;
402                        } 
403
404                        // the after tokens in the last sourceToken should not calculated.
405                        if ( i != pos && token.getTokensAfter( ).size( ) > 0 )
406                        {
407                                for ( int j = token.getTokensAfter( ).size( ) - 1; j >= 0; j-- )
408                                {
409                                        TSourceToken after = token.getTokensAfter( ).get( j );
410                                        if ( after.tokentype != ETokenType.ttreturn )
411                                        {
412                                                total += ( after.getAstext() != null ? after.getAstext().length( )
413                                                                : 0 );
414                                        }
415                                        else
416                                        {
417                                                return total;
418                                        }
419                                }
420                        }
421                        if ( i != pos )
422                        {
423                                if ( token.getReplaceToken( ) != null )
424                                {
425                                        if ( token.getReplaceToken( ).tokentype != ETokenType.ttreturn )
426                                        {
427                                                total += ( token.getReplaceToken().getAstext() != null ? token.getReplaceToken().getAstext().length( )
428                                                                : 0 );
429                                        }
430                                        else
431                                        {
432                                                return total;
433                                        }
434                                }
435                                else
436                                {
437                                        if ( token.tokentype != ETokenType.ttreturn )
438                                        {
439                                                if(token.tokentype == ETokenType.ttsimplecomment) {
440                                                        if(token.nextToken()!=null &&
441                                                                        token.nextToken().getAstext() !=null &&
442                                                                        token.nextToken().getAstext().trim().length() == 0) {
443                                                                total -= token.nextToken().getAstext().length( );
444                                                        }
445                                                        continue;
446                                                }
447                                                if(isOracleHint(token)) {
448                                                        if(token.nextToken()!=null &&
449                                                                        token.nextToken().getAstext() !=null &&
450                                                                        token.nextToken().getAstext().trim().length() == 0) {
451                                                                total -= token.nextToken().getAstext().length( );
452                                                        }
453                                                        continue;
454                                                }
455                                                total += ( token.getAstext() != null ? token.getAstext().length( )
456                                                                : 0 );
457                                        }
458                                        else
459                                        {
460                                                return total;
461                                        }
462                                }
463                        }
464
465                        if ( token.getTokensBefore( ).size( ) > 0 )
466                        {
467                                for ( int j = token.getTokensBefore( ).size( ) - 1; j >= 0; j-- )
468                                {
469                                        TSourceToken before = token.getTokensBefore( ).get( j );
470                                        if ( before.tokentype != ETokenType.ttreturn )
471                                        {
472                                                total += ( before.getAstext() != null ? before.getAstext().length( )
473                                                                : 0 );
474                                        }
475                                        else
476                                        {
477                                                return total;
478                                        }
479                                }
480                        }
481                }
482
483                return total;
484        }
485
486        private static boolean isOracleHint(TSourceToken token) {
487                return token!=null && token.getAstext() !=null && token.getAstext().startsWith("/*+");
488        }
489
490        /**
491         * calcaulate the text length from a given position to the line start. VT is
492         * the abbr for "virtual token" (which contains before tokens, after tokens
493         * and replace token)
494         * 
495         * @param token
496         * @return
497         */
498        public static int curColumnNumberVT( TSourceToken token )
499        {
500                return curColumnNumberVT( token.container, token.posinlist );
501        }
502
503        /**
504         * calculate the current indent length VT is the abbr for "virtual token"
505         * (which contains before tokens, after tokens and replace token)
506         * 
507         * @param list
508         * @param pos
509         * @return
510         */
511        public static int curIndentLenVT( TSourceTokenList list, int pos )
512        {
513                int indentLen = 0;
514                for ( int i = pos; i >= 0; i-- )
515                {
516                        TSourceToken token = list.get( i );
517                        
518                        if(token.tokenstatus == ETokenStatus.tsdeleted) {
519                                continue;
520                        } 
521
522                        // the after tokens in the last sourceToken should not calculated.
523                        if ( i != pos && token.getTokensAfter( ).size( ) > 0 )
524                        {
525                                for ( int j = token.getTokensAfter( ).size( ) - 1; j >= 0; j-- )
526                                {
527                                        TSourceToken after = token.getTokensAfter( ).get( j );
528                                        if ( after.tokentype == ETokenType.ttreturn )
529                                        {
530                                                return indentLen;
531                                        }
532                                        else if ( after.tokentype == ETokenType.ttwhitespace )
533                                        {
534                                                indentLen += ( after.getAstext() != null ? after.getAstext().length( )
535                                                                : 0 );
536                                        }
537                                        else
538                                        {
539                                                indentLen = 0;
540                                        }
541                                }
542                        }
543
544                        if ( i != pos )
545                        {
546                                if ( token.getReplaceToken( ) != null )
547                                {
548                                        if ( token.getReplaceToken( ).tokentype == ETokenType.ttreturn )
549                                        {
550                                                return indentLen;
551                                        }
552                                        else if ( token.getReplaceToken( ).tokentype == ETokenType.ttwhitespace )
553                                        {
554                                                indentLen += ( token.getReplaceToken().getAstext() != null ? token.getReplaceToken().getAstext().length( )
555                                                                : 0 );
556                                        }
557                                        else
558                                        {
559                                                indentLen = 0;
560                                        }
561
562                                }
563                                else
564                                {
565                                        if ( token.tokentype == ETokenType.ttreturn )
566                                        {
567                                                return indentLen;
568                                        }
569                                        else if ( token.tokentype == ETokenType.ttwhitespace )
570                                        {
571                                                indentLen += ( token.getAstext() != null ? token.getAstext().length( )
572                                                                : 0 );
573                                        }
574                                        else
575                                        {
576                                                indentLen = 0;
577                                        }
578                                }
579                        }
580
581                        if ( token.getTokensBefore( ).size( ) > 0 )
582                        {
583                                for ( int j = token.getTokensBefore( ).size( ) - 1; j >= 0; j-- )
584                                {
585                                        TSourceToken before = token.getTokensBefore( ).get( j );
586                                        if ( before.tokentype == ETokenType.ttreturn )
587                                        {
588                                                return indentLen;
589                                        }
590                                        else if ( before.tokentype == ETokenType.ttwhitespace )
591                                        {
592                                                indentLen += ( before.getAstext() != null ? before.getAstext().length( )
593                                                                : 0 );
594                                        }
595                                        else
596                                        {
597                                                indentLen = 0;
598                                        }
599                                }
600                        }
601
602                }
603                return indentLen;
604        }
605
606        /**
607         * calculate the current indent length VT is the abbr for "virtual token"
608         * (which contains before tokens, after tokens and replace token)
609         * 
610         * @param token
611         * @return
612         */
613        public static int curIndentLenVT( TSourceToken token )
614        {
615                return curIndentLenVT( token.container, token.posinlist );
616        }
617
618        /**
619         * create the white space source token
620         * 
621         * @param length
622         * @return
623         */
624        public static TSourceToken createWhitespaceSourceToken( int length )
625        {
626                return WhitespaceSourceTokenHelper.createWhitespaceSourceToken( length );
627        }
628
629        private static TSourceToken noFormatFlag = null;
630        static
631        {
632                noFormatFlag = new TSourceToken( "" );
633                noFormatFlag.tokentype = ETokenType.ttwhitespace;
634        }
635
636        public static TSourceToken createNoFormatFlagToken( )
637        {
638                return noFormatFlag;
639        }
640
641        public static TSourceToken createReturnSourceToken( )
642        {
643                TSourceToken t = new TSourceToken( "\n" );
644                t.tokentype = ETokenType.ttreturn;
645                return t;
646        }
647
648        private static final TSourceToken statementBreak = createReturnSourceToken( );
649
650        /**
651         * The one line break placed before a statement that started on the line
652         * of the previous statement's terminator, so that the columns computed
653         * while the statement is formatted start from that break. The empty-line
654         * pass between statements removes it again and lays out the real breaks.
655         */
656        public static TSourceToken createStatementBreakToken( )
657        {
658                return statementBreak;
659        }
660
661        /**
662         * combine all the white spaces into a single white space
663         * 
664         * @param list
665         * @param startPos
666         * @param endPos
667         */
668        public static void combineWhitespace( TSourceTokenList list, int startPos,
669                        int endPos )
670        {
671                boolean findWhitespace = false;
672                for ( int i = startPos; i < endPos; i++ )
673                {
674                        if ( i >= 0 && i < list.size( ) )
675                        {
676                                TSourceToken token = list.get( i );
677                                
678                                if(token.tokenstatus == ETokenStatus.tsdeleted) {
679                                        continue;
680                                } 
681                                
682                                if ( token.tokentype == ETokenType.ttwhitespace )
683                                {
684                                        token.setReplaceToken( SourceTokenConstant.EMPTY );
685                                        findWhitespace = true;
686                                }
687                        }
688                }
689                if ( findWhitespace )
690                {
691                        list.get( endPos )
692                                        .getTokensBefore( )
693                                        .add( createWhitespaceSourceToken( 1 ) );
694                }
695        }
696
697        /**
698         * insert the source token before the target source token
699         * 
700         * @param targetToken
701         * @param needAddToken
702         */
703        public static void addBefore( GFmtOpt opt, TSourceToken targetToken,
704                        TSourceToken needAddToken )
705        {
706                if ( needAddToken == null )
707                {
708                        return;
709                }
710
711                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
712                if ( buf != null )
713                {
714                        buf.append( "AddBefore:\ntargetToken:" )
715                                        .append(targetToken.getAstext())
716                                        .append( "(pos:" )
717                                        .append( targetToken.posinlist )
718                                        .append( ")" )
719                                        .append( " newToken:" )
720                                        .append( escapeNewlines( needAddToken.getAstext() ) )
721                                        .append( "(type:" )
722                                        .append( needAddToken.tokentype )
723                                        .append( ")" );
724                }
725
726                if ( buf != null && targetToken.getTokensBefore( ).size( ) > 0 )
727                {
728                        buf.append( "\noldTokenBefore:" );
729                        for ( int i = 0; i < targetToken.getTokensBefore( ).size( ); i++ )
730                        {
731                                TSourceToken token = targetToken.getTokensBefore( ).get( i );
732                                
733                                if(token.tokenstatus == ETokenStatus.tsdeleted) {
734                                        continue;
735                                } 
736                                
737                                buf.append( "[" )
738                                                .append( token.getAstext() != null ? escapeNewlines( token.getAstext() )
739                                                                : "" )
740                                                .append( "] " )
741                                                .append( "(type:" )
742                                                .append( token.tokentype )
743                                                .append( ")" );
744                        }
745                }
746
747                if ( buf != null )
748                {
749                        PPLogger.info( 2, buf.toString( ), null );
750                }
751
752                if ( opt.opearateSourceToken )
753                {
754                        targetToken.getTokensBefore( ).add( needAddToken );
755                }
756        }
757
758        public static void addAfter( GFmtOpt opt, TSourceToken targetToken,
759                        TSourceToken needAddToken )
760        {
761                if ( needAddToken == null )
762                {
763                        return;
764                }
765
766                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
767                if ( buf != null )
768                {
769                        buf.append( "AddAfter:\ntargetToken:" )
770                                        .append(targetToken.getAstext())
771                                        .append( "(pos:" )
772                                        .append( targetToken.posinlist )
773                                        .append( ")" )
774                                        .append( " newToken:" )
775                                        .append( needAddToken.getAstext() != null ? escapeNewlines( needAddToken.getAstext() )
776                                                        : "" )
777                                        .append( "(type:" )
778                                        .append( needAddToken.tokentype )
779                                        .append( ")" );
780                }
781
782                if ( buf != null && targetToken.getTokensAfter( ).size( ) > 0 )
783                {
784                        buf.append( "\noldTokenAfter:" );
785                        for ( int i = 0; i < targetToken.getTokensAfter( ).size( ); i++ )
786                        {
787                                TSourceToken token = targetToken.getTokensAfter( ).get( i );
788                                
789                                if(token.tokenstatus == ETokenStatus.tsdeleted) {
790                                        continue;
791                                } 
792                                
793                                buf.append( "[" )
794                                                .append( token.getAstext() != null ? escapeNewlines( token.getAstext() )
795                                                                : "" )
796                                                .append( "] " )
797                                                .append( "(type:" )
798                                                .append( token.tokentype )
799                                                .append( ")" );
800                        }
801                }
802
803                if ( buf != null )
804                {
805                        PPLogger.info( 2, buf.toString( ), null );
806                }
807
808                if ( opt.opearateSourceToken )
809                {
810                        targetToken.getTokensAfter( ).add( needAddToken );
811                }
812        }
813
814        /**
815         * remove all source token in the before source token list VT is the abbr
816         * for "virtual token" (that this method will operates before tokens, after
817         * tokens and replace token)
818         * 
819         * @param targetToken
820         */
821        public static void removeAllBeforeTokenVT( GFmtOpt opt,
822                        TSourceToken targetToken )
823        {
824                StringBuilder buf = PPLogger.isInfoEnabled( ) ? new StringBuilder( ) : null;
825                TSourceTokenList before = targetToken.getTokensBefore( );
826                if ( buf != null )
827                {
828                        buf.append( "RemoveAllBeforeTokenVT:" );
829                        for ( int i = 0; i < before.size( ); i++ )
830                        {
831                                TSourceToken token = before.get( i );
832                                // a deleted token prints nothing, so the log does not list it
833                                if ( token.tokenstatus == ETokenStatus.tsdeleted )
834                                {
835                                        continue;
836                                }
837                                buf.append( "\nremoveToken:" )
838                                                .append( "[" )
839                                                .append( token.getAstext() != null ? escapeNewlines( token.getAstext() )
840                                                                : "" )
841                                                .append( "] " )
842                                                .append( "(type:" )
843                                                .append( token.tokentype )
844                                                .append( ")" );
845                        }
846                }
847                // Every token goes, deleted ones included. The loop this replaces removed the head token only while
848                // operating and skipped a deleted head without removing it, so a deleted token, or a run with
849                // opearateSourceToken off, left the list non-empty and the loop never ended.
850                if ( opt.opearateSourceToken )
851                {
852                        before.clear( );
853                }
854                if ( buf != null )
855                {
856                        PPLogger.info( 2, buf.toString( ), null );
857                }
858        }
859
860        public static void removeThisToken( TSourceToken token )
861        {
862                token.setReplaceToken( SourceTokenOperator.createWhitespaceSourceToken( 1 ) );
863        }
864
865        /** The text with each line feed written as the two characters backslash, n: the form a log line shows. */
866        private static String escapeNewlines( String text )
867        {
868                if ( text.indexOf( '\n' ) < 0 )
869                {
870                        return text;
871                }
872                StringBuilder b = new StringBuilder( text.length( ) + 8 );
873                for ( int i = 0; i < text.length( ); i++ )
874                {
875                        char c = text.charAt( i );
876                        if ( c == '\n' )
877                        {
878                                b.append( "\\n" );
879                        }
880                        else
881                        {
882                                b.append( c );
883                        }
884                }
885                return b.toString( );
886        }
887}