KickJava   Java API By Example, From Geeks To Geeks.

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


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 com.puppycrawl.tools.checkstyle.api.Check;
22 import com.puppycrawl.tools.checkstyle.api.DetailAST;
23 import com.puppycrawl.tools.checkstyle.api.ScopeUtils;
24 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
25
26 import com.puppycrawl.tools.checkstyle.checks.CheckUtils;
27
28 import java.util.Arrays JavaDoc;
29
30 /**
31  * <p>
32  * Checks for magic numbers.
33  * </p>
34  * <p>
35  * An example of how to configure the check to ignore
36  * numbers 0, 1, 1.5, 2:
37  * </p>
38  * <pre>
39  * &lt;module name="MagicNumber"&gt;
40  * &lt;property name="ignoreNumbers" value="0, 1, 1.5, 2"/&gt;
41  * &lt;/module&gt;
42  * </pre>
43  * @author Rick Giles
44  * @author Lars Kühne
45  */

46 public class MagicNumberCheck extends Check
47 {
48     /**
49      * The token types that are allowed in the AST path from the
50      * number literal to the enclosing constant definition.
51      */

52     private static final int[] ALLOWED_PATH_TOKENTYPES = {
53         TokenTypes.ASSIGN,
54         TokenTypes.ARRAY_INIT,
55         TokenTypes.EXPR,
56         TokenTypes.UNARY_PLUS,
57         TokenTypes.UNARY_MINUS,
58         TokenTypes.TYPECAST,
59         TokenTypes.ELIST,
60         TokenTypes.LITERAL_NEW,
61         TokenTypes.METHOD_CALL,
62         TokenTypes.STAR,
63     };
64
65     static {
66         Arrays.sort(ALLOWED_PATH_TOKENTYPES);
67     }
68
69     /** the numbers to ignore in the check, sorted */
70     private double[] mIgnoreNumbers = {-1, 0, 1, 2};
71
72     /** {@inheritDoc} */
73     public int[] getDefaultTokens()
74     {
75         return new int[] {
76             TokenTypes.NUM_DOUBLE,
77             TokenTypes.NUM_FLOAT,
78             TokenTypes.NUM_INT,
79             TokenTypes.NUM_LONG,
80         };
81     }
82
83     /** {@inheritDoc} */
84     public void visitToken(DetailAST aAST)
85     {
86         if (inIgnoreList(aAST)) {
87             return;
88         }
89
90         final DetailAST constantDefAST = findContainingConstantDef(aAST);
91
92         if (constantDefAST == null) {
93             reportMagicNumber(aAST);
94         }
95         else {
96             DetailAST ast = aAST.getParent();
97             while (ast != constantDefAST) {
98                 final int type = ast.getType();
99                 if (Arrays.binarySearch(ALLOWED_PATH_TOKENTYPES, type) < 0) {
100                     reportMagicNumber(aAST);
101                     break;
102                 }
103
104                 ast = ast.getParent();
105             }
106         }
107     }
108
109     /**
110      * Finds the constant definition that contains aAST.
111      * @param aAST the AST
112      * @return the constant def or null if aAST is not
113      * contained in a constant definition
114      */

115     private DetailAST findContainingConstantDef(DetailAST aAST)
116     {
117         DetailAST varDefAST = aAST;
118         while ((varDefAST != null)
119                 && (varDefAST.getType() != TokenTypes.VARIABLE_DEF)
120                 && (varDefAST.getType() != TokenTypes.ENUM_CONSTANT_DEF))
121         {
122             varDefAST = varDefAST.getParent();
123         }
124
125         // no containing variable definition?
126
if (varDefAST == null) {
127             return null;
128         }
129
130         // implicit constant?
131
if (ScopeUtils.inInterfaceOrAnnotationBlock(varDefAST)
132             || (varDefAST.getType() == TokenTypes.ENUM_CONSTANT_DEF))
133         {
134             return varDefAST;
135         }
136
137         // explicit constant
138
final DetailAST modifiersAST =
139                 varDefAST.findFirstToken(TokenTypes.MODIFIERS);
140         if (modifiersAST.branchContains(TokenTypes.FINAL)) {
141             return varDefAST;
142         }
143
144         return null;
145     }
146
147     /**
148      * Reports aAST as a magic number, includes unary operators as needed.
149      * @param aAST the AST node that contains the number to report
150      */

151     private void reportMagicNumber(DetailAST aAST)
152     {
153         String JavaDoc text = aAST.getText();
154         final DetailAST parent = aAST.getParent();
155         DetailAST reportAST = aAST;
156         if (parent.getType() == TokenTypes.UNARY_MINUS) {
157             reportAST = parent;
158             text = "-" + text;
159         }
160         else if (parent.getType() == TokenTypes.UNARY_PLUS) {
161             reportAST = parent;
162             text = "+" + text;
163         }
164         log(reportAST.getLineNo(),
165                 reportAST.getColumnNo(),
166                 "magic.number",
167                 text);
168     }
169
170     /**
171      * Decides whether the number of an AST is in the ignore list of this
172      * check.
173      * @param aAST the AST to check
174      * @return true if the number of aAST is in the ignore list of this
175      * check.
176      */

177     private boolean inIgnoreList(DetailAST aAST)
178     {
179         double value = CheckUtils.parseDouble(aAST.getText(), aAST.getType());
180         final DetailAST parent = aAST.getParent();
181         if (parent.getType() == TokenTypes.UNARY_MINUS) {
182             value = -1 * value;
183         }
184         return (Arrays.binarySearch(mIgnoreNumbers, value) >= 0);
185     }
186
187     /**
188      * Sets the numbers to ignore in the check.
189      * BeanUtils converts numeric token list to double array automatically.
190      * @param aList list of numbers to ignore.
191      */

192     public void setIgnoreNumbers(double[] aList)
193     {
194         if ((aList == null) || (aList.length == 0)) {
195             mIgnoreNumbers = new double[0];
196         }
197         else {
198             mIgnoreNumbers = new double[aList.length];
199             System.arraycopy(aList, 0, mIgnoreNumbers, 0, aList.length);
200             Arrays.sort(mIgnoreNumbers);
201         }
202     }
203 }
204
Popular Tags