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 }