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