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

com.wizzardo.tools.reflection.Generic Maven / Gradle / Ivy

There is a newer version: 0.23
Show newest version
package com.wizzardo.tools.reflection;

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

import static com.wizzardo.tools.reflection.Misc.*;

/**
 * @author: wizzardo
 * Date: 2/21/14
 */
public class Generic {
    public final Class clazz;
    public final G parent;
    protected Map types;
    protected F fields;
    protected final G[] typeParameters;
    protected Generic[] interfaces;

    public static  Generic of(Class clazz) {
        return new Generic(clazz);
    }

    protected Generic(Class c, G parent, G[] typeParameters) {
        this.clazz = c;
        this.parent = parent;
        this.typeParameters = typeParameters;
    }

    public Generic(Type c) {
        this(c, (Map) null);
    }

    public Generic(Class c, Class... generics) {
        clazz = c;
        parent = null;
        if (generics == null) {
            typeParameters = createArray(0);
        } else {
            typeParameters = createArray(generics.length);
            for (int i = 0; i < generics.length; i++) {
                typeParameters[i] = create(generics[i]);
            }
        }

        types = getTypes(c, typeParameters);

        Type[] interfaces = clazz.getGenericInterfaces();
        this.interfaces = createArray(interfaces.length);
        initInterfaces(this.interfaces, interfaces, types, new HashMap>());
    }

    public Generic(Class c, G... generics) {
        clazz = c;
        parent = null;
        if (generics == null)
            typeParameters = createArray(0);
        else
            typeParameters = generics;

        types = getTypes(c, typeParameters);

        Type[] interfaces = clazz.getGenericInterfaces();
        this.interfaces = createArray(interfaces.length);
        initInterfaces(this.interfaces, interfaces, types, new HashMap>());
    }

    protected Generic(Type c, Map types) {
        this(c, types, new HashMap>());
    }

    protected Generic(Type c, Map types, Map> cyclicDependencies) {
        if (c instanceof ParameterizedType) {
            ParameterizedType type = (ParameterizedType) c;
            clazz = (Class) type.getRawType();
            Type[] args = type.getActualTypeArguments();
            TypeVariable>[] variables = clazz.getTypeParameters();

            this.types = new HashMap();
            this.typeParameters = createArray(args.length);
            for (int i = 0; i < args.length; i++) {
                this.typeParameters[i] = create(args[i], types, cyclicDependencies);
                this.types.put(variables[i].getName(), this.typeParameters[i]);
            }

            if (clazz.getGenericSuperclass() != null)
                parent = create(clazz.getGenericSuperclass(), this.types, cyclicDependencies);
            else
                parent = null;

            types = this.types;
        } else if (c instanceof TypeVariable) {
            if (types != null) {
                TypeVariable variable = (TypeVariable) c;
                G g = types.get(variable.getName());
                if (g == null) {
                    Type type = variable.getBounds()[0];
                    g = create(type, types, cyclicDependencies);
                }

                clazz = g.clazz;
                parent = (G) g.parent;
                typeParameters = (G[]) g.typeParameters;
                interfaces = g.interfaces;
                this.types = g.types;
                return;
            } else {
                clazz = (Class) Object.class;
                parent = null;
                typeParameters = createArray(0);
            }
        } else if (c instanceof GenericArrayType) {
            parent = null;
            clazz = (Class) Array.class;
            typeParameters = createArray(1);
            typeParameters[0] = create(((GenericArrayType) c).getGenericComponentType());
        } else if (c instanceof WildcardType) {
            WildcardType wildcardType = (WildcardType) c;
            Type type = wildcardType.getLowerBounds().length > 0 ? wildcardType.getLowerBounds()[0] : wildcardType.getUpperBounds()[0];
            Generic g = create(type);
            clazz = g.clazz;
            typeParameters = g.typeParameters;
            parent = g.parent;
            interfaces = g.interfaces;
            return;
        } else {
            Class cl = (Class) c;
            if (cl.isArray()) {
                clazz = (Class) Array.class;
                typeParameters = createArray(1);
                typeParameters[0] = create(cl.getComponentType());
                parent = null;
                interfaces = new Generic[0];
                return;
            }
            Generic cd = cyclicDependencies.get(cl);
            if (cd != null) {
                clazz = cd.clazz;
                parent = cd.parent;
                interfaces = cd.interfaces;
                typeParameters = cd.typeParameters;
                return;
            }

            clazz = cl;
            this.typeParameters = createArray(0);
            if (!clazz.isEnum() && clazz.getGenericSuperclass() != null)
                parent = create(clazz.getGenericSuperclass(), types, cyclicDependencies);
            else
                parent = null;

            cyclicDependencies.put(cl, this);
        }

        Type[] interfaces = clazz.getGenericInterfaces();
        this.interfaces = createArray(interfaces.length);
        initInterfaces(this.interfaces, interfaces, types, cyclicDependencies);
    }

