HtmlTag.java revision 3896:8e4dbcb99277
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
26package com.sun.tools.doclint;
27
28import java.util.Collections;
29import java.util.EnumMap;
30import java.util.EnumSet;
31import java.util.HashMap;
32import java.util.Map;
33import java.util.Set;
34import javax.lang.model.element.Name;
35
36import com.sun.tools.javac.util.StringUtils;
37
38import static com.sun.tools.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 */
58public 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
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
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
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
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
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
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
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    SUB(BlockType.INLINE, EndKind.REQUIRED,
309            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
310
311    SUP(BlockType.INLINE, EndKind.REQUIRED,
312            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
313
314    TABLE(BlockType.BLOCK, EndKind.REQUIRED,
315            EnumSet.of(Flag.EXPECT_CONTENT),
316            attrs(AttrKind.ALL, BORDER),
317            attrs(AttrKind.HTML4, SUMMARY, CELLPADDING, CELLSPACING, Attr.FRAME, RULES, WIDTH),
318            attrs(AttrKind.USE_CSS, ALIGN, BGCOLOR)) {
319        @Override
320        public boolean accepts(HtmlTag t) {
321            switch (t) {
322                case CAPTION:
323                case COLGROUP:
324                case THEAD: case TBODY: case TFOOT:
325                case TR: // HTML 3.2
326                    return true;
327                default:
328                    return false;
329            }
330        }
331    },
332
333    TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED,
334            EnumSet.of(Flag.EXPECT_CONTENT),
335            attrs(AttrKind.ALL, VALIGN),
336            attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) {
337        @Override
338        public boolean accepts(HtmlTag t) {
339            return (t == TR);
340        }
341    },
342
343    TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
344            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
345            attrs(AttrKind.ALL, COLSPAN, ROWSPAN, HEADERS, VALIGN),
346            attrs(AttrKind.HTML4, AXIS, Attr.ABBR, SCOPE, ALIGN, CHAR, CHAROFF),
347            attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
348
349    TEMPLATE(HtmlVersion.HTML5, BlockType.BLOCK, EndKind.REQUIRED,
350            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
351
352    TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED,
353            attrs(AttrKind.ALL, VALIGN),
354            attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) {
355        @Override
356        public boolean accepts(HtmlTag t) {
357            return (t == TR);
358        }
359    },
360
361    TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
362            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
363            attrs(AttrKind.ALL, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR,
364                VALIGN),
365            attrs(AttrKind.HTML4, AXIS, ALIGN, CHAR, CHAROFF),
366            attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
367
368    THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED,
369            attrs(AttrKind.ALL, VALIGN),
370            attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF)) {
371        @Override
372        public boolean accepts(HtmlTag t) {
373            return (t == TR);
374        }
375    },
376
377    TIME(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED),
378
379    TITLE(BlockType.OTHER, EndKind.REQUIRED),
380
381    TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
382            attrs(AttrKind.ALL, VALIGN),
383            attrs(AttrKind.HTML4, ALIGN, CHAR, CHAROFF),
384            attrs(AttrKind.USE_CSS, BGCOLOR)) {
385        @Override
386        public boolean accepts(HtmlTag t) {
387            return (t == TH) || (t == TD);
388        }
389    },
390
391    TT(HtmlVersion.HTML4, BlockType.INLINE, EndKind.REQUIRED,
392            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
393
394    U(BlockType.INLINE, EndKind.REQUIRED,
395            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
396
397    UL(BlockType.BLOCK, EndKind.REQUIRED,
398            EnumSet.of(Flag.EXPECT_CONTENT),
399            attrs(AttrKind.HTML4, COMPACT, TYPE)) {
400        @Override
401        public boolean accepts(HtmlTag t) {
402            return (t == LI);
403        }
404    },
405
406    WBR(HtmlVersion.HTML5, BlockType.INLINE, EndKind.REQUIRED),
407
408    VAR(BlockType.INLINE, EndKind.REQUIRED);
409
410    /**
411     * Enum representing the type of HTML element.
412     */
413    public static enum BlockType {
414        BLOCK,
415        INLINE,
416        LIST_ITEM,
417        TABLE_ITEM,
418        OTHER
419    }
420
421    /**
422     * Enum representing HTML end tag requirement.
423     */
424    public static enum EndKind {
425        NONE,
426        OPTIONAL,
427        REQUIRED
428    }
429
430    public static enum Flag {
431        ACCEPTS_BLOCK,
432        ACCEPTS_INLINE,
433        EXPECT_CONTENT,
434        NO_NEST
435    }
436
437    public static enum Attr {
438        ABBR,
439        ALIGN,
440        ALINK,
441        ALT,
442        ARIA_ACTIVEDESCENDANT,
443        ARIA_CONTROLS,
444        ARIA_DESCRIBEDBY,
445        ARIA_EXPANDED,
446        ARIA_LABEL,
447        ARIA_LABELLEDBY,
448        ARIA_LEVEL,
449        ARIA_MULTISELECTABLE,
450        ARIA_OWNS,
451        ARIA_POSINSET,
452        ARIA_SETSIZE,
453        ARIA_READONLY,
454        ARIA_REQUIRED,
455        ARIA_SELECTED,
456        ARIA_SORT,
457        AXIS,
458        BACKGROUND,
459        BGCOLOR,
460        BORDER,
461        CELLSPACING,
462        CELLPADDING,
463        CHAR,
464        CHAROFF,
465        CHARSET,
466        CITE,
467        CLEAR,
468        CLASS,
469        COLOR,
470        COLSPAN,
471        COMPACT,
472        COORDS,
473        CROSSORIGIN,
474        DATETIME,
475        FACE,
476        FRAME,
477        FRAMEBORDER,
478        HEADERS,
479        HEIGHT,
480        HREF,
481        HSPACE,
482        ID,
483        LINK,
484        LONGDESC,
485        MARGINHEIGHT,
486        MARGINWIDTH,
487        NAME,
488        NOSHADE,
489        NOWRAP,
490        PROFILE,
491        REV,
492        REVERSED,
493        ROLE,
494        ROWSPAN,
495        RULES,
496        SCHEME,
497        SCOPE,
498        SCROLLING,
499        SHAPE,
500        SIZE,
501        SPACE,
502        SRC,
503        START,
504        STYLE,
505        SUMMARY,
506        TARGET,
507        TEXT,
508        TYPE,
509        VALIGN,
510        VALUE,
511        VERSION,
512        VLINK,
513        VSPACE,
514        WIDTH;
515
516        private final String name;
517
518        Attr() {
519            name = StringUtils.toLowerCase(name().replace("_", "-"));
520        }
521
522        public String getText() {
523            return name;
524        }
525
526        static final Map<String,Attr> index = new HashMap<>();
527        static {
528            for (Attr t: values()) {
529                index.put(t.getText(), t);
530            }
531        }
532    }
533
534    public static enum AttrKind {
535        HTML4,
536        HTML5,
537        INVALID,
538        OBSOLETE,
539        USE_CSS,
540        ALL
541    }
542
543    // This class exists to avoid warnings from using parameterized vararg type
544    // Map<Attr,AttrKind> in signature of HtmlTag constructor.
545    private static class AttrMap extends EnumMap<Attr,AttrKind>  {
546        private static final long serialVersionUID = 0;
547        AttrMap() {
548            super(Attr.class);
549        }
550    }
551
552
553    public final HtmlVersion allowedVersion;
554    public final BlockType blockType;
555    public final EndKind endKind;
556    public final Set<Flag> flags;
557    private final Map<Attr,AttrKind> attrs;
558
559    HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) {
560        this(HtmlVersion.ALL, blockType, endKind, Collections.emptySet(), attrMaps);
561    }
562
563    HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, AttrMap... attrMaps) {
564        this(allowedVersion, blockType, endKind, Collections.emptySet(), attrMaps);
565    }
566
567    HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) {
568        this(HtmlVersion.ALL, blockType, endKind, flags, attrMaps);
569    }
570
571    HtmlTag(HtmlVersion allowedVersion, BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) {
572        this.allowedVersion = allowedVersion;
573        this.blockType = blockType;
574        this.endKind = endKind;
575        this.flags = flags;
576        this.attrs = new EnumMap<>(Attr.class);
577        for (Map<Attr,AttrKind> m: attrMaps)
578            this.attrs.putAll(m);
579        attrs.put(Attr.CLASS, AttrKind.ALL);
580        attrs.put(Attr.ID, AttrKind.ALL);
581        attrs.put(Attr.STYLE, AttrKind.ALL);
582        attrs.put(Attr.ROLE, AttrKind.HTML5);
583        // for now, assume that all ARIA attributes are allowed on all tags.
584        attrs.put(Attr.ARIA_ACTIVEDESCENDANT, AttrKind.HTML5);
585        attrs.put(Attr.ARIA_CONTROLS, AttrKind.HTML5);
586        attrs.put(Attr.ARIA_DESCRIBEDBY, AttrKind.HTML5);
587        attrs.put(Attr.ARIA_EXPANDED, AttrKind.HTML5);
588        attrs.put(Attr.ARIA_LABEL, AttrKind.HTML5);
589        attrs.put(Attr.ARIA_LABELLEDBY, AttrKind.HTML5);
590        attrs.put(Attr.ARIA_LEVEL, AttrKind.HTML5);
591        attrs.put(Attr.ARIA_MULTISELECTABLE, AttrKind.HTML5);
592        attrs.put(Attr.ARIA_OWNS, AttrKind.HTML5);
593        attrs.put(Attr.ARIA_POSINSET, AttrKind.HTML5);
594        attrs.put(Attr.ARIA_READONLY, AttrKind.HTML5);
595        attrs.put(Attr.ARIA_REQUIRED, AttrKind.HTML5);
596        attrs.put(Attr.ARIA_SELECTED, AttrKind.HTML5);
597        attrs.put(Attr.ARIA_SETSIZE, AttrKind.HTML5);
598        attrs.put(Attr.ARIA_SORT, AttrKind.HTML5);
599    }
600
601    public boolean accepts(HtmlTag t) {
602        if (flags.contains(Flag.ACCEPTS_BLOCK) && flags.contains(Flag.ACCEPTS_INLINE)) {
603            return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE);
604        } else if (flags.contains(Flag.ACCEPTS_BLOCK)) {
605            return (t.blockType == BlockType.BLOCK);
606        } else if (flags.contains(Flag.ACCEPTS_INLINE)) {
607            return (t.blockType == BlockType.INLINE);
608        } else
609            switch (blockType) {
610                case BLOCK:
611                case INLINE:
612                    return (t.blockType == BlockType.INLINE);
613                case OTHER:
614                    // OTHER tags are invalid in doc comments, and will be
615                    // reported separately, so silently accept/ignore any content
616                    return true;
617                default:
618                    // any combination which could otherwise arrive here
619                    // ought to have been handled in an overriding method
620                    throw new AssertionError(this + ":" + t);
621            }
622    }
623
624    public boolean acceptsText() {
625        // generally, anywhere we can put text we can also put inline tag
626        // so check if a typical inline tag is allowed
627        return accepts(B);
628    }
629
630    public String getText() {
631        return StringUtils.toLowerCase(name());
632    }
633
634    public Attr getAttr(Name attrName) {
635        return Attr.index.get(StringUtils.toLowerCase(attrName.toString()));
636    }
637
638    public AttrKind getAttrKind(Name attrName) {
639        AttrKind k = attrs.get(getAttr(attrName)); // null-safe
640        return (k == null) ? AttrKind.INVALID : k;
641    }
642
643    private static AttrMap attrs(AttrKind k, Attr... attrs) {
644        AttrMap map = new AttrMap();
645        for (Attr a: attrs) map.put(a, k);
646        return map;
647    }
648
649    private static final Map<String, HtmlTag> index = new HashMap<>();
650    static {
651        for (HtmlTag t: values()) {
652            index.put(t.getText(), t);
653        }
654    }
655
656    public static HtmlTag get(Name tagName) {
657        return index.get(StringUtils.toLowerCase(tagName.toString()));
658    }
659}
660