1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
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 | |
import com.puppycrawl.tools.checkstyle.checks.CheckUtils; |
26 | |
import java.util.Arrays; |
27 | |
|
28 | |
|
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
|
46 | 6 | public class MagicNumberCheck extends Check |
47 | |
{ |
48 | |
|
49 | |
|
50 | |
|
51 | |
|
52 | 1 | 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 | 1 | Arrays.sort(ALLOWED_PATH_TOKENTYPES); |
67 | 1 | } |
68 | |
|
69 | |
|
70 | 6 | private double[] mIgnoreNumbers = {-1, 0, 1, 2}; |
71 | |
|
72 | |
private boolean mIgnoreHashCodeMethod; |
73 | |
|
74 | |
private boolean mIgnoreAnnotation; |
75 | |
|
76 | |
@Override |
77 | |
public int[] getDefaultTokens() |
78 | |
{ |
79 | 6 | return new int[] { |
80 | |
TokenTypes.NUM_DOUBLE, |
81 | |
TokenTypes.NUM_FLOAT, |
82 | |
TokenTypes.NUM_INT, |
83 | |
TokenTypes.NUM_LONG, |
84 | |
}; |
85 | |
} |
86 | |
|
87 | |
@Override |
88 | |
public void visitToken(DetailAST aAST) |
89 | |
{ |
90 | 510 | if (mIgnoreAnnotation && isInAnnotation(aAST)) { |
91 | 5 | return; |
92 | |
} |
93 | |
|
94 | 505 | if (inIgnoreList(aAST) |
95 | |
|| (mIgnoreHashCodeMethod && isInHashCodeMethod(aAST))) |
96 | |
{ |
97 | 138 | return; |
98 | |
} |
99 | |
|
100 | 367 | final DetailAST constantDefAST = findContainingConstantDef(aAST); |
101 | |
|
102 | 367 | if (constantDefAST == null) { |
103 | 226 | reportMagicNumber(aAST); |
104 | |
} |
105 | |
else { |
106 | 141 | DetailAST ast = aAST.getParent(); |
107 | 563 | while (ast != constantDefAST) { |
108 | 428 | final int type = ast.getType(); |
109 | 428 | if (Arrays.binarySearch(ALLOWED_PATH_TOKENTYPES, type) < 0) { |
110 | 6 | reportMagicNumber(aAST); |
111 | 6 | break; |
112 | |
} |
113 | |
|
114 | 422 | ast = ast.getParent(); |
115 | 422 | } |
116 | |
} |
117 | 367 | } |
118 | |
|
119 | |
|
120 | |
|
121 | |
|
122 | |
|
123 | |
|
124 | |
|
125 | |
private DetailAST findContainingConstantDef(DetailAST aAST) |
126 | |
{ |
127 | 367 | DetailAST varDefAST = aAST; |
128 | |
while ((varDefAST != null) |
129 | |
&& (varDefAST.getType() != TokenTypes.VARIABLE_DEF) |
130 | 2041 | && (varDefAST.getType() != TokenTypes.ENUM_CONSTANT_DEF)) |
131 | |
{ |
132 | 1674 | varDefAST = varDefAST.getParent(); |
133 | |
} |
134 | |
|
135 | |
|
136 | 367 | if (varDefAST == null) { |
137 | 68 | return null; |
138 | |
} |
139 | |
|
140 | |
|
141 | 299 | if (ScopeUtils.inInterfaceOrAnnotationBlock(varDefAST) |
142 | |
|| (varDefAST.getType() == TokenTypes.ENUM_CONSTANT_DEF)) |
143 | |
{ |
144 | 23 | return varDefAST; |
145 | |
} |
146 | |
|
147 | |
|
148 | 276 | final DetailAST modifiersAST = |
149 | |
varDefAST.findFirstToken(TokenTypes.MODIFIERS); |
150 | 276 | if (modifiersAST.branchContains(TokenTypes.FINAL)) { |
151 | 118 | return varDefAST; |
152 | |
} |
153 | |
|
154 | 158 | return null; |
155 | |
} |
156 | |
|
157 | |
|
158 | |
|
159 | |
|
160 | |
|
161 | |
private void reportMagicNumber(DetailAST aAST) |
162 | |
{ |
163 | 232 | String text = aAST.getText(); |
164 | 232 | final DetailAST parent = aAST.getParent(); |
165 | 232 | DetailAST reportAST = aAST; |
166 | 232 | if (parent.getType() == TokenTypes.UNARY_MINUS) { |
167 | 10 | reportAST = parent; |
168 | 10 | text = "-" + text; |
169 | |
} |
170 | 222 | else if (parent.getType() == TokenTypes.UNARY_PLUS) { |
171 | 9 | reportAST = parent; |
172 | 9 | text = "+" + text; |
173 | |
} |
174 | 232 | log(reportAST.getLineNo(), |
175 | |
reportAST.getColumnNo(), |
176 | |
"magic.number", |
177 | |
text); |
178 | 232 | } |
179 | |
|
180 | |
|
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
|
187 | |
|
188 | |
|
189 | |
|
190 | |
|
191 | |
private boolean isInHashCodeMethod(DetailAST aAST) |
192 | |
{ |
193 | |
|
194 | 62 | if (!ScopeUtils.inCodeBlock(aAST)) { |
195 | 27 | return false; |
196 | |
} |
197 | |
|
198 | |
|
199 | 35 | DetailAST methodDefAST = aAST.getParent(); |
200 | |
while ((null != methodDefAST) |
201 | 189 | && (TokenTypes.METHOD_DEF != methodDefAST.getType())) |
202 | |
{ |
203 | 154 | methodDefAST = methodDefAST.getParent(); |
204 | |
} |
205 | |
|
206 | 35 | if (null == methodDefAST) { |
207 | 3 | return false; |
208 | |
} |
209 | |
|
210 | |
|
211 | 32 | final DetailAST identAST = |
212 | |
methodDefAST.findFirstToken(TokenTypes.IDENT); |
213 | 32 | if (!"hashCode".equals(identAST.getText())) { |
214 | 30 | return false; |
215 | |
} |
216 | |
|
217 | |
|
218 | 2 | final DetailAST paramAST = |
219 | |
methodDefAST.findFirstToken(TokenTypes.PARAMETERS); |
220 | 2 | if (0 != paramAST.getChildCount()) { |
221 | 1 | return false; |
222 | |
} |
223 | |
|
224 | |
|
225 | |
|
226 | 1 | return true; |
227 | |
} |
228 | |
|
229 | |
|
230 | |
|
231 | |
|
232 | |
|
233 | |
|
234 | |
|
235 | |
|
236 | |
private boolean inIgnoreList(DetailAST aAST) |
237 | |
{ |
238 | 505 | double value = CheckUtils.parseDouble(aAST.getText(), aAST.getType()); |
239 | 505 | final DetailAST parent = aAST.getParent(); |
240 | 505 | if (parent.getType() == TokenTypes.UNARY_MINUS) { |
241 | 16 | value = -1 * value; |
242 | |
} |
243 | 505 | return (Arrays.binarySearch(mIgnoreNumbers, value) >= 0); |
244 | |
} |
245 | |
|
246 | |
|
247 | |
|
248 | |
|
249 | |
|
250 | |
|
251 | |
public void setIgnoreNumbers(double[] aList) |
252 | |
{ |
253 | 3 | if ((aList == null) || (aList.length == 0)) { |
254 | 1 | mIgnoreNumbers = new double[0]; |
255 | |
} |
256 | |
else { |
257 | 2 | mIgnoreNumbers = new double[aList.length]; |
258 | 2 | System.arraycopy(aList, 0, mIgnoreNumbers, 0, aList.length); |
259 | 2 | Arrays.sort(mIgnoreNumbers); |
260 | |
} |
261 | 3 | } |
262 | |
|
263 | |
|
264 | |
|
265 | |
|
266 | |
|
267 | |
|
268 | |
public void setIgnoreHashCodeMethod(boolean aIgnoreHashCodeMethod) |
269 | |
{ |
270 | 1 | mIgnoreHashCodeMethod = aIgnoreHashCodeMethod; |
271 | 1 | } |
272 | |
|
273 | |
|
274 | |
|
275 | |
|
276 | |
|
277 | |
public void setIgnoreAnnotation(boolean aIgnoreAnnotation) |
278 | |
{ |
279 | 5 | mIgnoreAnnotation = aIgnoreAnnotation; |
280 | 5 | } |
281 | |
|
282 | |
|
283 | |
|
284 | |
|
285 | |
|
286 | |
|
287 | |
|
288 | |
|
289 | |
|
290 | |
private boolean isInAnnotation(DetailAST aAST) |
291 | |
{ |
292 | 420 | if ((null == aAST.getParent()) |
293 | |
|| (null == aAST.getParent().getParent())) |
294 | |
{ |
295 | 0 | return false; |
296 | |
} |
297 | |
|
298 | 420 | return (TokenTypes.ANNOTATION == aAST.getParent().getParent().getType()) |
299 | |
|| (TokenTypes.ANNOTATION_MEMBER_VALUE_PAIR |
300 | |
== aAST.getParent().getParent().getType()); |
301 | |
} |
302 | |
} |