1 /**
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd.typeresolution;
5
6 import java.util.ArrayList;
7 import java.util.Collections;
8 import java.util.HashMap;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.logging.Level;
12 import java.util.logging.Logger;
13
14 import net.sourceforge.pmd.ast.ASTAdditiveExpression;
15 import net.sourceforge.pmd.ast.ASTAllocationExpression;
16 import net.sourceforge.pmd.ast.ASTAndExpression;
17 import net.sourceforge.pmd.ast.ASTAnnotationTypeDeclaration;
18 import net.sourceforge.pmd.ast.ASTArrayDimsAndInits;
19 import net.sourceforge.pmd.ast.ASTBooleanLiteral;
20 import net.sourceforge.pmd.ast.ASTCastExpression;
21 import net.sourceforge.pmd.ast.ASTClassOrInterfaceDeclaration;
22 import net.sourceforge.pmd.ast.ASTClassOrInterfaceType;
23 import net.sourceforge.pmd.ast.ASTCompilationUnit;
24 import net.sourceforge.pmd.ast.ASTConditionalAndExpression;
25 import net.sourceforge.pmd.ast.ASTConditionalExpression;
26 import net.sourceforge.pmd.ast.ASTConditionalOrExpression;
27 import net.sourceforge.pmd.ast.ASTEnumDeclaration;
28 import net.sourceforge.pmd.ast.ASTEqualityExpression;
29 import net.sourceforge.pmd.ast.ASTExclusiveOrExpression;
30 import net.sourceforge.pmd.ast.ASTExpression;
31 import net.sourceforge.pmd.ast.ASTFieldDeclaration;
32 import net.sourceforge.pmd.ast.ASTImportDeclaration;
33 import net.sourceforge.pmd.ast.ASTInclusiveOrExpression;
34 import net.sourceforge.pmd.ast.ASTInstanceOfExpression;
35 import net.sourceforge.pmd.ast.ASTLiteral;
36 import net.sourceforge.pmd.ast.ASTMultiplicativeExpression;
37 import net.sourceforge.pmd.ast.ASTName;
38 import net.sourceforge.pmd.ast.ASTNullLiteral;
39 import net.sourceforge.pmd.ast.ASTPackageDeclaration;
40 import net.sourceforge.pmd.ast.ASTPostfixExpression;
41 import net.sourceforge.pmd.ast.ASTPreDecrementExpression;
42 import net.sourceforge.pmd.ast.ASTPreIncrementExpression;
43 import net.sourceforge.pmd.ast.ASTPrimaryExpression;
44 import net.sourceforge.pmd.ast.ASTPrimaryPrefix;
45 import net.sourceforge.pmd.ast.ASTPrimarySuffix;
46 import net.sourceforge.pmd.ast.ASTPrimitiveType;
47 import net.sourceforge.pmd.ast.ASTReferenceType;
48 import net.sourceforge.pmd.ast.ASTRelationalExpression;
49 import net.sourceforge.pmd.ast.ASTShiftExpression;
50 import net.sourceforge.pmd.ast.ASTStatementExpression;
51 import net.sourceforge.pmd.ast.ASTType;
52 import net.sourceforge.pmd.ast.ASTTypeDeclaration;
53 import net.sourceforge.pmd.ast.ASTUnaryExpression;
54 import net.sourceforge.pmd.ast.ASTUnaryExpressionNotPlusMinus;
55 import net.sourceforge.pmd.ast.ASTVariableDeclarator;
56 import net.sourceforge.pmd.ast.ASTVariableDeclaratorId;
57 import net.sourceforge.pmd.ast.JavaParserVisitorAdapter;
58 import net.sourceforge.pmd.ast.Node;
59 import net.sourceforge.pmd.ast.SimpleNode;
60 import net.sourceforge.pmd.ast.TypeNode;
61
62
63
64
65
66
67
68 public class ClassTypeResolver extends JavaParserVisitorAdapter {
69
70 private static final Logger LOG = Logger.getLogger(ClassTypeResolver.class.getName());
71
72 private static final Map<String, Class<?>> myPrimitiveTypes;
73 private static final Map<String, String> myJavaLang;
74
75 static {
76
77 Map<String, Class<?>> thePrimitiveTypes = new HashMap<String, Class<?>>();
78 thePrimitiveTypes.put("void", Void.TYPE);
79 thePrimitiveTypes.put("boolean", Boolean.TYPE);
80 thePrimitiveTypes.put("byte", Byte.TYPE);
81 thePrimitiveTypes.put("char", Character.TYPE);
82 thePrimitiveTypes.put("short", Short.TYPE);
83 thePrimitiveTypes.put("int", Integer.TYPE);
84 thePrimitiveTypes.put("long", Long.TYPE);
85 thePrimitiveTypes.put("float", Float.TYPE);
86 thePrimitiveTypes.put("double", Double.TYPE);
87 myPrimitiveTypes = Collections.unmodifiableMap(thePrimitiveTypes);
88
89 Map<String, String> theJavaLang = new HashMap<String, String>();
90 theJavaLang.put("Boolean", "java.lang.Boolean");
91 theJavaLang.put("Byte", "java.lang.Byte");
92 theJavaLang.put("Character", "java.lang.Character");
93 theJavaLang.put("CharSequence", "java.lang.CharSequence");
94 theJavaLang.put("Class", "java.lang.Class");
95 theJavaLang.put("ClassLoader", "java.lang.ClassLoader");
96 theJavaLang.put("Cloneable", "java.lang.Cloneable");
97 theJavaLang.put("Comparable", "java.lang.Comparable");
98 theJavaLang.put("Compiler", "java.lang.Compiler");
99 theJavaLang.put("Double", "java.lang.Double");
100 theJavaLang.put("Float", "java.lang.Float");
101 theJavaLang.put("InheritableThreadLocal", "java.lang.InheritableThreadLocal");
102 theJavaLang.put("Integer", "java.lang.Integer");
103 theJavaLang.put("Long", "java.lang.Long");
104 theJavaLang.put("Math", "java.lang.Math");
105 theJavaLang.put("Number", "java.lang.Number");
106 theJavaLang.put("Object", "java.lang.Object");
107 theJavaLang.put("Package", "java.lang.Package");
108 theJavaLang.put("Process", "java.lang.Process");
109 theJavaLang.put("Runnable", "java.lang.Runnable");
110 theJavaLang.put("Runtime", "java.lang.Runtime");
111 theJavaLang.put("RuntimePermission", "java.lang.RuntimePermission");
112 theJavaLang.put("SecurityManager", "java.lang.SecurityManager");
113 theJavaLang.put("Short", "java.lang.Short");
114 theJavaLang.put("StackTraceElement", "java.lang.StackTraceElement");
115 theJavaLang.put("StrictMath", "java.lang.StrictMath");
116 theJavaLang.put("String", "java.lang.String");
117 theJavaLang.put("StringBuffer", "java.lang.StringBuffer");
118 theJavaLang.put("System", "java.lang.System");
119 theJavaLang.put("Thread", "java.lang.Thread");
120 theJavaLang.put("ThreadGroup", "java.lang.ThreadGroup");
121 theJavaLang.put("ThreadLocal", "java.lang.ThreadLocal");
122 theJavaLang.put("Throwable", "java.lang.Throwable");
123 theJavaLang.put("Void", "java.lang.Void");
124 myJavaLang = Collections.unmodifiableMap(theJavaLang);
125 }
126
127 private final PMDASMClassLoader pmdClassLoader;
128 private Map<String, String> importedClasses;
129 private List<String> importedOnDemand;
130
131 public ClassTypeResolver() {
132 this(ClassTypeResolver.class.getClassLoader());
133 }
134
135 public ClassTypeResolver(ClassLoader classLoader) {
136 pmdClassLoader = new PMDASMClassLoader(classLoader);
137 }
138
139
140 public Object visit(ASTCompilationUnit node, Object data) {
141 String className = null;
142 try {
143 importedOnDemand = new ArrayList<String>();
144 className = getClassName(node);
145 if (className != null) {
146 populateClassName(node, className);
147 }
148 } catch (ClassNotFoundException e) {
149 LOG.log(Level.FINE, "Could not find class " + className + ", due to: " + e.getClass().getName() + ": " + e.getMessage());
150 } catch (NoClassDefFoundError e) {
151 LOG.log(Level.WARNING, "Could not find class " + className + ", due to: " + e.getClass().getName() + ": " + e.getMessage());
152 } catch (ClassFormatError e) {
153 LOG.log(Level.WARNING, "Could not find class " + className + ", due to: " + e.getClass().getName() + ": " + e.getMessage());
154 } finally {
155 populateImports(node);
156 }
157 return super.visit(node, data);
158 }
159
160 public Object visit(ASTImportDeclaration node, Object data) {
161 ASTName importedType = (ASTName)node.jjtGetChild(0);
162 if (importedType.getType() != null) {
163 node.setType(importedType.getType());
164 } else {
165 populateType(node, importedType.getImage());
166 }
167
168 if (node.getType() != null) {
169 node.setPackage(node.getType().getPackage());
170 }
171 return data;
172 }
173
174 public Object visit(ASTTypeDeclaration node, Object data) {
175 super.visit(node, data);
176 rollupTypeUnary(node);
177 return data;
178 }
179
180 public Object visit(ASTClassOrInterfaceType node, Object data) {
181 populateType(node, node.getImage());
182 return data;
183 }
184
185 public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
186 populateType(node, node.getImage());
187 return super.visit(node, data);
188 }
189
190 public Object visit(ASTEnumDeclaration node, Object data) {
191 populateType(node, node.getImage());
192 return super.visit(node, data);
193 }
194
195 public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
196 populateType(node, node.getImage());
197 return super.visit(node, data);
198 }
199
200 public Object visit(ASTName node, Object data) {
201
202
203
204
205
206
207 if (node.getNameDeclaration() == null) {
208
209
210
211 if (!(node.jjtGetParent() instanceof ASTPackageDeclaration || node.jjtGetParent() instanceof ASTImportDeclaration)) {
212 String name = node.getImage();
213 if (name.indexOf('.') != -1) {
214 name = name.substring(0, name.indexOf('.'));
215 }
216 populateType(node, name);
217 }
218 } else {
219
220 if (node.getNameDeclaration().getNode() instanceof TypeNode) {
221 node.setType(((TypeNode)node.getNameDeclaration().getNode()).getType());
222 }
223 }
224 return super.visit(node, data);
225 }
226
227 public Object visit(ASTFieldDeclaration node, Object data) {
228 super.visit(node, data);
229 rollupTypeUnary(node);
230 return data;
231 }
232
233 public Object visit(ASTVariableDeclarator node, Object data) {
234 super.visit(node, data);
235 rollupTypeUnary(node);
236 return data;
237 }
238
239 public Object visit(ASTVariableDeclaratorId node, Object data) {
240 if (node == null || node.getNameDeclaration() == null) {
241 return super.visit(node, data);
242 }
243 String name = node.getNameDeclaration().getTypeImage();
244 if (name.indexOf('.') != -1) {
245 name = name.substring(0, name.indexOf('.'));
246 }
247 populateType(node, name);
248 return super.visit(node, data);
249 }
250
251 public Object visit(ASTType node, Object data) {
252 super.visit(node, data);
253 rollupTypeUnary(node);
254 return data;
255 }
256
257 public Object visit(ASTReferenceType node, Object data) {
258 super.visit(node, data);
259 rollupTypeUnary(node);
260 return data;
261 }
262
263 public Object visit(ASTPrimitiveType node, Object data) {
264 populateType(node, node.getImage());
265 return super.visit(node, data);
266 }
267
268 public Object visit(ASTExpression node, Object data) {
269 super.visit(node, data);
270 rollupTypeUnary(node);
271 return data;
272 }
273
274 public Object visit(ASTConditionalExpression node, Object data) {
275 super.visit(node, data);
276 if (node.isTernary()) {
277
278 } else {
279 rollupTypeUnary(node);
280 }
281 return data;
282 }
283
284 public Object visit(ASTConditionalOrExpression node, Object data) {
285 populateType(node, "boolean");
286 return super.visit(node, data);
287 }
288
289 public Object visit(ASTConditionalAndExpression node, Object data) {
290 populateType(node, "boolean");
291 return super.visit(node, data);
292 }
293
294 public Object visit(ASTInclusiveOrExpression node, Object data) {
295 super.visit(node, data);
296 rollupTypeBinaryNumericPromotion(node);
297 return data;
298 }
299
300 public Object visit(ASTExclusiveOrExpression node, Object data) {
301 super.visit(node, data);
302 rollupTypeBinaryNumericPromotion(node);
303 return data;
304 }
305
306 public Object visit(ASTAndExpression node, Object data) {
307 super.visit(node, data);
308 rollupTypeBinaryNumericPromotion(node);
309 return data;
310 }
311
312 public Object visit(ASTEqualityExpression node, Object data) {
313 populateType(node, "boolean");
314 return super.visit(node, data);
315 }
316
317 public Object visit(ASTInstanceOfExpression node, Object data) {
318 populateType(node, "boolean");
319 return super.visit(node, data);
320 }
321
322 public Object visit(ASTRelationalExpression node, Object data) {
323 populateType(node, "boolean");
324 return super.visit(node, data);
325 }
326
327 public Object visit(ASTShiftExpression node, Object data) {
328 super.visit(node, data);
329
330 rollupTypeUnaryNumericPromotion(node);
331 return data;
332 }
333
334 public Object visit(ASTAdditiveExpression node, Object data) {
335 super.visit(node, data);
336 rollupTypeBinaryNumericPromotion(node);
337 return data;
338 }
339
340 public Object visit(ASTMultiplicativeExpression node, Object data) {
341 super.visit(node, data);
342 rollupTypeBinaryNumericPromotion(node);
343 return data;
344 }
345
346 public Object visit(ASTUnaryExpression node, Object data) {
347 super.visit(node, data);
348 rollupTypeUnaryNumericPromotion(node);
349 return data;
350 }
351
352 public Object visit(ASTPreIncrementExpression node, Object data) {
353 super.visit(node, data);
354 rollupTypeUnary(node);
355 return data;
356 }
357
358 public Object visit(ASTPreDecrementExpression node, Object data) {
359 super.visit(node, data);
360 rollupTypeUnary(node);
361 return data;
362 }
363
364 public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) {
365 super.visit(node, data);
366 if ("!".equals(node.getImage())) {
367 populateType(node, "boolean");
368 } else {
369 rollupTypeUnary(node);
370 }
371 return data;
372 }
373
374 public Object visit(ASTPostfixExpression node, Object data) {
375 super.visit(node, data);
376 rollupTypeUnary(node);
377 return data;
378 }
379
380 public Object visit(ASTCastExpression node, Object data) {
381 super.visit(node, data);
382 rollupTypeUnary(node);
383 return data;
384 }
385
386 public Object visit(ASTPrimaryExpression node, Object data) {
387 super.visit(node, data);
388 if (node.jjtGetNumChildren() == 1) {
389 rollupTypeUnary(node);
390 } else {
391
392 }
393 return data;
394 }
395
396 public Object visit(ASTPrimaryPrefix node, Object data) {
397 super.visit(node, data);
398 if (node.getImage() == null) {
399 rollupTypeUnary(node);
400 } else {
401
402 }
403 return data;
404 }
405
406 public Object visit(ASTPrimarySuffix node, Object data) {
407 super.visit(node, data);
408
409 return data;
410 }
411
412 public Object visit(ASTNullLiteral node, Object data) {
413
414 return super.visit(node, data);
415 }
416
417 public Object visit(ASTBooleanLiteral node, Object data) {
418 populateType(node, "boolean");
419 return super.visit(node, data);
420 }
421
422 public Object visit(ASTLiteral node, Object data) {
423 super.visit(node, data);
424 if (node.jjtGetNumChildren() != 0) {
425 rollupTypeUnary(node);
426 } else {
427 if (node.isIntLiteral()) {
428 String image = node.getImage();
429 if (image.endsWith("l") || image.endsWith("L")) {
430 populateType(node, "long");
431 } else {
432 try {
433 Integer.decode(image);
434 populateType(node, "int");
435 } catch (NumberFormatException e) {
436
437 }
438 }
439 } else if (node.isFloatLiteral()) {
440 String image = node.getImage();
441 if (image.endsWith("f") || image.endsWith("F")) {
442 populateType(node, "float");
443 } else if (image.endsWith("d") || image.endsWith("D")) {
444 populateType(node, "double");
445 } else {
446 try {
447 Double.parseDouble(image);
448 populateType(node, "double");
449 } catch (NumberFormatException e) {
450
451 }
452 }
453 } else if (node.isCharLiteral()) {
454 populateType(node, "char");
455 } else if (node.isStringLiteral()) {
456 populateType(node, "java.lang.String");
457 } else {
458 throw new IllegalStateException("PMD error, unknown literal type!");
459 }
460 }
461 return data;
462 }
463
464 public Object visit(ASTAllocationExpression node, Object data) {
465 super.visit(node, data);
466
467 if ((node.jjtGetNumChildren() >= 2 && node.jjtGetChild(1) instanceof ASTArrayDimsAndInits)
468 || (node.jjtGetNumChildren() >= 3 && node.jjtGetChild(2) instanceof ASTArrayDimsAndInits)) {
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487 } else {
488 rollupTypeUnary(node);
489 }
490 return data;
491 }
492
493 public Object visit(ASTStatementExpression node, Object data) {
494 super.visit(node, data);
495 rollupTypeUnary(node);
496 return data;
497 }
498
499
500 private void rollupTypeUnary(TypeNode typeNode) {
501 if (typeNode instanceof SimpleNode) {
502 SimpleNode simpleNode = (SimpleNode)typeNode;
503 if (simpleNode.jjtGetNumChildren() >= 1) {
504 Node child = simpleNode.jjtGetChild(0);
505 if (child instanceof TypeNode) {
506 typeNode.setType(((TypeNode)child).getType());
507 }
508 }
509 }
510 }
511
512
513 private void rollupTypeUnaryNumericPromotion(TypeNode typeNode) {
514 if (typeNode instanceof SimpleNode) {
515 SimpleNode simpleNode = (SimpleNode)typeNode;
516 if (simpleNode.jjtGetNumChildren() >= 1) {
517 Node child = simpleNode.jjtGetChild(0);
518 if (child instanceof TypeNode) {
519 Class<?> type = ((TypeNode)child).getType();
520 if (type != null) {
521 if ("byte".equals(type.getName()) || "short".equals(type.getName())
522 || "char".equals(type.getName())) {
523 populateType(typeNode, "int");
524 } else {
525 typeNode.setType(((TypeNode)child).getType());
526 }
527 }
528 }
529 }
530 }
531 }
532
533
534 private void rollupTypeBinaryNumericPromotion(TypeNode typeNode) {
535 if (typeNode instanceof SimpleNode) {
536 SimpleNode simpleNode = (SimpleNode)typeNode;
537 if (simpleNode.jjtGetNumChildren() >= 2) {
538 Node child1 = simpleNode.jjtGetChild(0);
539 Node child2 = simpleNode.jjtGetChild(1);
540 if (child1 instanceof TypeNode && child2 instanceof TypeNode) {
541 Class<?> type1 = ((TypeNode)child1).getType();
542 Class<?> type2 = ((TypeNode)child2).getType();
543 if (type1 != null && type2 != null) {
544
545 if ("java.lang.String".equals(type1.getName()) || "java.lang.String".equals(type2.getName())) {
546 populateType(typeNode, "java.lang.String");
547 } else if ("boolean".equals(type1.getName()) || "boolean".equals(type2.getName())) {
548 populateType(typeNode, "boolean");
549 } else if ("double".equals(type1.getName()) || "double".equals(type2.getName())) {
550 populateType(typeNode, "double");
551 } else if ("float".equals(type1.getName()) || "float".equals(type2.getName())) {
552 populateType(typeNode, "float");
553 } else if ("long".equals(type1.getName()) || "long".equals(type2.getName())) {
554 populateType(typeNode, "long");
555 } else {
556 populateType(typeNode, "int");
557 }
558 } else if (type1 != null || type2 != null) {
559
560
561 if ((type1 != null && "java.lang.String".equals(type1.getName()))
562 || (type2 != null && "java.lang.String".equals(type2.getName()))) {
563 populateType(typeNode, "java.lang.String");
564 }
565 }
566 }
567 }
568 }
569 }
570
571 private void populateType(TypeNode node, String className) {
572
573 String qualifiedName = className;
574 Class<?> myType = myPrimitiveTypes.get(className);
575 if (myType == null && importedClasses != null) {
576 if (importedClasses.containsKey(className)) {
577 qualifiedName = importedClasses.get(className);
578 } else if (importedClasses.containsValue(className)) {
579 qualifiedName = className;
580 }
581 if (qualifiedName != null) {
582 try {
583
584
585
586
587
588 myType = pmdClassLoader.loadClass(qualifiedName);
589 } catch (ClassNotFoundException e) {
590 myType = processOnDemand(qualifiedName);
591 } catch (NoClassDefFoundError e) {
592 myType = processOnDemand(qualifiedName);
593 } catch (ClassFormatError e) {
594 myType = processOnDemand(qualifiedName);
595 }
596 }
597 }
598 if (myType != null) {
599 node.setType(myType);
600 }
601 }
602
603 /**
604 * Check whether the supplied class name exists.
605 */
606 public boolean classNameExists(String fullyQualifiedClassName) {
607 try {
608 pmdClassLoader.loadClass(fullyQualifiedClassName);
609 return true;
610 } catch (ClassNotFoundException e) {
611 return false;
612 }
613 }
614
615 private Class<?> processOnDemand(String qualifiedName) {
616 for (String entry : importedOnDemand) {
617 try {
618 return pmdClassLoader.loadClass(entry + "." + qualifiedName);
619 } catch (Throwable e) {
620 }
621 }
622 return null;
623 }
624
625 private String getClassName(ASTCompilationUnit node) {
626 ASTClassOrInterfaceDeclaration classDecl = node.getFirstChildOfType(ASTClassOrInterfaceDeclaration.class);
627 if (classDecl == null) {
628 return null;
629 }
630 if (node.declarationsAreInDefaultPackage()) {
631 return classDecl.getImage();
632 }
633 ASTPackageDeclaration pkgDecl = node.getPackageDeclaration();
634 importedOnDemand.add(pkgDecl.getPackageNameImage());
635 return pkgDecl.getPackageNameImage() + "." + classDecl.getImage();
636 }
637
638 /**
639 * If the outer class wasn't found then we'll get in here
640 *
641 * @param node
642 */
643 private void populateImports(ASTCompilationUnit node) {
644 List<ASTImportDeclaration> theImportDeclarations = node.findChildrenOfType(ASTImportDeclaration.class);
645 importedClasses = new HashMap<String, String>();
646
647
648 for (ASTImportDeclaration anImportDeclaration : theImportDeclarations) {
649 String strPackage = anImportDeclaration.getPackageName();
650 if (anImportDeclaration.isImportOnDemand()) {
651 importedOnDemand.add(strPackage);
652 } else if (!anImportDeclaration.isImportOnDemand()) {
653 String strName = anImportDeclaration.getImportedName();
654 importedClasses.put(strName, strName);
655 importedClasses.put(strName.substring(strPackage.length() + 1), strName);
656 }
657 }
658
659 importedClasses.putAll(myJavaLang);
660 }
661
662 private void populateClassName(ASTCompilationUnit node, String className) throws ClassNotFoundException {
663 node.setType(pmdClassLoader.loadClass(className));
664 importedClasses = pmdClassLoader.getImportedClasses(className);
665 }
666
667 }