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

pl.jalokim.propertiestojson.resolvers.hierarchy.HierarchyClassResolver Maven / Gradle / Ivy

There is a newer version: 5.3.0
Show newest version
package pl.jalokim.propertiestojson.resolvers.hierarchy;

import lombok.Data;
import pl.jalokim.propertiestojson.util.exception.ParsePropertiesException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

import static pl.jalokim.propertiestojson.util.ListUtil.everyElementAsNewLine;

public class HierarchyClassResolver {

    private final String ERROR_MSG = "Found %s resolvers for instance type: %s%nfound resolvers:%n%s";

    private final List> resolverClasses;

    public HierarchyClassResolver(List> typesWhichCanResolve) {
        this.resolverClasses = typesWhichCanResolve;
    }

    public Class searchResolverClass(Object instance) {
        if(instance == null) {
            return null;
        }
        final Class instanceClass = instance.getClass();
        SearchContext searchContext = new SearchContext();

        for(Class resolverClass : resolverClasses) {
            if(resolverClass.isAssignableFrom(instanceClass)) {
                if(!resolverClass.isInterface()) {
                    int difference = countDifferenceForSuperClass(resolverClass, instanceClass);
                    addToFoundWhenIsClosetsMatch(searchContext, resolverClass, difference);
                }
                if(resolverClass.isInterface()) {
                    findDifferenceInSuperClasses(searchContext, resolverClass, instanceClass);
                    findDifferenceForSuperInterfaces(searchContext, resolverClass, getSuperInterfaces(instanceClass));
                }
            }
        }

        if(searchContext.getFoundClasses().isEmpty()) {
            return null;
        }
        if(searchContext.getFoundClasses().size() == 1) {
            return searchContext.getFoundClasses().get(0);
        }

        List> foundClasses = searchContext.getFoundClasses();
         Optional> onlyObjectTypeOpt = foundClasses.stream()
                                                           .filter(type -> !type.isInterface() && !type.isEnum())
                                                           .findAny();
        if (onlyObjectTypeOpt.isPresent()) {
            if (onlyObjectTypeOpt.get() != Object.class) {
                return onlyObjectTypeOpt.get();
            }
            List> foundInterfaces = new ArrayList<>(foundClasses);
            foundInterfaces.remove(onlyObjectTypeOpt.get());

            if (foundInterfaces.size() == 1) {
                return foundInterfaces.get(0);
            }
            foundClasses = foundInterfaces;
        }

        throw new ParsePropertiesException(String.format(ERROR_MSG,
                                                         foundClasses.size(),
                                                         instance.getClass().getCanonicalName(),
                                                         everyElementAsNewLine(foundClasses)));
    }

    private void addToFoundWhenIsClosetsMatch(SearchContext searchContext, Class resolverClass, int difference) {
        if(difference < searchContext.getCurrentNearCounter()) {
            searchContext.setCurrentNearCounter(difference);
            searchContext.clear();
            searchContext.add(resolverClass);
        } else if(searchContext.getCurrentNearCounter() == difference) {
            searchContext.add(resolverClass);
        }
    }

    private int countDifferenceForSuperClass(Class resolverClass, Class instanceClass) {
        Class currentClass = instanceClass;
        int counter = 0;
        while(!resolverClass.getCanonicalName().equals(currentClass.getCanonicalName())) {
            currentClass = currentClass.getSuperclass();
            counter++;
            if (currentClass == null) {
                return Integer.MAX_VALUE;
            }
        }
        return counter;
    }

    private void findDifferenceInSuperClasses(final SearchContext searchContext,final Class resolverClass, final Class instanceClass) {
        Class currentClass = instanceClass;
        while(currentClass != null) {
            searchContext.searchHierarchyLevel++;
            currentClass = currentClass.getSuperclass();
            if (currentClass == null) {
                break;
            }
            findDifferenceForSuperInterfaces(searchContext, resolverClass, getSuperInterfaces(currentClass));
        }
        searchContext.searchHierarchyLevel = 0;
    }

    private void findDifferenceForSuperInterfaces(final SearchContext searchContext, final Class resolverClass, List> interfaceClasses) {
        searchContext.searchHierarchyLevel++;
        for(Class interfaceClass : interfaceClasses) {
            List> superInterfaces = getSuperInterfaces(interfaceClass);
            if(!superInterfaces.isEmpty()) {
                findDifferenceForSuperInterfaces(searchContext, resolverClass, superInterfaces);
            }
            if (interfaceClass.getCanonicalName().equals(resolverClass.getCanonicalName())) {
                addToFoundWhenIsClosetsMatch(searchContext, resolverClass, searchContext.searchHierarchyLevel);
            }
        }
        searchContext.searchHierarchyLevel--;
    }

    private List> getSuperInterfaces(Class type) {
        return new ArrayList<>(Arrays.asList(type.getInterfaces()));
    }


    @Data
    private static class SearchContext {
        final List> foundClasses = new ArrayList<>();
        int currentNearCounter = Integer.MAX_VALUE;
        int searchHierarchyLevel = 0;

        public void add(Class type) {
            if (!foundClasses.contains(type)) {
                foundClasses.add(type);
            }

        }

        public void clear() {
            foundClasses.clear();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy