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
021
022/**
023 * Contains utility methods designed to work with annotations.
024 *
025 * @author Travis Schneeberger
026 */
027public final class AnnotationUtility
028{
029    /**
030     * private utility constructor.
031     * @throws UnsupportedOperationException if called
032     */
033    private AnnotationUtility()
034    {
035        throw new UnsupportedOperationException("do not instantiate.");
036    }
037
038    /**
039     * Checks to see if the AST is annotated with
040     * the passed in annotation.
041     *
042     * <p>
043     * This method will not look for imports or package
044     * statements to detect the passed in annotation.
045     * </p>
046     *
047     * <p>
048     * To check if an AST contains a passed in annotation
049     * taking into account fully-qualified names
050     * (ex: java.lang.Override, Override)
051     * this method will need to be called twice. Once for each
052     * name given.
053     * </p>
054     *
055     * @param ast the current node
056     * @param annotation the annotation name to check for
057     * @return true if contains the annotation
058     * @throws NullPointerException if the ast or
059     * annotation is null
060     */
061    public static boolean containsAnnotation(final DetailAST ast,
062        String annotation)
063    {
064        return AnnotationUtility.getAnnotation(ast, annotation) != null;
065    }
066
067    /**
068     * Checks to see if the AST is annotated with
069     * any annotation.
070     *
071     * @param ast the current node
072     * @return true if contains an annotation
073     * @throws NullPointerException if the ast is null
074     */
075    public static boolean containsAnnotation(final DetailAST ast)
076    {
077        final DetailAST holder = AnnotationUtility.getAnnotationHolder(ast);
078        return holder != null && holder.branchContains(TokenTypes.ANNOTATION);
079    }
080
081    /**
082     * Gets the AST that holds a series of annotations for the
083     * potentially annotated AST.  Returns <code>null</code>
084     * the passed in AST is not have an Annotation Holder.
085     *
086     * @param ast the current node
087     * @return the Annotation Holder
088     * @throws NullPointerException if the ast is null
089     */
090    public static DetailAST getAnnotationHolder(DetailAST ast)
091    {
092        if (ast == null) {
093            throw new NullPointerException("the ast is null");
094        }
095
096        final DetailAST annotationHolder;
097
098        if (ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
099            || ast.getType() == TokenTypes.PACKAGE_DEF)
100        {
101            annotationHolder = ast.findFirstToken(TokenTypes.ANNOTATIONS);
102        }
103        else {
104            annotationHolder = ast.findFirstToken(TokenTypes.MODIFIERS);
105        }
106
107        return annotationHolder;
108    }
109
110    /**
111     * Checks to see if the AST is annotated with
112     * the passed in annotation and return the AST
113     * representing that annotation.
114     *
115     * <p>
116     * This method will not look for imports or package
117     * statements to detect the passed in annotation.
118     * </p>
119     *
120     * <p>
121     * To check if an AST contains a passed in annotation
122     * taking into account fully-qualified names
123     * (ex: java.lang.Override, Override)
124     * this method will need to be called twice. Once for each
125     * name given.
126     * </p>
127     *
128     * @param ast the current node
129     * @param annotation the annotation name to check for
130     * @return the AST representing that annotation
131     * @throws NullPointerException if the ast or
132     * annotation is null
133     */
134    public static DetailAST getAnnotation(final DetailAST ast,
135        String annotation)
136    {
137        if (ast == null) {
138            throw new NullPointerException("the ast is null");
139        }
140
141        if (annotation == null) {
142            throw new NullPointerException("the annotation is null");
143        }
144
145        if (annotation.trim().length() == 0) {
146            throw new IllegalArgumentException("the annotation"
147                + "is empty or spaces");
148        }
149
150        final DetailAST holder = AnnotationUtility.getAnnotationHolder(ast);
151
152        for (DetailAST child = holder.getFirstChild();
153            child != null; child = child.getNextSibling())
154        {
155            if (child.getType() == TokenTypes.ANNOTATION) {
156                final DetailAST at = child.getFirstChild();
157                final String name =
158                    FullIdent.createFullIdent(at.getNextSibling()).getText();
159                if (annotation.equals(name)) {
160                    return child;
161                }
162            }
163        }
164
165        return null;
166    }
167
168    /**
169     * Checks to see what the passed in AST (representing
170     * an annotation) is annotating.
171     *
172     * @param ast the AST representing an annotation.
173     * @return the AST the annotation is annotating.
174     * @throws NullPointerException if the ast is null
175     * @throws IllegalArgumentException if the ast is not
176     * an {@link TokenTypes#ANNOTATION}
177     */
178    public static DetailAST annotatingWhat(DetailAST ast)
179    {
180        if (ast == null) {
181            throw new NullPointerException("the ast is null");
182        }
183
184        if (ast.getType() != TokenTypes.ANNOTATION) {
185            throw new IllegalArgumentException(
186                "The ast is not an annotation. AST: " + ast);
187        }
188
189        return ast.getParent().getParent();
190    }
191
192    /**
193     * Checks to see if the passed in AST (representing
194     * an annotation) is annotating the passed in type.
195     * @param ast the AST representing an annotation
196     * @param tokenType the passed in type
197     * @return true if the annotation is annotating a type
198     * equal to the passed in type
199     * @throws NullPointerException if the ast is null
200     * @throws IllegalArgumentException if the ast is not
201     * an {@link TokenTypes#ANNOTATION}
202     */
203    public static boolean isAnnotatingType(DetailAST ast, int tokenType)
204    {
205        final DetailAST astNode = AnnotationUtility.annotatingWhat(ast);
206        return astNode.getType() == tokenType;
207    }
208}