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

io.yawp.repository.EndpointScanner Maven / Gradle / Ivy

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

import io.yawp.commons.utils.ReflectionUtils;
import io.yawp.repository.actions.Action;
import io.yawp.repository.actions.ActionKey;
import io.yawp.repository.actions.InvalidActionMethodException;
import io.yawp.repository.annotations.Endpoint;
import io.yawp.repository.hooks.Hook;
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.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.reflections.Reflections;

@SuppressWarnings({ "rawtypes", "unchecked" })
public final class EndpointScanner {

	private final static Logger LOGGER = Logger.getLogger(EndpointScanner.class.getName());

	private boolean enableHooks;

	private Reflections endpointsPackage;

	private Map, EndpointFeatures> endpoints;

	public EndpointScanner(String packagePrefix) {
		endpointsPackage = new Reflections(packagePrefix);
		endpoints = new HashMap<>();
		enableHooks = true;
	}

	public RepositoryFeatures scan() {
		long start = System.currentTimeMillis();
		RepositoryFeatures repositoryFeatures = new RepositoryFeatures(generateEndpointsMap());
		long elapsed = System.currentTimeMillis() - start;
		LOGGER.info("Yawp! started in " + elapsed + " ms");
		return repositoryFeatures;
	}

	private void scanEndpoints() {
		Set> clazzes = endpointsPackage.getTypesAnnotatedWith(Endpoint.class);

		for (Class endpointClazz : clazzes) {
			endpoints.put(endpointClazz, new EndpointFeatures<>(endpointClazz));
		}
	}

	private Collection> generateEndpointsMap() {
		scanEndpoints();
		scanActions();
		scanTransformers();
		if (enableHooks) {
			scanHooks();
		}
		scanShields();
		return endpoints.values();
	}

	private void scanShields() {
		Set> clazzes = endpointsPackage.getSubTypesOf(Shield.class);

		for (Class shieldClazz : clazzes) {
			if (Modifier.isAbstract(shieldClazz.getModifiers())) {
				continue;
			}
			setShield(shieldClazz);
		}
	}

	private > void setShield(Class shieldClazz) {
		Class objectClazz = getShieldObject(shieldClazz);

		ShieldInfo shieldInfo = new ShieldInfo(shieldClazz);

		for (EndpointFeatures endpoint : getEndpoints(objectClazz, shieldClazz.getSimpleName())) {
			endpoint.setShield(shieldClazz);
			endpoint.setShieldInfo(shieldInfo);
		}
	}

	private void scanHooks() {
		Set> clazzes = endpointsPackage.getSubTypesOf(Hook.class);

		for (Class hookClazz : clazzes) {
			addHook(hookClazz);
		}
	}

	private > void addHook(Class hookClazz) {
		Class objectClazz = getHookObject(hookClazz);
		for (EndpointFeatures endpoint : getEndpoints(objectClazz, hookClazz.getSimpleName())) {
			endpoint.addHook(hookClazz);
		}
	}

	private void scanTransformers() {
		Set> clazzes = endpointsPackage.getSubTypesOf(Transformer.class);

		for (Class transformerClazz : clazzes) {
			Class objectClazz = ReflectionUtils.getGenericParameter(transformerClazz);
			addTransformerForObject(objectClazz, transformerClazz);
		}
	}

	private void addTransformerForObject(Class objectClazz, Class transformerClazz) {
		for (Method method : transformerClazz.getDeclaredMethods()) {
			if (!isValidTransformerMethod(method)) {
				continue;
			}

			for (EndpointFeatures endpoint : getEndpoints(objectClazz, transformerClazz.getSimpleName())) {
				endpoint.addTransformer(method.getName(), method);
			}
		}
	}

	private boolean isValidTransformerMethod(Method method) {
		return !method.isSynthetic();
	}

	private void scanActions() {
		Set> clazzes = endpointsPackage.getSubTypesOf(Action.class);

		for (Class actionClazz : clazzes) {
			Class objectClazz = ReflectionUtils.getGenericParameter(actionClazz);

			if (objectClazz == null) {
				continue;
			}

			for (Method method : actionClazz.getDeclaredMethods()) {
				addAction(objectClazz, method);
			}
		}
	}

	// TODO should we think that an objectClazz has more than one endpoint?
	private  List> getEndpoints(Class objectClazz, String featureClazz) {
		List> list = new ArrayList<>();
		for (Class endpoint : endpoints.keySet()) {
			if (objectClazz.isAssignableFrom(endpoint)) {
				list.add((EndpointFeatures) endpoints.get(endpoint));
			}
		}
		if (list.isEmpty()) {
			throw new RuntimeException("Tryed to create feature '" + featureClazz + "' with entity '" + objectClazz.getSimpleName()
					+ "' that is not an @Endpoint nor do any io.yawp inherits from it.");
		}
		return list;
	}

	private void addAction(Class objectClazz, Method method) {

		List actionKeys = parseActionKeys(objectClazz, method);

		if (actionKeys.isEmpty()) {
			return;
		}

		for (EndpointFeatures endpoint : getEndpoints(objectClazz, method.getDeclaringClass().getSimpleName())) {
			for (ActionKey ar : actionKeys) {
				endpoint.addAction(ar, method);
			}
		}
	}

	private List parseActionKeys(Class objectClazz, Method method) {
		try {
			return ActionKey.parseMethod(method);
		} catch (InvalidActionMethodException e) {
			throw new RuntimeException("Invalid Action: " + objectClazz.getName() + "." + method.getName(), e);
		}
	}

	public EndpointScanner enableHooks(boolean enableHooks) {
		this.enableHooks = enableHooks;
		return this;
	}

	private static  Class getHookObject(Class> hook) {
		return (Class) ReflectionUtils.getGenericParameter(hook);
	}

	private static  Class getShieldObject(Class> hook) {
		return (Class) ReflectionUtils.getGenericParameter(hook);
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy