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 aAST the current node
056     * @param aAnnotation the annotation name to check for
057     * @return true if contains the annotation
058     * @throws NullPointerException if the aAST or
059     * aAnnotation is null
060     */
061    public static boolean containsAnnotation(final DetailAST aAST,
062        String aAnnotation)
063    {
064        return AnnotationUtility.getAnnotation(aAST, aAnnotation) != null;
065    }
066
067    /**
068     * Checks to see if the AST is annotated with
069     * any annotation.
070     *
071     * @param aAST the current node
072     * @return true if contains an annotation
073     * @throws NullPointerException if the aAST is null
074     */
075    public static boolean containsAnnotation(final DetailAST aAST)
076    {
077        final DetailAST holder = AnnotationUtility.getAnnotationHolder(aAST);
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 aAST the current node
087     * @return the Annotation Holder
088     * @throws NullPointerException if the aAST is null
089     */
090    public static DetailAST getAnnotationHolder(DetailAST aAST)
091    {
092        if (aAST == null) {
093            throw new NullPointerException("the aAST is null");
094        }
095
096        final DetailAST annotationHolder;
097
098        if (aAST.getType() == TokenTypes.ENUM_CONSTANT_DEF
099            || aAST.getType() == TokenTypes.PACKAGE_DEF)
100        {
101            annotationHolder = aAST.findFirstToken(TokenTypes.ANNOTATIONS);
102        }
103        else {
104            annotationHolder = aAST.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 aAST the current node
129     * @param aAnnotation the annotation name to check for
130     * @return the AST representing that annotation
131     * @throws NullPointerException if the aAST or
132     * aAnnotation is null
133     */
134    public static DetailAST getAnnotation(final DetailAST aAST,
135        String aAnnotation)
136    {
137        if (aAST == null) {
138            throw new NullPointerException("the aAST is null");
139        }
140
141        if (aAnnotation == null) {
142            throw new NullPointerException("the aAnnotation is null");
143        }
144
145        if (aAnnotation.trim().length() == 0) {
146            throw new IllegalArgumentException("the aAnnotation"
147                + "is empty or spaces");
148        }
149
150        final DetailAST holder = AnnotationUtility.getAnnotationHolder(aAST);
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 aName =
158                    FullIdent.createFullIdent(at.getNextSibling()).getText();
159                if (aAnnotation.equals(aName)) {
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 aAST the AST representing an annotation.
173     * @return the AST the annotation is annotating.
174     * @throws NullPointerException if the aAST is null
175     * @throws IllegalArgumentException if the aAST is not
176     * an {@link TokenTypes#ANNOTATION}
177     */
178    public static DetailAST annotatingWhat(DetailAST aAST)
179    {
180        if (aAST == null) {
181            throw new NullPointerException("the aAST is null");
182        }
183
184        if (aAST.getType() != TokenTypes.ANNOTATION) {
185            throw new IllegalArgumentException(
186                "The aAST is not an annotation. AST: " + aAST);
187        }
188
189        return aAST.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 aAST the AST representing an annotation
196     * @param aTokenType 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 aAST is null
200     * @throws IllegalArgumentException if the aAST is not
201     * an {@link TokenTypes#ANNOTATION}
202     */
203    public static boolean isAnnotatingType(DetailAST aAST, int aTokenType)
204    {
205        final DetailAST ast = AnnotationUtility.annotatingWhat(aAST);
206        return ast.getType() == aTokenType;
207    }
208}