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

it.espr.injector.ClassInspector Maven / Gradle / Ivy

package it.espr.injector;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

public class ClassInspector {

	private Configuration configuration;

	private Utils utils;

	ClassInspector(Configuration configuration) {
		super();
		this.configuration = configuration;
		this.utils = new Utils();
	}

	@SuppressWarnings("unchecked")
	private class MyTypeSafeMap {
		private Map> map = new HashMap<>();

		public  void put(Class type, Bean value) {
			map.put(utils.key(value.name, type), value);
		}

		public  Bean get(Class type, String name) {
			return (Bean) map.get(utils.key(name, type));
		}
	}

	private MyTypeSafeMap cache = new MyTypeSafeMap();

	public  Bean inspect(Class type) throws BeanException {
		return this.inspect(type, null);
	}

	public  Bean inspect(Class type, String named) throws BeanException {
		Bean bean = this.cache.get(type, named);

		if (bean == null) {
			if (this.configuration.instances.containsKey(utils.key(named, type))) {
				bean = new Bean<>(named, utils.key(named, type), true, type, null, null, null);
			} else if (this.configuration.isBound(type)) {
				bean = this.inspectBindings(type, named);
			} else {
				String name = this.inspectName(type);

				if (!utils.isEmpty(named) && (utils.isEmpty(name) || !named.equals(name))) {
					return null;
				}

				Constructor constructor = inspectConstructors(type);
				String key = utils.key(name, type);
				boolean singleton = this.inspectSingleton(type);
				List> constructorParameters = inspectConstructorParameters(constructor);
				Map> fields = this.inspectFields(type);

				bean = new Bean(name, key, singleton, type, constructor, constructorParameters, fields);
			}
			this.cache.put(type, bean);
		}
		return bean;
	}

	public  Bean inspectBindings(Class type, String named) throws BeanException {
		Collection> candidates = this.configuration.getBindings(type);
		List> candidateBeans = new ArrayList<>();
		for (Class candidate : candidates) {
			Bean candidateBean = this.inspect(candidate, named);
			if (candidateBean != null) {
				candidateBeans.add(candidateBean);
			}
		}
		if (candidateBeans.size() != 1) {
			throw new BeanException("Found '" + candidateBeans.size() + "' candidates for bean for '" + type + "': Either you forgot to bind the bean or add @Named to it");
		}
		return candidateBeans.get(0);
	}

	private boolean inspectSingleton(Class type) {
		return type.isAnnotationPresent(Singleton.class);
	}

	private String inspectName(Class type) {
		String name = null;
		Named named = type.getAnnotation(Named.class);
		if (named != null && named.value() != null && !named.value().trim().equals("")) {
			name = named.value();
		}
		return name;
	}

	private Map> inspectFields(Class type) throws BeanException {
		Map> fields = new LinkedHashMap<>();

		Class c = type;
		while (!c.equals(Object.class)) {
			Field[] declaredFields = c.getDeclaredFields();
			for (Field field : declaredFields) {
				if (field.isAnnotationPresent(Inject.class)) {
					fields.put(field, null);
				}
			}
			c = c.getSuperclass();
		}

		for (Entry> entry : fields.entrySet()) {
			Class t = entry.getKey().getType();
			Named named = entry.getKey().getAnnotation(Named.class);
			String n = null;
			if (named != null && !utils.isEmpty(named.value())) {
				n = named.value().trim();
			}

			Bean bean = this.inspect(t, n);
			if (bean == null) {
				throw new BeanException("Can't find a bean for type '" + t + "' @Named as '" + n + "'");
			}
			entry.setValue(bean);
		}
		return fields.isEmpty() ? null : fields;
	}

	private List> inspectConstructorParameters(Constructor constructor) throws BeanException {
		List> constructorParametersBeans = null;
		Class[] constructorParameters = constructor.getParameterTypes();
		if (constructorParameters.length != 0) {
			constructorParametersBeans = new ArrayList<>();
			for (int index = 0; index < constructorParameters.length; index++) {
				try {
					String named = utils.getAnnotationValue(Named.class, constructor.getParameterAnnotations()[index]);
					constructorParametersBeans.add(this.inspect(constructorParameters[index], named));
				} catch (BeanException e) {
					throw new BeanException("Problem when inspecting '" + index + "' constructor parameter of type '" + constructorParameters[index] + "'");
				}
			}
		}
		return constructorParametersBeans;
	}

	@SuppressWarnings("unchecked")
	public  Constructor inspectConstructors(Class type) throws BeanException {
		List> constructors = Arrays.asList(type.getDeclaredConstructors());
		Iterator> iterator = constructors.iterator();
		while (iterator.hasNext()) {
			Constructor constructor = iterator.next();
			List modifiers = Arrays.asList(constructor.getModifiers());
			for (Integer modifier : modifiers) {
				if (Modifier.isPublic(modifier)) {
					break;
				}
				iterator.remove();
			}
		}
		if (constructors.size() != 1) {
			throw new BeanException("Found '" + constructors.size() + "' valid constructors - can resolve as a bean");
		}
		return (Constructor) constructors.get(0);
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy