View Javadoc

1   package org.springframework.roo.classpath.javaparser;
2   
3   import japa.parser.ast.ImportDeclaration;
4   import japa.parser.ast.TypeParameter;
5   import japa.parser.ast.body.ClassOrInterfaceDeclaration;
6   import japa.parser.ast.body.ModifierSet;
7   import japa.parser.ast.body.TypeDeclaration;
8   import japa.parser.ast.expr.AnnotationExpr;
9   import japa.parser.ast.expr.ClassExpr;
10  import japa.parser.ast.expr.Expression;
11  import japa.parser.ast.expr.FieldAccessExpr;
12  import japa.parser.ast.expr.MarkerAnnotationExpr;
13  import japa.parser.ast.expr.NameExpr;
14  import japa.parser.ast.expr.NormalAnnotationExpr;
15  import japa.parser.ast.expr.QualifiedNameExpr;
16  import japa.parser.ast.expr.SingleMemberAnnotationExpr;
17  import japa.parser.ast.type.ClassOrInterfaceType;
18  import japa.parser.ast.type.PrimitiveType;
19  import japa.parser.ast.type.ReferenceType;
20  import japa.parser.ast.type.Type;
21  import japa.parser.ast.type.VoidType;
22  import japa.parser.ast.type.WildcardType;
23  import japa.parser.ast.type.PrimitiveType.Primitive;
24  
25  import java.lang.reflect.Modifier;
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Set;
30  
31  import org.springframework.roo.model.DataType;
32  import org.springframework.roo.model.ImportRegistrationResolverImpl;
33  import org.springframework.roo.model.JavaPackage;
34  import org.springframework.roo.model.JavaSymbolName;
35  import org.springframework.roo.model.JavaType;
36  import org.springframework.roo.support.util.Assert;
37  
38  /**
39   * Assists with the usage of Java Parser.
40   * 
41   * <p>
42   * This class is for internal use by the Java Parser module and should NOT be used by other code.
43   * 
44   * @author Ben Alex
45   * @since 1.0
46   *
47   */
48  public class JavaParserUtils  {
49  	
50  	/**
51  	 * Converts the presented class name into a name expression (either a {@link NamedExpr} or
52  	 * {@link QualifiedNameExpr} depending on whether a package was presented).
53  	 * 
54  	 * @param className to convert (required; can be fully qualified or simple name only)
55  	 * @return a compatible expression (never returns null)
56  	 */
57  	public static final NameExpr getNameExpr(String className) {
58  		Assert.hasText(className, "Class name required");
59  		
60  		if (className.contains(".")) {
61  			int offset = className.lastIndexOf(".");
62  			String packageName = className.substring(0, offset);
63  			String typeName = className.substring(offset+1);
64  			return new QualifiedNameExpr(new NameExpr(packageName), typeName);
65  		}
66  		return new NameExpr(className);
67  	}
68  
69  	/**
70  	 * Converts a Java Parser modifier integer into a JDK {@link Modifier} integer.
71  	 * 
72  	 * @param modifiers the Java Parser int
73  	 * @return the equivalent JDK int
74  	 */
75  	public static final int getJdkModifier(int modifiers) {
76  		int result = 0;
77  		if (ModifierSet.isAbstract(modifiers)) {
78  			result = result |= Modifier.ABSTRACT;
79  		}
80  		if (ModifierSet.isFinal(modifiers)) {
81  			result = result |= Modifier.FINAL;
82  		}
83  		if (ModifierSet.isNative(modifiers)) {
84  			result = result |= Modifier.NATIVE;
85  		}
86  		if (ModifierSet.isPrivate(modifiers)) {
87  			result = result |= Modifier.PRIVATE;
88  		}
89  		if (ModifierSet.isProtected(modifiers)) {
90  			result = result |= Modifier.PROTECTED;
91  		}
92  		if (ModifierSet.isPublic(modifiers)) {
93  			result = result |= Modifier.PUBLIC;
94  		}
95  		if (ModifierSet.isStatic(modifiers)) {
96  			result = result |= Modifier.STATIC;
97  		}
98  		if (ModifierSet.isStrictfp(modifiers)) {
99  			result = result |= Modifier.STRICT;
100 		}
101 		if (ModifierSet.isSynchronized(modifiers)) {
102 			result = result |= Modifier.SYNCHRONIZED;
103 		}
104 		if (ModifierSet.isTransient(modifiers)) {
105 			result = result |= Modifier.TRANSIENT;
106 		}
107 		if (ModifierSet.isVolatile(modifiers)) {
108 			result = result |= Modifier.VOLATILE;
109 		}
110 		return result;
111 	}
112 	
113 	/**
114 	 * Converts a JDK {@link Modifier} integer into the equivalent Java Parser modifier.
115 	 * 
116 	 * @param modifiers the JDK int
117 	 * @return the equivalent Java Parser int
118 	 */
119 	public static final int getJavaParserModifier(int modifiers) {
120 		int result = 0;
121 		if (Modifier.isAbstract(modifiers)) {
122 			result = ModifierSet.addModifier(ModifierSet.ABSTRACT, result);
123 		}
124 		if (Modifier.isFinal(modifiers)) {
125 			result = ModifierSet.addModifier(ModifierSet.FINAL, result);
126 		}
127 		if (Modifier.isInterface(modifiers)) {
128 			// unsupported by Java Parser ModifierSet
129 		}
130 		if (Modifier.isNative(modifiers)) {
131 			result = ModifierSet.addModifier(ModifierSet.NATIVE, result);
132 		}
133 		if (Modifier.isPrivate(modifiers)) {
134 			result = ModifierSet.addModifier(ModifierSet.PRIVATE, result);
135 		}
136 		if (Modifier.isProtected(modifiers)) {
137 			result = ModifierSet.addModifier(ModifierSet.PROTECTED, result);
138 		}
139 		if (Modifier.isPublic(modifiers)) {
140 			result = ModifierSet.addModifier(ModifierSet.PUBLIC, result);
141 		}
142 		if (Modifier.isStatic(modifiers)) {
143 			result = ModifierSet.addModifier(ModifierSet.STATIC, result);
144 		}
145 		if (Modifier.isStrict(modifiers)) {
146 			result = ModifierSet.addModifier(ModifierSet.STRICTFP, result);
147 		}
148 		if (Modifier.isSynchronized(modifiers)) {
149 			result = ModifierSet.addModifier(ModifierSet.SYNCHRONIZED, result);
150 		}
151 		if (Modifier.isTransient(modifiers)) {
152 			result = ModifierSet.addModifier(ModifierSet.TRANSIENT, result);
153 		}
154 		if (Modifier.isVolatile(modifiers)) {
155 			result = ModifierSet.addModifier(ModifierSet.VOLATILE, result);
156 		}
157 		return result;
158 	}
159 
160 	/**
161 	 * Obtains the name expression ({@link NameExpr}) for the passed {@link AnnotationExpr}, 
162 	 * which is the annotation's type.
163 	 * 
164 	 * @param annotationExpr to retrieve the type name from (required)
165 	 * @return the name (never null)
166 	 */
167 	public static final NameExpr getNameExpr(AnnotationExpr annotationExpr) {
168 		Assert.notNull(annotationExpr, "Annotation expression required");
169 		if (annotationExpr instanceof MarkerAnnotationExpr) {
170 			MarkerAnnotationExpr a = (MarkerAnnotationExpr) annotationExpr;
171 			NameExpr nameToFind = a.getName();
172 			Assert.notNull(nameToFind, "Unable to determine annotation name from '" + annotationExpr + "'");
173 			return nameToFind;
174 		} else if (annotationExpr instanceof SingleMemberAnnotationExpr) {
175 			SingleMemberAnnotationExpr a = (SingleMemberAnnotationExpr) annotationExpr;
176 			NameExpr nameToFind = a.getName();
177 			Assert.notNull(nameToFind, "Unable to determine annotation name from '" + annotationExpr + "'");
178 			return nameToFind;
179 		} else if (annotationExpr instanceof NormalAnnotationExpr) {
180 			NormalAnnotationExpr a = (NormalAnnotationExpr) annotationExpr;
181 			NameExpr nameToFind = a.getName();
182 			Assert.notNull(nameToFind, "Unable to determine annotation name from '" + annotationExpr + "'");
183 			return nameToFind;
184 		}
185 		throw new UnsupportedOperationException("Unknown annotation expression type '" + annotationExpr.getClass().getName() + "'");
186 	}
187 
188 	/**
189 	 * Indicates whether two {@link NameExpr} expressions are equal.
190 	 * 
191 	 * <p>
192 	 * This method is necessary given {@link NameExpr} does not offer an equals method.
193 	 * 
194 	 * @param o1 the first entry to compare (null is acceptable)
195 	 * @param o2 the second entry to compare (null is acceptable)
196 	 * @return true if and only if both entries are identical
197 	 */
198 	private static final boolean isEqual(NameExpr o1, NameExpr o2) {
199 		if (o1 == null && o2 == null) {
200 			return true;
201 		}
202 		if (o1 == null && o2 != null) {
203 			return false;
204 		}
205 		if (o1 != null && o2 == null) {
206 			return false;
207 		}
208 		if (!o1.getName().equals(o2.getName())) {
209 			return false;
210 		}
211 		return o1.toString().equals(o2.toString());
212 	}
213 
214 	/**
215 	 * Resolves the effective {@link JavaType} a {@link Type} represents. A {@link Type} includes low-level
216 	 * types such as void, arrays and primitives.
217 	 * 
218 	 * @param compilationUnitServices to use for package resolution (required)
219 	 * @param type to locate (required)
220 	 * @param typeParameters names to consider type parameters (can be null if there are none)
221 	 * @return the {@link JavaType}, with proper indication of primitive and array status (never null)
222 	 */
223 	public static JavaType getJavaType(CompilationUnitServices compilationUnitServices, Type type, Set<JavaSymbolName> typeParameters) {
224 		Assert.notNull(compilationUnitServices, "Compilation unit services required");
225 		Assert.notNull(type, "The reference type must be provided");
226 		
227 		if (type instanceof VoidType) {
228 			return JavaType.VOID_PRIMITIVE;
229 		}
230 		
231 		int array = 0;
232 
233 		Type internalType = type;
234 		if (internalType instanceof ReferenceType) {
235 			array = ((ReferenceType)internalType).getArrayCount();
236 			if (array > 0) {
237 				internalType = ((ReferenceType)internalType).getType();
238 			}
239 		}
240 		
241 		if (internalType instanceof PrimitiveType) {
242 			PrimitiveType pt = (PrimitiveType) internalType;
243 			if (pt.getType().equals(Primitive.Boolean)) {
244 				return new JavaType(Boolean.class.getName(), array, DataType.PRIMITIVE, null, null);
245 			}
246 			if (pt.getType().equals(Primitive.Char)) {
247 				return new JavaType(Character.class.getName(), array, DataType.PRIMITIVE, null, null);
248 			}
249 			if (pt.getType().equals(Primitive.Byte)) {
250 				return new JavaType(Byte.class.getName(), array, DataType.PRIMITIVE, null, null);
251 			}
252 			if (pt.getType().equals(Primitive.Short)) {
253 				return new JavaType(Short.class.getName(), array, DataType.PRIMITIVE, null, null);
254 			}
255 			if (pt.getType().equals(Primitive.Int)) {
256 				return new JavaType(Integer.class.getName(), array, DataType.PRIMITIVE, null, null);
257 			}
258 			if (pt.getType().equals(Primitive.Long)) {
259 				return new JavaType(Long.class.getName(), array, DataType.PRIMITIVE, null, null);
260 			}
261 			if (pt.getType().equals(Primitive.Float)) {
262 				return new JavaType(Float.class.getName(), array, DataType.PRIMITIVE, null, null);
263 			}
264 			if (pt.getType().equals(Primitive.Double)) {
265 				return new JavaType(Double.class.getName(), array, DataType.PRIMITIVE, null, null);
266 			}
267 			throw new IllegalStateException("Unsupported primitive '" + pt.getType() + "'");
268 		}
269 		
270 		if (internalType instanceof WildcardType) {
271 			// We only provide very primitive support for wildcard types; Roo only needs metadata at the end of the day,
272 			// not complete binding support from an AST
273 			WildcardType wt = (WildcardType) internalType;
274 			if (wt.getSuper() != null) {
275 				ReferenceType rt = (ReferenceType) wt.getSuper();
276 				ClassOrInterfaceType cit = (ClassOrInterfaceType) rt.getType();
277 				JavaType effectiveType = getJavaTypeNow(compilationUnitServices, cit, typeParameters);
278 				return new JavaType(effectiveType.getFullyQualifiedTypeName(), rt.getArrayCount(), effectiveType.getDataType(), JavaType.WILDCARD_SUPER, effectiveType.getParameters());
279 			} else if (wt.getExtends() != null) {
280 				ReferenceType rt = (ReferenceType) wt.getExtends();
281 				ClassOrInterfaceType cit = (ClassOrInterfaceType) rt.getType();
282 				JavaType effectiveType = getJavaTypeNow(compilationUnitServices, cit, typeParameters);
283 				return new JavaType(effectiveType.getFullyQualifiedTypeName(), rt.getArrayCount(), effectiveType.getDataType(), JavaType.WILDCARD_EXTENDS, effectiveType.getParameters());
284 			} else {
285 				return new JavaType("java.lang.Object", 0, DataType.TYPE, JavaType.WILDCARD_NEITHER, null);
286 			}
287 		}
288 		
289 		ClassOrInterfaceType cit;
290 		if (internalType instanceof ClassOrInterfaceType) {
291 			cit = (ClassOrInterfaceType) internalType;
292 		} else if (internalType instanceof ReferenceType) {
293 			cit = (ClassOrInterfaceType) ((ReferenceType)type).getType();
294 		} else {
295 			throw new IllegalStateException("The presented type '" + internalType.getClass() + "' with value '" + internalType + "' is unsupported by JavaParserUtils");
296 		}
297 
298 		JavaType effectiveType = getJavaTypeNow(compilationUnitServices, cit, typeParameters);
299 		if (array > 0) {
300 			return new JavaType(effectiveType.getFullyQualifiedTypeName(), array, effectiveType.getDataType(), effectiveType.getArgName(), effectiveType.getParameters());
301 		}
302 		
303 		return effectiveType;
304 	}
305 	
306 	/**
307 	 * Resolves the effective {@link JavaType} a {@link NameExpr} represents.
308 	 * 
309 	 * <p>
310 	 * You should use {@link #getJavaType(JavaPackage, List, ClassOrInterfaceType)} where possible so that
311 	 * type arguments are preserved (a {@link NameExpr} does not contain type arguments).
312 	 * 
313 	 * <p>
314 	 * A name expression can be either qualified or unqualified. If qualified, that represents the fully-qualified name.
315 	 * If a name expression is unqualified, the imports are scanned. If the unqualified name expression is found in the
316 	 * imports, that import declaration represents the fully-qualified name. If the unqualified name expression is not
317 	 * found in the imports, it indicates the name to find is either in the same package as the qualified name expression,
318 	 * or the type relates to a member of java.lang. If part of java.lang, the
319 	 * fully qualified name is treated as part of java.lang. Otherwise the compilation unit package plus unqualified name
320 	 * expression represents the fully qualified name expression.
321 	 * 
322 	 * @param compilationUnitServices for package management (required)
323 	 * @param nameToFind to locate (required)
324 	 * @param typeParameters names to consider type parameters (can be null if there are none)
325 	 * @return the effective Java type (never null)
326 	 */
327 	public static final JavaType getJavaType(CompilationUnitServices compilationUnitServices, NameExpr nameToFind, Set<JavaSymbolName> typeParameters) {
328 		Assert.notNull(compilationUnitServices, "Compilation unit services required");
329 		Assert.notNull(nameToFind, "Name to find is required");
330 
331 		JavaPackage compilationUnitPackage = compilationUnitServices.getCompilationUnitPackage();
332 		
333 		if (nameToFind instanceof QualifiedNameExpr) {
334 			QualifiedNameExpr qne = (QualifiedNameExpr) nameToFind;
335 			
336 			// handle qualified name expressions that are related to inner types (eg Foo.Bar)
337 			NameExpr qneQualifier = qne.getQualifier();
338 			NameExpr enclosedBy = getNameExpr(compilationUnitServices.getEnclosingTypeName().getSimpleTypeName());
339 			if (isEqual(qneQualifier, enclosedBy)) {
340 				// this qualified name expression is simply an inner type reference
341 				String name = compilationUnitServices.getEnclosingTypeName().getFullyQualifiedTypeName() + "." + nameToFind.getName();
342 				return new JavaType(name);
343 			}
344 			
345 			// treat it as a fully-qualified name expression that includes the package
346 			return new JavaType(qne.toString());
347 		}
348 		
349 		// Unqualified name detected, so check if it's in the type parameter list
350 		if (typeParameters != null && typeParameters.contains(new JavaSymbolName(nameToFind.getName()))) {
351 			return new JavaType(nameToFind.getName(), 0, DataType.VARIABLE, null, null);
352 		}
353 		
354 		// We are searching for a non-qualified name expression (nameToFind), so check if the compilation unit itself declares that type
355 		for (TypeDeclaration internalType : compilationUnitServices.getInnerTypes()) {
356 			NameExpr nameExpr = getNameExpr(internalType.getName());
357 			if (isEqual(nameExpr, nameToFind)) {
358 				// found, so now we need to convert the internalType to a proper JavaType
359 				String name = compilationUnitServices.getEnclosingTypeName().getFullyQualifiedTypeName() + "." + nameToFind.getName();
360 				return new JavaType(name);
361 			}
362 		}
363 		
364 		ImportDeclaration importDeclaration = getImportDeclarationFor(compilationUnitServices, nameToFind);
365 		if (importDeclaration  == null) {
366 			if (ImportRegistrationResolverImpl.isPartOfJavaLang(nameToFind.getName())) {
367 				return new JavaType("java.lang." + nameToFind.getName());
368 			}
369 			String name = compilationUnitPackage.getFullyQualifiedPackageName() == "" ? nameToFind.getName() : compilationUnitPackage.getFullyQualifiedPackageName() + "." + nameToFind.getName();
370 			return new JavaType(name);
371 		}
372 		
373 		return new JavaType(importDeclaration.getName().toString());
374 	}
375 	
376 	/**
377 	 * Converts the indicated {@link JavaType} into a {@link ReferenceType}.
378 	 * 
379 	 * <p>
380 	 * Note that no effort is made to manage imports etc.
381 	 * 
382 	 * @param nameExpr to convert (required)
383 	 * @return the corresponding {@link ReferenceType} (never null)
384 	 */
385 	public static final ReferenceType getReferenceType(NameExpr nameExpr) {
386 		Assert.notNull(nameExpr, "Java type required");
387 		return new ReferenceType(getClassOrInterfaceType(nameExpr));
388 	}
389 	
390 	/**
391 	 * Converts the indicated {@link NameExpr} into a {@link ClassOrInterfaceType}.
392 	 * 
393 	 * <p>
394 	 * Note that no effort is made to manage imports etc.
395 	 * 
396 	 * @param nameExpr to convert (required)
397 	 * @return the corresponding {@link ClassOrInterfaceType} (never null)
398 	 */
399 	public static final ClassOrInterfaceType getClassOrInterfaceType(NameExpr nameExpr) {
400 		Assert.notNull(nameExpr, "Java type required");
401 		if (nameExpr instanceof QualifiedNameExpr) {
402 			QualifiedNameExpr qne = (QualifiedNameExpr) nameExpr;
403 			return new ClassOrInterfaceType(qne.getQualifier().getName() + "." + qne.getName());
404 		}
405 		return new ClassOrInterfaceType(nameExpr.getName());
406 	}
407 	
408 	/**
409 	 * Given a primitive type, computes the corresponding Java Parser type.
410 	 * 
411 	 * <p>
412 	 * Presenting a non-primitive type to this method will throw an exception. If you have a
413 	 * non-primitive type, use {@link #importTypeIfRequired(JavaPackage, List, JavaType)} and
414 	 * then present the {@link NameExpr} it returns to {@link #getClassOrInterfaceType(NameExpr)}.
415 	 * 
416 	 * @param javaType a primitive type (required, and must be primitive)
417 	 * @return the equivalent Java Parser {@link Type}
418 	 */
419 	public static final Type getType(JavaType javaType) {
420 		Assert.notNull(javaType, "Java type required");
421 		Assert.isTrue(javaType.isPrimitive(), "Java type must be primitive to be presented to this method");
422 		if (javaType.equals(JavaType.VOID_PRIMITIVE)) {
423 			return new VoidType();
424 		} else if (javaType.equals(JavaType.BOOLEAN_PRIMITIVE)) {
425 			return new PrimitiveType(Primitive.Boolean);
426 		} else if (javaType.equals(JavaType.BYTE_PRIMITIVE)) {
427 			return new PrimitiveType(Primitive.Byte);
428 		} else if (javaType.equals(JavaType.CHAR_PRIMITIVE)) {
429 			return new PrimitiveType(Primitive.Char);
430 		} else if (javaType.equals(JavaType.DOUBLE_PRIMITIVE)) {
431 			return new PrimitiveType(Primitive.Double);
432 		} else if (javaType.equals(JavaType.FLOAT_PRIMITIVE)) {
433 			return new PrimitiveType(Primitive.Float);
434 		} else if (javaType.equals(JavaType.INT_PRIMITIVE)) {
435 			return new PrimitiveType(Primitive.Int);
436 		} else if (javaType.equals(JavaType.LONG_PRIMITIVE)) {
437 			return new PrimitiveType(Primitive.Long);
438 		} else if (javaType.equals(JavaType.SHORT_PRIMITIVE)) {
439 			return new PrimitiveType(Primitive.Short);
440 		}
441 		throw new IllegalStateException("Unknown primitive " + javaType);
442 		
443 	}
444 
445 	/**
446 	 * Resolves the effective {@link JavaType} a {@link ClassOrInterfaceType} represents, including any
447 	 * type arguments.
448 	 * 
449 	 * @param compilationUnitServices for package management (required)
450 	 * @param cit the class or interface type to resolve (required)
451 	 * @return the effective Java type (never null)
452 	 */
453 	public static final JavaType getJavaTypeNow(CompilationUnitServices compilationUnitServices, ClassOrInterfaceType cit, Set<JavaSymbolName> typeParameters) {
454 		Assert.notNull(compilationUnitServices, "Compilation unit services required");
455 		Assert.notNull(cit, "ClassOrInterfaceType required");
456 
457 		JavaPackage compilationUnitPackage = compilationUnitServices.getCompilationUnitPackage();
458 		Assert.notNull(compilationUnitPackage, "Compilation unit package required");
459 
460 		String typeName = cit.getName();
461 		ClassOrInterfaceType scope = cit.getScope();
462 		while (scope != null) {
463 			typeName = scope.getName() + "." + typeName;
464 			scope = scope.getScope();
465 		}
466 		NameExpr nameExpr = getNameExpr(typeName);
467 		
468 		JavaType effectiveType = getJavaType(compilationUnitServices, nameExpr, typeParameters);
469 		
470 		// Handle any type arguments
471 		List<JavaType> typeParams = new ArrayList<JavaType>();
472 		if (cit.getTypeArgs() != null) {
473 			for (Type ta : cit.getTypeArgs()) {
474 				typeParams.add(getJavaType(compilationUnitServices, ta, typeParameters));
475 			}
476 		}
477 		
478 		return new JavaType(effectiveType.getFullyQualifiedTypeName(), effectiveType.getArray(), effectiveType.getDataType(), null, typeParams);
479 	}
480 	
481 	/**
482 	 * Resolves the effective {@link JavaType} a {@link ClassOrInterfaceDeclaration} represents, including any
483 	 * type parameters.
484 	 * 
485 	 * @param compilationUnitServices for package management (required)
486 	 * @param cid the class or interface declaration to resolve (required)
487 	 * @return the effective Java type (never null)
488 	 */
489 	public static final JavaType getJavaType(CompilationUnitServices compilationUnitServices, ClassOrInterfaceDeclaration cid) {
490 		Assert.notNull(compilationUnitServices, "Compilation unit services required");
491 		Assert.notNull(cid, "ClassOrInterfaceType required");
492 		
493 		// Convert the ClassOrInterfaceDeclaration name into a JavaType
494 		NameExpr nameExpr = getNameExpr(cid.getName());
495 		JavaType effectiveType = getJavaType(compilationUnitServices, nameExpr, null);
496 		
497 		// Populate JavaType with type parameters
498 		List<JavaType> typeParams = new ArrayList<JavaType>();
499 		List<TypeParameter> params = cid.getTypeParameters();
500 		if (params != null) {
501 			Set<JavaSymbolName> locatedTypeParams = new HashSet<JavaSymbolName>();
502 			for (TypeParameter candidate : params) {
503 				JavaSymbolName currentTypeParam = new JavaSymbolName(candidate.getName());
504 				locatedTypeParams.add(currentTypeParam);
505 				JavaType javaType = null;
506 				if (candidate.getTypeBound() == null) {
507 					javaType = new JavaType("java.lang.Object", 0, DataType.TYPE, currentTypeParam, null);
508 				} else {
509 					ClassOrInterfaceType cit = candidate.getTypeBound().get(0);
510 					javaType = JavaParserUtils.getJavaTypeNow(compilationUnitServices, cit, locatedTypeParams);
511 					javaType = new JavaType(javaType.getFullyQualifiedTypeName(), javaType.getArray(), javaType.getDataType(), currentTypeParam, javaType.getParameters());
512 				}
513 				typeParams.add(javaType);
514 			}
515 		}
516 		
517 		return new JavaType(effectiveType.getFullyQualifiedTypeName(), effectiveType.getArray(), effectiveType.getDataType(), null, typeParams);
518 	}
519 
520 	/**
521 	 * Looks up the import declaration applicable to the presented name expression.
522 	 * 
523 	 * <p>
524 	 * If a fully-qualified name is passed to this method, the corresponding import will be evaluated for a complete match.
525 	 * If a simple name is passed to this method, the corresponding import will be evaluated if its simple name matches.
526 	 * This therefore reflects the normal Java semantics for using simple type names that have been imported.
527 	 * 
528 	 * @param imports the compilation unit's imports (required)
529 	 * @param typesInCompilationUnit the types in the compilation unit (required)
530 	 * @param nameExpr the expression to locate an import for (which would generally be a {@link NameExpr} and thus not have a package identifier; required)
531 	 * @return the relevant import, or null if there is no import for the expression
532 	 */
533 	private static final ImportDeclaration getImportDeclarationFor(CompilationUnitServices compilationUnitServices, NameExpr nameExpr) {
534 		Assert.notNull(compilationUnitServices, "Compilation unit services required");
535 		Assert.notNull(nameExpr, "Name expression required");
536 		
537 		List<ImportDeclaration> imports = compilationUnitServices.getImports();
538 
539 		for (ImportDeclaration candidate : imports) {
540 			NameExpr candidateNameExpr = candidate.getName();
541 			Assert.isInstanceOf(QualifiedNameExpr.class, candidateNameExpr, "Expected import '" + candidate + "' to use a fully-qualified type name");
542 			QualifiedNameExpr candidateQualifiedName = (QualifiedNameExpr) candidateNameExpr;
543 			if (nameExpr instanceof QualifiedNameExpr) {
544 				// user is asking for a fully-qualified name; let's see if there is a full match
545 				if (isEqual(nameExpr, candidateQualifiedName)) {
546 					return candidate;
547 				}
548 			} else {
549 				// user is not asking for a fully-qualified name, so let's do a simple name comparison that discards the import's qualified-name package
550 				if (candidateQualifiedName.getName().equals(nameExpr.getName())) {
551 					return candidate;
552 				}
553 			}
554 		}
555 		return null;
556 	}
557 	
558 	public static final ReferenceType importParametersForType(JavaType targetType, List<ImportDeclaration> imports, JavaType typeToImport) {
559 		Assert.notNull(targetType, "Target type is required");
560 		Assert.notNull(imports, "Compilation unit imports required");
561 		Assert.notNull(typeToImport, "Java type to import is required");
562 		
563 		// TODO: do the import magic, but we'll defer that
564 		return new ReferenceType(getClassOrInterfaceType(new NameExpr(typeToImport.toString())));
565 	}
566 
567 	/**
568 	 * Attempts to import the presented {@link JavaType}.
569 	 * 
570 	 * <p>
571 	 * Whether imported or not, the method returns a {@link NameExpr} suitable for subsequent use
572 	 * when referring to that type.
573 	 * 
574 	 * <p>
575 	 * If an attempt is made to import a java.lang type, it is ignored.
576 	 * 
577 	 * <p>
578 	 * If an attempt is made to import a type without a package, it is ignored.
579 	 * 
580 	 * <p>
581 	 * We import every type usage even if the type usage is within the same package and would
582 	 * theoretically not require an import. This is undertaken so that there is no requirement
583 	 * to separately parse every unqualified type usage within the compilation unit so as to
584 	 * refrain from importing subsequently conflicting types.
585 	 * 
586 	 * @param targetType the compilation unit target type (required)
587 	 * @param imports the compilation unit's imports (required)
588 	 * @param typeToImport the type to be imported (required)
589 	 * @return the name expression to be used when referring to that type (never null)
590 	 */
591 	public static final NameExpr importTypeIfRequired(JavaType targetType, List<ImportDeclaration> imports, JavaType typeToImport) {
592 		Assert.notNull(targetType, "Target type is required");
593 		JavaPackage compilationUnitPackage = targetType.getPackage();
594 		Assert.notNull(imports, "Compilation unit imports required");
595 		Assert.notNull(typeToImport, "Java type to import is required");
596 		
597 		// If it's a primitive, it's really easy
598 		if (typeToImport.isPrimitive()) {
599 			return new NameExpr(typeToImport.getNameIncludingTypeParameters());
600 		}
601 		
602 		// This is pretty crude, but at least it emits source code for people (forget imports, though!)
603 		if (typeToImport.getArgName() != null) {
604 			return new NameExpr(typeToImport.toString());
605 		}
606 		
607 		// Handle if the type doesn't have a package at all
608 		if (typeToImport.isDefaultPackage()) {
609 			return new NameExpr(typeToImport.getSimpleTypeName());
610 		}
611 		
612 		NameExpr typeToImportExpr;
613 		if (typeToImport.getEnclosingType() == null) {
614 			typeToImportExpr = new QualifiedNameExpr(new NameExpr(typeToImport.getPackage().getFullyQualifiedPackageName()), typeToImport.getSimpleTypeName());
615 		} else {
616 			typeToImportExpr = new QualifiedNameExpr(new NameExpr(typeToImport.getEnclosingType().getFullyQualifiedTypeName()), typeToImport.getSimpleTypeName());
617 		}
618 		
619 		ImportDeclaration newImport = new ImportDeclaration(typeToImportExpr, false, false);
620 		
621 		boolean addImport = true;
622 		boolean useSimpleTypeName = false;
623 		for (ImportDeclaration existingImport : imports) {
624 			if (existingImport.getName().getName().equals(newImport.getName().getName())) {
625 				// do not import, as there is already an import with the simple type name
626 				addImport = false;
627 				
628 				// if this is a complete match, it indicates we can use the simple type name
629 				if (isEqual(existingImport.getName(), newImport.getName())) {
630 					useSimpleTypeName = true;
631 					break;
632 				}
633 			}
634 		}
635 		
636 		if (addImport && "java.lang".equals(typeToImport.getPackage().getFullyQualifiedPackageName())) {
637 			// So we would have imported, but we don't need to
638 			addImport = false;
639 			
640 			// The fact we could have imported means there was no other conflicting simple type names
641 			useSimpleTypeName = true;
642 		}
643 		
644 		if (addImport && typeToImport.getPackage().equals(compilationUnitPackage)) {
645 			// It is not theoretically necessary to add an import for something in the same package,
646 			// but we elect to explicitly perform an import so future conflicting types are not imported
647 			// addImport = false;
648 			// useSimpleTypeName = true;
649 		}
650 		
651 		if (addImport == true && targetType.getSimpleTypeName().equals(typeToImport.getSimpleTypeName())) {
652 			// So we would have imported it, but then it would conflict with the simple name of the type
653 			addImport = false;
654 			useSimpleTypeName = false;
655 		}
656 		
657 		if (addImport) {
658 			imports.add(newImport);
659 			useSimpleTypeName = true;
660 		}
661 		
662 		if (useSimpleTypeName) {
663 			return new NameExpr(typeToImport.getSimpleTypeName());
664 		} else {
665 			return new QualifiedNameExpr(new NameExpr(typeToImport.getPackage().getFullyQualifiedPackageName()), typeToImport.getSimpleTypeName());
666 		}
667 		
668 	}
669 
670 	/**
671 	 * Recognises {@link Expression}s of type {@link FieldAccessExpr} and {@link ClassExpr} and automatically imports them if required,
672 	 * returning the correct {@link Expression} that should subsequently be used.
673 	 * 
674 	 * <p>
675 	 * Even if an {@link Expression} is not resolved by this method into a type and/or imported, the method guarantees to always return
676 	 * an {@link Expression} that the caller can subsequently use in place of the passed {@link Expression}. In practical terms, the
677 	 * {@link Expression} passed to this method will be returned unless the type was already imported, just imported, or represented
678 	 * a java.lang type.
679 	 * 
680 	 * @param targetType the compilation unit target type (required)
681 	 * @param imports the existing imports (required)
682 	 * @param value that expression, which need not necessarily be resolvable to a type (required)
683 	 * @return the expression to now use, as appropriately resolved (never returns null)
684 	 */
685 	public static final Expression importExpressionIfRequired(JavaType targetType, List<ImportDeclaration> imports, Expression value) {
686 		Assert.notNull(targetType, "Target type required");
687 		Assert.notNull(imports, "Imports required");
688 		Assert.notNull(value, "Expression value required");
689 		
690 		if (value instanceof FieldAccessExpr) {
691 			Expression scope = ((FieldAccessExpr)value).getScope();
692 			String field = ((FieldAccessExpr)value).getField();
693 			if (scope instanceof QualifiedNameExpr) {
694 				String packageName = ((QualifiedNameExpr) scope).getQualifier().getName();
695 				String simpleName = ((QualifiedNameExpr) scope).getName();
696 				String fullyQualifiedName = packageName + "." + simpleName;
697 				JavaType javaType = new JavaType(fullyQualifiedName);
698 				NameExpr nameToUse = importTypeIfRequired(targetType, imports, javaType);
699 				if (!(nameToUse instanceof QualifiedNameExpr)) {
700 					return new FieldAccessExpr(nameToUse, field);
701 				}
702 			}
703 		} else if (value instanceof ClassExpr) {
704 			Type type = ((ClassExpr)value).getType();
705 			if (type instanceof ClassOrInterfaceType) {
706 				JavaType javaType = new JavaType(((ClassOrInterfaceType)type).getName());
707 				NameExpr nameToUse = importTypeIfRequired(targetType, imports, javaType);
708 				if (!(nameToUse instanceof QualifiedNameExpr)) {
709 					return new ClassExpr(new ClassOrInterfaceType(javaType.getSimpleTypeName()));
710 				}
711 			} else if (type instanceof ReferenceType && ((ReferenceType)type).getType() instanceof ClassOrInterfaceType) {
712 				ClassOrInterfaceType cit = (ClassOrInterfaceType) ((ReferenceType)type).getType();
713 				JavaType javaType = new JavaType(cit.getName());
714 				NameExpr nameToUse = importTypeIfRequired(targetType, imports, javaType);
715 				if (!(nameToUse instanceof QualifiedNameExpr)) {
716 					return new ClassExpr(new ClassOrInterfaceType(javaType.getSimpleTypeName()));
717 				}
718 			}
719 		} 		
720 		
721 		// Make no changes
722 		return value;
723 	}
724 
725 }