001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2015 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.checks; 021 022import java.util.Deque; 023import java.util.Map; 024import java.util.Queue; 025import java.util.Set; 026 027import com.google.common.collect.Lists; 028import com.google.common.collect.Maps; 029import com.google.common.collect.Sets; 030import com.puppycrawl.tools.checkstyle.api.Check; 031import com.puppycrawl.tools.checkstyle.api.DetailAST; 032import com.puppycrawl.tools.checkstyle.api.TokenTypes; 033import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; 034 035/** 036 * Abstract class for checks which need to collect information about 037 * declared members/parameters/variables. 038 * 039 * @author o_sukhodolsky 040 */ 041public abstract class AbstractDeclarationCollector extends Check { 042 /** 043 * Tree of all the parsed frames. 044 */ 045 private Map<DetailAST, LexicalFrame> frames; 046 047 /** 048 * Frame for the currently processed AST. 049 */ 050 private LexicalFrame current; 051 052 @Override 053 public void beginTree(DetailAST rootAST) { 054 final Deque<LexicalFrame> frameStack = Lists.newLinkedList(); 055 frameStack.add(new GlobalFrame()); 056 057 frames = Maps.newHashMap(); 058 059 DetailAST curNode = rootAST; 060 while (curNode != null) { 061 collectDeclarations(frameStack, curNode); 062 DetailAST toVisit = curNode.getFirstChild(); 063 while (curNode != null && toVisit == null) { 064 endCollectingDeclarations(frameStack, curNode); 065 toVisit = curNode.getNextSibling(); 066 if (toVisit == null) { 067 curNode = curNode.getParent(); 068 } 069 } 070 curNode = toVisit; 071 } 072 } 073 074 @Override 075 public void visitToken(DetailAST ast) { 076 switch (ast.getType()) { 077 case TokenTypes.CLASS_DEF : 078 case TokenTypes.INTERFACE_DEF : 079 case TokenTypes.ENUM_DEF : 080 case TokenTypes.ANNOTATION_DEF : 081 case TokenTypes.SLIST : 082 case TokenTypes.METHOD_DEF : 083 case TokenTypes.CTOR_DEF : 084 current = frames.get(ast); 085 break; 086 default : 087 // do nothing 088 } 089 } 090 091 /** 092 * Parse the next AST for declarations. 093 * 094 * @param frameStack Stack containing the FrameTree being built 095 * @param ast AST to parse 096 */ 097 private static void collectDeclarations(Deque<LexicalFrame> frameStack, 098 DetailAST ast) { 099 final LexicalFrame frame = frameStack.peek(); 100 switch (ast.getType()) { 101 case TokenTypes.VARIABLE_DEF : 102 collectVariableDeclarations(ast, frame); 103 break; 104 case TokenTypes.PARAMETER_DEF : 105 final DetailAST parameterAST = ast.findFirstToken(TokenTypes.IDENT); 106 frame.addName(parameterAST.getText()); 107 break; 108 case TokenTypes.CLASS_DEF : 109 case TokenTypes.INTERFACE_DEF : 110 case TokenTypes.ENUM_DEF : 111 case TokenTypes.ANNOTATION_DEF : 112 final DetailAST classAST = ast.findFirstToken(TokenTypes.IDENT); 113 frame.addName(classAST.getText()); 114 frameStack.addFirst(new ClassFrame(frame)); 115 break; 116 case TokenTypes.SLIST : 117 frameStack.addFirst(new BlockFrame(frame)); 118 break; 119 case TokenTypes.METHOD_DEF : 120 final String name = ast.findFirstToken(TokenTypes.IDENT).getText(); 121 if (frame instanceof ClassFrame) { 122 final DetailAST mods = 123 ast.findFirstToken(TokenTypes.MODIFIERS); 124 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 125 ((ClassFrame) frame).addStaticMethod(name); 126 } 127 else { 128 ((ClassFrame) frame).addInstanceMethod(name); 129 } 130 } 131 frameStack.addFirst(new MethodFrame(frame)); 132 break; 133 case TokenTypes.CTOR_DEF : 134 frameStack.addFirst(new MethodFrame(frame)); 135 break; 136 default: 137 // do nothing 138 } 139 } 140 141 /** 142 * Collect Variable Declarations. 143 * @param ast variable token 144 * @param frame current frame 145 */ 146 private static void collectVariableDeclarations(DetailAST ast, LexicalFrame frame) { 147 final String name = 148 ast.findFirstToken(TokenTypes.IDENT).getText(); 149 if (frame instanceof ClassFrame) { 150 final DetailAST mods = 151 ast.findFirstToken(TokenTypes.MODIFIERS); 152 if (ScopeUtils.isInInterfaceBlock(ast) 153 || mods.branchContains(TokenTypes.LITERAL_STATIC)) { 154 ((ClassFrame) frame).addStaticMember(name); 155 } 156 else { 157 ((ClassFrame) frame).addInstanceMember(name); 158 } 159 } 160 else { 161 frame.addName(name); 162 } 163 } 164 165 /** 166 * End parsing of the AST for declarations. 167 * 168 * @param frameStack Stack containing the FrameTree being built 169 * @param ast AST that was parsed 170 */ 171 private void endCollectingDeclarations(Queue<LexicalFrame> frameStack, 172 DetailAST ast) { 173 switch (ast.getType()) { 174 case TokenTypes.CLASS_DEF : 175 case TokenTypes.INTERFACE_DEF : 176 case TokenTypes.ENUM_DEF : 177 case TokenTypes.ANNOTATION_DEF : 178 case TokenTypes.SLIST : 179 case TokenTypes.METHOD_DEF : 180 case TokenTypes.CTOR_DEF : 181 frames.put(ast, frameStack.poll()); 182 break; 183 default : 184 // do nothing 185 } 186 } 187 188 /** 189 * Check if given name is a name for class field in current environment. 190 * @param name a name to check 191 * @return true is the given name is name of member. 192 */ 193 protected final boolean isClassField(String name) { 194 final LexicalFrame frame = findFrame(name); 195 return frame instanceof ClassFrame 196 && ((ClassFrame) frame).hasInstanceMember(name); 197 } 198 199 /** 200 * Check if given name is a name for class method in current environment. 201 * @param name a name to check 202 * @return true is the given name is name of method. 203 */ 204 protected final boolean isClassMethod(String name) { 205 final LexicalFrame frame = findFrame(name); 206 return frame instanceof ClassFrame 207 && ((ClassFrame) frame).hasInstanceMethod(name); 208 } 209 210 /** 211 * Find frame containing declaration. 212 * @param name name of the declaration to find 213 * @return LexicalFrame containing declaration or null 214 */ 215 private LexicalFrame findFrame(String name) { 216 if (current != null) { 217 return current.getIfContains(name); 218 } 219 else { 220 return null; 221 } 222 } 223 224 /** 225 * A declaration frame. 226 * @author Stephen Bloch 227 */ 228 private static class LexicalFrame { 229 /** Set of name of variables declared in this frame. */ 230 private final Set<String> varNames; 231 /** 232 * Parent frame. 233 */ 234 private final LexicalFrame parent; 235 236 /** 237 * Constructor -- invokable only via super() from subclasses. 238 * 239 * @param parent parent frame 240 */ 241 protected LexicalFrame(LexicalFrame parent) { 242 this.parent = parent; 243 varNames = Sets.newHashSet(); 244 } 245 246 /** Add a name to the frame. 247 * @param nameToAdd the name we're adding 248 */ 249 void addName(String nameToAdd) { 250 varNames.add(nameToAdd); 251 } 252 253 /** Check whether the frame contains a given name. 254 * @param nameToFind the name we're looking for 255 * @return whether it was found 256 */ 257 boolean contains(String nameToFind) { 258 return varNames.contains(nameToFind); 259 } 260 261 /** Check whether the frame contains a given name. 262 * @param nameToFind the name we're looking for 263 * @return whether it was found 264 */ 265 LexicalFrame getIfContains(String nameToFind) { 266 LexicalFrame frame = null; 267 268 if (contains(nameToFind)) { 269 frame = this; 270 } 271 else if (parent != null) { 272 frame = parent.getIfContains(nameToFind); 273 } 274 return frame; 275 } 276 } 277 278 /** 279 * The global frame; should hold only class names. 280 * @author Stephen Bloch 281 */ 282 private static class GlobalFrame extends LexicalFrame { 283 284 /** 285 * Constructor for the root of the FrameTree. 286 */ 287 protected GlobalFrame() { 288 super(null); 289 } 290 } 291 292 /** 293 * A frame initiated at method definition; holds parameter names. 294 * @author Stephen Bloch 295 */ 296 private static class MethodFrame extends LexicalFrame { 297 /** 298 * Creates method frame. 299 * @param parent parent frame 300 */ 301 protected MethodFrame(LexicalFrame parent) { 302 super(parent); 303 } 304 } 305 306 /** 307 * A frame initiated at class definition; holds instance variable 308 * names. For the present, I'm not worried about other class names, 309 * method names, etc. 310 * @author Stephen Bloch 311 */ 312 private static class ClassFrame extends LexicalFrame { 313 /** Set of name of instance members declared in this frame. */ 314 private final Set<String> instanceMembers; 315 /** Set of name of instance methods declared in this frame. */ 316 private final Set<String> instanceMethods; 317 /** Set of name of variables declared in this frame. */ 318 private final Set<String> staticMembers; 319 /** Set of name of static methods declared in this frame. */ 320 private final Set<String> staticMethods; 321 322 /** 323 * Creates new instance of ClassFrame. 324 * @param parent parent frame 325 */ 326 ClassFrame(LexicalFrame parent) { 327 super(parent); 328 instanceMembers = Sets.newHashSet(); 329 instanceMethods = Sets.newHashSet(); 330 staticMembers = Sets.newHashSet(); 331 staticMethods = Sets.newHashSet(); 332 } 333 334 /** 335 * Adds static member's name. 336 * @param name a name of static member of the class 337 */ 338 public void addStaticMember(final String name) { 339 staticMembers.add(name); 340 } 341 342 /** 343 * Adds static method's name. 344 * @param name a name of static method of the class 345 */ 346 public void addStaticMethod(final String name) { 347 staticMethods.add(name); 348 } 349 350 /** 351 * Adds instance member's name. 352 * @param name a name of instance member of the class 353 */ 354 public void addInstanceMember(final String name) { 355 instanceMembers.add(name); 356 } 357 358 /** 359 * Adds instance method's name. 360 * @param name a name of instance method of the class 361 */ 362 public void addInstanceMethod(final String name) { 363 instanceMethods.add(name); 364 } 365 366 /** 367 * Checks if a given name is a known instance member of the class. 368 * @param name a name to check 369 * @return true is the given name is a name of a known 370 * instance member of the class 371 */ 372 public boolean hasInstanceMember(final String name) { 373 return instanceMembers.contains(name); 374 } 375 376 /** 377 * Checks if a given name is a known instance method of the class. 378 * @param name a name to check 379 * @return true is the given name is a name of a known 380 * instance method of the class 381 */ 382 public boolean hasInstanceMethod(final String name) { 383 return instanceMethods.contains(name); 384 } 385 386 @Override 387 boolean contains(String nameToFind) { 388 return super.contains(nameToFind) 389 || instanceMembers.contains(nameToFind) 390 || instanceMethods.contains(nameToFind) 391 || staticMembers.contains(nameToFind) 392 || staticMethods.contains(nameToFind); 393 } 394 } 395 396 /** 397 * A frame initiated on entering a statement list; holds local variable 398 * names. For the present, I'm not worried about other class names, 399 * method names, etc. 400 * @author Stephen Bloch 401 */ 402 private static class BlockFrame extends LexicalFrame { 403 404 /** 405 * Creates block frame. 406 * @param parent parent frame 407 */ 408 protected BlockFrame(LexicalFrame parent) { 409 super(parent); 410 } 411 } 412}