Coverage Report - org.apache.maven.doxia.sink.XhtmlBaseSink
 
Classes in this File Line Coverage Branch Coverage Complexity
XhtmlBaseSink
82%
518/627
63%
160/252
2,03
 
 1  
 package org.apache.maven.doxia.sink;
 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.Enumeration;
 26  
 import java.util.HashMap;
 27  
 import java.util.LinkedList;
 28  
 import java.util.Map;
 29  
 import java.util.Set;
 30  
 import java.util.TreeSet;
 31  
 
 32  
 import javax.swing.text.MutableAttributeSet;
 33  
 import javax.swing.text.html.HTML.Attribute;
 34  
 import javax.swing.text.html.HTML.Tag;
 35  
 
 36  
 import org.apache.maven.doxia.markup.HtmlMarkup;
 37  
 import org.apache.maven.doxia.markup.Markup;
 38  
 import org.apache.maven.doxia.util.DoxiaUtils;
 39  
 import org.apache.maven.doxia.util.HtmlTools;
 40  
 
 41  
 import org.codehaus.plexus.util.StringUtils;
 42  
 import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
 43  
 
 44  
 /**
 45  
  * Abstract base xhtml sink implementation.
 46  
  *
 47  
  * @author Jason van Zyl
 48  
  * @author ltheussl
 49  
  * @version $Id: XhtmlBaseSink.java 1090706 2011-04-09 23:15:28Z hboutemy $
 50  
  * @since 1.1
 51  
  */
 52  
 public class XhtmlBaseSink
 53  
     extends AbstractXmlSink
 54  
     implements HtmlMarkup
 55  
 {
 56  
     // ----------------------------------------------------------------------
 57  
     // Instance fields
 58  
     // ----------------------------------------------------------------------
 59  
 
 60  
     /** The PrintWriter to write the result. */
 61  
     private final PrintWriter writer;
 62  
 
 63  
     /** Used to collect text events mainly for the head events. */
 64  78
     private StringBuffer textBuffer = new StringBuffer();
 65  
 
 66  
     /** An indication on if we're inside a head. */
 67  
     private boolean headFlag;
 68  
 
 69  
     /** An indication on if we're inside an image caption flag. */
 70  
     private boolean figureCaptionFlag;
 71  
 
 72  
     /** An indication on if we're inside a paragraph flag. */
 73  
     private boolean paragraphFlag;
 74  
 
 75  
     /** An indication on if we're in verbatim mode. */
 76  
     private boolean verbatimFlag;
 77  
 
 78  
     /** Stack of alignment int[] of table cells. */
 79  
     private final LinkedList<int[]> cellJustifStack;
 80  
 
 81  
     /** Stack of justification of table cells. */
 82  
     private final LinkedList<Boolean> isCellJustifStack;
 83  
 
 84  
     /** Stack of current table cell. */
 85  
     private final LinkedList<Integer> cellCountStack;
 86  
 
 87  
     /** Used to style successive table rows differently. */
 88  78
     private boolean evenTableRow = true;
 89  
 
 90  
     /** The stack of StringWriter to write the table result temporary, so we could play with the output DOXIA-177. */
 91  
     private final LinkedList<StringWriter> tableContentWriterStack;
 92  
 
 93  
     private final LinkedList<StringWriter> tableCaptionWriterStack;
 94  
 
 95  
     private final LinkedList<PrettyPrintXMLWriter> tableCaptionXMLWriterStack;
 96  
 
 97  
     /** The stack of table caption */
 98  
     private final LinkedList<String> tableCaptionStack;
 99  
 
 100  
     /** used to store attributes passed to table(). */
 101  
     protected MutableAttributeSet tableAttributes;
 102  
 
 103  
     /** Used to distinguish old-style figure handling. */
 104  
     private boolean legacyFigure;
 105  
 
 106  
     /** Used to distinguish old-style figure handling. */
 107  
     private boolean legacyFigureCaption;
 108  
 
 109  
     /** Indicates that an image is part of a figure. */
 110  
     private boolean inFigure;
 111  
 
 112  
     /** Flag to know if {@link #tableRows(int[], boolean)} is called or not. It is mainly to be backward compatible
 113  
      * with some plugins (like checkstyle) which uses:
 114  
      * <pre>
 115  
      * sink.table();
 116  
      * sink.tableRow();
 117  
      * </pre>
 118  
      * instead of
 119  
      * <pre>
 120  
      * sink.table();
 121  
      * sink.tableRows( justify, true );
 122  
      * sink.tableRow();
 123  
      * </pre>
 124  
      * */
 125  78
     protected boolean tableRows = false;
 126  
 
 127  
     /** Map of warn messages with a String as key to describe the error type and a Set as value.
 128  
      * Using to reduce warn messages. */
 129  
     private Map<String, Set<String>> warnMessages;
 130  
 
 131  
     // ----------------------------------------------------------------------
 132  
     // Constructor
 133  
     // ----------------------------------------------------------------------
 134  
 
 135  
     /**
 136  
      * Constructor, initialize the PrintWriter.
 137  
      *
 138  
      * @param out The writer to write the result.
 139  
      */
 140  
     public XhtmlBaseSink( Writer out )
 141  78
     {
 142  78
         this.writer = new PrintWriter( out );
 143  
 
 144  78
         this.cellJustifStack = new LinkedList<int[]>();
 145  78
         this.isCellJustifStack = new LinkedList<Boolean>();
 146  78
         this.cellCountStack = new LinkedList<Integer>();
 147  78
         this.tableContentWriterStack = new LinkedList<StringWriter>();
 148  78
         this.tableCaptionWriterStack = new LinkedList<StringWriter>();
 149  78
         this.tableCaptionXMLWriterStack = new LinkedList<PrettyPrintXMLWriter>();
 150  78
         this.tableCaptionStack = new LinkedList<String>();
 151  
 
 152  78
         init();
 153  78
     }
 154  
 
 155  
     // ----------------------------------------------------------------------
 156  
     // Accessor methods
 157  
     // ----------------------------------------------------------------------
 158  
 
 159  
     /**
 160  
      * To use mainly when playing with the head events.
 161  
      *
 162  
      * @return the current buffer of text events.
 163  
      */
 164  
     protected StringBuffer getTextBuffer()
 165  
     {
 166  0
         return this.textBuffer;
 167  
     }
 168  
 
 169  
     /**
 170  
      * <p>Setter for the field <code>headFlag</code>.</p>
 171  
      *
 172  
      * @param headFlag an header flag.
 173  
      */
 174  
     protected void setHeadFlag( boolean headFlag )
 175  
     {
 176  0
         this.headFlag = headFlag;
 177  0
     }
 178  
 
 179  
     /**
 180  
      * <p>isHeadFlag.</p>
 181  
      *
 182  
      * @return the current headFlag.
 183  
      */
 184  
     protected boolean isHeadFlag()
 185  
     {
 186  0
         return this.headFlag ;
 187  
     }
 188  
 
 189  
     /**
 190  
      * <p>Setter for the field <code>verbatimFlag</code>.</p>
 191  
      *
 192  
      * @param verb a verbatim flag.
 193  
      */
 194  
     protected void setVerbatimFlag( boolean verb )
 195  
     {
 196  0
         this.verbatimFlag = verb;
 197  0
     }
 198  
 
 199  
     /**
 200  
      * <p>isVerbatimFlag.</p>
 201  
      *
 202  
      * @return the current verbatim flag.
 203  
      */
 204  
     protected boolean isVerbatimFlag()
 205  
     {
 206  2
         return this.verbatimFlag ;
 207  
     }
 208  
 
 209  
     /**
 210  
      * <p>Setter for the field <code>cellJustif</code>.</p>
 211  
      *
 212  
      * @param justif the new cell justification array.
 213  
      */
 214  
     protected void setCellJustif( int[] justif )
 215  
     {
 216  10
         this.cellJustifStack.addLast( justif );
 217  10
         this.isCellJustifStack.addLast( Boolean.TRUE );
 218  10
     }
 219  
 
 220  
     /**
 221  
      * <p>Getter for the field <code>cellJustif</code>.</p>
 222  
      *
 223  
      * @return the current cell justification array.
 224  
      */
 225  
     protected int[] getCellJustif()
 226  
     {
 227  0
         return (int[]) this.cellJustifStack.getLast();
 228  
     }
 229  
 
 230  
     /**
 231  
      * <p>Setter for the field <code>cellCount</code>.</p>
 232  
      *
 233  
      * @param count the new cell count.
 234  
      */
 235  
     protected void setCellCount( int count )
 236  
     {
 237  0
         this.cellCountStack.addLast( count );
 238  0
     }
 239  
 
 240  
     /**
 241  
      * <p>Getter for the field <code>cellCount</code>.</p>
 242  
      *
 243  
      * @return the current cell count.
 244  
      */
 245  
     protected int getCellCount()
 246  
     {
 247  0
         return Integer.parseInt( this.cellCountStack.getLast().toString() );
 248  
     }
 249  
 
 250  
     /**
 251  
      * Reset all variables.
 252  
      *
 253  
      * @deprecated since 1.1.2, use {@link #init()} instead of.
 254  
      */
 255  
     protected void resetState()
 256  
     {
 257  0
         init();
 258  0
     }
 259  
 
 260  
     /** {@inheritDoc} */
 261  
     protected void init()
 262  
     {
 263  154
         super.init();
 264  
 
 265  154
         resetTextBuffer();
 266  
 
 267  154
         this.headFlag = false;
 268  154
         this.verbatimFlag = false;
 269  154
         this.evenTableRow = true;
 270  154
         this.cellJustifStack.clear();
 271  154
         this.isCellJustifStack.clear();
 272  154
         this.cellCountStack.clear();
 273  154
         this.tableContentWriterStack.clear();
 274  154
         this.tableCaptionWriterStack.clear();
 275  154
         this.tableCaptionXMLWriterStack.clear();
 276  154
         this.tableCaptionStack.clear();
 277  
 
 278  154
         this.headFlag = false;
 279  154
         this.figureCaptionFlag = false;
 280  154
         this.paragraphFlag = false;
 281  154
         this.verbatimFlag = false;
 282  154
         this.evenTableRow = true;
 283  154
         this.tableAttributes = null;
 284  154
         this.legacyFigure = false;
 285  154
         this.legacyFigureCaption = false;
 286  154
         this.inFigure = false;
 287  154
         this.tableRows = false;
 288  154
         this.warnMessages = null;
 289  154
     }
 290  
 
 291  
     /**
 292  
      * Reset the text buffer.
 293  
      */
 294  
     protected void resetTextBuffer()
 295  
     {
 296  154
         this.textBuffer = new StringBuffer();
 297  154
     }
 298  
 
 299  
     // ----------------------------------------------------------------------
 300  
     // Sections
 301  
     // ----------------------------------------------------------------------
 302  
 
 303  
     /** {@inheritDoc} */
 304  
     public void section( int level, SinkEventAttributes attributes )
 305  
     {
 306  2
         onSection( level, attributes );
 307  2
     }
 308  
 
 309  
     /** {@inheritDoc} */
 310  
     public void sectionTitle( int level, SinkEventAttributes attributes )
 311  
     {
 312  2
         onSectionTitle( level, attributes );
 313  2
     }
 314  
 
 315  
     /** {@inheritDoc} */
 316  
     public void sectionTitle_( int level )
 317  
     {
 318  2
         onSectionTitle_( level );
 319  2
     }
 320  
 
 321  
     /** {@inheritDoc} */
 322  
     public void section_( int level )
 323  
     {
 324  2
         onSection_( level );
 325  2
     }
 326  
 
 327  
     /** {@inheritDoc} */
 328  
     public void section1()
 329  
     {
 330  2
         onSection( SECTION_LEVEL_1, null );
 331  2
     }
 332  
 
 333  
     /** {@inheritDoc} */
 334  
     public void sectionTitle1()
 335  
     {
 336  2
         onSectionTitle( SECTION_LEVEL_1, null );
 337  2
     }
 338  
 
 339  
     /** {@inheritDoc} */
 340  
     public void sectionTitle1_()
 341  
     {
 342  2
         onSectionTitle_( SECTION_LEVEL_1 );
 343  2
     }
 344  
 
 345  
     /** {@inheritDoc} */
 346  
     public void section1_()
 347  
     {
 348  2
         onSection_( SECTION_LEVEL_1 );
 349  2
     }
 350  
 
 351  
     /** {@inheritDoc} */
 352  
     public void section2()
 353  
     {
 354  2
         onSection( SECTION_LEVEL_2, null );
 355  2
     }
 356  
 
 357  
     /** {@inheritDoc} */
 358  
     public void sectionTitle2()
 359  
     {
 360  2
         onSectionTitle( SECTION_LEVEL_2, null );
 361  2
     }
 362  
 
 363  
     /** {@inheritDoc} */
 364  
     public void sectionTitle2_()
 365  
     {
 366  2
         onSectionTitle_( SECTION_LEVEL_2 );
 367  2
     }
 368  
 
 369  
     /** {@inheritDoc} */
 370  
     public void section2_()
 371  
     {
 372  2
         onSection_( SECTION_LEVEL_2 );
 373  2
     }
 374  
 
 375  
     /** {@inheritDoc} */
 376  
     public void section3()
 377  
     {
 378  2
         onSection( SECTION_LEVEL_3, null );
 379  2
     }
 380  
 
 381  
     /** {@inheritDoc} */
 382  
     public void sectionTitle3()
 383  
     {
 384  2
         onSectionTitle( SECTION_LEVEL_3, null );
 385  2
     }
 386  
 
 387  
     /** {@inheritDoc} */
 388  
     public void sectionTitle3_()
 389  
     {
 390  2
         onSectionTitle_( SECTION_LEVEL_3 );
 391  2
     }
 392  
 
 393  
     /** {@inheritDoc} */
 394  
     public void section3_()
 395  
     {
 396  2
         onSection_( SECTION_LEVEL_3 );
 397  2
     }
 398  
 
 399  
     /** {@inheritDoc} */
 400  
     public void section4()
 401  
     {
 402  2
         onSection( SECTION_LEVEL_4, null );
 403  2
     }
 404  
 
 405  
     /** {@inheritDoc} */
 406  
     public void sectionTitle4()
 407  
     {
 408  2
         onSectionTitle( SECTION_LEVEL_4, null );
 409  2
     }
 410  
 
 411  
     /** {@inheritDoc} */
 412  
     public void sectionTitle4_()
 413  
     {
 414  2
         onSectionTitle_( SECTION_LEVEL_4 );
 415  2
     }
 416  
 
 417  
     /** {@inheritDoc} */
 418  
     public void section4_()
 419  
     {
 420  2
         onSection_( SECTION_LEVEL_4 );
 421  2
     }
 422  
 
 423  
     /** {@inheritDoc} */
 424  
     public void section5()
 425  
     {
 426  2
         onSection( SECTION_LEVEL_5, null );
 427  2
     }
 428  
 
 429  
     /** {@inheritDoc} */
 430  
     public void sectionTitle5()
 431  
     {
 432  2
         onSectionTitle( SECTION_LEVEL_5, null );
 433  2
     }
 434  
 
 435  
     /** {@inheritDoc} */
 436  
     public void sectionTitle5_()
 437  
     {
 438  2
         onSectionTitle_( SECTION_LEVEL_5 );
 439  2
     }
 440  
 
 441  
     /** {@inheritDoc} */
 442  
     public void section5_()
 443  
     {
 444  2
         onSection_( SECTION_LEVEL_5 );
 445  2
     }
 446  
 
 447  
     /**
 448  
      * Starts a section. The default class style is <code>section</code>.
 449  
      *
 450  
      * @param depth The level of the section.
 451  
      * @param attributes some attributes. May be null.
 452  
      * @see javax.swing.text.html.HTML.Tag#DIV
 453  
      */
 454  
     protected void onSection( int depth, SinkEventAttributes attributes )
 455  
     {
 456  12
         if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
 457  
         {
 458  12
             MutableAttributeSet att = new SinkEventAttributeSet();
 459  12
             att.addAttribute( Attribute.CLASS, "section" );
 460  
             // NOTE: any class entry in attributes will overwrite the above
 461  12
             att.addAttributes( SinkUtils.filterAttributes(
 462  
                     attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) );
 463  
 
 464  12
             att.removeAttribute( Attribute.ID.toString() );
 465  12
             writeStartTag( HtmlMarkup.DIV, att );
 466  
         }
 467  12
     }
 468  
 
 469  
     /**
 470  
      * Ends a section.
 471  
      *
 472  
      * @param depth The level of the section.
 473  
      * @see javax.swing.text.html.HTML.Tag#DIV
 474  
      */
 475  
     protected void onSection_( int depth )
 476  
     {
 477  12
         if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 )
 478  
         {
 479  12
             writeEndTag( HtmlMarkup.DIV );
 480  
         }
 481  12
     }
 482  
 
 483  
     /**
 484  
      * Starts a section title.
 485  
      *
 486  
      * @param depth The level of the section title.
 487  
      * @param attributes some attributes. May be null.
 488  
      * @see javax.swing.text.html.HTML.Tag#H2
 489  
      * @see javax.swing.text.html.HTML.Tag#H3
 490  
      * @see javax.swing.text.html.HTML.Tag#H4
 491  
      * @see javax.swing.text.html.HTML.Tag#H5
 492  
      * @see javax.swing.text.html.HTML.Tag#H6
 493  
      */
 494  
     protected void onSectionTitle( int depth, SinkEventAttributes attributes )
 495  
     {
 496  12
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 497  
                 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
 498  
 
 499  12
         if ( depth == SECTION_LEVEL_1 )
 500  
         {
 501  4
             writeStartTag( HtmlMarkup.H2, atts );
 502  
         }
 503  8
         else if ( depth == SECTION_LEVEL_2 )
 504  
         {
 505  2
             writeStartTag( HtmlMarkup.H3, atts );
 506  
         }
 507  6
         else if ( depth == SECTION_LEVEL_3 )
 508  
         {
 509  2
             writeStartTag( HtmlMarkup.H4, atts );
 510  
         }
 511  4
         else if ( depth == SECTION_LEVEL_4 )
 512  
         {
 513  2
             writeStartTag( HtmlMarkup.H5, atts );
 514  
         }
 515  2
         else if ( depth == SECTION_LEVEL_5 )
 516  
         {
 517  2
             writeStartTag( HtmlMarkup.H6, atts );
 518  
         }
 519  12
     }
 520  
 
 521  
     /**
 522  
      * Ends a section title.
 523  
      *
 524  
      * @param depth The level of the section title.
 525  
      * @see javax.swing.text.html.HTML.Tag#H2
 526  
      * @see javax.swing.text.html.HTML.Tag#H3
 527  
      * @see javax.swing.text.html.HTML.Tag#H4
 528  
      * @see javax.swing.text.html.HTML.Tag#H5
 529  
      * @see javax.swing.text.html.HTML.Tag#H6
 530  
      */
 531  
     protected void onSectionTitle_( int depth )
 532  
     {
 533  12
         if ( depth == SECTION_LEVEL_1 )
 534  
         {
 535  4
             writeEndTag( HtmlMarkup.H2 );
 536  
         }
 537  8
         else if ( depth == SECTION_LEVEL_2 )
 538  
         {
 539  2
             writeEndTag( HtmlMarkup.H3 );
 540  
         }
 541  6
         else if ( depth == SECTION_LEVEL_3 )
 542  
         {
 543  2
             writeEndTag( HtmlMarkup.H4 );
 544  
         }
 545  4
         else if ( depth == SECTION_LEVEL_4 )
 546  
         {
 547  2
             writeEndTag( HtmlMarkup.H5 );
 548  
         }
 549  2
         else if ( depth == SECTION_LEVEL_5 )
 550  
         {
 551  2
             writeEndTag( HtmlMarkup.H6 );
 552  
         }
 553  12
     }
 554  
 
 555  
     // -----------------------------------------------------------------------
 556  
     //
 557  
     // -----------------------------------------------------------------------
 558  
 
 559  
     /**
 560  
      * {@inheritDoc}
 561  
      * @see javax.swing.text.html.HTML.Tag#UL
 562  
      */
 563  
     public void list()
 564  
     {
 565  6
         list( null );
 566  6
     }
 567  
 
 568  
     /**
 569  
      * {@inheritDoc}
 570  
      * @see javax.swing.text.html.HTML.Tag#UL
 571  
      */
 572  
     public void list( SinkEventAttributes attributes )
 573  
     {
 574  10
         if ( paragraphFlag )
 575  
         {
 576  
             // The content of element type "p" must match
 577  
             // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
 578  
             // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
 579  0
             paragraph_();
 580  
         }
 581  
 
 582  10
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 583  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 584  
 
 585  10
         writeStartTag( HtmlMarkup.UL, atts );
 586  10
     }
 587  
 
 588  
     /**
 589  
      * {@inheritDoc}
 590  
      * @see javax.swing.text.html.HTML.Tag#UL
 591  
      */
 592  
     public void list_()
 593  
     {
 594  10
         writeEndTag( HtmlMarkup.UL );
 595  10
     }
 596  
 
 597  
     /**
 598  
      * {@inheritDoc}
 599  
      * @see javax.swing.text.html.HTML.Tag#LI
 600  
      */
 601  
     public void listItem()
 602  
     {
 603  12
         listItem( null );
 604  12
     }
 605  
 
 606  
     /**
 607  
      * {@inheritDoc}
 608  
      * @see javax.swing.text.html.HTML.Tag#LI
 609  
      */
 610  
     public void listItem( SinkEventAttributes attributes )
 611  
     {
 612  14
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 613  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 614  
 
 615  14
         writeStartTag( HtmlMarkup.LI, atts );
 616  14
     }
 617  
 
 618  
     /**
 619  
      * {@inheritDoc}
 620  
      * @see javax.swing.text.html.HTML.Tag#LI
 621  
      */
 622  
     public void listItem_()
 623  
     {
 624  14
         writeEndTag( HtmlMarkup.LI );
 625  14
     }
 626  
 
 627  
     /**
 628  
      * The default list style depends on the numbering.
 629  
      *
 630  
      * {@inheritDoc}
 631  
      * @see javax.swing.text.html.HTML.Tag#OL
 632  
      */
 633  
     public void numberedList( int numbering )
 634  
     {
 635  2
         numberedList( numbering, null );
 636  2
     }
 637  
 
 638  
     /**
 639  
      * The default list style depends on the numbering.
 640  
      *
 641  
      * {@inheritDoc}
 642  
      * @see javax.swing.text.html.HTML.Tag#OL
 643  
      */
 644  
     public void numberedList( int numbering, SinkEventAttributes attributes )
 645  
     {
 646  4
         if ( paragraphFlag )
 647  
         {
 648  
             // The content of element type "p" must match
 649  
             // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
 650  
             // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
 651  0
             paragraph_();
 652  
         }
 653  
 
 654  
         String style;
 655  4
         switch ( numbering )
 656  
         {
 657  
             case NUMBERING_UPPER_ALPHA:
 658  0
                 style = "upper-alpha";
 659  0
                 break;
 660  
             case NUMBERING_LOWER_ALPHA:
 661  0
                 style = "lower-alpha";
 662  0
                 break;
 663  
             case NUMBERING_UPPER_ROMAN:
 664  0
                 style = "upper-roman";
 665  0
                 break;
 666  
             case NUMBERING_LOWER_ROMAN:
 667  0
                 style = "lower-roman";
 668  0
                 break;
 669  
             case NUMBERING_DECIMAL:
 670  
             default:
 671  4
                 style = "decimal";
 672  
         }
 673  
 
 674  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 675  
                 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
 676  
 
 677  4
         if ( atts == null )
 678  
         {
 679  2
             atts = new SinkEventAttributeSet( 1 );
 680  
         }
 681  
 
 682  4
         atts.addAttribute( Attribute.STYLE, "list-style-type: " + style );
 683  
 
 684  4
         writeStartTag( HtmlMarkup.OL, atts );
 685  4
     }
 686  
 
 687  
     /**
 688  
      * {@inheritDoc}
 689  
      * @see javax.swing.text.html.HTML.Tag#OL
 690  
      */
 691  
     public void numberedList_()
 692  
     {
 693  4
         writeEndTag( HtmlMarkup.OL );
 694  4
     }
 695  
 
 696  
     /**
 697  
      * {@inheritDoc}
 698  
      * @see javax.swing.text.html.HTML.Tag#LI
 699  
      */
 700  
     public void numberedListItem()
 701  
     {
 702  2
         numberedListItem( null );
 703  2
     }
 704  
 
 705  
     /**
 706  
      * {@inheritDoc}
 707  
      * @see javax.swing.text.html.HTML.Tag#LI
 708  
      */
 709  
     public void numberedListItem( SinkEventAttributes attributes )
 710  
     {
 711  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 712  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 713  
 
 714  4
         writeStartTag( HtmlMarkup.LI, atts );
 715  4
     }
 716  
 
 717  
     /**
 718  
      * {@inheritDoc}
 719  
      * @see javax.swing.text.html.HTML.Tag#LI
 720  
      */
 721  
     public void numberedListItem_()
 722  
     {
 723  4
         writeEndTag( HtmlMarkup.LI );
 724  4
     }
 725  
 
 726  
     /**
 727  
      * {@inheritDoc}
 728  
      * @see javax.swing.text.html.HTML.Tag#DL
 729  
      */
 730  
     public void definitionList()
 731  
     {
 732  2
         definitionList( null );
 733  2
     }
 734  
 
 735  
     /**
 736  
      * {@inheritDoc}
 737  
      * @see javax.swing.text.html.HTML.Tag#DL
 738  
      */
 739  
     public void definitionList( SinkEventAttributes attributes )
 740  
     {
 741  4
         if ( paragraphFlag )
 742  
         {
 743  
             // The content of element type "p" must match
 744  
             // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
 745  
             // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
 746  0
             paragraph_();
 747  
         }
 748  
 
 749  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 750  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 751  
 
 752  4
         writeStartTag( HtmlMarkup.DL, atts );
 753  4
     }
 754  
 
 755  
     /**
 756  
      * {@inheritDoc}
 757  
      * @see javax.swing.text.html.HTML.Tag#DL
 758  
      */
 759  
     public void definitionList_()
 760  
     {
 761  4
         writeEndTag( HtmlMarkup.DL );
 762  4
     }
 763  
 
 764  
     /**
 765  
      * {@inheritDoc}
 766  
      * @see javax.swing.text.html.HTML.Tag#DT
 767  
      */
 768  
     public void definedTerm( SinkEventAttributes attributes )
 769  
     {
 770  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 771  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 772  
 
 773  4
         writeStartTag( HtmlMarkup.DT, atts );
 774  4
     }
 775  
 
 776  
     /**
 777  
      * {@inheritDoc}
 778  
      * @see javax.swing.text.html.HTML.Tag#DT
 779  
      */
 780  
     public void definedTerm()
 781  
     {
 782  2
         definedTerm( null );
 783  2
     }
 784  
 
 785  
     /**
 786  
      * {@inheritDoc}
 787  
      * @see javax.swing.text.html.HTML.Tag#DT
 788  
      */
 789  
     public void definedTerm_()
 790  
     {
 791  4
         writeEndTag( HtmlMarkup.DT );
 792  4
     }
 793  
 
 794  
     /**
 795  
      * {@inheritDoc}
 796  
      * @see javax.swing.text.html.HTML.Tag#DD
 797  
      */
 798  
     public void definition()
 799  
     {
 800  2
         definition( null );
 801  2
     }
 802  
 
 803  
     /**
 804  
      * {@inheritDoc}
 805  
      * @see javax.swing.text.html.HTML.Tag#DD
 806  
      */
 807  
     public void definition( SinkEventAttributes attributes )
 808  
     {
 809  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 810  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 811  
 
 812  4
         writeStartTag( HtmlMarkup.DD, atts );
 813  4
     }
 814  
 
 815  
     /**
 816  
      * {@inheritDoc}
 817  
      * @see javax.swing.text.html.HTML.Tag#DD
 818  
      */
 819  
     public void definition_()
 820  
     {
 821  4
         writeEndTag( HtmlMarkup.DD );
 822  4
     }
 823  
 
 824  
     /**
 825  
      * {@inheritDoc}
 826  
      * @see javax.swing.text.html.HTML.Tag#IMG
 827  
      * @deprecated Use {@link #figure(SinkEventAttributes)}, this method is only kept for
 828  
      * backward compatibility. Note that the behavior is different though, as this method
 829  
      * writes an img tag, while correctly the img tag should be written by  figureGraphics().
 830  
      */
 831  
     public void figure()
 832  
     {
 833  0
         write( String.valueOf( LESS_THAN ) + HtmlMarkup.IMG );
 834  0
         legacyFigure = true;
 835  0
     }
 836  
 
 837  
     /**
 838  
      * {@inheritDoc}
 839  
      * @see javax.swing.text.html.HTML.Tag#IMG
 840  
      */
 841  
     public void figure( SinkEventAttributes attributes )
 842  
     {
 843  2
         inFigure = true;
 844  
 
 845  2
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 846  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 847  
 
 848  2
         if ( atts == null )
 849  
         {
 850  0
             atts = new SinkEventAttributeSet( 1 );
 851  
         }
 852  
 
 853  2
         if ( !atts.isDefined( SinkEventAttributes.CLASS ) )
 854  
         {
 855  2
             atts.addAttribute( SinkEventAttributes.CLASS, "figure" );
 856  
         }
 857  
 
 858  2
         writeStartTag( HtmlMarkup.DIV, atts );
 859  2
     }
 860  
 
 861  
     /** {@inheritDoc} */
 862  
     public void figure_()
 863  
     {
 864  2
         if ( legacyFigure )
 865  
         {
 866  0
             if ( !figureCaptionFlag )
 867  
             {
 868  
                 // Attribute "alt" is required and must be specified for element type "img".
 869  0
                 write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE + QUOTE );
 870  
             }
 871  0
             write( String.valueOf( SPACE ) + SLASH + GREATER_THAN );
 872  0
             legacyFigure = false;
 873  
         }
 874  
         else
 875  
         {
 876  2
             writeEndTag( HtmlMarkup.DIV );
 877  2
             inFigure = false;
 878  
         }
 879  
 
 880  2
         figureCaptionFlag = false;
 881  2
     }
 882  
 
 883  
     /**
 884  
      * {@inheritDoc}
 885  
      * @deprecated Use {@link #figureGraphics(String,SinkEventAttributes)},
 886  
      * this method is only kept for backward compatibility. Note that the behavior is
 887  
      * different though, as this method does not write the img tag, only the src attribute.
 888  
      */
 889  
     public void figureGraphics( String name )
 890  
     {
 891  0
         write( String.valueOf( SPACE ) + Attribute.SRC + EQUAL + QUOTE + escapeHTML( name ) + QUOTE );
 892  0
     }
 893  
 
 894  
     /** {@inheritDoc} */
 895  
     public void figureGraphics( String src, SinkEventAttributes attributes )
 896  
     {
 897  6
         if ( inFigure )
 898  
         {
 899  2
             MutableAttributeSet atts = new SinkEventAttributeSet( 1 );
 900  2
             atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
 901  
 
 902  2
             writeStartTag( HtmlMarkup.P, atts );
 903  
         }
 904  
 
 905  6
         MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_IMG_ATTRIBUTES );
 906  6
         if ( filtered != null )
 907  
         {
 908  6
             filtered.removeAttribute( Attribute.SRC.toString() );
 909  
         }
 910  
 
 911  6
         int count = ( attributes == null ? 1 : attributes.getAttributeCount() + 1 );
 912  
 
 913  6
         MutableAttributeSet atts = new SinkEventAttributeSet( count );
 914  
 
 915  6
         atts.addAttribute( Attribute.SRC, escapeHTML( src ) );
 916  6
         atts.addAttributes( filtered );
 917  
 
 918  6
         if ( atts.getAttribute( Attribute.ALT.toString() ) == null )
 919  
         {
 920  6
             atts.addAttribute( Attribute.ALT.toString(), "" );
 921  
         }
 922  
 
 923  6
         writeStartTag( HtmlMarkup.IMG, atts, true );
 924  
 
 925  6
         if ( inFigure )
 926  
         {
 927  2
             writeEndTag( HtmlMarkup.P );
 928  
         }
 929  6
     }
 930  
 
 931  
     /**
 932  
      * {@inheritDoc}
 933  
      * @deprecated Use {@link #figureCaption(SinkEventAttributes)},
 934  
      * this method is only kept for backward compatibility. Note that the behavior is
 935  
      * different though, as this method only writes an alt attribute.
 936  
      */
 937  
     public void figureCaption()
 938  
     {
 939  0
         figureCaptionFlag = true;
 940  0
         write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
 941  0
         legacyFigureCaption = true;
 942  0
     }
 943  
 
 944  
     /** {@inheritDoc} */
 945  
     public void figureCaption( SinkEventAttributes attributes )
 946  
     {
 947  2
         if ( legacyFigureCaption )
 948  
         {
 949  0
             write( String.valueOf( SPACE ) + Attribute.ALT + EQUAL + QUOTE );
 950  0
             legacyFigureCaption = false;
 951  0
             figureCaptionFlag = true;
 952  
         }
 953  
         else
 954  
         {
 955  2
             SinkEventAttributeSet atts = new SinkEventAttributeSet( 1 );
 956  2
             atts.addAttribute( SinkEventAttributes.ALIGN, "center" );
 957  2
             atts.addAttributes( SinkUtils.filterAttributes(
 958  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  ) );
 959  
 
 960  2
             paragraph( atts );
 961  2
             italic();
 962  
         }
 963  2
     }
 964  
 
 965  
     /** {@inheritDoc} */
 966  
     public void figureCaption_()
 967  
     {
 968  2
         if ( legacyFigureCaption )
 969  
         {
 970  0
             write( String.valueOf( QUOTE ) );
 971  
         }
 972  
         else
 973  
         {
 974  2
             italic_();
 975  2
             paragraph_();
 976  
         }
 977  2
     }
 978  
 
 979  
     /**
 980  
      * {@inheritDoc}
 981  
      * @see javax.swing.text.html.HTML.Tag#P
 982  
      */
 983  
     public void paragraph()
 984  
     {
 985  4
         paragraph( null );
 986  4
     }
 987  
 
 988  
     /**
 989  
      * {@inheritDoc}
 990  
      * @see javax.swing.text.html.HTML.Tag#P
 991  
      */
 992  
     public void paragraph( SinkEventAttributes attributes )
 993  
     {
 994  8
         paragraphFlag = true;
 995  
 
 996  8
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 997  
                 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
 998  
 
 999  8
         writeStartTag( HtmlMarkup.P, atts );
 1000  8
     }
 1001  
 
 1002  
     /**
 1003  
      * {@inheritDoc}
 1004  
      * @see javax.swing.text.html.HTML.Tag#P
 1005  
      */
 1006  
     public void paragraph_()
 1007  
     {
 1008  8
         if ( paragraphFlag )
 1009  
         {
 1010  8
             writeEndTag( HtmlMarkup.P );
 1011  8
             paragraphFlag = false;
 1012  
         }
 1013  8
     }
 1014  
 
 1015  
     /**
 1016  
      * The default class style for boxed is <code>source</code>.
 1017  
      *
 1018  
      * {@inheritDoc}
 1019  
      * @see javax.swing.text.html.HTML.Tag#DIV
 1020  
      * @see javax.swing.text.html.HTML.Tag#PRE
 1021  
      */
 1022  
     public void verbatim( boolean boxed )
 1023  
     {
 1024  2
         if ( boxed )
 1025  
         {
 1026  2
             verbatim( SinkEventAttributeSet.BOXED );
 1027  
         }
 1028  
         else
 1029  
         {
 1030  0
             verbatim( null );
 1031  
         }
 1032  2
     }
 1033  
 
 1034  
     /**
 1035  
      * The default class style for boxed is <code>source</code>.
 1036  
      *
 1037  
      * {@inheritDoc}
 1038  
      * @see javax.swing.text.html.HTML.Tag#DIV
 1039  
      * @see javax.swing.text.html.HTML.Tag#PRE
 1040  
      */
 1041  
     public void verbatim( SinkEventAttributes attributes )
 1042  
     {
 1043  4
         if ( paragraphFlag )
 1044  
         {
 1045  
             // The content of element type "p" must match
 1046  
             // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
 1047  
             // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
 1048  0
             paragraph_();
 1049  
         }
 1050  
 
 1051  4
         verbatimFlag = true;
 1052  
 
 1053  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 1054  
                 attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES  );
 1055  
 
 1056  4
         if ( atts == null )
 1057  
         {
 1058  0
             atts = new SinkEventAttributeSet();
 1059  
         }
 1060  
 
 1061  4
         boolean boxed = false;
 1062  
 
 1063  4
         if ( atts.isDefined( SinkEventAttributes.DECORATION ) )
 1064  
         {
 1065  2
             boxed =
 1066  
                 "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() );
 1067  
         }
 1068  
 
 1069  4
         if ( boxed )
 1070  
         {
 1071  2
             atts.addAttribute( Attribute.CLASS, "source" );
 1072  
         }
 1073  
 
 1074  4
         atts.removeAttribute( SinkEventAttributes.DECORATION );
 1075  
 
 1076  4
         String width = (String) atts.getAttribute( Attribute.WIDTH.toString() );
 1077  4
         atts.removeAttribute( Attribute.WIDTH.toString() );
 1078  
 
 1079  4
         writeStartTag( HtmlMarkup.DIV, atts );
 1080  
 
 1081  4
         if ( width != null )
 1082  
         {
 1083  0
             atts.addAttribute( Attribute.WIDTH.toString(), width );
 1084  
         }
 1085  
 
 1086  4
         atts.removeAttribute( Attribute.ALIGN.toString() );
 1087  4
         atts.removeAttribute( Attribute.CLASS.toString() );
 1088  
 
 1089  4
         writeStartTag( HtmlMarkup.PRE, atts );
 1090  4
     }
 1091  
 
 1092  
     /**
 1093  
      * {@inheritDoc}
 1094  
      * @see javax.swing.text.html.HTML.Tag#DIV
 1095  
      * @see javax.swing.text.html.HTML.Tag#PRE
 1096  
      */
 1097  
     public void verbatim_()
 1098  
     {
 1099  4
         writeEndTag( HtmlMarkup.PRE );
 1100  4
         writeEndTag( HtmlMarkup.DIV );
 1101  
 
 1102  4
         verbatimFlag = false;
 1103  
 
 1104  4
     }
 1105  
 
 1106  
     /**
 1107  
      * {@inheritDoc}
 1108  
      * @see javax.swing.text.html.HTML.Tag#HR
 1109  
      */
 1110  
     public void horizontalRule()
 1111  
     {
 1112  2
         horizontalRule( null );
 1113  2
     }
 1114  
 
 1115  
     /**
 1116  
      * {@inheritDoc}
 1117  
      * @see javax.swing.text.html.HTML.Tag#HR
 1118  
      */
 1119  
     public void horizontalRule( SinkEventAttributes attributes )
 1120  
     {
 1121  4
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 1122  
                 attributes, SinkUtils.SINK_HR_ATTRIBUTES  );
 1123  
 
 1124  4
         writeSimpleTag( HtmlMarkup.HR, atts );
 1125  4
     }
 1126  
 
 1127  
     /** {@inheritDoc} */
 1128  
     public void table()
 1129  
     {
 1130  
         // start table with tableRows
 1131  4
         table( null );
 1132  4
     }
 1133  
 
 1134  
     /** {@inheritDoc} */
 1135  
     public void table( SinkEventAttributes attributes )
 1136  
     {
 1137  10
         this.tableContentWriterStack.addLast( new StringWriter() );
 1138  10
         this.tableRows = false;
 1139  
 
 1140  10
         if ( paragraphFlag )
 1141  
         {
 1142  
             // The content of element type "p" must match
 1143  
             // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong|
 1144  
             // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)".
 1145  0
             paragraph_();
 1146  
         }
 1147  
 
 1148  
         // start table with tableRows
 1149  10
         if ( attributes == null )
 1150  
         {
 1151  4
             this.tableAttributes = new SinkEventAttributeSet( 0 );
 1152  
         }
 1153  
         else
 1154  
         {
 1155  6
             this.tableAttributes = SinkUtils.filterAttributes(
 1156  
                 attributes, SinkUtils.SINK_TABLE_ATTRIBUTES  );
 1157  
         }
 1158  10
     }
 1159  
 
 1160  
     /**
 1161  
      * {@inheritDoc}
 1162  
      * @see javax.swing.text.html.HTML.Tag#TABLE
 1163  
      */
 1164  
     public void table_()
 1165  
     {
 1166  10
         this.tableRows = false;
 1167  
 
 1168  10
         writeEndTag( HtmlMarkup.TABLE );
 1169  
 
 1170  10
         if ( !this.cellCountStack.isEmpty() )
 1171  
         {
 1172  8
             this.cellCountStack.removeLast().toString();
 1173  
         }
 1174  
 
 1175  10
         if ( this.tableContentWriterStack.isEmpty() )
 1176  
         {
 1177  0
             if ( getLog().isWarnEnabled() )
 1178  
             {
 1179  0
                 getLog().warn( "No table content." );
 1180  
             }
 1181  0
             return;
 1182  
         }
 1183  
 
 1184  10
         String tableContent = this.tableContentWriterStack.removeLast().toString();
 1185  
 
 1186  10
         String tableCaption = null;
 1187  10
         if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null )
 1188  
         {
 1189  8
             tableCaption = this.tableCaptionStack.removeLast().toString();
 1190  
         }
 1191  
 
 1192  10
         if ( tableCaption != null )
 1193  
         {
 1194  
             // DOXIA-177
 1195  8
             StringBuffer sb = new StringBuffer();
 1196  8
             sb.append( tableContent.substring( 0, tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) );
 1197  8
             sb.append( tableCaption );
 1198  8
             sb.append( tableContent.substring( tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) );
 1199  
 
 1200  8
             write( sb.toString() );
 1201  8
         }
 1202  
         else
 1203  
         {
 1204  2
             write( tableContent );
 1205  
         }
 1206  10
     }
 1207  
 
 1208  
     /**
 1209  
      * The default class style is <code>bodyTable</code>.
 1210  
      * The default align is <code>center</code>.
 1211  
      *
 1212  
      * {@inheritDoc}
 1213  
      * @see javax.swing.text.html.HTML.Tag#TABLE
 1214  
      */
 1215  
     public void tableRows( int[] justification, boolean grid )
 1216  
     {
 1217  10
         this.tableRows = true;
 1218  
 
 1219  10
         setCellJustif( justification );
 1220  
 
 1221  10
         if ( this.tableAttributes == null )
 1222  
         {
 1223  2
             this.tableAttributes = new SinkEventAttributeSet( 0 );
 1224  
         }
 1225  
 
 1226  10
         MutableAttributeSet att = new SinkEventAttributeSet();
 1227  10
         if ( !this.tableAttributes.isDefined( Attribute.BORDER.toString() ) )
 1228  
         {
 1229  10
             att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) );
 1230  
         }
 1231  
 
 1232  10
         if ( !this.tableAttributes.isDefined( Attribute.CLASS.toString() ) )
 1233  
         {
 1234  10
             att.addAttribute( Attribute.CLASS, "bodyTable" );
 1235  
         }
 1236  
 
 1237  10
         att.addAttributes( this.tableAttributes );
 1238  10
         this.tableAttributes.removeAttributes( this.tableAttributes );
 1239  
 
 1240  10
         writeStartTag( HtmlMarkup.TABLE, att );
 1241  
 
 1242  10
         this.cellCountStack.addLast( new Integer( 0 ) );
 1243  10
     }
 1244  
 
 1245  
     /** {@inheritDoc} */
 1246  
     public void tableRows_()
 1247  
     {
 1248  10
         this.tableRows = false;
 1249  10
         if ( !this.cellJustifStack.isEmpty() )
 1250  
         {
 1251  10
             this.cellJustifStack.removeLast();
 1252  
         }
 1253  10
         if ( !this.isCellJustifStack.isEmpty() )
 1254  
         {
 1255  10
             this.isCellJustifStack.removeLast();
 1256  
         }
 1257  
 
 1258  10
         this.evenTableRow = true;
 1259  10
     }
 1260  
 
 1261  
     /**
 1262  
      * The default class style is <code>a</code> or <code>b</code> depending the row id.
 1263  
      *
 1264  
      * {@inheritDoc}
 1265  
      * @see javax.swing.text.html.HTML.Tag#TR
 1266  
      */
 1267  
     public void tableRow()
 1268  
     {
 1269  
         // To be backward compatible
 1270  12
         if ( !this.tableRows )
 1271  
         {
 1272  0
             tableRows( null, false );
 1273  
         }
 1274  12
         tableRow( null );
 1275  12
     }
 1276  
 
 1277  
     /**
 1278  
      * The default class style is <code>a</code> or <code>b</code> depending the row id.
 1279  
      *
 1280  
      * {@inheritDoc}
 1281  
      * @see javax.swing.text.html.HTML.Tag#TR
 1282  
      */
 1283  
     public void tableRow( SinkEventAttributes attributes )
 1284  
     {
 1285  14
         MutableAttributeSet att = new SinkEventAttributeSet();
 1286  
 
 1287  14
         if ( evenTableRow )
 1288  
         {
 1289  8
             att.addAttribute( Attribute.CLASS, "a" );
 1290  
         }
 1291  
         else
 1292  
         {
 1293  6
             att.addAttribute( Attribute.CLASS, "b" );
 1294  
         }
 1295  
 
 1296  14
         att.addAttributes( SinkUtils.filterAttributes(
 1297  
                 attributes, SinkUtils.SINK_TR_ATTRIBUTES  ) );
 1298  
 
 1299  14
         writeStartTag( HtmlMarkup.TR, att );
 1300  
 
 1301  14
         evenTableRow = !evenTableRow;
 1302  
 
 1303  14
         if ( !this.cellCountStack.isEmpty() )
 1304  
         {
 1305  12
             this.cellCountStack.removeLast();
 1306  12
             this.cellCountStack.addLast( new Integer( 0 ) );
 1307  
         }
 1308  14
     }
 1309  
 
 1310  
     /**
 1311  
      * {@inheritDoc}
 1312  
      * @see javax.swing.text.html.HTML.Tag#TR
 1313  
      */
 1314  
     public void tableRow_()
 1315  
     {
 1316  14
         writeEndTag( HtmlMarkup.TR );
 1317  14
     }
 1318  
 
 1319  
     /** {@inheritDoc} */
 1320  
     public void tableCell()
 1321  
     {
 1322  24
         tableCell( (SinkEventAttributeSet) null );
 1323  24
     }
 1324  
 
 1325  
     /** {@inheritDoc} */
 1326  
     public void tableHeaderCell()
 1327  
     {
 1328  0
         tableHeaderCell( (SinkEventAttributeSet) null );
 1329  0
     }
 1330  
 
 1331  
     /** {@inheritDoc} */
 1332  
     public void tableCell( String width )
 1333  
     {
 1334  0
         MutableAttributeSet att = new SinkEventAttributeSet();
 1335  0
         att.addAttribute( Attribute.WIDTH, width );
 1336  
 
 1337  0
         tableCell( false, att );
 1338  0
     }
 1339  
 
 1340  
     /** {@inheritDoc} */
 1341  
     public void tableHeaderCell( String width )
 1342  
     {
 1343  0
         MutableAttributeSet att = new SinkEventAttributeSet();
 1344  0
         att.addAttribute( Attribute.WIDTH, width );
 1345  
 
 1346  0
         tableCell( true, att );
 1347  0
     }
 1348  
 
 1349  
     /** {@inheritDoc} */
 1350  
     public void tableCell( SinkEventAttributes attributes )
 1351  
     {
 1352  26
         tableCell( false, attributes );
 1353  26
     }
 1354  
 
 1355  
     /** {@inheritDoc} */
 1356  
     public void tableHeaderCell( SinkEventAttributes attributes )
 1357  
     {
 1358  2
         tableCell( true, attributes );
 1359  2
     }
 1360  
 
 1361  
     /**
 1362  
      * @param headerRow true if it is an header row
 1363  
      * @param attributes the cell attributes
 1364  
      * @see javax.swing.text.html.HTML.Tag#TH
 1365  
      * @see javax.swing.text.html.HTML.Tag#TD
 1366  
      */
 1367  
     private void tableCell( boolean headerRow, MutableAttributeSet attributes )
 1368  
     {
 1369  28
         Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
 1370  
 
 1371  28
         MutableAttributeSet att = new SinkEventAttributeSet();
 1372  
 
 1373  28
         if ( attributes == null )
 1374  
         {
 1375  24
             attributes = new SinkEventAttributeSet( 0 );
 1376  
         }
 1377  
 
 1378  28
         att.addAttributes( SinkUtils.filterAttributes(
 1379  
                 attributes, SinkUtils.SINK_TD_ATTRIBUTES  ) );
 1380  
 
 1381  28
         writeStartTag( t, att );
 1382  28
     }
 1383  
 
 1384  
     /** {@inheritDoc} */
 1385  
     public void tableCell_()
 1386  
     {
 1387  26
         tableCell_( false );
 1388  26
     }
 1389  
 
 1390  
     /** {@inheritDoc} */
 1391  
     public void tableHeaderCell_()
 1392  
     {
 1393  2
         tableCell_( true );
 1394  2
     }
 1395  
 
 1396  
     /**
 1397  
      * Ends a table cell.
 1398  
      *
 1399  
      * @param headerRow true if it is an header row
 1400  
      * @see javax.swing.text.html.HTML.Tag#TH
 1401  
      * @see javax.swing.text.html.HTML.Tag#TD
 1402  
      */
 1403  
     private void tableCell_( boolean headerRow )
 1404  
     {
 1405  28
         Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD );
 1406  
 
 1407  28
         writeEndTag( t );
 1408  
 
 1409  28
         if ( !this.isCellJustifStack.isEmpty() && this.isCellJustifStack.getLast().equals( Boolean.TRUE )
 1410  
             && !this.cellCountStack.isEmpty() )
 1411  
         {
 1412  24
             int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() );
 1413  24
             this.cellCountStack.addLast( new Integer( ++cellCount ) );
 1414  
         }
 1415  28
     }
 1416  
 
 1417  
     /**
 1418  
      * {@inheritDoc}
 1419  
      * @see javax.swing.text.html.HTML.Tag#CAPTION
 1420  
      */
 1421  
     public void tableCaption()
 1422  
     {
 1423  6
         tableCaption( null );
 1424  6
     }
 1425  
 
 1426  
     /**
 1427  
      * {@inheritDoc}
 1428  
      * @see javax.swing.text.html.HTML.Tag#CAPTION
 1429  
      */
 1430  
     public void tableCaption( SinkEventAttributes attributes )
 1431  
     {
 1432  8
         StringWriter sw = new StringWriter();
 1433  8
         this.tableCaptionWriterStack.addLast( sw );
 1434  8
         this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) );
 1435  
 
 1436  
         // TODO: tableCaption should be written before tableRows (DOXIA-177)
 1437  8
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 1438  
                 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES  );
 1439  
 
 1440  8
         writeStartTag( HtmlMarkup.CAPTION, atts );
 1441  8
     }
 1442  
 
 1443  
     /**
 1444  
      * {@inheritDoc}
 1445  
      * @see javax.swing.text.html.HTML.Tag#CAPTION
 1446  
      */
 1447  
     public void tableCaption_()
 1448  
     {
 1449  8
         writeEndTag( HtmlMarkup.CAPTION );
 1450  
 
 1451  8
         if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
 1452  
         {
 1453  8
             this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() );
 1454  8
             this.tableCaptionXMLWriterStack.removeLast();
 1455  
         }
 1456  8
     }
 1457  
 
 1458  
     /**
 1459  
      * {@inheritDoc}
 1460  
      * @see javax.swing.text.html.HTML.Tag#A
 1461  
      */
 1462  
     public void anchor( String name )
 1463  
     {
 1464  0
         anchor( name, null );
 1465  0
     }
 1466  
 
 1467  
     /**
 1468  
      * {@inheritDoc}
 1469  
      * @see javax.swing.text.html.HTML.Tag#A
 1470  
      */
 1471  
     public void anchor( String name, SinkEventAttributes attributes )
 1472  
     {
 1473  2
         if ( name == null )
 1474  
         {
 1475  0
             throw new NullPointerException( "Anchor name cannot be null!" );
 1476  
         }
 1477  
 
 1478  2
         if ( headFlag )
 1479  
         {
 1480  0
             return;
 1481  
         }
 1482  
 
 1483  2
         MutableAttributeSet atts = SinkUtils.filterAttributes(
 1484  
                 attributes, SinkUtils.SINK_BASE_ATTRIBUTES  );
 1485  
 
 1486  2
         String id = name;
 1487  
 
 1488  2
         if ( !DoxiaUtils.isValidId( id ) )
 1489  
         {
 1490  0
             id = DoxiaUtils.encodeId( name, true );
 1491  
 
 1492  0
             String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'";
 1493  0
             logMessage( "modifiedLink", msg );
 1494  
         }
 1495  
 
 1496  2
         MutableAttributeSet att = new SinkEventAttributeSet();
 1497  2
         att.addAttribute( Attribute.NAME, id );
 1498  2
         att.addAttributes( atts );
 1499  
 
 1500  2
         writeStartTag( HtmlMarkup.A, att );
 1501  2
     }
 1502  
 
 1503  
     /**
 1504  
      * {@inheritDoc}
 1505  
      * @see javax.swing.text.html.HTML.Tag#A
 1506  
      */
 1507  
     public void anchor_()
 1508  
     {
 1509  2
         if ( !headFlag )
 1510  
         {
 1511  2
             writeEndTag( HtmlMarkup.A );
 1512  
         }
 1513  2
     }
 1514  
 
 1515  
     /** {@inheritDoc} */
 1516  
     public void link( String name )
 1517  
     {
 1518  10
         link( name, null );
 1519  10
     }
 1520  
 
 1521  
     /** {@inheritDoc} */
 1522  
     public void link( String name, SinkEventAttributes attributes )
 1523  
     {
 1524  12
         if ( attributes == null )
 1525  
         {
 1526  10
             link( name, null, null );
 1527  
         }
 1528  
         else
 1529  
         {
 1530  2
             String target = (String) attributes.getAttribute( Attribute.TARGET.toString() );
 1531  2
             MutableAttributeSet atts = SinkUtils.filterAttributes(
 1532  
                     attributes, SinkUtils.SINK_LINK_ATTRIBUTES  );
 1533  
 
 1534  2
             link( name, target, atts );
 1535  
         }
 1536  12
     }
 1537  
 
 1538  
     /**
 1539  
      * Adds a link with an optional target.
 1540  
      * The default style class for external link is <code>externalLink</code>.
 1541  
      *
 1542  
      * @param href the link href.
 1543  
      * @param target the link target, may be null.
 1544  
      * @param attributes an AttributeSet, may be null.
 1545  
      *      This is supposed to be filtered already.
 1546  
      * @see javax.swing.text.html.HTML.Tag#A
 1547  
      */
 1548  
     private void link( String href, String target, MutableAttributeSet attributes )
 1549  
     {
 1550  12
         if ( href == null )
 1551  
         {
 1552  0
             throw new NullPointerException( "Link name cannot be null!" );
 1553  
         }
 1554  
 
 1555  12
         if ( headFlag )
 1556  
         {
 1557  0
             return;
 1558  
         }
 1559  
 
 1560  12
         MutableAttributeSet att = new SinkEventAttributeSet();
 1561  
 
 1562  12
         if ( DoxiaUtils.isExternalLink( href  ) )
 1563  
         {
 1564  0
             att.addAttribute( Attribute.CLASS, "externalLink" );
 1565  
         }
 1566  
 
 1567  12
         att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( href  ) );
 1568  
 
 1569  12
         if ( target != null )
 1570  
         {
 1571  0
             att.addAttribute( Attribute.TARGET, target );
 1572  
         }
 1573  
 
 1574  12
         if ( attributes != null )
 1575  
         {
 1576  2
             attributes.removeAttribute( Attribute.HREF.toString() );
 1577  2
             attributes.removeAttribute( Attribute.TARGET.toString() );
 1578  2
             att.addAttributes( attributes );
 1579  
         }
 1580  
 
 1581  12
         writeStartTag( HtmlMarkup.A, att );
 1582  12
     }
 1583  
 
 1584  
     /**
 1585  
      * {@inheritDoc}
 1586  
      * @see javax.swing.text.html.HTML.Tag#A
 1587  
      */
 1588  
     public void link_()
 1589  
     {
 1590  12
         if ( !headFlag )
 1591  
         {
 1592  12
             writeEndTag( HtmlMarkup.A );
 1593  
         }
 1594  12
     }
 1595  
 
 1596  
     /**
 1597  
      * {@inheritDoc}
 1598  
      * @see javax.swing.text.html.HTML.Tag#I
 1599  
      */
 1600  
     public void italic()
 1601  
     {
 1602  6
         if ( !headFlag )
 1603  
         {
 1604  6
             writeStartTag( HtmlMarkup.I );
 1605  
         }
 1606  6
     }
 1607  
 
 1608  
     /**
 1609  
      * {@inheritDoc}
 1610  
      * @see javax.swing.text.html.HTML.Tag#I
 1611  
      */
 1612  
     public void italic_()
 1613  
     {
 1614  6
         if ( !headFlag )
 1615  
         {
 1616  6
             writeEndTag( HtmlMarkup.I );
 1617  
         }
 1618  6
     }
 1619  
 
 1620  
     /**
 1621  
      * {@inheritDoc}
 1622  
      * @see javax.swing.text.html.HTML.Tag#B
 1623  
      */
 1624  
     public void bold()
 1625  
     {
 1626  2
         if ( !headFlag )
 1627  
         {
 1628  2
             writeStartTag( HtmlMarkup.B );
 1629  
         }
 1630  2
     }
 1631  
 
 1632  
     /**
 1633  
      * {@inheritDoc}
 1634  
      * @see javax.swing.text.html.HTML.Tag#B
 1635  
      */
 1636  
     public void bold_()
 1637  
     {
 1638  2
         if ( !headFlag )
 1639  
         {
 1640  2
             writeEndTag( HtmlMarkup.B );
 1641  
         }
 1642  2
     }
 1643  
 
 1644  
     /**
 1645  
      * {@inheritDoc}
 1646  
      * @see javax.swing.text.html.HTML.Tag#TT
 1647  
      */
 1648  
     public void monospaced()
 1649  
     {
 1650  2
         if ( !headFlag )
 1651  
         {
 1652  2
             writeStartTag( HtmlMarkup.TT );
 1653  
         }
 1654  2
     }
 1655  
 
 1656  
     /**
 1657  
      * {@inheritDoc}
 1658  
      * @see javax.swing.text.html.HTML.Tag#TT
 1659  
      */
 1660  
     public void monospaced_()
 1661  
     {
 1662  2
         if ( !headFlag )
 1663  
         {
 1664  2
             writeEndTag( HtmlMarkup.TT );
 1665  
         }
 1666  2
     }
 1667  
 
 1668  
     /**
 1669  
      * {@inheritDoc}
 1670  
      * @see javax.swing.text.html.HTML.Tag#BR
 1671  
      */
 1672  
     public void lineBreak()
 1673  
     {
 1674  0
         lineBreak( null );
 1675  0
     }
 1676  
 
 1677  
     /**
 1678  
      * {@inheritDoc}
 1679  
      * @see javax.swing.text.html.HTML.Tag#BR
 1680  
      */
 1681  
     public void lineBreak( SinkEventAttributes attributes )
 1682  
     {
 1683  2
         if ( headFlag || isVerbatimFlag() )
 1684  
         {
 1685  0
             getTextBuffer().append( EOL );
 1686  
         }
 1687  
         else
 1688  
         {
 1689  2
             MutableAttributeSet atts = SinkUtils.filterAttributes(
 1690  
                 attributes, SinkUtils.SINK_BR_ATTRIBUTES  );
 1691  
 
 1692  2
             writeSimpleTag( HtmlMarkup.BR, atts );
 1693  
         }
 1694  2
     }
 1695  
 
 1696  
     /** {@inheritDoc} */
 1697  
     public void pageBreak()
 1698  
     {
 1699  2
         comment( "PB" );
 1700  2
     }
 1701  
 
 1702  
     /** {@inheritDoc} */
 1703  
     public void nonBreakingSpace()
 1704  
     {
 1705  2
         if ( headFlag )
 1706  
         {
 1707  0
             getTextBuffer().append( ' ' );
 1708  
         }
 1709  
         else
 1710  
         {
 1711  2
             write( "&#160;" );
 1712  
         }
 1713  2
     }
 1714  
 
 1715  
     /** {@inheritDoc} */
 1716  
     public void text( String text )
 1717  
     {
 1718  50
         if ( headFlag )
 1719  
         {
 1720  0
             getTextBuffer().append( text );
 1721  
         }
 1722  50
         else if ( verbatimFlag )
 1723  
         {
 1724  0
             verbatimContent( text );
 1725  
         }
 1726  
         else
 1727  
         {
 1728  50
             content( text );
 1729  
         }
 1730  50
     }
 1731  
 
 1732  
     /** {@inheritDoc} */
 1733  
     public void text( String text, SinkEventAttributes attributes )
 1734  
     {
 1735  2
         if ( attributes == null )
 1736  
         {
 1737  0
             text( text );
 1738  
         }
 1739  
         else
 1740  
         {
 1741  2
             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
 1742  
             {
 1743  0
                 writeStartTag( HtmlMarkup.U );
 1744  
             }
 1745  2
             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
 1746  
             {
 1747  0
                 writeStartTag( HtmlMarkup.S );
 1748  
             }
 1749  2
             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
 1750  
             {
 1751  0
                 writeStartTag( HtmlMarkup.SUB );
 1752  
             }
 1753  2
             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
 1754  
             {
 1755  0
                 writeStartTag( HtmlMarkup.SUP );
 1756  
             }
 1757  
 
 1758  2
             text( text );
 1759  
 
 1760  2
             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sup" ) )
 1761  
             {
 1762  0
                 writeEndTag( HtmlMarkup.SUP );
 1763  
             }
 1764  2
             if ( attributes.containsAttribute( SinkEventAttributes.VALIGN, "sub" ) )
 1765  
             {
 1766  0
                 writeEndTag( HtmlMarkup.SUB );
 1767  
             }
 1768  2
             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "line-through" ) )
 1769  
             {
 1770  0
                 writeEndTag( HtmlMarkup.S );
 1771  
             }
 1772  2
             if ( attributes.containsAttribute( SinkEventAttributes.DECORATION, "underline" ) )
 1773  
             {
 1774  0
                 writeEndTag( HtmlMarkup.U );
 1775  
             }
 1776  
         }
 1777  2
     }
 1778  
 
 1779  
     /** {@inheritDoc} */
 1780  
     public void rawText( String text )
 1781  
     {
 1782  2
         if ( headFlag )
 1783  
         {
 1784  0
             getTextBuffer().append( text );
 1785  
         }
 1786  
         else
 1787  
         {
 1788  2
             write( text );
 1789  
         }
 1790  2
     }
 1791  
 
 1792  
     /** {@inheritDoc} */
 1793  
     public void comment( String comment )
 1794  
     {
 1795  4
         if ( StringUtils.isNotEmpty( comment ) && comment.indexOf( "--" ) != -1 )
 1796  
         {
 1797  0
             String originalComment = comment;
 1798  
             // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments
 1799  0
             while ( comment.indexOf( "--" ) != -1 )
 1800  
             {
 1801  0
                 comment = StringUtils.replace( comment, "--", "- -" );
 1802  
             }
 1803  
 
 1804  0
             getLog()
 1805  
                     .warn( "[Xhtml Sink] Modified invalid comment: '" + originalComment + "' to '" + comment + "'" );
 1806  
         }
 1807  
 
 1808  4
         StringBuffer buf = new StringBuffer( comment.length() + 9 );
 1809  
 
 1810  4
         buf.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ).append( SPACE );
 1811  4
         buf.append( comment );
 1812  4
         buf.append( SPACE ).append( MINUS ).append( MINUS ).append( GREATER_THAN );
 1813  
 
 1814  4
         write( buf.toString() );
 1815  4
     }
 1816  
 
 1817  
     /**
 1818  
      * Add an unknown event.
 1819  
      * This can be used to generate html tags for which no corresponding sink event exists.
 1820  
      *
 1821  
      * <p>
 1822  
      * If {@link org.apache.maven.doxia.util.HtmlTools#getHtmlTag(String) HtmlTools.getHtmlTag( name )}
 1823  
      * does not return null, the corresponding tag will be written.
 1824  
      * </p>
 1825  
      *
 1826  
      * <p>For example, the div block</p>
 1827  
      *
 1828  
      * <pre>
 1829  
      *  &lt;div class="detail" style="display:inline"&gt;text&lt;/div&gt;
 1830  
      * </pre>
 1831  
      *
 1832  
      * <p>can be generated via the following event sequence:</p>
 1833  
      *
 1834  
      * <pre>
 1835  
      *  SinkEventAttributeSet atts = new SinkEventAttributeSet();
 1836  
      *  atts.addAttribute( SinkEventAttributes.CLASS, "detail" );
 1837  
      *  atts.addAttribute( SinkEventAttributes.STYLE, "display:inline" );
 1838  
      *  sink.unknown( "div", new Object[]{new Integer( HtmlMarkup.TAG_TYPE_START )}, atts );
 1839  
      *  sink.text( "text" );
 1840  
      *  sink.unknown( "div", new Object[]{new Integer( HtmlMarkup.TAG_TYPE_END )}, null );
 1841  
      * </pre>
 1842  
      *
 1843  
      * @param name the name of the event. If this is not a valid xhtml tag name
 1844  
      *      as defined in {@link org.apache.maven.doxia.markup.HtmlMarkup} then the event is ignored.
 1845  
      * @param requiredParams If this is null or the first argument is not an Integer then the event is ignored.
 1846  
      *      The first argument should indicate the type of the unknown event, its integer value should be one of
 1847  
      *      {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_START TAG_TYPE_START},
 1848  
      *      {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_END TAG_TYPE_END},
 1849  
      *      {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_SIMPLE TAG_TYPE_SIMPLE},
 1850  
      *      {@link org.apache.maven.doxia.markup.HtmlMarkup#ENTITY_TYPE ENTITY_TYPE}, or
 1851  
      *      {@link org.apache.maven.doxia.markup.HtmlMarkup#CDATA_TYPE CDATA_TYPE},
 1852  
      *      otherwise the event will be ignored.
 1853  
      * @param attributes a set of attributes for the event. May be null.
 1854  
      *      The attributes will always be written, no validity check is performed.
 1855  
      */
 1856  
     public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes )
 1857  
     {
 1858  6
         if ( requiredParams == null || !( requiredParams[0] instanceof Integer ) )
 1859  
         {
 1860  2
             String msg = "No type information for unknown event: '" + name + "', ignoring!";
 1861  2
             logMessage( "noTypeInfo", msg );
 1862  
 
 1863  2
             return;
 1864  
         }
 1865  
 
 1866  4
         int tagType = ( (Integer) requiredParams[0] ).intValue();
 1867  
 
 1868  4
         if ( tagType == ENTITY_TYPE )
 1869  
         {
 1870  0
             rawText( name );
 1871  
 
 1872  0
             return;
 1873  
         }
 1874  
 
 1875  4
         if ( tagType == CDATA_TYPE )
 1876  
         {
 1877  0
             rawText( EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL );
 1878  
 
 1879  0
             return;
 1880  
         }
 1881  
 
 1882  4
         Tag tag = HtmlTools.getHtmlTag( name );
 1883  
 
 1884  4
         if ( tag == null )
 1885  
         {
 1886  0
             String msg = "No HTML tag found for unknown event: '" + name + "', ignoring!";
 1887  0
             logMessage( "noHtmlTag", msg );
 1888  0
         }
 1889  
         else
 1890  
         {
 1891  4
             if ( tagType == TAG_TYPE_SIMPLE )
 1892  
             {
 1893  0
                 writeSimpleTag( tag, escapeAttributeValues( attributes ) );
 1894  
             }
 1895  4
             else if ( tagType == TAG_TYPE_START )
 1896  
             {
 1897  2
                 writeStartTag( tag, escapeAttributeValues( attributes ) );
 1898  
             }
 1899  2
             else if ( tagType == TAG_TYPE_END )
 1900  
             {
 1901  2
                 writeEndTag( tag );
 1902  
             }
 1903  
             else
 1904  
             {
 1905  0
                 String msg = "No type information for unknown event: '" + name + "', ignoring!";
 1906  0
                 logMessage( "noTypeInfo", msg );
 1907  
             }
 1908  
         }
 1909  4
     }
 1910  
 
 1911  
     private SinkEventAttributes escapeAttributeValues( SinkEventAttributes attributes )
 1912  
     {
 1913  2
         SinkEventAttributeSet set = new SinkEventAttributeSet( attributes.getAttributeCount() );
 1914  
 
 1915  2
         Enumeration<?> names = attributes.getAttributeNames();
 1916  
 
 1917  4
         while ( names.hasMoreElements() )
 1918  
         {
 1919  2
             Object name = names.nextElement();
 1920  
 
 1921  2
             set.addAttribute( name, escapeHTML( attributes.getAttribute( name ).toString() ) );
 1922  2
         }
 1923  
 
 1924  2
         return set;
 1925  
     }
 1926  
 
 1927  
     /** {@inheritDoc} */
 1928  
     public void flush()
 1929  
     {
 1930  0
         writer.flush();
 1931  0
     }
 1932  
 
 1933  
     /** {@inheritDoc} */
 1934  
     public void close()
 1935  
     {
 1936  76
         writer.close();
 1937  
 
 1938  76
         if ( getLog().isWarnEnabled() && this.warnMessages != null )
 1939  
         {
 1940  2
             for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() )
 1941  
             {
 1942  2
                 for ( String msg : entry.getValue() )
 1943  
                 {
 1944  2
                     getLog().warn( msg );
 1945  
                 }
 1946  
             }
 1947  
 
 1948  2
             this.warnMessages = null;
 1949  
         }
 1950  
 
 1951  76
         init();
 1952  76
     }
 1953  
 
 1954  
     // ----------------------------------------------------------------------
 1955  
     //
 1956  
     // ----------------------------------------------------------------------
 1957  
 
 1958  
     /**
 1959  
      * Write HTML escaped text to output.
 1960  
      *
 1961  
      * @param text The text to write.
 1962  
      */
 1963  
     protected void content( String text )
 1964  
     {
 1965  
         // small hack due to DOXIA-314
 1966  50
         text = escapeHTML( text );
 1967  50
         text = StringUtils.replace( text, "&amp;#", "&#" );
 1968  50
         write( text );
 1969  50
     }
 1970  
 
 1971  
     /**
 1972  
      * Write HTML escaped text to output.
 1973  
      *
 1974  
      * @param text The text to write.
 1975  
      */
 1976  
     protected void verbatimContent( String text )
 1977  
     {
 1978  0
         write( escapeHTML( text ) );
 1979  0
     }
 1980  
 
 1981  
     /**
 1982  
      * Forward to HtmlTools.escapeHTML( text ).
 1983  
      *
 1984  
      * @param text the String to escape, may be null
 1985  
      * @return the text escaped, "" if null String input
 1986  
      * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String)
 1987  
      */
 1988  
     protected static String escapeHTML( String text )
 1989  
     {
 1990  58
         return HtmlTools.escapeHTML( text, false );
 1991  
     }
 1992  
 
 1993  
     /**
 1994  
      * Forward to HtmlTools.encodeURL( text ).
 1995  
      *
 1996  
      * @param text the String to encode, may be null.
 1997  
      * @return the text encoded, null if null String input.
 1998  
      * @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String)
 1999  
      */
 2000  
     protected static String encodeURL( String text )
 2001  
     {
 2002  0
         return HtmlTools.encodeURL( text );
 2003  
     }
 2004  
 
 2005  
     /** {@inheritDoc} */
 2006  
     protected void write( String text )
 2007  
     {
 2008  412
         if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null )
 2009  
         {
 2010  8
             this.tableCaptionXMLWriterStack.getLast().writeText( unifyEOLs( text ) );
 2011  
         }
 2012  404
         else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null )
 2013  
         {
 2014  114
             this.tableContentWriterStack.getLast().write( unifyEOLs( text ) );
 2015  
         }
 2016  
         else
 2017  
         {
 2018  290
             writer.write( unifyEOLs( text ) );
 2019  
         }
 2020  412
     }
 2021  
 
 2022  
     /** {@inheritDoc} */
 2023  
     protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag )
 2024  
     {
 2025  186
         if ( this.tableCaptionXMLWriterStack.isEmpty() )
 2026  
         {
 2027  178
             super.writeStartTag ( t, att, isSimpleTag );
 2028  
         }
 2029  
         else
 2030  
         {
 2031  8
             String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString();
 2032  8
             this.tableCaptionXMLWriterStack.getLast().startElement( tag );
 2033  
 
 2034  8
             if ( att != null )
 2035  
             {
 2036  2
                 Enumeration<?> names = att.getAttributeNames();
 2037  4
                 while ( names.hasMoreElements() )
 2038  
                 {
 2039  2
                     Object key = names.nextElement();
 2040  2
                     Object value = att.getAttribute( key );
 2041  
 
 2042  2
                     this.tableCaptionXMLWriterStack.getLast().addAttribute( key.toString(), value.toString() );
 2043  2
                 }
 2044  
             }
 2045  
 
 2046  8
             if ( isSimpleTag )
 2047  
             {
 2048  0
                 this.tableCaptionXMLWriterStack.getLast().endElement();
 2049  
             }
 2050  
         }
 2051  186
     }
 2052  
 
 2053  
     /** {@inheritDoc} */
 2054  
     protected void writeEndTag( Tag t )
 2055  
     {
 2056  174
         if ( this.tableCaptionXMLWriterStack.isEmpty() )
 2057  
         {
 2058  166
             super.writeEndTag( t );
 2059  
         }
 2060  
         else
 2061  
         {
 2062  8
             this.tableCaptionXMLWriterStack.getLast().endElement();
 2063  
         }
 2064  174
     }
 2065  
 
 2066  
     /**
 2067  
      * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>.
 2068  
      *
 2069  
      * @param key not null
 2070  
      * @param msg not null
 2071  
      * @see #close()
 2072  
      * @since 1.1.1
 2073  
      */
 2074  
     private void logMessage( String key, String msg )
 2075  
     {
 2076  2
         msg = "[XHTML Sink] " + msg;
 2077  2
         if ( getLog().isDebugEnabled() )
 2078  
         {
 2079  0
             getLog().debug( msg );
 2080  
 
 2081  0
             return;
 2082  
         }
 2083  
 
 2084  2
         if ( warnMessages == null )
 2085  
         {
 2086  2
             warnMessages = new HashMap<String, Set<String>>();
 2087  
         }
 2088  
 
 2089  2
         Set<String> set = warnMessages.get( key );
 2090  2
         if ( set == null )
 2091  
         {
 2092  2
             set = new TreeSet<String>();
 2093  
         }
 2094  2
         set.add( msg );
 2095  2
         warnMessages.put( key, set );
 2096  2
     }
 2097  
 }