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 26 package jdk.javadoc.internal.doclint; 27 28 import java.util.Collections; 29 import java.util.EnumMap; 30 import java.util.EnumSet; 31 import java.util.HashMap; 32 import java.util.Map; 33 import java.util.Set; 34 import javax.lang.model.element.Name; 35 36 import com.sun.tools.javac.util.StringUtils; 37 38 import static jdk.javadoc.internal.doclint.HtmlTag.Attr.*; 39 40 /** 41 * Enum representing HTML tags. 42 * 43 * The intent of this class is to embody the semantics of W3C HTML 4.01 44 * to the extent supported/used by javadoc. 45 * In time, we may wish to transition javadoc and doclint to using HTML 5. 46 * 47 * This is derivative of com.sun.tools.doclets.formats.html.markup.HtmlTag. 48 * Eventually, these two should be merged back together, and possibly made 49 * public. 50 * 51 * @see <a href="http://www.w3.org/TR/REC-html40/">HTML 4.01 Specification</a> 52 * @see <a href="http://www.w3.org/TR/html5/">HTML 5 Specification</a> 53 * @see <a href="http://www.w3.org/TR/wai-aria/ ">WAI-ARIA Specification</a> 54 * @see <a href="http://www.w3.org/TR/aria-in-html/#recommendations-table">WAI-ARIA Recommendations Table</a> 55 * @author Bhavesh Patel 56 * @author Jonathan Gibbons (revised) 57 */ 58 public enum HtmlTag { 59 A(BlockType.INLINE, EndKind.REQUIRED, 60 attrs(AttrKind.ALL, HREF, TARGET, ID), 61 attrs(AttrKind.HTML4, REV, CHARSET, SHAPE, COORDS, NAME)), 62 63 ABBR(BlockType.INLINE, EndKind.REQUIRED, 64 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 65 66 ACRONYM(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED, 67 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 68 69 ADDRESS(BlockType.INLINE, EndKind.REQUIRED, 70 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 71 72 ARTICLE(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 73 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 74 75 ASIDE(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 76 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 77 78 B(BlockType.INLINE, EndKind.REQUIRED, 79 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 80 81 BDI(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED), 82 83 BIG(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED, 84 EnumSet.of(Flag.EXPECT_CONTENT)), 85 86 BLOCKQUOTE(BlockType.BLOCK, EndKind.REQUIRED, 87 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 88 89 BODY(BlockType.OTHER, EndKind.REQUIRED), 90 91 BR(BlockType.INLINE, EndKind.NONE, 92 attrs(AttrKind.USE_CSS, CLEAR)), 93 94 CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED, 95 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT), 96 attrs(AttrKind.USE_CSS, ALIGN)), 97 98 CENTER(HtmlVersion.HTML4, BlockType.BLOCK, EndKind.REQUIRED, 99 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 100 101 CITE(BlockType.INLINE, EndKind.REQUIRED, 102 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 103 104 CODE(BlockType.INLINE, EndKind.REQUIRED, 105 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 106 107 COL(BlockType.TABLE_ITEM, EndKind.NONE, 108 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF, VALIGN, WIDTH)), 109 COLGROUP(BlockType.TABLE_ITEM, EndKind.REQUIRED, attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF, VALIGN, WIDTH))110 COLGROUP(BlockType.TABLE_ITEM, EndKind.REQUIRED, 111 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF, VALIGN, WIDTH)) { 112 @Override 113 public boolean accepts(HtmlTag t) { 114 return (t == COL); 115 } 116 }, 117 118 DD(BlockType.LIST_ITEM, EndKind.OPTIONAL, 119 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)), 120 121 DEL(BlockType.INLINE, EndKind.REQUIRED, 122 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), 123 attrs(AttrKind.ALL, Attr.CITE, Attr.DATETIME)), 124 125 DFN(BlockType.INLINE, EndKind.REQUIRED, 126 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 127 128 DIV(BlockType.BLOCK, EndKind.REQUIRED, 129 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 130 attrs(AttrKind.USE_CSS, ALIGN)), 131 DL(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.USE_CSS, COMPACT))132 DL(BlockType.BLOCK, EndKind.REQUIRED, 133 EnumSet.of(Flag.EXPECT_CONTENT), 134 attrs(AttrKind.USE_CSS, COMPACT)) { 135 @Override 136 public boolean accepts(HtmlTag t) { 137 return (t == DT) || (t == DD); 138 } 139 }, 140 141 DT(BlockType.LIST_ITEM, EndKind.OPTIONAL, 142 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)), 143 144 EM(BlockType.INLINE, EndKind.REQUIRED, 145 EnumSet.of(Flag.NO_NEST)), 146 147 FONT(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED, // tag itself is deprecated 148 EnumSet.of(Flag.EXPECT_CONTENT), 149 attrs(AttrKind.USE_CSS, SIZE, COLOR, FACE)), 150 FOOTER(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE))151 FOOTER(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 152 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)) { 153 @Override 154 public boolean accepts(HtmlTag t) { 155 switch (t) { 156 case HEADER: case FOOTER: case MAIN: 157 return false; 158 default: 159 return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); 160 } 161 } 162 }, 163 164 FIGURE(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 165 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 166 167 FIGCAPTION(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED), 168 169 FRAME(HtmlVersion.HTML4, BlockType.OTHER, EndKind.NONE), 170 171 FRAMESET(HtmlVersion.HTML4, BlockType.OTHER, EndKind.REQUIRED), 172 173 H1(BlockType.BLOCK, EndKind.REQUIRED, 174 attrs(AttrKind.USE_CSS, ALIGN)), 175 H2(BlockType.BLOCK, EndKind.REQUIRED, 176 attrs(AttrKind.USE_CSS, ALIGN)), 177 H3(BlockType.BLOCK, EndKind.REQUIRED, 178 attrs(AttrKind.USE_CSS, ALIGN)), 179 H4(BlockType.BLOCK, EndKind.REQUIRED, 180 attrs(AttrKind.USE_CSS, ALIGN)), 181 H5(BlockType.BLOCK, EndKind.REQUIRED, 182 attrs(AttrKind.USE_CSS, ALIGN)), 183 H6(BlockType.BLOCK, EndKind.REQUIRED, 184 attrs(AttrKind.USE_CSS, ALIGN)), 185 186 HEAD(BlockType.OTHER, EndKind.REQUIRED), 187 HEADER(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE))188 HEADER(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 189 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)) { 190 @Override 191 public boolean accepts(HtmlTag t) { 192 switch (t) { 193 case HEADER: case FOOTER: case MAIN: 194 return false; 195 default: 196 return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); 197 } 198 } 199 }, 200 201 HR(BlockType.BLOCK, EndKind.NONE, 202 attrs(AttrKind.HTML4, WIDTH), 203 attrs(AttrKind.USE_CSS, ALIGN, NOSHADE, SIZE)), 204 205 HTML(BlockType.OTHER, EndKind.REQUIRED), 206 207 I(BlockType.INLINE, EndKind.REQUIRED, 208 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 209 210 IFRAME(BlockType.OTHER, EndKind.REQUIRED), 211 212 IMG(BlockType.INLINE, EndKind.NONE, 213 attrs(AttrKind.ALL, SRC, ALT, HEIGHT, WIDTH), 214 attrs(AttrKind.HTML5, CROSSORIGIN), 215 attrs(AttrKind.OBSOLETE, NAME), 216 attrs(AttrKind.USE_CSS, ALIGN, HSPACE, VSPACE, BORDER)), 217 218 INS(BlockType.INLINE, EndKind.REQUIRED, 219 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), 220 attrs(AttrKind.ALL, Attr.CITE, Attr.DATETIME)), 221 222 KBD(BlockType.INLINE, EndKind.REQUIRED, 223 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 224 225 LI(BlockType.LIST_ITEM, EndKind.OPTIONAL, 226 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 227 attrs(AttrKind.ALL, VALUE), 228 attrs(AttrKind.USE_CSS, TYPE)), 229 230 LINK(BlockType.OTHER, EndKind.NONE), 231 232 MAIN(HtmlVersion.HTML5, BlockType.OTHER, EndKind.REQUIRED), 233 234 MARK(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED), 235 MENU(BlockType.BLOCK, EndKind.REQUIRED)236 MENU(BlockType.BLOCK, EndKind.REQUIRED) { 237 @Override 238 public boolean accepts(HtmlTag t) { 239 return (t == LI); 240 } 241 }, 242 243 META(BlockType.OTHER, EndKind.NONE), 244 245 NAV(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 246 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 247 248 NOFRAMES(HtmlVersion.HTML4, BlockType.OTHER, EndKind.REQUIRED), 249 250 NOSCRIPT(BlockType.BLOCK, EndKind.REQUIRED), 251 OL(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.ALL, START, TYPE), attrs(AttrKind.HTML5, REVERSED), attrs(AttrKind.USE_CSS, COMPACT))252 OL(BlockType.BLOCK, EndKind.REQUIRED, 253 EnumSet.of(Flag.EXPECT_CONTENT), 254 attrs(AttrKind.ALL, START, TYPE), 255 attrs(AttrKind.HTML5, REVERSED), 256 attrs(AttrKind.USE_CSS, COMPACT)) { 257 @Override 258 public boolean accepts(HtmlTag t) { 259 return (t == LI); 260 } 261 }, 262 263 P(BlockType.BLOCK, EndKind.OPTIONAL, 264 EnumSet.of(Flag.EXPECT_CONTENT), 265 attrs(AttrKind.USE_CSS, ALIGN)), 266 PRE(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.USE_CSS, WIDTH))267 PRE(BlockType.BLOCK, EndKind.REQUIRED, 268 EnumSet.of(Flag.EXPECT_CONTENT), 269 attrs(AttrKind.USE_CSS, WIDTH)) { 270 @Override 271 public boolean accepts(HtmlTag t) { 272 switch (t) { 273 case IMG: case BIG: case SMALL: case SUB: case SUP: 274 return false; 275 default: 276 return (t.blockType == BlockType.INLINE); 277 } 278 } 279 }, 280 281 Q(BlockType.INLINE, EndKind.REQUIRED, 282 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 283 284 S(BlockType.INLINE, EndKind.REQUIRED, 285 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 286 287 SAMP(BlockType.INLINE, EndKind.REQUIRED, 288 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 289 290 SCRIPT(BlockType.OTHER, EndKind.REQUIRED, 291 attrs(AttrKind.ALL, SRC)), 292 293 SECTION(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 294 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 295 296 SMALL(BlockType.INLINE, EndKind.REQUIRED, 297 EnumSet.of(Flag.EXPECT_CONTENT)), 298 299 SPAN(BlockType.INLINE, EndKind.REQUIRED, 300 EnumSet.of(Flag.EXPECT_CONTENT)), 301 302 STRIKE(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED, 303 EnumSet.of(Flag.EXPECT_CONTENT)), 304 305 STRONG(BlockType.INLINE, EndKind.REQUIRED, 306 EnumSet.of(Flag.EXPECT_CONTENT)), 307 308 STYLE(BlockType.OTHER, EndKind.REQUIRED), 309 310 SUB(BlockType.INLINE, EndKind.REQUIRED, 311 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 312 313 SUP(BlockType.INLINE, EndKind.REQUIRED, 314 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 315 TABLE(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.ALL, BORDER), attrs(AttrKind.HTML4, SUMMARY, CELLPADDING, CELLSPACING, Attr.FRAME, RULES, WIDTH), attrs(AttrKind.USE_CSS, ALIGN, BGCOLOR))316 TABLE(BlockType.BLOCK, EndKind.REQUIRED, 317 EnumSet.of(Flag.EXPECT_CONTENT), 318 attrs(AttrKind.ALL, BORDER), 319 attrs(AttrKind.HTML4, SUMMARY, CELLPADDING, CELLSPACING, Attr.FRAME, RULES, WIDTH), 320 attrs(AttrKind.USE_CSS, ALIGN, BGCOLOR)) { 321 @Override 322 public boolean accepts(HtmlTag t) { 323 switch (t) { 324 case CAPTION: 325 case COLGROUP: 326 case THEAD: case TBODY: case TFOOT: 327 case TR: // HTML 3.2 328 return true; 329 default: 330 return false; 331 } 332 } 333 }, 334 TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.ALL, VALIGN), attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF))335 TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED, 336 EnumSet.of(Flag.EXPECT_CONTENT), 337 attrs(AttrKind.ALL, VALIGN), 338 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) { 339 @Override 340 public boolean accepts(HtmlTag t) { 341 return (t == TR); 342 } 343 }, 344 345 TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 346 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 347 attrs(AttrKind.ALL, COLSPAN, ROWSPAN, HEADERS, VALIGN), 348 attrs(AttrKind.HTML4, AXIS, Attr.ABBR, SCOPE, ALIGN, CHAR, CHAROFF), 349 attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)), 350 351 TEMPLATE(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED, 352 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 353 TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED, attrs(AttrKind.ALL, VALIGN), attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF))354 TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED, 355 attrs(AttrKind.ALL, VALIGN), 356 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) { 357 @Override 358 public boolean accepts(HtmlTag t) { 359 return (t == TR); 360 } 361 }, 362 363 TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 364 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 365 attrs(AttrKind.ALL, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR, 366 VALIGN), 367 attrs(AttrKind.HTML4, AXIS, ALIGN, CHAR, CHAROFF), 368 attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)), 369 THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED, attrs(AttrKind.ALL, VALIGN), attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF))370 THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED, 371 attrs(AttrKind.ALL, VALIGN), 372 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) { 373 @Override 374 public boolean accepts(HtmlTag t) { 375 return (t == TR); 376 } 377 }, 378 379 TIME(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED), 380 381 TITLE(BlockType.OTHER, EndKind.REQUIRED), 382 TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL, attrs(AttrKind.ALL, VALIGN), attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF), attrs(AttrKind.USE_CSS, BGCOLOR))383 TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 384 attrs(AttrKind.ALL, VALIGN), 385 attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF), 386 attrs(AttrKind.USE_CSS, BGCOLOR)) { 387 @Override 388 public boolean accepts(HtmlTag t) { 389 return (t == TH) || (t == TD); 390 } 391 }, 392 393 TT(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED, 394 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 395 396 U(BlockType.INLINE, EndKind.REQUIRED, 397 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 398 UL(BlockType.BLOCK, EndKind.REQUIRED, EnumSet.of(Flag.EXPECT_CONTENT), attrs(AttrKind.HTML4, COMPACT, TYPE))399 UL(BlockType.BLOCK, EndKind.REQUIRED, 400 EnumSet.of(Flag.EXPECT_CONTENT), 401 attrs(AttrKind.HTML4, COMPACT, TYPE)) { 402 @Override 403 public boolean accepts(HtmlTag t) { 404 return (t == LI); 405 } 406 }, 407 408 WBR(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED), 409 410 VAR(BlockType.INLINE, EndKind.REQUIRED); 411 412 /** 413 * Enum representing the type of HTML element. 414 */ 415 public static enum BlockType { 416 BLOCK, 417 INLINE, 418 LIST_ITEM, 419 TABLE_ITEM, 420 OTHER 421 } 422 423 /** 424 * Enum representing HTML end tag requirement. 425 */ 426 public static enum EndKind { 427 NONE, 428 OPTIONAL, 429 REQUIRED 430 } 431 432 public static enum Flag { 433 ACCEPTS_BLOCK, 434 ACCEPTS_INLINE, 435 EXPECT_CONTENT, 436 NO_NEST 437 } 438 439 public static enum Attr { 440 ABBR, 441 ALIGN, 442 ALINK, 443 ALT, 444 ARIA_ACTIVEDESCENDANT, 445 ARIA_CONTROLS, 446 ARIA_DESCRIBEDBY, 447 ARIA_EXPANDED, 448 ARIA_LABEL, 449 ARIA_LABELLEDBY, 450 ARIA_LEVEL, 451 ARIA_MULTISELECTABLE, 452 ARIA_OWNS, 453 ARIA_POSINSET, 454 ARIA_SETSIZE, 455 ARIA_READONLY, 456 ARIA_REQUIRED, 457 ARIA_SELECTED, 458 ARIA_SORT, 459 AXIS, 460 BACKGROUND, 461 BGCOLOR, 462 BORDER, 463 CELLSPACING, 464 CELLPADDING, 465 CHAR, 466 CHAROFF, 467 CHARSET, 468 CITE, 469 CLEAR, 470 CLASS, 471 COLOR, 472 COLSPAN, 473 COMPACT, 474 COORDS, 475 CROSSORIGIN, 476 DATETIME, 477 FACE, 478 FRAME, 479 FRAMEBORDER, 480 HEADERS, 481 HEIGHT, 482 HREF, 483 HSPACE, 484 ID, 485 LINK, 486 LONGDESC, 487 MARGINHEIGHT, 488 MARGINWIDTH, 489 NAME, 490 NOSHADE, 491 NOWRAP, 492 PROFILE, 493 REV, 494 REVERSED, 495 ROLE, 496 ROWSPAN, 497 RULES, 498 SCHEME, 499 SCOPE, 500 SCROLLING, 501 SHAPE, 502 SIZE, 503 SPACE, 504 SRC, 505 START, 506 STYLE, 507 SUMMARY, 508 TARGET, 509 TEXT, 510 TYPE, 511 VALIGN, 512 VALUE, 513 VERSION, 514 VLINK, 515 VSPACE, 516 WIDTH; 517 518 private final String name; 519 Attr()520 Attr() { 521 name = StringUtils.toLowerCase(name().replace("_", "-")); 522 } 523 getText()524 public String getText() { 525 return name; 526 } 527 528 static final Map<String,Attr> index = new HashMap<>(); 529 static { 530 for (Attr t: values()) { t.getText()531 index.put(t.getText(), t); 532 } 533 } 534 } 535 536 public static enum AttrKind { 537 HTML4, 538 HTML5, 539 INVALID, 540 OBSOLETE, 541 USE_CSS, 542 ALL 543 } 544 545 // This class exists to avoid warnings from using parameterized vararg type 546 // Map<Attr,AttrKind> in signature of HtmlTag constructor. 547 private static class AttrMap extends EnumMap<Attr,AttrKind> { 548 private static final long serialVersionUID = 0; AttrMap()549 AttrMap() { 550 super(Attr.class); 551 } 552 } 553 554 555 public final HtmlVersion allowedVersion; 556 public final BlockType blockType; 557 public final EndKind endKind; 558 public final Set<Flag> flags; 559 private final Map<Attr,AttrKind> attrs; 560 HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps)561 HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) { 562 this(HtmlVersion.ALL, blockType, endKind, Collections.emptySet(), attrMaps); 563 } 564 HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, AttrMap... attrMaps)565 HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, AttrMap... attrMaps) { 566 this(allowedVersion, blockType, endKind, Collections.emptySet(), attrMaps); 567 } 568 HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps)569 HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) { 570 this(HtmlVersion.ALL, blockType, endKind, flags, attrMaps); 571 } 572 HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps)573 HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) { 574 this.allowedVersion = allowedVersion; 575 this.blockType = blockType; 576 this.endKind = endKind; 577 this.flags = flags; 578 this.attrs = new EnumMap<>(Attr.class); 579 for (Map<Attr,AttrKind> m: attrMaps) 580 this.attrs.putAll(m); 581 attrs.put(Attr.CLASS, AttrKind.ALL); 582 attrs.put(Attr.ID, AttrKind.ALL); 583 attrs.put(Attr.STYLE, AttrKind.ALL); 584 attrs.put(Attr.ROLE, AttrKind.HTML5); 585 // for now, assume that all ARIA attributes are allowed on all tags. 586 attrs.put(Attr.ARIA_ACTIVEDESCENDANT, AttrKind.HTML5); 587 attrs.put(Attr.ARIA_CONTROLS, AttrKind.HTML5); 588 attrs.put(Attr.ARIA_DESCRIBEDBY, AttrKind.HTML5); 589 attrs.put(Attr.ARIA_EXPANDED, AttrKind.HTML5); 590 attrs.put(Attr.ARIA_LABEL, AttrKind.HTML5); 591 attrs.put(Attr.ARIA_LABELLEDBY, AttrKind.HTML5); 592 attrs.put(Attr.ARIA_LEVEL, AttrKind.HTML5); 593 attrs.put(Attr.ARIA_MULTISELECTABLE, AttrKind.HTML5); 594 attrs.put(Attr.ARIA_OWNS, AttrKind.HTML5); 595 attrs.put(Attr.ARIA_POSINSET, AttrKind.HTML5); 596 attrs.put(Attr.ARIA_READONLY, AttrKind.HTML5); 597 attrs.put(Attr.ARIA_REQUIRED, AttrKind.HTML5); 598 attrs.put(Attr.ARIA_SELECTED, AttrKind.HTML5); 599 attrs.put(Attr.ARIA_SETSIZE, AttrKind.HTML5); 600 attrs.put(Attr.ARIA_SORT, AttrKind.HTML5); 601 } 602 accepts(HtmlTag t)603 public boolean accepts(HtmlTag t) { 604 if (flags.contains(Flag.ACCEPTS_BLOCK) && flags.contains(Flag.ACCEPTS_INLINE)) { 605 return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); 606 } else if (flags.contains(Flag.ACCEPTS_BLOCK)) { 607 return (t.blockType == BlockType.BLOCK); 608 } else if (flags.contains(Flag.ACCEPTS_INLINE)) { 609 return (t.blockType == BlockType.INLINE); 610 } else 611 switch (blockType) { 612 case BLOCK: 613 case INLINE: 614 return (t.blockType == BlockType.INLINE); 615 case OTHER: 616 // OTHER tags are invalid in doc comments, and will be 617 // reported separately, so silently accept/ignore any content 618 return true; 619 default: 620 // any combination which could otherwise arrive here 621 // ought to have been handled in an overriding method 622 throw new AssertionError(this + ":" + t); 623 } 624 } 625 acceptsText()626 public boolean acceptsText() { 627 // generally, anywhere we can put text we can also put inline tag 628 // so check if a typical inline tag is allowed 629 return accepts(B); 630 } 631 getText()632 public String getText() { 633 return StringUtils.toLowerCase(name()); 634 } 635 getAttr(Name attrName)636 public Attr getAttr(Name attrName) { 637 return Attr.index.get(StringUtils.toLowerCase(attrName.toString())); 638 } 639 getAttrKind(Name attrName)640 public AttrKind getAttrKind(Name attrName) { 641 AttrKind k = attrs.get(getAttr(attrName)); // null-safe 642 return (k == null) ? AttrKind.INVALID : k; 643 } 644 attrs(AttrKind k, Attr... attrs)645 private static AttrMap attrs(AttrKind k, Attr... attrs) { 646 AttrMap map = new AttrMap(); 647 for (Attr a: attrs) map.put(a, k); 648 return map; 649 } 650 651 private static final Map<String, HtmlTag> index = new HashMap<>(); 652 static { 653 for (HtmlTag t: values()) { t.getText()654 index.put(t.getText(), t); 655 } 656 } 657 get(Name tagName)658 public static HtmlTag get(Name tagName) { 659 return index.get(StringUtils.toLowerCase(tagName.toString())); 660 } 661 } 662