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.AbstractXmlSink;
36  import org.apache.maven.doxia.sink.SinkEventAttributes;
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  import org.codehaus.plexus.util.StringUtils;
43  
44  /**
45   * <a href="http://www.oasis-open.org/docbook">Docbook</a> Sink implementation.
46   * <br/>
47   * It uses the Docbook v4.4 DTD <a href="http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd">
48   * http://www.oasis-open.org/docbook/sgml/4.4/docbookx.dtd</a>.
49   *
50   * @version $Id: DocBookSink.java 1090706 2011-04-09 23:15:28Z hboutemy $
51   * @since 1.0
52   */
53  public class DocBookSink
54      extends AbstractXmlSink
55      implements DocbookMarkup, SimplifiedDocbookMarkup
56  {
57      /** DocBook V4.4 SGML public id: "-//OASIS//DTD DocBook V4.4//EN"
58       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_PUBLIC_ID} instead of. */
59      public static final String DEFAULT_SGML_PUBLIC_ID = DocbookMarkup.DEFAULT_SGML_PUBLIC_ID;
60  
61      /** DocBook XML V4.4 XML public id: "-//OASIS//DTD DocBook XML V4.4//EN"
62       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_PUBLIC_ID} instead of. */
63      public static final String DEFAULT_XML_PUBLIC_ID = DocbookMarkup.DEFAULT_XML_PUBLIC_ID;
64  
65      /** DocBook XML V4.4 XML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" target="alexandria_uri">http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
66       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_XML_SYSTEM_ID} instead of. */
67      public static final String DEFAULT_XML_SYSTEM_ID = DocbookMarkup.DEFAULT_XML_SYSTEM_ID;
68  
69      /** DocBook XML V4.4 SGML system id: "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" target="alexandria_uri">http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"
70       * @deprecated since 1.1, use {@link DocbookMarkup#DEFAULT_SGML_SYSTEM_ID} instead of. */
71      public static final String DEFAULT_SGML_SYSTEM_ID = DocbookMarkup.DEFAULT_SGML_SYSTEM_ID;
72  
73      /** The output writer. */
74      private PrintWriter out;
75  
76      /** xmlMode. */
77      private boolean xmlMode = false;
78  
79      /** styleSheet. */
80      private String styleSheet = null;
81  
82      /** language. */
83      private String lang = null;
84  
85      /** publicId. */
86      private String publicId = null;
87  
88      /** systemId. */
89      private String systemId = null;
90  
91      /** italicBegin. */
92      private String italicBeginTag;
93  
94      /** italicEnd. */
95      private String italicEndTag;
96  
97      /** boldBegin. */
98      private String boldBeginTag;
99  
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         StringBuffer buffer = new StringBuffer( 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 }