View Javadoc
1   package org.apache.maven.doxia.module.docbook;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.PrintWriter;
23  import java.io.StringWriter;
24  import java.io.Writer;
25  import java.util.HashMap;
26  import java.util.Locale;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.StringTokenizer;
30  import java.util.TreeSet;
31  
32  import javax.swing.text.MutableAttributeSet;
33  import javax.swing.text.SimpleAttributeSet;
34  
35  import org.apache.maven.doxia.sink.SinkEventAttributes;
36  import org.apache.maven.doxia.sink.impl.AbstractXmlSink;
37  import org.apache.maven.doxia.sink.Sink;
38  import org.apache.maven.doxia.util.DoxiaUtils;
39  import org.apache.maven.doxia.util.HtmlTools;
40  
41  import org.codehaus.plexus.util.FileUtils;
42  
43  /**
44   * <a href="http://www.oasis-open.org/docbook">Docbook</a> Sink implementation.
45   * <br/>
46   * It uses the Docbook v4.4 DTD <a href="http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd">
47   * http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd</a>.
48   *
49   * @version $Id: DocBookSink.java 1726411 2016-01-23 16:34:09Z hboutemy $
50   * @since 1.0
51   */
52  public class DocBookSink
53      extends AbstractXmlSink
54      implements DocbookMarkup, SimplifiedDocbookMarkup
55  {
56      /** DocBook V4.4 SGML public id: "-//OASIS//DTD DocBook V4.4//EN"
57       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_PUBLIC_ID} instead of. */
58      public static final String DEFAULT_SGML_PUBLIC_ID = DocbookMarkup.DEFAULT_SGML_PUBLIC_ID;
59  
60      /** DocBook XML V4.4 XML public id: "-//OASIS//DTD DocBook XML V4.4//EN"
61       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_PUBLIC_ID} instead of. */
62      public static final String DEFAULT_XML_PUBLIC_ID = DocbookMarkup.DEFAULT_XML_PUBLIC_ID;
63  
64      /** DocBook XML V4.4 XML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
65       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_SYSTEM_ID} instead of. */
66      public static final String DEFAULT_XML_SYSTEM_ID = DocbookMarkup.DEFAULT_XML_SYSTEM_ID;
67  
68      /** DocBook XML V4.4 SGML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
69       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_SYSTEM_ID} instead of. */
70      public static final String DEFAULT_SGML_SYSTEM_ID = DocbookMarkup.DEFAULT_SGML_SYSTEM_ID;
71  
72      /** The output writer. */
73      private PrintWriter out;
74  
75      /** xmlMode. */
76      private boolean xmlMode = false;
77  
78      /** styleSheet. */
79      private String styleSheet = null;
80  
81      /** language. */
82      private String lang = null;
83  
84      /** publicId. */
85      private String publicId = null;
86  
87      /** systemId. */
88      private String systemId = null;
89  
90      /** italicBegin. */
91      private String italicBeginTag;
92  
93      /** italicEnd. */
94      private String italicEndTag;
95  
96      /** boldBegin. */
97      private String boldBeginTag;
98  
99      /** 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 }