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