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