View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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 antlr.collections.AST;
23  import com.puppycrawl.tools.checkstyle.api.DetailAST;
24  import com.puppycrawl.tools.checkstyle.api.Scope;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  
27  /**
28   * Contains utility methods for working on scope.
29   *
30   */
31  public final class ScopeUtils {
32  
33      /** Prevent instantiation. */
34      private ScopeUtils() {
35      }
36  
37      /**
38       * Returns the Scope specified by the modifier set.
39       *
40       * @param aMods root node of a modifier set
41       * @return a {@code Scope} value
42       */
43      public static Scope getScopeFromMods(DetailAST aMods) {
44          // default scope
45          Scope returnValue = Scope.PACKAGE;
46          for (AST token = aMods.getFirstChild(); token != null
47                  && returnValue == Scope.PACKAGE;
48                  token = token.getNextSibling()) {
49              if ("public".equals(token.getText())) {
50                  returnValue = Scope.PUBLIC;
51              }
52              else if ("protected".equals(token.getText())) {
53                  returnValue = Scope.PROTECTED;
54              }
55              else if ("private".equals(token.getText())) {
56                  returnValue = Scope.PRIVATE;
57              }
58          }
59          return returnValue;
60      }
61  
62      /**
63       * Returns the scope of the surrounding "block".
64       * @param node the node to return the scope for
65       * @return the Scope of the surrounding block
66       */
67      public static Scope getSurroundingScope(DetailAST node) {
68          Scope returnValue = null;
69          for (DetailAST token = node.getParent();
70               token != null;
71               token = token.getParent()) {
72              final int type = token.getType();
73              if (type == TokenTypes.CLASS_DEF
74                  || type == TokenTypes.INTERFACE_DEF
75                  || type == TokenTypes.ANNOTATION_DEF
76                  || type == TokenTypes.ENUM_DEF) {
77                  final DetailAST mods =
78                      token.findFirstToken(TokenTypes.MODIFIERS);
79                  final Scope modScope = getScopeFromMods(mods);
80                  if (returnValue == null || returnValue.isIn(modScope)) {
81                      returnValue = modScope;
82                  }
83              }
84              else if (type == TokenTypes.LITERAL_NEW) {
85                  returnValue = Scope.ANONINNER;
86                  // because Scope.ANONINNER is not in any other Scope
87                  break;
88              }
89          }
90  
91          return returnValue;
92      }
93  
94      /**
95       * Returns whether a node is directly contained within an interface block.
96       *
97       * @param node the node to check if directly contained within an interface block.
98       * @return a {@code boolean} value
99       */
100     public static boolean isInInterfaceBlock(DetailAST node) {
101         return isInBlockOf(node, TokenTypes.INTERFACE_DEF);
102     }
103 
104     /**
105      * Returns whether a node is directly contained within an annotation block.
106      *
107      * @param node the node to check if directly contained within an annotation block.
108      * @return a {@code boolean} value
109      */
110     public static boolean isInAnnotationBlock(DetailAST node) {
111         return isInBlockOf(node, TokenTypes.ANNOTATION_DEF);
112     }
113 
114     /**
115      * Returns whether a node is directly contained within a specified block.
116      *
117      * @param node the node to check if directly contained within a specified block.
118      * @param tokenType type of token.
119      * @return a {@code boolean} value
120      */
121     private static boolean isInBlockOf(DetailAST node, int tokenType) {
122         boolean returnValue = false;
123 
124         // Loop up looking for a containing interface block
125         for (DetailAST token = node.getParent();
126              token != null && !returnValue;
127              token = token.getParent()) {
128             final int type = token.getType();
129             if (type == tokenType) {
130                 returnValue = true;
131             }
132             else if (type == TokenTypes.CLASS_DEF
133                 || type == TokenTypes.ENUM_DEF
134                 || type == TokenTypes.INTERFACE_DEF
135                 || type == TokenTypes.ANNOTATION_DEF
136                 || type == TokenTypes.LITERAL_NEW) {
137                 break;
138             }
139         }
140 
141         return returnValue;
142     }
143 
144     /**
145      * Returns whether a node is directly contained within an interface or
146      * annotation block.
147      *
148      * @param node the node to check if directly contained within an interface
149      *     or annotation block.
150      * @return a {@code boolean} value
151      */
152     public static boolean isInInterfaceOrAnnotationBlock(DetailAST node) {
153         return isInInterfaceBlock(node) || isInAnnotationBlock(node);
154     }
155 
156     /**
157      * Returns whether a node is directly contained within an enum block.
158      *
159      * @param node the node to check if directly contained within an enum block.
160      * @return a {@code boolean} value
161      */
162     public static boolean isInEnumBlock(DetailAST node) {
163         boolean returnValue = false;
164 
165         // Loop up looking for a containing interface block
166         for (DetailAST token = node.getParent();
167              token != null && !returnValue;
168              token = token.getParent()) {
169             final int type = token.getType();
170             if (type == TokenTypes.ENUM_DEF) {
171                 returnValue = true;
172             }
173             else if (type == TokenTypes.INTERFACE_DEF
174                 || type == TokenTypes.ANNOTATION_DEF
175                 || type == TokenTypes.CLASS_DEF
176                 || type == TokenTypes.LITERAL_NEW) {
177                 break;
178             }
179         }
180 
181         return returnValue;
182     }
183 
184     /**
185      * Returns whether the scope of a node is restricted to a code block.
186      * A code block is a method or constructor body, an initializer block, or lambda body.
187      *
188      * @param node the node to check
189      * @return a {@code boolean} value
190      */
191     public static boolean isInCodeBlock(DetailAST node) {
192         boolean returnValue = false;
193 
194         // Loop up looking for a containing code block
195         for (DetailAST token = node.getParent();
196              token != null;
197              token = token.getParent()) {
198             final int type = token.getType();
199             if (type == TokenTypes.METHOD_DEF
200                     || type == TokenTypes.CTOR_DEF
201                     || type == TokenTypes.INSTANCE_INIT
202                     || type == TokenTypes.STATIC_INIT
203                     || type == TokenTypes.LAMBDA) {
204                 returnValue = true;
205                 break;
206             }
207         }
208 
209         return returnValue;
210     }
211 
212     /**
213      * Returns whether a node is contained in the outer most type block.
214      *
215      * @param node the node to check
216      * @return a {@code boolean} value
217      */
218     public static boolean isOuterMostType(DetailAST node) {
219         boolean returnValue = true;
220         for (DetailAST parent = node.getParent();
221              parent != null;
222              parent = parent.getParent()) {
223             if (parent.getType() == TokenTypes.CLASS_DEF
224                 || parent.getType() == TokenTypes.INTERFACE_DEF
225                 || parent.getType() == TokenTypes.ANNOTATION_DEF
226                 || parent.getType() == TokenTypes.ENUM_DEF) {
227                 returnValue = false;
228                 break;
229             }
230         }
231 
232         return returnValue;
233     }
234 
235     /**
236      * Determines whether a node is a local variable definition.
237      * I.e. if it is declared in a code block, a for initializer,
238      * or a catch parameter.
239      * @param node the node to check.
240      * @return whether aAST is a local variable definition.
241      */
242     public static boolean isLocalVariableDef(DetailAST node) {
243         boolean localVariableDef = false;
244         // variable declaration?
245         if (node.getType() == TokenTypes.VARIABLE_DEF) {
246             final DetailAST parent = node.getParent();
247             final int type = parent.getType();
248             localVariableDef = type == TokenTypes.SLIST
249                     || type == TokenTypes.FOR_INIT
250                     || type == TokenTypes.FOR_EACH_CLAUSE;
251         }
252         // catch parameter?
253         if (node.getType() == TokenTypes.PARAMETER_DEF) {
254             final DetailAST parent = node.getParent();
255             localVariableDef = parent.getType() == TokenTypes.LITERAL_CATCH;
256         }
257 
258         if (node.getType() == TokenTypes.RESOURCE) {
259             localVariableDef = true;
260         }
261         return localVariableDef;
262     }
263 
264     /**
265      * Determines whether a node is a class field definition.
266      * I.e. if a variable is not declared in a code block, a for initializer,
267      * or a catch parameter.
268      * @param node the node to check.
269      * @return whether a node is a class field definition.
270      */
271     public static boolean isClassFieldDef(DetailAST node) {
272         return node.getType() == TokenTypes.VARIABLE_DEF && !isLocalVariableDef(node);
273     }
274 
275     /**
276      * Checks whether ast node is in a specific scope.
277      * @param ast the node to check.
278      * @param scope a {@code Scope} value.
279      * @return true if the ast node is in the scope.
280      */
281     public static boolean isInScope(DetailAST ast, Scope scope) {
282         final Scope surroundingScopeOfAstToken = getSurroundingScope(ast);
283         return surroundingScopeOfAstToken == scope;
284     }
285 
286 }