package com.softwaremill.common.cdi.objectservice.auto;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Extension to process @OS and @OSImpl annotations
*/
public class AutoObjectServiceExtension implements Serializable, Extension {
Set<Bean> beansToAdd = new HashSet<Bean>();
Map<Class, Map<Class, Class>> autoOSMap = new HashMap<Class, Map<Class, Class>>();
public <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> event, BeanManager beanManager) {
OS createdWithAnnotation = event.getAnnotatedType().getAnnotation(OS.class);
if (createdWithAnnotation != null) {
beansToAdd.add(new AutoOSBean(beanManager, event.getAnnotatedType().getJavaClass(), this));
// there's never default implementation for this injection point.
// Veto it and create Bean to be injacted later.
event.veto();
}
OSImpl osImpl = event.getAnnotatedType().getAnnotation(OSImpl.class);
if (osImpl != null) {
// but remember the class for it's AutoOS interface
Class implClass = event.getAnnotatedType().getJavaClass();
Class autoClass = null;
Class servicedClass = null;
for (Type iface : implClass.getGenericInterfaces()) {
if (iface instanceof ParameterizedType) {
ParameterizedType type = (ParameterizedType)iface;
Class rawClass = ((Class) type.getRawType());
if (rawClass.getAnnotation(OS.class) != null) {
if (autoClass != null) {
throw new AutoOSException(implClass.getCanonicalName() +
" implements more then one @OS interface. Found @OS interfaces: " +
autoClass.getCanonicalName() + " , " + rawClass.getCanonicalName());
}
autoClass = rawClass;
servicedClass = ((Class) type.getActualTypeArguments()[0]);
}
}
}
if (autoClass == null) {
throw new AutoOSException("@AutoOSImpl has to implement @AutoOS");
}
Map<Class, Class> mapPerAuto;
if ((mapPerAuto = autoOSMap.get(autoClass)) == null) {
autoOSMap.put(autoClass, mapPerAuto = new HashMap<Class, Class>());
}
mapPerAuto.put(servicedClass, implClass);
}
}
public void afterBeanDiscovery(@Observes AfterBeanDiscovery abd) {
for (Bean bean : beansToAdd) {
abd.addBean(bean);
}
}
}