001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2017 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 * @author Oliver Burn 031 */ 032public final class ScopeUtils { 033 /** Prevent instantiation. */ 034 private ScopeUtils() { 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 boolean returnValue = false; 102 103 // Loop up looking for a containing interface block 104 for (DetailAST token = node.getParent(); 105 token != null && !returnValue; 106 token = token.getParent()) { 107 108 final int type = token.getType(); 109 110 if (type == TokenTypes.INTERFACE_DEF) { 111 returnValue = true; 112 } 113 else if (type == TokenTypes.CLASS_DEF 114 || type == TokenTypes.ENUM_DEF 115 || type == TokenTypes.ANNOTATION_DEF 116 || type == TokenTypes.LITERAL_NEW) { 117 break; 118 } 119 } 120 121 return returnValue; 122 } 123 124 /** 125 * Returns whether a node is directly contained within an annotation block. 126 * 127 * @param node the node to check if directly contained within an annotation block. 128 * @return a {@code boolean} value 129 */ 130 public static boolean isInAnnotationBlock(DetailAST node) { 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 == TokenTypes.ANNOTATION_DEF) { 139 returnValue = true; 140 } 141 else if (type == TokenTypes.CLASS_DEF 142 || type == TokenTypes.ENUM_DEF 143 || type == TokenTypes.INTERFACE_DEF 144 || type == TokenTypes.LITERAL_NEW) { 145 break; 146 } 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}