HtmlTree.java revision 3828:d30434bde0a8
1/* 2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.javadoc.internal.doclets.formats.html.markup; 27 28import java.io.IOException; 29import java.io.Writer; 30import java.nio.charset.Charset; 31import java.util.ArrayList; 32import java.util.BitSet; 33import java.util.Collections; 34import java.util.Iterator; 35import java.util.LinkedHashMap; 36import java.util.List; 37import java.util.Map; 38 39import jdk.javadoc.internal.doclets.formats.html.markup.HtmlAttr.Role; 40import jdk.javadoc.internal.doclets.toolkit.Content; 41import jdk.javadoc.internal.doclets.toolkit.util.DocletConstants; 42 43/** 44 * Class for generating HTML tree for javadoc output. 45 * 46 * <p><b>This is NOT part of any supported API. 47 * If you write code that depends on this, you do so at your own risk. 48 * This code and its internal interfaces are subject to change or 49 * deletion without notice.</b> 50 * 51 * @author Bhavesh Patel 52 */ 53public class HtmlTree extends Content { 54 55 private HtmlTag htmlTag; 56 private Map<HtmlAttr,String> attrs = Collections.emptyMap(); 57 private List<Content> content = Collections.emptyList(); 58 public static final Content EMPTY = new StringContent(""); 59 60 /** 61 * Constructor to construct HtmlTree object. 62 * 63 * @param tag HTML tag for the HtmlTree object 64 */ 65 public HtmlTree(HtmlTag tag) { 66 htmlTag = nullCheck(tag); 67 } 68 69 /** 70 * Constructor to construct HtmlTree object. 71 * 72 * @param tag HTML tag for the HtmlTree object 73 * @param contents contents to be added to the tree 74 */ 75 public HtmlTree(HtmlTag tag, Content... contents) { 76 this(tag); 77 for (Content content: contents) 78 addContent(content); 79 } 80 81 /** 82 * Adds an attribute for the HTML tag. 83 * 84 * @param attrName name of the attribute 85 * @param attrValue value of the attribute 86 */ 87 public void addAttr(HtmlAttr attrName, String attrValue) { 88 if (attrs.isEmpty()) 89 attrs = new LinkedHashMap<>(3); 90 attrs.put(nullCheck(attrName), escapeHtmlChars(attrValue)); 91 } 92 93 public void setTitle(Content body) { 94 addAttr(HtmlAttr.TITLE, stripHtml(body)); 95 } 96 97 public void setRole(Role role) { 98 addAttr(HtmlAttr.ROLE, role.toString()); 99 } 100 101 /** 102 * Adds a style for the HTML tag. 103 * 104 * @param style style to be added 105 */ 106 public void addStyle(HtmlStyle style) { 107 addAttr(HtmlAttr.CLASS, style.toString()); 108 } 109 110 /** 111 * Adds content for the HTML tag. 112 * 113 * @param tagContent tag content to be added 114 */ 115 public void addContent(Content tagContent) { 116 if (tagContent instanceof ContentBuilder) { 117 for (Content content: ((ContentBuilder)tagContent).contents) { 118 addContent(content); 119 } 120 } 121 else if (tagContent == HtmlTree.EMPTY || tagContent.isValid()) { 122 if (content.isEmpty()) 123 content = new ArrayList<>(); 124 content.add(tagContent); 125 } 126 } 127 128 /** 129 * This method adds a string content to the htmltree. If the last content member 130 * added is a StringContent, append the string to that StringContent or else 131 * create a new StringContent and add it to the html tree. 132 * 133 * @param stringContent string content that needs to be added 134 */ 135 @Override 136 public void addContent(CharSequence stringContent) { 137 if (!content.isEmpty()) { 138 Content lastContent = content.get(content.size() - 1); 139 if (lastContent instanceof StringContent) 140 lastContent.addContent(stringContent); 141 else 142 addContent(new StringContent(stringContent)); 143 } 144 else 145 addContent(new StringContent(stringContent)); 146 } 147 148 public int charCount() { 149 int n = 0; 150 for (Content c : content) 151 n += c.charCount(); 152 return n; 153 } 154 155 /** 156 * Given a string, escape all special html characters and 157 * return the result. 158 * 159 * @param s The string to check. 160 * @return the original string with all of the HTML characters escaped. 161 */ 162 private static String escapeHtmlChars(String s) { 163 for (int i = 0; i < s.length(); i++) { 164 char ch = s.charAt(i); 165 switch (ch) { 166 // only start building a new string if we need to 167 case '<': case '>': case '&': 168 StringBuilder sb = new StringBuilder(s.substring(0, i)); 169 for ( ; i < s.length(); i++) { 170 ch = s.charAt(i); 171 switch (ch) { 172 case '<': sb.append("<"); break; 173 case '>': sb.append(">"); break; 174 case '&': sb.append("&"); break; 175 default: sb.append(ch); break; 176 } 177 } 178 return sb.toString(); 179 } 180 } 181 return s; 182 } 183 184 /** 185 * A set of ASCII URI characters to be left unencoded. 186 */ 187 public static final BitSet NONENCODING_CHARS = new BitSet(256); 188 189 static { 190 // alphabetic characters 191 for (int i = 'a'; i <= 'z'; i++) { 192 NONENCODING_CHARS.set(i); 193 } 194 for (int i = 'A'; i <= 'Z'; i++) { 195 NONENCODING_CHARS.set(i); 196 } 197 // numeric characters 198 for (int i = '0'; i <= '9'; i++) { 199 NONENCODING_CHARS.set(i); 200 } 201 // Reserved characters as per RFC 3986. These are set of delimiting characters. 202 String noEnc = ":/?#[]@!$&'()*+,;="; 203 // Unreserved characters as per RFC 3986 which should not be percent encoded. 204 noEnc += "-._~"; 205 for (int i = 0; i < noEnc.length(); i++) { 206 NONENCODING_CHARS.set(noEnc.charAt(i)); 207 } 208 } 209 210 private static String encodeURL(String url) { 211 StringBuilder sb = new StringBuilder(); 212 for (byte c : url.getBytes(Charset.forName("UTF-8"))) { 213 if (NONENCODING_CHARS.get(c & 0xFF)) { 214 sb.append((char) c); 215 } else { 216 sb.append(String.format("%%%02X", c & 0xFF)); 217 } 218 } 219 return sb.toString(); 220 } 221 222 /** 223 * Generates an HTML anchor tag. 224 * 225 * @param ref reference url for the anchor tag 226 * @param body content for the anchor tag 227 * @return an HtmlTree object 228 */ 229 public static HtmlTree A(String ref, Content body) { 230 HtmlTree htmltree = new HtmlTree(HtmlTag.A, nullCheck(body)); 231 htmltree.addAttr(HtmlAttr.HREF, encodeURL(ref)); 232 return htmltree; 233 } 234 235 /** 236 * Generates an HTML anchor tag with an id or a name attribute and content. 237 * 238 * @param htmlVersion the version of the generated HTML 239 * @param attr name or id attribute for the anchor tag 240 * @param body content for the anchor tag 241 * @return an HtmlTree object 242 */ 243 public static HtmlTree A(HtmlVersion htmlVersion, String attr, Content body) { 244 HtmlTree htmltree = new HtmlTree(HtmlTag.A); 245 htmltree.addAttr((htmlVersion == HtmlVersion.HTML4) 246 ? HtmlAttr.NAME 247 : HtmlAttr.ID, 248 nullCheck(attr)); 249 htmltree.addContent(nullCheck(body)); 250 return htmltree; 251 } 252 253 /** 254 * Generates an HTML anchor tag with id attribute and a body. 255 * 256 * @param id id for the anchor tag 257 * @param body body for the anchor tag 258 * @return an HtmlTree object 259 */ 260 public static HtmlTree A_ID(String id, Content body) { 261 HtmlTree htmltree = new HtmlTree(HtmlTag.A); 262 htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); 263 htmltree.addContent(nullCheck(body)); 264 return htmltree; 265 } 266 267 /** 268 * Generates a CAPTION tag with some content. 269 * 270 * @param body content for the tag 271 * @return an HtmlTree object for the CAPTION tag 272 */ 273 public static HtmlTree CAPTION(Content body) { 274 HtmlTree htmltree = new HtmlTree(HtmlTag.CAPTION, nullCheck(body)); 275 return htmltree; 276 } 277 278 /** 279 * Generates a CODE tag with some content. 280 * 281 * @param body content for the tag 282 * @return an HtmlTree object for the CODE tag 283 */ 284 public static HtmlTree CODE(Content body) { 285 HtmlTree htmltree = new HtmlTree(HtmlTag.CODE, nullCheck(body)); 286 return htmltree; 287 } 288 289 /** 290 * Generates a DD tag with some content. 291 * 292 * @param body content for the tag 293 * @return an HtmlTree object for the DD tag 294 */ 295 public static HtmlTree DD(Content body) { 296 HtmlTree htmltree = new HtmlTree(HtmlTag.DD, nullCheck(body)); 297 return htmltree; 298 } 299 300 /** 301 * Generates a DL tag with some content. 302 * 303 * @param body content for the tag 304 * @return an HtmlTree object for the DL tag 305 */ 306 public static HtmlTree DL(Content body) { 307 HtmlTree htmltree = new HtmlTree(HtmlTag.DL, nullCheck(body)); 308 return htmltree; 309 } 310 311 /** 312 * Generates a DIV tag with the style class attributes. It also encloses 313 * a content. 314 * 315 * @param styleClass stylesheet class for the tag 316 * @param body content for the tag 317 * @return an HtmlTree object for the DIV tag 318 */ 319 public static HtmlTree DIV(HtmlStyle styleClass, Content body) { 320 HtmlTree htmltree = new HtmlTree(HtmlTag.DIV, nullCheck(body)); 321 if (styleClass != null) 322 htmltree.addStyle(styleClass); 323 return htmltree; 324 } 325 326 /** 327 * Generates a DIV tag with some content. 328 * 329 * @param body content for the tag 330 * @return an HtmlTree object for the DIV tag 331 */ 332 public static HtmlTree DIV(Content body) { 333 return DIV(null, body); 334 } 335 336 /** 337 * Generates a DT tag with some content. 338 * 339 * @param body content for the tag 340 * @return an HtmlTree object for the DT tag 341 */ 342 public static HtmlTree DT(Content body) { 343 HtmlTree htmltree = new HtmlTree(HtmlTag.DT, nullCheck(body)); 344 return htmltree; 345 } 346 347 /** 348 * Generates a FOOTER tag with role attribute. 349 * 350 * @return an HtmlTree object for the FOOTER tag 351 */ 352 public static HtmlTree FOOTER() { 353 HtmlTree htmltree = new HtmlTree(HtmlTag.FOOTER); 354 htmltree.setRole(Role.CONTENTINFO); 355 return htmltree; 356 } 357 358 /** 359 * Generates a HEADER tag with role attribute. 360 * 361 * @return an HtmlTree object for the HEADER tag 362 */ 363 public static HtmlTree HEADER() { 364 HtmlTree htmltree = new HtmlTree(HtmlTag.HEADER); 365 htmltree.setRole(Role.BANNER); 366 return htmltree; 367 } 368 369 /** 370 * Generates a heading tag (h1 to h6) with the title and style class attributes. It also encloses 371 * a content. 372 * 373 * @param headingTag the heading tag to be generated 374 * @param printTitle true if title for the tag needs to be printed else false 375 * @param styleClass stylesheet class for the tag 376 * @param body content for the tag 377 * @return an HtmlTree object for the tag 378 */ 379 public static HtmlTree HEADING(HtmlTag headingTag, boolean printTitle, 380 HtmlStyle styleClass, Content body) { 381 HtmlTree htmltree = new HtmlTree(headingTag, nullCheck(body)); 382 if (printTitle) 383 htmltree.setTitle(body); 384 if (styleClass != null) 385 htmltree.addStyle(styleClass); 386 return htmltree; 387 } 388 389 /** 390 * Generates a heading tag (h1 to h6) with style class attribute. It also encloses 391 * a content. 392 * 393 * @param headingTag the heading tag to be generated 394 * @param styleClass stylesheet class for the tag 395 * @param body content for the tag 396 * @return an HtmlTree object for the tag 397 */ 398 public static HtmlTree HEADING(HtmlTag headingTag, HtmlStyle styleClass, Content body) { 399 return HEADING(headingTag, false, styleClass, body); 400 } 401 402 /** 403 * Generates a heading tag (h1 to h6) with the title attribute. It also encloses 404 * a content. 405 * 406 * @param headingTag the heading tag to be generated 407 * @param printTitle true if the title for the tag needs to be printed else false 408 * @param body content for the tag 409 * @return an HtmlTree object for the tag 410 */ 411 public static HtmlTree HEADING(HtmlTag headingTag, boolean printTitle, Content body) { 412 return HEADING(headingTag, printTitle, null, body); 413 } 414 415 /** 416 * Generates a heading tag (h1 to h6) with some content. 417 * 418 * @param headingTag the heading tag to be generated 419 * @param body content for the tag 420 * @return an HtmlTree object for the tag 421 */ 422 public static HtmlTree HEADING(HtmlTag headingTag, Content body) { 423 return HEADING(headingTag, false, null, body); 424 } 425 426 /** 427 * Generates an HTML tag with lang attribute. It also adds head and body 428 * content to the HTML tree. 429 * 430 * @param lang language for the HTML document 431 * @param head head for the HTML tag 432 * @param body body for the HTML tag 433 * @return an HtmlTree object for the HTML tag 434 */ 435 public static HtmlTree HTML(String lang, Content head, Content body) { 436 HtmlTree htmltree = new HtmlTree(HtmlTag.HTML, nullCheck(head), nullCheck(body)); 437 htmltree.addAttr(HtmlAttr.LANG, nullCheck(lang)); 438 return htmltree; 439 } 440 441 /** 442 * Generates a IFRAME tag. 443 * 444 * @param src the url of the document to be shown in the frame 445 * @param name specifies the name of the frame 446 * @param title the title for the frame 447 * @return an HtmlTree object for the IFRAME tag 448 */ 449 public static HtmlTree IFRAME(String src, String name, String title) { 450 HtmlTree htmltree = new HtmlTree(HtmlTag.IFRAME); 451 htmltree.addAttr(HtmlAttr.SRC, nullCheck(src)); 452 htmltree.addAttr(HtmlAttr.NAME, nullCheck(name)); 453 htmltree.addAttr(HtmlAttr.TITLE, nullCheck(title)); 454 return htmltree; 455 } 456 457 /** 458 * Generates a INPUT tag with some id. 459 * 460 * @param type the type of input 461 * @param id id for the tag 462 * @return an HtmlTree object for the INPUT tag 463 */ 464 public static HtmlTree INPUT(String type, String id) { 465 HtmlTree htmltree = new HtmlTree(HtmlTag.INPUT); 466 htmltree.addAttr(HtmlAttr.TYPE, nullCheck(type)); 467 htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); 468 htmltree.addAttr(HtmlAttr.VALUE, " "); 469 htmltree.addAttr(HtmlAttr.DISABLED, "disabled"); 470 return htmltree; 471 } 472 473 /** 474 * Generates a LI tag with some content. 475 * 476 * @param body content for the tag 477 * @return an HtmlTree object for the LI tag 478 */ 479 public static HtmlTree LI(Content body) { 480 return LI(null, body); 481 } 482 483 /** 484 * Generates a LI tag with some content. 485 * 486 * @param styleClass style for the tag 487 * @param body content for the tag 488 * @return an HtmlTree object for the LI tag 489 */ 490 public static HtmlTree LI(HtmlStyle styleClass, Content body) { 491 HtmlTree htmltree = new HtmlTree(HtmlTag.LI, nullCheck(body)); 492 if (styleClass != null) 493 htmltree.addStyle(styleClass); 494 return htmltree; 495 } 496 497 /** 498 * Generates a LINK tag with the rel, type, href and title attributes. 499 * 500 * @param rel relevance of the link 501 * @param type type of link 502 * @param href the path for the link 503 * @param title title for the link 504 * @return an HtmlTree object for the LINK tag 505 */ 506 public static HtmlTree LINK(String rel, String type, String href, String title) { 507 HtmlTree htmltree = new HtmlTree(HtmlTag.LINK); 508 htmltree.addAttr(HtmlAttr.REL, nullCheck(rel)); 509 htmltree.addAttr(HtmlAttr.TYPE, nullCheck(type)); 510 htmltree.addAttr(HtmlAttr.HREF, nullCheck(href)); 511 htmltree.addAttr(HtmlAttr.TITLE, nullCheck(title)); 512 return htmltree; 513 } 514 515 /** 516 * Generates a MAIN tag with role attribute. 517 * 518 * @return an HtmlTree object for the MAIN tag 519 */ 520 public static HtmlTree MAIN() { 521 HtmlTree htmltree = new HtmlTree(HtmlTag.MAIN); 522 htmltree.setRole(Role.MAIN); 523 return htmltree; 524 } 525 526 /** 527 * Generates a MAIN tag with role attribute and some content. 528 * 529 * @param body content of the MAIN tag 530 * @return an HtmlTree object for the MAIN tag 531 */ 532 public static HtmlTree MAIN(Content body) { 533 HtmlTree htmltree = new HtmlTree(HtmlTag.MAIN, nullCheck(body)); 534 htmltree.setRole(Role.MAIN); 535 return htmltree; 536 } 537 538 /** 539 * Generates a MAIN tag with role attribute, style attribute and some content. 540 * 541 * @param styleClass style of the MAIN tag 542 * @param body content of the MAIN tag 543 * @return an HtmlTree object for the MAIN tag 544 */ 545 public static HtmlTree MAIN(HtmlStyle styleClass, Content body) { 546 HtmlTree htmltree = HtmlTree.MAIN(body); 547 if (styleClass != null) { 548 htmltree.addStyle(styleClass); 549 } 550 return htmltree; 551 } 552 553 /** 554 * Generates a META tag with the http-equiv, content and charset attributes. 555 * 556 * @param httpEquiv http equiv attribute for the META tag 557 * @param content type of content 558 * @param charSet character set used 559 * @return an HtmlTree object for the META tag 560 */ 561 public static HtmlTree META(String httpEquiv, String content, String charSet) { 562 HtmlTree htmltree = new HtmlTree(HtmlTag.META); 563 String contentCharset = content + "; charset=" + charSet; 564 htmltree.addAttr(HtmlAttr.HTTP_EQUIV, nullCheck(httpEquiv)); 565 htmltree.addAttr(HtmlAttr.CONTENT, contentCharset); 566 return htmltree; 567 } 568 569 /** 570 * Generates a META tag with the name and content attributes. 571 * 572 * @param name name attribute 573 * @param content type of content 574 * @return an HtmlTree object for the META tag 575 */ 576 public static HtmlTree META(String name, String content) { 577 HtmlTree htmltree = new HtmlTree(HtmlTag.META); 578 htmltree.addAttr(HtmlAttr.NAME, nullCheck(name)); 579 htmltree.addAttr(HtmlAttr.CONTENT, nullCheck(content)); 580 return htmltree; 581 } 582 583 /** 584 * Generates a NAV tag with the role attribute. 585 * 586 * @return an HtmlTree object for the NAV tag 587 */ 588 public static HtmlTree NAV() { 589 HtmlTree htmltree = new HtmlTree(HtmlTag.NAV); 590 htmltree.setRole(Role.NAVIGATION); 591 return htmltree; 592 } 593 594 /** 595 * Generates a NOSCRIPT tag with some content. 596 * 597 * @param body content of the noscript tag 598 * @return an HtmlTree object for the NOSCRIPT tag 599 */ 600 public static HtmlTree NOSCRIPT(Content body) { 601 HtmlTree htmltree = new HtmlTree(HtmlTag.NOSCRIPT, nullCheck(body)); 602 return htmltree; 603 } 604 605 /** 606 * Generates a P tag with some content. 607 * 608 * @param body content of the Paragraph tag 609 * @return an HtmlTree object for the P tag 610 */ 611 public static HtmlTree P(Content body) { 612 return P(null, body); 613 } 614 615 /** 616 * Generates a P tag with some content. 617 * 618 * @param styleClass style of the Paragraph tag 619 * @param body content of the Paragraph tag 620 * @return an HtmlTree object for the P tag 621 */ 622 public static HtmlTree P(HtmlStyle styleClass, Content body) { 623 HtmlTree htmltree = new HtmlTree(HtmlTag.P, nullCheck(body)); 624 if (styleClass != null) 625 htmltree.addStyle(styleClass); 626 return htmltree; 627 } 628 629 /** 630 * Generates a SCRIPT tag with the type and src attributes. 631 * 632 * @param type type of link 633 * @param src the path for the script 634 * @return an HtmlTree object for the SCRIPT tag 635 */ 636 public static HtmlTree SCRIPT(String src) { 637 HtmlTree htmltree = HtmlTree.SCRIPT(); 638 htmltree.addAttr(HtmlAttr.SRC, nullCheck(src)); 639 return htmltree; 640 } 641 642 /** 643 * Generates a SCRIPT tag with the type attribute. 644 * 645 * @return an HtmlTree object for the SCRIPT tag 646 */ 647 public static HtmlTree SCRIPT() { 648 HtmlTree htmltree = new HtmlTree(HtmlTag.SCRIPT); 649 htmltree.addAttr(HtmlAttr.TYPE, "text/javascript"); 650 return htmltree; 651 } 652 653 /** 654 * Generates a SECTION tag with role attribute. 655 * 656 * @return an HtmlTree object for the SECTION tag 657 */ 658 public static HtmlTree SECTION() { 659 HtmlTree htmltree = new HtmlTree(HtmlTag.SECTION); 660 htmltree.setRole(Role.REGION); 661 return htmltree; 662 } 663 664 /** 665 * Generates a SECTION tag with role attribute and some content. 666 * 667 * @param body content of the section tag 668 * @return an HtmlTree object for the SECTION tag 669 */ 670 public static HtmlTree SECTION(Content body) { 671 HtmlTree htmltree = new HtmlTree(HtmlTag.SECTION, nullCheck(body)); 672 htmltree.setRole(Role.REGION); 673 return htmltree; 674 } 675 676 /** 677 * Generates a SMALL tag with some content. 678 * 679 * @param body content for the tag 680 * @return an HtmlTree object for the SMALL tag 681 */ 682 public static HtmlTree SMALL(Content body) { 683 HtmlTree htmltree = new HtmlTree(HtmlTag.SMALL, nullCheck(body)); 684 return htmltree; 685 } 686 687 /** 688 * Generates a SPAN tag with some content. 689 * 690 * @param body content for the tag 691 * @return an HtmlTree object for the SPAN tag 692 */ 693 public static HtmlTree SPAN(Content body) { 694 return SPAN(null, body); 695 } 696 697 /** 698 * Generates a SPAN tag with style class attribute and some content. 699 * 700 * @param styleClass style class for the tag 701 * @param body content for the tag 702 * @return an HtmlTree object for the SPAN tag 703 */ 704 public static HtmlTree SPAN(HtmlStyle styleClass, Content body) { 705 HtmlTree htmltree = new HtmlTree(HtmlTag.SPAN, nullCheck(body)); 706 if (styleClass != null) 707 htmltree.addStyle(styleClass); 708 return htmltree; 709 } 710 711 /** 712 * Generates a SPAN tag with id and style class attributes. It also encloses 713 * a content. 714 * 715 * @param id the id for the tag 716 * @param styleClass stylesheet class for the tag 717 * @param body content for the tag 718 * @return an HtmlTree object for the SPAN tag 719 */ 720 public static HtmlTree SPAN(String id, HtmlStyle styleClass, Content body) { 721 HtmlTree htmltree = new HtmlTree(HtmlTag.SPAN, nullCheck(body)); 722 htmltree.addAttr(HtmlAttr.ID, nullCheck(id)); 723 if (styleClass != null) 724 htmltree.addStyle(styleClass); 725 return htmltree; 726 } 727 728 /** 729 * Generates a Table tag with style class and summary attributes and some content. 730 * 731 * @param styleClass style of the table 732 * @param summary summary for the table 733 * @param body content for the table 734 * @return an HtmlTree object for the TABLE tag 735 */ 736 public static HtmlTree TABLE(HtmlStyle styleClass, String summary, Content body) { 737 HtmlTree htmltree = new HtmlTree(HtmlTag.TABLE, nullCheck(body)); 738 if (styleClass != null) 739 htmltree.addStyle(styleClass); 740 htmltree.addAttr(HtmlAttr.SUMMARY, nullCheck(summary)); 741 return htmltree; 742 } 743 744 /** 745 * Generates a Table tag with style class attribute and some content. 746 * 747 * @param styleClass style of the table 748 * @param body content for the table 749 * @return an HtmlTree object for the TABLE tag 750 */ 751 public static HtmlTree TABLE(HtmlStyle styleClass, Content body) { 752 HtmlTree htmltree = new HtmlTree(HtmlTag.TABLE, nullCheck(body)); 753 if (styleClass != null) { 754 htmltree.addStyle(styleClass); 755 } 756 return htmltree; 757 } 758 759 /** 760 * Generates a TD tag with style class attribute and some content. 761 * 762 * @param styleClass style for the tag 763 * @param body content for the tag 764 * @return an HtmlTree object for the TD tag 765 */ 766 public static HtmlTree TD(HtmlStyle styleClass, Content body) { 767 HtmlTree htmltree = new HtmlTree(HtmlTag.TD, nullCheck(body)); 768 if (styleClass != null) 769 htmltree.addStyle(styleClass); 770 return htmltree; 771 } 772 773 /** 774 * Generates a TD tag for an HTML table with some content. 775 * 776 * @param body content for the tag 777 * @return an HtmlTree object for the TD tag 778 */ 779 public static HtmlTree TD(Content body) { 780 return TD(null, body); 781 } 782 783 /** 784 * Generates a TH tag with style class and scope attributes and some content. 785 * 786 * @param styleClass style for the tag 787 * @param scope scope of the tag 788 * @param body content for the tag 789 * @return an HtmlTree object for the TH tag 790 */ 791 public static HtmlTree TH(HtmlStyle styleClass, String scope, Content body) { 792 HtmlTree htmltree = new HtmlTree(HtmlTag.TH, nullCheck(body)); 793 if (styleClass != null) 794 htmltree.addStyle(styleClass); 795 htmltree.addAttr(HtmlAttr.SCOPE, nullCheck(scope)); 796 return htmltree; 797 } 798 799 /** 800 * Generates a TH tag with scope attribute and some content. 801 * 802 * @param scope scope of the tag 803 * @param body content for the tag 804 * @return an HtmlTree object for the TH tag 805 */ 806 public static HtmlTree TH(String scope, Content body) { 807 return TH(null, scope, body); 808 } 809 810 /** 811 * Generates a TH tag with style class, scope attribute and some content. 812 * 813 * @param styleClass style for the tag 814 * @param body content for the tag 815 * @return an HtmlTree object for the TH tag 816 */ 817 public static HtmlTree TH_ROW_SCOPE(HtmlStyle styleClass, Content body) { 818 return TH(styleClass, "row", body); 819 } 820 821 /** 822 * Generates a TITLE tag with some content. 823 * 824 * @param body content for the tag 825 * @return an HtmlTree object for the TITLE tag 826 */ 827 public static HtmlTree TITLE(Content body) { 828 HtmlTree htmltree = new HtmlTree(HtmlTag.TITLE, nullCheck(body)); 829 return htmltree; 830 } 831 832 /** 833 * Generates a TR tag for an HTML table with some content. 834 * 835 * @param body content for the tag 836 * @return an HtmlTree object for the TR tag 837 */ 838 public static HtmlTree TR(Content body) { 839 HtmlTree htmltree = new HtmlTree(HtmlTag.TR, nullCheck(body)); 840 return htmltree; 841 } 842 843 /** 844 * Generates a UL tag with the style class attribute and some content. 845 * 846 * @param styleClass style for the tag 847 * @param first initial content to be added 848 * @param more a series of additional content nodes to be added 849 * @return an HtmlTree object for the UL tag 850 */ 851 public static HtmlTree UL(HtmlStyle styleClass, Content first, Content... more) { 852 HtmlTree htmlTree = new HtmlTree(HtmlTag.UL); 853 htmlTree.addContent(nullCheck(first)); 854 for (Content c : more) { 855 htmlTree.addContent(nullCheck(c)); 856 } 857 htmlTree.addStyle(nullCheck(styleClass)); 858 return htmlTree; 859 } 860 861 /** 862 * {@inheritDoc} 863 */ 864 public boolean isEmpty() { 865 return (!hasContent() && !hasAttrs()); 866 } 867 868 /** 869 * Returns true if the HTML tree has content. 870 * 871 * @return true if the HTML tree has content else return false 872 */ 873 public boolean hasContent() { 874 return (!content.isEmpty()); 875 } 876 877 /** 878 * Returns true if the HTML tree has attributes. 879 * 880 * @return true if the HTML tree has attributes else return false 881 */ 882 public boolean hasAttrs() { 883 return (!attrs.isEmpty()); 884 } 885 886 /** 887 * Returns true if the HTML tree has a specific attribute. 888 * 889 * @param attrName name of the attribute to check within the HTML tree 890 * @return true if the HTML tree has the specified attribute else return false 891 */ 892 public boolean hasAttr(HtmlAttr attrName) { 893 return (attrs.containsKey(attrName)); 894 } 895 896 /** 897 * Returns true if the HTML tree is valid. This check is more specific to 898 * standard doclet and not exactly similar to W3C specifications. But it 899 * ensures HTML validation. 900 * 901 * @return true if the HTML tree is valid 902 */ 903 public boolean isValid() { 904 switch (htmlTag) { 905 case A : 906 return (hasAttr(HtmlAttr.NAME) || hasAttr(HtmlAttr.ID) || (hasAttr(HtmlAttr.HREF) && hasContent())); 907 case BR : 908 return (!hasContent() && (!hasAttrs() || hasAttr(HtmlAttr.CLEAR))); 909 case IFRAME : 910 return (hasAttr(HtmlAttr.SRC) && !hasContent()); 911 case HR : 912 case INPUT: 913 return (!hasContent()); 914 case IMG : 915 return (hasAttr(HtmlAttr.SRC) && hasAttr(HtmlAttr.ALT) && !hasContent()); 916 case LINK : 917 return (hasAttr(HtmlAttr.HREF) && !hasContent()); 918 case META : 919 return (hasAttr(HtmlAttr.CONTENT) && !hasContent()); 920 case SCRIPT : 921 return ((hasAttr(HtmlAttr.TYPE) && hasAttr(HtmlAttr.SRC) && !hasContent()) || 922 (hasAttr(HtmlAttr.TYPE) && hasContent())); 923 default : 924 return hasContent(); 925 } 926 } 927 928 /** 929 * Returns true if the element is an inline element. 930 * 931 * @return true if the HTML tag is an inline element 932 */ 933 public boolean isInline() { 934 return (htmlTag.blockType == HtmlTag.BlockType.INLINE); 935 } 936 937 /** 938 * {@inheritDoc} 939 */ 940 @Override 941 public boolean write(Writer out, boolean atNewline) throws IOException { 942 if (!isInline() && !atNewline) 943 out.write(DocletConstants.NL); 944 String tagString = htmlTag.toString(); 945 out.write("<"); 946 out.write(tagString); 947 Iterator<HtmlAttr> iterator = attrs.keySet().iterator(); 948 HtmlAttr key; 949 String value; 950 while (iterator.hasNext()) { 951 key = iterator.next(); 952 value = attrs.get(key); 953 out.write(" "); 954 out.write(key.toString()); 955 if (!value.isEmpty()) { 956 out.write("=\""); 957 out.write(value); 958 out.write("\""); 959 } 960 } 961 out.write(">"); 962 boolean nl = false; 963 for (Content c : content) 964 nl = c.write(out, nl); 965 if (htmlTag.endTagRequired()) { 966 out.write("</"); 967 out.write(tagString); 968 out.write(">"); 969 } 970 if (!isInline()) { 971 out.write(DocletConstants.NL); 972 return true; 973 } else { 974 return false; 975 } 976 } 977 978 /** 979 * Given a Content node, strips all html characters and 980 * return the result. 981 * 982 * @param body The content node to check. 983 * @return the plain text from the content node 984 * 985 */ 986 private static String stripHtml(Content body) { 987 String rawString = body.toString(); 988 // remove HTML tags 989 rawString = rawString.replaceAll("\\<.*?>", " "); 990 // consolidate multiple spaces between a word to a single space 991 rawString = rawString.replaceAll("\\b\\s{2,}\\b", " "); 992 // remove extra whitespaces 993 return rawString.trim(); 994 } 995} 996