All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.osgl.util.Generics Maven / Gradle / Ivy

The newest version!
package org.osgl.util;

/*-
 * #%L
 * Java Tool
 * %%
 * Copyright (C) 2014 - 2017 OSGL (Open Source General Library)
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import org.osgl.$;
import org.osgl.exception.UnexpectedException;

import java.io.Serializable;
import java.lang.reflect.*;
import java.util.*;

/**
 * Provides utilities for generic relevant operations
 */
public class Generics {

    public static  Class 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
     * 

* 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}` *

*

* 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 typeParamImplementations(Class theClass, Class rootClass) { if (rootClass.getTypeParameters().length == 0) { return C.list(); } try { return typeParamImplementations(theClass, rootClass, new ArrayList(), true); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(S.fmt("Cannot infer type parameter implementation on %s against %s", theClass.getName(), rootClass.getName()), e); } } /** * Same function with {@link #typeParamImplementations(Class, Class)}. * * However this method will return an empty list instead of throwing out exception when * type parameter not found. * * @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 tryGetTypeParamImplementations(Class theClass, Class rootClass) { if (rootClass.getTypeParameters().length == 0) { return C.list(); } try { return typeParamImplementations(theClass, rootClass, new ArrayList(), false); } catch (IndexOutOfBoundsException e) { throw new IllegalArgumentException(S.fmt("Cannot infer type parameter implementation on %s against %s", theClass.getName(), rootClass.getName()), e); } } public static Map subLookup(Map lookup, String prefix) { if (S.blank(prefix)) { return lookup; } if (!prefix.endsWith(".")) { prefix += "."; } Map subLookup = new HashMap<>(); if (null == lookup) { return subLookup; } for (Map.Entry entry : lookup.entrySet()) { String key = entry.getKey(); if (key.startsWith(prefix)) { subLookup.put(key.substring(prefix.length()), entry.getValue()); } } return subLookup; } /** * Build class type variable name and type variable implementation lookup * * @param theClass the class to build the lookup * @return the lookup */ public static Map buildTypeParamImplLookup(Class theClass) { Map lookup = new HashMap<>(); buildTypeParamImplLookup(theClass, lookup); return lookup; } public static void buildTypeParamImplLookup(Class theClass, Map 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(); buildTypeParamImplLookup("", typeParams, typeArgs, lookup); } buildTypeParamImplLookup(theClass.getSuperclass(), lookup); } public static void buildTypeParamImplLookup(String prefix, Type[] typeParams, TypeVariable[] typeArgs, Map lookup) { 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(lookupKey(typeVar, prefix), (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(lookupKey(typeVar, prefix), impl); } else { Type[] ta = var.getBounds(); if (null != ta && ta.length == 1) { Type bound = ta[0]; if (bound instanceof Class) { lookup.put(lookupKey(typeVar, prefix), (Class) bound); } } } // cascade populate nested type lookup pairs Map subLookup = subLookup(lookup, name); if (!subLookup.isEmpty()) { String newPrefix = typeVar.getName() + "."; for (Map.Entry entry : subLookup.entrySet()) { lookup.put(newPrefix + entry.getKey(), entry.getValue()); } } } else if (typeParam instanceof ParameterizedType) { ParameterizedType ptype0 = (ParameterizedType) typeParam; TypeVariable typeVar = typeArgs[i]; Type rawType = ptype0.getRawType(); if (rawType instanceof Class) { lookup.put(lookupKey(typeVar, prefix), (Class) rawType); buildTypeParamImplLookup(lookupKey(typeVar, prefix), ptype0.getActualTypeArguments(), ((Class) rawType).getTypeParameters(), lookup); } else { throw new UnexpectedException("Unknown typeParam: " + ptype0); } } } } private static String lookupKey(TypeVariable typeVar, String prefix) { return S.isBlank(prefix) ? typeVar.getName() : S.concat(prefix, ".", typeVar.getName()); } /** * Return the real return type of a method. * * Normally it returns {@link Method#getReturnType()} * * In case a method is declared in a super type and the return type is declared as a generic {@link TypeVariable}, * when a sub class with type variable implementation presented, then it shall return the implementation type. E.g * * Super type: * * ```java * public abstract class Foo { * T getFoo(); * } * ``` * * Sub type: * * ```java * public class StringFoo extends Foo { * String getFoo() {return "foo";} * } * ``` * * Usage of `getReturnType`: * * ```java * Method method = Foo.class.getMethod("getFoo"); * Class realReturnType = Generics.getReturnType(method, StringFoo.class); // return String.class * ``` * * @param method the method * @param theClass the class on which the method is invoked * @return the return type */ public static Class getReturnType(Method method, Class theClass) { Type type = method.getGenericReturnType(); if (type == Class.class) { return $.cast(type); } if (type instanceof TypeVariable) { Map lookup = Generics.buildTypeParamImplLookup(theClass); String name = ((TypeVariable) type).getName(); Class realType = lookup.get(name); if (null != realType) { return realType; } } return method.getReturnType(); } private static List typeParamImplementations(Class theClass, Class rootClass, List subClassTypeParams, boolean raiseExceptionIfNotFound) { 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; boolean theClassIsInterface = theClass.isInterface(); while (!(superType instanceof ParameterizedType) && Object.class != superType) { if (theClassIsInterface) { try { if (null == superClass) { superClass = theClass; } Type[] types = superClass.getGenericInterfaces(); if (types.length == 0) { break; } superType = types[0]; Class[] intfs = theClass.getInterfaces(); superClass = intfs[0]; } catch (RuntimeException e) { throw new UnexpectedException("Cannot find type implementation for %s", theClass); } } else { if (null == superClass) { superClass = theClass.getSuperclass(); } superType = superClass.getGenericSuperclass(); superClass = superClass.getSuperclass(); } } if (superType instanceof ParameterizedType) { TypeVariable[] declaredTypeVariables = theClass.getTypeParameters(); ParameterizedType pSuperType = $.cast(superType); Type[] superTypeParams = pSuperType.getActualTypeArguments(); List nextList = new ArrayList<>(); 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) { if (subClassTypeParams.size() <= i) { break; } TypeVariable declared = declaredTypeVariables[i]; if ($.eq(declared, stp)) { nextList.add(subClassTypeParams.get(i)); found = true; break; } } if (!found) { if (raiseExceptionIfNotFound) { E.illegalArgumentIf(!found, "Cannot find type implementation for %s", theClass); } return C.list(); } } } superClass = (Class) pSuperType.getRawType(); if ($.eq(superClass, rootClass)) { return nextList; } return typeParamImplementations(superClass, rootClass, nextList, raiseExceptionIfNotFound); } if (raiseExceptionIfNotFound) { throw E.unexpected("Cannot find type param implementation: super type %s of %s is not a parameterized type", superType, theClass); } return C.list(); } public static void main(String[] args) { Class c = Serializable.class; System.out.println(c.getGenericSuperclass()); System.out.println($.toString2(c.getGenericInterfaces())); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy