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