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 antlr.collections.AST; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.Scope; 025import com.puppycrawl.tools.checkstyle.api.TokenTypes; 026 027/** 028 * Contains utility methods for working on scope. 029 * 030 */ 031public final class ScopeUtil { 032 033 /** Prevent instantiation. */ 034 private ScopeUtil() { 035 } 036 037 /** 038 * Returns the Scope specified by the modifier set. 039 * 040 * @param aMods root node of a modifier set 041 * @return a {@code Scope} value 042 */ 043 public static Scope getScopeFromMods(DetailAST aMods) { 044 // default scope 045 Scope returnValue = Scope.PACKAGE; 046 for (AST token = aMods.getFirstChild(); token != null 047 && returnValue == Scope.PACKAGE; 048 token = token.getNextSibling()) { 049 if ("public".equals(token.getText())) { 050 returnValue = Scope.PUBLIC; 051 } 052 else if ("protected".equals(token.getText())) { 053 returnValue = Scope.PROTECTED; 054 } 055 else if ("private".equals(token.getText())) { 056 returnValue = Scope.PRIVATE; 057 } 058 } 059 return returnValue; 060 } 061 062 /** 063 * Returns the scope of the surrounding "block". 064 * @param node the node to return the scope for 065 * @return the Scope of the surrounding block 066 */ 067 public static Scope getSurroundingScope(DetailAST node) { 068 Scope returnValue = null; 069 for (DetailAST token = node.getParent(); 070 token != null; 071 token = token.getParent()) { 072 final int type = token.getType(); 073 if (type == TokenTypes.CLASS_DEF 074 || type == TokenTypes.INTERFACE_DEF 075 || type == TokenTypes.ANNOTATION_DEF 076 || type == TokenTypes.ENUM_DEF) { 077 final DetailAST mods = 078 token.findFirstToken(TokenTypes.MODIFIERS); 079 final Scope modScope = getScopeFromMods(mods); 080 if (returnValue == null || returnValue.isIn(modScope)) { 081 returnValue = modScope; 082 } 083 } 084 else if (type == TokenTypes.LITERAL_NEW) { 085 returnValue = Scope.ANONINNER; 086 // because Scope.ANONINNER is not in any other Scope 087 break; 088 } 089 } 090 091 return returnValue; 092 } 093 094 /** 095 * Returns whether a node is directly contained within an interface block. 096 * 097 * @param node the node to check if directly contained within an interface block. 098 * @return a {@code boolean} value 099 */ 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}