001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 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; 023 024import com.puppycrawl.tools.checkstyle.api.DetailAST; 025import com.puppycrawl.tools.checkstyle.api.Scope; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027 028/** 029 * Contains utility methods for working on scope. 030 * 031 * @author Oliver Burn 032 */ 033public final class ScopeUtils { 034 /** Prevent instantiation. */ 035 private ScopeUtils() { 036 } 037 038 /** 039 * Returns the Scope specified by the modifier set. 040 * 041 * @param aMods root node of a modifier set 042 * @return a {@code Scope} value 043 */ 044 public static Scope getScopeFromMods(DetailAST aMods) { 045 // default scope 046 Scope returnValue = Scope.PACKAGE; 047 for (AST token = aMods.getFirstChild(); token != null 048 && returnValue == Scope.PACKAGE; 049 token = token.getNextSibling()) { 050 if ("public".equals(token.getText())) { 051 returnValue = Scope.PUBLIC; 052 } 053 else if ("protected".equals(token.getText())) { 054 returnValue = Scope.PROTECTED; 055 } 056 else if ("private".equals(token.getText())) { 057 returnValue = Scope.PRIVATE; 058 } 059 } 060 return returnValue; 061 } 062 063 /** 064 * Returns the scope of the surrounding "block". 065 * @param node the node to return the scope for 066 * @return the Scope of the surrounding block 067 */ 068 public static Scope getSurroundingScope(DetailAST node) { 069 Scope returnValue = null; 070 for (DetailAST token = node.getParent(); 071 token != null; 072 token = token.getParent()) { 073 final int type = token.getType(); 074 if (type == TokenTypes.CLASS_DEF 075 || type == TokenTypes.INTERFACE_DEF 076 || type == TokenTypes.ANNOTATION_DEF 077 || type == TokenTypes.ENUM_DEF) { 078 final DetailAST mods = 079 token.findFirstToken(TokenTypes.MODIFIERS); 080 final Scope modScope = getScopeFromMods(mods); 081 if (returnValue == null || returnValue.isIn(modScope)) { 082 returnValue = modScope; 083 } 084 } 085 else if (type == TokenTypes.LITERAL_NEW) { 086 returnValue = Scope.ANONINNER; 087 // because Scope.ANONINNER is not in any other Scope 088 break; 089 } 090 } 091 092 return returnValue; 093 } 094 095 /** 096 * Returns whether a node is directly contained within an interface block. 097 * 098 * @param node the node to check if directly contained within an interface block. 099 * @return a {@code boolean} value 100 */ 101 public static boolean isInInterfaceBlock(DetailAST node) { 102 boolean returnValue = false; 103 104 // Loop up looking for a containing interface block 105 for (DetailAST token = node.getParent(); 106 token != null && !returnValue; 107 token = token.getParent()) { 108 109 final int type = token.getType(); 110 111 if (type == TokenTypes.INTERFACE_DEF) { 112 returnValue = true; 113 } 114 else if (type == TokenTypes.CLASS_DEF 115 || type == TokenTypes.ENUM_DEF 116 || type == TokenTypes.ANNOTATION_DEF 117 || type == TokenTypes.LITERAL_NEW) { 118 break; 119 } 120 } 121 122 return returnValue; 123 } 124 125 /** 126 * Returns whether a node is directly contained within an annotation block. 127 * 128 * @param node the node to check if directly contained within an annotation block. 129 * @return a {@code boolean} value 130 */ 131 public static boolean isInAnnotationBlock(DetailAST node) { 132 boolean returnValue = false; 133 134 // Loop up looking for a containing interface block 135 for (DetailAST token = node.getParent(); 136 token != null && !returnValue; 137 token = token.getParent()) { 138 final int type = token.getType(); 139 if (type == TokenTypes.ANNOTATION_DEF) { 140 returnValue = true; 141 } 142 else if (type == TokenTypes.CLASS_DEF 143 || type == TokenTypes.ENUM_DEF 144 || type == TokenTypes.INTERFACE_DEF 145 || type == TokenTypes.LITERAL_NEW) { 146 break; 147 } 148 149 } 150 151 return returnValue; 152 } 153 154 /** 155 * Returns whether a node is directly contained within an interface or 156 * annotation block. 157 * 158 * @param node the node to check if directly contained within an interface 159 * or annotation block. 160 * @return a {@code boolean} value 161 */ 162 public static boolean isInInterfaceOrAnnotationBlock(DetailAST node) { 163 return isInInterfaceBlock(node) || isInAnnotationBlock(node); 164 } 165 166 /** 167 * Returns whether a node is directly contained within an enum block. 168 * 169 * @param node the node to check if directly contained within an enum block. 170 * @return a {@code boolean} value 171 */ 172 public static boolean isInEnumBlock(DetailAST node) { 173 boolean returnValue = false; 174 175 // Loop up looking for a containing interface block 176 for (DetailAST token = node.getParent(); 177 token != null && !returnValue; 178 token = token.getParent()) { 179 final int type = token.getType(); 180 if (type == TokenTypes.ENUM_DEF) { 181 returnValue = true; 182 } 183 else if (type == TokenTypes.INTERFACE_DEF 184 || type == TokenTypes.ANNOTATION_DEF 185 || type == TokenTypes.CLASS_DEF 186 || type == TokenTypes.LITERAL_NEW) { 187 break; 188 } 189 } 190 191 return returnValue; 192 } 193 194 /** 195 * Returns whether the scope of a node is restricted to a code block. 196 * A code block is a method or constructor body, or a initializer block. 197 * 198 * @param node the node to check 199 * @return a {@code boolean} value 200 */ 201 public static boolean isInCodeBlock(DetailAST node) { 202 boolean returnValue = false; 203 204 // Loop up looking for a containing code block 205 for (DetailAST token = node.getParent(); 206 token != null; 207 token = token.getParent()) { 208 final int type = token.getType(); 209 if (type == TokenTypes.METHOD_DEF 210 || type == TokenTypes.CTOR_DEF 211 || type == TokenTypes.INSTANCE_INIT 212 || type == TokenTypes.STATIC_INIT) { 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 return localVariableDef; 267 } 268 269 /** 270 * Determines whether a node is a class field definition. 271 * I.e. if a variable is not declared in a code block, a for initializer, 272 * or a catch parameter. 273 * @param node the node to check. 274 * @return whether a node is a class field definition. 275 */ 276 public static boolean isClassFieldDef(DetailAST node) { 277 return node.getType() == TokenTypes.VARIABLE_DEF && !isLocalVariableDef(node); 278 } 279 280 /** 281 * Checks whether ast node is in a specific scope. 282 * @param ast the node to check. 283 * @param scope a {@code Scope} value. 284 * @return true if the ast node is in the scope. 285 */ 286 public static boolean isInScope(DetailAST ast, Scope scope) { 287 final Scope surroundingScopeOfAstToken = getSurroundingScope(ast); 288 return surroundingScopeOfAstToken == scope; 289 } 290}