001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2018 the original author or authors.
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////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.utils;
021
022import com.puppycrawl.tools.checkstyle.api.DetailAST;
023import com.puppycrawl.tools.checkstyle.api.FullIdent;
024import com.puppycrawl.tools.checkstyle.api.TokenTypes;
025
026/**
027 * Contains utility methods designed to work with annotations.
028 *
029 */
030public final class AnnotationUtil {
031
032    /**
033     * Common message.
034     */
035    private static final String THE_AST_IS_NULL = "the ast is null";
036
037    /**
038     * Private utility constructor.
039     * @throws UnsupportedOperationException if called
040     */
041    private AnnotationUtil() {
042        throw new UnsupportedOperationException("do not instantiate.");
043    }
044
045    /**
046     * Checks to see if the AST is annotated with
047     * the passed in annotation.
048     *
049     * <p>
050     * This method will not look for imports or package
051     * statements to detect the passed in annotation.
052     * </p>
053     *
054     * <p>
055     * To check if an AST contains a passed in annotation
056     * taking into account fully-qualified names
057     * (ex: java.lang.Override, Override)
058     * this method will need to be called twice. Once for each
059     * name given.
060     * </p>
061     *
062     * @param ast the current node
063     * @param annotation the annotation name to check for
064     * @return true if contains the annotation
065     */
066    public static boolean containsAnnotation(final DetailAST ast,
067        String annotation) {
068        if (ast == null) {
069            throw new IllegalArgumentException(THE_AST_IS_NULL);
070        }
071        return getAnnotation(ast, annotation) != null;
072    }
073
074    /**
075     * Checks to see if the AST is annotated with
076     * any annotation.
077     *
078     * @param ast the current node
079     * @return true if contains an annotation
080     */
081    public static boolean containsAnnotation(final DetailAST ast) {
082        if (ast == null) {
083            throw new IllegalArgumentException(THE_AST_IS_NULL);
084        }
085        final DetailAST holder = getAnnotationHolder(ast);
086        return holder != null && holder.findFirstToken(TokenTypes.ANNOTATION) != null;
087    }
088
089    /**
090     * Gets the AST that holds a series of annotations for the
091     * potentially annotated AST.  Returns {@code null}
092     * the passed in AST is not have an Annotation Holder.
093     *
094     * @param ast the current node
095     * @return the Annotation Holder
096     */
097    public static DetailAST getAnnotationHolder(DetailAST ast) {
098        if (ast == null) {
099            throw new IllegalArgumentException(THE_AST_IS_NULL);
100        }
101
102        final DetailAST annotationHolder;
103
104        if (ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
105            || ast.getType() == TokenTypes.PACKAGE_DEF) {
106            annotationHolder = ast.findFirstToken(TokenTypes.ANNOTATIONS);
107        }
108        else {
109            annotationHolder = ast.findFirstToken(TokenTypes.MODIFIERS);
110        }
111
112        return annotationHolder;
113    }
114
115    /**
116     * Checks to see if the AST is annotated with
117     * the passed in annotation and return the AST
118     * representing that annotation.
119     *
120     * <p>
121     * This method will not look for imports or package
122     * statements to detect the passed in annotation.
123     * </p>
124     *
125     * <p>
126     * To check if an AST contains a passed in annotation
127     * taking into account fully-qualified names
128     * (ex: java.lang.Override, Override)
129     * this method will need to be called twice. Once for each
130     * name given.
131     * </p>
132     *
133     * @param ast the current node
134     * @param annotation the annotation name to check for
135     * @return the AST representing that annotation
136     */
137    public static DetailAST getAnnotation(final DetailAST ast,
138        String annotation) {
139        if (ast == null) {
140            throw new IllegalArgumentException(THE_AST_IS_NULL);
141        }
142
143        if (annotation == null) {
144            throw new IllegalArgumentException("the annotation is null");
145        }
146
147        if (CommonUtil.isBlank(annotation)) {
148            throw new IllegalArgumentException(
149                    "the annotation is empty or spaces");
150        }
151
152        final DetailAST holder = getAnnotationHolder(ast);
153        DetailAST result = null;
154        for (DetailAST child = holder.getFirstChild();
155            child != null; child = child.getNextSibling()) {
156            if (child.getType() == TokenTypes.ANNOTATION) {
157                final DetailAST firstChild = child.findFirstToken(TokenTypes.AT);
158                final String name =
159                    FullIdent.createFullIdent(firstChild.getNextSibling()).getText();
160                if (annotation.equals(name)) {
161                    result = child;
162                    break;
163                }
164            }
165        }
166
167        return result;
168    }
169
170}