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