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> mFrames; 046 047 /** 048 * Frame for the currently processed AST 049 */ 050 private LexicalFrame mCurrent; 051 052 @Override 053 public void beginTree(DetailAST aRootAST) 054 { 055 final Deque<LexicalFrame> aFrameStack = Lists.newLinkedList(); 056 aFrameStack.add(new GlobalFrame()); 057 058 mFrames = Maps.newHashMap(); 059 060 DetailAST curNode = aRootAST; 061 while (curNode != null) { 062 collectDeclarations(aFrameStack, curNode); 063 DetailAST toVisit = curNode.getFirstChild(); 064 while (curNode != null && toVisit == null) { 065 endCollectingDeclarations(aFrameStack, 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 aAST) 077 { 078 switch (aAST.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.mCurrent = this.mFrames.get(aAST); 087 break; 088 default : 089 // do nothing 090 } 091 } // end visitToken 092 093 /** 094 * Parse the next AST for declarations 095 * 096 * @param aFrameStack Stack containing the FrameTree being built 097 * @param aAST AST to parse 098 */ 099 private void collectDeclarations(Deque<LexicalFrame> aFrameStack, 100 DetailAST aAST) 101 { 102 final LexicalFrame frame = aFrameStack.peek(); 103 switch (aAST.getType()) { 104 case TokenTypes.VARIABLE_DEF : { 105 final String name = 106 aAST.findFirstToken(TokenTypes.IDENT).getText(); 107 if (frame instanceof ClassFrame) { 108 final DetailAST mods = 109 aAST.findFirstToken(TokenTypes.MODIFIERS); 110 if (ScopeUtils.inInterfaceBlock(aAST) 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 = aAST.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 = aAST.findFirstToken(TokenTypes.IDENT); 134 frame.addName(nameAST.getText()); 135 aFrameStack.addFirst(new ClassFrame(frame)); 136 break; 137 } 138 case TokenTypes.SLIST : 139 aFrameStack.addFirst(new BlockFrame(frame)); 140 break; 141 case TokenTypes.METHOD_DEF : { 142 final String name = aAST.findFirstToken(TokenTypes.IDENT).getText(); 143 if (frame instanceof ClassFrame) { 144 final DetailAST mods = 145 aAST.findFirstToken(TokenTypes.MODIFIERS); 146 if (mods.branchContains(TokenTypes.LITERAL_STATIC)) { 147 ((ClassFrame) frame).addStaticMember(name); 148 } 149 else { 150 ((ClassFrame) frame).addInstanceMember(name); 151 } 152 } 153 } 154 case TokenTypes.CTOR_DEF : 155 aFrameStack.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 aFrameStack Stack containing the FrameTree being built 167 * @param aAST AST that was parsed 168 */ 169 private void endCollectingDeclarations(Queue<LexicalFrame> aFrameStack, 170 DetailAST aAST) 171 { 172 switch (aAST.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.mFrames.put(aAST, aFrameStack.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 aName a name to check 190 * @return true is the given name is name of method or member. 191 */ 192 protected final boolean isClassField(String aName) 193 { 194 final LexicalFrame frame = findFrame(aName); 195 return (frame instanceof ClassFrame) 196 && ((ClassFrame) frame).hasInstanceMember(aName); 197 } 198 199 /** 200 * Find frame containing declaration 201 * @param aName name of the declaration to find 202 * @return LexicalFrame containing declaration or null 203 */ 204 private LexicalFrame findFrame(String aName) 205 { 206 if (mCurrent != null) { 207 return mCurrent.getIfContains(aName); 208 } 209 else { 210 return null; 211 } 212 } 213 214 /** 215 * A declaration frame. 216 * @author Stephen Bloch 217 * June 19, 2003 218 */ 219 private abstract static class LexicalFrame 220 { 221 /** Set of name of variables declared in this frame. */ 222 private final Set<String> mVarNames; 223 /** 224 * Parent frame. 225 */ 226 private final LexicalFrame mParent; 227 228 /** 229 * constructor -- invokable only via super() from subclasses 230 * 231 * @param aParent parent frame 232 */ 233 protected LexicalFrame(LexicalFrame aParent) 234 { 235 mParent = aParent; 236 mVarNames = Sets.newHashSet(); 237 } 238 239 /** add a name to the frame. 240 * @param aNameToAdd the name we're adding 241 */ 242 void addName(String aNameToAdd) 243 { 244 mVarNames.add(aNameToAdd); 245 } 246 247 /** check whether the frame contains a given name. 248 * @param aNameToFind the name we're looking for 249 * @return whether it was found 250 */ 251 boolean contains(String aNameToFind) 252 { 253 return mVarNames.contains(aNameToFind); 254 } 255 256 /** check whether the frame contains a given name. 257 * @param aNameToFind the name we're looking for 258 * @return whether it was found 259 */ 260 LexicalFrame getIfContains(String aNameToFind) 261 { 262 if (contains(aNameToFind)) { 263 return this; 264 } 265 else if (mParent != null) { 266 return mParent.getIfContains(aNameToFind); 267 } 268 else { 269 return null; 270 } 271 } 272 } 273 274 /** 275 * The global frame; should hold only class names. 276 * @author Stephen Bloch 277 */ 278 private static class GlobalFrame extends LexicalFrame 279 { 280 281 /** 282 * Constructor for the root of the FrameTree 283 */ 284 protected GlobalFrame() 285 { 286 super(null); 287 } 288 } 289 290 /** 291 * A frame initiated at method definition; holds parameter names. 292 * @author Stephen Bloch 293 */ 294 private static class MethodFrame extends LexicalFrame 295 { 296 /** 297 * @param aParent parent frame 298 */ 299 protected MethodFrame(LexicalFrame aParent) 300 { 301 super(aParent); 302 } 303 } 304 305 /** 306 * A frame initiated at class definition; holds instance variable 307 * names. For the present, I'm not worried about other class names, 308 * method names, etc. 309 * @author Stephen Bloch 310 */ 311 private static class ClassFrame extends LexicalFrame 312 { 313 /** Set of name of instance members declared in this frame. */ 314 private final Set<String> mInstanceMembers; 315 /** Set of name of variables declared in this frame. */ 316 private final Set<String> mStaticMembers; 317 318 /** 319 * Creates new instance of ClassFrame 320 * @param aParent parent frame 321 */ 322 public ClassFrame(LexicalFrame aParent) 323 { 324 super(aParent); 325 mInstanceMembers = Sets.newHashSet(); 326 mStaticMembers = Sets.newHashSet(); 327 } 328 329 /** 330 * Adds static member's name. 331 * @param aName a name of static member of the class 332 */ 333 public void addStaticMember(final String aName) 334 { 335 mStaticMembers.add(aName); 336 } 337 338 /** 339 * Adds instance member's name. 340 * @param aName a name of instance member of the class 341 */ 342 public void addInstanceMember(final String aName) 343 { 344 mInstanceMembers.add(aName); 345 } 346 347 /** 348 * Checks if a given name is a known instance member of the class. 349 * @param aName a name to check 350 * @return true is the given name is a name of a known 351 * instance member of the class 352 */ 353 public boolean hasInstanceMember(final String aName) 354 { 355 return mInstanceMembers.contains(aName); 356 } 357 358 @Override 359 boolean contains(String aNameToFind) 360 { 361 return super.contains(aNameToFind) 362 || mInstanceMembers.contains(aNameToFind) 363 || mStaticMembers.contains(aNameToFind); 364 } 365 } 366 367 /** 368 * A frame initiated on entering a statement list; holds local variable 369 * names. For the present, I'm not worried about other class names, 370 * method names, etc. 371 * @author Stephen Bloch 372 */ 373 private static class BlockFrame extends LexicalFrame 374 { 375 376 /** 377 * @param aParent parent frame 378 */ 379 protected BlockFrame(LexicalFrame aParent) 380 { 381 super(aParent); 382 } 383 } 384}