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

name.remal.reflection.HierarchyUtils Maven / Gradle / Ivy

package name.remal.reflection;

import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isPrivate;
import static java.lang.reflect.Modifier.isStatic;
import static java.util.Collections.singletonList;
import static name.remal.ArrayUtils.indexOf;
import static name.remal.UncheckedCast.uncheckedCast;

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import name.remal.gradle_plugins.api.RelocateClasses;
import name.remal.reflection.ClassLoaderUtils.ClassLoaderWrapper;
import org.jetbrains.annotations.NotNull;

@RelocateClasses(TypeToken.class)
public class HierarchyUtils {

    public static int compareByHierarchySize(@NotNull Class class1, @NotNull Class class2) {
        return Integer.compare(getHierarchy(class1).size(), getHierarchy(class2).size());
    }

    @NotNull
    public static List<@NotNull Package> getPackageHierarchy(@NotNull Class clazz) {
        List result = new ArrayList<>();

        ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper(clazz.getClassLoader());

        String name = clazz.getName();
        int lastDotPos;
        while (0 < (lastDotPos = name.lastIndexOf('.'))) {
            name = name.substring(0, lastDotPos);
            Package pckg = classLoaderWrapper.getPackageOrNull(name);
            if (pckg != null) result.add(pckg);
        }

        return result;
    }

    @NotNull
    public static  List<@NotNull Class> getSuperClassesHierarchy(@NotNull Class clazz) {
        List> result = new ArrayList<>();
        Class curClass = clazz;
        while (curClass != null) {
            result.add(curClass);
            if (Object.class == curClass) break;
            curClass = curClass.getSuperclass();
        }
        return result;
    }

    @NotNull
    public static  List<@NotNull Class> getHierarchy(@NotNull Class clazz) {
        Set> hierarchy = new LinkedHashSet<>();

        Queue> queue = new LinkedList<>();
        queue.add(clazz);
        while (true) {
            Class curClass = queue.poll();
            if (curClass == null) break;

            if (hierarchy.add(curClass)) {
                Class superclass = curClass.getSuperclass();
                if (superclass != null) {
                    queue.add(superclass);
                }

                for (Class interfaceClass : curClass.getInterfaces()) {
                    queue.add(uncheckedCast(interfaceClass));
                }
            }
        }

        return new ArrayList<>(hierarchy);
    }

    @NotNull
    public static  List<@NotNull Type> getGenericHierarchy(@NotNull Class clazz) {
        TypeToken typeToken = TypeToken.of(clazz);
        List> hierarchy = getHierarchy(clazz);
        List result = new ArrayList<>(hierarchy.size());
        for (Class parentClass : hierarchy) {
            result.add(typeToken.getSupertype(parentClass).getType());
        }
        return result;
    }

    public static boolean canBeOverridden(@NotNull Method method) {
        if (isStatic(method.getModifiers())) return false;
        if (isPrivate(method.getModifiers())) return false;
        if (isFinal(method.getModifiers())) return false;
        if (isFinal(method.getDeclaringClass().getModifiers())) return false;
        return true;
    }

