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