View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
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.AbstractNode;
15  import net.sourceforge.pmd.lang.ast.Node;
16  import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression;
17  import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression;
18  import net.sourceforge.pmd.lang.java.ast.ASTAndExpression;
19  import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
20  import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits;
21  import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral;
22  import net.sourceforge.pmd.lang.java.ast.ASTCastExpression;
23  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
24  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
25  import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
26  import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit;
27  import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression;
28  import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression;
29  import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression;
30  import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
31  import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression;
32  import net.sourceforge.pmd.lang.java.ast.ASTExclusiveOrExpression;
33  import net.sourceforge.pmd.lang.java.ast.ASTExpression;
34  import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration;
35  import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
36  import net.sourceforge.pmd.lang.java.ast.ASTInclusiveOrExpression;
37  import net.sourceforge.pmd.lang.java.ast.ASTInstanceOfExpression;
38  import net.sourceforge.pmd.lang.java.ast.ASTLiteral;
39  import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
40  import net.sourceforge.pmd.lang.java.ast.ASTMultiplicativeExpression;
41  import net.sourceforge.pmd.lang.java.ast.ASTName;
42  import net.sourceforge.pmd.lang.java.ast.ASTNormalAnnotation;
43  import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral;
44  import net.sourceforge.pmd.lang.java.ast.ASTPackageDeclaration;
45  import net.sourceforge.pmd.lang.java.ast.ASTPostfixExpression;
46  import net.sourceforge.pmd.lang.java.ast.ASTPreDecrementExpression;
47  import net.sourceforge.pmd.lang.java.ast.ASTPreIncrementExpression;
48  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression;
49  import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix;
50  import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix;
51  import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType;
52  import net.sourceforge.pmd.lang.java.ast.ASTReferenceType;
53  import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression;
54  import net.sourceforge.pmd.lang.java.ast.ASTShiftExpression;
55  import net.sourceforge.pmd.lang.java.ast.ASTSingleMemberAnnotation;
56  import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression;
57  import net.sourceforge.pmd.lang.java.ast.ASTType;
58  import net.sourceforge.pmd.lang.java.ast.ASTTypeDeclaration;
59  import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression;
60  import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus;
61  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator;
62  import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId;
63  import net.sourceforge.pmd.lang.java.ast.JavaParserVisitorAdapter;
64  import net.sourceforge.pmd.lang.java.ast.TypeNode;
65  
66  //
67  // Helpful reading:
68  // http://www.janeg.ca/scjp/oper/promotions.html
69  // http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html
70  //
71  
72  public class ClassTypeResolver extends JavaParserVisitorAdapter {
73  
74  	private static final Logger LOG = Logger.getLogger(ClassTypeResolver.class.getName());
75  
76  	private static final Map<String, Class<?>> PRIMITIVE_TYPES;
77  	private static final Map<String, String> JAVA_LANG;
78  
79  	static {
80  		// Note: Assumption here that primitives come from same parent ClassLoader regardless of what ClassLoader we are passed
81  		Map<String, Class<?>> thePrimitiveTypes = new HashMap<String, Class<?>>();
82  		thePrimitiveTypes.put("void", Void.TYPE);
83  		thePrimitiveTypes.put("boolean", Boolean.TYPE);
84  		thePrimitiveTypes.put("byte", Byte.TYPE);
85  		thePrimitiveTypes.put("char", Character.TYPE);
86  		thePrimitiveTypes.put("short", Short.TYPE);
87  		thePrimitiveTypes.put("int", Integer.TYPE);
88  		thePrimitiveTypes.put("long", Long.TYPE);
89  		thePrimitiveTypes.put("float", Float.TYPE);
90  		thePrimitiveTypes.put("double", Double.TYPE);
91  		PRIMITIVE_TYPES = Collections.unmodifiableMap(thePrimitiveTypes);
92  
93  		Map<String, String> theJavaLang = new HashMap<String, String>();
94  		theJavaLang.put("Boolean", "java.lang.Boolean");
95  		theJavaLang.put("Byte", "java.lang.Byte");
96  		theJavaLang.put("Character", "java.lang.Character");
97  		theJavaLang.put("CharSequence", "java.lang.CharSequence");
98  		theJavaLang.put("Class", "java.lang.Class");
99  		theJavaLang.put("ClassLoader", "java.lang.ClassLoader");
100 		theJavaLang.put("Cloneable", "java.lang.Cloneable");
101 		theJavaLang.put("Comparable", "java.lang.Comparable");
102 		theJavaLang.put("Compiler", "java.lang.Compiler");
103 		theJavaLang.put("Double", "java.lang.Double");
104 		theJavaLang.put("Float", "java.lang.Float");
105 		theJavaLang.put("InheritableThreadLocal", "java.lang.InheritableThreadLocal");
106 		theJavaLang.put("Integer", "java.lang.Integer");
107 		theJavaLang.put("Long", "java.lang.Long");
108 		theJavaLang.put("Math", "java.lang.Math");
109 		theJavaLang.put("Number", "java.lang.Number");
110 		theJavaLang.put("Object", "java.lang.Object");
111 		theJavaLang.put("Package", "java.lang.Package");
112 		theJavaLang.put("Process", "java.lang.Process");
113 		theJavaLang.put("Runnable", "java.lang.Runnable");
114 		theJavaLang.put("Runtime", "java.lang.Runtime");
115 		theJavaLang.put("RuntimePermission", "java.lang.RuntimePermission");
116 		theJavaLang.put("SecurityManager", "java.lang.SecurityManager");
117 		theJavaLang.put("Short", "java.lang.Short");
118 		theJavaLang.put("StackTraceElement", "java.lang.StackTraceElement");
119 		theJavaLang.put("StrictMath", "java.lang.StrictMath");
120 		theJavaLang.put("String", "java.lang.String");
121 		theJavaLang.put("StringBuffer", "java.lang.StringBuffer");
122 		theJavaLang.put("System", "java.lang.System");
123 		theJavaLang.put("Thread", "java.lang.Thread");
124 		theJavaLang.put("ThreadGroup", "java.lang.ThreadGroup");
125 		theJavaLang.put("ThreadLocal", "java.lang.ThreadLocal");
126 		theJavaLang.put("Throwable", "java.lang.Throwable");
127 		theJavaLang.put("Void", "java.lang.Void");
128 		JAVA_LANG = Collections.unmodifiableMap(theJavaLang);
129 	}
130 
131 	private final PMDASMClassLoader pmdClassLoader;
132 	private Map<String, String> importedClasses;
133 	private List<String> importedOnDemand;
134 	private int anonymousClassCounter = 0;
135 	
136 	public ClassTypeResolver() {
137 		this(ClassTypeResolver.class.getClassLoader());
138 	}
139 
140 	public ClassTypeResolver(ClassLoader classLoader) {
141 		pmdClassLoader = PMDASMClassLoader.getInstance(classLoader);
142 	}
143 
144 	// FUTURE ASTCompilationUnit should not be a TypeNode.  Clean this up accordingly.
145 	@Override
146 	public Object visit(ASTCompilationUnit node, Object data) {
147 		String className = null;
148 		try {
149 			importedOnDemand = new ArrayList<String>();
150 			importedClasses = new HashMap<String, String>();
151 			className = getClassName(node);
152 			if (className != null) {
153 				populateClassName(node, className);
154 			}
155 		} catch (ClassNotFoundException e) {
156 			LOG.log(Level.FINE, "Could not find class " + className + ", due to: " + e.getClass().getName() + ": " + e.getMessage());
157 		} catch (LinkageError e) {
158 			LOG.log(Level.WARNING, "Could not find class " + className + ", due to: " + e.getClass().getName() + ": " + e.getMessage());
159 		} finally {
160 			populateImports(node);
161 		}
162 		return super.visit(node, data);
163 	}
164 
165 	@Override
166 	public Object visit(ASTImportDeclaration node, Object data) {
167 		ASTName importedType = (ASTName)node.jjtGetChild(0);
168 		if (importedType.getType() != null) {
169 			node.setType(importedType.getType());
170 		} else {
171 			populateType(node, importedType.getImage());
172 		}
173 
174 		if (node.getType() != null) {
175 			node.setPackage(node.getType().getPackage());
176 		}
177 		return data;
178 	}
179 
180 	@Override
181 	public Object visit(ASTTypeDeclaration node, Object data) {
182 		super.visit(node, data);
183 		rollupTypeUnary(node);
184 		return data;
185 	}
186 
187 	@Override
188 	public Object visit(ASTClassOrInterfaceType node, Object data) {
189 		String typeName = node.getImage();
190 		if (node.jjtGetParent().hasDescendantOfType(ASTClassOrInterfaceBody.class)) {
191 			anonymousClassCounter++;
192 		    AbstractNode parent = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
193 		    if (parent == null) {
194 		        parent = node.getFirstParentOfType(ASTEnumDeclaration.class);
195 		    }
196             typeName = parent.getImage() + "$" + anonymousClassCounter;
197 		}
198 		populateType(node, typeName);
199 		return data;
200 	}
201 
202 	@Override
203 	public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
204 		populateType(node, node.getImage());
205 		return super.visit(node, data);
206 	}
207 
208 	@Override
209 	public Object visit(ASTEnumDeclaration node, Object data) {
210 		populateType(node, node.getImage());
211 		return super.visit(node, data);
212 	}
213 
214 	@Override
215 	public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
216 		populateType(node, node.getImage());
217 		return super.visit(node, data);
218 	}
219 
220 	@Override
221 	public Object visit(ASTName node, Object data) {
222 		/*
223 		 * Only doing this for nodes where getNameDeclaration is null this means
224 		 * it's not a named node, i.e. Static reference or Annotation Doing this
225 		 * for memory - TODO: Investigate if there is a valid memory concern or
226 		 * not
227 		 */
228 		if (node.getNameDeclaration() == null) {
229 			// Skip these scenarios as there is no type to populate in these cases:
230 			// 1) Parent is a PackageDeclaration, which is not a type
231 			// 2) Parent is a ImportDeclaration, this is handled elsewhere.
232 			if (!(node.jjtGetParent() instanceof ASTPackageDeclaration || node.jjtGetParent() instanceof ASTImportDeclaration)) {
233 				String name = node.getImage();
234 				if (name.indexOf('.') != -1) {
235 					name = name.substring(0, name.indexOf('.'));
236 				}
237 				populateType(node, name);
238 			}
239 		} else {
240 			// Carry over the type from the declaration
241 			if (node.getNameDeclaration().getNode() instanceof TypeNode) {
242 				node.setType(((TypeNode)node.getNameDeclaration().getNode()).getType());
243 			}
244 		}
245 		return super.visit(node, data);
246 	}
247 
248 	@Override
249 	public Object visit(ASTFieldDeclaration node, Object data) {
250 		super.visit(node, data);
251 		rollupTypeUnary(node);
252 		return data;
253 	}
254 
255 	@Override
256 	public Object visit(ASTVariableDeclarator node, Object data) {
257 		super.visit(node, data);
258 		rollupTypeUnary(node);
259 		return data;
260 	}
261 
262 	@Override
263 	public Object visit(ASTVariableDeclaratorId node, Object data) {
264 		if (node == null || node.getNameDeclaration() == null) {
265 			return super.visit(node, data);
266 		}
267 		String name = node.getNameDeclaration().getTypeImage();
268 		if (name != null) {
269     		if (name.indexOf('.') != -1) {
270     			name = name.substring(0, name.indexOf('.'));
271     		}
272     		populateType(node, name);
273 		}
274 		return super.visit(node, data);
275 	}
276 
277 	@Override
278 	public Object visit(ASTType node, Object data) {
279 		super.visit(node, data);
280 		rollupTypeUnary(node);
281 		return data;
282 	}
283 
284 	@Override
285 	public Object visit(ASTReferenceType node, Object data) {
286 		super.visit(node, data);
287 		rollupTypeUnary(node);
288 		return data;
289 	}
290 
291 	@Override
292 	public Object visit(ASTPrimitiveType node, Object data) {
293 		populateType(node, node.getImage());
294 		return super.visit(node, data);
295 	}
296 
297 	@Override
298 	public Object visit(ASTExpression node, Object data) {
299 		super.visit(node, data);
300 		rollupTypeUnary(node);
301 		return data;
302 	}
303 
304 	@Override
305 	public Object visit(ASTConditionalExpression node, Object data) {
306 		super.visit(node, data);
307 		if (node.isTernary()) {
308 			// TODO Rules for Ternary are complex
309 		} else {
310 			rollupTypeUnary(node);
311 		}
312 		return data;
313 	}
314 
315 	@Override
316 	public Object visit(ASTConditionalOrExpression node, Object data) {
317 		populateType(node, "boolean");
318 		return super.visit(node, data);
319 	}
320 
321 	@Override
322 	public Object visit(ASTConditionalAndExpression node, Object data) {
323 		populateType(node, "boolean");
324 		return super.visit(node, data);
325 	}
326 
327 	@Override
328 	public Object visit(ASTInclusiveOrExpression node, Object data) {
329 		super.visit(node, data);
330 		rollupTypeBinaryNumericPromotion(node);
331 		return data;
332 	}
333 
334 	@Override
335 	public Object visit(ASTExclusiveOrExpression node, Object data) {
336 		super.visit(node, data);
337 		rollupTypeBinaryNumericPromotion(node);
338 		return data;
339 	}
340 
341 	@Override
342 	public Object visit(ASTAndExpression node, Object data) {
343 		super.visit(node, data);
344 		rollupTypeBinaryNumericPromotion(node);
345 		return data;
346 	}
347 
348 	@Override
349 	public Object visit(ASTEqualityExpression node, Object data) {
350 		populateType(node, "boolean");
351 		return super.visit(node, data);
352 	}
353 
354 	@Override
355 	public Object visit(ASTInstanceOfExpression node, Object data) {
356 		populateType(node, "boolean");
357 		return super.visit(node, data);
358 	}
359 
360 	@Override
361 	public Object visit(ASTRelationalExpression node, Object data) {
362 		populateType(node, "boolean");
363 		return super.visit(node, data);
364 	}
365 
366 	@Override
367 	public Object visit(ASTShiftExpression node, Object data) {
368 		super.visit(node, data);
369 		// Unary promotion on LHS is type of a shift operation
370 		rollupTypeUnaryNumericPromotion(node);
371 		return data;
372 	}
373 
374 	@Override
375 	public Object visit(ASTAdditiveExpression node, Object data) {
376 		super.visit(node, data);
377 		rollupTypeBinaryNumericPromotion(node);
378 		return data;
379 	}
380 
381 	@Override
382 	public Object visit(ASTMultiplicativeExpression node, Object data) {
383 		super.visit(node, data);
384 		rollupTypeBinaryNumericPromotion(node);
385 		return data;
386 	}
387 
388 	@Override
389 	public Object visit(ASTUnaryExpression node, Object data) {
390 		super.visit(node, data);
391 		rollupTypeUnaryNumericPromotion(node);
392 		return data;
393 	}
394 
395 	@Override
396 	public Object visit(ASTPreIncrementExpression node, Object data) {
397 		super.visit(node, data);
398 		rollupTypeUnary(node);
399 		return data;
400 	}
401 
402 	@Override
403 	public Object visit(ASTPreDecrementExpression node, Object data) {
404 		super.visit(node, data);
405 		rollupTypeUnary(node);
406 		return data;
407 	}
408 
409 	@Override
410 	public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) {
411 		super.visit(node, data);
412 		if ("!".equals(node.getImage())) {
413 			populateType(node, "boolean");
414 		} else {
415 			rollupTypeUnary(node);
416 		}
417 		return data;
418 	}
419 
420 	@Override
421 	public Object visit(ASTPostfixExpression node, Object data) {
422 		super.visit(node, data);
423 		rollupTypeUnary(node);
424 		return data;
425 	}
426 
427 	@Override
428 	public Object visit(ASTCastExpression node, Object data) {
429 		super.visit(node, data);
430 		rollupTypeUnary(node);
431 		return data;
432 	}
433 
434 	@Override
435 	public Object visit(ASTPrimaryExpression node, Object data) {
436 		super.visit(node, data);
437 		if (node.jjtGetNumChildren() == 1) {
438 			rollupTypeUnary(node);
439 		} else {
440 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
441 		}
442 		return data;
443 	}
444 
445 	@Override
446 	public Object visit(ASTPrimaryPrefix node, Object data) {
447 		super.visit(node, data);
448 		if (node.getImage() == null) {
449 			rollupTypeUnary(node);
450 		} else {
451 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
452 		}
453 		return data;
454 	}
455 
456 	@Override
457 	public Object visit(ASTPrimarySuffix node, Object data) {
458 		super.visit(node, data);
459 		// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
460 		return data;
461 	}
462 
463 	@Override
464 	public Object visit(ASTNullLiteral node, Object data) {
465 		// No explicit type
466 		return super.visit(node, data);
467 	}
468 
469 	@Override
470 	public Object visit(ASTBooleanLiteral node, Object data) {
471 		populateType(node, "boolean");
472 		return super.visit(node, data);
473 	}
474 
475 	@Override
476 	public Object visit(ASTLiteral node, Object data) {
477 		super.visit(node, data);
478 		if (node.jjtGetNumChildren() != 0) {
479 			rollupTypeUnary(node);
480 		} else {
481 			if (node.isIntLiteral()) {
482 				String image = node.getImage();
483 				if (image.endsWith("l") || image.endsWith("L")) {
484 					populateType(node, "long");
485 				} else {
486 					try {
487 						Integer.decode(image);
488 						populateType(node, "int");
489 					} catch (NumberFormatException e) {
490 						// Bad literal, 'long' requires use of 'l' or 'L' suffix.
491 					}
492 				}
493 			} else if (node.isFloatLiteral()) {
494 				String image = node.getImage();
495 				if (image.endsWith("f") || image.endsWith("F")) {
496 					populateType(node, "float");
497 				} else if (image.endsWith("d") || image.endsWith("D")) {
498 					populateType(node, "double");
499 				} else {
500 					try {
501 						Double.parseDouble(image);
502 						populateType(node, "double");
503 					} catch (NumberFormatException e) {
504 						// Bad literal, 'float' requires use of 'f' or 'F' suffix.
505 					}
506 				}
507 			} else if (node.isCharLiteral()) {
508 				populateType(node, "char");
509 			} else if (node.isStringLiteral()) {
510 				populateType(node, "java.lang.String");
511 			} else {
512 				throw new IllegalStateException("PMD error, unknown literal type!");
513 			}
514 		}
515 		return data;
516 	}
517 
518 	@Override
519 	public Object visit(ASTAllocationExpression node, Object data) {
520 		super.visit(node, data);
521 
522 		if (node.jjtGetNumChildren() >= 2 && node.jjtGetChild(1) instanceof ASTArrayDimsAndInits
523 				|| node.jjtGetNumChildren() >= 3 && node.jjtGetChild(2) instanceof ASTArrayDimsAndInits) {
524 			//
525 			// Classes for Array types cannot be found directly using reflection.
526 			// As far as I can tell you have to create an array instance of the necessary
527 			// dimensionality, and then ask for the type from the instance.  OMFG that's ugly.
528 			//
529 
530 			// TODO Need to create utility method to allow array type creation which will use
531 			// caching to avoid repeated object creation.
532 			// TODO Modify Parser to tell us array dimensions count.
533 			// TODO Parser seems to do some work to handle arrays in certain case already.
534 			// Examine those to figure out what's going on, make sure _all_ array scenarios
535 			// are ultimately covered.  Appears to use a Dimensionable interface to handle
536 			// only a part of the APIs (not bump), but is implemented several times, so
537 			// look at refactoring to eliminate duplication.  Dimensionable is also used
538 			// on AccessNodes for some scenarios, need to account for that.  Might be
539 			// missing some TypeNode candidates we can add to the AST and have to deal
540 			// with here (e.g. FormalParameter)?  Plus some existing usages may be
541 			// incorrect.
542 		} else {
543 			rollupTypeUnary(node);
544 		}
545 		return data;
546 	}
547 
548 	@Override
549 	public Object visit(ASTStatementExpression node, Object data) {
550 		super.visit(node, data);
551 		rollupTypeUnary(node);
552 		return data;
553 	}
554 
555     @Override
556     public Object visit(ASTNormalAnnotation node, Object data) {
557         super.visit(node, data);
558         rollupTypeUnary(node);
559         return data;
560     }
561 
562     @Override
563     public Object visit(ASTMarkerAnnotation node, Object data) {
564         super.visit(node, data);
565         rollupTypeUnary(node);
566         return data;
567     }
568 
569     @Override
570     public Object visit(ASTSingleMemberAnnotation node, Object data) {
571         super.visit(node, data);
572         rollupTypeUnary(node);
573         return data;
574     }
575 
576 	// Roll up the type based on type of the first child node.
577 	private void rollupTypeUnary(TypeNode typeNode) {
578 		if (typeNode instanceof Node) {
579 			Node node = (Node)typeNode;
580 			if (node.jjtGetNumChildren() >= 1) {
581 				Node child = node.jjtGetChild(0);
582 				if (child instanceof TypeNode) {
583 					typeNode.setType(((TypeNode)child).getType());
584 				}
585 			}
586 		}
587 	}
588 
589 	// Roll up the type based on type of the first child node using Unary Numeric Promotion per JLS 5.6.1
590 	private void rollupTypeUnaryNumericPromotion(TypeNode typeNode) {
591 		if (typeNode instanceof Node) {
592 			Node node = (Node)typeNode;
593 			if (node.jjtGetNumChildren() >= 1) {
594 				Node child = node.jjtGetChild(0);
595 				if (child instanceof TypeNode) {
596 					Class<?> type = ((TypeNode)child).getType();
597 					if (type != null) {
598 						if ("byte".equals(type.getName()) || "short".equals(type.getName())
599 								|| "char".equals(type.getName())) {
600 							populateType(typeNode, "int");
601 						} else {
602 							typeNode.setType(((TypeNode)child).getType());
603 						}
604 					}
605 				}
606 			}
607 		}
608 	}
609 
610 	// Roll up the type based on type of the first and second child nodes using Binary Numeric Promotion per JLS 5.6.2
611 	private void rollupTypeBinaryNumericPromotion(TypeNode typeNode) {
612 		if (typeNode instanceof Node) {
613 			Node node = (Node)typeNode;
614 			if (node.jjtGetNumChildren() >= 2) {
615 				Node child1 = node.jjtGetChild(0);
616 				Node child2 = node.jjtGetChild(1);
617 				if (child1 instanceof TypeNode && child2 instanceof TypeNode) {
618 					Class<?> type1 = ((TypeNode)child1).getType();
619 					Class<?> type2 = ((TypeNode)child2).getType();
620 					if (type1 != null && type2 != null) {
621 						// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
622 						if ("java.lang.String".equals(type1.getName()) || "java.lang.String".equals(type2.getName())) {
623 							populateType(typeNode, "java.lang.String");
624 						} else if ("boolean".equals(type1.getName()) || "boolean".equals(type2.getName())) {
625 							populateType(typeNode, "boolean");
626 						} else if ("double".equals(type1.getName()) || "double".equals(type2.getName())) {
627 							populateType(typeNode, "double");
628 						} else if ("float".equals(type1.getName()) || "float".equals(type2.getName())) {
629 							populateType(typeNode, "float");
630 						} else if ("long".equals(type1.getName()) || "long".equals(type2.getName())) {
631 							populateType(typeNode, "long");
632 						} else {
633 							populateType(typeNode, "int");
634 						}
635 					} else if (type1 != null || type2 != null) {
636 						// If one side is known to be a String, then the result is a String
637 						// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
638 						if (type1 != null && "java.lang.String".equals(type1.getName())
639 								|| type2 != null && "java.lang.String".equals(type2.getName())) {
640 							populateType(typeNode, "java.lang.String");
641 						}
642 					}
643 				}
644 			}
645 		}
646 	}
647 
648 	private void populateType(TypeNode node, String className) {
649 
650 		String qualifiedName = className;
651 		Class<?> myType = PRIMITIVE_TYPES.get(className);
652 		if (myType == null && importedClasses != null) {
653 			if (importedClasses.containsKey(className)) {
654 				qualifiedName = importedClasses.get(className);
655 			} else if (importedClasses.containsValue(className)) {
656 				qualifiedName = className;
657 			}
658 			if (qualifiedName != null) {
659 				try {
660 					/*
661 					 * TODO - the map right now contains just class names. if we
662 					 * use a map of classname/class then we don't have to hit
663 					 * the class loader for every type - much faster
664 					 */
665 					myType = pmdClassLoader.loadClass(qualifiedName);
666 				} catch (ClassNotFoundException e) {
667 					myType = processOnDemand(qualifiedName);
668 				} catch (LinkageError e) {
669 					myType = processOnDemand(qualifiedName);
670 				}
671 			}
672 		}
673 		if (myType == null && qualifiedName != null && !qualifiedName.contains(".")) {
674 		    // try again with java.lang....
675 		    try {
676 		        myType = pmdClassLoader.loadClass("java.lang." + qualifiedName);
677 		    } catch (Exception e) {
678 		        // ignored
679 		    }
680 		}
681 		if (myType != null) {
682 			node.setType(myType);
683 		}
684 	}
685 
686 	/**
687 	 * Check whether the supplied class name exists.
688 	 */
689 	public boolean classNameExists(String fullyQualifiedClassName) {
690 		try {
691 			pmdClassLoader.loadClass(fullyQualifiedClassName);
692 			return true; //Class found
693 		} catch (ClassNotFoundException e) {
694 			return false;
695 		}
696 	}
697 
698 	private Class<?> processOnDemand(String qualifiedName) {
699 		for (String entry : importedOnDemand) {
700 			try {
701 				return pmdClassLoader.loadClass(entry + "." + qualifiedName);
702 			} catch (Throwable e) {
703 			}
704 		}
705 		return null;
706 	}
707 
708 	private String getClassName(ASTCompilationUnit node) {
709 		ASTClassOrInterfaceDeclaration classDecl = node.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class);
710 		if (classDecl == null) {
711 			return null; // Happens if this compilation unit only contains an enum
712 		}
713 		if (node.declarationsAreInDefaultPackage()) {
714 			return classDecl.getImage();
715 		}
716 		ASTPackageDeclaration pkgDecl = node.getPackageDeclaration();
717 		importedOnDemand.add(pkgDecl.getPackageNameImage());
718 		return pkgDecl.getPackageNameImage() + "." + classDecl.getImage();
719 	}
720 
721 	/**
722 	 * If the outer class wasn't found then we'll get in here
723 	 *
724 	 * @param node
725 	 */
726 	private void populateImports(ASTCompilationUnit node) {
727 		List<ASTImportDeclaration> theImportDeclarations = node.findChildrenOfType(ASTImportDeclaration.class);
728 
729 		importedClasses.putAll(JAVA_LANG);
730 
731 		// go through the imports
732 		for (ASTImportDeclaration anImportDeclaration : theImportDeclarations) {
733 			String strPackage = anImportDeclaration.getPackageName();
734 			if (anImportDeclaration.isImportOnDemand()) {
735 				importedOnDemand.add(strPackage);
736 			} else if (!anImportDeclaration.isImportOnDemand()) {
737 				String strName = anImportDeclaration.getImportedName();
738 				importedClasses.put(strName, strName);
739 				importedClasses.put(strName.substring(strPackage.length() + 1), strName);
740 			}
741 		}
742 	}
743 
744 	private void populateClassName(ASTCompilationUnit node, String className) throws ClassNotFoundException {
745 		node.setType(pmdClassLoader.loadClass(className));
746 		importedClasses.putAll(pmdClassLoader.getImportedClasses(className));
747 	}
748 
749 }