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

io.yawp.repository.scanner.EndpointTree Maven / Gradle / Ivy

There is a newer version: 2.08alpha
Show newest version
package io.yawp.repository.scanner;

import io.yawp.commons.utils.ReflectionUtils;
import io.yawp.repository.actions.Action;
import io.yawp.repository.actions.ActionKey;
import io.yawp.repository.actions.ActionMethod;
import io.yawp.repository.actions.InvalidActionMethodException;
import io.yawp.repository.hooks.Hook;
import io.yawp.repository.pipes.Pipe;
import io.yawp.repository.shields.Shield;
import io.yawp.repository.shields.ShieldInfo;
import io.yawp.repository.transformers.Transformer;

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

public class EndpointTree {

    private Class endpointClazz;

    private FeatureTree actionTree = new FeatureTree<>(Action.class);

    private FeatureTree hookTree = new FeatureTree<>(Hook.class);

    private FeatureTree transformerTree = new FeatureTree<>(Transformer.class);

    private FeatureTree shieldTree = new FeatureTree<>(Shield.class);

    private FeatureTree pipeTree = new FeatureTree<>(Pipe.class);

    private List> pipesSink = new ArrayList<>();

    public EndpointTree(Class endpointClazz) {
        this.endpointClazz = endpointClazz;
    }

    public void addAction(Class actionClazz) {
        actionTree.add(actionClazz);
    }

    public void addTransformer(Class transformerClazz) {
        transformerTree.add(transformerClazz);
    }

    public void addHook(Class hookClazz) {
        hookTree.add(hookClazz);
    }

    public void addShield(Class shieldClazz) {
        shieldTree.add(shieldClazz);
    }

    public void addPipe(Class pipeClazz) {
        pipeTree.add(pipeClazz);
    }

    public void addPipeSink(Class pipeClazz) {
        pipesSink.add(pipeClazz);
    }

    public Map loadActions(Map, Map> cache) {
        Map map = new HashMap<>();

        for (Class actionClazz : actionTree.getLeafs()) {
            addActionKeys(map, actionClazz, cache);
        }

        return map;
    }

    public Map loadTransformers(Map, Map> cache) {
        Map map = new HashMap<>();

        for (Class transformerClazz : transformerTree.getLeafs()) {
            addTransformerMethods(map, transformerClazz, cache);
        }

        return map;
    }

    public Set> loadHooks() {
        return hookTree.getLeafs();
    }

    public  ShieldInfo loadShield() {
        Set> shieldClazzes = shieldTree.getLeafs();
        if (shieldClazzes.size() == 0) {
            return null;
        }

        if (shieldClazzes.size() > 1) {
            throwExceptionMultipleShields(shieldClazzes);
        }
        return new ShieldInfo((Class>) shieldClazzes.iterator().next());
    }

    public Set> loadPipes() {
        return pipeTree.getLeafs();
    }

    public List> loadPipesSink() {
        return pipesSink;
    }

    private void throwExceptionMultipleShields(Set> shieldClazzes) {
        throw new RuntimeException(String.format("Trying to add multiple shields for endpoint '%s' -> %s",
                endpointClazz.getName(),
                createShieldsString(shieldClazzes)));
    }

    private String createShieldsString(Set> shieldClazzes) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Class shieldClazz : shieldClazzes) {
            if (!first) {
                sb.append(", ");
            } else {
                first = false;
            }
            sb.append(shieldClazz.getName());
        }
        return sb.toString();
    }

    private void addTransformerMethods(Map map, Class transformerClazz, Map, Map> cache) {

        if (cache.containsKey(transformerClazz)) {
            map.putAll(cache.get(transformerClazz));
            return;
        }

        Map addToCache = new HashMap<>();
        cache.put(transformerClazz, addToCache);

        for (Method method : ReflectionUtils.getPublicMethodsRecursively(transformerClazz, Transformer.class)) {
            String name = method.getName();

            if (isTransformerOverriden(name, addToCache)) {
                continue;
            }

            assertTransformerNotDuplicated(map, name, method);
            map.put(name, method);
            addToCache.put(name, method);
        }
    }

    private boolean isTransformerOverriden(String name, Map addToCache) {
        return addToCache.containsKey(name);
    }

    private void assertTransformerNotDuplicated(Map map, String name, Method method) {
        if (map.containsKey(name)) {
            Method existingMethod = map.get(name);

            if (method.equals(existingMethod)) {
                return;
            }

            throw new RuntimeException("Trying to add two transformers with the same name '" + name + "' to "
                    + endpointClazz.getName() + ": one at " + existingMethod.getDeclaringClass().getName() + " and the other at "
                    + method.getDeclaringClass().getName());
        }
    }

    private void addActionKeys(Map map, Class actionClazz, Map, Map> cache) {

        if (cache.containsKey(actionClazz)) {
            map.putAll(cache.get(actionClazz));
            return;
        }

        Map addToCache = new HashMap<>();
        cache.put(actionClazz, addToCache);

        for (Method method : ReflectionUtils.getPublicMethodsRecursively(actionClazz, Action.class)) {
            //for (Method method : actionClazz.getDeclaredMethods()) {
            if (!ActionMethod.isAction(method)) {
                continue;
            }

            ActionMethod actionMethod = createActionMethod(method);
            List actionKeys = actionMethod.getActionKeys();

            for (ActionKey actionKey : actionKeys) {
                if (isActionOverriden(actionKey, addToCache)) {
                    continue;
                }

                assertActionNotDuplicated(map, actionKey, method);
                map.put(actionKey, actionMethod);
                addToCache.put(actionKey, actionMethod);
            }
        }
    }

    private boolean isActionOverriden(ActionKey actionKey, Map addToCache) {
        return addToCache.containsKey(actionKey);
    }

    private void assertActionNotDuplicated(Map map, ActionKey actionKey, Method method) {
        if (map.get(actionKey) != null) {
            Method existingMethod = map.get(actionKey).getMethod();

            if (method.equals(existingMethod)) {
                return;
            }

            throw new RuntimeException("Trying to add two actions with the same name '" + actionKey + "' to "
                    + endpointClazz.getName() + ": one at " + existingMethod.getDeclaringClass().getName() + " and the other at "
                    + method.getDeclaringClass().getName());
        }
    }

    private ActionMethod createActionMethod(Method method) {
        try {
            return new ActionMethod(method);
        } catch (InvalidActionMethodException e) {
            throw new RuntimeException("Invalid Action: " + method.getDeclaringClass().getName() + "." + method.getName(), e);
        }
    }
}