KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > puppycrawl > tools > checkstyle > checks > coding > HiddenFieldCheck


1 ////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2005 Oliver Burn
4
//
5
// This library is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation; either
8
// version 2.1 of the License, or (at your option) any later version.
9
//
10
// This library is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
// Lesser General Public License for more details.
14
//
15
// You should have received a copy of the GNU Lesser General Public
16
// License along with this library; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
////////////////////////////////////////////////////////////////////////////////
19
package com.puppycrawl.tools.checkstyle.checks.coding;
20
21 import java.util.HashSet JavaDoc;
22 import java.util.Set JavaDoc;
23 import java.util.regex.Pattern JavaDoc;
24 import java.util.regex.PatternSyntaxException JavaDoc;
25
26 import org.apache.commons.beanutils.ConversionException;
27
28 import com.puppycrawl.tools.checkstyle.api.Check;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
31 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
32 import com.puppycrawl.tools.checkstyle.api.Utils;
33
34 /**
35  * <p>Checks that a local variable or a parameter does not shadow
36  * a field that is defined in the same class.
37  * </p>
38  * <p>
39  * An example of how to configure the check is:
40  * </p>
41  * <pre>
42  * &lt;module name="HiddenField"/&gt;
43  * </pre>
44  * <p>
45  * An example of how to configure the check so that it checks variables but not
46  * parameters is:
47  * </p>
48  * <pre>
49  * &lt;module name="HiddenField"&gt;
50  * &lt;property name="tokens" value="VARIABLE_DEF"/&gt;
51  * &lt;/module&gt;
52  * </pre>
53  * <p>
54  * An example of how to configure the check so that it ignores the parameter of
55  * a setter method is:
56  * </p>
57  * <pre>
58  * &lt;module name="HiddenField"&gt;
59  * &lt;property name="ignoreSetter" value="true"/&gt;
60  * &lt;/module&gt;
61  * </pre>
62  * <p>
63  * An example of how to configure the check so that it ignores constructor
64  * parameters is:
65  * </p>
66  * <pre>
67  * &lt;module name="HiddenField"&gt;
68  * &lt;property name="ignoreConstructorParameter" value="true"/&gt;
69  * &lt;/module&gt;
70  * </pre>
71  * @author Rick Giles
72  * @version 1.0
73  */