    protected void initInterfaces(Generic[] result, Type[] interfaces, Map types, Map> cyclicDependencies) {
        for (int i = 0; i < interfaces.length; i++) {
            result[i] = create(interfaces[i], types, cyclicDependencies);
        }
    }

    private Map getTypes(Class c, G[] generics) {
        TypeVariable>[] variables = c.getTypeParameters();
        if (variables.length == 0 || generics.length == 0)
            return null;

        Map types = new HashMap();
        for (int i = 0; i < variables.length && i < generics.length; i++) {
            types.put(variables[i].getName(), generics[i]);
        }
        return types;
    }

    public int typesCount() {
        return typeParameters.length;
    }

    public int getTypesCount() {
        return typesCount();
    }

    public G type(int i) {
        return typeParameters[i];
    }

    public G getType(int i) {
        return type(i);
    }

    public G type(String name) {
        return types.get(name);
    }

    public G getType(String name) {
        return type(name);
    }

    public Map types() {
        if (types == null)
            return Collections.emptyMap();

        return Collections.unmodifiableMap(types);
    }

    public Map getTypes() {
        return types();
    }

    public int getInterfacesCount() {
        return interfaces.length;
    }

    public Generic getInterface(int i) {
        return interfaces[i];
    }

    @Override
    public String toString() {
        if (typesCount() == 0)
            return clazz.getSimpleName();

        return join(new StringBuilder(32)
                        .append(clazz.getSimpleName())
                        .append('<'),
                typeParameters, ",")
                .append('>')
                .toString();
    }

    public G getGenericType(Field f) {
        Generic g = this;
        while (g != null && g.clazz != f.getDeclaringClass()) {
            g = g.parent;
        }
        if (g != null && f.getGenericType() instanceof TypeVariable) {
            return (G) g.types.get(((TypeVariable) f.getGenericType()).getName());
        }
        return null;
    }

    public F getFields() {
        if (fields != null)
            return fields;

        fields = (F) new Fields(this);
        return fields;
    }

    protected G[] createArray(int size) {
        return (G[]) new Generic[size];
    }

    protected G create(Type c) {
        return (G) new Generic(c);
    }

    protected G create(Class c, Class... generics) {
        return (G) new Generic(c, generics);
    }

    protected G create(Type c, Map types) {
        return (G) new Generic(c, types);
    }

    protected G create(Type c, Map types, Map> cyclicDependencies) {
        return (G) new Generic(c, types, cyclicDependencies);
    }

    public G parent() {
        return parent;
    }

    public List methods() {
        List methods = new ArrayList();

        fillMethods(methods, this);

        return methods;
    }

    protected void fillMethods(List methods, Generic generic) {
        Method[] declaredMethods = generic.clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            Generic returnType = new Generic(method.getGenericReturnType(), generic.types);
            Type[] genericParameterTypes = method.getGenericParameterTypes();
            List args = new ArrayList(genericParameterTypes.length);
            for (Type genericParameterType : genericParameterTypes) {
                args.add(new Generic(genericParameterType, generic.types));
            }
            methods.add(new GenericMethod(method, returnType, Collections.unmodifiableList(args)));
        }

        for (Generic g : generic.interfaces) {
            fillMethods(methods, g);
        }

        if (generic.parent != null)
            fillMethods(methods, generic.parent);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Generic generic = (Generic) o;

        if (!clazz.equals(generic.clazz)) return false;
        return Arrays.equals(typeParameters, generic.typeParameters);
    }

    @Override
    public int hashCode() {
        int result = clazz.hashCode();
        result = 31 * result + Arrays.hashCode(typeParameters);
        return result;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy