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