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 org.apache.maven.doxia.sink.SinkEventAttributes;
29  import org.apache.maven.doxia.sink.impl.AbstractTextSink;
30  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
31  import org.codehaus.plexus.util.StringUtils;
32  
33  /**
34   * APT generator implementation.
35   * <br>
36   * <b>Note</b>: The encoding used is UTF-8.
37   *
38   * @author eredmond
39   * @since 1.0
40   */
41  public class AptSink
42      extends AbstractTextSink
43      implements AptMarkup
44  {
45      // ----------------------------------------------------------------------
46      // Instance fields
47      // ----------------------------------------------------------------------
48  
49      /**  A buffer that holds the current text when headerFlag or bufferFlag set to <code>true</code>. */
50      private StringBuffer buffer;
51  
52      /**  A buffer that holds the table caption. */
53      private StringBuilder tableCaptionBuffer;
54  
55      /**  author. */
56      private String author;
57  
58      /**  title. */
59      private String title;
60  
61      /**  date. */
62      private String date;
63  
64      /** startFlag. */
65      private boolean startFlag;
66  
67      /**  tableCaptionFlag. */
68      private boolean tableCaptionFlag;
69  
70      /**  tableCellFlag. */
71      private boolean tableCellFlag;
72  
73      /**  headerFlag. */
74      private boolean headerFlag;
75  
76      /**  bufferFlag. */
77      private boolean bufferFlag;
78  
79      /**  itemFlag. */
80      private boolean itemFlag;
81  
82      /**  verbatimFlag. */
83      private boolean verbatimFlag;
84  
85      /**  boxed verbatim. */
86      private boolean isBoxed;
87  
88      /**  gridFlag for tables. */
89      private boolean gridFlag;
90  
91      /**  number of cells in a table. */
92      private int cellCount;
93  
94      /**  The writer to use. */
95      private final PrintWriter writer;
96  
97      /**  justification of table cells. */
98      private int[] cellJustif;
99  
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 }