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