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