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