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