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