View Javadoc

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