Thu, 24 May 2018 18:02:46 +0800
Merge
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 */
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.*;
39 import com.sun.tools.javac.util.StringUtils;
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)),
61 B(BlockType.INLINE, EndKind.REQUIRED,
62 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
64 BIG(BlockType.INLINE, EndKind.REQUIRED,
65 EnumSet.of(Flag.EXPECT_CONTENT)),
67 BLOCKQUOTE(BlockType.BLOCK, EndKind.REQUIRED,
68 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
70 BODY(BlockType.OTHER, EndKind.REQUIRED),
72 BR(BlockType.INLINE, EndKind.NONE,
73 attrs(AttrKind.USE_CSS, CLEAR)),
75 CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED,
76 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
78 CENTER(BlockType.BLOCK, EndKind.REQUIRED,
79 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
81 CITE(BlockType.INLINE, EndKind.REQUIRED,
82 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
84 CODE(BlockType.INLINE, EndKind.REQUIRED,
85 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
87 DD(BlockType.LIST_ITEM, EndKind.OPTIONAL,
88 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
90 DFN(BlockType.INLINE, EndKind.REQUIRED,
91 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
93 DIV(BlockType.BLOCK, EndKind.REQUIRED,
94 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
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 },
105 DT(BlockType.LIST_ITEM, EndKind.OPTIONAL,
106 EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
108 EM(BlockType.INLINE, EndKind.REQUIRED,
109 EnumSet.of(Flag.NO_NEST)),
111 FONT(BlockType.INLINE, EndKind.REQUIRED, // tag itself is deprecated
112 EnumSet.of(Flag.EXPECT_CONTENT),
113 attrs(AttrKind.USE_CSS, SIZE, COLOR, FACE)),
115 FRAME(BlockType.OTHER, EndKind.NONE),
117 FRAMESET(BlockType.OTHER, EndKind.REQUIRED),
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),
126 HEAD(BlockType.OTHER, EndKind.REQUIRED),
128 HR(BlockType.BLOCK, EndKind.NONE,
129 attrs(AttrKind.OK, WIDTH)), // OK in 4.01; not allowed in 5
131 HTML(BlockType.OTHER, EndKind.REQUIRED),
133 I(BlockType.INLINE, EndKind.REQUIRED,
134 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
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)),
141 LI(BlockType.LIST_ITEM, EndKind.OPTIONAL,
142 EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
143 attrs(AttrKind.OK, VALUE)),
145 LINK(BlockType.OTHER, EndKind.NONE),
147 MENU(BlockType.BLOCK, EndKind.REQUIRED) {
148 @Override
149 public boolean accepts(HtmlTag t) {
150 return (t == LI);
151 }
152 },
154 META(BlockType.OTHER, EndKind.NONE),
156 NOFRAMES(BlockType.OTHER, EndKind.REQUIRED),
158 NOSCRIPT(BlockType.BLOCK, EndKind.REQUIRED),
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 },
169 P(BlockType.BLOCK, EndKind.OPTIONAL,
170 EnumSet.of(Flag.EXPECT_CONTENT),
171 attrs(AttrKind.USE_CSS, ALIGN)),
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 },
186 SCRIPT(BlockType.OTHER, EndKind.REQUIRED,
187 attrs(AttrKind.OK, SRC)),
189 SMALL(BlockType.INLINE, EndKind.REQUIRED,
190 EnumSet.of(Flag.EXPECT_CONTENT)),
192 SPAN(BlockType.INLINE, EndKind.REQUIRED,
193 EnumSet.of(Flag.EXPECT_CONTENT)),
195 STRONG(BlockType.INLINE, EndKind.REQUIRED,
196 EnumSet.of(Flag.EXPECT_CONTENT)),
198 SUB(BlockType.INLINE, EndKind.REQUIRED,
199 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
201 SUP(BlockType.INLINE, EndKind.REQUIRED,
202 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
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 },
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 },
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)),
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 },
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)),
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 },
259 TITLE(BlockType.OTHER, EndKind.REQUIRED),
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 },
270 TT(BlockType.INLINE, EndKind.REQUIRED,
271 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
273 U(BlockType.INLINE, EndKind.REQUIRED,
274 EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
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 },
285 VAR(BlockType.INLINE, EndKind.REQUIRED);
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 }
298 /**
299 * Enum representing HTML end tag requirement.
300 */
301 public static enum EndKind {
302 NONE,
303 OPTIONAL,
304 REQUIRED;
305 }
307 public static enum Flag {
308 ACCEPTS_BLOCK,
309 ACCEPTS_INLINE,
310 EXPECT_CONTENT,
311 NO_NEST
312 }
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;
356 public String getText() {
357 return StringUtils.toLowerCase(name());
358 }
360 static final Map<String,Attr> index = new HashMap<String,Attr>();
361 static {
362 for (Attr t: values()) {
363 index.put(t.getText(), t);
364 }
365 }
366 }
368 public static enum AttrKind {
369 INVALID,
370 OBSOLETE,
371 USE_CSS,
372 OK
373 }
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;
379 AttrMap() {
380 super(Attr.class);
381 }
382 }
385 public final BlockType blockType;
386 public final EndKind endKind;
387 public final Set<Flag> flags;
388 private final Map<Attr,AttrKind> attrs;
390 HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) {
391 this(blockType, endKind, Collections.<Flag>emptySet(), attrMaps);
392 }
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 }
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 }
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 }
435 public String getText() {
436 return StringUtils.toLowerCase(name());
437 }
439 public Attr getAttr(Name attrName) {
440 return Attr.index.get(StringUtils.toLowerCase(attrName.toString()));
441 }
443 public AttrKind getAttrKind(Name attrName) {
444 AttrKind k = attrs.get(getAttr(attrName)); // null-safe
445 return (k == null) ? AttrKind.INVALID : k;
446 }
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 }
454 private static final Map<String,HtmlTag> index = new HashMap<String,HtmlTag>();
455 static {
456 for (HtmlTag t: values()) {
457 index.put(t.getText(), t);
458 }
459 }
461 static HtmlTag get(Name tagName) {
462 return index.get(StringUtils.toLowerCase(tagName.toString()));
463 }
465 }