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