    @NotNull
    public static List<@NotNull Method> getHierarchy(@NotNull Method method) {
        if (isPrivate(method.getModifiers()) || isStatic(method.getModifiers())) return singletonList(method);

        Class declaringClass = method.getDeclaringClass();
        if (declaringClass.getSuperclass() == null && 0 == declaringClass.getInterfaces().length) return singletonList(method);

        List result = new ArrayList<>();
        result.add(method);

        final List> hierarchyWithoutDeclaring;
        {
            List> hierarchy = uncheckedCast(getHierarchy(declaringClass));
            hierarchyWithoutDeclaring = hierarchy.subList(1, hierarchy.size());
        }

        if (0 == method.getParameterCount()) {
            for (Class clazz : hierarchyWithoutDeclaring) {
                for (Method curMethod : clazz.getDeclaredMethods()) {
                    if (curMethod.isSynthetic()) continue;
                    if (!canBeOverridden(curMethod)) continue;
                    if (curMethod.getParameterCount() != method.getParameterCount()) continue;
                    if (!Objects.equals(curMethod.getName(), method.getName())) continue;
                    result.add(curMethod);
                    break;
                }
            }

        } else {
            TypeToken typeToken = TypeToken.of(declaringClass);
            for (Class clazz : hierarchyWithoutDeclaring) {
                TypeToken classTypeToken = null;
                for (Method curMethod : clazz.getDeclaredMethods()) {
                    if (curMethod.isSynthetic()) continue;
                    if (!canBeOverridden(curMethod)) continue;
                    if (curMethod.getParameterCount() != method.getParameterCount()) continue;
                    if (!Objects.equals(curMethod.getName(), method.getName())) continue;

                    boolean areParamsMatch = true;
                    //noinspection ConstantConditions
                    if (classTypeToken == null) {
                        classTypeToken = typeToken.getSupertype(uncheckedCast(clazz));
                    }
                    for (int i = 0; i < curMethod.getParameterCount(); ++i) {
                        Class methodClass = method.getParameterTypes()[i];
                        Type curType = curMethod.getGenericParameterTypes()[i];
                        TypeToken curTypeToken = classTypeToken.resolveType(curType);
                        Class curClass = curTypeToken.getRawType();
                        if (!Objects.equals(methodClass, curClass)) {
                            areParamsMatch = false;
                            break;
                        }
                    }

                    if (areParamsMatch) {
                        result.add(curMethod);
                        break;
                    }
                }
            }
        }

        return result;
    }

    public static boolean isOverriddenBy(@NotNull Method parentMethod, @NotNull Method childMethod) {
        if (Objects.equals(parentMethod, childMethod)) return true;
        if (!canBeOverridden(parentMethod)) return false;
        if (isStatic(childMethod.getModifiers()) || isPrivate(childMethod.getModifiers())) return false;
        if (parentMethod.getParameterCount() != childMethod.getParameterCount()) return false;
        if (!Objects.equals(parentMethod.getName(), childMethod.getName())) return false;
        if (!parentMethod.getDeclaringClass().isAssignableFrom(childMethod.getDeclaringClass())) return false;

        if (0 == parentMethod.getParameterCount()) return true;

        if (!hasGenericParameters(parentMethod) && !hasGenericParameters(childMethod)) {
            return Arrays.equals(parentMethod.getParameterTypes(), childMethod.getParameterTypes());
        }

        TypeToken childTypeToken = TypeToken.of(childMethod.getDeclaringClass());
        TypeToken parentTypeToken = childTypeToken.getSupertype(uncheckedCast(parentMethod.getDeclaringClass()));
        for (int i = 0; i < parentMethod.getParameterCount(); ++i) {
            if (!Objects.equals(
                childMethod.getParameterTypes()[i],
                parentTypeToken.resolveType(parentMethod.getGenericParameterTypes()[i]).getRawType()
            )) {
                return false;
            }
        }
        return true;
    }

    private static boolean hasGenericParameters(@NotNull Method method) {
        for (Type type : method.getGenericParameterTypes()) {
            if (!(type instanceof Class)) return true;
        }
        return false;
    }

    @NotNull
    public static List<@NotNull Parameter> getHierarchy(@NotNull Parameter parameter) {
        List result = new ArrayList<>();
        result.add(parameter);

        Executable declaringExecutable = parameter.getDeclaringExecutable();
        if (declaringExecutable instanceof Method) {
            List hierarchy = getHierarchy((Method) declaringExecutable);
            if (2 <= hierarchy.size()) {
                int index = indexOf(declaringExecutable.getParameters(), parameter);
                for (Method method : hierarchy.subList(1, hierarchy.size())) {
                    result.add(method.getParameters()[index]);
                }
            }
        }

        return result;
    }

    @NotNull
    public static List<@NotNull Method> getAllNotOverriddenMethods(@NotNull Class type) {
        List result = new ArrayList<>();
        for (Class curClass : getHierarchy(type)) {
            List methodsToAdd = new ArrayList<>();
            for (Method method : curClass.getDeclaredMethods()) {
                boolean isOverridden = false;
                if (canBeOverridden(method)) {
                    for (Method methodToCheck : result) {
                        if (isOverriddenBy(method, methodToCheck)) {
                            isOverridden = true;
                            break;
                        }
                    }
                }
                if (!isOverridden) {
                    methodsToAdd.add(method);
                }
            }
            result.addAll(methodsToAdd);
        }
        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy