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