001package org.apache.maven.doxia.module.twiki;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.PrintWriter;
023import java.io.StringWriter;
024import java.io.Writer;
025import java.util.ArrayList;
026import java.util.List;
027import java.util.Stack;
028
029import javax.swing.text.MutableAttributeSet;
030import javax.swing.text.html.HTML.Attribute;
031import javax.swing.text.html.HTML.Tag;
032
033import org.apache.maven.doxia.sink.SinkEventAttributes;
034import org.apache.maven.doxia.sink.impl.AbstractTextSink;
035import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
036import org.apache.maven.doxia.sink.impl.SinkUtils;
037import org.apache.maven.doxia.util.HtmlTools;
038import org.codehaus.plexus.util.StringUtils;
039
040/**
041 * TWiki Sink implementation.
042 * <br>
043 * <b>Note</b>: The encoding used is UTF-8.
044 *
045 * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a>
046 * @since 1.0
047 */
048public class TWikiSink
049    extends AbstractTextSink
050    implements TWikiMarkup
051{
052    /**  The writer to use. */
053    private final PrintWriter out;
054
055    /**  The writer to use. */
056    private StringWriter writer;
057
058    /** An indication on if we're in bold mode. */
059    private boolean boldFlag;
060
061    /** An indication on if we're in head mode. */
062    private boolean headFlag;
063
064    private int levelList = 0;
065
066    /**  listStyles. */
067    private final Stack<String> listStyles;
068
069    /** Keep track of the nested bold flag. */
070    protected Stack<Boolean> boldStack = new Stack<>();
071
072    /** Keep track of the closing tags for inline events. */
073    protected Stack<List<String>> inlineStack = new Stack<>();
074
075    /**
076     * Constructor, initialize the Writer and the variables.
077     *
078     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
079     * You could use <code>newWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
080     */
081    protected TWikiSink( Writer writer )
082    {
083        this.out = new PrintWriter( writer );
084        this.listStyles = new Stack<>();
085
086        init();
087    }
088
089    /** {@inheritDoc} */
090    public void anchor( String name )
091    {
092        write( EOL );
093        write( ANCHOR_MARKUP + name );
094    }
095
096    /** {@inheritDoc} */
097    public void anchor( String name, SinkEventAttributes attributes )
098    {
099        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}