001package org.apache.maven.doxia.module.docbook;
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.HashMap;
027import java.util.List;
028import java.util.Locale;
029import java.util.Map;
030import java.util.Set;
031import java.util.Stack;
032import java.util.StringTokenizer;
033import java.util.TreeSet;
034
035import javax.swing.text.MutableAttributeSet;
036import javax.swing.text.SimpleAttributeSet;
037
038import org.apache.maven.doxia.sink.Sink;
039import org.apache.maven.doxia.sink.SinkEventAttributes;
040import org.apache.maven.doxia.sink.impl.AbstractXmlSink;
041import org.apache.maven.doxia.sink.impl.SinkEventAttributeSet;
042import org.apache.maven.doxia.util.DoxiaUtils;
043import org.codehaus.plexus.util.FileUtils;
044
045import static org.apache.maven.doxia.util.HtmlTools.escapeHTML;
046
047/**
048 * <a href="http://www.oasis-open.org/docbook">Docbook</a> Sink implementation.
049 * <br>
050 * It uses the Docbook v4.4 DTD <a href="http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd">
051 * http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd</a>.
052 *
053 * @since 1.0
054 */
055public class DocBookSink
056    extends AbstractXmlSink
057    implements DocbookMarkup, SimplifiedDocbookMarkup
058{
059    /** DocBook V4.4 SGML public id: "-//OASIS//DTD DocBook V4.4//EN"
060     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_PUBLIC_ID} instead of. */
061    public static final String DEFAULT_SGML_PUBLIC_ID = DocbookMarkup.DEFAULT_SGML_PUBLIC_ID;
062
063    /** DocBook XML V4.4 XML public id: "-//OASIS//DTD DocBook XML V4.4//EN"
064     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_PUBLIC_ID} instead of. */
065    public static final String DEFAULT_XML_PUBLIC_ID = DocbookMarkup.DEFAULT_XML_PUBLIC_ID;
066
067    /** DocBook XML V4.4 XML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
068     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_SYSTEM_ID} instead of. */
069    public static final String DEFAULT_XML_SYSTEM_ID = DocbookMarkup.DEFAULT_XML_SYSTEM_ID;
070
071    /** DocBook XML V4.4 SGML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
072     * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_SYSTEM_ID} instead of. */
073    public static final String DEFAULT_SGML_SYSTEM_ID = DocbookMarkup.DEFAULT_SGML_SYSTEM_ID;
074
075    /** The output writer. */
076    private PrintWriter out;
077
078    /** xmlMode. */
079    private boolean xmlMode = false;
080
081    /** styleSheet. */
082    private String styleSheet = null;
083
084    /** language. */
085    private String lang = null;
086
087    /** publicId. */
088    private String publicId = null;
089
090    /** systemId. */
091    private String systemId = null;
092
093    /** italicBegin. */
094    private String italicBeginTag;
095
096    /** italicEnd. */
097    private String italicEndTag;
098
099    /** boldBegin. */
100    private String boldBeginTag;
101
102    /** boldEnd. */
103    private String boldEndTag;
104
105    /** monospacedBegin. */
106    private String monospacedBeginTag;
107
108    /** monospacedEnd. */
109    private String monospacedEndTag;
110
111    /** horizontalRule. */
112    private String horizontalRuleElement;
113
114    /** pageBreak. */
115    private String pageBreakElement;
116
117    /** lineBreak. */
118    private String lineBreakElement;
119
120    /** An image source file. */
121    private String graphicsFileName;
122
123    /** hasTitle. */
124    private boolean hasTitle;
125
126    /** authorDate. */
127    private boolean authorDateFlag;
128
129    /** verbatim. */
130    private boolean verbatimFlag;
131
132    /** externalLink. */
133    private boolean externalLinkFlag;
134
135    /** tableHasCaption. */
136    private boolean tableHasCaption;
137
138    /** Used for table rows. */
139    private PrintWriter savedOut;
140
141    /** tableRows. */
142    private String tableRows;
143
144    /** tableRows writer. */
145    private StringWriter tableRowsWriter;
146
147    /** tableHasGrid. */
148    private boolean tableHasGrid;
149
150    private boolean skip;
151
152    private boolean paragraph;
153
154    private String encoding;
155
156    /** Keep track of the closing tags for inline events. */
157    protected Stack<List<String>> inlineStack = new Stack<>();
158
159    /** Map of warn messages with a String as key to describe the error type and a Set as value.
160     * Using to reduce warn messages. */
161    private Map<String, Set<String>> warnMessages;
162
163    /**
164     * Constructor, initialize the Writer.
165     *
166     * @param writer not null writer to write the result. <b>Should</b> be an UTF-8 Writer.
167     * You could use <code>newXmlWriter</code> methods from {@link org.codehaus.plexus.util.WriterFactory}.
168     */
169    protected DocBookSink( Writer writer )
170    {
171        this( writer, null );
172    }
173
174    /**
175     * Constructor, initialize the Writer and tells which encoding is used.
176     *
177     * @param writer not null writer to write the result.
178     * @param encoding the encoding used, that should be written to the generated HTML content
179     * if not <code>null</code>.
180     */
181    protected DocBookSink( Writer writer, String encoding )
182    {
183        this.out = new PrintWriter( writer );
184        this.encoding = encoding;
185
186        setItalicElement( "<emphasis>" );
187        setBoldElement( "<emphasis role=\"bold\">" );
188        setMonospacedElement( "<literal>" );
189        setHorizontalRuleElement( "<!-- HR -->" );
190        setPageBreakElement( "<!-- PB -->" );
191        setLineBreakElement( "<!-- LB -->" );
192    }
193
194    /**
195     * Constructor, initialize the Writer and tells which encoding and languageId are used.
196     *
197     * @param writer not null writer to write the result.
198     * @param encoding the encoding used, that should be written to the generated HTML content
199     * if not <code>null</code>.
200     * @param languageId language identifier for the root element as defined by
201     * <a href="ftp://ftp.isi.edu/in-notes/bcp/bcp47.txt">IETF BCP 47</a>, Tags for the Identification of Languages;
202     * in addition, the empty string may be specified.
203     * @since 1.1
204     */
205    protected DocBookSink( Writer writer, String encoding, String languageId )
206    {
207        this( writer, encoding );
208
209        this.lang = languageId;
210    }
211
212    /**
213     * <p>escapeSGML</p>
214     *
215     * @param text The text to escape.
216     * @param xmlMode xmlMode.
217     * @return The escaped text.
218     * @deprecated Use HtmlTools#escapeHTML(String,boolean).
219     */
220    public static String escapeSGML( String text, boolean xmlMode )
221    {
222        return escapeHTML( text, xmlMode );
223    }
224
225    /**
226     * Sets the xml mode.
227     *
228     * @param mode the mode to set.
229     * @deprecated xml mode is not used.
230     */
231    public void setXMLMode( boolean mode )
232    {
233        this.xmlMode = mode;
234    }
235
236    /**
237     * Returns the current xmlMode.
238     *
239     * @return the current xmlMode.
240     * @deprecated xml mode is not used.
241     */
242    public boolean isXMLMode()
243    {
244        return xmlMode;
245    }
246
247    /**
248     * Sets the encoding. The encoding specified here must be consistent with then encoding
249     * used in the Writer used by this DocBookSink instance.
250     *
251     * @param enc the encoding to set.
252     */
253    public void setEncoding( String enc )
254    {
255        encoding = enc;
256    }
257
258    /**
259     * Returns the encoding.
260     *
261     * @return the encoding set (can be <code>null</code>).
262     */
263    public String getEncoding()
264    {
265        return encoding;
266    }
267
268    /**
269     * Sets the styleSheet.
270     *
271     * @param sheet the styleSheet to set.
272     */
273    public void setStyleSheet( String sheet )
274    {
275        this.styleSheet = sheet;
276    }
277
278    /**
279     * Returns the current styleSheet.
280     *
281     * @return the current styleSheet.
282     */
283    public String getStyleSheet()
284    {
285        return styleSheet;
286    }
287
288    /**
289     * Sets the publicId.
290     *
291     * @param id the publicId to set.
292     */
293    public void setPublicId( String id )
294    {
295        this.publicId = id;
296    }
297
298    /**
299     * Returns the current publicId.
300     *
301     * @return the current publicId.
302     */
303    public String getPublicId()
304    {
305        return publicId;
306    }
307
308    /**
309     * Sets the systemId.
310     *
311     * @param id the systemId to set.
312     */
313    public void setSystemId( String id )
314    {
315        this.systemId = id;
316    }
317
318    /**
319     * Returns the current systemId.
320     *
321     * @return the current systemId.
322     */
323    public String getSystemId()
324    {
325        return systemId;
326    }
327
328    /**
329     * Sets the language.
330     *
331     * @param language the language to set.
332     */
333    public void setLanguage( String language )
334    {
335        this.lang = language;
336    }
337
338    /**
339     * Returns the current language.
340     *
341     * @return the current language.
342     */
343    public String getLanguage()
344    {
345        return lang;
346    }
347
348    /**
349     * Sets the current italicBeginTag and constructs the corresponding end tag from it.
350     *
351     * @param tag the tag to set. If tag is null, the empty string is used.
352     */
353    public void setItalicElement( String tag )
354    {
355        if ( tag == null )
356        {
357            tag = "";
358        }
359        this.italicBeginTag = tag;
360        italicEndTag = makeEndTag( italicBeginTag );
361    }
362
363    /**
364     * Constructs the corresponding end tag from the given begin tag.
365     *
366     * @param beginTag the begin tag to set. If null, the empty string is returned.
367     * @return the corresponding end tag.
368     */
369    private String makeEndTag( String beginTag )
370    {
371        int length = beginTag.length();
372        if ( length == 0 )
373        {
374            return "";
375        }
376
377        if ( beginTag.charAt( 0 ) != '<' || beginTag.charAt( length - 1 ) != '>' )
378        {
379            throw new IllegalArgumentException( "'" + beginTag + "', not a tag" );
380        }
381
382        StringTokenizer tokens = new StringTokenizer( beginTag, "<> \t\n\r\f" );
383        if ( !tokens.hasMoreTokens() )
384        {
385            throw new IllegalArgumentException( "'" + beginTag + "', invalid tag" );
386        }
387
388        return "</" + tokens.nextToken() + ">";
389    }
390
391    /**
392     * Returns the current italicBeginTag.
393     *
394     * @return the current italicBeginTag. Defaults to {@code <emphasis>}.
395     */
396    public String getItalicElement()
397    {
398        return italicBeginTag;
399    }
400
401    /**
402     * Sets the current boldBeginTag and constructs the corresponding end tag from it.
403     *
404     * @param tag the tag to set. If tag is null, the empty string is used.
405     */
406    public void setBoldElement( String tag )
407    {
408        if ( tag == null )
409        {
410            tag = "";
411        }
412        this.boldBeginTag = tag;
413        boldEndTag = makeEndTag( boldBeginTag );
414    }
415
416    /**
417     * Returns the current boldBeginTag.
418     *
419     * @return the current boldBeginTag. Defaults to {@code <emphasis role=\"bold\">}.
420     */
421    public String getBoldElement()
422    {
423        return boldBeginTag;
424    }
425
426    /**
427     * Sets the current monospacedBeginTag and constructs the corresponding end tag from it.
428     *
429     * @param tag the tag to set. If tag is null, the empty string is used.
430     */
431    public void setMonospacedElement( String tag )
432    {
433        if ( tag == null )
434        {
435            tag = "";
436        }
437        this.monospacedBeginTag = tag;
438        monospacedEndTag = makeEndTag( monospacedBeginTag );
439    }
440
441    /**
442     * Returns the current monospacedBeginTag.
443     *
444     * @return the current monospacedBeginTag. Defaults to {@code <literal>}.
445     */
446    public String getMonospacedElement()
447    {
448        return monospacedBeginTag;
449    }
450
451    /**
452     * Sets the current horizontalRuleElement.
453     *
454     * @param element the element to set.
455     */
456    public void setHorizontalRuleElement( String element )
457    {
458        this.horizontalRuleElement = element;
459    }
460
461    /**
462     * Returns the current horizontalRuleElement.
463     *
464     * @return the current horizontalRuleElement. Defaults to "<!-- HR -->".
465     */
466    public String getHorizontalRuleElement()
467    {
468        return horizontalRuleElement;
469    }
470
471    /**
472     * Sets the current pageBreakElement.
473     *
474     * @param element the element to set.
475     */
476    public void setPageBreakElement( String element )
477    {
478        this.pageBreakElement = element;
479    }
480
481    /**
482     * Returns the current pageBreakElement.
483     *
484     * @return the current pageBreakElement. Defaults to "<!-- PB -->".
485     */
486    public String getPageBreakElement()
487    {
488        return pageBreakElement;
489    }
490
491    /**
492     * Sets the current lineBreakElement.
493     *
494     * @param element the element to set.
495     */
496    public void setLineBreakElement( String element )
497    {
498        this.lineBreakElement = element;
499    }
500
501    /**
502     * Returns the current lineBreakElement.
503     *
504     * @return the current lineBreakElement. Defaults to "<!-- LB -->".
505     */
506    public String getLineBreakElement()
507    {
508        return lineBreakElement;
509    }
510
511    /**
512     * Reset all variables.
513     *
514     * @deprecated since 1.1.2, use {@link #init()} instead of.
515     */
516    protected void resetState()
517    {
518       init();
519    }
520
521    /**
522     * {@inheritDoc}
523     */
524    protected void init()
525    {
526        hasTitle = false;
527        authorDateFlag = false;
528        verbatimFlag = false;
529        externalLinkFlag = false;
530        graphicsFileName = null;
531        tableHasCaption = false;
532        savedOut = null;
533        tableRows = null;
534        tableHasGrid = false;
535    }
536
537    // ----------------------------------------------------------------------
538    //
539    // ----------------------------------------------------------------------
540
541    /**
542     * {@inheritDoc}
543     *
544     * @see SimplifiedDocbookMarkup#DEFAULT_XML_PUBLIC_ID
545     * @see SimplifiedDocbookMarkup#DEFAULT_XML_SYSTEM_ID
546     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
547     */
548    public void head()
549    {
550        init();
551
552        MutableAttributeSet att = writeXmlHeader( "article" );
553
554        writeStartTag( SimplifiedDocbookMarkup.ARTICLE_TAG, att );
555    }
556
557    /**
558     * writeXmlHeader.
559     *
560     * @param root not null.
561     * @return an attribute set.
562     * @see SimplifiedDocbookMarkup#DEFAULT_XML_PUBLIC_ID
563     * @see SimplifiedDocbookMarkup#DEFAULT_XML_SYSTEM_ID
564     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
565     * @since 1.1
566     */
567    protected MutableAttributeSet writeXmlHeader( String root )
568    {
569        markup( "<?xml version=\"1.0\"" );
570        if ( encoding != null )
571        {
572            markup( " encoding=\"" + encoding + "\"" );
573        }
574        markup( "?>" );
575
576        if ( styleSheet != null )
577        {
578            markup( "<?xml-stylesheet type=\"text/css\" href=\"" + styleSheet + "\" ?>" );
579        }
580
581        String pubId;
582        markup( "<!DOCTYPE " + root + " PUBLIC" );
583
584        if ( publicId == null )
585        {
586            pubId = SimplifiedDocbookMarkup.DEFAULT_XML_PUBLIC_ID;
587        }
588        else
589        {
590            pubId = publicId;
591        }
592        markup( " \"" + pubId + "\"" );
593        String sysId = systemId;
594        if ( sysId == null )
595        {
596                sysId = SimplifiedDocbookMarkup.DEFAULT_XML_SYSTEM_ID;
597        }
598        markup( " \"" + sysId + "\">" );
599
600        MutableAttributeSet att = new SimpleAttributeSet();
601        if ( lang != null )
602        {
603            att.addAttribute( LANG_ATTRIBUTE, lang );
604        }
605        return att;
606    }
607
608    /**
609     * {@inheritDoc}
610     *
611     * @see SimplifiedDocbookMarkup#ARTICLEINFO_TAG
612     */
613    public void head_()
614    {
615        if ( hasTitle )
616        {
617            writeEndTag( SimplifiedDocbookMarkup.ARTICLEINFO_TAG );
618            hasTitle = false;
619        }
620    }
621
622    /**
623     * {@inheritDoc}
624     *
625     * @see SimplifiedDocbookMarkup#ARTICLEINFO_TAG
626     * @see SimplifiedDocbookMarkup#TITLE_TAG
627     */
628    public void title()
629    {
630        writeStartTag( SimplifiedDocbookMarkup.ARTICLEINFO_TAG );
631        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
632        hasTitle = true;
633    }
634
635    /**
636     * {@inheritDoc}
637     *
638     * @see SimplifiedDocbookMarkup#TITLE_TAG
639     */
640    public void title_()
641    {
642        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
643    }
644
645    /**
646     * {@inheritDoc}
647     *
648     * @see SimplifiedDocbookMarkup#CORPAUTHOR_TAG
649     */
650    public void author()
651    {
652        authorDateFlag = true;
653        writeStartTag( SimplifiedDocbookMarkup.CORPAUTHOR_TAG );
654    }
655
656    /**
657     * {@inheritDoc}
658     *
659     * @see SimplifiedDocbookMarkup#CORPAUTHOR_TAG
660     */
661    public void author_()
662    {
663        writeEndTag( SimplifiedDocbookMarkup.CORPAUTHOR_TAG );
664        authorDateFlag = false;
665    }
666
667    /**
668     * {@inheritDoc}
669     *
670     * @see SimplifiedDocbookMarkup#DATE_TAG
671     */
672    public void date()
673    {
674        authorDateFlag = true;
675        writeStartTag( SimplifiedDocbookMarkup.DATE_TAG );
676    }
677
678    /**
679     * {@inheritDoc}
680     *
681     * @see SimplifiedDocbookMarkup#DATE_TAG
682     */
683    public void date_()
684    {
685        writeEndTag( SimplifiedDocbookMarkup.DATE_TAG );
686        authorDateFlag = false;
687    }
688
689    /**
690     * {@inheritDoc}
691     *
692     * @see SimplifiedDocbookMarkup#ARTICLE_TAG
693     */
694    public void body_()
695    {
696        writeEndTag( SimplifiedDocbookMarkup.ARTICLE_TAG );
697        out.flush();
698        init();
699    }
700
701    /**
702     * {@inheritDoc}
703     *
704     * @see SimplifiedDocbookMarkup#SIDEBAR_TAG
705     */
706    public void sidebar()
707    {
708        writeStartTag( SimplifiedDocbookMarkup.SIDEBAR_TAG );
709    }
710
711    /**
712     * {@inheritDoc}
713     *
714     * @see SimplifiedDocbookMarkup#SIDEBAR_TAG
715     */
716    public void sidebar_()
717    {
718        writeEndTag( SimplifiedDocbookMarkup.SIDEBAR_TAG );
719    }
720
721    /**
722     * {@inheritDoc}
723     *
724     * @see SimplifiedDocbookMarkup#SECTION_TAG
725     */
726    public void section1()
727    {
728        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
729    }
730
731    /**
732     * {@inheritDoc}
733     *
734     * @see SimplifiedDocbookMarkup#SECTION_TAG
735     */
736    public void section1_()
737    {
738        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
739    }
740
741    /**
742     * {@inheritDoc}
743     *
744     * @see SimplifiedDocbookMarkup#SECTION_TAG
745     */
746    public void section2()
747    {
748        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
749    }
750
751    /**
752     * {@inheritDoc}
753     *
754     * @see SimplifiedDocbookMarkup#SECTION_TAG
755     */
756    public void section2_()
757    {
758        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
759    }
760
761    /**
762     * {@inheritDoc}
763     *
764     * @see SimplifiedDocbookMarkup#SECTION_TAG
765     */
766    public void section3()
767    {
768        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
769    }
770
771    /**
772     * {@inheritDoc}
773     *
774     * @see SimplifiedDocbookMarkup#SECTION_TAG
775     */
776    public void section3_()
777    {
778        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
779    }
780
781    /**
782     * {@inheritDoc}
783     *
784     * @see SimplifiedDocbookMarkup#SECTION_TAG
785     */
786    public void section4()
787    {
788        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
789    }
790
791    /**
792     * {@inheritDoc}
793     *
794     * @see SimplifiedDocbookMarkup#SECTION_TAG
795     */
796    public void section4_()
797    {
798        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
799    }
800
801    /**
802     * {@inheritDoc}
803     *
804     * @see SimplifiedDocbookMarkup#SECTION_TAG
805     */
806    public void section5()
807    {
808        writeStartTag( SimplifiedDocbookMarkup.SECTION_TAG );
809    }
810
811    /**
812     * {@inheritDoc}
813     *
814     * @see SimplifiedDocbookMarkup#SECTION_TAG
815     */
816    public void section5_()
817    {
818        writeEndTag( SimplifiedDocbookMarkup.SECTION_TAG );
819    }
820
821    /**
822     * {@inheritDoc}
823     *
824     * @see SimplifiedDocbookMarkup#TITLE_TAG
825     */
826    public void sectionTitle()
827    {
828        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
829    }
830
831    /**
832     * {@inheritDoc}
833     *
834     * @see SimplifiedDocbookMarkup#TITLE_TAG
835     */
836    public void sectionTitle_()
837    {
838        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
839    }
840
841    /**
842     * {@inheritDoc}
843     *
844     * @see SimplifiedDocbookMarkup#TITLE_TAG
845     */
846    public void sectionTitle1()
847    {
848        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
849    }
850
851    /**
852     * {@inheritDoc}
853     *
854     * @see SimplifiedDocbookMarkup#TITLE_TAG
855     */
856    public void sectionTitle1_()
857    {
858        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
859    }
860
861    /**
862     * {@inheritDoc}
863     *
864     * @see SimplifiedDocbookMarkup#TITLE_TAG
865     */
866    public void sectionTitle2()
867    {
868        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
869    }
870
871    /**
872     * {@inheritDoc}
873     *
874     * @see SimplifiedDocbookMarkup#TITLE_TAG
875     */
876    public void sectionTitle2_()
877    {
878        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
879    }
880
881    /**
882     * {@inheritDoc}
883     *
884     * @see SimplifiedDocbookMarkup#TITLE_TAG
885     */
886    public void sectionTitle3()
887    {
888        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
889    }
890
891    /**
892     * {@inheritDoc}
893     *
894     * @see SimplifiedDocbookMarkup#TITLE_TAG
895     */
896    public void sectionTitle3_()
897    {
898        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
899    }
900
901    /**
902     * {@inheritDoc}
903     *
904     * @see SimplifiedDocbookMarkup#TITLE_TAG
905     */
906    public void sectionTitle4()
907    {
908        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
909    }
910
911    /**
912     * {@inheritDoc}
913     *
914     * @see SimplifiedDocbookMarkup#TITLE_TAG
915     */
916    public void sectionTitle4_()
917    {
918        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
919    }
920
921    /**
922     * {@inheritDoc}
923     *
924     * @see SimplifiedDocbookMarkup#TITLE_TAG
925     */
926    public void sectionTitle5()
927    {
928        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
929    }
930
931    /**
932     * {@inheritDoc}
933     *
934     * @see SimplifiedDocbookMarkup#TITLE_TAG
935     */
936    public void sectionTitle5_()
937    {
938        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
939    }
940
941    /**
942     * {@inheritDoc}
943     *
944     * @see SimplifiedDocbookMarkup#SECTIONINFO_TAG
945     */
946    public void header()
947    {
948        writeStartTag( SimplifiedDocbookMarkup.SECTIONINFO_TAG );
949    }
950
951    /**
952     * {@inheritDoc}
953     *
954     * @see SimplifiedDocbookMarkup#SECTIONINFO_TAG
955     */
956    public void header_()
957    {
958        writeEndTag( SimplifiedDocbookMarkup.SECTIONINFO_TAG );
959    }
960
961    /**
962     * {@inheritDoc}
963     *
964     * @see SimplifiedDocbookMarkup#ITEMIZEDLIST_TAG
965     */
966    public void list()
967    {
968        paragraph_();
969        writeStartTag( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG );
970    }
971
972    /**
973     * {@inheritDoc}
974     *
975     * @see SimplifiedDocbookMarkup#ITEMIZEDLIST_TAG
976     */
977    public void list_()
978    {
979        writeEndTag( SimplifiedDocbookMarkup.ITEMIZEDLIST_TAG );
980    }
981
982    /**
983     * {@inheritDoc}
984     *
985     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
986     */
987    public void listItem()
988    {
989        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
990        paragraph();
991    }
992
993    /**
994     * {@inheritDoc}
995     *
996     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
997     */
998    public void listItem_()
999    {
1000        paragraph_();
1001        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
1002    }
1003
1004    /**
1005     * {@inheritDoc}
1006     * @see SimplifiedDocbookMarkup#ORDEREDLIST_TAG
1007     * @see SimplifiedDocbookMarkup#NUMERATION_ATTRIBUTE
1008     */
1009    public void numberedList( int numbering )
1010    {
1011        String numeration = DocbookUtils.docbookListNumbering( numbering );
1012
1013        paragraph_();
1014
1015        MutableAttributeSet att = new SimpleAttributeSet();
1016        att.addAttribute( SimplifiedDocbookMarkup.NUMERATION_ATTRIBUTE, numeration );
1017
1018        writeStartTag( SimplifiedDocbookMarkup.ORDEREDLIST_TAG, att );
1019    }
1020
1021    /**
1022     * {@inheritDoc}
1023     *
1024     * @see SimplifiedDocbookMarkup#ORDEREDLIST_TAG
1025     */
1026    public void numberedList_()
1027    {
1028        writeEndTag( SimplifiedDocbookMarkup.ORDEREDLIST_TAG );
1029    }
1030
1031    /**
1032     * {@inheritDoc}
1033     *
1034     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
1035     */
1036    public void numberedListItem()
1037    {
1038        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
1039        paragraph();
1040    }
1041
1042    /**
1043     * {@inheritDoc}
1044     *
1045     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
1046     */
1047    public void numberedListItem_()
1048    {
1049        paragraph_();
1050        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
1051    }
1052
1053    /**
1054     * {@inheritDoc}
1055     *
1056     * @see SimplifiedDocbookMarkup#VARIABLELIST_TAG
1057     */
1058    public void definitionList()
1059    {
1060        paragraph_();
1061        writeStartTag( SimplifiedDocbookMarkup.VARIABLELIST_TAG );
1062    }
1063
1064    /**
1065     * {@inheritDoc}
1066     *
1067     * @see SimplifiedDocbookMarkup#VARIABLELIST_TAG
1068     */
1069    public void definitionList_()
1070    {
1071        writeEndTag( SimplifiedDocbookMarkup.VARIABLELIST_TAG );
1072    }
1073
1074    /**
1075     * {@inheritDoc}
1076     *
1077     * @see SimplifiedDocbookMarkup#VARLISTENTRY_TAG
1078     */
1079    public void definitionListItem()
1080    {
1081        writeStartTag( SimplifiedDocbookMarkup.VARLISTENTRY_TAG );
1082    }
1083
1084    /**
1085     * {@inheritDoc}
1086     *
1087     * @see SimplifiedDocbookMarkup#VARLISTENTRY_TAG
1088     */
1089    public void definitionListItem_()
1090    {
1091        writeEndTag( SimplifiedDocbookMarkup.VARLISTENTRY_TAG );
1092    }
1093
1094    /**
1095     * {@inheritDoc}
1096     *
1097     * @see SimplifiedDocbookMarkup#TERM_TAG
1098     */
1099    public void definedTerm()
1100    {
1101        writeStartTag( SimplifiedDocbookMarkup.TERM_TAG );
1102    }
1103
1104    /**
1105     * {@inheritDoc}
1106     *
1107     * @see SimplifiedDocbookMarkup#TERM_TAG
1108     */
1109    public void definedTerm_()
1110    {
1111        writeEndTag( SimplifiedDocbookMarkup.TERM_TAG );
1112    }
1113
1114    /**
1115     * {@inheritDoc}
1116     *
1117     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
1118     */
1119    public void definition()
1120    {
1121        writeStartTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
1122        paragraph();
1123    }
1124
1125    /**
1126     * {@inheritDoc}
1127     *
1128     * @see SimplifiedDocbookMarkup#LISTITEM_TAG
1129     */
1130    public void definition_()
1131    {
1132        paragraph_();
1133        writeEndTag( SimplifiedDocbookMarkup.LISTITEM_TAG );
1134    }
1135
1136    /**
1137     * {@inheritDoc}
1138     *
1139     * @see SimplifiedDocbookMarkup#PARA_TAG
1140     */
1141    public void paragraph()
1142    {
1143        if ( !paragraph )
1144        {
1145            writeStartTag( SimplifiedDocbookMarkup.PARA_TAG );
1146            paragraph = true;
1147        }
1148    }
1149
1150    /**
1151     * {@inheritDoc}
1152     *
1153     * @see SimplifiedDocbookMarkup#PARA_TAG
1154     */
1155    public void paragraph_()
1156    {
1157        if ( paragraph )
1158        {
1159            writeEndTag( SimplifiedDocbookMarkup.PARA_TAG );
1160            paragraph = false;
1161        }
1162    }
1163
1164    /**
1165     * {@inheritDoc}
1166     * @see SimplifiedDocbookMarkup#PROGRAMLISTING_TAG
1167     */
1168    public void verbatim( boolean boxed )
1169    {
1170        verbatimFlag = true;
1171        paragraph_();
1172        writeStartTag( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG );
1173    }
1174
1175    /**
1176     * {@inheritDoc}
1177     *
1178     * @see SimplifiedDocbookMarkup#PROGRAMLISTING_TAG
1179     */
1180    public void verbatim_()
1181    {
1182        writeEndTag( SimplifiedDocbookMarkup.PROGRAMLISTING_TAG );
1183        verbatimFlag = false;
1184    }
1185
1186    /**
1187     * {@inheritDoc}
1188     */
1189    public void horizontalRule()
1190    {
1191        markup( horizontalRuleElement );
1192    }
1193
1194    /**
1195     * {@inheritDoc}
1196     */
1197    public void pageBreak()
1198    {
1199        markup( pageBreakElement );
1200    }
1201
1202    /**
1203     * {@inheritDoc}
1204     */
1205    public void figure()
1206    {
1207        writeStartTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
1208    }
1209
1210    /**
1211     * {@inheritDoc}
1212     */
1213    public void figure_()
1214    {
1215        writeEndTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
1216    }
1217
1218    /**
1219     * <p>graphicElement</p>
1220     *
1221     * @see SimplifiedDocbookMarkup#MEDIAOBJECT_TAG
1222     * @see SimplifiedDocbookMarkup#IMAGEOBJECT_TAG
1223     * @see SimplifiedDocbookMarkup#IMAGEDATA_TAG
1224     * @see SimplifiedDocbookMarkup#FORMAT_ATTRIBUTE
1225     * @see SimplifiedDocbookMarkup#FILEREF_ATTRIBUTE
1226     * @deprecated do not use!
1227     */
1228    protected void graphicElement()
1229    {
1230        if ( graphicsFileName != null )
1231        {
1232            String format = FileUtils.extension( graphicsFileName ).toUpperCase( Locale.ENGLISH );
1233            if ( format.length() == 0 )
1234            {
1235                format = "JPEG";
1236            }
1237
1238            writeStartTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
1239            writeStartTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
1240
1241            MutableAttributeSet att = new SimpleAttributeSet();
1242            att.addAttribute( SimplifiedDocbookMarkup.FORMAT_ATTRIBUTE, escapeHTML( format, true ) );
1243            att.addAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE, escapeHTML( graphicsFileName, true ) );
1244
1245            writeSimpleTag( SimplifiedDocbookMarkup.IMAGEDATA_TAG, att );
1246
1247            writeEndTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
1248            writeEndTag( SimplifiedDocbookMarkup.MEDIAOBJECT_TAG );
1249            graphicsFileName = null;
1250        }
1251    }
1252
1253    /** {@inheritDoc} */
1254    public void figureGraphics( String name )
1255    {
1256        String format = FileUtils.extension( name ).toUpperCase( Locale.ENGLISH );
1257
1258        writeStartTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
1259
1260        MutableAttributeSet att = new SimpleAttributeSet();
1261        att.addAttribute( SimplifiedDocbookMarkup.FORMAT_ATTRIBUTE, escapeHTML( format, true ) );
1262        att.addAttribute( SimplifiedDocbookMarkup.FILEREF_ATTRIBUTE, escapeHTML( name, true ) );
1263
1264        writeSimpleTag( SimplifiedDocbookMarkup.IMAGEDATA_TAG, att );
1265
1266        writeEndTag( SimplifiedDocbookMarkup.IMAGEOBJECT_TAG );
1267    }
1268
1269    /**
1270     * {@inheritDoc}
1271     *
1272     * @see SimplifiedDocbookMarkup#FIGURE_TAG
1273     * @see SimplifiedDocbookMarkup#TITLE_TAG
1274     */
1275    public void figureCaption()
1276    {
1277        writeStartTag( SimplifiedDocbookMarkup.CAPTION_TAG );
1278        writeStartTag( SimplifiedDocbookMarkup.PARA_TAG );
1279    }
1280
1281    /**
1282     * {@inheritDoc}
1283     *
1284     * @see SimplifiedDocbookMarkup#FIGURE_TAG
1285     * @see SimplifiedDocbookMarkup#TITLE_TAG
1286     */
1287    public void figureCaption_()
1288    {
1289        writeEndTag( SimplifiedDocbookMarkup.PARA_TAG );
1290        writeEndTag( SimplifiedDocbookMarkup.CAPTION_TAG );
1291    }
1292
1293    /**
1294     * {@inheritDoc}
1295     */
1296    public void table()
1297    {
1298        tableHasCaption = false;
1299    }
1300
1301    /**
1302     * {@inheritDoc}
1303     *
1304     * @see SimplifiedDocbookMarkup#INFORMALTABLE_TAG
1305     * @see SimplifiedDocbookMarkup#FRAME_ATTRIBUTE
1306     * @see SimplifiedDocbookMarkup#ROWSEP_ATTRIBUTE
1307     * @see SimplifiedDocbookMarkup#COLSEP_ATTRIBUTE
1308     * @see SimplifiedDocbookMarkup#TABLE_TAG
1309     */
1310    public void table_()
1311    {
1312        if ( tableHasCaption )
1313        {
1314            tableHasCaption = false;
1315            // Formal table+title already written to original destination ---
1316
1317            out.write( tableRows  );
1318            writeEndTag( TABLE_TAG );
1319        }
1320        else
1321        {
1322            String frame = "none";
1323            String sep = "0";
1324            if ( tableHasGrid )
1325            {
1326                frame = "all";
1327                sep = "1";
1328            }
1329
1330            MutableAttributeSet att = new SimpleAttributeSet();
1331            att.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE, frame );
1332            att.addAttribute( SimplifiedDocbookMarkup.ROWSEP_ATTRIBUTE, sep );
1333            att.addAttribute( SimplifiedDocbookMarkup.COLSEP_ATTRIBUTE, sep );
1334
1335            writeStartTag( SimplifiedDocbookMarkup.INFORMALTABLE_TAG, att );
1336
1337            out.write( tableRows  );
1338
1339            writeEndTag( SimplifiedDocbookMarkup.INFORMALTABLE_TAG );
1340        }
1341
1342        tableRows = null;
1343        tableHasGrid = false;
1344    }
1345
1346    /**
1347     * {@inheritDoc}
1348     * @see SimplifiedDocbookMarkup#TGROUP_TAG
1349     * @see SimplifiedDocbookMarkup#COLS_ATTRIBUTE
1350     * @see SimplifiedDocbookMarkup#COLSPEC_TAG
1351     */
1352    public void tableRows( int[] justification, boolean grid )
1353    {
1354        tableHasGrid = grid;
1355
1356        // Divert output to a string ---
1357        out.flush();
1358        savedOut = out;
1359        tableRowsWriter = new StringWriter();
1360        out = new PrintWriter( tableRowsWriter );
1361
1362        MutableAttributeSet att = new SimpleAttributeSet();
1363        att.addAttribute( SimplifiedDocbookMarkup.COLS_ATTRIBUTE, String.valueOf( justification.length ) );
1364
1365        writeStartTag( SimplifiedDocbookMarkup.TGROUP_TAG, att );
1366
1367        for ( int i1 : justification )
1368        {
1369            String justif;
1370            switch ( i1 )
1371            {
1372                case Sink.JUSTIFY_LEFT:
1373                    justif = "left";
1374                    break;
1375                case Sink.JUSTIFY_RIGHT:
1376                    justif = "right";
1377                    break;
1378                case Sink.JUSTIFY_CENTER:
1379                default:
1380                    justif = "center";
1381                    break;
1382            }
1383
1384            att = new SimpleAttributeSet();
1385            att.addAttribute( "align", justif );
1386
1387            writeSimpleTag( SimplifiedDocbookMarkup.COLSPEC_TAG, att );
1388        }
1389
1390        writeStartTag( SimplifiedDocbookMarkup.TBODY_TAG );
1391    }
1392
1393    /**
1394     * {@inheritDoc}
1395     *
1396     * @see SimplifiedDocbookMarkup#TGROUP_TAG
1397     * @see SimplifiedDocbookMarkup#TBODY_TAG
1398     */
1399    public void tableRows_()
1400    {
1401        writeEndTag( SimplifiedDocbookMarkup.TBODY_TAG );
1402        writeEndTag( SimplifiedDocbookMarkup.TGROUP_TAG );
1403
1404        // Remember diverted output and restore original destination ---
1405        out.flush();
1406        if ( tableRowsWriter == null )
1407        {
1408            throw new IllegalArgumentException( "tableRows( int[] justification, boolean grid )"
1409                                                + " was not called before." );
1410        }
1411
1412        tableRows = tableRowsWriter.toString();
1413        tableRowsWriter = null;
1414        out = savedOut;
1415    }
1416
1417    /**
1418     * {@inheritDoc}
1419     *
1420     * @see SimplifiedDocbookMarkup#ROW_TAG
1421     */
1422    public void tableRow()
1423    {
1424        writeStartTag( SimplifiedDocbookMarkup.ROW_TAG );
1425    }
1426
1427    /**
1428     * {@inheritDoc}
1429     *
1430     * @see SimplifiedDocbookMarkup#ROW_TAG
1431     */
1432    public void tableRow_()
1433    {
1434        writeEndTag( SimplifiedDocbookMarkup.ROW_TAG );
1435    }
1436
1437    /**
1438     * {@inheritDoc}
1439     *
1440     * @see SimplifiedDocbookMarkup#ENTRY_TAG
1441     */
1442    public void tableCell()
1443    {
1444        writeStartTag( SimplifiedDocbookMarkup.ENTRY_TAG );
1445    }
1446
1447    /**
1448     * {@inheritDoc}
1449     *
1450     * @see SimplifiedDocbookMarkup#ENTRY_TAG
1451     */
1452    public void tableCell_()
1453    {
1454        writeEndTag( SimplifiedDocbookMarkup.ENTRY_TAG );
1455    }
1456
1457    /**
1458     * {@inheritDoc}
1459     *
1460     * @see SimplifiedDocbookMarkup#ENTRY_TAG
1461     */
1462    public void tableHeaderCell()
1463    {
1464        writeStartTag( SimplifiedDocbookMarkup.ENTRY_TAG );
1465    }
1466
1467    /**
1468     * {@inheritDoc}
1469     *
1470     * @see SimplifiedDocbookMarkup#ENTRY_TAG
1471     */
1472    public void tableHeaderCell_()
1473    {
1474        writeEndTag( SimplifiedDocbookMarkup.ENTRY_TAG );
1475    }
1476
1477    /**
1478     * {@inheritDoc}
1479     *
1480     * @see SimplifiedDocbookMarkup#TABLE_TAG
1481     * @see SimplifiedDocbookMarkup#FRAME_ATTRIBUTE
1482     * @see SimplifiedDocbookMarkup#ROWSEP_ATTRIBUTE
1483     * @see SimplifiedDocbookMarkup#COLSEP_ATTRIBUTE
1484     * @see SimplifiedDocbookMarkup#TITLE_TAG
1485     */
1486    public void tableCaption()
1487    {
1488        tableHasCaption = true;
1489
1490        String frame;
1491        int sep;
1492        if ( tableHasGrid )
1493        {
1494            frame = "all";
1495            sep = 1;
1496        }
1497        else
1498        {
1499            frame = "none";
1500            sep = 0;
1501        }
1502
1503        MutableAttributeSet att = new SimpleAttributeSet();
1504        att.addAttribute( SimplifiedDocbookMarkup.FRAME_ATTRIBUTE, frame );
1505        att.addAttribute( SimplifiedDocbookMarkup.ROWSEP_ATTRIBUTE, String.valueOf( sep ) );
1506        att.addAttribute( SimplifiedDocbookMarkup.COLSEP_ATTRIBUTE, String.valueOf( sep ) );
1507
1508        writeStartTag( TABLE_TAG, att );
1509
1510        writeStartTag( SimplifiedDocbookMarkup.TITLE_TAG );
1511    }
1512
1513    /**
1514     * {@inheritDoc}
1515     *
1516     * @see SimplifiedDocbookMarkup#TITLE_TAG
1517     */
1518    public void tableCaption_()
1519    {
1520        writeEndTag( SimplifiedDocbookMarkup.TITLE_TAG );
1521    }
1522
1523    /**
1524     * {@inheritDoc}
1525     * @see SimplifiedDocbookMarkup#ANCHOR_TAG
1526     */
1527    public void anchor( String name )
1528    {
1529        if ( name == null )
1530        {
1531            throw new NullPointerException( "Anchor name cannot be null!" );
1532        }
1533
1534        if ( authorDateFlag )
1535        {
1536            return;
1537        }
1538
1539        String id = name;
1540
1541        if ( !DoxiaUtils.isValidId( id ) )
1542        {
1543            id = DoxiaUtils.encodeId( name, true );
1544
1545            String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
1546            logMessage( "modifiedLink", msg );
1547        }
1548
1549        MutableAttributeSet att = new SimpleAttributeSet();
1550        att.addAttribute( ID_ATTRIBUTE, id );
1551
1552        writeSimpleTag( SimplifiedDocbookMarkup.ANCHOR_TAG, att );
1553    }
1554
1555    /**
1556     * {@inheritDoc}
1557     *
1558     * @see SimplifiedDocbookMarkup#ANCHOR_TAG
1559     */
1560    public void anchor_()
1561    {
1562        comment( " anchor_end " );
1563    }
1564
1565    /**
1566     * {@inheritDoc}
1567     * @see SimplifiedDocbookMarkup#ULINK_TAG
1568     * @see SimplifiedDocbookMarkup#URL_ATTRIBUTE
1569     * @see SimplifiedDocbookMarkup#LINK_TAG
1570     * @see SimplifiedDocbookMarkup#LINKEND_ATTRIBUTE
1571     */
1572    public void link( String name )
1573    {
1574        if ( name == null )
1575        {
1576            throw new NullPointerException( "Link name cannot be null!" );
1577        }
1578
1579        if ( DoxiaUtils.isInternalLink( name ) )
1580        {
1581            String linkend = name.substring( 1 );
1582            MutableAttributeSet att = new SimpleAttributeSet();
1583            att.addAttribute( SimplifiedDocbookMarkup.LINKEND_ATTRIBUTE, escapeHTML( linkend ) );
1584
1585            writeStartTag( SimplifiedDocbookMarkup.LINK_TAG, att );
1586        }
1587        else
1588        {
1589            externalLinkFlag = true;
1590            MutableAttributeSet att = new SimpleAttributeSet();
1591            att.addAttribute( SimplifiedDocbookMarkup.URL_ATTRIBUTE, escapeHTML( name, true ) );
1592
1593            writeStartTag( SimplifiedDocbookMarkup.ULINK_TAG, att );
1594        }
1595    }
1596
1597    /**
1598     * {@inheritDoc}
1599     *
1600     * @see SimplifiedDocbookMarkup#ULINK_TAG
1601     * @see SimplifiedDocbookMarkup#LINK_TAG
1602     */
1603    public void link_()
1604    {
1605        if ( externalLinkFlag )
1606        {
1607            writeEndTag( SimplifiedDocbookMarkup.ULINK_TAG );
1608            externalLinkFlag = false;
1609        }
1610        else
1611        {
1612            writeEndTag( SimplifiedDocbookMarkup.LINK_TAG );
1613        }
1614    }
1615
1616    /**
1617     * {@inheritDoc}
1618     */
1619    public void inline()
1620    {
1621        inline( null );
1622    }
1623
1624    /** {@inheritDoc} */
1625    public void inline( SinkEventAttributes attributes )
1626    {
1627        List<String> tags = new ArrayList<>();
1628
1629        if ( attributes != null )
1630        {
1631
1632            if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "italic" ) )
1633            {
1634                markup( italicBeginTag );
1635                tags.add( 0, italicEndTag );
1636            }
1637
1638            if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "bold" ) )
1639            {
1640                write( boldBeginTag );
1641                tags.add( 0, boldEndTag );
1642            }
1643
1644            if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, "code" ) )
1645            {
1646                if ( !authorDateFlag )
1647                {
1648                    write( monospacedBeginTag );
1649                    tags.add( 0, monospacedEndTag );
1650                }
1651            }
1652
1653        }
1654
1655        inlineStack.push( tags );
1656    }
1657
1658    /**
1659     * {@inheritDoc}
1660     */
1661    public void inline_()
1662    {
1663        for ( String tag: inlineStack.pop() )
1664        {
1665            markup( tag );
1666        }
1667    }
1668
1669    /**
1670     * {@inheritDoc}
1671     */
1672    public void italic()
1673    {
1674        inline( SinkEventAttributeSet.Semantics.ITALIC );
1675    }
1676
1677    /**
1678     * {@inheritDoc}
1679     */
1680    public void italic_()
1681    {
1682        inline_();
1683    }
1684
1685    /**
1686     * {@inheritDoc}
1687     */
1688    public void bold()
1689    {
1690        inline( SinkEventAttributeSet.Semantics.BOLD );
1691    }
1692
1693    /**
1694     * {@inheritDoc}
1695     */
1696    public void bold_()
1697    {
1698        inline_();
1699    }
1700
1701    /**
1702     * {@inheritDoc}
1703     */
1704    public void monospaced()
1705    {
1706        inline( SinkEventAttributeSet.Semantics.CODE );
1707    }
1708
1709    /**
1710     * {@inheritDoc}
1711     */
1712    public void monospaced_()
1713    {
1714        inline_();
1715    }
1716
1717    /**
1718     * {@inheritDoc}
1719     */
1720    public void lineBreak()
1721    {
1722        markup( lineBreakElement );
1723    }
1724
1725    /**
1726     * {@inheritDoc}
1727     */
1728    public void nonBreakingSpace()
1729    {
1730        markup( "&#x00A0;" );
1731        //markup( "&nbsp;" );
1732    }
1733
1734    /** {@inheritDoc} */
1735    public void text( String text )
1736    {
1737        if ( verbatimFlag )
1738        {
1739            verbatimContent( text );
1740        }
1741        else
1742        {
1743            content( text );
1744        }
1745    }
1746
1747    /** {@inheritDoc} */
1748    public void comment( String comment )
1749    {
1750        if ( comment != null )
1751        {
1752            String originalComment = comment;
1753
1754            // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
1755            while ( comment.contains( "--" ) )
1756            {
1757                comment = comment.replace( "--", "- -" );
1758            }
1759
1760            if ( comment.endsWith( "-" ) )
1761            {
1762                comment += " ";
1763            }
1764
1765            if ( !originalComment.equals( comment ) )
1766            {
1767                String msg = "Modified invalid comment: '" + originalComment + "' to '" + comment + "'";
1768                logMessage( "modifiedComment", msg );
1769            }
1770
1771            StringBuilder buffer = new StringBuilder( comment.length() + 7 );
1772
1773            buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS );
1774            buffer.append( comment );
1775            buffer.append( MINUS ).append( MINUS ).append( GREATER_THAN );
1776
1777            markup( buffer.toString() );
1778        }
1779    }
1780
1781    /**
1782     * {@inheritDoc}
1783     *
1784     * Unknown events just log a warning message but are ignored otherwise.
1785     * @see org.apache.maven.doxia.sink.Sink#unknown(String,Object[],SinkEventAttributes)
1786     */
1787    public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
1788    {
1789        String msg = "Unknown Sink event: '" + name + "', ignoring!";
1790        logMessage( "unknownEvent", msg );
1791    }
1792
1793    // -----------------------------------------------------------------------
1794
1795    /**
1796     * Write text to output, preserving white space.
1797     *
1798     * @param text The text to write.
1799     */
1800    protected void markup( String text )
1801    {
1802        if ( !skip )
1803        {
1804            out.write( text );
1805        }
1806    }
1807
1808    /**
1809     * Write SGML escaped text to output, not preserving white space.
1810     *
1811     * @param text The text to write.
1812     */
1813    protected void content( String text )
1814    {
1815        if ( !skip )
1816        {
1817            out.write( escapeHTML( text, true ) );
1818        }
1819    }
1820
1821    /**
1822     * Write SGML escaped text to output, preserving white space.
1823     *
1824     * @param text The text to write.
1825     */
1826    protected void verbatimContent( String text )
1827    {
1828        if ( !skip )
1829        {
1830            out.write( escapeHTML( text, true ) );
1831        }
1832    }
1833
1834    // -----------------------------------------------------------------------
1835
1836    /**
1837     * {@inheritDoc}
1838     */
1839    public void flush()
1840    {
1841        out.flush();
1842    }
1843
1844    /**
1845     * {@inheritDoc}
1846     */
1847    public void close()
1848    {
1849        out.close();
1850
1851        if ( getLog().isWarnEnabled() && this.warnMessages != null )
1852        {
1853            for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
1854            {
1855                for ( String msg : entry.getValue() )
1856                {
1857                    getLog().warn( msg );
1858                }
1859            }
1860
1861            this.warnMessages = null;
1862        }
1863    }
1864
1865    /** {@inheritDoc} */
1866    protected void write( String text )
1867    {
1868        markup( unifyEOLs( text ) );
1869    }
1870
1871    /**
1872     * <p>Setter for the field <code>skip</code>.</p>
1873     *
1874     * @param skip the skip to set.
1875     * @since 1.1
1876     */
1877    public void setSkip( boolean skip )
1878    {
1879        this.skip = skip;
1880    }
1881
1882    /**
1883     * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
1884     *
1885     * @param key not null
1886     * @param msg not null
1887     * @see #close()
1888     * @since 1.1.1
1889     */
1890    private void logMessage( String key, String msg )
1891    {
1892        msg = "[Docbook Sink] " + msg;
1893        if ( getLog().isDebugEnabled() )
1894        {
1895            getLog().debug( msg );
1896
1897            return;
1898        }
1899
1900        if ( warnMessages == null )
1901        {
1902            warnMessages = new HashMap<>();
1903        }
1904
1905        Set<String> set = warnMessages.get( key );
1906        if ( set == null )
1907        {
1908            set = new TreeSet<>();
1909        }
1910        set.add( msg );
1911        warnMessages.put( key, set );
1912    }
1913}