001package org.apache.maven.doxia.sink.impl; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.PrintWriter; 023import java.io.StringWriter; 024import java.io.Writer; 025import java.util.ArrayList; 026import java.util.EmptyStackException; 027import java.util.Enumeration; 028import java.util.HashMap; 029import java.util.LinkedList; 030import java.util.List; 031import java.util.Map; 032import java.util.Set; 033import java.util.Stack; 034import java.util.TreeSet; 035 036import javax.swing.text.MutableAttributeSet; 037import javax.swing.text.html.HTML.Attribute; 038import javax.swing.text.html.HTML.Tag; 039 040import org.apache.maven.doxia.markup.HtmlMarkup; 041import org.apache.maven.doxia.markup.Markup; 042import org.apache.maven.doxia.sink.SinkEventAttributes; 043import org.apache.maven.doxia.util.DoxiaUtils; 044import org.apache.maven.doxia.util.HtmlTools; 045 046import org.codehaus.plexus.util.StringUtils; 047import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter; 048 049/** 050 * Abstract base xhtml5 sink implementation. 051 */ 052public class Xhtml5BaseSink 053 extends AbstractXmlSink 054 implements HtmlMarkup 055{ 056 // ---------------------------------------------------------------------- 057 // Instance fields 058 // ---------------------------------------------------------------------- 059 060 /** The PrintWriter to write the result. */ 061 private final PrintWriter writer; 062 063 /** Used to collect text events mainly for the head events. */ 064 private StringBuffer textBuffer = new StringBuffer(); 065 066 /** An indication on if we're inside a head. */ 067 private boolean headFlag; 068 069 /** Keep track of the main and div tags for content events. */ 070 protected Stack<Tag> contentStack = new Stack<>(); 071 072 /** Keep track of the closing tags for inline events. */ 073 protected Stack<List<Tag>> inlineStack = new Stack<>(); 074 075 /** An indication on if we're inside a paragraph flag. */ 076 private boolean paragraphFlag; 077 078 /** An indication on if we're in verbatim mode. */ 079 private boolean verbatimFlag; 080 081 /** Stack of alignment int[] of table cells. */ 082 private final LinkedList<int[]> cellJustifStack; 083 084 /** Stack of justification of table cells. */ 085 private final LinkedList<Boolean> isCellJustifStack; 086 087 /** Stack of current table cell. */ 088 private final LinkedList<Integer> cellCountStack; 089 090 /** Used to style successive table rows differently. */ 091 private boolean evenTableRow = true; 092 093 /** The stack of StringWriter to write the table result temporary, so we could play with the output DOXIA-177. */ 094 private final LinkedList<StringWriter> tableContentWriterStack; 095 096 private final LinkedList<StringWriter> tableCaptionWriterStack; 097 098 private final LinkedList<PrettyPrintXMLWriter> tableCaptionXMLWriterStack; 099 100 /** The stack of table caption */ 101 private final LinkedList<String> tableCaptionStack; 102 103 /** used to store attributes passed to table(). */ 104 protected MutableAttributeSet tableAttributes; 105 106 /** Flag to know if {@link #tableRows(int[], boolean)} is called or not. It is mainly to be backward compatible 107 * with some plugins (like checkstyle) which uses: 108 * <pre> 109 * sink.table(); 110 * sink.tableRow(); 111 * </pre> 112 * instead of 113 * <pre> 114 * sink.table(); 115 * sink.tableRows( justify, true ); 116 * sink.tableRow(); 117 * </pre> 118 * */ 119 protected boolean tableRows = false; 120 121 /** Map of warn messages with a String as key to describe the error type and a Set as value. 122 * Using to reduce warn messages. */ 123 private Map<String, Set<String>> warnMessages; 124 125 // ---------------------------------------------------------------------- 126 // Constructor 127 // ---------------------------------------------------------------------- 128 129 /** 130 * Constructor, initialize the PrintWriter. 131 * 132 * @param out The writer to write the result. 133 */ 134 public Xhtml5BaseSink( Writer out ) 135 { 136 this.writer = new PrintWriter( out ); 137 138 this.cellJustifStack = new LinkedList<>(); 139 this.isCellJustifStack = new LinkedList<>(); 140 this.cellCountStack = new LinkedList<>(); 141 this.tableContentWriterStack = new LinkedList<>(); 142 this.tableCaptionWriterStack = new LinkedList<>(); 143 this.tableCaptionXMLWriterStack = new LinkedList<>(); 144 this.tableCaptionStack = new LinkedList<>(); 145 146 init(); 147 } 148 149 // ---------------------------------------------------------------------- 150 // Accessor methods 151 // ---------------------------------------------------------------------- 152 153 /** 154 * To use mainly when playing with the head events. 155 * 156 * @return the current buffer of text events. 157 */ 158 protected StringBuffer getTextBuffer() 159 { 160 return this.textBuffer; 161 } 162 163 /** 164 * <p>Setter for the field <code>headFlag</code>.</p> 165 * 166 * @param headFlag an header flag. 167 */ 168 protected void setHeadFlag( boolean headFlag ) 169 { 170 this.headFlag = headFlag; 171 } 172 173 /** 174 * <p>isHeadFlag.</p> 175 * 176 * @return the current headFlag. 177 */ 178 protected boolean isHeadFlag() 179 { 180 return this.headFlag ; 181 } 182 183 /** 184 * <p>Setter for the field <code>verbatimFlag</code>.</p> 185 * 186 * @param verb a verbatim flag. 187 */ 188 protected void setVerbatimFlag( boolean verb ) 189 { 190 this.verbatimFlag = verb; 191 } 192 193 /** 194 * <p>isVerbatimFlag.</p> 195 * 196 * @return the current verbatim flag. 197 */ 198 protected boolean isVerbatimFlag() 199 { 200 return this.verbatimFlag ; 201 } 202 203 /** 204 * <p>Setter for the field <code>cellJustif</code>.</p> 205 * 206 * @param justif the new cell justification array. 207 */ 208 protected void setCellJustif( int[] justif ) 209 { 210 this.cellJustifStack.addLast( justif ); 211 this.isCellJustifStack.addLast( Boolean.TRUE ); 212 } 213 214 /** 215 * <p>Getter for the field <code>cellJustif</code>.</p> 216 * 217 * @return the current cell justification array. 218 */ 219 protected int[] getCellJustif() 220 { 221 return this.cellJustifStack.getLast(); 222 } 223 224 /** 225 * <p>Setter for the field <code>cellCount</code>.</p> 226 * 227 * @param count the new cell count. 228 */ 229 protected void setCellCount( int count ) 230 { 231 this.cellCountStack.addLast( count ); 232 } 233 234 /** 235 * <p>Getter for the field <code>cellCount</code>.</p> 236 * 237 * @return the current cell count. 238 */ 239 protected int getCellCount() 240 { 241 return Integer.parseInt( this.cellCountStack.getLast().toString() ); 242 } 243 244 /** 245 * Reset all variables. 246 * 247 * @deprecated since 1.1.2, use {@link #init()} instead of. 248 */ 249 protected void resetState() 250 { 251 init(); 252 } 253 254 /** {@inheritDoc} */ 255 @Override 256 protected void init() 257 { 258 super.init(); 259 260 resetTextBuffer(); 261 262 this.cellJustifStack.clear(); 263 this.isCellJustifStack.clear(); 264 this.cellCountStack.clear(); 265 this.tableContentWriterStack.clear(); 266 this.tableCaptionWriterStack.clear(); 267 this.tableCaptionXMLWriterStack.clear(); 268 this.tableCaptionStack.clear(); 269 this.inlineStack.clear(); 270 271 this.headFlag = false; 272 this.paragraphFlag = false; 273 this.verbatimFlag = false; 274 275 this.evenTableRow = true; 276 this.tableAttributes = null; 277 this.tableRows = false; 278 this.warnMessages = null; 279 } 280 281 /** 282 * Reset the text buffer. 283 */ 284 protected void resetTextBuffer() 285 { 286 this.textBuffer = new StringBuffer(); 287 } 288 289 // ---------------------------------------------------------------------- 290 // Sections 291 // ---------------------------------------------------------------------- 292 293 /** 294 * {@inheritDoc} 295 */ 296 @Override 297 public void article() 298 { 299 article( null ); 300 } 301 302 /** 303 * {@inheritDoc} 304 */ 305 @Override 306 public void article( SinkEventAttributes attributes ) 307 { 308 MutableAttributeSet atts = SinkUtils.filterAttributes( 309 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 310 311 writeStartTag( HtmlMarkup.ARTICLE, atts ); 312 } 313 314 /** 315 * {@inheritDoc} 316 */ 317 @Override 318 public void article_() 319 { 320 writeEndTag( HtmlMarkup.ARTICLE ); 321 } 322 323 /** 324 * {@inheritDoc} 325 */ 326 @Override 327 public void navigation() 328 { 329 navigation( null ); 330 } 331 332 /** 333 * {@inheritDoc} 334 */ 335 @Override 336 public void navigation( SinkEventAttributes attributes ) 337 { 338 MutableAttributeSet atts = SinkUtils.filterAttributes( 339 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 340 341 writeStartTag( HtmlMarkup.NAV, atts ); 342 } 343 344 /** 345 * {@inheritDoc} 346 */ 347 @Override 348 public void navigation_() 349 { 350 writeEndTag( HtmlMarkup.NAV ); 351 } 352 353 /** 354 * {@inheritDoc} 355 */ 356 @Override 357 public void sidebar() 358 { 359 sidebar( null ); 360 } 361 362 /** 363 * {@inheritDoc} 364 */ 365 @Override 366 public void sidebar( SinkEventAttributes attributes ) 367 { 368 MutableAttributeSet atts = SinkUtils.filterAttributes( 369 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 370 371 writeStartTag( HtmlMarkup.ASIDE, atts ); 372 } 373 374 /** 375 * {@inheritDoc} 376 */ 377 @Override 378 public void sidebar_() 379 { 380 writeEndTag( HtmlMarkup.ASIDE ); 381 } 382 383 /** {@inheritDoc} */ 384 @Override 385 public void section( int level, SinkEventAttributes attributes ) 386 { 387 onSection( level, attributes ); 388 } 389 390 /** {@inheritDoc} */ 391 @Override 392 public void sectionTitle( int level, SinkEventAttributes attributes ) 393 { 394 onSectionTitle( level, attributes ); 395 } 396 397 /** {@inheritDoc} */ 398 @Override 399 public void sectionTitle_( int level ) 400 { 401 onSectionTitle_( level ); 402 } 403 404 /** {@inheritDoc} */ 405 @Override 406 public void section_( int level ) 407 { 408 onSection_( level ); 409 } 410 411 /** {@inheritDoc} */ 412 @Override 413 public void section1() 414 { 415 onSection( SECTION_LEVEL_1, null ); 416 } 417 418 /** {@inheritDoc} */ 419 @Override 420 public void sectionTitle1() 421 { 422 onSectionTitle( SECTION_LEVEL_1, null ); 423 } 424 425 /** {@inheritDoc} */ 426 @Override 427 public void sectionTitle1_() 428 { 429 onSectionTitle_( SECTION_LEVEL_1 ); 430 } 431 432 /** {@inheritDoc} */ 433 @Override 434 public void section1_() 435 { 436 onSection_( SECTION_LEVEL_1 ); 437 } 438 439 /** {@inheritDoc} */ 440 @Override 441 public void section2() 442 { 443 onSection( SECTION_LEVEL_2, null ); 444 } 445 446 /** {@inheritDoc} */ 447 @Override 448 public void sectionTitle2() 449 { 450 onSectionTitle( SECTION_LEVEL_2, null ); 451 } 452 453 /** {@inheritDoc} */ 454 @Override 455 public void sectionTitle2_() 456 { 457 onSectionTitle_( SECTION_LEVEL_2 ); 458 } 459 460 /** {@inheritDoc} */ 461 @Override 462 public void section2_() 463 { 464 onSection_( SECTION_LEVEL_2 ); 465 } 466 467 /** {@inheritDoc} */ 468 @Override 469 public void section3() 470 { 471 onSection( SECTION_LEVEL_3, null ); 472 } 473 474 /** {@inheritDoc} */ 475 @Override 476 public void sectionTitle3() 477 { 478 onSectionTitle( SECTION_LEVEL_3, null ); 479 } 480 481 /** {@inheritDoc} */ 482 @Override 483 public void sectionTitle3_() 484 { 485 onSectionTitle_( SECTION_LEVEL_3 ); 486 } 487 488 /** {@inheritDoc} */ 489 @Override 490 public void section3_() 491 { 492 onSection_( SECTION_LEVEL_3 ); 493 } 494 495 /** {@inheritDoc} */ 496 @Override 497 public void section4() 498 { 499 onSection( SECTION_LEVEL_4, null ); 500 } 501 502 /** {@inheritDoc} */ 503 @Override 504 public void sectionTitle4() 505 { 506 onSectionTitle( SECTION_LEVEL_4, null ); 507 } 508 509 /** {@inheritDoc} */ 510 @Override 511 public void sectionTitle4_() 512 { 513 onSectionTitle_( SECTION_LEVEL_4 ); 514 } 515 516 /** {@inheritDoc} */ 517 @Override 518 public void section4_() 519 { 520 onSection_( SECTION_LEVEL_4 ); 521 } 522 523 /** {@inheritDoc} */ 524 @Override 525 public void section5() 526 { 527 onSection( SECTION_LEVEL_5, null ); 528 } 529 530 /** {@inheritDoc} */ 531 @Override 532 public void sectionTitle5() 533 { 534 onSectionTitle( SECTION_LEVEL_5, null ); 535 } 536 537 /** {@inheritDoc} */ 538 @Override 539 public void sectionTitle5_() 540 { 541 onSectionTitle_( SECTION_LEVEL_5 ); 542 } 543 544 /** {@inheritDoc} */ 545 @Override 546 public void section5_() 547 { 548 onSection_( SECTION_LEVEL_5 ); 549 } 550 551 /** 552 * Starts a section. The default class style is <code>section</code>. 553 * 554 * @param depth The level of the section. 555 * @param attributes some attributes. May be null. 556 * @see javax.swing.text.html.HTML.Tag#SECTION 557 */ 558 protected void onSection( int depth, SinkEventAttributes attributes ) 559 { 560 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 ) 561 { 562 MutableAttributeSet att = new SinkEventAttributeSet(); 563 att.addAttributes( SinkUtils.filterAttributes( 564 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ) ); 565 566 writeStartTag( HtmlMarkup.SECTION, att ); 567 } 568 } 569 570 /** 571 * Ends a section. 572 * 573 * @param depth The level of the section. 574 * @see javax.swing.text.html.HTML.Tag#DIV 575 */ 576 protected void onSection_( int depth ) 577 { 578 if ( depth >= SECTION_LEVEL_1 && depth <= SECTION_LEVEL_5 ) 579 { 580 writeEndTag( HtmlMarkup.SECTION ); 581 } 582 } 583 584 /** 585 * Starts a section title. 586 * 587 * @param depth The level of the section title. 588 * @param attributes some attributes. May be null. 589 * @see javax.swing.text.html.HTML.Tag#H2 590 * @see javax.swing.text.html.HTML.Tag#H3 591 * @see javax.swing.text.html.HTML.Tag#H4 592 * @see javax.swing.text.html.HTML.Tag#H5 593 * @see javax.swing.text.html.HTML.Tag#H6 594 */ 595 protected void onSectionTitle( int depth, SinkEventAttributes attributes ) 596 { 597 MutableAttributeSet atts = SinkUtils.filterAttributes( 598 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 599 600 if ( depth == SECTION_LEVEL_1 ) 601 { 602 writeStartTag( HtmlMarkup.H2, atts ); 603 } 604 else if ( depth == SECTION_LEVEL_2 ) 605 { 606 writeStartTag( HtmlMarkup.H3, atts ); 607 } 608 else if ( depth == SECTION_LEVEL_3 ) 609 { 610 writeStartTag( HtmlMarkup.H4, atts ); 611 } 612 else if ( depth == SECTION_LEVEL_4 ) 613 { 614 writeStartTag( HtmlMarkup.H5, atts ); 615 } 616 else if ( depth == SECTION_LEVEL_5 ) 617 { 618 writeStartTag( HtmlMarkup.H6, atts ); 619 } 620 } 621 622 /** 623 * Ends a section title. 624 * 625 * @param depth The level of the section title. 626 * @see javax.swing.text.html.HTML.Tag#H2 627 * @see javax.swing.text.html.HTML.Tag#H3 628 * @see javax.swing.text.html.HTML.Tag#H4 629 * @see javax.swing.text.html.HTML.Tag#H5 630 * @see javax.swing.text.html.HTML.Tag#H6 631 */ 632 protected void onSectionTitle_( int depth ) 633 { 634 if ( depth == SECTION_LEVEL_1 ) 635 { 636 writeEndTag( HtmlMarkup.H2 ); 637 } 638 else if ( depth == SECTION_LEVEL_2 ) 639 { 640 writeEndTag( HtmlMarkup.H3 ); 641 } 642 else if ( depth == SECTION_LEVEL_3 ) 643 { 644 writeEndTag( HtmlMarkup.H4 ); 645 } 646 else if ( depth == SECTION_LEVEL_4 ) 647 { 648 writeEndTag( HtmlMarkup.H5 ); 649 } 650 else if ( depth == SECTION_LEVEL_5 ) 651 { 652 writeEndTag( HtmlMarkup.H6 ); 653 } 654 } 655 656 /** 657 * {@inheritDoc} 658 */ 659 @Override 660 public void header() 661 { 662 header( null ); 663 } 664 665 /** 666 * {@inheritDoc} 667 */ 668 @Override 669 public void header( SinkEventAttributes attributes ) 670 { 671 MutableAttributeSet atts = SinkUtils.filterAttributes( 672 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 673 674 writeStartTag( HtmlMarkup.HEADER, atts ); 675 } 676 677 /** 678 * {@inheritDoc} 679 */ 680 @Override 681 public void header_() 682 { 683 writeEndTag( HtmlMarkup.HEADER ); 684 } 685 686 /** 687 * {@inheritDoc} 688 */ 689 @Override 690 public void content() 691 { 692 content( (SinkEventAttributes) null ); 693 } 694 695 /** 696 * {@inheritDoc} 697 */ 698 @Override 699 public void content( SinkEventAttributes attributes ) 700 { 701 MutableAttributeSet atts = SinkUtils.filterAttributes( 702 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 703 704 if ( contentStack.empty() ) 705 { 706 writeStartTag( contentStack.push( HtmlMarkup.MAIN ), atts ); 707 } 708 else 709 { 710 if ( atts == null ) 711 { 712 atts = new SinkEventAttributeSet( 1 ); 713 } 714 715 if ( !atts.isDefined( SinkEventAttributes.CLASS ) ) 716 { 717 atts.addAttribute( SinkEventAttributes.CLASS, "content" ); 718 } 719 720 writeStartTag( contentStack.push( HtmlMarkup.DIV ), atts ); 721 } 722 } 723 724 /** 725 * {@inheritDoc} 726 */ 727 @Override 728 public void content_() 729 { 730 try 731 { 732 writeEndTag( contentStack.pop() ); 733 } 734 catch ( EmptyStackException ese ) 735 { 736 /* do nothing if the stack is empty */ 737 } 738 } 739 740 /** 741 * {@inheritDoc} 742 */ 743 @Override 744 public void footer() 745 { 746 footer( null ); 747 } 748 749 /** 750 * {@inheritDoc} 751 */ 752 @Override 753 public void footer( SinkEventAttributes attributes ) 754 { 755 MutableAttributeSet atts = SinkUtils.filterAttributes( 756 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 757 758 writeStartTag( HtmlMarkup.FOOTER, atts ); 759 } 760 761 /** 762 * {@inheritDoc} 763 */ 764 @Override 765 public void footer_() 766 { 767 writeEndTag( HtmlMarkup.FOOTER ); 768 } 769 770 // ----------------------------------------------------------------------- 771 // 772 // ----------------------------------------------------------------------- 773 774 /** 775 * {@inheritDoc} 776 * @see javax.swing.text.html.HTML.Tag#UL 777 */ 778 @Override 779 public void list() 780 { 781 list( null ); 782 } 783 784 /** 785 * {@inheritDoc} 786 * @see javax.swing.text.html.HTML.Tag#UL 787 */ 788 @Override 789 public void list( SinkEventAttributes attributes ) 790 { 791 if ( paragraphFlag ) 792 { 793 // The content of element type "p" must match 794 // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong| 795 // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)". 796 paragraph_(); 797 } 798 799 MutableAttributeSet atts = SinkUtils.filterAttributes( 800 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 801 802 writeStartTag( HtmlMarkup.UL, atts ); 803 } 804 805 /** 806 * {@inheritDoc} 807 * @see javax.swing.text.html.HTML.Tag#UL 808 */ 809 @Override 810 public void list_() 811 { 812 writeEndTag( HtmlMarkup.UL ); 813 } 814 815 /** 816 * {@inheritDoc} 817 * @see javax.swing.text.html.HTML.Tag#LI 818 */ 819 @Override 820 public void listItem() 821 { 822 listItem( null ); 823 } 824 825 /** 826 * {@inheritDoc} 827 * @see javax.swing.text.html.HTML.Tag#LI 828 */ 829 @Override 830 public void listItem( SinkEventAttributes attributes ) 831 { 832 MutableAttributeSet atts = SinkUtils.filterAttributes( 833 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 834 835 writeStartTag( HtmlMarkup.LI, atts ); 836 } 837 838 /** 839 * {@inheritDoc} 840 * @see javax.swing.text.html.HTML.Tag#LI 841 */ 842 @Override 843 public void listItem_() 844 { 845 writeEndTag( HtmlMarkup.LI ); 846 } 847 848 /** 849 * The default list style depends on the numbering. 850 * 851 * {@inheritDoc} 852 * @see javax.swing.text.html.HTML.Tag#OL 853 */ 854 @Override 855 public void numberedList( int numbering ) 856 { 857 numberedList( numbering, null ); 858 } 859 860 /** 861 * The default list style depends on the numbering. 862 * 863 * {@inheritDoc} 864 * @see javax.swing.text.html.HTML.Tag#OL 865 */ 866 @Override 867 public void numberedList( int numbering, SinkEventAttributes attributes ) 868 { 869 if ( paragraphFlag ) 870 { 871 // The content of element type "p" must match 872 // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong| 873 // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)". 874 paragraph_(); 875 } 876 877 String style; 878 switch ( numbering ) 879 { 880 case NUMBERING_UPPER_ALPHA: 881 style = "upper-alpha"; 882 break; 883 case NUMBERING_LOWER_ALPHA: 884 style = "lower-alpha"; 885 break; 886 case NUMBERING_UPPER_ROMAN: 887 style = "upper-roman"; 888 break; 889 case NUMBERING_LOWER_ROMAN: 890 style = "lower-roman"; 891 break; 892 case NUMBERING_DECIMAL: 893 default: 894 style = "decimal"; 895 } 896 897 MutableAttributeSet atts = SinkUtils.filterAttributes( 898 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 899 900 if ( atts == null ) 901 { 902 atts = new SinkEventAttributeSet( 1 ); 903 } 904 905 atts.addAttribute( Attribute.STYLE, "list-style-type: " + style ); 906 907 writeStartTag( HtmlMarkup.OL, atts ); 908 } 909 910 /** 911 * {@inheritDoc} 912 * @see javax.swing.text.html.HTML.Tag#OL 913 */ 914 @Override 915 public void numberedList_() 916 { 917 writeEndTag( HtmlMarkup.OL ); 918 } 919 920 /** 921 * {@inheritDoc} 922 * @see javax.swing.text.html.HTML.Tag#LI 923 */ 924 @Override 925 public void numberedListItem() 926 { 927 numberedListItem( null ); 928 } 929 930 /** 931 * {@inheritDoc} 932 * @see javax.swing.text.html.HTML.Tag#LI 933 */ 934 @Override 935 public void numberedListItem( SinkEventAttributes attributes ) 936 { 937 MutableAttributeSet atts = SinkUtils.filterAttributes( 938 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 939 940 writeStartTag( HtmlMarkup.LI, atts ); 941 } 942 943 /** 944 * {@inheritDoc} 945 * @see javax.swing.text.html.HTML.Tag#LI 946 */ 947 @Override 948 public void numberedListItem_() 949 { 950 writeEndTag( HtmlMarkup.LI ); 951 } 952 953 /** 954 * {@inheritDoc} 955 * @see javax.swing.text.html.HTML.Tag#DL 956 */ 957 @Override 958 public void definitionList() 959 { 960 definitionList( null ); 961 } 962 963 /** 964 * {@inheritDoc} 965 * @see javax.swing.text.html.HTML.Tag#DL 966 */ 967 @Override 968 public void definitionList( SinkEventAttributes attributes ) 969 { 970 if ( paragraphFlag ) 971 { 972 // The content of element type "p" must match 973 // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong| 974 // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)". 975 paragraph_(); 976 } 977 978 MutableAttributeSet atts = SinkUtils.filterAttributes( 979 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 980 981 writeStartTag( HtmlMarkup.DL, atts ); 982 } 983 984 /** 985 * {@inheritDoc} 986 * @see javax.swing.text.html.HTML.Tag#DL 987 */ 988 @Override 989 public void definitionList_() 990 { 991 writeEndTag( HtmlMarkup.DL ); 992 } 993 994 /** 995 * {@inheritDoc} 996 * @see javax.swing.text.html.HTML.Tag#DT 997 */ 998 @Override 999 public void definedTerm( SinkEventAttributes attributes ) 1000 { 1001 MutableAttributeSet atts = SinkUtils.filterAttributes( 1002 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 1003 1004 writeStartTag( HtmlMarkup.DT, atts ); 1005 } 1006 1007 /** 1008 * {@inheritDoc} 1009 * @see javax.swing.text.html.HTML.Tag#DT 1010 */ 1011 @Override 1012 public void definedTerm() 1013 { 1014 definedTerm( null ); 1015 } 1016 1017 /** 1018 * {@inheritDoc} 1019 * @see javax.swing.text.html.HTML.Tag#DT 1020 */ 1021 @Override 1022 public void definedTerm_() 1023 { 1024 writeEndTag( HtmlMarkup.DT ); 1025 } 1026 1027 /** 1028 * {@inheritDoc} 1029 * @see javax.swing.text.html.HTML.Tag#DD 1030 */ 1031 @Override 1032 public void definition() 1033 { 1034 definition( null ); 1035 } 1036 1037 /** 1038 * {@inheritDoc} 1039 * @see javax.swing.text.html.HTML.Tag#DD 1040 */ 1041 @Override 1042 public void definition( SinkEventAttributes attributes ) 1043 { 1044 MutableAttributeSet atts = SinkUtils.filterAttributes( 1045 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 1046 1047 writeStartTag( HtmlMarkup.DD, atts ); 1048 } 1049 1050 /** 1051 * {@inheritDoc} 1052 * @see javax.swing.text.html.HTML.Tag#DD 1053 */ 1054 @Override 1055 public void definition_() 1056 { 1057 writeEndTag( HtmlMarkup.DD ); 1058 } 1059 1060 /** 1061 * {@inheritDoc} 1062 */ 1063 @Override 1064 public void figure() 1065 { 1066 figure( null ); 1067 } 1068 1069 /** 1070 * {@inheritDoc} 1071 */ 1072 @Override 1073 public void figure( SinkEventAttributes attributes ) 1074 { 1075 writeStartTag( HtmlMarkup.FIGURE, attributes ); 1076 } 1077 1078 /** {@inheritDoc} */ 1079 @Override 1080 public void figure_() 1081 { 1082 writeEndTag( HtmlMarkup.FIGURE ); 1083 } 1084 1085 /** {@inheritDoc} */ 1086 @Override 1087 public void figureGraphics( String name ) 1088 { 1089 figureGraphics( name, null ); 1090 } 1091 1092 /** {@inheritDoc} */ 1093 @Override 1094 public void figureGraphics( String src, SinkEventAttributes attributes ) 1095 { 1096 MutableAttributeSet filtered = SinkUtils.filterAttributes( attributes, SinkUtils.SINK_IMG_ATTRIBUTES ); 1097 if ( filtered != null ) 1098 { 1099 filtered.removeAttribute( Attribute.SRC.toString() ); 1100 } 1101 1102 int count = ( attributes == null ? 1 : attributes.getAttributeCount() + 1 ); 1103 1104 MutableAttributeSet atts = new SinkEventAttributeSet( count ); 1105 1106 atts.addAttribute( Attribute.SRC, HtmlTools.escapeHTML( src, true ) ); 1107 atts.addAttributes( filtered ); 1108 1109 if ( atts.getAttribute( Attribute.ALT.toString() ) == null ) 1110 { 1111 atts.addAttribute( Attribute.ALT.toString(), "" ); 1112 } 1113 1114 writeStartTag( HtmlMarkup.IMG, atts, true ); 1115 } 1116 1117 /** 1118 * {@inheritDoc} 1119 */ 1120 @Override 1121 public void figureCaption() 1122 { 1123 figureCaption( null ); 1124 } 1125 1126 /** {@inheritDoc} */ 1127 @Override 1128 public void figureCaption( SinkEventAttributes attributes ) 1129 { 1130 writeStartTag( HtmlMarkup.FIGCAPTION, attributes ); 1131 } 1132 1133 /** {@inheritDoc} */ 1134 @Override 1135 public void figureCaption_() 1136 { 1137 writeEndTag( HtmlMarkup.FIGCAPTION ); 1138 } 1139 1140 /** 1141 * {@inheritDoc} 1142 * @see javax.swing.text.html.HTML.Tag#P 1143 */ 1144 @Override 1145 public void paragraph() 1146 { 1147 paragraph( null ); 1148 } 1149 1150 /** 1151 * {@inheritDoc} 1152 * @see javax.swing.text.html.HTML.Tag#P 1153 */ 1154 @Override 1155 public void paragraph( SinkEventAttributes attributes ) 1156 { 1157 paragraphFlag = true; 1158 1159 MutableAttributeSet atts = SinkUtils.filterAttributes( 1160 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 1161 1162 writeStartTag( HtmlMarkup.P, atts ); 1163 } 1164 1165 /** 1166 * {@inheritDoc} 1167 * @see javax.swing.text.html.HTML.Tag#P 1168 */ 1169 @Override 1170 public void paragraph_() 1171 { 1172 if ( paragraphFlag ) 1173 { 1174 writeEndTag( HtmlMarkup.P ); 1175 paragraphFlag = false; 1176 } 1177 } 1178 1179 /** 1180 * {@inheritDoc} 1181 * @see javax.swing.text.html.HTML.Tag#DATA 1182 */ 1183 @Override 1184 public void data( String value ) 1185 { 1186 data( value, null ); 1187 } 1188 1189 /** 1190 * {@inheritDoc} 1191 * @see javax.swing.text.html.HTML.Tag#DATA 1192 */ 1193 @Override 1194 public void data( String value, SinkEventAttributes attributes ) 1195 { 1196 MutableAttributeSet atts = SinkUtils.filterAttributes( 1197 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 1198 1199 MutableAttributeSet att = new SinkEventAttributeSet(); 1200 if ( value != null ) 1201 { 1202 att.addAttribute( Attribute.VALUE, value ); 1203 } 1204 att.addAttributes( atts ); 1205 1206 writeStartTag( HtmlMarkup.DATA, att ); 1207 } 1208 1209 /** 1210 * {@inheritDoc} 1211 * @see javax.swing.text.html.HTML.Tag#DATA 1212 */ 1213 @Override 1214 public void data_() 1215 { 1216 writeEndTag( HtmlMarkup.DATA ); 1217 } 1218 1219 /** 1220 * {@inheritDoc} 1221 * @see javax.swing.text.html.HTML.Tag#TIME 1222 */ 1223 @Override 1224 public void time( String datetime ) 1225 { 1226 time( datetime, null ); 1227 } 1228 1229 /** 1230 * {@inheritDoc} 1231 * @see javax.swing.text.html.HTML.Tag#DATA 1232 */ 1233 @Override 1234 public void time( String datetime, SinkEventAttributes attributes ) 1235 { 1236 MutableAttributeSet atts = SinkUtils.filterAttributes( 1237 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 1238 1239 MutableAttributeSet att = new SinkEventAttributeSet(); 1240 if ( datetime != null ) 1241 { 1242 att.addAttribute( "datetime", datetime ); 1243 } 1244 att.addAttributes( atts ); 1245 1246 writeStartTag( HtmlMarkup.TIME, att ); 1247 } 1248 1249 /** 1250 * {@inheritDoc} 1251 */ 1252 @Override 1253 public void time_() 1254 { 1255 writeEndTag( HtmlMarkup.TIME ); 1256 } 1257 1258 /** 1259 * {@inheritDoc} 1260 * @see javax.swing.text.html.HTML.Tag#ADDRESS 1261 */ 1262 @Override 1263 public void address() 1264 { 1265 address( null ); 1266 } 1267 1268 /** 1269 * {@inheritDoc} 1270 * @see javax.swing.text.html.HTML.Tag#ADDRESS 1271 */ 1272 @Override 1273 public void address( SinkEventAttributes attributes ) 1274 { 1275 MutableAttributeSet atts = SinkUtils.filterAttributes( 1276 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 1277 1278 writeStartTag( HtmlMarkup.ADDRESS, atts ); 1279 } 1280 1281 /** 1282 * {@inheritDoc} 1283 * @see javax.swing.text.html.HTML.Tag#ADDRESS 1284 */ 1285 @Override 1286 public void address_() 1287 { 1288 writeEndTag( HtmlMarkup.ADDRESS ); 1289 } 1290 1291 /** 1292 * {@inheritDoc} 1293 * @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE 1294 */ 1295 @Override 1296 public void blockquote() 1297 { 1298 blockquote( null ); 1299 } 1300 1301 /** 1302 * {@inheritDoc} 1303 * @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE 1304 */ 1305 @Override 1306 public void blockquote( SinkEventAttributes attributes ) 1307 { 1308 MutableAttributeSet atts = SinkUtils.filterAttributes( 1309 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 1310 1311 writeStartTag( HtmlMarkup.BLOCKQUOTE, atts ); 1312 } 1313 1314 /** 1315 * {@inheritDoc} 1316 * @see javax.swing.text.html.HTML.Tag#BLOCKQUOTE 1317 */ 1318 @Override 1319 public void blockquote_() 1320 { 1321 writeEndTag( HtmlMarkup.BLOCKQUOTE ); 1322 } 1323 1324 /** 1325 * {@inheritDoc} 1326 * @see javax.swing.text.html.HTML.Tag#DIV 1327 */ 1328 @Override 1329 public void division() 1330 { 1331 division( null ); 1332 } 1333 1334 /** 1335 * {@inheritDoc} 1336 * @see javax.swing.text.html.HTML.Tag#DIV 1337 */ 1338 @Override 1339 public void division( SinkEventAttributes attributes ) 1340 { 1341 MutableAttributeSet atts = SinkUtils.filterAttributes( 1342 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 1343 1344 writeStartTag( HtmlMarkup.DIV, atts ); 1345 } 1346 1347 /** 1348 * {@inheritDoc} 1349 * @see javax.swing.text.html.HTML.Tag#DIV 1350 */ 1351 @Override 1352 public void division_() 1353 { 1354 writeEndTag( HtmlMarkup.DIV ); 1355 } 1356 1357 /** 1358 * The default class style for boxed is <code>source</code>. 1359 * 1360 * {@inheritDoc} 1361 * @see javax.swing.text.html.HTML.Tag#DIV 1362 * @see javax.swing.text.html.HTML.Tag#PRE 1363 */ 1364 @Override 1365 public void verbatim( boolean boxed ) 1366 { 1367 if ( boxed ) 1368 { 1369 verbatim( SinkEventAttributeSet.BOXED ); 1370 } 1371 else 1372 { 1373 verbatim( null ); 1374 } 1375 } 1376 1377 /** 1378 * The default class style for boxed is <code>source</code>. 1379 * 1380 * {@inheritDoc} 1381 * @see javax.swing.text.html.HTML.Tag#DIV 1382 * @see javax.swing.text.html.HTML.Tag#PRE 1383 */ 1384 @Override 1385 public void verbatim( SinkEventAttributes attributes ) 1386 { 1387 if ( paragraphFlag ) 1388 { 1389 // The content of element type "p" must match 1390 // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong| 1391 // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)". 1392 paragraph_(); 1393 } 1394 1395 verbatimFlag = true; 1396 1397 MutableAttributeSet atts = SinkUtils.filterAttributes( 1398 attributes, SinkUtils.SINK_VERBATIM_ATTRIBUTES ); 1399 1400 if ( atts == null ) 1401 { 1402 atts = new SinkEventAttributeSet(); 1403 } 1404 1405 boolean boxed = false; 1406 1407 if ( atts.isDefined( SinkEventAttributes.DECORATION ) ) 1408 { 1409 boxed = 1410 "boxed".equals( atts.getAttribute( SinkEventAttributes.DECORATION ).toString() ); 1411 } 1412 1413 SinkEventAttributes divAtts = null; 1414 1415 if ( boxed ) 1416 { 1417 divAtts = new SinkEventAttributeSet( Attribute.CLASS.toString(), "source" ); 1418 } 1419 1420 atts.removeAttribute( SinkEventAttributes.DECORATION ); 1421 1422 writeStartTag( HtmlMarkup.DIV, divAtts ); 1423 writeStartTag( HtmlMarkup.PRE, atts ); 1424 } 1425 1426 /** 1427 * {@inheritDoc} 1428 * @see javax.swing.text.html.HTML.Tag#DIV 1429 * @see javax.swing.text.html.HTML.Tag#PRE 1430 */ 1431 @Override 1432 public void verbatim_() 1433 { 1434 writeEndTag( HtmlMarkup.PRE ); 1435 writeEndTag( HtmlMarkup.DIV ); 1436 1437 verbatimFlag = false; 1438 1439 } 1440 1441 /** 1442 * {@inheritDoc} 1443 * @see javax.swing.text.html.HTML.Tag#HR 1444 */ 1445 @Override 1446 public void horizontalRule() 1447 { 1448 horizontalRule( null ); 1449 } 1450 1451 /** 1452 * {@inheritDoc} 1453 * @see javax.swing.text.html.HTML.Tag#HR 1454 */ 1455 @Override 1456 public void horizontalRule( SinkEventAttributes attributes ) 1457 { 1458 MutableAttributeSet atts = SinkUtils.filterAttributes( 1459 attributes, SinkUtils.SINK_HR_ATTRIBUTES ); 1460 1461 writeSimpleTag( HtmlMarkup.HR, atts ); 1462 } 1463 1464 /** {@inheritDoc} */ 1465 @Override 1466 public void table() 1467 { 1468 // start table with tableRows 1469 table( null ); 1470 } 1471 1472 /** {@inheritDoc} */ 1473 @Override 1474 public void table( SinkEventAttributes attributes ) 1475 { 1476 this.tableContentWriterStack.addLast( new StringWriter() ); 1477 this.tableRows = false; 1478 1479 if ( paragraphFlag ) 1480 { 1481 // The content of element type "p" must match 1482 // "(a|br|span|bdo|object|applet|img|map|iframe|tt|i|b|u|s|strike|big|small|font|basefont|em|strong| 1483 // dfn|code|q|samp|kbd|var|cite|abbr|acronym|sub|sup|input|select|textarea|label|button|ins|del|script)". 1484 paragraph_(); 1485 } 1486 1487 // start table with tableRows 1488 if ( attributes == null ) 1489 { 1490 this.tableAttributes = new SinkEventAttributeSet( 0 ); 1491 } 1492 else 1493 { 1494 this.tableAttributes = SinkUtils.filterAttributes( 1495 attributes, SinkUtils.SINK_TABLE_ATTRIBUTES ); 1496 } 1497 } 1498 1499 /** 1500 * {@inheritDoc} 1501 * @see javax.swing.text.html.HTML.Tag#TABLE 1502 */ 1503 @Override 1504 public void table_() 1505 { 1506 this.tableRows = false; 1507 1508 writeEndTag( HtmlMarkup.TABLE ); 1509 1510 if ( !this.cellCountStack.isEmpty() ) 1511 { 1512 this.cellCountStack.removeLast().toString(); 1513 } 1514 1515 if ( this.tableContentWriterStack.isEmpty() ) 1516 { 1517 if ( getLog().isWarnEnabled() ) 1518 { 1519 getLog().warn( "No table content." ); 1520 } 1521 return; 1522 } 1523 1524 String tableContent = this.tableContentWriterStack.removeLast().toString(); 1525 1526 String tableCaption = null; 1527 if ( !this.tableCaptionStack.isEmpty() && this.tableCaptionStack.getLast() != null ) 1528 { 1529 tableCaption = this.tableCaptionStack.removeLast(); 1530 } 1531 1532 if ( tableCaption != null ) 1533 { 1534 // DOXIA-177 1535 StringBuilder sb = new StringBuilder(); 1536 sb.append( tableContent, 0, tableContent.indexOf( Markup.GREATER_THAN ) + 1 ); 1537 sb.append( tableCaption ); 1538 sb.append( tableContent.substring( tableContent.indexOf( Markup.GREATER_THAN ) + 1 ) ); 1539 1540 write( sb.toString() ); 1541 } 1542 else 1543 { 1544 write( tableContent ); 1545 } 1546 } 1547 1548 /** 1549 * The default class style is <code>bodyTable</code>. 1550 * The default align is <code>center</code>. 1551 * 1552 * {@inheritDoc} 1553 * @see javax.swing.text.html.HTML.Tag#TABLE 1554 */ 1555 @Override 1556 public void tableRows( int[] justification, boolean grid ) 1557 { 1558 this.tableRows = true; 1559 1560 setCellJustif( justification ); 1561 1562 if ( this.tableAttributes == null ) 1563 { 1564 this.tableAttributes = new SinkEventAttributeSet( 0 ); 1565 } 1566 1567 MutableAttributeSet att = new SinkEventAttributeSet(); 1568 if ( !this.tableAttributes.isDefined( Attribute.BORDER.toString() ) ) 1569 { 1570 att.addAttribute( Attribute.BORDER, ( grid ? "1" : "0" ) ); 1571 } 1572 1573 if ( !this.tableAttributes.isDefined( Attribute.CLASS.toString() ) ) 1574 { 1575 att.addAttribute( Attribute.CLASS, "bodyTable" ); 1576 } 1577 1578 att.addAttributes( this.tableAttributes ); 1579 this.tableAttributes.removeAttributes( this.tableAttributes ); 1580 1581 writeStartTag( HtmlMarkup.TABLE, att ); 1582 1583 this.cellCountStack.addLast( 0 ); 1584 } 1585 1586 /** {@inheritDoc} */ 1587 @Override 1588 public void tableRows_() 1589 { 1590 this.tableRows = false; 1591 if ( !this.cellJustifStack.isEmpty() ) 1592 { 1593 this.cellJustifStack.removeLast(); 1594 } 1595 if ( !this.isCellJustifStack.isEmpty() ) 1596 { 1597 this.isCellJustifStack.removeLast(); 1598 } 1599 1600 this.evenTableRow = true; 1601 } 1602 1603 /** 1604 * The default class style is <code>a</code> or <code>b</code> depending the row id. 1605 * 1606 * {@inheritDoc} 1607 * @see javax.swing.text.html.HTML.Tag#TR 1608 */ 1609 @Override 1610 public void tableRow() 1611 { 1612 // To be backward compatible 1613 if ( !this.tableRows ) 1614 { 1615 tableRows( null, false ); 1616 } 1617 tableRow( null ); 1618 } 1619 1620 /** 1621 * The default class style is <code>a</code> or <code>b</code> depending the row id. 1622 * 1623 * {@inheritDoc} 1624 * @see javax.swing.text.html.HTML.Tag#TR 1625 */ 1626 @Override 1627 public void tableRow( SinkEventAttributes attributes ) 1628 { 1629 MutableAttributeSet att = new SinkEventAttributeSet(); 1630 1631 if ( evenTableRow ) 1632 { 1633 att.addAttribute( Attribute.CLASS, "a" ); 1634 } 1635 else 1636 { 1637 att.addAttribute( Attribute.CLASS, "b" ); 1638 } 1639 1640 att.addAttributes( SinkUtils.filterAttributes( 1641 attributes, SinkUtils.SINK_TR_ATTRIBUTES ) ); 1642 1643 writeStartTag( HtmlMarkup.TR, att ); 1644 1645 evenTableRow = !evenTableRow; 1646 1647 if ( !this.cellCountStack.isEmpty() ) 1648 { 1649 this.cellCountStack.removeLast(); 1650 this.cellCountStack.addLast( 0 ); 1651 } 1652 } 1653 1654 /** 1655 * {@inheritDoc} 1656 * @see javax.swing.text.html.HTML.Tag#TR 1657 */ 1658 @Override 1659 public void tableRow_() 1660 { 1661 writeEndTag( HtmlMarkup.TR ); 1662 } 1663 1664 /** {@inheritDoc} */ 1665 @Override 1666 public void tableCell() 1667 { 1668 tableCell( (SinkEventAttributeSet) null ); 1669 } 1670 1671 /** {@inheritDoc} */ 1672 @Override 1673 public void tableHeaderCell() 1674 { 1675 tableHeaderCell( (SinkEventAttributeSet) null ); 1676 } 1677 1678 /** {@inheritDoc} */ 1679 @Override 1680 public void tableCell( String width ) 1681 { 1682 MutableAttributeSet att = new SinkEventAttributeSet(); 1683 att.addAttribute( Attribute.WIDTH, width ); 1684 1685 tableCell( false, att ); 1686 } 1687 1688 /** {@inheritDoc} */ 1689 @Override 1690 public void tableHeaderCell( String width ) 1691 { 1692 MutableAttributeSet att = new SinkEventAttributeSet(); 1693 att.addAttribute( Attribute.WIDTH, width ); 1694 1695 tableCell( true, att ); 1696 } 1697 1698 /** {@inheritDoc} */ 1699 @Override 1700 public void tableCell( SinkEventAttributes attributes ) 1701 { 1702 tableCell( false, attributes ); 1703 } 1704 1705 /** {@inheritDoc} */ 1706 @Override 1707 public void tableHeaderCell( SinkEventAttributes attributes ) 1708 { 1709 tableCell( true, attributes ); 1710 } 1711 1712 /** 1713 * @param headerRow true if it is an header row 1714 * @param attributes the cell attributes 1715 * @see javax.swing.text.html.HTML.Tag#TH 1716 * @see javax.swing.text.html.HTML.Tag#TD 1717 */ 1718 private void tableCell( boolean headerRow, MutableAttributeSet attributes ) 1719 { 1720 Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD ); 1721 1722 if ( attributes == null ) 1723 { 1724 writeStartTag( t, null ); 1725 } 1726 else 1727 { 1728 writeStartTag( t, 1729 SinkUtils.filterAttributes( attributes, SinkUtils.SINK_TD_ATTRIBUTES ) ); 1730 } 1731 } 1732 1733 /** {@inheritDoc} */ 1734 @Override 1735 public void tableCell_() 1736 { 1737 tableCell_( false ); 1738 } 1739 1740 /** {@inheritDoc} */ 1741 @Override 1742 public void tableHeaderCell_() 1743 { 1744 tableCell_( true ); 1745 } 1746 1747 /** 1748 * Ends a table cell. 1749 * 1750 * @param headerRow true if it is an header row 1751 * @see javax.swing.text.html.HTML.Tag#TH 1752 * @see javax.swing.text.html.HTML.Tag#TD 1753 */ 1754 private void tableCell_( boolean headerRow ) 1755 { 1756 Tag t = ( headerRow ? HtmlMarkup.TH : HtmlMarkup.TD ); 1757 1758 writeEndTag( t ); 1759 1760 if ( !this.isCellJustifStack.isEmpty() && this.isCellJustifStack.getLast().equals( Boolean.TRUE ) 1761 && !this.cellCountStack.isEmpty() ) 1762 { 1763 int cellCount = Integer.parseInt( this.cellCountStack.removeLast().toString() ); 1764 this.cellCountStack.addLast( ++cellCount ); 1765 } 1766 } 1767 1768 /** 1769 * {@inheritDoc} 1770 * @see javax.swing.text.html.HTML.Tag#CAPTION 1771 */ 1772 @Override 1773 public void tableCaption() 1774 { 1775 tableCaption( null ); 1776 } 1777 1778 /** 1779 * {@inheritDoc} 1780 * @see javax.swing.text.html.HTML.Tag#CAPTION 1781 */ 1782 @Override 1783 public void tableCaption( SinkEventAttributes attributes ) 1784 { 1785 StringWriter sw = new StringWriter(); 1786 this.tableCaptionWriterStack.addLast( sw ); 1787 this.tableCaptionXMLWriterStack.addLast( new PrettyPrintXMLWriter( sw ) ); 1788 1789 // TODO: tableCaption should be written before tableRows (DOXIA-177) 1790 MutableAttributeSet atts = SinkUtils.filterAttributes( 1791 attributes, SinkUtils.SINK_SECTION_ATTRIBUTES ); 1792 1793 writeStartTag( HtmlMarkup.CAPTION, atts ); 1794 } 1795 1796 /** 1797 * {@inheritDoc} 1798 * @see javax.swing.text.html.HTML.Tag#CAPTION 1799 */ 1800 @Override 1801 public void tableCaption_() 1802 { 1803 writeEndTag( HtmlMarkup.CAPTION ); 1804 1805 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null ) 1806 { 1807 this.tableCaptionStack.addLast( this.tableCaptionWriterStack.removeLast().toString() ); 1808 this.tableCaptionXMLWriterStack.removeLast(); 1809 } 1810 } 1811 1812 /** 1813 * {@inheritDoc} 1814 * @see javax.swing.text.html.HTML.Tag#A 1815 */ 1816 @Override 1817 public void anchor( String name ) 1818 { 1819 anchor( name, null ); 1820 } 1821 1822 /** 1823 * {@inheritDoc} 1824 * @see javax.swing.text.html.HTML.Tag#A 1825 */ 1826 @Override 1827 public void anchor( String name, SinkEventAttributes attributes ) 1828 { 1829 if ( name == null ) 1830 { 1831 throw new NullPointerException( "Anchor name cannot be null!" ); 1832 } 1833 1834 if ( headFlag ) 1835 { 1836 return; 1837 } 1838 1839 MutableAttributeSet atts = SinkUtils.filterAttributes( 1840 attributes, SinkUtils.SINK_BASE_ATTRIBUTES ); 1841 1842 String id = name; 1843 1844 if ( !DoxiaUtils.isValidId( id ) ) 1845 { 1846 id = DoxiaUtils.encodeId( name, true ); 1847 1848 String msg = "Modified invalid anchor name: '" + name + "' to '" + id + "'"; 1849 logMessage( "modifiedLink", msg ); 1850 } 1851 1852 MutableAttributeSet att = new SinkEventAttributeSet(); 1853 att.addAttribute( Attribute.NAME, id ); 1854 att.addAttributes( atts ); 1855 1856 writeStartTag( HtmlMarkup.A, att ); 1857 } 1858 1859 /** 1860 * {@inheritDoc} 1861 * @see javax.swing.text.html.HTML.Tag#A 1862 */ 1863 @Override 1864 public void anchor_() 1865 { 1866 if ( !headFlag ) 1867 { 1868 writeEndTag( HtmlMarkup.A ); 1869 } 1870 } 1871 1872 /** {@inheritDoc} */ 1873 @Override 1874 public void link( String name ) 1875 { 1876 link( name, null ); 1877 } 1878 1879 /** {@inheritDoc} */ 1880 @Override 1881 public void link( String name, SinkEventAttributes attributes ) 1882 { 1883 if ( attributes == null ) 1884 { 1885 link( name, null, null ); 1886 } 1887 else 1888 { 1889 String target = (String) attributes.getAttribute( Attribute.TARGET.toString() ); 1890 MutableAttributeSet atts = SinkUtils.filterAttributes( 1891 attributes, SinkUtils.SINK_LINK_ATTRIBUTES ); 1892 1893 link( name, target, atts ); 1894 } 1895 } 1896 1897 /** 1898 * Adds a link with an optional target. 1899 * The default style class for external link is <code>externalLink</code>. 1900 * 1901 * @param href the link href. 1902 * @param target the link target, may be null. 1903 * @param attributes an AttributeSet, may be null. 1904 * This is supposed to be filtered already. 1905 * @see javax.swing.text.html.HTML.Tag#A 1906 */ 1907 private void link( String href, String target, MutableAttributeSet attributes ) 1908 { 1909 if ( href == null ) 1910 { 1911 throw new NullPointerException( "Link name cannot be null!" ); 1912 } 1913 1914 if ( headFlag ) 1915 { 1916 return; 1917 } 1918 1919 MutableAttributeSet att = new SinkEventAttributeSet(); 1920 1921 if ( DoxiaUtils.isExternalLink( href ) ) 1922 { 1923 att.addAttribute( Attribute.CLASS, "externalLink" ); 1924 } 1925 1926 att.addAttribute( Attribute.HREF, HtmlTools.escapeHTML( href ) ); 1927 1928 if ( target != null ) 1929 { 1930 att.addAttribute( Attribute.TARGET, target ); 1931 } 1932 1933 if ( attributes != null ) 1934 { 1935 attributes.removeAttribute( Attribute.HREF.toString() ); 1936 attributes.removeAttribute( Attribute.TARGET.toString() ); 1937 att.addAttributes( attributes ); 1938 } 1939 1940 writeStartTag( HtmlMarkup.A, att ); 1941 } 1942 1943 /** 1944 * {@inheritDoc} 1945 * @see javax.swing.text.html.HTML.Tag#A 1946 */ 1947 @Override 1948 public void link_() 1949 { 1950 if ( !headFlag ) 1951 { 1952 writeEndTag( HtmlMarkup.A ); 1953 } 1954 } 1955 1956 /** {@inheritDoc} */ 1957 @Override 1958 public void inline() 1959 { 1960 inline( null ); 1961 } 1962 1963 private void inlineSemantics( SinkEventAttributes attributes, String semantic, 1964 List<Tag> tags, Tag tag ) 1965 { 1966 if ( attributes.containsAttribute( SinkEventAttributes.SEMANTICS, semantic ) ) 1967 { 1968 writeStartTag( tag ); 1969 tags.add( 0, tag ); 1970 } 1971 } 1972 1973 /** {@inheritDoc} */ 1974 @Override 1975 public void inline( SinkEventAttributes attributes ) 1976 { 1977 if ( !headFlag ) 1978 { 1979 List<Tag> tags = new ArrayList<>(); 1980 1981 if ( attributes != null ) 1982 { 1983 inlineSemantics( attributes, "emphasis", tags, HtmlMarkup.EM ); 1984 inlineSemantics( attributes, "strong", tags, HtmlMarkup.STRONG ); 1985 inlineSemantics( attributes, "small", tags, HtmlMarkup.SMALL ); 1986 inlineSemantics( attributes, "line-through", tags, HtmlMarkup.S ); 1987 inlineSemantics( attributes, "citation", tags, HtmlMarkup.CITE ); 1988 inlineSemantics( attributes, "quote", tags, HtmlMarkup.Q ); 1989 inlineSemantics( attributes, "definition", tags, HtmlMarkup.DFN ); 1990 inlineSemantics( attributes, "abbreviation", tags, HtmlMarkup.ABBR ); 1991 inlineSemantics( attributes, "italic", tags, HtmlMarkup.I ); 1992 inlineSemantics( attributes, "bold", tags, HtmlMarkup.B ); 1993 inlineSemantics( attributes, "code", tags, HtmlMarkup.CODE ); 1994 inlineSemantics( attributes, "variable", tags, HtmlMarkup.VAR ); 1995 inlineSemantics( attributes, "sample", tags, HtmlMarkup.SAMP ); 1996 inlineSemantics( attributes, "keyboard", tags, HtmlMarkup.KBD ); 1997 inlineSemantics( attributes, "superscript", tags, HtmlMarkup.SUP ); 1998 inlineSemantics( attributes, "subscript", tags, HtmlMarkup.SUB ); 1999 inlineSemantics( attributes, "annotation", tags, HtmlMarkup.U ); 2000 inlineSemantics( attributes, "highlight", tags, HtmlMarkup.MARK ); 2001 inlineSemantics( attributes, "ruby", tags, HtmlMarkup.RUBY ); 2002 inlineSemantics( attributes, "rubyBase", tags, HtmlMarkup.RB ); 2003 inlineSemantics( attributes, "rubyText", tags, HtmlMarkup.RT ); 2004 inlineSemantics( attributes, "rubyTextContainer", tags, HtmlMarkup.RTC ); 2005 inlineSemantics( attributes, "rubyParentheses", tags, HtmlMarkup.RP ); 2006 inlineSemantics( attributes, "bidirectionalIsolation", tags, HtmlMarkup.BDI ); 2007 inlineSemantics( attributes, "bidirectionalOverride", tags, HtmlMarkup.BDO ); 2008 inlineSemantics( attributes, "phrase", tags, HtmlMarkup.SPAN ); 2009 inlineSemantics( attributes, "insert", tags, HtmlMarkup.INS ); 2010 inlineSemantics( attributes, "delete", tags, HtmlMarkup.DEL ); 2011 } 2012 2013 inlineStack.push( tags ); 2014 } 2015 } 2016 2017 /** {@inheritDoc} */ 2018 @Override 2019 public void inline_() 2020 { 2021 if ( !headFlag ) 2022 { 2023 for ( Tag tag: inlineStack.pop() ) 2024 { 2025 writeEndTag( tag ); 2026 } 2027 } 2028 } 2029 2030 /** 2031 * {@inheritDoc} 2032 * @see javax.swing.text.html.HTML.Tag#I 2033 */ 2034 @Override 2035 public void italic() 2036 { 2037 inline( SinkEventAttributeSet.Semantics.ITALIC ); 2038 } 2039 2040 /** 2041 * {@inheritDoc} 2042 * @see javax.swing.text.html.HTML.Tag#I 2043 */ 2044 @Override 2045 public void italic_() 2046 { 2047 inline_(); 2048 } 2049 2050 /** 2051 * {@inheritDoc} 2052 * @see javax.swing.text.html.HTML.Tag#B 2053 */ 2054 @Override 2055 public void bold() 2056 { 2057 inline( SinkEventAttributeSet.Semantics.BOLD ); 2058 } 2059 2060 /** 2061 * {@inheritDoc} 2062 * @see javax.swing.text.html.HTML.Tag#B 2063 */ 2064 @Override 2065 public void bold_() 2066 { 2067 inline_(); 2068 } 2069 2070 /** 2071 * {@inheritDoc} 2072 * @see javax.swing.text.html.HTML.Tag#CODE 2073 */ 2074 @Override 2075 public void monospaced() 2076 { 2077 inline( SinkEventAttributeSet.Semantics.CODE ); 2078 } 2079 2080 /** 2081 * {@inheritDoc} 2082 * @see javax.swing.text.html.HTML.Tag#CODE 2083 */ 2084 @Override 2085 public void monospaced_() 2086 { 2087 inline_(); 2088 } 2089 2090 /** 2091 * {@inheritDoc} 2092 * @see javax.swing.text.html.HTML.Tag#BR 2093 */ 2094 @Override 2095 public void lineBreak() 2096 { 2097 lineBreak( null ); 2098 } 2099 2100 /** 2101 * {@inheritDoc} 2102 * @see javax.swing.text.html.HTML.Tag#BR 2103 */ 2104 @Override 2105 public void lineBreak( SinkEventAttributes attributes ) 2106 { 2107 if ( headFlag || isVerbatimFlag() ) 2108 { 2109 getTextBuffer().append( EOL ); 2110 } 2111 else 2112 { 2113 MutableAttributeSet atts = SinkUtils.filterAttributes( 2114 attributes, SinkUtils.SINK_BR_ATTRIBUTES ); 2115 2116 writeSimpleTag( HtmlMarkup.BR, atts ); 2117 } 2118 } 2119 2120 /** 2121 * {@inheritDoc} 2122 * @see javax.swing.text.html.HTML.Tag#WBR 2123 */ 2124 @Override 2125 public void lineBreakOpportunity() 2126 { 2127 lineBreakOpportunity( null ); 2128 } 2129 2130 /** 2131 * {@inheritDoc} 2132 * @see javax.swing.text.html.HTML.Tag#WBR 2133 */ 2134 @Override 2135 public void lineBreakOpportunity( SinkEventAttributes attributes ) 2136 { 2137 if ( !headFlag && !isVerbatimFlag() ) 2138 { 2139 MutableAttributeSet atts = SinkUtils.filterAttributes( 2140 attributes, SinkUtils.SINK_BR_ATTRIBUTES ); 2141 2142 writeSimpleTag( HtmlMarkup.WBR, atts ); 2143 } 2144 } 2145 2146 /** {@inheritDoc} */ 2147 @Override 2148 public void pageBreak() 2149 { 2150 comment( " PB " ); 2151 } 2152 2153 /** {@inheritDoc} */ 2154 @Override 2155 public void nonBreakingSpace() 2156 { 2157 if ( headFlag ) 2158 { 2159 getTextBuffer().append( ' ' ); 2160 } 2161 else 2162 { 2163 write( " " ); 2164 } 2165 } 2166 2167 /** {@inheritDoc} */ 2168 @Override 2169 public void text( String text ) 2170 { 2171 if ( headFlag ) 2172 { 2173 getTextBuffer().append( text ); 2174 } 2175 else if ( verbatimFlag ) 2176 { 2177 verbatimContent( text ); 2178 } 2179 else 2180 { 2181 content( text ); 2182 } 2183 } 2184 2185 /** {@inheritDoc} */ 2186 @Override 2187 public void text( String text, SinkEventAttributes attributes ) 2188 { 2189 text( text ); 2190 } 2191 2192 /** {@inheritDoc} */ 2193 @Override 2194 public void rawText( String text ) 2195 { 2196 if ( headFlag ) 2197 { 2198 getTextBuffer().append( text ); 2199 } 2200 else 2201 { 2202 write( text ); 2203 } 2204 } 2205 2206 /** {@inheritDoc} */ 2207 @Override 2208 public void comment( String comment ) 2209 { 2210 if ( comment != null ) 2211 { 2212 final String originalComment = comment; 2213 2214 // http://www.w3.org/TR/2000/REC-xml-20001006#sec-comments 2215 while ( comment.contains( "--" ) ) 2216 { 2217 comment = comment.replace( "--", "- -" ); 2218 } 2219 2220 if ( comment.endsWith( "-" ) ) 2221 { 2222 comment += " "; 2223 } 2224 2225 if ( !originalComment.equals( comment ) ) 2226 { 2227 getLog().warn( "[Xhtml5 Sink] Modified invalid comment '" + originalComment 2228 + "' to '" + comment + "'" ); 2229 } 2230 2231 final StringBuilder buffer = new StringBuilder( comment.length() + 7 ); 2232 2233 buffer.append( LESS_THAN ).append( BANG ).append( MINUS ).append( MINUS ); 2234 buffer.append( comment ); 2235 buffer.append( MINUS ).append( MINUS ).append( GREATER_THAN ); 2236 2237 write( buffer.toString() ); 2238 } 2239 } 2240 2241 /** 2242 * Add an unknown event. 2243 * This can be used to generate html tags for which no corresponding sink event exists. 2244 * 2245 * <p> 2246 * If {@link org.apache.maven.doxia.util.HtmlTools#getHtmlTag(String) HtmlTools.getHtmlTag( name )} 2247 * does not return null, the corresponding tag will be written. 2248 * </p> 2249 * 2250 * <p>For example, the div block</p> 2251 * 2252 * <pre> 2253 * <div class="detail" style="display:inline">text</div> 2254 * </pre> 2255 * 2256 * <p>can be generated via the following event sequence:</p> 2257 * 2258 * <pre> 2259 * SinkEventAttributeSet atts = new SinkEventAttributeSet(); 2260 * atts.addAttribute( SinkEventAttributes.CLASS, "detail" ); 2261 * atts.addAttribute( SinkEventAttributes.STYLE, "display:inline" ); 2262 * sink.unknown( "div", new Object[]{new Integer( HtmlMarkup.TAG_TYPE_START )}, atts ); 2263 * sink.text( "text" ); 2264 * sink.unknown( "div", new Object[]{new Integer( HtmlMarkup.TAG_TYPE_END )}, null ); 2265 * </pre> 2266 * 2267 * @param name the name of the event. If this is not a valid xhtml tag name 2268 * as defined in {@link org.apache.maven.doxia.markup.HtmlMarkup} then the event is ignored. 2269 * @param requiredParams If this is null or the first argument is not an Integer then the event is ignored. 2270 * The first argument should indicate the type of the unknown event, its integer value should be one of 2271 * {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_START TAG_TYPE_START}, 2272 * {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_END TAG_TYPE_END}, 2273 * {@link org.apache.maven.doxia.markup.HtmlMarkup#TAG_TYPE_SIMPLE TAG_TYPE_SIMPLE}, 2274 * {@link org.apache.maven.doxia.markup.HtmlMarkup#ENTITY_TYPE ENTITY_TYPE}, or 2275 * {@link org.apache.maven.doxia.markup.HtmlMarkup#CDATA_TYPE CDATA_TYPE}, 2276 * otherwise the event will be ignored. 2277 * @param attributes a set of attributes for the event. May be null. 2278 * The attributes will always be written, no validity check is performed. 2279 */ 2280 @Override 2281 public void unknown( String name, Object[] requiredParams, SinkEventAttributes attributes ) 2282 { 2283 if ( requiredParams == null || !( requiredParams[0] instanceof Integer ) ) 2284 { 2285 String msg = "No type information for unknown event: '" + name + "', ignoring!"; 2286 logMessage( "noTypeInfo", msg ); 2287 2288 return; 2289 } 2290 2291 int tagType = (Integer) requiredParams[0]; 2292 2293 if ( tagType == ENTITY_TYPE ) 2294 { 2295 rawText( name ); 2296 2297 return; 2298 } 2299 2300 if ( tagType == CDATA_TYPE ) 2301 { 2302 rawText( EOL + "//<![CDATA[" + requiredParams[1] + "]]>" + EOL ); 2303 2304 return; 2305 } 2306 2307 Tag tag = HtmlTools.getHtmlTag( name ); 2308 2309 if ( tag == null ) 2310 { 2311 String msg = "No HTML tag found for unknown event: '" + name + "', ignoring!"; 2312 logMessage( "noHtmlTag", msg ); 2313 } 2314 else 2315 { 2316 if ( tagType == TAG_TYPE_SIMPLE ) 2317 { 2318 writeSimpleTag( tag, escapeAttributeValues( attributes ) ); 2319 } 2320 else if ( tagType == TAG_TYPE_START ) 2321 { 2322 writeStartTag( tag, escapeAttributeValues( attributes ) ); 2323 } 2324 else if ( tagType == TAG_TYPE_END ) 2325 { 2326 writeEndTag( tag ); 2327 } 2328 else 2329 { 2330 String msg = "No type information for unknown event: '" + name + "', ignoring!"; 2331 logMessage( "noTypeInfo", msg ); 2332 } 2333 } 2334 } 2335 2336 private SinkEventAttributes escapeAttributeValues( SinkEventAttributes attributes ) 2337 { 2338 SinkEventAttributeSet set = new SinkEventAttributeSet( attributes.getAttributeCount() ); 2339 2340 Enumeration<?> names = attributes.getAttributeNames(); 2341 2342 while ( names.hasMoreElements() ) 2343 { 2344 Object name = names.nextElement(); 2345 2346 set.addAttribute( name, escapeHTML( attributes.getAttribute( name ).toString() ) ); 2347 } 2348 2349 return set; 2350 } 2351 2352 /** {@inheritDoc} */ 2353 @Override 2354 public void flush() 2355 { 2356 writer.flush(); 2357 } 2358 2359 /** {@inheritDoc} */ 2360 @Override 2361 public void close() 2362 { 2363 writer.close(); 2364 2365 if ( getLog().isWarnEnabled() && this.warnMessages != null ) 2366 { 2367 for ( Map.Entry<String, Set<String>> entry : this.warnMessages.entrySet() ) 2368 { 2369 for ( String msg : entry.getValue() ) 2370 { 2371 getLog().warn( msg ); 2372 } 2373 } 2374 2375 this.warnMessages = null; 2376 } 2377 2378 init(); 2379 } 2380 2381 // ---------------------------------------------------------------------- 2382 // 2383 // ---------------------------------------------------------------------- 2384 2385 /** 2386 * Write HTML escaped text to output. 2387 * 2388 * @param text The text to write. 2389 */ 2390 protected void content( String text ) 2391 { 2392 // small hack due to DOXIA-314 2393 String txt = escapeHTML( text ); 2394 txt = StringUtils.replace( txt, "&#", "&#" ); 2395 write( txt ); 2396 } 2397 2398 /** 2399 * Write HTML escaped text to output. 2400 * 2401 * @param text The text to write. 2402 */ 2403 protected void verbatimContent( String text ) 2404 { 2405 write( escapeHTML( text ) ); 2406 } 2407 2408 /** 2409 * Forward to HtmlTools.escapeHTML( text ). 2410 * 2411 * @param text the String to escape, may be null 2412 * @return the text escaped, "" if null String input 2413 * @see org.apache.maven.doxia.util.HtmlTools#escapeHTML(String) 2414 */ 2415 protected static String escapeHTML( String text ) 2416 { 2417 return HtmlTools.escapeHTML( text, false ); 2418 } 2419 2420 /** 2421 * Forward to HtmlTools.encodeURL( text ). 2422 * 2423 * @param text the String to encode, may be null. 2424 * @return the text encoded, null if null String input. 2425 * @see org.apache.maven.doxia.util.HtmlTools#encodeURL(String) 2426 */ 2427 protected static String encodeURL( String text ) 2428 { 2429 return HtmlTools.encodeURL( text ); 2430 } 2431 2432 /** {@inheritDoc} */ 2433 protected void write( String text ) 2434 { 2435 if ( !this.tableCaptionXMLWriterStack.isEmpty() && this.tableCaptionXMLWriterStack.getLast() != null ) 2436 { 2437 this.tableCaptionXMLWriterStack.getLast().writeMarkup( unifyEOLs( text ) ); 2438 } 2439 else if ( !this.tableContentWriterStack.isEmpty() && this.tableContentWriterStack.getLast() != null ) 2440 { 2441 this.tableContentWriterStack.getLast().write( unifyEOLs( text ) ); 2442 } 2443 else 2444 { 2445 writer.write( unifyEOLs( text ) ); 2446 } 2447 } 2448 2449 /** {@inheritDoc} */ 2450 @Override 2451 protected void writeStartTag( Tag t, MutableAttributeSet att, boolean isSimpleTag ) 2452 { 2453 if ( this.tableCaptionXMLWriterStack.isEmpty() ) 2454 { 2455 super.writeStartTag ( t, att, isSimpleTag ); 2456 } 2457 else 2458 { 2459 String tag = ( getNameSpace() != null ? getNameSpace() + ":" : "" ) + t.toString(); 2460 this.tableCaptionXMLWriterStack.getLast().startElement( tag ); 2461 2462 if ( att != null ) 2463 { 2464 Enumeration<?> names = att.getAttributeNames(); 2465 while ( names.hasMoreElements() ) 2466 { 2467 Object key = names.nextElement(); 2468 Object value = att.getAttribute( key ); 2469 2470 this.tableCaptionXMLWriterStack.getLast().addAttribute( key.toString(), value.toString() ); 2471 } 2472 } 2473 2474 if ( isSimpleTag ) 2475 { 2476 this.tableCaptionXMLWriterStack.getLast().endElement(); 2477 } 2478 } 2479 } 2480 2481 /** {@inheritDoc} */ 2482 @Override 2483 protected void writeEndTag( Tag t ) 2484 { 2485 if ( this.tableCaptionXMLWriterStack.isEmpty() ) 2486 { 2487 super.writeEndTag( t ); 2488 } 2489 else 2490 { 2491 this.tableCaptionXMLWriterStack.getLast().endElement(); 2492 } 2493 } 2494 2495 /** 2496 * If debug mode is enabled, log the <code>msg</code> as is, otherwise add unique msg in <code>warnMessages</code>. 2497 * 2498 * @param key not null 2499 * @param msg not null 2500 * @see #close() 2501 * @since 1.1.1 2502 */ 2503 private void logMessage( String key, String msg ) 2504 { 2505 final String mesg = "[XHTML5 Sink] " + msg; 2506 if ( getLog().isDebugEnabled() ) 2507 { 2508 getLog().debug( mesg ); 2509 2510 return; 2511 } 2512 2513 if ( warnMessages == null ) 2514 { 2515 warnMessages = new HashMap<>(); 2516 } 2517 2518 Set<String> set = warnMessages.get( key ); 2519 if ( set == null ) 2520 { 2521 set = new TreeSet<>(); 2522 } 2523 set.add( mesg ); 2524 warnMessages.put( key, set ); 2525 } 2526}