View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.utils;
21  
22  import com.puppycrawl.tools.checkstyle.api.DetailAST;
23  import com.puppycrawl.tools.checkstyle.api.FullIdent;
24  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25  
26  /**
27   * Contains utility methods designed to work with annotations.
28   *
29   * @author Travis Schneeberger
30   */
31  public final class AnnotationUtility {
32  
33      /**
34       * Common message.
35       */
36      private static final String THE_AST_IS_NULL = "the ast is null";
37  
38      /**
39       * Private utility constructor.
40       * @throws UnsupportedOperationException if called
41       */
42      private AnnotationUtility() {
43          throw new UnsupportedOperationException("do not instantiate.");
44      }
45  
46      /**
47       * Checks to see if the AST is annotated with
48       * the passed in annotation.
49       *
50       * <p>
51       * This method will not look for imports or package
52       * statements to detect the passed in annotation.
53       * </p>
54       *
55       * <p>
56       * To check if an AST contains a passed in annotation
57       * taking into account fully-qualified names
58       * (ex: java.lang.Override, Override)
59       * this method will need to be called twice. Once for each
60       * name given.
61       * </p>
62       *
63       * @param ast the current node
64       * @param annotation the annotation name to check for
65       * @return true if contains the annotation
66       */
67      public static boolean containsAnnotation(final DetailAST ast,
68          String annotation) {
69          if (ast == null) {
70              throw new IllegalArgumentException(THE_AST_IS_NULL);
71          }
72          return getAnnotation(ast, annotation) != null;
73      }
74  
75      /**
76       * Checks to see if the AST is annotated with
77       * any annotation.
78       *
79       * @param ast the current node
80       * @return true if contains an annotation
81       */
82      public static boolean containsAnnotation(final DetailAST ast) {
83          if (ast == null) {
84              throw new IllegalArgumentException(THE_AST_IS_NULL);
85          }
86          final DetailAST holder = getAnnotationHolder(ast);
87          return holder != null && holder.findFirstToken(TokenTypes.ANNOTATION) != null;
88      }
89  
90      /**
91       * Gets the AST that holds a series of annotations for the
92       * potentially annotated AST.  Returns {@code null}
93       * the passed in AST is not have an Annotation Holder.
94       *
95       * @param ast the current node
96       * @return the Annotation Holder
97       */
98      public static DetailAST getAnnotationHolder(DetailAST ast) {
99          if (ast == null) {
100             throw new IllegalArgumentException(THE_AST_IS_NULL);
101         }
102 
103         final DetailAST annotationHolder;
104 
105         if (ast.getType() == TokenTypes.ENUM_CONSTANT_DEF
106             || ast.getType() == TokenTypes.PACKAGE_DEF) {
107             annotationHolder = ast.findFirstToken(TokenTypes.ANNOTATIONS);
108         }
109         else {
110             annotationHolder = ast.findFirstToken(TokenTypes.MODIFIERS);
111         }
112 
113         return annotationHolder;
114     }
115 
116     /**
117      * Checks to see if the AST is annotated with
118      * the passed in annotation and return the AST
119      * representing that annotation.
120      *
121      * <p>
122      * This method will not look for imports or package
123      * statements to detect the passed in annotation.
124      * </p>
125      *
126      * <p>
127      * To check if an AST contains a passed in annotation
128      * taking into account fully-qualified names
129      * (ex: java.lang.Override, Override)
130      * this method will need to be called twice. Once for each
131      * name given.
132      * </p>
133      *
134      * @param ast the current node
135      * @param annotation the annotation name to check for
136      * @return the AST representing that annotation
137      */
138     public static DetailAST getAnnotation(final DetailAST ast,
139         String annotation) {
140         if (ast == null) {
141             throw new IllegalArgumentException(THE_AST_IS_NULL);
142         }
143 
144         if (annotation == null) {
145             throw new IllegalArgumentException("the annotation is null");
146         }
147 
148         if (CommonUtils.isBlank(annotation)) {
149             throw new IllegalArgumentException(
150                     "the annotation is empty or spaces");
151         }
152 
153         final DetailAST holder = getAnnotationHolder(ast);
154         DetailAST result = null;
155         for (DetailAST child = holder.getFirstChild();
156             child != null; child = child.getNextSibling()) {
157             if (child.getType() == TokenTypes.ANNOTATION) {
158                 final DetailAST firstChild = child.findFirstToken(TokenTypes.AT);
159                 final String name =
160                     FullIdent.createFullIdent(firstChild.getNextSibling()).getText();
161                 if (annotation.equals(name)) {
162                     result = child;
163                     break;
164                 }
165             }
166         }
167 
168         return result;
169     }
170 
171 }