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