001package org.apache.maven.doxia.module.apt;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.PrintWriter;
023import java.io.Writer;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Stack;
027
028import org.apache.maven.doxia.sink.SinkEventAttributes;
029import org.apache.maven.doxia.sink.impl.AbstractTextSink;
030import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
031import org.codehaus.plexus.util.StringUtils;
032
033/**
034 * APT generator implementation.
035 * <br>
036 * <b>Note</b>: The encoding used is UTF-8.
037 *
038 * @author eredmond
039 * @since 1.0
040 */
041public class AptSink
042    extends AbstractTextSink
043    implements AptMarkup
044{
045    // ----------------------------------------------------------------------
046    // Instance fields
047    // ----------------------------------------------------------------------
048
049    /**  A buffer that holds the current text when headerFlag or bufferFlag set to <code>true</code>. */
050    private StringBuffer buffer;
051
052    /**  A buffer that holds the table caption. */
053    private StringBuilder tableCaptionBuffer;
054
055    /**  author. */
056    private String author;
057
058    /**  title. */
059    private String title;
060
061    /**  date. */
062    private String date;
063
064    /** startFlag. */
065    private boolean startFlag;
066
067    /**  tableCaptionFlag. */
068    private boolean tableCaptionFlag;
069
070    /**  tableCellFlag. */
071    private boolean tableCellFlag;
072
073    /**  headerFlag. */
074    private boolean headerFlag;
075
076    /**  bufferFlag. */
077    private boolean bufferFlag;
078
079    /**  itemFlag. */
080    private boolean itemFlag;
081
082    /**  verbatimFlag. */
083    private boolean verbatimFlag;
084
085    /**  boxed verbatim. */
086    private boolean isBoxed;
087
088    /**  gridFlag for tables. */
089    private boolean gridFlag;
090
091    /**  number of cells in a table. */
092    private int cellCount;
093
094    /**  The writer to use. */
095    private final PrintWriter writer;
096
097    /**  justification of table cells. */
098    private int[] cellJustif;
099
100    /**  a line of a row in a table. */
101    private String rowLine;
102
103    /**  listNestingIndent. */
104    private String listNestingIndent;
105
106    /**  listStyles. */
107    private final Stack<String> listStyles;
108
109    /** Keep track of the closing tags for inline events. */
110    protected Stack<List<String>> inlineStack = new Stack<>();
111
112    // ----------------------------------------------------------------------
113    // Public protected methods
114    // ----------------------------------------------------------------------
115
116    /**
117     * Constructor, initialize the Writer and the variables.
118     *
119     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
120     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
121     */
122    protected AptSink( Writer writer )
123    {
124        this.writer = new PrintWriter( writer );
125        this.listStyles = new Stack<>();
126
127        init();
128    }
129
130    /**
131     * Returns the buffer that holds the current text.
132     *
133     * @return A StringBuffer.
134     */
135    protected StringBuffer getBuffer()
136    {
137        return buffer;
138    }
139
140    /**
141     * Used to determine whether we are in head mode.
142     *
143     * @param headFlag True for head mode.
144     */
145    protected void setHeadFlag( boolean headFlag )
146    {
147        this.headerFlag = headFlag;
148    }
149
150    /**
151     * Reset all variables.
152     *
153     * @deprecated since 1.1.2, use {@link #init()} instead of.
154     */
155    protected void resetState()
156    {
157        init();
158    }
159
160    /**
161     * {@inheritDoc}
162     */
163    protected void init()
164    {
165        super.init();
166
167        resetBuffer();
168
169        this.tableCaptionBuffer = new StringBuilder();
170        this.listNestingIndent = "";
171
172        this.author = null;
173        this.title = null;
174        this.date = null;
175        this.startFlag = true;
176        this.tableCaptionFlag = false;
177        this.tableCellFlag = false;
178        this.headerFlag = false;
179        this.bufferFlag = false;
180        this.itemFlag = false;
181        this.verbatimFlag = false;
182        this.isBoxed = false;
183        this.gridFlag = false;
184        this.cellCount = 0;
185        this.cellJustif = null;
186        this.rowLine = null;
187        this.listStyles.clear();
188        this.inlineStack.clear();
189    }
190
191    /**
192     * Reset the StringBuilder.
193     */
194    protected void resetBuffer()
195    {
196        buffer = new StringBuffer();
197    }
198
199    /**
200     * Reset the TableCaptionBuffer.
201     */
202    protected void resetTableCaptionBuffer()
203    {
204        tableCaptionBuffer = new StringBuilder();
205    }
206
207    /**
208     * {@inheritDoc}
209     */
210    public void head()
211    {
212        boolean startFlag = this.startFlag;
213
214        init();
215
216        headerFlag = true;
217        this.startFlag = startFlag;
218    }
219
220    /**
221     * {@inheritDoc}
222     */
223    public void head_()
224    {
225        headerFlag = false;
226
227        if ( ! startFlag )
228        {
229            write( EOL );
230        }
231        write( HEADER_START_MARKUP + EOL );
232        if ( title != null )
233        {
234            write( " " + title + EOL );
235        }
236        write( HEADER_START_MARKUP + EOL );
237        if ( author != null )
238        {
239            write( " " + author + EOL );
240        }
241        write( HEADER_START_MARKUP + EOL );
242        if ( date != null )
243        {
244            write( " " + date + EOL );
245        }
246        write( HEADER_START_MARKUP + EOL );
247    }
248
249    /**
250     * {@inheritDoc}
251     */
252    public void title_()
253    {
254        if ( buffer.length() > 0 )
255        {
256            title = buffer.toString();
257            resetBuffer();
258        }
259    }
260
261    /**
262     * {@inheritDoc}
263     */
264    public void author_()
265    {
266        if ( buffer.length() > 0 )
267        {
268            author = buffer.toString();
269            resetBuffer();
270        }
271    }
272
273    /**
274     * {@inheritDoc}
275     */
276    public void date_()
277    {
278        if ( buffer.length() > 0 )
279        {
280            date = buffer.toString();
281            resetBuffer();
282        }
283    }
284
285    /**
286     * {@inheritDoc}
287     */
288    public void section1_()
289    {
290        write( EOL );
291    }
292
293    /**
294     * {@inheritDoc}
295     */
296    public void section2_()
297    {
298        write( EOL );
299    }
300
301    /**
302     * {@inheritDoc}
303     */
304    public void section3_()
305    {
306        write( EOL );
307    }
308
309    /**
310     * {@inheritDoc}
311     */
312    public void section4_()
313    {
314        write( EOL );
315    }
316
317    /**
318     * {@inheritDoc}
319     */
320    public void section5_()
321    {
322        write( EOL );
323    }
324
325    /**
326     * {@inheritDoc}
327     */
328    public void sectionTitle1()
329    {
330        write( EOL );
331    }
332
333    /**
334     * {@inheritDoc}
335     */
336    public void sectionTitle1_()
337    {
338        write( EOL + EOL );
339    }
340
341    /**
342     * {@inheritDoc}
343     */
344    public void sectionTitle2()
345    {
346        write( EOL + SECTION_TITLE_START_MARKUP );
347    }
348
349    /**
350     * {@inheritDoc}
351     */
352    public void sectionTitle2_()
353    {
354        write( EOL + EOL );
355    }
356
357    /**
358     * {@inheritDoc}
359     */
360    public void sectionTitle3()
361    {
362        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 2 ) );
363    }
364
365    /**
366     * {@inheritDoc}
367     */
368    public void sectionTitle3_()
369    {
370        write( EOL + EOL );
371    }
372
373    /**
374     * {@inheritDoc}
375     */
376    public void sectionTitle4()
377    {
378        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 3 ) );
379    }
380
381    /**
382     * {@inheritDoc}
383     */
384    public void sectionTitle4_()
385    {
386        write( EOL + EOL );
387    }
388
389    /**
390     * {@inheritDoc}
391     */
392    public void sectionTitle5()
393    {
394        write( EOL + StringUtils.repeat( SECTION_TITLE_START_MARKUP, 4 ) );
395    }
396
397    /**
398     * {@inheritDoc}
399     */
400    public void sectionTitle5_()
401    {
402        write( EOL + EOL );
403    }
404
405    /**
406     * {@inheritDoc}
407     */
408    public void list()
409    {
410        listNestingIndent += " ";
411        listStyles.push( LIST_START_MARKUP );
412        write( EOL );
413    }
414
415    /**
416     * {@inheritDoc}
417     */
418    public void list_()
419    {
420        if ( listNestingIndent.length() <= 1 )
421        {
422            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
423        }
424        else
425        {
426            write( EOL );
427        }
428        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
429        listStyles.pop();
430        itemFlag = false;
431    }
432
433    /**
434     * {@inheritDoc}
435     */
436    public void listItem()
437    {
438        //if ( !numberedList )
439        //write( EOL + listNestingIndent + "*" );
440        //else
441        numberedListItem();
442        itemFlag = true;
443    }
444
445    /**
446     * {@inheritDoc}
447     */
448    public void listItem_()
449    {
450        write( EOL );
451        itemFlag = false;
452    }
453
454    /** {@inheritDoc} */
455    public void numberedList( int numbering )
456    {
457        listNestingIndent += " ";
458        write( EOL );
459
460        String style;
461        switch ( numbering )
462        {
463            case NUMBERING_UPPER_ALPHA:
464                style = String.valueOf( NUMBERING_UPPER_ALPHA_CHAR );
465                break;
466            case NUMBERING_LOWER_ALPHA:
467                style = String.valueOf( NUMBERING_LOWER_ALPHA_CHAR );
468                break;
469            case NUMBERING_UPPER_ROMAN:
470                style = String.valueOf( NUMBERING_UPPER_ROMAN_CHAR );
471                break;
472            case NUMBERING_LOWER_ROMAN:
473                style = String.valueOf( NUMBERING_LOWER_ROMAN_CHAR );
474                break;
475            case NUMBERING_DECIMAL:
476            default:
477                style = String.valueOf( NUMBERING );
478        }
479
480        listStyles.push( style );
481    }
482
483    /**
484     * {@inheritDoc}
485     */
486    public void numberedList_()
487    {
488        if ( listNestingIndent.length() <= 1 )
489        {
490            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
491        }
492        else
493        {
494            write( EOL );
495        }
496        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
497        listStyles.pop();
498        itemFlag = false;
499    }
500
501    /**
502     * {@inheritDoc}
503     */
504    public void numberedListItem()
505    {
506        String style = listStyles.peek();
507        if ( style.equals( String.valueOf( STAR ) ) )
508        {
509            write( EOL + listNestingIndent + STAR + SPACE );
510        }
511        else
512        {
513            write( EOL + listNestingIndent + LEFT_SQUARE_BRACKET
514                + LEFT_SQUARE_BRACKET + style + RIGHT_SQUARE_BRACKET
515                + RIGHT_SQUARE_BRACKET + SPACE );
516        }
517        itemFlag = true;
518    }
519
520    /**
521     * {@inheritDoc}
522     */
523    public void numberedListItem_()
524    {
525        write( EOL );
526        itemFlag = false;
527    }
528
529    /**
530     * {@inheritDoc}
531     */
532    public void definitionList()
533    {
534        listNestingIndent += " ";
535        listStyles.push( "" );
536        write( EOL );
537    }
538
539    /**
540     * {@inheritDoc}
541     */
542    public void definitionList_()
543    {
544        if ( listNestingIndent.length() <= 1 )
545        {
546            write( EOL + listNestingIndent + LIST_END_MARKUP + EOL );
547        }
548        else
549        {
550            write( EOL );
551        }
552        listNestingIndent = StringUtils.chomp( listNestingIndent, " " );
553        listStyles.pop();
554        itemFlag = false;
555    }
556
557    /**
558     * {@inheritDoc}
559     */
560    public void definedTerm()
561    {
562        write( EOL + " [" );
563    }
564
565    /**
566     * {@inheritDoc}
567     */
568    public void definedTerm_()
569    {
570        write( "] " );
571    }
572
573    /**
574     * {@inheritDoc}
575     */
576    public void definition()
577    {
578        itemFlag = true;
579    }
580
581    /**
582     * {@inheritDoc}
583     */
584    public void definition_()
585    {
586        write( EOL );
587        itemFlag = false;
588    }
589
590    /**
591     * {@inheritDoc}
592     */
593    public void pageBreak()
594    {
595        write( EOL + PAGE_BREAK + EOL );
596    }
597
598    /**
599     * {@inheritDoc}
600     */
601    public void paragraph()
602    {
603        if ( tableCellFlag )
604        {
605            // ignore paragraphs in table cells
606        }
607        else if ( itemFlag )
608        {
609            write( EOL + EOL + "  " + listNestingIndent );
610        }
611        else
612        {
613            write( EOL + " " );
614        }
615    }
616
617    /**
618     * {@inheritDoc}
619     */
620    public void paragraph_()
621    {
622        if ( tableCellFlag )
623        {
624            // ignore paragraphs in table cells
625        }
626        else
627        {
628            write( EOL + EOL );
629        }
630    }
631
632    /** {@inheritDoc} */
633    public void verbatim( boolean boxed )
634    {
635        verbatimFlag = true;
636        this.isBoxed = boxed;
637        write( EOL );
638        if ( boxed )
639        {
640            write( EOL + BOXED_VERBATIM_START_MARKUP + EOL );
641        }
642        else
643        {
644            write( EOL + NON_BOXED_VERBATIM_START_MARKUP + EOL );
645        }
646    }
647
648    /**
649     * {@inheritDoc}
650     */
651    public void verbatim_()
652    {
653        if ( isBoxed )
654        {
655            write( EOL + BOXED_VERBATIM_END_MARKUP + EOL );
656        }
657        else
658        {
659            write( EOL + NON_BOXED_VERBATIM_END_MARKUP + EOL );
660        }
661        isBoxed = false;
662        verbatimFlag = false;
663    }
664
665    /**
666     * {@inheritDoc}
667     */
668    public void horizontalRule()
669    {
670        write( EOL + HORIZONTAL_RULE_MARKUP + EOL );
671    }
672
673    /**
674     * {@inheritDoc}
675     */
676    public void table()
677    {
678        write( EOL );
679    }
680
681    /**
682     * {@inheritDoc}
683     */
684    public void table_()
685    {
686        if ( rowLine != null )
687        {
688            write( rowLine );
689        }
690        rowLine = null;
691
692        if ( tableCaptionBuffer.length() > 0 )
693        {
694            text( tableCaptionBuffer.toString() + EOL );
695        }
696
697        resetTableCaptionBuffer();
698    }
699
700    /** {@inheritDoc} */
701    public void tableRows( int[] justification, boolean grid )
702    {
703        cellJustif = justification;
704        gridFlag = grid;
705    }
706
707    /**
708     * {@inheritDoc}
709     */
710    public void tableRows_()
711    {
712        cellJustif = null;
713        gridFlag = false;
714    }
715
716    /**
717     * {@inheritDoc}
718     */
719    public void tableRow()
720    {
721        bufferFlag = true;
722        cellCount = 0;
723    }
724
725    /**
726     * {@inheritDoc}
727     */
728    public void tableRow_()
729    {
730        bufferFlag = false;
731
732        // write out the header row first, then the data in the buffer
733        buildRowLine();
734
735        write( rowLine );
736
737        // TODO: This will need to be more clever, for multi-line cells
738        if ( gridFlag )
739        {
740            write( TABLE_ROW_SEPARATOR_MARKUP );
741        }
742
743        write( buffer.toString() );
744
745        resetBuffer();
746
747        write( EOL );
748
749        // only reset cell count if this is the last row
750        cellCount = 0;
751    }
752
753    /** Construct a table row. */
754    private void buildRowLine()
755    {
756        StringBuilder rLine = new StringBuilder();
757        rLine.append( TABLE_ROW_START_MARKUP );
758
759        for ( int i = 0; i < cellCount; i++ )
760        {
761            if ( cellJustif != null )
762            {
763                switch ( cellJustif[i] )
764                {
765                case 1:
766                    rLine.append( TABLE_COL_LEFT_ALIGNED_MARKUP );
767                    break;
768                case 2:
769                    rLine.append( TABLE_COL_RIGHT_ALIGNED_MARKUP );
770                    break;
771                default:
772                    rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
773                }
774            }
775            else
776            {
777                rLine.append( TABLE_COL_CENTERED_ALIGNED_MARKUP );
778            }
779        }
780        rLine.append( EOL );
781
782        this.rowLine = rLine.toString();
783    }
784
785    /**
786     * {@inheritDoc}
787     */
788    public void tableCell()
789    {
790        tableCell( false );
791    }
792
793    /**
794     * {@inheritDoc}
795     */
796    public void tableHeaderCell()
797    {
798        tableCell( true );
799    }
800
801    /**
802     * Starts a table cell.
803     *
804     * @param headerRow If this cell is part of a header row.
805     */
806    public void tableCell( boolean headerRow )
807    {
808        if ( headerRow )
809        {
810            buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
811        }
812        tableCellFlag = true;
813    }
814
815    /**
816     * {@inheritDoc}
817     */
818    public void tableCell_()
819    {
820        endTableCell();
821    }
822
823    /**
824     * {@inheritDoc}
825     */
826    public void tableHeaderCell_()
827    {
828        endTableCell();
829    }
830
831    /**
832     * Ends a table cell.
833     */
834    private void endTableCell()
835    {
836        tableCellFlag = false;
837        buffer.append( TABLE_CELL_SEPARATOR_MARKUP );
838        cellCount++;
839    }
840
841    /**
842     * {@inheritDoc}
843     */
844    public void tableCaption()
845    {
846        tableCaptionFlag = true;
847    }
848
849    /**
850     * {@inheritDoc}
851     */
852    public void tableCaption_()
853    {
854        tableCaptionFlag = false;
855    }
856
857    /**
858     * {@inheritDoc}
859     */
860    public void figureCaption_()
861    {
862        write( EOL );
863    }
864
865    /** {@inheritDoc} */
866    public void figureGraphics( String name )
867    {
868        write( EOL + "[" + name + "] " );
869    }
870
871    /** {@inheritDoc} */
872    public void anchor( String name )
873    {
874        write( ANCHOR_START_MARKUP );
875    }
876
877    /**
878     * {@inheritDoc}
879     */
880    public void anchor_()
881    {
882        write( ANCHOR_END_MARKUP );
883    }
884
885    /** {@inheritDoc} */
886    public void link( String name )
887    {
888        if ( !headerFlag )
889        {
890            write( LINK_START_1_MARKUP );
891            text( name.startsWith( "#" ) ? name.substring( 1 ) : name );
892            write( LINK_START_2_MARKUP );
893        }
894    }
895
896    /**
897     * {@inheritDoc}
898     */
899    public void link_()
900    {
901        if ( !headerFlag )
902        {
903            write( LINK_END_MARKUP );
904        }
905    }
906
907    /**
908     * A link with a target.
909     *
910     * @param name The name of the link.
911     * @param target The link target.
912     */
913    public void link( String name, String target )
914    {
915        if ( !headerFlag )
916        {
917            write( LINK_START_1_MARKUP );
918            text( target );
919            write( LINK_START_2_MARKUP );
920            text( name );
921        }
922    }
923
924    /**
925     * {@inheritDoc}
926     */
927    public void inline()
928    {
929        inline( null );
930    }
931
932    /** {@inheritDoc} */
933    public void inline( SinkEventAttributes attributes )
934    {
935        if ( !headerFlag )
936        {
937            List<String> tags = new ArrayList<>();
938
939            if ( attributes != null )
940            {
941
942                if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) )
943                {
944                    write( ITALIC_START_MARKUP );
945                    tags.add( 0, ITALIC_END_MARKUP );
946                }
947
948                if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) )
949                {
950                    write( BOLD_START_MARKUP );
951                    tags.add( 0, BOLD_END_MARKUP );
952                }
953
954                if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) )
955                {
956                    write( MONOSPACED_START_MARKUP );
957                    tags.add( 0, MONOSPACED_END_MARKUP );
958                }
959
960            }
961
962            inlineStack.push( tags );
963        }
964    }
965
966    /**
967     * {@inheritDoc}
968     */
969    public void inline_()
970    {
971        if ( !headerFlag )
972        {
973            for ( String tag: inlineStack.pop() )
974            {
975                write( tag );
976            }
977        }
978    }
979
980    /**
981     * {@inheritDoc}
982     */
983    public void italic()
984    {
985        inline( SinkEventAttributeSet.Semantics.ITALIC );
986    }
987
988    /**
989     * {@inheritDoc}
990     */
991    public void italic_()
992    {
993        inline_();
994    }
995
996    /**
997     * {@inheritDoc}
998     */
999    public void bold()
1000    {
1001        inline( SinkEventAttributeSet.Semantics.BOLD );
1002    }
1003
1004    /**
1005     * {@inheritDoc}
1006     */
1007    public void bold_()
1008    {
1009        inline_();
1010    }
1011
1012    /**
1013     * {@inheritDoc}
1014     */
1015    public void monospaced()
1016    {
1017        inline( SinkEventAttributeSet.Semantics.CODE );
1018    }
1019
1020    /**
1021     * {@inheritDoc}
1022     */
1023    public void monospaced_()
1024    {
1025        inline_();
1026    }
1027
1028    /**
1029     * {@inheritDoc}
1030     */
1031    public void lineBreak()
1032    {
1033        if ( headerFlag || bufferFlag )
1034        {
1035            buffer.append( EOL );
1036        }
1037        else if ( verbatimFlag )
1038        {
1039            write( EOL );
1040        }
1041        else
1042        {
1043            write( "\\" + EOL );
1044        }
1045    }
1046
1047    /**
1048     * {@inheritDoc}
1049     */
1050    public void nonBreakingSpace()
1051    {
1052        if ( headerFlag || bufferFlag )
1053        {
1054            buffer.append( NON_BREAKING_SPACE_MARKUP );
1055        }
1056        else
1057        {
1058            write( NON_BREAKING_SPACE_MARKUP );
1059        }
1060    }
1061
1062    /** {@inheritDoc} */
1063    public void text( String text )
1064    {
1065        if ( tableCaptionFlag )
1066        {
1067            tableCaptionBuffer.append( text );
1068        }
1069        else if ( headerFlag || bufferFlag )
1070        {
1071            buffer.append( text );
1072        }
1073        else if ( verbatimFlag )
1074        {
1075            verbatimContent( text );
1076        }
1077        else
1078        {
1079            content( text );
1080        }
1081    }
1082
1083    /** {@inheritDoc} */
1084    public void rawText( String text )
1085    {
1086        write( text );
1087    }
1088
1089    /** {@inheritDoc} */
1090    public void comment( String comment )
1091    {
1092        rawText( ( startFlag ? "" : EOL ) + COMMENT + COMMENT + comment );
1093    }
1094
1095    /**
1096     * {@inheritDoc}
1097     *
1098     * Unkown events just log a warning message but are ignored otherwise.
1099     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
1100     */
1101    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1102    {
1103        getLog().warn( "[Apt Sink] Unknown Sink event: '" + name + "', ignoring!" );
1104    }
1105
1106    /**
1107     * Write text to output.
1108     *
1109     * @param text The text to write.
1110     */
1111    protected void write( String text )
1112    {
1113        startFlag = false;
1114        if ( tableCellFlag )
1115        {
1116            buffer.append( text );
1117        }
1118        else
1119        {
1120            writer.write( unifyEOLs( text ) );
1121        }
1122    }
1123
1124    /**
1125     * Write Apt escaped text to output.
1126     *
1127     * @param text The text to write.
1128     */
1129    protected void content( String text )
1130    {
1131        write( escapeAPT( text ) );
1132    }
1133
1134    /**
1135     * Write Apt escaped text to output.
1136     *
1137     * @param text The text to write.
1138     */
1139    protected void verbatimContent( String text )
1140    {
1141        write( escapeAPT( text ) );
1142    }
1143
1144    /**
1145     * {@inheritDoc}
1146     */
1147    public void flush()
1148    {
1149        writer.flush();
1150    }
1151
1152    /**
1153     * {@inheritDoc}
1154     */
1155    public void close()
1156    {
1157        writer.close();
1158
1159        init();
1160    }
1161
1162    // ----------------------------------------------------------------------
1163    // Private methods
1164    // ----------------------------------------------------------------------
1165
1166    /**
1167     * Escape special characters in a text in APT:
1168     *
1169     * <pre>
1170     * \~, \=, \-, \+, \*, \[, \], \<, \>, \{, \}, \\
1171     * </pre>
1172     *
1173     * @param text the String to escape, may be null
1174     * @return the text escaped, "" if null String input
1175     */
1176    private static String escapeAPT( String text )
1177    {
1178        if ( text == null )
1179        {
1180            return "";
1181        }
1182
1183        int length = text.length();
1184        StringBuilder buffer = new StringBuilder( length );
1185
1186        for ( int i = 0; i < length; ++i )
1187        {
1188            char c = text.charAt( i );
1189            switch ( c )
1190            { // 0080
1191                case '\\':
1192                case '~':
1193                case '=':
1194                case '-':
1195                case '+':
1196                case '*':
1197                case '[':
1198                case ']':
1199                case '<':
1200                case '>':
1201                case '{':
1202                case '}':
1203                    buffer.append( '\\' );
1204                    buffer.append( c );
1205                    break;
1206                default:
1207                    buffer.append( c );
1208            }
1209        }
1210
1211        return buffer.toString();
1212    }
1213}