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<>();
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<>();
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<>();
150 			importedClasses = new HashMap<>();
151 			className = getClassName(node);
152 			if (className != null) {
153 				populateClassName(node, className);
154 			}
155 		} catch (ClassNotFoundException e) {
156 			if (LOG.isLoggable(Level.FINE)) {
157 				LOG.log(Level.FINE, "Could not find class " + className + ", due to: " + e);
158 			}
159 		} catch (NoClassDefFoundError e) {
160 			if (LOG.isLoggable(Level.FINE)) {
161  				LOG.log(Level.FINE, "Could not find class " + className + ", due to: " + e);
162  			}
163 		} catch (LinkageError e) {
164 			if (LOG.isLoggable(Level.WARNING)) {
165 				LOG.log(Level.WARNING, "Could not find class " + className + ", due to: " + e);
166 			}
167 		} finally {
168 			populateImports(node);
169 		}
170 		return super.visit(node, data);
171 	}
172 
173 	@Override
174 	public Object visit(ASTImportDeclaration node, Object data) {
175 		ASTName importedType = (ASTName)node.jjtGetChild(0);
176 		if (importedType.getType() != null) {
177 			node.setType(importedType.getType());
178 		} else {
179 			populateType(node, importedType.getImage());
180 		}
181 
182 		if (node.getType() != null) {
183 			node.setPackage(node.getType().getPackage());
184 		}
185 		return data;
186 	}
187 
188 	@Override
189 	public Object visit(ASTTypeDeclaration node, Object data) {
190 		super.visit(node, data);
191 		rollupTypeUnary(node);
192 		return data;
193 	}
194 
195 	@Override
196 	public Object visit(ASTClassOrInterfaceType node, Object data) {
197 		String typeName = node.getImage();
198 		if (node.jjtGetParent().hasDescendantOfType(ASTClassOrInterfaceBody.class)) {
199 			anonymousClassCounter++;
200 		    AbstractNode parent = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class);
201 		    if (parent == null) {
202 		        parent = node.getFirstParentOfType(ASTEnumDeclaration.class);
203 		    }
204             typeName = parent.getImage() + "$" + anonymousClassCounter;
205 		}
206 		populateType(node, typeName);
207 		return data;
208 	}
209 
210 	@Override
211 	public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
212 		populateType(node, node.getImage());
213 		return super.visit(node, data);
214 	}
215 
216 	@Override
217 	public Object visit(ASTEnumDeclaration node, Object data) {
218 		populateType(node, node.getImage());
219 		return super.visit(node, data);
220 	}
221 
222 	@Override
223 	public Object visit(ASTAnnotationTypeDeclaration node, Object data) {
224 		populateType(node, node.getImage());
225 		return super.visit(node, data);
226 	}
227 
228 	@Override
229 	public Object visit(ASTName node, Object data) {
230 		/*
231 		 * Only doing this for nodes where getNameDeclaration is null this means
232 		 * it's not a named node, i.e. Static reference or Annotation Doing this
233 		 * for memory - TODO: Investigate if there is a valid memory concern or
234 		 * not
235 		 */
236 		if (node.getNameDeclaration() == null) {
237 			// Skip these scenarios as there is no type to populate in these cases:
238 			// 1) Parent is a PackageDeclaration, which is not a type
239 			// 2) Parent is a ImportDeclaration, this is handled elsewhere.
240 			if (!(node.jjtGetParent() instanceof ASTPackageDeclaration || node.jjtGetParent() instanceof ASTImportDeclaration)) {
241 				String name = node.getImage();
242 				if (name.indexOf('.') != -1) {
243 					name = name.substring(0, name.indexOf('.'));
244 				}
245 				populateType(node, name);
246 			}
247 		} else {
248 			// Carry over the type from the declaration
249 			if (node.getNameDeclaration().getNode() instanceof TypeNode) {
250 				node.setType(((TypeNode)node.getNameDeclaration().getNode()).getType());
251 			}
252 		}
253 		return super.visit(node, data);
254 	}
255 
256 	@Override
257 	public Object visit(ASTFieldDeclaration node, Object data) {
258 		super.visit(node, data);
259 		rollupTypeUnary(node);
260 		return data;
261 	}
262 
263 	@Override
264 	public Object visit(ASTVariableDeclarator node, Object data) {
265 		super.visit(node, data);
266 		rollupTypeUnary(node);
267 		return data;
268 	}
269 
270 	@Override
271 	public Object visit(ASTVariableDeclaratorId node, Object data) {
272 		if (node == null || node.getNameDeclaration() == null) {
273 			return super.visit(node, data);
274 		}
275 		String name = node.getNameDeclaration().getTypeImage();
276 		if (name != null) {
277     		if (name.indexOf('.') != -1) {
278     			name = name.substring(0, name.indexOf('.'));
279     		}
280     		populateType(node, name);
281 		}
282 		return super.visit(node, data);
283 	}
284 
285 	@Override
286 	public Object visit(ASTType node, Object data) {
287 		super.visit(node, data);
288 		rollupTypeUnary(node);
289 		return data;
290 	}
291 
292 	@Override
293 	public Object visit(ASTReferenceType node, Object data) {
294 		super.visit(node, data);
295 		rollupTypeUnary(node);
296 		return data;
297 	}
298 
299 	@Override
300 	public Object visit(ASTPrimitiveType node, Object data) {
301 		populateType(node, node.getImage());
302 		return super.visit(node, data);
303 	}
304 
305 	@Override
306 	public Object visit(ASTExpression node, Object data) {
307 		super.visit(node, data);
308 		rollupTypeUnary(node);
309 		return data;
310 	}
311 
312 	@Override
313 	public Object visit(ASTConditionalExpression node, Object data) {
314 		super.visit(node, data);
315 		if (node.isTernary()) {
316 			// TODO Rules for Ternary are complex
317 		} else {
318 			rollupTypeUnary(node);
319 		}
320 		return data;
321 	}
322 
323 	@Override
324 	public Object visit(ASTConditionalOrExpression node, Object data) {
325 		populateType(node, "boolean");
326 		return super.visit(node, data);
327 	}
328 
329 	@Override
330 	public Object visit(ASTConditionalAndExpression node, Object data) {
331 		populateType(node, "boolean");
332 		return super.visit(node, data);
333 	}
334 
335 	@Override
336 	public Object visit(ASTInclusiveOrExpression node, Object data) {
337 		super.visit(node, data);
338 		rollupTypeBinaryNumericPromotion(node);
339 		return data;
340 	}
341 
342 	@Override
343 	public Object visit(ASTExclusiveOrExpression node, Object data) {
344 		super.visit(node, data);
345 		rollupTypeBinaryNumericPromotion(node);
346 		return data;
347 	}
348 
349 	@Override
350 	public Object visit(ASTAndExpression node, Object data) {
351 		super.visit(node, data);
352 		rollupTypeBinaryNumericPromotion(node);
353 		return data;
354 	}
355 
356 	@Override
357 	public Object visit(ASTEqualityExpression node, Object data) {
358 		populateType(node, "boolean");
359 		return super.visit(node, data);
360 	}
361 
362 	@Override
363 	public Object visit(ASTInstanceOfExpression node, Object data) {
364 		populateType(node, "boolean");
365 		return super.visit(node, data);
366 	}
367 
368 	@Override
369 	public Object visit(ASTRelationalExpression node, Object data) {
370 		populateType(node, "boolean");
371 		return super.visit(node, data);
372 	}
373 
374 	@Override
375 	public Object visit(ASTShiftExpression node, Object data) {
376 		super.visit(node, data);
377 		// Unary promotion on LHS is type of a shift operation
378 		rollupTypeUnaryNumericPromotion(node);
379 		return data;
380 	}
381 
382 	@Override
383 	public Object visit(ASTAdditiveExpression node, Object data) {
384 		super.visit(node, data);
385 		rollupTypeBinaryNumericPromotion(node);
386 		return data;
387 	}
388 
389 	@Override
390 	public Object visit(ASTMultiplicativeExpression node, Object data) {
391 		super.visit(node, data);
392 		rollupTypeBinaryNumericPromotion(node);
393 		return data;
394 	}
395 
396 	@Override
397 	public Object visit(ASTUnaryExpression node, Object data) {
398 		super.visit(node, data);
399 		rollupTypeUnaryNumericPromotion(node);
400 		return data;
401 	}
402 
403 	@Override
404 	public Object visit(ASTPreIncrementExpression node, Object data) {
405 		super.visit(node, data);
406 		rollupTypeUnary(node);
407 		return data;
408 	}
409 
410 	@Override
411 	public Object visit(ASTPreDecrementExpression node, Object data) {
412 		super.visit(node, data);
413 		rollupTypeUnary(node);
414 		return data;
415 	}
416 
417 	@Override
418 	public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) {
419 		super.visit(node, data);
420 		if ("!".equals(node.getImage())) {
421 			populateType(node, "boolean");
422 		} else {
423 			rollupTypeUnary(node);
424 		}
425 		return data;
426 	}
427 
428 	@Override
429 	public Object visit(ASTPostfixExpression node, Object data) {
430 		super.visit(node, data);
431 		rollupTypeUnary(node);
432 		return data;
433 	}
434 
435 	@Override
436 	public Object visit(ASTCastExpression node, Object data) {
437 		super.visit(node, data);
438 		rollupTypeUnary(node);
439 		return data;
440 	}
441 
442 	@Override
443 	public Object visit(ASTPrimaryExpression node, Object data) {
444 		super.visit(node, data);
445 		if (node.jjtGetNumChildren() == 1) {
446 			rollupTypeUnary(node);
447 		} else {
448 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
449 		}
450 		return data;
451 	}
452 
453 	@Override
454 	public Object visit(ASTPrimaryPrefix node, Object data) {
455 		super.visit(node, data);
456 		if (node.getImage() == null) {
457 			rollupTypeUnary(node);
458 		} else {
459 			// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
460 		}
461 		return data;
462 	}
463 
464 	@Override
465 	public Object visit(ASTPrimarySuffix node, Object data) {
466 		super.visit(node, data);
467 		// TODO OMG, this is complicated.  PrimaryExpression, PrimaryPrefix and PrimarySuffix are all related.
468 		return data;
469 	}
470 
471 	@Override
472 	public Object visit(ASTNullLiteral node, Object data) {
473 		// No explicit type
474 		return super.visit(node, data);
475 	}
476 
477 	@Override
478 	public Object visit(ASTBooleanLiteral node, Object data) {
479 		populateType(node, "boolean");
480 		return super.visit(node, data);
481 	}
482 
483 	@Override
484 	public Object visit(ASTLiteral node, Object data) {
485 		super.visit(node, data);
486 		if (node.jjtGetNumChildren() != 0) {
487 			rollupTypeUnary(node);
488 		} else {
489 			if (node.isIntLiteral()) {
490 			    populateType(node, "int");
491 			} else if (node.isLongLiteral()) {
492 				populateType(node, "long");
493 			} else if (node.isFloatLiteral()) {
494 			    populateType(node, "float");
495 			} else if (node.isDoubleLiteral()) {
496 			    populateType(node, "double");
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     @Override
546     public Object visit(ASTNormalAnnotation node, Object data) {
547         super.visit(node, data);
548         rollupTypeUnary(node);
549         return data;
550     }
551 
552     @Override
553     public Object visit(ASTMarkerAnnotation node, Object data) {
554         super.visit(node, data);
555         rollupTypeUnary(node);
556         return data;
557     }
558 
559     @Override
560     public Object visit(ASTSingleMemberAnnotation node, Object data) {
561         super.visit(node, data);
562         rollupTypeUnary(node);
563         return data;
564     }
565 
566 	// Roll up the type based on type of the first child node.
567 	private void rollupTypeUnary(TypeNode typeNode) {
568 		Node node = typeNode;
569 		if (node.jjtGetNumChildren() >= 1) {
570 			Node child = node.jjtGetChild(0);
571 			if (child instanceof TypeNode) {
572 				typeNode.setType(((TypeNode)child).getType());
573 			}
574 		}
575 	}
576 
577 	// Roll up the type based on type of the first child node using Unary Numeric Promotion per JLS 5.6.1
578 	private void rollupTypeUnaryNumericPromotion(TypeNode typeNode) {
579 		Node node = typeNode;
580 		if (node.jjtGetNumChildren() >= 1) {
581 			Node child = node.jjtGetChild(0);
582 			if (child instanceof TypeNode) {
583 				Class<?> type = ((TypeNode)child).getType();
584 				if (type != null) {
585 					if ("byte".equals(type.getName()) || "short".equals(type.getName())
586 							|| "char".equals(type.getName())) {
587 						populateType(typeNode, "int");
588 					} else {
589 						typeNode.setType(((TypeNode)child).getType());
590 					}
591 				}
592 			}
593 		}
594 	}
595 
596 	// Roll up the type based on type of the first and second child nodes using Binary Numeric Promotion per JLS 5.6.2
597 	private void rollupTypeBinaryNumericPromotion(TypeNode typeNode) {
598 		Node node = typeNode;
599 		if (node.jjtGetNumChildren() >= 2) {
600 			Node child1 = node.jjtGetChild(0);
601 			Node child2 = node.jjtGetChild(1);
602 			if (child1 instanceof TypeNode && child2 instanceof TypeNode) {
603 				Class<?> type1 = ((TypeNode)child1).getType();
604 				Class<?> type2 = ((TypeNode)child2).getType();
605 				if (type1 != null && type2 != null) {
606 					// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
607 					if ("java.lang.String".equals(type1.getName()) || "java.lang.String".equals(type2.getName())) {
608 						populateType(typeNode, "java.lang.String");
609 					} else if ("boolean".equals(type1.getName()) || "boolean".equals(type2.getName())) {
610 						populateType(typeNode, "boolean");
611 					} else if ("double".equals(type1.getName()) || "double".equals(type2.getName())) {
612 						populateType(typeNode, "double");
613 					} else if ("float".equals(type1.getName()) || "float".equals(type2.getName())) {
614 						populateType(typeNode, "float");
615 					} else if ("long".equals(type1.getName()) || "long".equals(type2.getName())) {
616 						populateType(typeNode, "long");
617 					} else {
618 						populateType(typeNode, "int");
619 					}
620 				} else if (type1 != null || type2 != null) {
621 					// If one side is known to be a String, then the result is a String
622 					// Yeah, String is not numeric, but easiest place to handle it, only affects ASTAdditiveExpression
623 					if (type1 != null && "java.lang.String".equals(type1.getName())
624 							|| type2 != null && "java.lang.String".equals(type2.getName())) {
625 						populateType(typeNode, "java.lang.String");
626 					}
627 				}
628 			}
629 		}
630 	}
631 
632 	private void populateType(TypeNode node, String className) {
633 
634 		String qualifiedName = className;
635 		Class<?> myType = PRIMITIVE_TYPES.get(className);
636 		if (myType == null && importedClasses != null) {
637 			if (importedClasses.containsKey(className)) {
638 				qualifiedName = importedClasses.get(className);
639 			} else if (importedClasses.containsValue(className)) {
640 				qualifiedName = className;
641 			}
642 			if (qualifiedName != null) {
643 				try {
644 					/*
645 					 * TODO - the map right now contains just class names. if we
646 					 * use a map of classname/class then we don't have to hit
647 					 * the class loader for every type - much faster
648 					 */
649 					myType = pmdClassLoader.loadClass(qualifiedName);
650 				} catch (ClassNotFoundException e) {
651 					myType = processOnDemand(qualifiedName);
652 				} catch (NoClassDefFoundError e) {
653 					myType = processOnDemand(qualifiedName);
654 				} catch (LinkageError e) {
655 					myType = processOnDemand(qualifiedName);
656 				}
657 			}
658 		}
659 		if (myType == null && qualifiedName != null && qualifiedName.contains(".")) {
660 		    // try if the last part defines a inner class
661 		    String qualifiedNameInner = qualifiedName.substring(0, qualifiedName.lastIndexOf('.'))
662 		            + "$" + qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1);
663 		    try {
664 		        myType = pmdClassLoader.loadClass(qualifiedNameInner);
665 		    } catch (Exception e) {
666 		        // ignored
667 		    }
668 		}
669 		if (myType == null && qualifiedName != null && !qualifiedName.contains(".")) {
670 		    // try again with java.lang....
671 		    try {
672 		        myType = pmdClassLoader.loadClass("java.lang." + qualifiedName);
673 		    } catch (Exception e) {
674 		        // ignored
675 		    }
676 		}
677 		if (myType != null) {
678 			node.setType(myType);
679 		}
680 	}
681 
682 	/**
683 	 * Check whether the supplied class name exists.
684 	 */
685 	public boolean classNameExists(String fullyQualifiedClassName) {
686 		try {
687 			pmdClassLoader.loadClass(fullyQualifiedClassName);
688 			return true; //Class found
689 		} catch (ClassNotFoundException e) {
690 			return false;
691 		} catch (NoClassDefFoundError e) {
692 			return false;
693 		}
694 	}
695 
696 	public Class<?> loadClass(String fullyQualifiedClassName) {
697 	    try {
698             return pmdClassLoader.loadClass(fullyQualifiedClassName);
699         } catch (ClassNotFoundException e) {
700             return null;
701         }
702 	}
703 
704 	private Class<?> processOnDemand(String qualifiedName) {
705 		for (String entry : importedOnDemand) {
706 			try {
707 				return pmdClassLoader.loadClass(entry + "." + qualifiedName);
708 			} catch (Throwable e) {
709 			}
710 		}
711 		return null;
712 	}
713 
714 	private String getClassName(ASTCompilationUnit node) {
715 		ASTClassOrInterfaceDeclaration classDecl = node.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class);
716 		if (classDecl == null) {
717 			return null; // Happens if this compilation unit only contains an enum
718 		}
719 		if (node.declarationsAreInDefaultPackage()) {
720 			return classDecl.getImage();
721 		}
722 		ASTPackageDeclaration pkgDecl = node.getPackageDeclaration();
723 		importedOnDemand.add(pkgDecl.getPackageNameImage());
724 		return pkgDecl.getPackageNameImage() + "." + classDecl.getImage();
725 	}
726 
727 	/**
728 	 * If the outer class wasn't found then we'll get in here
729 	 *
730 	 * @param node
731 	 */
732 	private void populateImports(ASTCompilationUnit node) {
733 		List<ASTImportDeclaration> theImportDeclarations = node.findChildrenOfType(ASTImportDeclaration.class);
734 
735 		importedClasses.putAll(JAVA_LANG);
736 
737 		// go through the imports
738 		for (ASTImportDeclaration anImportDeclaration : theImportDeclarations) {
739 			String strPackage = anImportDeclaration.getPackageName();
740 			if (anImportDeclaration.isImportOnDemand()) {
741 				importedOnDemand.add(strPackage);
742 			} else if (!anImportDeclaration.isImportOnDemand()) {
743 				String strName = anImportDeclaration.getImportedName();
744 				importedClasses.put(strName, strName);
745 				importedClasses.put(strName.substring(strPackage.length() + 1), strName);
746 			}
747 		}
748 	}
749 
750 	private void populateClassName(ASTCompilationUnit node, String className) throws ClassNotFoundException {
751 		node.setType(pmdClassLoader.loadClass(className));
752 		importedClasses.putAll(pmdClassLoader.getImportedClasses(className));
753 	}
754 
755 }