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