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