74 public class HiddenFieldCheck
75     extends Check
76 {
77     /** stack of sets of field names,
78      * one for each class of a set of nested classes.
79      */

80     private FieldFrame mCurrentFrame;
81
82     /** the regexp to match against */
83     private Pattern JavaDoc mRegexp;
84
85     /** controls whether to check the parameter of a property setter method */
86     private boolean mIgnoreSetter;
87
88     /** controls whether to check the parameter of a constructor */
89     private boolean mIgnoreConstructorParameter;
90
91     /** controls whether to check the parameter of abstract methods. */
92     private boolean mIgnoreAbstractMethods;
93
94     /** {@inheritDoc} */
95     public int[] getDefaultTokens()
96     {
97         return new int[] {
98             TokenTypes.VARIABLE_DEF,
99             TokenTypes.PARAMETER_DEF,
100             TokenTypes.CLASS_DEF,
101             TokenTypes.ENUM_DEF,
102             TokenTypes.ENUM_CONSTANT_DEF,
103         };
104     }
105
106     /** {@inheritDoc} */
107     public int[] getAcceptableTokens()
108     {
109         return new int[] {
110             TokenTypes.VARIABLE_DEF,
111             TokenTypes.PARAMETER_DEF,
112         };
113     }
114
115     /** {@inheritDoc} */
116     public int[] getRequiredTokens()
117     {
118         return new int[] {
119             TokenTypes.CLASS_DEF,
120             TokenTypes.ENUM_DEF,
121             TokenTypes.ENUM_CONSTANT_DEF,
122         };
123     }
124
125     /** {@inheritDoc} */
126     public void beginTree(DetailAST aRootAST)
127     {
128         mCurrentFrame = new FieldFrame(null, true);
129     }
130
131     /** {@inheritDoc} */
132     public void visitToken(DetailAST aAST)
133     {
134         if ((aAST.getType() == TokenTypes.VARIABLE_DEF)
135             || (aAST.getType() == TokenTypes.PARAMETER_DEF))
136         {
137             processVariable(aAST);
138             return;
139         }
140
141         //A more thorough check of enum constant class bodies is
142
//possible (checking for hidden fields against the enum
143
//class body in addition to enum constant class bodies)
144
//but not attempted as it seems out of the scope of this
145
//check.
146
final DetailAST typeMods = aAST.findFirstToken(TokenTypes.MODIFIERS);
147         final boolean isStaticInnerType =
148                 (typeMods != null)
149                         && typeMods.branchContains(TokenTypes.LITERAL_STATIC);
150         final FieldFrame frame =
151                 new FieldFrame(mCurrentFrame, isStaticInnerType);
152
153         //add fields to container
154
final DetailAST objBlock = aAST.findFirstToken(TokenTypes.OBJBLOCK);
155         // enum constants may not have bodies
156
if (objBlock != null) {
157             DetailAST child = (DetailAST) objBlock.getFirstChild();
158             while (child != null) {
159                 if (child.getType() == TokenTypes.VARIABLE_DEF) {
160                     final String JavaDoc name =
161                         child.findFirstToken(TokenTypes.IDENT).getText();
162                     final DetailAST mods =
163                         child.findFirstToken(TokenTypes.MODIFIERS);
164                     if (mods.branchContains(TokenTypes.LITERAL_STATIC)) {
165                         frame.addStaticField(name);
166                     }
167                     else {
168                         frame.addInstanceField(name);
169                     }
170                 }
171                 child = (DetailAST) child.getNextSibling();
172             }
173         }
174         // push container
175
mCurrentFrame = frame;
176     }
177
178     /** {@inheritDoc} */
179     public void leaveToken(DetailAST aAST)
180     {
181         if ((aAST.getType() == TokenTypes.CLASS_DEF)
182             || (aAST.getType() == TokenTypes.ENUM_DEF)
183             || (aAST.getType() == TokenTypes.ENUM_CONSTANT_DEF))
184         {
185             //pop
186
mCurrentFrame = mCurrentFrame.getParent();
187         }
188     }
189
190     /**
191      * Process a variable token.
192      * Check whether a local variable or parameter shadows a field.
193      * Store a field for later comparison with local variables and parameters.
194      * @param aAST the variable token.
195      */

196     private void processVariable(DetailAST aAST)
197     {
198         if (ScopeUtils.inInterfaceOrAnnotationBlock(aAST)
199             || (!ScopeUtils.isLocalVariableDef(aAST)
200             && (aAST.getType() != TokenTypes.PARAMETER_DEF)))
201         {
202             // do nothing
203
return;
204         }
205         //local variable or parameter. Does it shadow a field?
206
final DetailAST nameAST = aAST.findFirstToken(TokenTypes.IDENT);
207         final String JavaDoc name = nameAST.getText();
208         if ((mCurrentFrame.containsStaticField(name)
209              || (!inStatic(aAST) && mCurrentFrame.containsInstanceField(name)))
210             && ((mRegexp == null) || (!getRegexp().matcher(name).find()))
211             && !isIgnoredSetterParam(aAST, name)
212             && !isIgnoredConstructorParam(aAST)
213             && !isIgnoredParamOfAbstractMethod(aAST))
214         {
215             log(nameAST, "hidden.field", name);
216         }
217     }
218
219     /**
220      * Determines whether an AST node is in a static method or static
221      * initializer.
222      * @param aAST the node to check.
223      * @return true if aAST is in a static method or a static block;
224      */

225     private static boolean inStatic(DetailAST aAST)
226     {
227         DetailAST parent = aAST.getParent();
228         while (parent != null) {
229             switch (parent.getType()) {
230             case TokenTypes.STATIC_INIT:
231                 return true;
232             case TokenTypes.METHOD_DEF:
233                 final DetailAST mods =
234                     parent.findFirstToken(TokenTypes.MODIFIERS);
235                 return mods.branchContains(TokenTypes.LITERAL_STATIC);
236             default:
237                 parent = parent.getParent();
238             }
239         }
240         return false;
241     }
242
243     /**
244      * Decides whether to ignore an AST node that is the parameter of a
245      * setter method, where the property setter method for field 'xyz' has
246      * name 'setXyz', one parameter named 'xyz', and return type void.
247      * @param aAST the AST to check.
248      * @param aName the name of aAST.
249      * @return true if aAST should be ignored because check property
250      * ignoreSetter is true and aAST is the parameter of a setter method.
251      */

252     private boolean isIgnoredSetterParam(DetailAST aAST, String JavaDoc aName)
253     {
254         if (!(aAST.getType() == TokenTypes.PARAMETER_DEF)
255             || !mIgnoreSetter)
256         {
257             return false;
258         }
259         //single parameter?
260
final DetailAST parametersAST = aAST.getParent();
261         if (parametersAST.getChildCount() != 1) {
262             return false;
263         }
264         //method parameter, not constructor parameter?
265
final DetailAST methodAST = parametersAST.getParent();
266         if (methodAST.getType() != TokenTypes.METHOD_DEF) {
267             return false;
268         }
269         //property setter name?
270
final String JavaDoc expectedName =
271             "set" + aName.substring(0, 1).toUpperCase() + aName.substring(1);
272         final DetailAST methodNameAST =
273             methodAST.findFirstToken(TokenTypes.IDENT);
274         final String JavaDoc methodName = methodNameAST.getText();
275         if (!methodName.equals(expectedName)) {
276             return false;
277         }
278         //void?
279
final DetailAST typeAST = methodAST.findFirstToken(TokenTypes.TYPE);
280         return typeAST.branchContains(TokenTypes.LITERAL_VOID);
281     }
282
283     /**
284      * Decides whether to ignore an AST node that is the parameter of a
285      * constructor.
286      * @param aAST the AST to check.
287      * @return true if aAST should be ignored because check property
288      * ignoreConstructorParameter is true and aAST is a constructor parameter.
289      */

290     private boolean isIgnoredConstructorParam(DetailAST aAST)
291     {
292         if ((aAST.getType() != TokenTypes.PARAMETER_DEF)
293             || !mIgnoreConstructorParameter)
294         {
295             return false;
296         }
297         final DetailAST parametersAST = aAST.getParent();
298         final DetailAST constructorAST = parametersAST.getParent();
299         return (constructorAST.getType() == TokenTypes.CTOR_DEF);
300     }
301
302     /**
303      * Decides whether to ignore an AST node that is the parameter of an
304      * abstract method.
305      * @param aAST the AST to check.
306      * @return true if aAST should be ignored because check property
307      * ignoreAbstactMethods is true and aAST is a parameter of abstract
308      * methods.
309      */

310     private boolean isIgnoredParamOfAbstractMethod(DetailAST aAST)
311     {
312         if ((aAST.getType() != TokenTypes.PARAMETER_DEF)
313             || !mIgnoreAbstractMethods)
314         {
315             return false;
316         }
317         final DetailAST method = aAST.getParent().getParent();
318         if (method.getType() != TokenTypes.METHOD_DEF) {
319             return false;
320         }
321         final DetailAST mods = method.findFirstToken(TokenTypes.MODIFIERS);
322         return ((mods != null) && mods.branchContains(TokenTypes.ABSTRACT));
323     }
324
325     /**
326      * Set the ignore format to the specified regular expression.
327      * @param aFormat a <code>String</code> value
328      * @throws ConversionException unable to parse aFormat
329      */

330     public void setIgnoreFormat(String JavaDoc aFormat)
331         throws ConversionException
332     {
333         try {
334             mRegexp = Utils.getPattern(aFormat);
335         }
336         catch (final PatternSyntaxException JavaDoc e) {
337             throw new ConversionException("unable to parse " + aFormat, e);
338         }
339     }
340
341     /**
342      * Set whether to ignore the parameter of a property setter method.
343      * @param aIgnoreSetter decide whether to ignore the parameter of
344      * a property setter method.
345      */

346     public void setIgnoreSetter(boolean aIgnoreSetter)
347     {
348         mIgnoreSetter = aIgnoreSetter;
349     }
350
351     /**
352      * Set whether to ignore constructor parameters.
353      * @param aIgnoreConstructorParameter decide whether to ignore
354      * constructor parameters.
355      */

356     public void setIgnoreConstructorParameter(
357         boolean aIgnoreConstructorParameter)
358     {
359         mIgnoreConstructorParameter = aIgnoreConstructorParameter;
360     }
361
362     /**
363      * Set whether to ignore parameters of abstract methods.
364      * @param aIgnoreAbstractMethods decide whether to ignore
365      * parameters of abstract methods.
366      */

367     public void setIgnoreAbstractMethods(
368         boolean aIgnoreAbstractMethods)
369     {
370         mIgnoreAbstractMethods = aIgnoreAbstractMethods;
371     }
372
373     /** @return the regexp to match against */
374     public Pattern JavaDoc getRegexp()
375     {
376         return mRegexp;
377     }
378
379     /**
380      * Holds the names of static and instance fields of a type.
381      * @author Rick Giles
382      * Describe class FieldFrame
383      * @author Rick Giles
384      * @version Oct 26, 2003
385      */

386     private static class FieldFrame
387     {
388         /** is this a static inner type */
389         private boolean mStaticType;
390
391         /** parent frame. */
392         private FieldFrame mParent;
393
394         /** set of instance field names */
395         private final Set JavaDoc mInstanceFields = new HashSet JavaDoc();
396
397         /** set of static field names */
398         private final Set JavaDoc mStaticFields = new HashSet JavaDoc();
399
400         /** Creates new frame.
401          * @param aStaticType is this a static inner type (class or enum).
402          * @param aParent parent frame.
403          */

404         public FieldFrame(FieldFrame aParent, boolean aStaticType)
405         {
406             mParent = aParent;
407             mStaticType = aStaticType;
408         }
409
410         /** Is this frame for static inner type.
411          * @return is this field frame for static inner type.
412          */

413         boolean isStaticType()
414         {
415             return mStaticType;
416         }
417
418         /**
419          * Adds an instance field to this FieldFrame.
420          * @param aField the name of the instance field.
421          */

422         public void addInstanceField(String JavaDoc aField)
423         {
424             mInstanceFields.add(aField);
425         }
426
427         /**
428          * Adds a static field to this FieldFrame.
429          * @param aField the name of the instance field.
430          */

431         public void addStaticField(String JavaDoc aField)
432         {
433             mStaticFields.add(aField);
434         }
435
436         /**
437          * Determines whether this FieldFrame contains an instance field.
438          * @param aField the field to check.
439          * @return true if this FieldFrame contains instance field aField.
440          */

441         public boolean containsInstanceField(String JavaDoc aField)
442         {
443             if (mInstanceFields.contains(aField)) {
444                 return true;
445             }
446             if (mStaticType) {
447                 return false;
448             }
449
450             return (mParent != null) && mParent.containsInstanceField(aField);
451         }
452
453         /**
454          * Determines whether this FieldFrame contains a static field.
455          * @param aField the field to check.
456          * @return true if this FieldFrame contains static field aField.
457          */

458         public boolean containsStaticField(String JavaDoc aField)
459         {
460             if (mStaticFields.contains(aField)) {
461                 return true;
462             }
463
464             return (mParent != null) && mParent.containsStaticField(aField);
465         }
466
467         /**
468          * Getter for parent frame.
469          * @return parent frame.
470          */

471         public FieldFrame getParent()
472         {
473             return mParent;
474         }
475     }
476 }
477
Popular Tags