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