View Javadoc
1   package org.apache.maven.doxia.module.twiki;
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.StringWriter;
24  import java.io.Writer;
25  import java.util.ArrayList;
26  import java.util.List;
27  import java.util.Stack;
28  
29  import javax.swing.text.MutableAttributeSet;
30  import javax.swing.text.html.HTML.Attribute;
31  import javax.swing.text.html.HTML.Tag;
32  
33  import org.apache.maven.doxia.sink.SinkEventAttributes;
34  import org.apache.maven.doxia.sink.impl.AbstractTextSink;
35  import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
36  import org.apache.maven.doxia.sink.impl.SinkUtils;
37  import org.apache.maven.doxia.util.HtmlTools;
38  import org.codehaus.plexus.util.StringUtils;
39  
40  /**
41   * TWiki Sink implementation.
42   * <br>
43   * <b>Note</b>: The encoding used is UTF-8.
44   *
45   * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
46   * @since 1.0
47   */
48  public class TWikiSink
49      extends AbstractTextSink
50      implements TWikiMarkup
51  {
52      /**  The writer to use. */
53      private final PrintWriter out;
54  
55      /**  The writer to use. */
56      private StringWriter writer;
57  
58      /** An indication on if we're in bold mode. */
59      private boolean boldFlag;
60  
61      /** An indication on if we're in head mode. */
62      private boolean headFlag;
63  
64      private int levelList = 0;
65  
66      /**  listStyles. */
67      private final Stack<String> listStyles;
68  
69      /** Keep track of the nested bold flag. */
70      protected Stack<Boolean> boldStack = new Stack<>();
71  
72      /** Keep track of the closing tags for inline events. */
73      protected Stack<List<String>> inlineStack = new Stack<>();
74  
75      /**
76       * Constructor, initialize the Writer and the variables.
77       *
78       * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
79       * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
80       */
81      protected TWikiSink( Writer writer )
82      {
83          this.out = new PrintWriter( writer );
84          this.listStyles = new Stack<>();
85  
86          init();
87      }
88  
89      /** {@inheritDoc} */
90      public void anchor( String name )
91      {
92          write( EOL );
93          write( ANCHOR_MARKUP + name );
94      }
95  
96      /** {@inheritDoc} */
97      public void anchor( String name, SinkEventAttributes attributes )
98      {
99          anchor( name );
100     }
101 
102     /**
103      * Not used.
104      * {@inheritDoc}
105      */
106     public void anchor_()
107     {
108         // nop
109     }
110 
111     /**
112      * Not used.
113      * {@inheritDoc}
114      */
115     public void author()
116     {
117         // nop
118     }
119 
120     /** {@inheritDoc} */
121     public void author( SinkEventAttributes attributes )
122     {
123         author();
124     }
125 
126     /**
127      * Not used.
128      * {@inheritDoc}
129      */
130     public void author_()
131     {
132         // nop
133     }
134 
135     /**
136      * Not used.
137      * {@inheritDoc}
138      */
139     public void body()
140     {
141         // nop
142     }
143 
144     /** {@inheritDoc} */
145     public void body( SinkEventAttributes attributes )
146     {
147         body();
148     }
149 
150     /**
151      * Not used.
152      * {@inheritDoc}
153      */
154     public void body_()
155     {
156         // nop
157     }
158 
159     /**
160      * {@inheritDoc}
161      */
162     public void bold()
163     {
164         inline( SinkEventAttributeSet.Semantics.BOLD );
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
170     public void bold_()
171     {
172         inline_();
173     }
174 
175     /**
176      * Not used.
177      * {@inheritDoc}
178      */
179     public void comment( String comment )
180     {
181         // nop
182     }
183 
184     /**
185      * {@inheritDoc}
186      */
187     public void close()
188     {
189         out.write( writer.toString() );
190         out.close();
191 
192         init();
193     }
194 
195     /**
196      * Not used.
197      * {@inheritDoc}
198      */
199     public void date()
200     {
201         // nop
202     }
203 
204     /** {@inheritDoc} */
205     public void date( SinkEventAttributes attributes )
206     {
207         date();
208     }
209 
210     /**
211      * Not used.
212      * {@inheritDoc}
213      */
214     public void date_()
215     {
216         // nop
217     }
218 
219     /**
220      * Not used.
221      * {@inheritDoc}
222      */
223     public void definedTerm()
224     {
225         // nop
226     }
227 
228     /** {@inheritDoc} */
229     public void definedTerm( SinkEventAttributes attributes )
230     {
231         definedTerm();
232     }
233 
234     /**
235      * Not used.
236      * {@inheritDoc}
237      */
238     public void definedTerm_()
239     {
240         // nop
241     }
242 
243     /**
244      * {@inheritDoc}
245      */
246     public void definition()
247     {
248         write( DEFINITION_LIST_DEFINITION_MARKUP );
249     }
250 
251     /** {@inheritDoc} */
252     public void definition( SinkEventAttributes attributes )
253     {
254         definition();
255     }
256 
257     /**
258      * {@inheritDoc}
259      */
260     public void definition_()
261     {
262         writeEOL();
263     }
264 
265     /**
266      * Not used.
267      * {@inheritDoc}
268      */
269     public void definitionList()
270     {
271         // nop
272     }
273 
274     /** {@inheritDoc} */
275     public void definitionList( SinkEventAttributes attributes )
276     {
277         definitionList();
278     }
279 
280     /**
281      * Not used.
282      * {@inheritDoc}
283      */
284     public void definitionList_()
285     {
286         // nop
287     }
288 
289     /**
290      * {@inheritDoc}
291      */
292     public void definitionListItem()
293     {
294         write( DEFINITION_LIST_ITEM_MARKUP );
295     }
296 
297     /** {@inheritDoc} */
298     public void definitionListItem( SinkEventAttributes attributes )
299     {
300         definitionListItem();
301     }
302 
303     /**
304      * Not used.
305      * {@inheritDoc}
306      */
307     public void definitionListItem_()
308     {
309         // nop
310     }
311 
312     /**
313      * {@inheritDoc}
314      */
315     public void figure()
316     {
317         write( LESS_THAN + Tag.IMG.toString() + SPACE );
318     }
319 
320     /** {@inheritDoc} */
321     public void figure( SinkEventAttributes attributes )
322     {
323         figure();
324     }
325 
326     /**
327      * Not used.
328      * {@inheritDoc}
329      */
330     public void figure_()
331     {
332         write( SLASH + String.valueOf( GREATER_THAN ) );
333     }
334 
335     /**
336      * Not used.
337      * {@inheritDoc}
338      */
339     public void figureCaption()
340     {
341         write( Attribute.ALT.toString() + EQUAL + QUOTE );
342     }
343 
344     /** {@inheritDoc} */
345     public void figureCaption( SinkEventAttributes attributes )
346     {
347         figureCaption();
348     }
349 
350     /**
351      * Not used.
352      * {@inheritDoc}
353      */
354     public void figureCaption_()
355     {
356         write( QUOTE + String.valueOf( SPACE ) );
357     }
358 
359     /** {@inheritDoc} */
360     public void figureGraphics( String name )
361     {
362         write( Attribute.SRC.toString() + EQUAL + QUOTE + name + QUOTE + SPACE );
363     }
364 
365     /** {@inheritDoc} */
366     public void figureGraphics( String src, SinkEventAttributes attributes )
367     {
368         figureGraphics( src );
369     }
370 
371     /**
372      * {@inheritDoc}
373      */
374     public void flush()
375     {
376         close();
377         writer.flush();
378     }
379 
380     /**
381      * {@inheritDoc}
382      */
383     public void head()
384     {
385         init();
386 
387         headFlag = true;
388     }
389 
390     /** {@inheritDoc} */
391     public void head( SinkEventAttributes attributes )
392     {
393         head();
394     }
395 
396     /**
397      * {@inheritDoc}
398      */
399     public void head_()
400     {
401         headFlag = false;
402     }
403 
404     /**
405      * {@inheritDoc}
406      */
407     public void horizontalRule()
408     {
409         writeEOL( true );
410         write( HORIZONTAL_RULE_MARKUP );
411         writeEOL( true );
412     }
413 
414     /** {@inheritDoc} */
415     public void horizontalRule( SinkEventAttributes attributes )
416     {
417         horizontalRule();
418     }
419 
420     /**
421      * {@inheritDoc}
422      */
423     public void inline()
424     {
425         inline( null );
426     }
427 
428     /** {@inheritDoc} */
429     public void inline( SinkEventAttributes attributes )
430     {
431         List<String> tags = new ArrayList<>();
432 
433         boldStack.push( boldFlag );
434 
435         if ( attributes != null )
436         {
437 
438             if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) )
439             {
440                 boldFlag = true;
441                 write( BOLD_START_MARKUP );
442                 tags.add( 0, BOLD_END_MARKUP );
443             }
444 
445             if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) )
446             {
447                 if ( boldFlag )
448                 {
449                     String tmp = writer.toString();
450                     writer = new StringWriter();
451                     writer.write( tmp.substring( 0, tmp.length() - 1 ) );
452                     write( BOLD_ITALIC_START_MARKUP );
453                     tags.add( 0, BOLD_ITALIC_END_MARKUP );
454                 }
455                 else
456                 {
457                     write( ITALIC_START_MARKUP );
458                     tags.add( ITALIC_END_MARKUP );
459                 }
460             }
461 
462             if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) )
463             {
464                 if ( boldFlag )
465                 {
466                     String tmp = writer.toString();
467                     writer = new StringWriter();
468                     writer.write( tmp.substring( 0, tmp.length() - 1 ) );
469                     write( BOLD_MONOSPACED_START_MARKUP );
470                     tags.add( 0, BOLD_MONOSPACED_END_MARKUP );
471                 }
472                 else
473                 {
474                     write( MONOSPACED_START_MARKUP );
475                     tags.add( 0, MONOSPACED_END_MARKUP );
476                 }
477             }
478 
479         }
480 
481         inlineStack.push( tags );
482     }
483 
484     /**
485      * {@inheritDoc}
486      */
487     public void inline_()
488     {
489         for ( String tag: inlineStack.pop() )
490         {
491             write( tag );
492         }
493         this.boldFlag = boldStack.pop();
494     }
495 
496     /**
497      * {@inheritDoc}
498      */
499     public void italic()
500     {
501         inline( SinkEventAttributeSet.Semantics.ITALIC );
502     }
503 
504     /**
505      * {@inheritDoc}
506      */
507     public void italic_()
508     {
509         inline_();
510     }
511 
512     /**
513      * Not used.
514      * {@inheritDoc}
515      */
516     public void lineBreak()
517     {
518         // nop
519     }
520 
521     /** {@inheritDoc} */
522     public void lineBreak( SinkEventAttributes attributes )
523     {
524         lineBreak();
525     }
526 
527     /** {@inheritDoc} */
528     public void link( String name )
529     {
530         write( LINK_START_MARKUP + name + LINK_MIDDLE_MARKUP );
531     }
532 
533     /** {@inheritDoc} */
534     public void link( String name, SinkEventAttributes attributes )
535     {
536         link( name );
537     }
538 
539     /**
540      * {@inheritDoc}
541      */
542     public void link_()
543     {
544         write( LINK_END_MARKUP );
545     }
546 
547     /**
548      * {@inheritDoc}
549      */
550     public void list()
551     {
552         if ( !writer.toString().endsWith( EOL + EOL ) )
553         {
554             writeEOL( true );
555         }
556 
557         levelList++;
558     }
559 
560     /** {@inheritDoc} */
561     public void list( SinkEventAttributes attributes )
562     {
563         list();
564     }
565 
566     /**
567      * {@inheritDoc}
568      */
569     public void list_()
570     {
571         levelList--;
572     }
573 
574     /**
575      * {@inheritDoc}
576      */
577     public void listItem()
578     {
579         String indent = StringUtils.repeat( THREE_SPACES_MARKUP, levelList );
580         write( indent + LIST_ITEM_MARKUP );
581     }
582 
583     /** {@inheritDoc} */
584     public void listItem( SinkEventAttributes attributes )
585     {
586         listItem();
587     }
588 
589     /**
590      * {@inheritDoc}
591      */
592     public void listItem_()
593     {
594         writeEOL( true );
595     }
596 
597     /**
598      * {@inheritDoc}
599      */
600     public void monospaced()
601     {
602         inline( SinkEventAttributeSet.Semantics.CODE );
603     }
604 
605     /**
606      * {@inheritDoc}
607      */
608     public void monospaced_()
609     {
610         inline_();
611     }
612 
613     /**
614      * Not used.
615      * {@inheritDoc}
616      */
617     public void nonBreakingSpace()
618     {
619         // nop
620     }
621 
622     /** {@inheritDoc} */
623     public void numberedList( int numbering )
624     {
625         levelList++;
626 
627         String style;
628         switch ( numbering )
629         {
630             case NUMBERING_UPPER_ALPHA:
631                 style = NUMBERING_UPPER_ALPHA_MARKUP;
632                 break;
633             case NUMBERING_LOWER_ALPHA:
634                 style = NUMBERING_LOWER_ALPHA_MARKUP;
635                 break;
636             case NUMBERING_UPPER_ROMAN:
637                 style = NUMBERING_UPPER_ROMAN_MARKUP;
638                 break;
639             case NUMBERING_LOWER_ROMAN:
640                 style = NUMBERING_LOWER_ROMAN_MARKUP;
641                 break;
642             case NUMBERING_DECIMAL:
643             default:
644                 style = NUMBERING_MARKUP;
645         }
646 
647         listStyles.push( style );
648     }
649 
650     /** {@inheritDoc} */
651     public void numberedList( int numbering, SinkEventAttributes attributes )
652     {
653         numberedList( numbering );
654     }
655 
656     /**
657      * {@inheritDoc}
658      */
659     public void numberedList_()
660     {
661         levelList--;
662         listStyles.pop();
663     }
664 
665     /**
666      * {@inheritDoc}
667      */
668     public void numberedListItem()
669     {
670         writeEOL( true );
671         String style = listStyles.peek();
672         String indent = StringUtils.repeat( THREE_SPACES_MARKUP, levelList );
673         write( indent + style + SPACE );
674     }
675 
676     /** {@inheritDoc} */
677     public void numberedListItem( SinkEventAttributes attributes )
678     {
679         numberedListItem();
680     }
681 
682     /**
683      * {@inheritDoc}
684      */
685     public void numberedListItem_()
686     {
687         writeEOL( true );
688     }
689 
690     /**
691      * Not used.
692      * {@inheritDoc}
693      */
694     public void pageBreak()
695     {
696         // nop
697     }
698 
699     /**
700      * Not used.
701      * {@inheritDoc}
702      */
703     public void paragraph()
704     {
705         // nop
706     }
707 
708     /** {@inheritDoc} */
709     public void paragraph( SinkEventAttributes attributes )
710     {
711         paragraph();
712     }
713 
714     /**
715      * {@inheritDoc}
716      */
717     public void paragraph_()
718     {
719         writeEOL( true );
720         writeEOL();
721     }
722 
723     /**
724      * Not used.
725      * {@inheritDoc}
726      */
727     public void rawText( String text )
728     {
729         // nop
730     }
731 
732     /**
733      * Not used.
734      * {@inheritDoc}
735      */
736     public void section( int level, SinkEventAttributes attributes )
737     {
738         // nop
739     }
740 
741     /**
742      * Not used.
743      * {@inheritDoc}
744      */
745     public void section1()
746     {
747         // nop
748     }
749 
750     /**
751      * Not used.
752      * {@inheritDoc}
753      */
754     public void section1_()
755     {
756         // nop
757     }
758 
759     /**
760      * Not used.
761      * {@inheritDoc}
762      */
763     public void section2()
764     {
765         // nop
766     }
767 
768     /**
769      * Not used.
770      * {@inheritDoc}
771      */
772     public void section2_()
773     {
774         // nop
775     }
776 
777     /**
778      * Not used.
779      * {@inheritDoc}
780      */
781     public void section3()
782     {
783         // nop
784     }
785 
786     /**
787      * Not used.
788      * {@inheritDoc}
789      */
790     public void section3_()
791     {
792         // nop
793     }
794 
795     /**
796      * Not used.
797      * {@inheritDoc}
798      */
799     public void section4()
800     {
801         // nop
802     }
803 
804     /**
805      * Not used.
806      * {@inheritDoc}
807      */
808     public void section4_()
809     {
810         // nop
811     }
812 
813     /**
814      * Not used.
815      * {@inheritDoc}
816      */
817     public void section5()
818     {
819         // nop
820     }
821 
822     /**
823      * Not used.
824      * {@inheritDoc}
825      */
826     public void section5_()
827     {
828         // nop
829     }
830 
831     /**
832      * Not used.
833      * {@inheritDoc}
834      */
835     public void section_( int level )
836     {
837         // nop
838     }
839 
840     /**
841      * Not used.
842      * {@inheritDoc}
843      */
844     public void sectionTitle()
845     {
846         // nop
847     }
848 
849     /** {@inheritDoc} */
850     public void sectionTitle( int level, SinkEventAttributes attributes )
851     {
852         if ( level > 0 && level < 6 )
853         {
854             write( StringUtils.repeat( "-", 3 ) + StringUtils.repeat( "+", level ) );
855         }
856     }
857 
858     /**
859      * {@inheritDoc}
860      */
861     public void sectionTitle1()
862     {
863         sectionTitle( 1, null );
864     }
865 
866     /**
867      * {@inheritDoc}
868      */
869     public void sectionTitle1_()
870     {
871         sectionTitle_( 1 );
872     }
873 
874     /**
875      * {@inheritDoc}
876      */
877     public void sectionTitle2()
878     {
879         sectionTitle( 2, null );
880     }
881 
882     /**
883      * {@inheritDoc}
884      */
885     public void sectionTitle2_()
886     {
887         sectionTitle_( 2 );
888     }
889 
890     /**
891      * {@inheritDoc}
892      */
893     public void sectionTitle3()
894     {
895         sectionTitle( 3, null );
896     }
897 
898     /**
899      * {@inheritDoc}
900      */
901     public void sectionTitle3_()
902     {
903         sectionTitle_( 3 );
904     }
905 
906     /**
907      * {@inheritDoc}
908      */
909     public void sectionTitle4()
910     {
911         sectionTitle( 4, null );
912     }
913 
914     /**
915      * {@inheritDoc}
916      */
917     public void sectionTitle4_()
918     {
919         sectionTitle_( 4 );
920     }
921 
922     /**
923      * {@inheritDoc}
924      */
925     public void sectionTitle5()
926     {
927         sectionTitle( 5, null );
928     }
929 
930     /**
931      * {@inheritDoc}
932      */
933     public void sectionTitle5_()
934     {
935         sectionTitle_( 5 );
936     }
937 
938     /**
939      * Not used.
940      * {@inheritDoc}
941      */
942     public void sectionTitle_()
943     {
944         // nop
945     }
946 
947     /** {@inheritDoc} */
948     public void sectionTitle_( int level )
949     {
950         writeEOL( true );
951         writeEOL();
952     }
953 
954     /**
955      * Not used.
956      * {@inheritDoc}
957      */
958     public void table()
959     {
960         // nop
961     }
962 
963     /** {@inheritDoc} */
964     public void table( SinkEventAttributes attributes )
965     {
966         table();
967     }
968 
969     /**
970      * Not used.
971      * {@inheritDoc}
972      */
973     public void table_()
974     {
975         // nop
976     }
977 
978     /**
979      * Not used.
980      * {@inheritDoc}
981      */
982     public void tableCaption()
983     {
984         // nop
985     }
986 
987     /** {@inheritDoc} */
988     public void tableCaption( SinkEventAttributes attributes )
989     {
990         tableCaption();
991     }
992 
993     /**
994      * Not used.
995      * {@inheritDoc}
996      */
997     public void tableCaption_()
998     {
999         // nop
1000     }
1001 
1002     /**
1003      * {@inheritDoc}
1004      */
1005     public void tableCell()
1006     {
1007         write( " " );
1008     }
1009 
1010     /** {@inheritDoc} */
1011     public void tableCell( SinkEventAttributes attributes )
1012     {
1013         tableCell();
1014     }
1015 
1016     /** {@inheritDoc} */
1017     public void tableCell( String width )
1018     {
1019         tableCell();
1020     }
1021 
1022     /**
1023      * {@inheritDoc}
1024      */
1025     public void tableCell_()
1026     {
1027         write( TABLE_CELL_MARKUP );
1028     }
1029 
1030     /**
1031      * {@inheritDoc}
1032      */
1033     public void tableHeaderCell()
1034     {
1035         write( TABLE_CELL_HEADER_START_MARKUP );
1036     }
1037 
1038     /** {@inheritDoc} */
1039     public void tableHeaderCell( SinkEventAttributes attributes )
1040     {
1041         tableHeaderCell();
1042     }
1043 
1044     /** {@inheritDoc} */
1045     public void tableHeaderCell( String width )
1046     {
1047         tableHeaderCell();
1048     }
1049 
1050     /**
1051      * {@inheritDoc}
1052      */
1053     public void tableHeaderCell_()
1054     {
1055         write( TABLE_CELL_HEADER_END_MARKUP );
1056     }
1057 
1058     /**
1059      * {@inheritDoc}
1060      */
1061     public void tableRow()
1062     {
1063         write( TABLE_ROW_MARKUP );
1064     }
1065 
1066     /** {@inheritDoc} */
1067     public void tableRow( SinkEventAttributes attributes )
1068     {
1069         tableRow();
1070     }
1071 
1072     /**
1073      * {@inheritDoc}
1074      */
1075     public void tableRow_()
1076     {
1077         writeEOL( true );
1078     }
1079 
1080     /**
1081      * Not used.
1082      * {@inheritDoc}
1083      */
1084     public void tableRows( int[] justification, boolean grid )
1085     {
1086         // nop
1087     }
1088 
1089     /**
1090      * Not used.
1091      * {@inheritDoc}
1092      */
1093     public void tableRows_()
1094     {
1095         // nop
1096     }
1097 
1098     /** {@inheritDoc} */
1099     public void text( String text )
1100     {
1101         if ( headFlag )
1102         {
1103             return;
1104         }
1105 
1106         content( text );
1107     }
1108 
1109     /** {@inheritDoc} */
1110     public void text( String text, SinkEventAttributes attributes )
1111     {
1112         if ( attributes == null )
1113         {
1114             text( text );
1115         }
1116         else
1117         {
1118             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
1119             {
1120                 writeStartTag( Tag.U );
1121             }
1122             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
1123             {
1124                 writeStartTag( Tag.S );
1125             }
1126             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
1127             {
1128                 writeStartTag( Tag.SUB );
1129             }
1130             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
1131             {
1132                 writeStartTag( Tag.SUP );
1133             }
1134 
1135             text( text );
1136 
1137             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
1138             {
1139                 writeEndTag( Tag.SUP );
1140             }
1141             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
1142             {
1143                 writeEndTag( Tag.SUB );
1144             }
1145             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
1146             {
1147                 writeEndTag( Tag.S );
1148             }
1149             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
1150             {
1151                 writeEndTag( Tag.U );
1152             }
1153         }
1154     }
1155 
1156     /**
1157      * Not used.
1158      * {@inheritDoc}
1159      */
1160     public void title()
1161     {
1162         // nop
1163     }
1164 
1165     /** {@inheritDoc} */
1166     public void title( SinkEventAttributes attributes )
1167     {
1168         title();
1169     }
1170 
1171     /**
1172      * Not used.
1173      * {@inheritDoc}
1174      */
1175     public void title_()
1176     {
1177         // nop
1178     }
1179 
1180     /**
1181      * Not used.
1182      * {@inheritDoc}
1183      */
1184     public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1185     {
1186         // nop
1187     }
1188 
1189     /** {@inheritDoc} */
1190     public void verbatim( boolean boxed )
1191     {
1192         SinkEventAttributeSet att = new SinkEventAttributeSet();
1193 
1194         if ( boxed )
1195         {
1196             att.addAttribute( SinkEventAttributes.DECORATION, "boxed" );
1197         }
1198 
1199         verbatim( att );
1200     }
1201 
1202     /**
1203      * {@inheritDoc}
1204      *
1205      * @param attributes a {@link org.apache.maven.doxia.sink.SinkEventAttributes} object.
1206      */
1207     public void verbatim( SinkEventAttributes attributes )
1208     {
1209         MutableAttributeSet atts = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES );
1210 
1211         if ( atts == null )
1212         {
1213             atts = new SinkEventAttributeSet();
1214         }
1215 
1216         boolean boxed = false;
1217 
1218         if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
1219         {
1220             boxed = "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
1221         }
1222 
1223         if ( boxed )
1224         {
1225             atts.addAttribute( Attribute.CLASS, "source" );
1226         }
1227 
1228         atts.removeAttribute( SinkEventAttributes.DECORATION );
1229 
1230         String width = (String) atts.getAttribute( Attribute.WIDTH.toString() );
1231         atts.removeAttribute( Attribute.WIDTH.toString() );
1232 
1233         writeStartTag( Tag.DIV, atts );
1234         writeEOL( true );
1235 
1236         if ( width != null )
1237         {
1238             atts.addAttribute( Attribute.WIDTH.toString(), width );
1239         }
1240 
1241         atts.removeAttribute( Attribute.ALIGN.toString() );
1242         atts.removeAttribute( Attribute.CLASS.toString() );
1243 
1244         writeStartTag( VERBATIM_TAG, atts );
1245     }
1246 
1247     /**
1248      * {@inheritDoc}
1249      */
1250     public void verbatim_()
1251     {
1252         writeEndTag( VERBATIM_TAG );
1253         writeEOL( true );
1254         writeEndTag( Tag.DIV );
1255         writeEOL( true );
1256     }
1257 
1258     // ----------------------------------------------------------------------
1259     // Private methods
1260     // ----------------------------------------------------------------------
1261 
1262     private void write( String text )
1263     {
1264         writer.write( unifyEOLs( text ) );
1265     }
1266 
1267     /**
1268      * Starts a Tag. For instance:
1269      * <pre>
1270      * &lt;tag&gt;
1271      * </pre>
1272      * <br>
1273      *
1274      * @param t a non null tag
1275      * @see #writeStartTag(javax.swing.text.html.HTML.Tag)
1276      */
1277     private void writeStartTag( Tag t )
1278     {
1279         writeStartTag( t, null );
1280     }
1281 
1282     /**
1283      * Starts a Tag with attributes. For instance:
1284      * <pre>
1285      * &lt;tag attName="attValue"&gt;
1286      * </pre>
1287      * <br>
1288      *
1289      * @param t a non null tag
1290      * @param att a set of attributes
1291      * @see #writeStartTag(javax.swing.text.html.HTML.Tag, javax.swing.text.MutableAttributeSet, boolean)
1292      */
1293     private void writeStartTag( Tag t, MutableAttributeSet att )
1294     {
1295         writeStartTag( t, att, false );
1296     }
1297 
1298     /**
1299      * Starts a Tag with attributes. For instance:
1300      * <pre>
1301      * &lt;tag attName="attValue"&gt;
1302      * </pre>
1303      * <br>
1304      *
1305      * @param t a non null tag
1306      * @param att a set of attributes
1307      * @param isSimpleTag boolean to write as a simple tag
1308      */
1309     private void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
1310     {
1311         if ( t == null )
1312         {
1313             throw new IllegalArgumentException( "A tag is required" );
1314         }
1315 
1316         StringBuilder sb = new StringBuilder();
1317         sb.append( LESS_THAN );
1318 
1319         sb.append( t.toString() );
1320 
1321         sb.append( SinkUtils.getAttributeString( att ) );
1322 
1323         if ( isSimpleTag )
1324         {
1325             sb.append( SPACE ).append( SLASH );
1326         }
1327 
1328         sb.append( GREATER_THAN );
1329 
1330         write( sb.toString() );
1331     }
1332 
1333     /**
1334      * Writes a system EOL.
1335      */
1336     private void writeEOL()
1337     {
1338         write( EOL );
1339     }
1340 
1341     /**
1342      * Writes a system EOL, with or without trim.
1343      */
1344     private void writeEOL( boolean trim )
1345     {
1346         if ( !trim )
1347         {
1348             writeEOL();
1349             return;
1350         }
1351 
1352         String tmp = writer.toString().trim();
1353         writer = new StringWriter();
1354         writer.write( tmp );
1355         write( EOL );
1356     }
1357 
1358     /**
1359      * Ends a Tag without writing an EOL. For instance: <pre>&lt;/tag&gt;</pre>.
1360      *
1361      * @param t a tag.
1362      */
1363     private void writeEndTag( Tag t )
1364     {
1365         StringBuilder sb = new StringBuilder();
1366         sb.append( LESS_THAN );
1367         sb.append( SLASH );
1368 
1369         sb.append( t.toString() );
1370         sb.append( GREATER_THAN );
1371 
1372         write( sb.toString() );
1373     }
1374 
1375     /**
1376      * Write HTML escaped text to output.
1377      *
1378      * @param text The text to write.
1379      */
1380     protected void content( String text )
1381     {
1382         write( escapeHTML( text ) );
1383     }
1384 
1385     /**
1386      * {@inheritDoc}
1387      */
1388     protected void init()
1389     {
1390         super.init();
1391 
1392         this.writer = new StringWriter();
1393         this.headFlag = false;
1394         this.levelList = 0;
1395         this.listStyles.clear();
1396         this.boldFlag = false;
1397     }
1398 
1399     /**
1400      * Forward to HtmlTools.escapeHTML( text ).
1401      *
1402      * @param text the String to escape, may be null
1403      * @return the text escaped, "" if null String input
1404      * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
1405      */
1406     protected static String escapeHTML( String text )
1407     {
1408         return HtmlTools.escapeHTML( text );
1409     }
1410 }