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