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