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