io.yawp.repository.scanner.EndpointTree Maven / Gradle / Ivy
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 extends Action> actionClazz) {
actionTree.add(actionClazz);
}
public void addTransformer(Class extends Transformer> transformerClazz) {
transformerTree.add(transformerClazz);
}
public void addHook(Class extends Hook> hookClazz) {
hookTree.add(hookClazz);
}
public void addShield(Class extends Shield> shieldClazz) {
shieldTree.add(shieldClazz);
}
public void addPipe(Class extends Pipe> pipeClazz) {
pipeTree.add(pipeClazz);
}
public void addPipeSink(Class extends Pipe> pipeClazz) {
pipesSink.add(pipeClazz);
}
public Map loadActions(Map, Map> cache) {
Map map = new HashMap<>();
for (Class extends Action> actionClazz : actionTree.getLeaves()) {
addActionKeys(map, actionClazz, cache);
}
return map;
}
public Map loadTransformers(Map, Map> cache) {
Map map = new HashMap<>();
for (Class extends Transformer> transformerClazz : transformerTree.getLeaves()) {
addTransformerMethods(map, transformerClazz, cache);
}
return map;
}
public Set> loadHooks() {
return hookTree.getLeaves();
}
public ShieldInfo loadShield() {
Set> shieldClazzes = shieldTree.getLeaves();
if (shieldClazzes.size() == 0) {
return null;
}
if (shieldClazzes.size() > 1) {
throwExceptionMultipleShields(shieldClazzes);
}
return new ShieldInfo((Class extends Shield super TT>>) shieldClazzes.iterator().next());
}
public Set> loadPipes() {
return pipeTree.getLeaves();
}
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 extends Shield> shieldClazz : shieldClazzes) {
if (!first) {
sb.append(", ");
} else {
first = false;
}
sb.append(shieldClazz.getName());
}
return sb.toString();
}
private void addTransformerMethods(Map map, Class extends Transformer> 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 extends Action> 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);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy