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