HtmlTag.java revision 2571:10fc81ac75b4
1/*
2 * Copyright (c) 2010, 2014, 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.Set;
29import java.util.Collections;
30import java.util.EnumMap;
31import java.util.EnumSet;
32import java.util.HashMap;
33import java.util.Map;
34
35import javax.lang.model.element.Name;
36
37import static com.sun.tools.doclint.HtmlTag.Attr.*;
38
39import com.sun.tools.javac.util.StringUtils;
40
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 */
57public enum HtmlTag {
58    A(BlockType.INLINE, EndKind.REQUIRED,
59            attrs(AttrKind.OK, HREF, TARGET, NAME)),
60
61    ABBR(BlockType.INLINE, EndKind.REQUIRED,
62            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
63
64    ACRONYM(BlockType.INLINE, EndKind.REQUIRED,
65            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
66
67    ADDRESS(BlockType.INLINE, EndKind.REQUIRED,
68            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
69
70    B(BlockType.INLINE, EndKind.REQUIRED,
71            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
72
73    BIG(BlockType.INLINE, EndKind.REQUIRED,
74            EnumSet.of(Flag.EXPECT_CONTENT)),
75
76    BLOCKQUOTE(BlockType.BLOCK, EndKind.REQUIRED,
77            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
78
79    BODY(BlockType.OTHER, EndKind.REQUIRED),
80
81    BR(BlockType.INLINE, EndKind.NONE,
82            attrs(AttrKind.USE_CSS, CLEAR)),
83
84    CAPTION(BlockType.TABLE_ITEM, EndKind.REQUIRED,
85            EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
86
87    CENTER(BlockType.BLOCK, EndKind.REQUIRED,
88            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
89
90    CITE(BlockType.INLINE, EndKind.REQUIRED,
91            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
92
93    CODE(BlockType.INLINE, EndKind.REQUIRED,
94            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
95
96    DD(BlockType.LIST_ITEM, EndKind.OPTIONAL,
97            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
98
99    DEL(BlockType.INLINE, EndKind.REQUIRED,
100            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST),
101            attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)),
102
103    DFN(BlockType.INLINE, EndKind.REQUIRED,
104            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
105
106    DIV(BlockType.BLOCK, EndKind.REQUIRED,
107            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE)),
108
109    DL(BlockType.BLOCK, EndKind.REQUIRED,
110            EnumSet.of(Flag.EXPECT_CONTENT),
111            attrs(AttrKind.USE_CSS, COMPACT)) {
112        @Override
113        public boolean accepts(HtmlTag t) {
114            return (t == DT) || (t == DD);
115        }
116    },
117
118    DT(BlockType.LIST_ITEM, EndKind.OPTIONAL,
119            EnumSet.of(Flag.ACCEPTS_INLINE, Flag.EXPECT_CONTENT)),
120
121    EM(BlockType.INLINE, EndKind.REQUIRED,
122            EnumSet.of(Flag.NO_NEST)),
123
124    FONT(BlockType.INLINE, EndKind.REQUIRED, // tag itself is deprecated
125            EnumSet.of(Flag.EXPECT_CONTENT),
126            attrs(AttrKind.USE_CSS, SIZE, COLOR, FACE)),
127
128    FRAME(BlockType.OTHER, EndKind.NONE),
129
130    FRAMESET(BlockType.OTHER, EndKind.REQUIRED),
131
132    H1(BlockType.BLOCK, EndKind.REQUIRED),
133    H2(BlockType.BLOCK, EndKind.REQUIRED),
134    H3(BlockType.BLOCK, EndKind.REQUIRED),
135    H4(BlockType.BLOCK, EndKind.REQUIRED),
136    H5(BlockType.BLOCK, EndKind.REQUIRED),
137    H6(BlockType.BLOCK, EndKind.REQUIRED),
138
139    HEAD(BlockType.OTHER, EndKind.REQUIRED),
140
141    HR(BlockType.BLOCK, EndKind.NONE,
142            attrs(AttrKind.OK, WIDTH)), // OK in 4.01; not allowed in 5
143
144    HTML(BlockType.OTHER, EndKind.REQUIRED),
145
146    I(BlockType.INLINE, EndKind.REQUIRED,
147            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
148
149    IMG(BlockType.INLINE, EndKind.NONE,
150            attrs(AttrKind.OK, SRC, ALT, HEIGHT, WIDTH),
151            attrs(AttrKind.OBSOLETE, NAME),
152            attrs(AttrKind.USE_CSS, ALIGN, HSPACE, VSPACE, BORDER)),
153
154    INS(BlockType.INLINE, EndKind.REQUIRED,
155            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST),
156            attrs(AttrKind.OK, Attr.CITE, Attr.DATETIME)),
157
158    KBD(BlockType.INLINE, EndKind.REQUIRED,
159            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
160
161    LI(BlockType.LIST_ITEM, EndKind.OPTIONAL,
162            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
163            attrs(AttrKind.OK, VALUE)),
164
165    LINK(BlockType.OTHER, EndKind.NONE),
166
167    MENU(BlockType.BLOCK, EndKind.REQUIRED) {
168        @Override
169        public boolean accepts(HtmlTag t) {
170            return (t == LI);
171        }
172    },
173
174    META(BlockType.OTHER, EndKind.NONE),
175
176    NOFRAMES(BlockType.OTHER, EndKind.REQUIRED),
177
178    NOSCRIPT(BlockType.BLOCK, EndKind.REQUIRED),
179
180    OL(BlockType.BLOCK, EndKind.REQUIRED,
181            EnumSet.of(Flag.EXPECT_CONTENT),
182            attrs(AttrKind.OK, START, TYPE)) {
183        @Override
184        public boolean accepts(HtmlTag t) {
185            return (t == LI);
186        }
187    },
188
189    P(BlockType.BLOCK, EndKind.OPTIONAL,
190            EnumSet.of(Flag.EXPECT_CONTENT),
191            attrs(AttrKind.USE_CSS, ALIGN)),
192
193    PRE(BlockType.BLOCK, EndKind.REQUIRED,
194            EnumSet.of(Flag.EXPECT_CONTENT)) {
195        @Override
196        public boolean accepts(HtmlTag t) {
197            switch (t) {
198                case IMG: case BIG: case SMALL: case SUB: case SUP:
199                    return false;
200                default:
201                    return (t.blockType == BlockType.INLINE);
202            }
203        }
204    },
205
206    Q(BlockType.INLINE, EndKind.REQUIRED,
207            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
208
209    S(BlockType.INLINE, EndKind.REQUIRED,
210            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
211
212    SAMP(BlockType.INLINE, EndKind.REQUIRED,
213            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
214
215    SCRIPT(BlockType.OTHER, EndKind.REQUIRED),
216
217    SMALL(BlockType.INLINE, EndKind.REQUIRED,
218            EnumSet.of(Flag.EXPECT_CONTENT)),
219
220    SPAN(BlockType.INLINE, EndKind.REQUIRED,
221            EnumSet.of(Flag.EXPECT_CONTENT)),
222
223    STRIKE(BlockType.INLINE, EndKind.REQUIRED,
224            EnumSet.of(Flag.EXPECT_CONTENT)),
225
226    STRONG(BlockType.INLINE, EndKind.REQUIRED,
227            EnumSet.of(Flag.EXPECT_CONTENT)),
228
229    SUB(BlockType.INLINE, EndKind.REQUIRED,
230            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
231
232    SUP(BlockType.INLINE, EndKind.REQUIRED,
233            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
234
235    TABLE(BlockType.BLOCK, EndKind.REQUIRED,
236            EnumSet.of(Flag.EXPECT_CONTENT),
237            attrs(AttrKind.OK, SUMMARY, Attr.FRAME, RULES, BORDER,
238                CELLPADDING, CELLSPACING, WIDTH), // width OK in 4.01; not allowed in 5
239            attrs(AttrKind.USE_CSS, ALIGN, BGCOLOR)) {
240        @Override
241        public boolean accepts(HtmlTag t) {
242            switch (t) {
243                case CAPTION:
244                case THEAD: case TBODY: case TFOOT:
245                case TR: // HTML 3.2
246                    return true;
247                default:
248                    return false;
249            }
250        }
251    },
252
253    TBODY(BlockType.TABLE_ITEM, EndKind.REQUIRED,
254            EnumSet.of(Flag.EXPECT_CONTENT),
255            attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
256        @Override
257        public boolean accepts(HtmlTag t) {
258            return (t == TR);
259        }
260    },
261
262    TD(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
263            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
264            attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR, AXIS,
265                ALIGN, CHAR, CHAROFF, VALIGN),
266            attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
267
268    TFOOT(BlockType.TABLE_ITEM, EndKind.REQUIRED,
269            attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
270        @Override
271        public boolean accepts(HtmlTag t) {
272            return (t == TR);
273        }
274    },
275
276    TH(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
277            EnumSet.of(Flag.ACCEPTS_BLOCK, Flag.ACCEPTS_INLINE),
278            attrs(AttrKind.OK, COLSPAN, ROWSPAN, HEADERS, SCOPE, Attr.ABBR, AXIS,
279                ALIGN, CHAR, CHAROFF, VALIGN),
280            attrs(AttrKind.USE_CSS, WIDTH, BGCOLOR, HEIGHT, NOWRAP)),
281
282    THEAD(BlockType.TABLE_ITEM, EndKind.REQUIRED,
283            attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN)) {
284        @Override
285        public boolean accepts(HtmlTag t) {
286            return (t == TR);
287        }
288    },
289
290    TITLE(BlockType.OTHER, EndKind.REQUIRED),
291
292    TR(BlockType.TABLE_ITEM, EndKind.OPTIONAL,
293            attrs(AttrKind.OK, ALIGN, CHAR, CHAROFF, VALIGN),
294            attrs(AttrKind.USE_CSS, BGCOLOR)) {
295        @Override
296        public boolean accepts(HtmlTag t) {
297            return (t == TH) || (t == TD);
298        }
299    },
300
301    TT(BlockType.INLINE, EndKind.REQUIRED,
302            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
303
304    U(BlockType.INLINE, EndKind.REQUIRED,
305            EnumSet.of(Flag.EXPECT_CONTENT, Flag.NO_NEST)),
306
307    UL(BlockType.BLOCK, EndKind.REQUIRED,
308            EnumSet.of(Flag.EXPECT_CONTENT),
309            attrs(AttrKind.OK, COMPACT, TYPE)) { // OK in 4.01; not allowed in 5
310        @Override
311        public boolean accepts(HtmlTag t) {
312            return (t == LI);
313        }
314    },
315
316    VAR(BlockType.INLINE, EndKind.REQUIRED);
317
318    /**
319     * Enum representing the type of HTML element.
320     */
321    public static enum BlockType {
322        BLOCK,
323        INLINE,
324        LIST_ITEM,
325        TABLE_ITEM,
326        OTHER
327    }
328
329    /**
330     * Enum representing HTML end tag requirement.
331     */
332    public static enum EndKind {
333        NONE,
334        OPTIONAL,
335        REQUIRED
336    }
337
338    public static enum Flag {
339        ACCEPTS_BLOCK,
340        ACCEPTS_INLINE,
341        EXPECT_CONTENT,
342        NO_NEST
343    }
344
345    public static enum Attr {
346        ABBR,
347        ALIGN,
348        ALT,
349        AXIS,
350        BGCOLOR,
351        BORDER,
352        CELLSPACING,
353        CELLPADDING,
354        CHAR,
355        CHAROFF,
356        CITE,
357        CLEAR,
358        CLASS,
359        COLOR,
360        COLSPAN,
361        COMPACT,
362        DATETIME,
363        FACE,
364        FRAME,
365        HEADERS,
366        HEIGHT,
367        HREF,
368        HSPACE,
369        ID,
370        NAME,
371        NOWRAP,
372        REVERSED,
373        ROWSPAN,
374        RULES,
375        SCOPE,
376        SIZE,
377        SPACE,
378        SRC,
379        START,
380        STYLE,
381        SUMMARY,
382        TARGET,
383        TYPE,
384        VALIGN,
385        VALUE,
386        VSPACE,
387        WIDTH;
388
389        public String getText() {
390            return StringUtils.toLowerCase(name());
391        }
392
393        static final Map<String,Attr> index = new HashMap<>();
394        static {
395            for (Attr t: values()) {
396                index.put(t.getText(), t);
397            }
398        }
399    }
400
401    public static enum AttrKind {
402        INVALID,
403        OBSOLETE,
404        USE_CSS,
405        OK
406    }
407
408    // This class exists to avoid warnings from using parameterized vararg type
409    // Map<Attr,AttrKind> in signature of HtmlTag constructor.
410    private static class AttrMap extends EnumMap<Attr,AttrKind>  {
411        private static final long serialVersionUID = 0;
412        AttrMap() {
413            super(Attr.class);
414        }
415    }
416
417
418    public final BlockType blockType;
419    public final EndKind endKind;
420    public final Set<Flag> flags;
421    private final Map<Attr,AttrKind> attrs;
422
423    HtmlTag(BlockType blockType, EndKind endKind, AttrMap... attrMaps) {
424        this(blockType, endKind, Collections.<Flag>emptySet(), attrMaps);
425    }
426
427    HtmlTag(BlockType blockType, EndKind endKind, Set<Flag> flags, AttrMap... attrMaps) {
428        this.blockType = blockType;
429        this.endKind = endKind;
430        this.flags = flags;
431        this.attrs = new EnumMap<>(Attr.class);
432        for (Map<Attr,AttrKind> m: attrMaps)
433            this.attrs.putAll(m);
434        attrs.put(Attr.CLASS, AttrKind.OK);
435        attrs.put(Attr.ID, AttrKind.OK);
436        attrs.put(Attr.STYLE, AttrKind.OK);
437    }
438
439    public boolean accepts(HtmlTag t) {
440        if (flags.contains(Flag.ACCEPTS_BLOCK) && flags.contains(Flag.ACCEPTS_INLINE)) {
441            return (t.blockType == BlockType.BLOCK) || (t.blockType == BlockType.INLINE);
442        } else if (flags.contains(Flag.ACCEPTS_BLOCK)) {
443            return (t.blockType == BlockType.BLOCK);
444        } else if (flags.contains(Flag.ACCEPTS_INLINE)) {
445            return (t.blockType == BlockType.INLINE);
446        } else
447            switch (blockType) {
448                case BLOCK:
449                case INLINE:
450                    return (t.blockType == BlockType.INLINE);
451                case OTHER:
452                    // OTHER tags are invalid in doc comments, and will be
453                    // reported separately, so silently accept/ignore any content
454                    return true;
455                default:
456                    // any combination which could otherwise arrive here
457                    // ought to have been handled in an overriding method
458                    throw new AssertionError(this + ":" + t);
459            }
460    }
461
462    public boolean acceptsText() {
463        // generally, anywhere we can put text we can also put inline tag
464        // so check if a typical inline tag is allowed
465        return accepts(B);
466    }
467
468    public String getText() {
469        return StringUtils.toLowerCase(name());
470    }
471
472    public Attr getAttr(Name attrName) {
473        return Attr.index.get(StringUtils.toLowerCase(attrName.toString()));
474    }
475
476    public AttrKind getAttrKind(Name attrName) {
477        AttrKind k = attrs.get(getAttr(attrName)); // null-safe
478        return (k == null) ? AttrKind.INVALID : k;
479    }
480
481    private static AttrMap attrs(AttrKind k, Attr... attrs) {
482        AttrMap map = new AttrMap();
483        for (Attr a: attrs) map.put(a, k);
484        return map;
485    }
486
487    private static final Map<String,HtmlTag> index = new HashMap<>();
488    static {
489        for (HtmlTag t: values()) {
490            index.put(t.getText(), t);
491        }
492    }
493
494    static HtmlTag get(Name tagName) {
495        return index.get(StringUtils.toLowerCase(tagName.toString()));
496    }
497
498}
499