001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019package com.puppycrawl.tools.checkstyle.api;
020
021import com.google.common.collect.ImmutableMap;
022import java.util.Map;
023
024/**
025 * This enum defines the various Javadoc tags and there properties.
026 *
027 * <p>
028 * This class was modeled after documentation located at
029 * <a href="http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javadoc.html">
030 * javadoc</a>
031 *
032 * and
033 *
034 * <a href="http://java.sun.com/j2se/javadoc/writingdoccomments/index.html">
035 * how to write</a>.
036 * </p>
037 *
038 * <p>
039 * Some of this documentation was a little incomplete (ex: valid placement of
040 * code, value, and literal tags).
041 * </p>
042 *
043 * <p>
044 * Whenever an inconsistency was found the author's judgment was used.
045 * </p>
046 *
047 * <p>
048 * For now, the number of required/optional tag arguments are not included
049 * because some Javadoc tags have very complex rules for determining this
050 * (ex: {@code {@value}} tag).
051 * </p>
052 *
053 * <p>
054 * Also, the {@link #isValidOn(DetailAST) isValidOn} method does not consider
055 * classes defined in a local code block (method, init block, etc.).
056 * </p>
057 *
058 * @author Travis Schneeberger
059 */
060public enum JavadocTagInfo
061{
062    /**
063     * {@code @author}.
064     */
065    AUTHOR("@author", "author", Type.BLOCK, true, true)
066    {
067        /** {@inheritDoc} */
068        @Override
069        public boolean isValidOn(final DetailAST ast)
070        {
071            final int type = ast.getType();
072            return type == TokenTypes.PACKAGE_DEF
073                || type == TokenTypes.CLASS_DEF
074                || type == TokenTypes.INTERFACE_DEF
075                || type == TokenTypes.ENUM_DEF
076                || type == TokenTypes.ANNOTATION_DEF;
077        }
078    },
079
080    /**
081     * {@code {@code}}.
082     */
083    CODE("{@code}", "code", Type.INLINE, true, true)
084    {
085        /** {@inheritDoc} */
086        @Override
087        public boolean isValidOn(final DetailAST ast)
088        {
089            final int type = ast.getType();
090            return type == TokenTypes.PACKAGE_DEF
091                || type == TokenTypes.CLASS_DEF
092                || type == TokenTypes.INTERFACE_DEF
093                || type == TokenTypes.ENUM_DEF
094                || type == TokenTypes.ANNOTATION_DEF
095                || type == TokenTypes.METHOD_DEF
096                || type == TokenTypes.CTOR_DEF
097                || (type == TokenTypes.VARIABLE_DEF
098                && !ScopeUtils.isLocalVariableDef(ast));
099        }
100    },
101
102    /**
103     * {@code {@docRoot}}.
104     */
105    DOC_ROOT("{@docRoot}", "docRoot", Type.INLINE, true, true)
106    {
107        /** {@inheritDoc} */
108        @Override
109        public boolean isValidOn(final DetailAST ast)
110        {
111            final int type = ast.getType();
112            return type == TokenTypes.PACKAGE_DEF
113                || type == TokenTypes.CLASS_DEF
114                || type == TokenTypes.INTERFACE_DEF
115                || type == TokenTypes.ENUM_DEF
116                || type == TokenTypes.ANNOTATION_DEF
117                || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF
118                || (type == TokenTypes.VARIABLE_DEF
119                && !ScopeUtils.isLocalVariableDef(ast));
120        }
121    },
122
123    /**
124     * {@code @deprecated}.
125     */
126    DEPRECATED("@deprecated", "deprecated", Type.BLOCK, false, false)
127    {
128        /** {@inheritDoc} */
129        @Override
130        public boolean isValidOn(final DetailAST ast)
131        {
132            final int type = ast.getType();
133            return type == TokenTypes.CLASS_DEF
134                || type == TokenTypes.INTERFACE_DEF
135                || type == TokenTypes.ENUM_DEF
136                || type == TokenTypes.ANNOTATION_DEF
137                || type == TokenTypes.METHOD_DEF
138                || type == TokenTypes.CTOR_DEF
139                || type == TokenTypes.ENUM_CONSTANT_DEF
140                || type == TokenTypes.ANNOTATION_FIELD_DEF
141                || (type == TokenTypes.VARIABLE_DEF
142                && !ScopeUtils.isLocalVariableDef(ast));
143        }
144    },
145
146    /**
147     * {@code @exception}.
148     */
149    EXCEPTION("@exception", "exception", Type.BLOCK, false, false)
150    {
151        /** {@inheritDoc} */
152        @Override
153        public boolean isValidOn(final DetailAST ast)
154        {
155            final int type = ast.getType();
156            return type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF;
157        }
158    },
159
160    /**
161     * {@code {@inheritDoc}}.
162     */
163    INHERIT_DOC("{@inheritDoc}", "inheritDoc", Type.INLINE, false, false)
164    {
165        /** {@inheritDoc} */
166        @Override
167        public boolean isValidOn(final DetailAST ast)
168        {
169            final int type = ast.getType();
170
171            return type == TokenTypes.METHOD_DEF
172                && !ast.branchContains(TokenTypes.LITERAL_STATIC)
173                && ScopeUtils.getScopeFromMods(ast
174                    .findFirstToken(TokenTypes.MODIFIERS)) != Scope.PRIVATE;
175        }
176    },
177
178    /**
179     * {@code {@link}}.
180     */
181    LINK("{@link}", "link", Type.INLINE, true, true)
182    {
183        /** {@inheritDoc} */
184        @Override
185        public boolean isValidOn(final DetailAST ast)
186        {
187            final int type = ast.getType();
188            return type == TokenTypes.PACKAGE_DEF
189                || type == TokenTypes.CLASS_DEF
190                || type == TokenTypes.INTERFACE_DEF
191                || type == TokenTypes.ENUM_DEF
192                || type == TokenTypes.ANNOTATION_DEF
193                || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF
194                || (type == TokenTypes.VARIABLE_DEF
195                && !ScopeUtils.isLocalVariableDef(ast));
196        }
197    },
198
199    /**
200     * {@code {@linkplain}}.
201     */
202    LINKPLAIN("{@linkplain}", "linkplain", Type.INLINE, true, true)
203    {
204        /** {@inheritDoc} */
205        @Override
206        public boolean isValidOn(final DetailAST ast)
207        {
208            final int type = ast.getType();
209            return type == TokenTypes.PACKAGE_DEF
210                || type == TokenTypes.CLASS_DEF
211                || type == TokenTypes.INTERFACE_DEF
212                || type == TokenTypes.ENUM_DEF
213                || type == TokenTypes.ANNOTATION_DEF
214                || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF
215                || (type == TokenTypes.VARIABLE_DEF
216                && !ScopeUtils.isLocalVariableDef(ast));
217        }
218    },
219
220    /**
221     * {@code {@literal}}.
222     */
223    LITERAL("{@literal}", "literal", Type.INLINE, true, true)
224    {
225        /** {@inheritDoc} */
226        @Override
227        public boolean isValidOn(final DetailAST ast)
228        {
229            final int type = ast.getType();
230            return type == TokenTypes.PACKAGE_DEF
231                || type == TokenTypes.CLASS_DEF
232                || type == TokenTypes.INTERFACE_DEF
233                || type == TokenTypes.ENUM_DEF
234                || type == TokenTypes.ANNOTATION_DEF
235                || type == TokenTypes.METHOD_DEF || type == TokenTypes.CTOR_DEF
236                || (type == TokenTypes.VARIABLE_DEF
237                && !ScopeUtils.isLocalVariableDef(ast));
238        }
239    },
240
241    /**
242     * {@code @param}.
243     */
244    PARAM("@param", "param", Type.BLOCK, false, false)
245    {
246        /** {@inheritDoc} */
247        @Override
248        public boolean isValidOn(final DetailAST ast)
249        {
250            final int type = ast.getType();
251            return type == TokenTypes.CLASS_DEF
252                || type == TokenTypes.INTERFACE_DEF
253                || type == TokenTypes.METHOD_DEF
254                || type == TokenTypes.CTOR_DEF;
255        }
256    },
257
258    /**
259     * {@code @return}.
260     */
261    RETURN("@return", "return", Type.BLOCK, false, false)
262    {
263        /** {@inheritDoc} */
264        @Override
265        public boolean isValidOn(final DetailAST ast)
266        {
267            final int type = ast.getType();
268            final DetailAST returnType = ast.findFirstToken(TokenTypes.TYPE);
269
270            return type == TokenTypes.METHOD_DEF
271                && returnType.getFirstChild().getType()
272                != TokenTypes.LITERAL_VOID;
273
274        }
275    },
276
277    /**
278     * {@code @see}.
279     */
280    SEE("@see", "see", Type.BLOCK, true, true)
281    {
282        /** {@inheritDoc} */
283        @Override
284        public boolean isValidOn(final DetailAST ast)
285        {
286            final int type = ast.getType();
287            return type == TokenTypes.PACKAGE_DEF
288                || type == TokenTypes.CLASS_DEF
289                || type == TokenTypes.INTERFACE_DEF
290                || type == TokenTypes.ENUM_DEF
291                || type == TokenTypes.ANNOTATION_DEF
292                || type == TokenTypes.METHOD_DEF
293                || type == TokenTypes.CTOR_DEF
294                || (type == TokenTypes.VARIABLE_DEF
295                && !ScopeUtils.isLocalVariableDef(ast));
296        }
297    },
298
299    /**
300     * {@code @serial}.
301     */
302    SERIAL("@serial", "serial", Type.BLOCK, true, false)
303    {
304        /** {@inheritDoc} */
305        @Override
306        public boolean isValidOn(final DetailAST ast)
307        {
308            final int type = ast.getType();
309
310            return type == TokenTypes.VARIABLE_DEF
311                && !ScopeUtils.isLocalVariableDef(ast);
312        }
313    },
314
315    /**
316     * {@code @serialData}.
317     */
318    SERIAL_DATA("@serialData", "serialData", Type.BLOCK, false, false)
319    {
320        /** {@inheritDoc} */
321        @Override
322        public boolean isValidOn(final DetailAST ast)
323        {
324            final int type = ast.getType();
325            final DetailAST methodNameAst = ast
326                .findFirstToken(TokenTypes.IDENT);
327            final String methodName = methodNameAst.getText();
328
329            return type == TokenTypes.METHOD_DEF
330                && ("writeObject".equals(methodName)
331                    || "readObject".equals(methodName)
332                    || "writeExternal".equals(methodName)
333                    || "readExternal".equals(methodName)
334                    || "writeReplace".equals(methodName)
335                    || "readResolve"
336                    .equals(methodName));
337        }
338    },
339
340    /**
341     * {@code @serialField}.
342     */
343    SERIAL_FIELD("@serialField", "serialField", Type.BLOCK, false, false)
344    {
345        /** {@inheritDoc} */
346        @Override
347        public boolean isValidOn(final DetailAST ast)
348        {
349            final int type = ast.getType();
350            final DetailAST varType = ast.findFirstToken(TokenTypes.TYPE);
351
352            return type == TokenTypes.VARIABLE_DEF
353                && varType.getType() == TokenTypes.ARRAY_DECLARATOR
354                && "ObjectStreafield"
355                    .equals(varType.getFirstChild().getText());
356        }
357    },
358
359    /**
360     * {@code @since}.
361     */
362    SINCE("@since", "since", Type.BLOCK, true, true)
363    {
364        /** {@inheritDoc} */
365        @Override
366        public boolean isValidOn(final DetailAST ast)
367        {
368            final int type = ast.getType();
369            return type == TokenTypes.PACKAGE_DEF
370                || type == TokenTypes.CLASS_DEF
371                || type == TokenTypes.INTERFACE_DEF
372                || type == TokenTypes.ENUM_DEF
373                || type == TokenTypes.ANNOTATION_DEF
374                || type == TokenTypes.METHOD_DEF
375                || type == TokenTypes.CTOR_DEF
376                || (type == TokenTypes.VARIABLE_DEF
377                && !ScopeUtils.isLocalVariableDef(ast));
378        }
379    },
380
381    /**
382     * {@code @throws}.
383     */
384    THROWS("@throws", "throws", Type.BLOCK, false, false)
385    {
386        /** {@inheritDoc} */
387        @Override
388        public boolean isValidOn(final DetailAST ast)
389        {
390            final int type = ast.getType();
391            return type == TokenTypes.METHOD_DEF
392                || type == TokenTypes.CTOR_DEF;
393        }
394    },
395
396    /**
397     * {@code {@value}}.
398     */
399    VALUE("{@value}", "value", Type.INLINE, true, true)
400    {
401        /** {@inheritDoc} */
402        @Override
403        public boolean isValidOn(final DetailAST ast)
404        {
405            final int type = ast.getType();
406            return type == TokenTypes.PACKAGE_DEF
407                || type == TokenTypes.CLASS_DEF
408                || type == TokenTypes.INTERFACE_DEF
409                || type == TokenTypes.ENUM_DEF
410                || type == TokenTypes.ANNOTATION_DEF
411                || type == TokenTypes.METHOD_DEF
412                || type == TokenTypes.CTOR_DEF
413                || (type == TokenTypes.VARIABLE_DEF
414                && !ScopeUtils.isLocalVariableDef(ast));
415        }
416    },
417
418    /**
419     * {@code @version}.
420     */
421    VERSION("@version", "version", Type.BLOCK, true, true)
422    {
423        /** {@inheritDoc} */
424        @Override
425        public boolean isValidOn(final DetailAST ast)
426        {
427            final int type = ast.getType();
428            return type == TokenTypes.PACKAGE_DEF
429                || type == TokenTypes.CLASS_DEF
430                || type == TokenTypes.INTERFACE_DEF
431                || type == TokenTypes.ENUM_DEF
432                || type == TokenTypes.ANNOTATION_DEF;
433        }
434    };
435
436    /** holds tag text to tag enum mappings **/
437    private static final Map<String, JavadocTagInfo> TEXT_TO_TAG;
438    /** holds tag name to tag enum mappings **/
439    private static final Map<String, JavadocTagInfo> NAME_TO_TAG;
440
441    static
442    {
443        final ImmutableMap.Builder<String, JavadocTagInfo> textToTagBuilder =
444            new ImmutableMap.Builder<String, JavadocTagInfo>();
445
446        final ImmutableMap.Builder<String, JavadocTagInfo> nameToTagBuilder =
447            new ImmutableMap.Builder<String, JavadocTagInfo>();
448
449        for (final JavadocTagInfo tag : JavadocTagInfo.values()) {
450            textToTagBuilder.put(tag.getText(), tag);
451            nameToTagBuilder.put(tag.getName(), tag);
452        }
453
454        TEXT_TO_TAG = textToTagBuilder.build();
455        NAME_TO_TAG = nameToTagBuilder.build();
456    }
457
458    /** the tag text **/
459    private final String text;
460    /** the tag name **/
461    private final String name;
462    /** the tag type **/
463    private final Type type;
464    /** if tag is valid in package.html **/
465    private final boolean validInPackageHtml;
466    /** if tag is valid in overview.html **/
467    private final boolean validInOverviewHtml;
468
469    /**
470     * Sets the various properties of a Javadoc tag.
471     *
472     * @param text the tag text
473     * @param name the tag name
474     * @param type the type of tag
475     * @param validInPackageHtml whether the tag is valid
476     * in package.html file
477     * @param validInOverviewHtml whether the tag is valid
478     * in overview.html file
479     */
480    private JavadocTagInfo(final String text, final String name,
481        final Type type, final boolean validInPackageHtml,
482        final boolean validInOverviewHtml)
483    {
484        this.text = text;
485        this.name = name;
486        this.type = type;
487        this.validInPackageHtml = validInPackageHtml;
488        this.validInOverviewHtml = validInOverviewHtml;
489    }
490
491    /**
492     * Checks if a particular Javadoc tag is valid within a Javadoc block of a
493     * given AST.
494     *
495     * <p>
496     * For example: Given a call to
497     * <code>JavadocTag.RETURN{@link #isValidOn(DetailAST)}</code>.
498     * </p>
499     *
500     * <p>
501     * If passing in a DetailAST representing a non-void METHOD_DEF
502     * <code> true </code> would be returned. If passing in a DetailAST
503     * representing a CLASS_DEF <code> false </code> would be returned because
504     * CLASS_DEF's cannot return a value.
505     * </p>
506     *
507     * @param ast the AST representing a type that can be Javadoc'd
508     * @return true if tag is valid.
509     */
510    public abstract boolean isValidOn(DetailAST ast);
511
512    /**
513     * Checks if tag is valid in a package.html Javadoc file.
514     *
515     * @return true if tag is valid.
516     */
517    public boolean isValidInPackageHtml()
518    {
519        return this.validInPackageHtml;
520    }
521
522    /**
523     * Checks if tag is valid in a overview.html Javadoc file.
524     *
525     * @return true if tag is valid.
526     */
527    public boolean isValidInOverviewHtml()
528    {
529        return this.validInOverviewHtml;
530    }
531
532    /**
533     * Gets the tag text.
534     * @return the tag text
535     */
536    public String getText()
537    {
538        return this.text;
539    }
540
541    /**
542     * Gets the tag name.
543     * @return the tag name
544     */
545    public String getName()
546    {
547        return this.name;
548    }
549
550    /**
551     * Gets the Tag type defined by {@link JavadocTagInfo.Type Type}.
552     * @return the Tag type
553     */
554    public Type getType()
555    {
556        return this.type;
557    }
558
559    /**
560     * returns a JavadocTag from the tag text.
561     * @param text String representing the tag text
562     * @return Returns a JavadocTag type from a String representing the tag
563     * @throws NullPointerException if the text is null
564     * @throws IllegalArgumentException if the text is not a valid tag
565     */
566    public static JavadocTagInfo fromText(final String text)
567    {
568        if (text == null) {
569            throw new NullPointerException("the text is null");
570        }
571
572        final JavadocTagInfo tag = TEXT_TO_TAG.get(text);
573
574        if (tag == null) {
575            throw new IllegalArgumentException("the text [" + text
576                + "] is not a valid Javadoc tag text");
577        }
578
579        return tag;
580    }
581
582    /**
583     * returns a JavadocTag from the tag name.
584     * @param name String name of the tag
585     * @return Returns a JavadocTag type from a String representing the tag
586     * @throws NullPointerException if the text is null
587     * @throws IllegalArgumentException if the text is not a valid tag. The name
588     *    can be checked using {@link JavadocTagInfo#isValidName(String)}
589     */
590    public static JavadocTagInfo fromName(final String name)
591    {
592        if (name == null) {
593            throw new NullPointerException("the name is null");
594        }
595
596        final JavadocTagInfo tag = NAME_TO_TAG.get(name);
597
598        if (tag == null) {
599            throw new IllegalArgumentException("the name [" + name
600                + "] is not a valid Javadoc tag name");
601        }
602
603        return tag;
604    }
605
606    /**
607     * Returns whether the provided name is for a valid tag.
608     * @param name the tag name to check.
609     * @return whether the provided name is for a valid tag.
610     */
611    public static boolean isValidName(final String name)
612    {
613        return NAME_TO_TAG.containsKey(name);
614    }
615
616    /**
617     * {@inheritDoc}
618     */
619    @Override
620    public String toString()
621    {
622        return "text [" + this.text + "] name [" + this.name
623            + "] type [" + this.type
624            + "] validInPackageHtml [" + this.validInPackageHtml
625            + "] validInOverviewHtml [" + this.validInOverviewHtml + "]";
626    }
627
628    /**
629     * The Javadoc Type.
630     *
631     * For example a {@code @param} tag is a block tag while a
632     * {@code {@link}} tag is a inline tag.
633     *
634     * @author Travis Schneeberger
635     */
636    public enum Type
637    {
638        /** block type. **/
639        BLOCK,
640
641        /** inline type. **/
642        INLINE;
643    }
644}