package org.osgl.util; import org.osgl.$; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Provides utilities for generic relevant operations */ public class Generics { public static <T> Class<T> classOf(Type type) { if (type instanceof Class) { return $.cast(type); } else if (type instanceof ParameterizedType) { return $.cast(((ParameterizedType) type).getRawType()); } else { throw new IllegalArgumentException("Cannot find class from type: " + type); } } /** * Returns implementation of type parameters declared in the root class/interface by the given sub class * <p> * For example, suppose a super class has generic type params: `MyBase<MODEL, QUERY>`, and a * sub class is declared as `MyModel<MyModel, MyQuery>`, then passing `MyModel.class` to * this method shall return a list of `{MyModel.class, MyQuery.class}` * <p> * * If the specified class doesn't have generic type declared then it shall return * an empty list * * * @param theClass the end class * @param rootClass the root class or interface * @return a list of type variable implementation on root class */ public static List<Type> typeParamImplementations(Class theClass, Class rootClass) { if (rootClass.getTypeParameters().length == 0) { return C.list(); } try { return typeParamImplementations(theClass, rootClass, new ArrayList<Type>()); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(S.fmt("Cannot infer type parameter implementation on %s against %s", theClass.getName(), rootClass.getName()), e); } } /** * Build class type variable name and type variable implementation lookup * @param theClass the class to build the lookup * @return the lookup */ public static Map<String, Class> buildTypeParamImplLookup(Class theClass) { Map<String, Class> lookup = new HashMap<>(); buildTypeParamImplLookup(theClass, lookup); return lookup; } public static void buildTypeParamImplLookup(Class theClass, Map<String, Class> lookup) { if (null == theClass || Object.class == theClass) { return; } // what type variable of the super class implemented by this class Type superType = theClass.getGenericSuperclass(); if (superType instanceof ParameterizedType) { ParameterizedType ptype = $.cast(superType); Type[] typeParams = ptype.getActualTypeArguments(); TypeVariable[] typeArgs = theClass.getSuperclass().getTypeParameters(); int len = typeParams.length; for (int i = 0; i < len; ++i) { Type typeParam = typeParams[i]; if (typeParam instanceof Class) { TypeVariable typeVar = typeArgs[i]; lookup.put(typeVar.getName(), (Class) typeParam); } else if (typeParam instanceof TypeVariable) { TypeVariable var = $.cast(typeParam); String name = var.getName(); Class impl = lookup.get(name); TypeVariable typeVar = typeArgs[i]; if (null != impl) { lookup.put(typeVar.getName(), impl); } else { Type[] ta = var.getBounds(); if (null != ta && ta.length == 1) { Type bound = ta[0]; if (bound instanceof Class) { lookup.put(typeVar.getName(), (Class) bound); } } } } } } buildTypeParamImplLookup(theClass.getSuperclass(), lookup); } private static List<Type> typeParamImplementations(Class theClass, Class rootClass, List<Type> subClassTypeParams) { Type superType = null; Type[] interfaces = theClass.getGenericInterfaces(); for (Type intf : interfaces) { if (intf instanceof ParameterizedType) { Type rawType = ((ParameterizedType) intf).getRawType(); if ($.eq(rawType, rootClass)) { superType = intf; } } } if (null == superType) { superType = theClass.getGenericSuperclass(); } Class superClass = null; while (!(superType instanceof ParameterizedType) && Object.class != superType) { if (null == superClass) { superClass = theClass.getSuperclass(); } superType = superClass.getGenericSuperclass(); superClass = superClass.getSuperclass(); } if (superType instanceof ParameterizedType) { TypeVariable<Class>[] declaredTypeVariables = theClass.getTypeParameters(); ParameterizedType pSuperType = $.cast(superType); Type[] superTypeParams = pSuperType.getActualTypeArguments(); List<Type> nextList = new ArrayList<Type>(); for (Type stp: superTypeParams) { if (stp instanceof Class || stp instanceof ParameterizedType) { nextList.add(stp); } else if (stp instanceof TypeVariable) { boolean found = false; for (int i = 0; i < declaredTypeVariables.length; ++i) { TypeVariable declared = declaredTypeVariables[i]; if ($.eq(declared, stp)) { nextList.add(subClassTypeParams.get(i)); found = true; } } E.illegalStateIf(!found, "Cannot find type implementation for %s", theClass); } } superClass = (Class) pSuperType.getRawType(); if ($.eq(superClass, rootClass)) { return nextList; } return typeParamImplementations(superClass, rootClass, nextList); } throw E.unexpected("Cannot find type param implementation: super type %s of %s is not a parameterized type", superType, theClass); } public static void main(String[] args) { Class c = Serializable.class; System.out.println(c.getGenericSuperclass()); System.out.println($.toString2(c.getGenericInterfaces())); } }