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; |
20 | |
|
21 | |
import com.google.common.collect.Maps; |
22 | |
import com.google.common.collect.Sets; |
23 | |
import com.puppycrawl.tools.checkstyle.api.Check; |
24 | |
import com.puppycrawl.tools.checkstyle.api.DetailAST; |
25 | |
import com.puppycrawl.tools.checkstyle.api.FastStack; |
26 | |
import com.puppycrawl.tools.checkstyle.api.FullIdent; |
27 | |
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; |
28 | |
import com.puppycrawl.tools.checkstyle.api.TokenTypes; |
29 | |
import java.util.Map; |
30 | |
import java.util.Set; |
31 | |
|
32 | |
|
33 | |
|
34 | |
|
35 | |
|
36 | |
|
37 | |
|
38 | |
|
39 | |
|
40 | 35 | public abstract class AbstractTypeAwareCheck extends Check |
41 | |
{ |
42 | |
|
43 | 35 | private final Set<String> mImports = Sets.newHashSet(); |
44 | |
|
45 | |
|
46 | |
private FullIdent mPackageFullIdent; |
47 | |
|
48 | |
|
49 | |
private String mCurrentClass; |
50 | |
|
51 | |
|
52 | |
private ClassResolver mClassResolver; |
53 | |
|
54 | |
|
55 | 35 | private final FastStack<Map<String, ClassInfo>> mTypeParams = |
56 | |
FastStack.newInstance(); |
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | |
|
62 | |
|
63 | |
|
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
|
70 | 35 | private boolean mLogLoadErrors = true; |
71 | |
|
72 | |
|
73 | |
|
74 | |
|
75 | |
|
76 | |
|
77 | |
|
78 | |
public final void setLogLoadErrors(boolean aLogLoadErrors) |
79 | |
{ |
80 | 0 | mLogLoadErrors = aLogLoadErrors; |
81 | 0 | } |
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | |
|
87 | |
private boolean mSuppressLoadErrors; |
88 | |
|
89 | |
|
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
public final void setSuppressLoadErrors(boolean aSuppressLoadErrors) |
95 | |
{ |
96 | 0 | mSuppressLoadErrors = aSuppressLoadErrors; |
97 | 0 | } |
98 | |
|
99 | |
|
100 | |
|
101 | |
|
102 | |
|
103 | |
|
104 | |
protected abstract void processAST(DetailAST aAST); |
105 | |
|
106 | |
@Override |
107 | |
public final int[] getRequiredTokens() |
108 | |
{ |
109 | 0 | return new int[] { |
110 | |
TokenTypes.PACKAGE_DEF, |
111 | |
TokenTypes.IMPORT, |
112 | |
TokenTypes.CLASS_DEF, |
113 | |
TokenTypes.ENUM_DEF, |
114 | |
}; |
115 | |
} |
116 | |
|
117 | |
@Override |
118 | |
public void beginTree(DetailAST aRootAST) |
119 | |
{ |
120 | 35 | mPackageFullIdent = FullIdent.createFullIdent(null); |
121 | 35 | mImports.clear(); |
122 | |
|
123 | 35 | mImports.add("java.lang.*"); |
124 | 35 | mClassResolver = null; |
125 | 35 | mCurrentClass = ""; |
126 | 35 | mTypeParams.clear(); |
127 | 35 | } |
128 | |
|
129 | |
@Override |
130 | |
public final void visitToken(DetailAST aAST) |
131 | |
{ |
132 | 579 | if (aAST.getType() == TokenTypes.PACKAGE_DEF) { |
133 | 20 | processPackage(aAST); |
134 | |
} |
135 | 559 | else if (aAST.getType() == TokenTypes.IMPORT) { |
136 | 16 | processImport(aAST); |
137 | |
} |
138 | 543 | else if ((aAST.getType() == TokenTypes.CLASS_DEF) |
139 | |
|| (aAST.getType() == TokenTypes.ENUM_DEF)) |
140 | |
{ |
141 | 107 | processClass(aAST); |
142 | |
} |
143 | |
else { |
144 | 436 | if (aAST.getType() == TokenTypes.METHOD_DEF) { |
145 | 390 | processTypeParams(aAST); |
146 | |
} |
147 | 436 | processAST(aAST); |
148 | |
} |
149 | 579 | } |
150 | |
|
151 | |
@Override |
152 | |
public final void leaveToken(DetailAST aAST) |
153 | |
{ |
154 | 579 | if ((aAST.getType() == TokenTypes.CLASS_DEF) |
155 | |
|| (aAST.getType() == TokenTypes.ENUM_DEF)) |
156 | |
{ |
157 | |
|
158 | 107 | int dotIdx = mCurrentClass.lastIndexOf("$"); |
159 | 107 | if (dotIdx == -1) { |
160 | |
|
161 | 48 | dotIdx = mCurrentClass.lastIndexOf("."); |
162 | |
} |
163 | 107 | if (dotIdx == -1) { |
164 | |
|
165 | 48 | mCurrentClass = ""; |
166 | |
} |
167 | |
else { |
168 | 59 | mCurrentClass = mCurrentClass.substring(0, dotIdx); |
169 | |
} |
170 | 107 | mTypeParams.pop(); |
171 | 107 | } |
172 | 472 | else if (aAST.getType() == TokenTypes.METHOD_DEF) { |
173 | 390 | mTypeParams.pop(); |
174 | |
} |
175 | 82 | else if ((aAST.getType() != TokenTypes.PACKAGE_DEF) |
176 | |
&& (aAST.getType() != TokenTypes.IMPORT)) |
177 | |
{ |
178 | 46 | leaveAST(aAST); |
179 | |
} |
180 | 579 | } |
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | |
|
187 | |
|
188 | |
protected void leaveAST(DetailAST aAST) |
189 | |
{ |
190 | 46 | } |
191 | |
|
192 | |
|
193 | |
|
194 | |
|
195 | |
|
196 | |
|
197 | |
|
198 | |
|
199 | |
|
200 | |
protected boolean isUnchecked(Class<?> aException) |
201 | |
{ |
202 | 46 | return isSubclass(aException, RuntimeException.class) |
203 | |
|| isSubclass(aException, Error.class); |
204 | |
} |
205 | |
|
206 | |
|
207 | |
|
208 | |
|
209 | |
|
210 | |
|
211 | |
|
212 | |
|
213 | |
|
214 | |
|
215 | |
|
216 | |
protected boolean isSubclass(Class<?> aChild, Class<?> aParent) |
217 | |
{ |
218 | 106 | return (aParent != null) && (aChild != null) |
219 | |
&& aParent.isAssignableFrom(aChild); |
220 | |
} |
221 | |
|
222 | |
|
223 | |
private ClassResolver getClassResolver() |
224 | |
{ |
225 | 121 | if (mClassResolver == null) { |
226 | 17 | mClassResolver = |
227 | |
new ClassResolver(getClassLoader(), |
228 | |
mPackageFullIdent.getText(), |
229 | |
mImports); |
230 | |
} |
231 | 121 | return mClassResolver; |
232 | |
} |
233 | |
|
234 | |
|
235 | |
|
236 | |
|
237 | |
|
238 | |
|
239 | |
|
240 | |
|
241 | |
protected final Class<?> resolveClass(String aClassName, |
242 | |
String aCurrentClass) |
243 | |
{ |
244 | |
try { |
245 | 121 | return getClassResolver().resolve(aClassName, aCurrentClass); |
246 | |
} |
247 | 0 | catch (final ClassNotFoundException e) { |
248 | 0 | return null; |
249 | |
} |
250 | |
} |
251 | |
|
252 | |
|
253 | |
|
254 | |
|
255 | |
|
256 | |
|
257 | |
|
258 | |
protected final Class<?> tryLoadClass(Token aIdent, String aCurrentClass) |
259 | |
{ |
260 | 121 | final Class<?> clazz = resolveClass(aIdent.getText(), aCurrentClass); |
261 | 121 | if (clazz == null) { |
262 | 0 | logLoadError(aIdent); |
263 | |
} |
264 | 121 | return clazz; |
265 | |
} |
266 | |
|
267 | |
|
268 | |
|
269 | |
|
270 | |
|
271 | |
|
272 | |
protected abstract void logLoadError(Token aIdent); |
273 | |
|
274 | |
|
275 | |
|
276 | |
|
277 | |
|
278 | |
|
279 | |
|
280 | |
|
281 | |
protected final void logLoadErrorImpl(int aLineNo, int aColumnNo, |
282 | |
String aMsgKey, Object... aValues) |
283 | |
{ |
284 | 0 | if (!mLogLoadErrors) { |
285 | 0 | final LocalizedMessage msg = new LocalizedMessage(aLineNo, |
286 | |
aColumnNo, |
287 | |
getMessageBundle(), |
288 | |
aMsgKey, |
289 | |
aValues, |
290 | |
getSeverityLevel(), |
291 | |
getId(), |
292 | |
this.getClass(), |
293 | |
null); |
294 | 0 | throw new RuntimeException(msg.getMessage()); |
295 | |
} |
296 | |
|
297 | 0 | if (!mSuppressLoadErrors) { |
298 | 0 | log(aLineNo, aColumnNo, aMsgKey, aValues); |
299 | |
} |
300 | 0 | } |
301 | |
|
302 | |
|
303 | |
|
304 | |
|
305 | |
|
306 | |
private void processPackage(DetailAST aAST) |
307 | |
{ |
308 | 20 | final DetailAST nameAST = aAST.getLastChild().getPreviousSibling(); |
309 | 20 | mPackageFullIdent = FullIdent.createFullIdent(nameAST); |
310 | 20 | } |
311 | |
|
312 | |
|
313 | |
|
314 | |
|
315 | |
|
316 | |
private void processImport(DetailAST aAST) |
317 | |
{ |
318 | 16 | final FullIdent name = FullIdent.createFullIdentBelow(aAST); |
319 | 16 | if (name != null) { |
320 | 16 | mImports.add(name.getText()); |
321 | |
} |
322 | 16 | } |
323 | |
|
324 | |
|
325 | |
|
326 | |
|
327 | |
|
328 | |
private void processTypeParams(DetailAST aAST) |
329 | |
{ |
330 | 497 | final DetailAST typeParams = |
331 | |
aAST.findFirstToken(TokenTypes.TYPE_PARAMETERS); |
332 | |
|
333 | 497 | final Map<String, ClassInfo> paramsMap = Maps.newHashMap(); |
334 | 497 | mTypeParams.push(paramsMap); |
335 | |
|
336 | 497 | if (typeParams == null) { |
337 | 481 | return; |
338 | |
} |
339 | |
|
340 | 16 | for (DetailAST child = typeParams.getFirstChild(); |
341 | 78 | child != null; |
342 | 62 | child = child.getNextSibling()) |
343 | |
{ |
344 | 62 | if (child.getType() == TokenTypes.TYPE_PARAMETER) { |
345 | 23 | final DetailAST param = child; |
346 | 23 | final String alias = |
347 | |
param.findFirstToken(TokenTypes.IDENT).getText(); |
348 | 23 | final DetailAST bounds = |
349 | |
param.findFirstToken(TokenTypes.TYPE_UPPER_BOUNDS); |
350 | 23 | if (bounds != null) { |
351 | 17 | final FullIdent name = |
352 | |
FullIdent.createFullIdentBelow(bounds); |
353 | 17 | final ClassInfo ci = |
354 | |
createClassInfo(new Token(name), getCurrentClassName()); |
355 | 17 | paramsMap.put(alias, ci); |
356 | |
} |
357 | |
} |
358 | |
} |
359 | 16 | } |
360 | |
|
361 | |
|
362 | |
|
363 | |
|
364 | |
|
365 | |
private void processClass(DetailAST aAST) |
366 | |
{ |
367 | 107 | final DetailAST ident = aAST.findFirstToken(TokenTypes.IDENT); |
368 | 107 | mCurrentClass += ("".equals(mCurrentClass) ? "" : "$") |
369 | |
+ ident.getText(); |
370 | |
|
371 | 107 | processTypeParams(aAST); |
372 | 107 | } |
373 | |
|
374 | |
|
375 | |
|
376 | |
|
377 | |
|
378 | |
protected final String getCurrentClassName() |
379 | |
{ |
380 | 238 | return mCurrentClass; |
381 | |
} |
382 | |
|
383 | |
|
384 | |
|
385 | |
|
386 | |
|
387 | |
|
388 | |
|
389 | |
protected final ClassInfo createClassInfo(final Token aName, |
390 | |
final String aSurroundingClass) |
391 | |
{ |
392 | 238 | final ClassInfo ci = findClassAlias(aName.getText()); |
393 | 238 | if (ci != null) { |
394 | 56 | return new ClassAlias(aName, ci); |
395 | |
} |
396 | 182 | return new RegularClass(aName, aSurroundingClass, this); |
397 | |
} |
398 | |
|
399 | |
|
400 | |
|
401 | |
|
402 | |
|
403 | |
|
404 | |
protected final ClassInfo findClassAlias(final String aName) |
405 | |
{ |
406 | 238 | ClassInfo ci = null; |
407 | 648 | for (int i = mTypeParams.size() - 1; i >= 0; i--) { |
408 | 466 | final Map<String, ClassInfo> paramMap = mTypeParams.peek(i); |
409 | 466 | ci = paramMap.get(aName); |
410 | 466 | if (ci != null) { |
411 | 56 | break; |
412 | |
} |
413 | |
} |
414 | 238 | return ci; |
415 | |
} |
416 | |
|
417 | |
|
418 | |
|
419 | |
|
420 | |
protected abstract static class ClassInfo |
421 | |
{ |
422 | |
|
423 | |
private final Token mName; |
424 | |
|
425 | |
|
426 | |
public final Token getName() |
427 | |
{ |
428 | 362 | return mName; |
429 | |
} |
430 | |
|
431 | |
|
432 | |
public abstract Class<?> getClazz(); |
433 | |
|
434 | |
|
435 | |
|
436 | |
|
437 | |
|
438 | |
protected ClassInfo(final Token aName) |
439 | 238 | { |
440 | 238 | if (aName == null) { |
441 | 0 | throw new NullPointerException( |
442 | |
"ClassInfo's name should be non-null"); |
443 | |
} |
444 | 238 | mName = aName; |
445 | 238 | } |
446 | |
} |
447 | |
|
448 | |
|
449 | 182 | private static final class RegularClass extends ClassInfo |
450 | |
{ |
451 | |
|
452 | |
private final String mSurroundingClass; |
453 | |
|
454 | 182 | private boolean mIsLoadable = true; |
455 | |
|
456 | |
private Class<?> mClass; |
457 | |
|
458 | |
private final AbstractTypeAwareCheck mCheck; |
459 | |
|
460 | |
|
461 | |
|
462 | |
|
463 | |
|
464 | |
|
465 | |
|
466 | |
private RegularClass(final Token aName, |
467 | |
final String aSurroundingClass, |
468 | |
final AbstractTypeAwareCheck aCheck) |
469 | |
{ |
470 | 182 | super(aName); |
471 | 182 | mSurroundingClass = aSurroundingClass; |
472 | 182 | mCheck = aCheck; |
473 | 182 | } |
474 | |
|
475 | |
private boolean isLoadable() |
476 | |
{ |
477 | 232 | return mIsLoadable; |
478 | |
} |
479 | |
|
480 | |
@Override |
481 | |
public Class<?> getClazz() |
482 | |
{ |
483 | 232 | if (isLoadable() && (mClass == null)) { |
484 | 121 | setClazz(mCheck.tryLoadClass(getName(), mSurroundingClass)); |
485 | |
} |
486 | 232 | return mClass; |
487 | |
} |
488 | |
|
489 | |
|
490 | |
|
491 | |
|
492 | |
|
493 | |
private void setClazz(Class<?> aClass) |
494 | |
{ |
495 | 121 | mClass = aClass; |
496 | 121 | mIsLoadable = (mClass != null); |
497 | 121 | } |
498 | |
|
499 | |
@Override |
500 | |
public String toString() |
501 | |
{ |
502 | 0 | return "RegularClass[name=" + getName() |
503 | |
+ ", in class=" + mSurroundingClass |
504 | |
+ ", loadable=" + mIsLoadable |
505 | |
+ ", class=" + mClass + "]"; |
506 | |
} |
507 | |
} |
508 | |
|
509 | |
|
510 | |
private static class ClassAlias extends ClassInfo |
511 | |
{ |
512 | |
|
513 | |
private final ClassInfo mClassInfo; |
514 | |
|
515 | |
|
516 | |
|
517 | |
|
518 | |
|
519 | |
|
520 | |
ClassAlias(final Token aName, ClassInfo aClassInfo) |
521 | |
{ |
522 | 56 | super(aName); |
523 | 56 | mClassInfo = aClassInfo; |
524 | 56 | } |
525 | |
|
526 | |
@Override |
527 | |
public final Class<?> getClazz() |
528 | |
{ |
529 | 59 | return mClassInfo.getClazz(); |
530 | |
} |
531 | |
|
532 | |
@Override |
533 | |
public String toString() |
534 | |
{ |
535 | 0 | return "ClassAlias[alias " + getName() |
536 | |
+ " for " + mClassInfo + "]"; |
537 | |
} |
538 | |
} |
539 | |
|
540 | |
|
541 | |
|
542 | |
|
543 | 35 | protected static class Token |
544 | |
{ |
545 | |
|
546 | |
private final int mColumn; |
547 | |
|
548 | |
private final int mLine; |
549 | |
|
550 | |
private final String mText; |
551 | |
|
552 | |
|
553 | |
|
554 | |
|
555 | |
|
556 | |
|
557 | |
|
558 | |
public Token(String aText, int aLine, int aColumn) |
559 | 82 | { |
560 | 82 | mText = aText; |
561 | 82 | mLine = aLine; |
562 | 82 | mColumn = aColumn; |
563 | 82 | } |
564 | |
|
565 | |
|
566 | |
|
567 | |
|
568 | |
|
569 | |
public Token(FullIdent aFullIdent) |
570 | 156 | { |
571 | 156 | mText = aFullIdent.getText(); |
572 | 156 | mLine = aFullIdent.getLineNo(); |
573 | 156 | mColumn = aFullIdent.getColumnNo(); |
574 | 156 | } |
575 | |
|
576 | |
|
577 | |
public int getLineNo() |
578 | |
{ |
579 | 28 | return mLine; |
580 | |
} |
581 | |
|
582 | |
|
583 | |
public int getColumnNo() |
584 | |
{ |
585 | 28 | return mColumn; |
586 | |
} |
587 | |
|
588 | |
|
589 | |
public String getText() |
590 | |
{ |
591 | 586 | return mText; |
592 | |
} |
593 | |
|
594 | |
@Override |
595 | |
public String toString() |
596 | |
{ |
597 | 0 | return "Token[" + getText() + "(" + getLineNo() |
598 | |
+ "x" + getColumnNo() + ")]"; |
599 | |
} |
600 | |
} |
601 | |
} |