HtmlTag.java revision 2571:10fc81ac75b4
1/* 2 * Copyright (c) 2010, 2014, 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.doclint; 27 28import java.util.Set; 29import java.util.Collections; 30import java.util.EnumMap; 31import java.util.EnumSet; 32import java.util.HashMap; 33import java.util.Map; 34 35import javax.lang.model.element.Name; 36 37import static com.sun.tools.doclint.HtmlTag.Attr.*; 38 39import com.sun.tools.javac.util.StringUtils; 40 41/** 42 * Enum representing HTML tags. 43 * 44 * The intent of this class is to embody the semantics of W3C HTML 4.01 45 * to the extent supported/used by javadoc. 46 * In time, we may wish to transition javadoc and doclint to using HTML 5. 47 * 48 * This is derivative of com.sun.tools.doclets.formats.html.markup.HtmlTag. 49 * Eventually, these two should be merged back together, and possibly made 50 * public. 51 * 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/html5/">HTML 5 Specification</a> 54 * @author Bhavesh Patel 55 * @author Jonathan Gibbons (revised) 56 */ 57public enum HtmlTag { 58 A(BlockType.INLINE, EndKind.REQUIRED, 59 attrs(AttrKind.OK, HREF, TARGET, NAME)), 60 61 ABBR(BlockType.INLINE, EndKind.REQUIRED, 62 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 63 64 ACRONYM(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 B(BlockType.INLINE, EndKind.REQUIRED, 71 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 72 73 BIG(BlockType.INLINE, EndKind.REQUIRED, 74 EnumSet.of(Flag.EXPECT_CONTENT)), 75 76 BLOCKQUOTE(BlockType.BLOCK, EndKind.REQUIRED, 77 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 78 79 BODY(BlockType.OTHER, EndKind.REQUIRED), 80 81 BR(BlockType.INLINE, EndKind.NONE, 82 attrs(AttrKind.USE_CSS, CLEAR)), 83 84 CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED, 85 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)), 86 87 CENTER(BlockType.BLOCK, EndKind.REQUIRED, 88 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 89 90 CITE(BlockType.INLINE, EndKind.REQUIRED, 91 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 92 93 CODE(BlockType.INLINE, EndKind.REQUIRED, 94 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 95 96 DD(BlockType.LIST_ITEM, EndKind.OPTIONAL, 97 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)), 98 99 DEL(BlockType.INLINE, EndKind.REQUIRED, 100 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), 101 attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)), 102 103 DFN(BlockType.INLINE, EndKind.REQUIRED, 104 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 105 106 DIV(BlockType.BLOCK, EndKind.REQUIRED, 107 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)), 108 109 DL(BlockType.BLOCK, EndKind.REQUIRED, 110 EnumSet.of(Flag.EXPECT_CONTENT), 111 attrs(AttrKind.USE_CSS, COMPACT)) { 112 @Override 113 public boolean accepts(HtmlTag t) { 114 return (t == DT) || (t == DD); 115 } 116 }, 117 118 DT(BlockType.LIST_ITEM, EndKind.OPTIONAL, 119 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)), 120 121 EM(BlockType.INLINE, EndKind.REQUIRED, 122 EnumSet.of(Flag.NO_NEST)), 123 124 FONT(BlockType.INLINE, EndKind.REQUIRED, // tag itself is deprecated 125 EnumSet.of(Flag.EXPECT_CONTENT), 126 attrs(AttrKind.USE_CSS, SIZE, COLOR, FACE)), 127 128 FRAME(BlockType.OTHER, EndKind.NONE), 129 130 FRAMESET(BlockType.OTHER, EndKind.REQUIRED), 131 132 H1(BlockType.BLOCK, EndKind.REQUIRED), 133 H2(BlockType.BLOCK, EndKind.REQUIRED), 134 H3(BlockType.BLOCK, EndKind.REQUIRED), 135 H4(BlockType.BLOCK, EndKind.REQUIRED), 136 H5(BlockType.BLOCK, EndKind.REQUIRED), 137 H6(BlockType.BLOCK, EndKind.REQUIRED), 138 139 HEAD(BlockType.OTHER, EndKind.REQUIRED), 140 141 HR(BlockType.BLOCK, EndKind.NONE, 142 attrs(AttrKind.OK, WIDTH)), // OK in 4.01; not allowed in 5 143 144 HTML(BlockType.OTHER, EndKind.REQUIRED), 145 146 I(BlockType.INLINE, EndKind.REQUIRED, 147 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 148 149 IMG(BlockType.INLINE, EndKind.NONE, 150 attrs(AttrKind.OK, SRC, ALT, HEIGHT, WIDTH), 151 attrs(AttrKind.OBSOLETE, NAME), 152 attrs(AttrKind.USE_CSS, ALIGN, HSPACE, VSPACE, BORDER)), 153 154 INS(BlockType.INLINE, EndKind.REQUIRED, 155 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST), 156 attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)), 157 158 KBD(BlockType.INLINE, EndKind.REQUIRED, 159 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 160 161 LI(BlockType.LIST_ITEM, EndKind.OPTIONAL, 162 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 163 attrs(AttrKind.OK, VALUE)), 164 165 LINK(BlockType.OTHER, EndKind.NONE), 166 167 MENU(BlockType.BLOCK, EndKind.REQUIRED) { 168 @Override 169 public boolean accepts(HtmlTag t) { 170 return (t == LI); 171 } 172 }, 173 174 META(BlockType.OTHER, EndKind.NONE), 175 176 NOFRAMES(BlockType.OTHER, EndKind.REQUIRED), 177 178 NOSCRIPT(BlockType.BLOCK, EndKind.REQUIRED), 179 180 OL(BlockType.BLOCK, EndKind.REQUIRED, 181 EnumSet.of(Flag.EXPECT_CONTENT), 182 attrs(AttrKind.OK, START, TYPE)) { 183 @Override 184 public boolean accepts(HtmlTag t) { 185 return (t == LI); 186 } 187 }, 188 189 P(BlockType.BLOCK, EndKind.OPTIONAL, 190 EnumSet.of(Flag.EXPECT_CONTENT), 191 attrs(AttrKind.USE_CSS, ALIGN)), 192 193 PRE(BlockType.BLOCK, EndKind.REQUIRED, 194 EnumSet.of(Flag.EXPECT_CONTENT)) { 195 @Override 196 public boolean accepts(HtmlTag t) { 197 switch (t) { 198 case IMG: case BIG: case SMALL: case SUB: case SUP: 199 return false; 200 default: 201 return (t.blockType == BlockType.INLINE); 202 } 203 } 204 }, 205 206 Q(BlockType.INLINE, EndKind.REQUIRED, 207 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 208 209 S(BlockType.INLINE, EndKind.REQUIRED, 210 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 211 212 SAMP(BlockType.INLINE, EndKind.REQUIRED, 213 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 214 215 SCRIPT(BlockType.OTHER, EndKind.REQUIRED), 216 217 SMALL(BlockType.INLINE, EndKind.REQUIRED, 218 EnumSet.of(Flag.EXPECT_CONTENT)), 219 220 SPAN(BlockType.INLINE, EndKind.REQUIRED, 221 EnumSet.of(Flag.EXPECT_CONTENT)), 222 223 STRIKE(BlockType.INLINE, EndKind.REQUIRED, 224 EnumSet.of(Flag.EXPECT_CONTENT)), 225 226 STRONG(BlockType.INLINE, EndKind.REQUIRED, 227 EnumSet.of(Flag.EXPECT_CONTENT)), 228 229 SUB(BlockType.INLINE, EndKind.REQUIRED, 230 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 231 232 SUP(BlockType.INLINE, EndKind.REQUIRED, 233 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 234 235 TABLE(BlockType.BLOCK, EndKind.REQUIRED, 236 EnumSet.of(Flag.EXPECT_CONTENT), 237 attrs(AttrKind.OK, SUMMARY, Attr.FRAME, RULES, BORDER, 238 CELLPADDING, CELLSPACING, WIDTH), // width OK in 4.01; not allowed in 5 239 attrs(AttrKind.USE_CSS, ALIGN, BGCOLOR)) { 240 @Override 241 public boolean accepts(HtmlTag t) { 242 switch (t) { 243 case CAPTION: 244 case THEAD: case TBODY: case TFOOT: 245 case TR: // HTML 3.2 246 return true; 247 default: 248 return false; 249 } 250 } 251 }, 252 253 TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED, 254 EnumSet.of(Flag.EXPECT_CONTENT), 255 attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) { 256 @Override 257 public boolean accepts(HtmlTag t) { 258 return (t == TR); 259 } 260 }, 261 262 TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 263 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 264 attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR, AXIS, 265 ALIGN, CHAR, CHAROFF, VALIGN), 266 attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)), 267 268 TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED, 269 attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) { 270 @Override 271 public boolean accepts(HtmlTag t) { 272 return (t == TR); 273 } 274 }, 275 276 TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 277 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE), 278 attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR, AXIS, 279 ALIGN, CHAR, CHAROFF, VALIGN), 280 attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)), 281 282 THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED, 283 attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) { 284 @Override 285 public boolean accepts(HtmlTag t) { 286 return (t == TR); 287 } 288 }, 289 290 TITLE(BlockType.OTHER, EndKind.REQUIRED), 291 292 TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL, 293 attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN), 294 attrs(AttrKind.USE_CSS, BGCOLOR)) { 295 @Override 296 public boolean accepts(HtmlTag t) { 297 return (t == TH) || (t == TD); 298 } 299 }, 300 301 TT(BlockType.INLINE, EndKind.REQUIRED, 302 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 303 304 U(BlockType.INLINE, EndKind.REQUIRED, 305 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)), 306 307 UL(BlockType.BLOCK, EndKind.REQUIRED, 308 EnumSet.of(Flag.EXPECT_CONTENT), 309 attrs(AttrKind.OK, COMPACT, TYPE)) { // OK in 4.01; not allowed in 5 310 @Override 311 public boolean accepts(HtmlTag t) { 312 return (t == LI); 313 } 314 }, 315 316 VAR(BlockType.INLINE, EndKind.REQUIRED); 317 318 /** 319 * Enum representing the type of HTML element. 320 */ 321 public static enum BlockType { 322 BLOCK, 323 INLINE, 324 LIST_ITEM, 325 TABLE_ITEM, 326 OTHER 327 } 328 329 /** 330 * Enum representing HTML end tag requirement. 331 */ 332 public static enum EndKind { 333 NONE, 334 OPTIONAL, 335 REQUIRED 336 } 337 338 public static enum Flag { 339 ACCEPTS_BLOCK, 340 ACCEPTS_INLINE, 341 EXPECT_CONTENT, 342 NO_NEST 343 } 344 345 public static enum Attr { 346 ABBR, 347 ALIGN, 348 ALT, 349 AXIS, 350 BGCOLOR, 351 BORDER, 352 CELLSPACING, 353 CELLPADDING, 354 CHAR, 355 CHAROFF, 356 CITE, 357 CLEAR, 358 CLASS, 359 COLOR, 360 COLSPAN, 361 COMPACT, 362 DATETIME, 363 FACE, 364 FRAME, 365 HEADERS, 366 HEIGHT, 367 HREF, 368 HSPACE, 369 ID, 370 NAME, 371 NOWRAP, 372 REVERSED, 373 ROWSPAN, 374 RULES, 375 SCOPE, 376 SIZE, 377 SPACE, 378 SRC, 379 START, 380 STYLE, 381 SUMMARY, 382 TARGET, 383 TYPE, 384 VALIGN, 385 VALUE, 386 VSPACE, 387 WIDTH; 388 389 public String getText() { 390 return StringUtils.toLowerCase(name()); 391 } 392 393 static final Map<String,Attr> index = new HashMap<>(); 394 static { 395 for (Attr t: values()) { 396 index.put(t.getText(), t); 397 } 398 } 399 } 400 401 public static enum AttrKind { 402 INVALID, 403 OBSOLETE, 404 USE_CSS, 405 OK 406 } 407 408 // This class exists to avoid warnings from using parameterized vararg type 409 // Map<Attr,AttrKind> in signature of HtmlTag constructor. 410 private static class AttrMap extends EnumMap<Attr,AttrKind> { 411 private static final long serialVersionUID = 0; 412 AttrMap() { 413 super(Attr.class); 414 } 415 } 416 417 418 public final BlockType blockType; 419 public final EndKind endKind; 420 public final Set<Flag> flags; 421 private final Map<Attr,AttrKind> attrs; 422 423 HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) { 424 this(blockType, endKind, Collections.<Flag>emptySet(), attrMaps); 425 } 426 427 HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) { 428 this.blockType = blockType; 429 this.endKind = endKind; 430 this.flags = flags; 431 this.attrs = new EnumMap<>(Attr.class); 432 for (Map<Attr,AttrKind> m: attrMaps) 433 this.attrs.putAll(m); 434 attrs.put(Attr.CLASS, AttrKind.OK); 435 attrs.put(Attr.ID, AttrKind.OK); 436 attrs.put(Attr.STYLE, AttrKind.OK); 437 } 438 439 public boolean accepts(HtmlTag t) { 440 if (flags.contains(Flag.ACCEPTS_BLOCK) && flags.contains(Flag.ACCEPTS_INLINE)) { 441 return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE); 442 } else if (flags.contains(Flag.ACCEPTS_BLOCK)) { 443 return (t.blockType == BlockType.BLOCK); 444 } else if (flags.contains(Flag.ACCEPTS_INLINE)) { 445 return (t.blockType == BlockType.INLINE); 446 } else 447 switch (blockType) { 448 case BLOCK: 449 case INLINE: 450 return (t.blockType == BlockType.INLINE); 451 case OTHER: 452 // OTHER tags are invalid in doc comments, and will be 453 // reported separately, so silently accept/ignore any content 454 return true; 455 default: 456 // any combination which could otherwise arrive here 457 // ought to have been handled in an overriding method 458 throw new AssertionError(this + ":" + t); 459 } 460 } 461 462 public boolean acceptsText() { 463 // generally, anywhere we can put text we can also put inline tag 464 // so check if a typical inline tag is allowed 465 return accepts(B); 466 } 467 468 public String getText() { 469 return StringUtils.toLowerCase(name()); 470 } 471 472 public Attr getAttr(Name attrName) { 473 return Attr.index.get(StringUtils.toLowerCase(attrName.toString())); 474 } 475 476 public AttrKind getAttrKind(Name attrName) { 477 AttrKind k = attrs.get(getAttr(attrName)); // null-safe 478 return (k == null) ? AttrKind.INVALID : k; 479 } 480 481 private static AttrMap attrs(AttrKind k, Attr... attrs) { 482 AttrMap map = new AttrMap(); 483 for (Attr a: attrs) map.put(a, k); 484 return map; 485 } 486 487 private static final Map<String,HtmlTag> index = new HashMap<>(); 488 static { 489 for (HtmlTag t: values()) { 490 index.put(t.getText(), t); 491 } 492 } 493 494 static HtmlTag get(Name tagName) { 495 return index.get(StringUtils.toLowerCase(tagName.toString())); 496 } 497 498} 499