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