/** * */ package ecologylab.serialization; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import ecologylab.generic.Debug; import ecologylab.platformspecifics.FundamentalPlatformSpecifics; import ecologylab.serialization.annotations.simpl_collection; import ecologylab.serialization.annotations.simpl_composite; import ecologylab.serialization.annotations.simpl_scalar; /** * This class encapsulates generic type variables declarations on classes and fields. * * Different uses of this class: * * <p> * When used for definition before 'extends' (in ClassDescriptor): name + constraintClassDescriptor * (+ constraintGenericTypeVarArgs) | constraintGenericTypeVar:<br> * name: the name of the new generic type var,<br> * constraintClassDescriptor: when the constraint is a concrete class, this holds the class * descriptor;<br> * constraintGenericTypeVar: when the constraint is another generic type var, this refers to the * definition of that generic type var;<br> * constraintGenericTypeVarArgs: when the constraint is parameterized, this holds type arguments. * </p> * * <p> * When used with field types (in FieldDescriptor): * <ul> * <li>if the field is purely generic, name + referredGenericTypeVar:<br> * name: the generic type var name used as the type,<br> * referredGenericTypeVar: refers to the definition of this generic type var in class definition.</li> * <li>if the field is parameterized, the FieldDescriptor should already have the class part of the * field type, and its genericTypeVars field should have a list of type arguments. each type * argument has classDescriptor / referredGenericTypeVar:<br> * classDescriptor: if the argument is a concrete class,<br> * referredGenericTypeVar: if the argument is parameterized or another generic type var. it should * refer to the definition of that generic type var in class definition.</li> * </ul> * </p> * * <p> * When used with base type after 'extends' (in ClassDescriptor): similar to the parameterized case * when it is used for field type. * </p> * * @author quyin * */ public class GenericTypeVar extends Debug { // The declared name of the generic type variable. such as 'M' for Media<M> test; // Wild card operator '?' will also be populated in this field. // The classDescriptor will be null if this parameter is populated. @simpl_scalar String name; // This variable holds the ClassDecriptor of the class declared as a constraint to the generic type variable. // e.g. for M, this holds ClassDescriptor<Media> in class MediaSearchResult<M extends Media>. @simpl_composite ClassDescriptor constraintClassDescriptor = null; // This variable holds the generic type variable as the constraint. // e.g. for T1, this holds a reference to the definition of T in class MyClass<T1 extends T>. @simpl_composite GenericTypeVar constraintGenericTypeVar = null; // This variable holds the args of generic type variables of a parameterized constraint. // e.g. for M, this holds references to definitions of R & S in class MediaSearchResult<M extends Media<R,S>>. @simpl_collection("generic_type_var") ArrayList<GenericTypeVar> constraintGenericTypeVarArgs = null; // ClassDescriptor of the type arg. Not used for defining a new generic type var. // e.g. ClassDescriptor<Media> in MediaSearchResult<Media>; @simpl_composite ClassDescriptor classDescriptor = null; // If the type arg is parameterized, this holds the type arguments. each element in this collection // should have a name and a reference to the definition of that generic type var used as arg. // e.g. M & T in MediaSearchResult<Media<M,T>> (in this case the field classDescriptor should be ClassDescriptor<MediaSearchResult>) @simpl_collection("generic_type_var") ArrayList<GenericTypeVar> genericTypeVarArgs = null; // Refers to another generic type var, typically the definition. // e.g. the 2nd M in class MediaSearchResult<M extends Media, M> (that GenericTypeVar object will have name=M and referredGenericTypeVar to the 1st M) // may be used in other cases. see the javadoc of this class. @simpl_composite GenericTypeVar referredGenericTypeVar = null; /** * A list of GenericTypeVars that can be referred to recursively inside this GenericTypeVar. */ ArrayList<GenericTypeVar> scope; public GenericTypeVar() { //for simpl de/serialzation } public String getName() { return name; } public void setName(String name) { this.name = name; } public ClassDescriptor getConstraintClassDescriptor() { return constraintClassDescriptor; } public void setConstraintClassDescriptor(ClassDescriptor constraintClassDescriptor) { this.constraintClassDescriptor = constraintClassDescriptor; } public GenericTypeVar getConstraintGenericTypeVar() { return constraintGenericTypeVar; } public void setConstraintGenericTypeVar(GenericTypeVar constraintGenericTypeVar) { this.constraintGenericTypeVar = constraintGenericTypeVar; } public ArrayList<GenericTypeVar> getConstraintGenericTypeVarArgs() { return constraintGenericTypeVarArgs; } public void addContraintGenericTypeVarArg(GenericTypeVar g) { if (constraintGenericTypeVarArgs == null) constraintGenericTypeVarArgs = new ArrayList<GenericTypeVar>(); constraintGenericTypeVarArgs.add(g); } public ClassDescriptor getClassDescriptor() { return classDescriptor; } public void setClassDescriptor(ClassDescriptor classDescriptor) { this.classDescriptor = classDescriptor; } public ArrayList<GenericTypeVar> getGenericTypeVarArgs() { return genericTypeVarArgs; } public void addGenericTypeVarArg(GenericTypeVar arg) { if (genericTypeVarArgs == null) genericTypeVarArgs = new ArrayList<GenericTypeVar>(); genericTypeVarArgs.add(arg); } public GenericTypeVar getReferredGenericTypeVar() { return referredGenericTypeVar; } public void setReferredGenericTypeVar(GenericTypeVar referredGenericTypeVar) { this.referredGenericTypeVar = referredGenericTypeVar; } public ArrayList<GenericTypeVar> getScope() { return scope; } /** * Creates a GenericTypeVar object as the definition of a new generic type var, from a java * reflection TypeVariable object. * * @param typeVariable * @param scope * the scope of current generic type vars; used to resolve generic type var names. * @return */ public static GenericTypeVar getGenericTypeVarDef(TypeVariable<?> typeVariable, ArrayList<GenericTypeVar> scope) { GenericTypeVar g = new GenericTypeVar(); g.scope = scope; g.name = typeVariable.getName(); // resolve constraints resolveGenericTypeVarDefinitionConstraints(g, typeVariable.getBounds()); g.scope = null; return g; } /** * Creates a GenericTypeVar object as in a type reference (usage), from a java reflection Type * object. * * @param type * @param scope * @return */ public static GenericTypeVar getGenericTypeVarRef(Type type, ArrayList<GenericTypeVar> scope) { GenericTypeVar g = new GenericTypeVar(); g.scope = scope; // case 1: arg is a concrete class if (type instanceof Class<?>) { Class<?> typeClass = (Class<?>) type; g.classDescriptor = ClassDescriptor.getClassDescriptor(typeClass); return g; } // case 2: arg is another generic type var if (type instanceof TypeVariable<?>) { TypeVariable<?> typeVar = (TypeVariable<?>) type; String argName = typeVar.getName(); if (argName != null && scope != null) { for (GenericTypeVar var : scope) if (argName.equals(var.getName())) { g.name = var.getName(); g.referredGenericTypeVar = var; break; } } } // case 3: arg is a wildcard checkTypeWildcardTypeImpl(g, type); // case 4: arg is parameterized checkTypeParameterizedTypeImpl(g, type); g.scope = null; return g; } /** * Resolves constraints on the definition of a generic type var. * * @param g * @param bounds */ public static void resolveGenericTypeVarDefinitionConstraints(GenericTypeVar g, Type[] bounds) { if (bounds == null) return; Type bound = bounds[0]; // case 1: constraint is a concrete class if (bound instanceof Class<?>) { Class<?> boundClass = (Class<?>) bound; if (Object.class != boundClass) g.constraintClassDescriptor = ClassDescriptor.getClassDescriptor(boundClass); } // case 2: constraint is another generic type var if (bound instanceof TypeVariable<?>) { TypeVariable<?> boundTypeVar = (TypeVariable<?>) bound; // look up the scope to find the bound generic type var (must have been defined) String boundName = boundTypeVar.getName(); if (boundName != null && g.scope != null) { for (GenericTypeVar var : g.scope) if (boundName.equals(var.getName())) { g.setConstraintGenericTypeVar(var); break; } } // g.addContraintGenericTypeVarArg(getGenericTypeVar(boundTypeVar)); } // case 3: constraint is parameterized -- the most complicated case checkBoundParameterizedTypeImpl(g, bound); } /** * Resolves constraints on a generic type var that is used in a type reference (usage). * * @param g * @param bounds */ public static void resolveGenericTypeVarReferenceConstraints(GenericTypeVar g, Type[] bounds) { if (bounds == null) return; Type bound = bounds[0]; // case 1: constraint is a concrete class if (bound instanceof Class<?>) { Class<?> boundClass = (Class<?>) bound; if (Object.class != boundClass) g.classDescriptor = ClassDescriptor.getClassDescriptor(boundClass); } // case 2: constraint is another generic type var if (bound instanceof TypeVariable<?>) { TypeVariable<?> boundTypeVar = (TypeVariable<?>) bound; // look up the scope to find the bound generic type var (must have been defined) String boundName = boundTypeVar.getName(); if (boundName != null && g.scope != null) { for (GenericTypeVar var : g.scope) if (boundName.equals(var.getName())) { g.setConstraintGenericTypeVar(var); break; } } } // case 3: constraint is parameterized -- the most complicated case checkBoundParameterizedTypeImpl(g, bound); } public static void checkBoundParameterizedTypeImpl (GenericTypeVar g, Type bounds) { FundamentalPlatformSpecifics.get().checkBoundParameterizedTypeImpl(g, bounds); } // added two helper functions for GenericTypeVar public static void checkTypeWildcardTypeImpl(GenericTypeVar g, Type type) { FundamentalPlatformSpecifics.get().checkTypeWildcardTypeImpl(g, type); } public static void checkTypeParameterizedTypeImpl(GenericTypeVar g, Type type) { FundamentalPlatformSpecifics.get().checkTypeParameterizedTypeImpl(g, type); } public boolean isDef() { return name != null && name.length() > 0 && (constraintClassDescriptor != null || constraintGenericTypeVar != null) && referredGenericTypeVar == null; } /** * Return a string representation of this GenericTypeVar object. The syntax is like in Java. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); if (isDef()) { sb.append(name); if (constraintGenericTypeVar != null) { sb.append(" extends ").append(constraintGenericTypeVar.name); } else if (constraintClassDescriptor != null) { sb.append(" extends ").append(constraintClassDescriptor.getDescribedClassSimpleName()); if (constraintGenericTypeVarArgs != null && constraintGenericTypeVarArgs.size() > 0) { sb.append("<"); for (int i = 0; i < constraintGenericTypeVarArgs.size(); ++i) { GenericTypeVar g = constraintGenericTypeVarArgs.get(i); sb.append(i == 0 ? "" : ",").append(g.toString()); } sb.append(">"); } } } else { if (name != null || referredGenericTypeVar != null) { sb.append(name); } else if (classDescriptor != null) { sb.append(classDescriptor.getDescribedClassSimpleName()); if (genericTypeVarArgs != null && genericTypeVarArgs.size() > 0) { sb.append("<"); for (int i = 0; i < genericTypeVarArgs.size(); ++i) { GenericTypeVar g = genericTypeVarArgs.get(i); sb.append(i == 0 ? "" : ",").append(g.toString()); } sb.append(">"); } } } // if (name != null && name.length() > 0) // sb.append(name); // // if (name != null && name != "") // sb += name; // else if (classDescriptor != null) // sb += classDescriptor.getDescribedClassSimpleName(); // // if (genericTypeVarArgs != null) // { // for (GenericTypeVar g : genericTypeVarArgs) // { // sb += "<"; // sb += g.toString(); // sb += ">"; // } // } // // if (constraintClassDescriptor != null || constraintGenericTypeVarArgs != null) // sb += " extends "; // // if (constraintClassDescriptor != null) // sb += constraintClassDescriptor.getDescribedClassSimpleName(); // // if (constraintGenericTypeVarArgs != null) // { // for (GenericTypeVar g : constraintGenericTypeVarArgs) // { // sb += "<"; // sb += g.toString(); // sb += ">"; // } // } return sb.toString(); } // these methods has been moved to the platform specific package in the corresponding project: // public static ArrayList<GenericTypeVar> getGenericTypeVars(Type parameterizedType); // public static ArrayList<GenericTypeVar> getGenericTypeVars(ParameterizedTypeImpl parameterizedType); }