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.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  // Helpful reading:
64  // http://www.janeg.ca/scjp/oper/promotions.html
65  // http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html
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  		// Note: Assumption here that primitives come from same parent ClassLoader regardless of what ClassLoader we are passed
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 	// FUTURE ASTCompilationUnit should not be a TypeNode.  Clean this up accordingly.
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 		 * Only doing this for nodes where getNameDeclaration is null this means
216 		 * it's not a named node, i.e. Static reference or Annotation Doing this
217 		 * for memory - TODO: Investigate if there is a valid memory concern or
218 		 * not
219 		 */
220 		if (node.getNameDeclaration() == null) {
221 			// Skip these scenarios as there is no type to populate in these cases:
222 			// 1) Parent is a PackageDeclaration, which is not a type
223 			// 2) Parent is a ImportDeclaration, this is handled elsewhere.
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 			// Carry over the type from the declaration
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 			// TODO Rules for Ternary are complex
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 		// Unary promotion on LHS is type of a shift operation
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 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
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 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
442 		}
443 		return data;
444 	}
445 
446 	@Override
447 	public Object visit(ASTPrimarySuffix node, Object data) {
448 		super.visit(node, data);
449 		// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
450 		return data;
451 	}
452 
453 	@Override
454 	public Object visit(ASTNullLiteral node, Object data) {
455 		// No explicit type
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 						// Bad literal, 'long' requires use of 'l' or 'L' suffix.
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 						// Bad literal, 'float' requires use of 'f' or 'F' suffix.
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 			// Classes for Array types cannot be found directly using reflection.
516 			// As far as I can tell you have to create an array instance of the necessary
517 			// dimensionality, and then ask for the type from the instance.  OMFG that's ugly.
518 			//
519 
520 			// TODO Need to create utility method to allow array type creation which will use
521 			// caching to avoid repeated object creation.
522 			// TODO Modify Parser to tell us array dimensions count.
523 			// TODO Parser seems to do some work to handle arrays in certain case already.
524 			// Examine those to figure out what's going on, make sure _all_ array scenarios
525 			// are ultimately covered.  Appears to use a Dimensionable interface to handle
526 			// only a part of the APIs (not bump), but is implemented several times, so
527 			// look at refactoring to eliminate duplication.  Dimensionable is also used
528 			// on AccessNodes for some scenarios, need to account for that.  Might be
529 			// missing some TypeNode candidates we can add to the AST and have to deal
530 			// with here (e.g. FormalParameter)?  Plus some existing usages may be
531 			// incorrect.
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 	// Roll up the type based on type of the first child node.
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 	// Roll up the type based on type of the first child node using Unary Numeric Promotion per JLS 5.6.1
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 	// Roll up the type based on type of the first and second child nodes using Binary Numeric Promotion per JLS 5.6.2
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 						// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
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 						// If one side is known to be a String, then the result is a String
606 						// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
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 					 * TODO - the map right now contains just class names. if we
631 					 * use a map of classname/class then we don't have to hit
632 					 * the class loader for every type - much faster
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 	 * Check whether the supplied class name exists.
649 	 */
650 	public boolean classNameExists(String fullyQualifiedClassName) {
651 		try {
652 			pmdClassLoader.loadClass(fullyQualifiedClassName);
653 			return true; //Class found
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; // Happens if this compilation unit only contains an enum
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 	 * If the outer class wasn't found then we'll get in here
684 	 *
685 	 * @param node
686 	 */
687 	private void populateImports(ASTCompilationUnit node) {
688 		List<ASTImportDeclaration> theImportDeclarations = node.findChildrenOfType(ASTImportDeclaration.class);
689 
690 		importedClasses.putAll(JAVA_LANG);
691 
692 		// go through the imports
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 }