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 }