package com.flextrade.jfixture.utility; import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; import java.util.List; public abstract class SpecimenType<T> implements Type { private final Class rawType; private final GenericTypeCollection genericTypeArguments; protected SpecimenType() { Type genericSuperclass = this.getClass().getGenericSuperclass(); if(!(genericSuperclass instanceof ParameterizedType)) { throw new IllegalArgumentException("No generic type argument provided"); } ParameterizedType pt = (ParameterizedType)genericSuperclass; SpecimenTypeFields fields = getFields(pt.getActualTypeArguments()[0]); this.rawType = fields.rawType; this.genericTypeArguments = fields.genericTypeArguments; } private SpecimenType(Type type) { SpecimenTypeFields fields = getFields(type); this.rawType = fields.rawType; this.genericTypeArguments = fields.genericTypeArguments; } private SpecimenType(Type type, SpecimenType contextualType) { SpecimenType st = convertPossibleGenericTypeToSpecimenType(type, contextualType); this.rawType = st.rawType; this.genericTypeArguments = st.genericTypeArguments; } private SpecimenType(Class rawType, GenericTypeCollection genericTypeArguments) { this.rawType = rawType; this.genericTypeArguments = genericTypeArguments; } public static SpecimenType<?> of(Type type) { return new SpecimenType<Object>(type){}; } public static <T> SpecimenType<T> of(Class<T> clazz) { return new SpecimenType<T>(clazz){}; } static SpecimenType<?> withGenericContext(Type type, SpecimenType contextualType) { return new SpecimenType(type, contextualType){}; } public final Class getRawType() { return this.rawType; } public final GenericTypeCollection getGenericTypeArguments() { return this.genericTypeArguments; } private static SpecimenType convertPossibleGenericTypeToSpecimenType(Type originalType, SpecimenType contextualType) { if (originalType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)originalType; List<GenericType> genericTypesForSpecimen = getGenericTypes(parameterizedType, contextualType); Class rawType = (Class)parameterizedType.getRawType(); GenericTypeCollection genericTypeCollection = new GenericTypeCollection(genericTypesForSpecimen.toArray(new GenericType[genericTypesForSpecimen.size()])); return new SpecimenType(rawType, genericTypeCollection){}; } if (originalType instanceof TypeVariable) { // e.g. <T> return contextualType.getGenericTypeArguments().getType(originalType.toString()); } return SpecimenType.of(originalType); } private static List<GenericType> getGenericTypes(ParameterizedType parameterizedType, SpecimenType contextualType) { List<SpecimenType> resolvedGenericTypes = resolveGenericArguments(parameterizedType, contextualType); List<GenericType> genericTypesForSpecimen = new ArrayList<GenericType>(); for(SpecimenType resolvedGenericType : resolvedGenericTypes) { String typeName = contextualType.genericTypeArguments.getNameFromType(resolvedGenericType); GenericType gt = new GenericType(resolvedGenericType, typeName); genericTypesForSpecimen.add(gt); } return genericTypesForSpecimen; } private static List<SpecimenType> resolveGenericArguments(ParameterizedType parameterizedType, SpecimenType contextualType) { List<SpecimenType> resolvedGenericTypes = new ArrayList<SpecimenType>(); Type[] genericTypes = parameterizedType.getActualTypeArguments(); for(Type genericType : genericTypes) { SpecimenType resolved = convertPossibleGenericTypeToSpecimenType(genericType, contextualType); resolvedGenericTypes.add(resolved); } return resolvedGenericTypes; } private static SpecimenTypeFields getFields(Type type) { if(type instanceof SpecimenType) return getSpecimenTypeFields((SpecimenType) type); if(type instanceof Class) return getClassTypeFields(type); if(type instanceof ParameterizedType) return getParameterizedTypeFields((ParameterizedType) type); if(type instanceof GenericArrayType) return getGenericArrayFields((GenericArrayType) type); else if(type instanceof WildcardType) throw new UnsupportedOperationException("Wildcard types not supported"); throw new UnsupportedOperationException(String.format("Unknown Type : %s", type)); } private static GenericTypeCollection createGenericTypeNameMap(ParameterizedType parameterizedType) { Class<?> rawType = (Class) parameterizedType.getRawType(); Type[] genericArguments = parameterizedType.getActualTypeArguments(); TypeVariable[] typeParameters = rawType.getTypeParameters(); GenericType[] genericTypes = new GenericType[genericArguments.length]; for (int i = 0; i < genericArguments.length; i++) { genericTypes[i] = new GenericType(SpecimenType.of(genericArguments[i]), typeParameters[i].getName()); } return new GenericTypeCollection(genericTypes); } private static SpecimenTypeFields getGenericArrayFields(GenericArrayType type) { SpecimenTypeFields fields = new SpecimenTypeFields(); Type componentType = type.getGenericComponentType(); fields.rawType = Array.newInstance(SpecimenType.of(componentType).getRawType(), 0).getClass(); fields.genericTypeArguments = GenericTypeCollection.empty(); return fields; } private static SpecimenTypeFields getParameterizedTypeFields(ParameterizedType type) { SpecimenTypeFields fields = new SpecimenTypeFields(); fields.rawType = nonPrimitiveType(type.getRawType()); fields.genericTypeArguments = createGenericTypeNameMap(type); return fields; } private static SpecimenTypeFields getClassTypeFields(Type type) { SpecimenTypeFields fields = new SpecimenTypeFields(); fields.rawType = nonPrimitiveType(type); fields.genericTypeArguments = GenericTypeCollection.empty(); return fields; } private static SpecimenTypeFields getSpecimenTypeFields(SpecimenType type) { SpecimenTypeFields fields = new SpecimenTypeFields(); fields.rawType = type.rawType; fields.genericTypeArguments = type.genericTypeArguments; return fields; } @Override public final int hashCode() { return this.rawType.hashCode() ^ this.genericTypeArguments.hashCode(); } @Override public final boolean equals(Object obj) { if(!(obj instanceof Type)) return false; SpecimenType other; if(obj instanceof Class<?>){ Class<?> clazz = (Class<?>)obj; // Test object isn't generic so if this isn't generic either and the raw type matches then we're equal return this.genericTypeArguments.getLength() == 0 && clazz.equals(this.getRawType()); } if(obj instanceof ParameterizedType && this.genericTypeArguments.getLength() == 0) { // Test object is generic, but this type isn't so no point carrying on return false; } if(!(obj instanceof SpecimenType)) { other = SpecimenType.of((Type) obj); } else { other = (SpecimenType)obj; } return this.rawType.equals(other.rawType) && this.genericTypeArguments.equals(other.genericTypeArguments); } private static Class nonPrimitiveType(Type type) { Class clazz = (Class)type; if(!(clazz.isPrimitive())) return (Class)type; return PrimitiveTypeMap.map.get(clazz); } @Override public final String toString() { if(this.genericTypeArguments.getLength() == 0) { return justClassName(this.rawType); } StringBuilder sb = new StringBuilder(); sb.append(justClassName(this.rawType)); sb.append("<"); for(int i = 0; i < this.genericTypeArguments.getLength(); i++) { sb.append(justClassName(this.genericTypeArguments.get(i).getType())); if(i < this.genericTypeArguments.getLength() -1) sb.append(", "); } sb.append(">"); return sb.toString(); } private static String justClassName(Type type) { if(type instanceof SpecimenType) return type.toString(); if(!(type instanceof Class)) throw new RuntimeException("This shouldn't happen"); Class clazz =(Class)type; return clazz.getName(); } private static class SpecimenTypeFields { Class rawType; GenericTypeCollection genericTypeArguments; } }