package org.springframework.roo.model; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Vector; /** * The declaration of a Java type (i.e. contains no details of its members). * Instances are immutable. * <p> * Note that a Java type can be contained within a package, but a package is not * a type. * <p> * This class is used whenever a formal reference to a Java type is required. It * provides convenient ways to determine the type's simple name and package * name. A related {@link org.springframework.core.convert.converter.Converter} * is also offered. * * @author Ben Alex * @since 1.0 */ public class JavaType implements Comparable<JavaType> { public static final JavaType BOOLEAN_OBJECT = new JavaType("java.lang.Boolean"); public static final JavaType BOOLEAN_PRIMITIVE = new JavaType("java.lang.Boolean", 0, DataType.PRIMITIVE, null, null); public static final JavaType BYTE_ARRAY_PRIMITIVE = new JavaType("java.lang.Byte", 1, DataType.PRIMITIVE, null, null); public static final JavaType BYTE_OBJECT = new JavaType("java.lang.Byte"); public static final JavaType BYTE_PRIMITIVE = new JavaType("java.lang.Byte", 0, DataType.PRIMITIVE, null, null); public static final JavaType CHAR_OBJECT = new JavaType("java.lang.Character"); public static final JavaType CHAR_PRIMITIVE = new JavaType("java.lang.Character", 0, DataType.PRIMITIVE, null, null); public static final JavaType CLASS = new JavaType("java.lang.Class"); // The fully-qualified names of common collection types private static final Set<String> COMMON_COLLECTION_TYPES = new HashSet<String>(); private static final String[] CORE_TYPE_PREFIXES = {"java.", "javax."}; public static final JavaType DOUBLE_OBJECT = new JavaType("java.lang.Double"); public static final JavaType DOUBLE_PRIMITIVE = new JavaType("java.lang.Double", 0, DataType.PRIMITIVE, null, null); public static final JavaType FLOAT_OBJECT = new JavaType("java.lang.Float"); public static final JavaType FLOAT_PRIMITIVE = new JavaType("java.lang.Float", 0, DataType.PRIMITIVE, null, null); public static final JavaType HASH_MAP = new JavaType("java.util.HashMap"); public static final JavaType HASH_SET = new JavaType("java.util.HashSet"); public static final JavaType INT_OBJECT = new JavaType("java.lang.Integer"); public static final JavaType INT_PRIMITIVE = new JavaType("java.lang.Integer", 0, DataType.PRIMITIVE, null, null); public static final JavaType LIST = new JavaType("java.util.List"); public static final JavaType LONG_OBJECT = new JavaType("java.lang.Long"); public static final JavaType LONG_PRIMITIVE = new JavaType("java.lang.Long", 0, DataType.PRIMITIVE, null, null); public static final JavaType OBJECT = new JavaType("java.lang.Object"); public static final JavaType OBJECTS = new JavaType("java.util.Objects"); public static final JavaType OBJECT_ARRAY = new JavaType("java.lang.Object", 1, DataType.TYPE, null, null); public static final JavaType NUMBER = new JavaType("java.lang.Number"); public static final JavaType SERIALIZABLE = new JavaType("java.io.Serializable"); public static final JavaType SET = new JavaType("java.util.Set"); public static final JavaType SHORT_OBJECT = new JavaType("java.lang.Short"); public static final JavaType SHORT_PRIMITIVE = new JavaType("java.lang.Short", 0, DataType.PRIMITIVE, null, null); public static final JavaType STRING = new JavaType("java.lang.String"); public static final JavaType STRING_ARRAY = new JavaType("java.lang.String", 1, DataType.TYPE, null, null); public static final JavaType OVERRIDE = new JavaType("java.lang.Override"); public static final JavaType ITERABLE = new JavaType("java.lang.Iterable"); public static final JavaType ITERATOR = new JavaType("java.util.Iterator"); public static final JavaType ARRAYS = new JavaType("java.util.Arrays"); public static final JavaType COLLECTIONS = new JavaType("java.util.Collections"); public static final JavaType ARRAY_LIST = new JavaType("java.util.ArrayList"); // javax types public static final JavaType REQUEST_WRAPPER = new JavaType("javax.xml.ws.RequestWrapper"); public static final JavaType RESPONSE_WRAPPER = new JavaType("javax.xml.ws.ResponseWrapper"); public static final JavaType ENDPOINT = new JavaType("javax.xml.ws.Endpoint"); public static final JavaType WEB_METHOD = new JavaType("javax.jws.WebMethod"); public static final JavaType WEB_PARAM = new JavaType("javax.jws.WebParam"); public static final JavaType WEB_RESULT = new JavaType("javax.jws.WebResult"); public static final JavaType WEB_SERVICE = new JavaType("javax.jws.WebService"); public static final JavaType XML_ROOT_ELEMENT = new JavaType( "javax.xml.bind.annotation.XmlRootElement"); public static final JavaType XML_ID_REF = new JavaType("javax.xml.bind.annotation.XmlIDREF"); public static final JavaType XML_ELEMENT = new JavaType("javax.xml.bind.annotation.XmlElement"); public static final JavaType XML_ELEMENT_WRAPPER = new JavaType( "javax.xml.bind.annotation.XmlElementWrapper"); public static final JavaType XML_TRANSIENT = new JavaType( "javax.xml.bind.annotation.XmlTransient"); public static final JavaType XML_ID = new JavaType("javax.xml.bind.annotation.XmlID"); public static final JavaType XML_ATTRIBUTE = new JavaType( "javax.xml.bind.annotation.XmlAttribute"); public static final JavaType XML_JAVATYPE_ADAPTER = new JavaType( "javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter"); /** * @deprecated use {@link #STRING} instead */ @Deprecated public static final JavaType STRING_OBJECT = STRING; public static final JavaType VOID_OBJECT = new JavaType("java.lang.Void"); public static final JavaType VOID_PRIMITIVE = new JavaType("java.lang.Void", 0, DataType.PRIMITIVE, null, null); // Used for wildcard type parameters; it must be one or the other public static final JavaSymbolName WILDCARD_EXTENDS_ARG = new JavaSymbolName( "_ROO_WILDCARD_EXTENDS_"); // List<? extends YY> public static final JavaSymbolName WILDCARD_NEITHER_ARG = new JavaSymbolName( "_ROO_WILDCARD_NEITHER_"); // List<?> public static final JavaSymbolName GENERIC_TYPE_ARG = new JavaSymbolName("T"); // List<?> public static final JavaType WILDCARD_NEITHER = new JavaType(OBJECT.getFullyQualifiedTypeName(), 0, DataType.TYPE, JavaType.WILDCARD_NEITHER_ARG, null); public static final JavaType GENERIC_TYPE = new JavaType("T"); public static final JavaSymbolName WILDCARD_SUPER_ARG = new JavaSymbolName("_ROO_WILDCARD_SUPER_"); // List<? super XXXX> static { COMMON_COLLECTION_TYPES.add(ArrayList.class.getName()); COMMON_COLLECTION_TYPES.add(Collection.class.getName()); COMMON_COLLECTION_TYPES.add(HashMap.class.getName()); COMMON_COLLECTION_TYPES.add(HashSet.class.getName()); COMMON_COLLECTION_TYPES.add(List.class.getName()); COMMON_COLLECTION_TYPES.add(Map.class.getName()); COMMON_COLLECTION_TYPES.add(Set.class.getName()); COMMON_COLLECTION_TYPES.add(TreeMap.class.getName()); COMMON_COLLECTION_TYPES.add(Vector.class.getName()); } /** * Factory method for a {@link JavaType} with full details. Recall that * {@link JavaType} is immutable and therefore this is the only way of * setting these non-default values. This is a factory method rather than a * constructor so as not to cause ambiguity problems for existing callers of * {@link #JavaType(String, int, DataType, JavaSymbolName, List)} * * @param fullyQualifiedTypeName the name (as per the rules above) * @param arrayDimensions the number of array dimensions (0 = not an array, * 1 = one-dimensional array, etc.) * @param dataType the {@link DataType} (required) * @param argName the type argument name to this particular Java type (can * be null if unassigned) * @param parameters the type parameters applicable (can be null if there * aren't any) * @return a JavaType instance constructed based on the passed in details * @since 1.2.0 */ public static JavaType getInstance(final String fullyQualifiedTypeName, final int arrayDimensions, final DataType dataType, final JavaSymbolName argName, final JavaType... parameters) { return new JavaType(fullyQualifiedTypeName, arrayDimensions, dataType, argName, Arrays.asList(parameters)); } /** * Factory method for a {@link JavaType} with full details. Recall that * {@link JavaType} is immutable and therefore this is the only way of * setting these non-default values. This is a factory method rather than a * constructor so as not to cause ambiguity problems for existing callers of * {@link #JavaType(String, int, DataType, JavaSymbolName, List)} * * @param fullyQualifiedTypeName the name (as per the rules above) * @param enclosingType the type's enclosing type * @param arrayDimensions the number of array dimensions (0 = not an array, * 1 = one-dimensional array, etc.) * @param dataType the {@link DataType} (required) * @param argName the type argument name to this particular Java type (can * be null if unassigned) * @param parameters the type parameters applicable (can be null if there * aren't any) * @return a JavaType instance constructed based on the passed in details * @since 1.2.0 */ public static JavaType getInstance(final String fullyQualifiedTypeName, final JavaType enclosingType, final int arrayDimensions, final DataType dataType, final JavaSymbolName argName, final JavaType... parameters) { return new JavaType(fullyQualifiedTypeName, enclosingType, arrayDimensions, dataType, argName, Arrays.asList(parameters), null); } /** * Returns a {@link JavaType} for a {@link List} of the given element type * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 1.2.0 */ public static JavaType listOf(final JavaType elementType) { return new JavaType(List.class.getName(), 0, DataType.TYPE, null, Arrays.asList(elementType)); } /** * Returns a {@link JavaType} for a {@link Map} of the given element type * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType mapOf(final JavaType keyType, final JavaType elementType) { return new JavaType(Map.class.getName(), 0, DataType.TYPE, null, Arrays.asList(keyType, elementType)); } /** * Returns a {@link JavaType} for a {@link Iterable} of the given element type * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType iterableOf(final JavaType elementType) { return new JavaType(Iterable.class.getName(), 0, DataType.TYPE, null, Arrays.asList(elementType)); } /** * Returns a {@link JavaType} for a {@link Collection} of the given element type * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType collectionOf(final JavaType elementType) { return new JavaType(Collection.class.getName(), 0, DataType.TYPE, null, Arrays.asList(elementType)); } /** * Returns a {@link JavaType} for a _collectionType_ of the given element type * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType collectionOf(final JavaType collectionType, final JavaType elementType) { return new JavaType(collectionType.getFullyQualifiedTypeName(), 0, DataType.TYPE, null, Arrays.asList(elementType)); } /** * Returns a {@link JavaType} for a _wrapperType_ of the given element type: * By example: JavaType.wrapperOf(JavaType.MAP,JavaType.STRING,JavaType.STRING) returns * "Map<String,String>" * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType wrapperOf(final JavaType wrapperType, final JavaType... elementTypes) { return new JavaType(wrapperType.getFullyQualifiedTypeName(), 0, DataType.TYPE, null, Arrays.asList(elementTypes)); } /** * Returns a {@link JavaType} for a _wrapperType_ with wildcard: * JavaType.wrapperWilcard(JavaType.LIST) return List<?> * * @param elementType the type of element in the list (required) * @return a non-<code>null</code> type * @since 2.0.0 */ public static JavaType wrapperWilcard(final JavaType wrapperType) { return wrapperOf(wrapperType, WILDCARD_NEITHER); } /** * Returns a {@link JavaType} for a _wrapperType_ with generic: * JavaType.wrapperWilcard(JavaType.LIST) return List<T> * * @param elementType the type of element in the list (required) * @return a non `null` type * @since 2.0.0 */ public static JavaType wrapperGenericType(final JavaType wrapperType) { return wrapperOf(wrapperType, GENERIC_TYPE); } /** Return {@link JavaType} which is a Widcard of generics with "extends" * value. * * By exampe: JavaType.listOf(JavaType.wilcardExtends(JavaType.NUMBER)) generates List<? extends Number> * * @param extendsOf * @return */ public static JavaType wilcardExtends(final JavaType extendsOf) { return new JavaType(extendsOf.getFullyQualifiedTypeName(), 0, DataType.TYPE, JavaType.WILDCARD_EXTENDS_ARG, null); } /** Return {@link JavaType} which is a Widcard of generics with "supper" * value. * * By exampe: JavaType.listOf(JavaType.wilcardSupper(JavaType.NUMBER)) generates List<? supper Number> * * @param supperOf * @return */ public static JavaType wilcardSupper(final JavaType supperOf) { return new JavaType(supperOf.getFullyQualifiedTypeName(), 0, DataType.TYPE, JavaType.WILDCARD_SUPER_ARG, null); } private final JavaSymbolName argName; private final int arrayDimensions; private final DataType dataType; private final boolean defaultPackage; private final JavaType enclosingType; private final String fullyQualifiedTypeName; private final List<JavaType> parameters; private final String simpleTypeName; private String module; /** * Constructor equivalent to {@link #JavaType(String)}, but takes a Class * for convenience and type safety. * * @param type the class for which to create an instance (required) * @since 1.2.0 */ public JavaType(final Class<?> type) { this(type.getName()); } /** * Constructs a {@link JavaType}. * <p> * The fully qualified type name will be enforced as follows: * <ul> * <li>The rules listed in * {@link JavaSymbolName#assertJavaNameLegal(String)} * <li>First letter of simple type name must be upper-case</li> * </ul> * <p> * A fully qualified type name may include or exclude a package designator. * * @param fullyQualifiedTypeName the name (as per the above rules; * mandatory) */ public JavaType(final String fullyQualifiedTypeName) { this(fullyQualifiedTypeName, 0, DataType.TYPE, null, null); } /** * Constructs a {@link JavaType}. * <p> * The fully qualified type name will be enforced as follows: * <ul> * <li>The rules listed in * {@link JavaSymbolName#assertJavaNameLegal(String)} * <li>First letter of simple type name must be upper-case</li> * </ul> * <p> * A fully qualified type name may include or exclude a package designator. * * @param fullyQualifiedTypeName the name (as per the above rules; * mandatory) * @param module the module where is created (optional) */ public JavaType(final String fullyQualifiedTypeName, String module) { this(fullyQualifiedTypeName, 0, DataType.TYPE, null, null, module); } /** * Construct a {@link JavaType} with full details. Recall that * {@link JavaType} is immutable and therefore this is the only way of * setting these non-default values. * * @param fullyQualifiedTypeName the name (as per the rules above) * @param arrayDimensions the number of array dimensions (0 = not an array, * 1 = one-dimensional array, etc.) * @param dataType the {@link DataType} (required) * @param argName the type argument name to this particular Java type (can * be null if unassigned) * @param parameters the type parameters applicable (can be null if there * aren't any) */ public JavaType(final String fullyQualifiedTypeName, final int arrayDimensions, final DataType dataType, final JavaSymbolName argName, final List<JavaType> parameters) { this(fullyQualifiedTypeName, null, arrayDimensions, dataType, argName, parameters, null); } /** * Construct a {@link JavaType} with full details. Recall that * {@link JavaType} is immutable and therefore this is the only way of * setting these non-default values. * * @param fullyQualifiedTypeName the name (as per the rules above) * @param arrayDimensions the number of array dimensions (0 = not an array, * 1 = one-dimensional array, etc.) * @param dataType the {@link DataType} (required) * @param argName the type argument name to this particular Java type (can * be null if unassigned) * @param parameters the type parameters applicable (can be null if there * aren't any) * @param module the module where is created (optional) */ public JavaType(final String fullyQualifiedTypeName, final int arrayDimensions, final DataType dataType, final JavaSymbolName argName, final List<JavaType> parameters, final String module) { this(fullyQualifiedTypeName, null, arrayDimensions, dataType, argName, parameters, module); } /** * Constructs a {@link JavaType}. * <p> * The fully qualified type name will be enforced as follows: * <ul> * <li>The rules listed in * {@link JavaSymbolName#assertJavaNameLegal(String)} * <li>First letter of simple type name must be upper-case</li> * </ul> * <p> * A fully qualified type name may include or exclude a package designator. * * @param fullyQualifiedTypeName the name (as per the above rules; * mandatory) * @param enclosingType the type's enclosing type */ public JavaType(final String fullyQualifiedTypeName, final JavaType enclosingType) { this(fullyQualifiedTypeName, enclosingType, 0, DataType.TYPE, null, null, enclosingType .getModule()); } /** * Constructs a {@link JavaType}. * <p> * The fully qualified type name will be enforced as follows: * <ul> * <li>The rules listed in * {@link JavaSymbolName#assertJavaNameLegal(String)} * <li>First letter of simple type name must be upper-case</li> * </ul> * <p> * A fully qualified type name may include or exclude a package designator. * * @param fullyQualifiedTypeName the name (as per the above rules; * mandatory) * @param enclosingType the type's enclosing type * @param module the module where is created (optional) */ public JavaType(final String fullyQualifiedTypeName, final JavaType enclosingType, final String module) { this(fullyQualifiedTypeName, enclosingType, 0, DataType.TYPE, null, null, module); } /** * Construct a {@link JavaType} with full details. Recall that * {@link JavaType} is immutable and therefore this is the only way of * setting these non-default values. * * @param fullyQualifiedTypeName the name (as per the rules above) * @param enclosingType the type's enclosing type * @param arrayDimensions the number of array dimensions (0 = not an array, * 1 = one-dimensional array, etc.) * @param dataType the {@link DataType} (required) * @param argName the type argument name to this particular Java type (can * be null if unassigned) * @param parameters the type parameters applicable (can be null if there * aren't any) * @param module the module where is created (optional) */ public JavaType(final String fullyQualifiedTypeName, final JavaType enclosingType, final int arrayDimensions, final DataType dataType, final JavaSymbolName argName, final List<JavaType> parameters, final String module) { Validate.notBlank(fullyQualifiedTypeName, "Fully qualified type name required"); Validate.notNull(dataType, "Data type required"); JavaSymbolName.assertJavaNameLegal(fullyQualifiedTypeName); this.argName = argName; this.arrayDimensions = arrayDimensions; this.dataType = dataType; this.fullyQualifiedTypeName = fullyQualifiedTypeName; defaultPackage = !fullyQualifiedTypeName.contains("."); if (enclosingType == null) { this.enclosingType = determineEnclosingType(); } else { this.enclosingType = enclosingType; } if (defaultPackage) { simpleTypeName = fullyQualifiedTypeName; } else { final int offset = fullyQualifiedTypeName.lastIndexOf("."); simpleTypeName = fullyQualifiedTypeName.substring(offset + 1); } this.parameters = new ArrayList<JavaType>(); if (parameters != null) { this.parameters.addAll(parameters); } this.module = module; } @Override public int compareTo(final JavaType o) { // NB: If adding more fields to this class ensure the equals(Object) // method is updated accordingly if (o == null) { return -1; } if (equals(o)) { return 0; } return toString().compareTo(o.toString()); } private JavaType determineEnclosingType() { final int offset = fullyQualifiedTypeName.lastIndexOf("."); if (offset == -1) { // There is no dot in the name, so there's no way there's an // enclosing type return null; } final String possibleName = fullyQualifiedTypeName.substring(0, offset); final int offset2 = possibleName.lastIndexOf("."); // Start by handling if the type name is Foo.Bar (ie an enclosed type // within the default package) String enclosedWithinPackage = null; String enclosedWithinTypeName = possibleName; // Handle the probability the type name is within a package like // com.alpha.Foo.Bar if (offset2 > -1) { enclosedWithinPackage = possibleName.substring(0, offset2); enclosedWithinTypeName = possibleName.substring(offset2 + 1); } if (Character.isUpperCase(enclosedWithinTypeName.charAt(0))) { // First letter is upper-case, so treat it as a type name for now final String preTypeNamePortion = enclosedWithinPackage == null ? "" : enclosedWithinPackage + "."; return new JavaType(preTypeNamePortion + enclosedWithinTypeName); } return null; } @Override public boolean equals(final Object obj) { // NB: Not using the normal convention of delegating to compareTo (for // efficiency reasons) return obj != null && obj instanceof JavaType && fullyQualifiedTypeName.equals(((JavaType) obj).getFullyQualifiedTypeName()) && dataType == ((JavaType) obj).getDataType() && arrayDimensions == ((JavaType) obj).getArray() && ((JavaType) obj).getParameters().containsAll(parameters); } public JavaSymbolName getArgName() { return argName; } public int getArray() { return arrayDimensions; } private String getArraySuffix() { if (arrayDimensions == 0) { return ""; } final StringBuilder sb = new StringBuilder(); for (int i = 0; i < arrayDimensions; i++) { sb.append("[]"); } return sb.toString(); } /** * Returns this type's base type, being <code>this</code> for single-valued * types, otherwise the element type for collection types. * * @return <code>null</code> for an untyped collection * @since 1.2.1 */ public JavaType getBaseType() { if (isCommonCollectionType()) { if (parameters.isEmpty()) { return null; } return parameters.get(0); } return this; } public DataType getDataType() { return dataType; } /** * @return the enclosing type, if any (will return null if there is no * enclosing type) */ public JavaType getEnclosingType() { return enclosingType; } /** * @return the fully qualified name (complies with the rules specified in * the constructor) */ public String getFullyQualifiedTypeName() { return fullyQualifiedTypeName; } /** * Obtains the name of this type, including type parameters. It will be * formatted in a manner compatible with non-static use. No type name import * resolution will take place. This is a side-effect free method. * * @return the type name, including parameters, as legal Java code (never * null or empty) */ public String getNameIncludingTypeParameters() { return getNameIncludingTypeParameters(false, null, new HashMap<String, String>()); } /** * Obtains the name of this type, including type parameters. It will be * formatted in a manner compatible with either static or non-static usage, * as per the passed argument. Type names will attempt to be resolved (and * automatically registered) using the passed resolver. This method will * have side-effects on the passed resolver. * * @param staticForm true if the output should be compatible with static use * @param resolver the resolver to use (may be null in which case no import * resolution will occur) * @return the type name, including parameters, as legal Java code (never * null or empty) */ public String getNameIncludingTypeParameters(final boolean staticForm, final ImportRegistrationResolver resolver) { return getNameIncludingTypeParameters(staticForm, resolver, new HashMap<String, String>()); } private String getNameIncludingTypeParameters(final boolean staticForm, final ImportRegistrationResolver resolver, final Map<String, String> types) { if (DataType.PRIMITIVE == dataType) { Validate.isTrue(parameters.isEmpty(), "A primitive cannot have parameters"); if (fullyQualifiedTypeName.equals(Integer.class.getName())) { return "int" + getArraySuffix(); } else if (fullyQualifiedTypeName.equals(Character.class.getName())) { return "char" + getArraySuffix(); } else if (fullyQualifiedTypeName.equals(Void.class.getName())) { return "void"; } return StringUtils.uncapitalize(getSimpleTypeName() + getArraySuffix()); } final StringBuilder sb = new StringBuilder(); if (WILDCARD_EXTENDS_ARG.equals(argName)) { sb.append("?"); if (dataType == DataType.TYPE || !staticForm) { sb.append(" extends "); } else if (types.containsKey(fullyQualifiedTypeName)) { sb.append(" extends ").append(types.get(fullyQualifiedTypeName)); } } else if (WILDCARD_SUPER_ARG.equals(argName)) { sb.append("?"); if (dataType == DataType.TYPE || !staticForm) { sb.append(" super "); } else if (types.containsKey(fullyQualifiedTypeName)) { sb.append(" extends ").append(types.get(fullyQualifiedTypeName)); } } else if (WILDCARD_NEITHER_ARG.equals(argName)) { sb.append("?"); } else if (argName != null && !staticForm) { sb.append(argName); if (dataType == DataType.TYPE) { if (!fullyQualifiedTypeName.equals(OBJECT.getFullyQualifiedTypeName())) { sb.append(" extends "); } } } if (!WILDCARD_NEITHER_ARG.equals(argName)) { // It wasn't a WILDCARD_NEITHER, so we might need to continue with // more details if (dataType == DataType.TYPE || !staticForm) { if (resolver != null) { if (resolver.isFullyQualifiedFormRequiredAfterAutoImport(this, staticForm)) { sb.append(fullyQualifiedTypeName); } else { sb.append(getSimpleTypeName()); } } else { if (fullyQualifiedTypeName.equals(OBJECT.getFullyQualifiedTypeName())) { // It's Object, so we need to only append if this isn't // a type arg if (argName == null) { sb.append(fullyQualifiedTypeName); } } else { // It's ok to just append it as it's not Object sb.append(fullyQualifiedTypeName); } } } if (parameters.size() > 0 && (dataType == DataType.TYPE || !staticForm)) { sb.append("<"); int counter = 0; for (final JavaType param : parameters) { counter++; if (counter > 1) { sb.append(", "); } sb.append(param.getNameIncludingTypeParameters(staticForm, resolver, types)); counter++; } sb.append(">"); } sb.append(getArraySuffix()); } if (argName != null && !argName.equals(WILDCARD_EXTENDS_ARG) && !argName.equals(WILDCARD_SUPER_ARG) && !argName.equals(WILDCARD_NEITHER_ARG)) { types.put(argName.getSymbolName(), sb.toString()); } return sb.toString(); } /** * @return the package name (never null) */ public JavaPackage getPackage() { if (isDefaultPackage() && !Character.isUpperCase(fullyQualifiedTypeName.charAt(0))) { return new JavaPackage("", module); } if (enclosingType != null) { final String enclosingTypeFullyQualifiedTypeName = enclosingType.getFullyQualifiedTypeName(); final int offset = enclosingTypeFullyQualifiedTypeName.lastIndexOf("."); // Handle case where the package name after the last period starts // with a capital letter. if (offset > -1 && Character.isUpperCase(enclosingTypeFullyQualifiedTypeName.charAt(offset + 1))) { return new JavaPackage(enclosingTypeFullyQualifiedTypeName, module); } return enclosingType.getPackage(); } final int offset = fullyQualifiedTypeName.lastIndexOf("."); return offset == -1 ? new JavaPackage("") : new JavaPackage(fullyQualifiedTypeName.substring(0, offset), module); } public List<JavaType> getParameters() { return Collections.unmodifiableList(parameters); } /** * Returns the name of the source file that contains this type, starting * from its base package. For example, for a type called "com.example.Foo", * this method returns "com/example/Foo.java", delimited by the platform- * specific separator ("/" in this example). * * @return a non-blank path */ public String getRelativeFileName() { return fullyQualifiedTypeName.replace('.', File.separatorChar) + ".java"; } /** * @return the name (does not contain any periods; never null or empty) */ public String getSimpleTypeName() { return simpleTypeName; } /** * @return the module name where javaType is located */ public String getModule() { return module; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (fullyQualifiedTypeName == null ? 0 : fullyQualifiedTypeName.hashCode()); result = prime * result + (dataType == null ? 0 : dataType.hashCode()); result = prime * result + arrayDimensions; return result; } public boolean isArray() { return arrayDimensions > 0; } /** * Indicates whether this type is any kind of boolean. * * @return see above * @since 1.2.1 */ public boolean isBoolean() { return equals(BOOLEAN_OBJECT) || equals(BOOLEAN_PRIMITIVE); } public boolean isCommonCollectionType() { return COMMON_COLLECTION_TYPES.contains(fullyQualifiedTypeName); } /** * Indicates whether this type is part of core Java. * * @return see above */ public boolean isCoreType() { for (final String coreTypePrefix : CORE_TYPE_PREFIXES) { if (fullyQualifiedTypeName.startsWith(coreTypePrefix)) { return true; } } return false; } public boolean isDefaultPackage() { return defaultPackage; } /** * Indicates whether a field or variable of this type can contain multiple * values * * @return see above * @since 1.2.0 */ public boolean isMultiValued() { return isCommonCollectionType() || isArray(); } /** * Indicates whether this type is any kind of number * * @return see above * @since 2.0 */ public boolean isNumber() { try { return ClassUtils.getClass(getFullyQualifiedTypeName()).getSuperclass().equals(Number.class); } catch (ClassNotFoundException e) { return false; } } /** * Indicates whether this type is a primitive, or in the case of an array, * whether its elements are primitive. * * @return see above */ public boolean isPrimitive() { return DataType.PRIMITIVE == dataType; } /** * Returns the non-primitive type that represent this type if it is primitive. Otherwise, returns itself. * @return */ public JavaType toObjectType() { if (isPrimitive()) { return new JavaType(getFullyQualifiedTypeName(), getArray(), DataType.TYPE, getArgName(), getParameters(), getModule()); } return this; } @Override public String toString() { return getNameIncludingTypeParameters(); } }