package org.squirrelframework.foundation.component;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.squirrelframework.foundation.util.ReflectUtils;
import org.squirrelframework.foundation.util.TypeReference;
/**
* Central factory class for components used by squirrel-foundation.
*
* @author Henry.He
*
*/
public class SquirrelProvider implements SquirrelSingleton {
private static SquirrelProvider instance = new SquirrelProvider();
public static SquirrelProvider getInstance() {
return instance;
}
public static void setInstance(SquirrelProvider instance) {
SquirrelProvider.instance = instance;
}
private Map<Class<?>, Class<?>> implementationRegistry = new ConcurrentHashMap<Class<?>, Class<?>>();
public <T> T newInstance(TypeReference<T> typeRef) {
return newInstance(typeRef, null, null);
}
public <T> T newInstance(TypeReference<T> typeRef, Class<?>[] argTypes, Object[] args) {
Class<T> clz = typeRef.getRawType();
return clz.cast(newInstance(clz, argTypes, args));
}
/**
* Create a new instance of the requested class using the internal registry.
* @param clz class of new instance
* @param <T> type of new instance
* @return new instance
*/
public <T> T newInstance(Class<T> clz) {
return newInstance(clz, null, null);
}
/**
* Create a new instance of the requested class using the internal registry.
* @param clz class of new instance
* @param argTypes arguments type of new instance constructor
* @param args arguments of new instance constructor
* @param <T> type of new instance
* @return new instance
*/
public <T> T newInstance(Class<T> clz, Class<?>[] argTypes, Object[] args) {
Class<T> implementationClass = getImplementation(clz);
if (args == null) {
return postProcess(clz, ReflectUtils.newInstance(implementationClass));
}
Constructor<T> constructor = ReflectUtils.getConstructor(implementationClass, argTypes);
return postProcess(clz, ReflectUtils.newInstance(constructor, args));
}
private <T> T postProcess(Class<T> clz, T component) {
SquirrelPostProcessor<T> postProcessor =
SquirrelPostProcessorProvider.getInstance().getPostProcessor(clz);
if(postProcessor!=null && component!=null) {
postProcessor.postProcess(component);
}
return component;
}
/**
* Register the implementation class for a certain class. Note, if there is already an entry in the registry for
* the class, then it will be overwritten.
*
* @param clazz register class
* @param implementationClass implementation class of register class
*/
public void register(Class<?> clazz, Class<?> implementationClass) {
// TODO: handle the case that there is already an entry...
implementationRegistry.put(clazz, implementationClass);
}
public void unregister(Class<?> clazz) {
implementationRegistry.remove(clazz);
}
public void clearRegistry() {
implementationRegistry.clear();
}
/**
* Return the current registered implementation.
* If class has register a implementation class, return register implementation class. If register class or implement
* class is an interface, try to find corresponding implementation class over naming convention.
* (implementation class simple name = interface class simple name + "Impl") First try to find the implementation class
* with conventional naming under the same package as interface class. If still not exist, try to find implementation class
* in (interface class package + ".impl").
*
* @param clz registered class
* @return current registered implementation
*/
public <T> Class<T> getImplementation(Class<T> clz) {
return resolveImplIfInterface(clz, new HashSet<Class<?>>());
}
private <T> Class<T> resolveImplIfInterface(Class<T> clz, Set<Class<?>> visited) {
if (!visited.add(clz)) {
throw new IllegalStateException("Registration cycles: " + visited);
}
if (!clz.isInterface()) {
Class<T> possibleImpl = fromRegistry(clz);
if(possibleImpl!=null && !possibleImpl.isInterface())
clz = possibleImpl;
return clz;
}
Class<T> possibleImpl = fromRegistry(clz);
if (possibleImpl == null) {
possibleImpl = findImplementationClass(clz);
// We only register actual implementations so cannot introduce
// cycles through this...
register(clz, possibleImpl);
}
return resolveImplIfInterface(possibleImpl, visited);
}
private <T> Class<T> fromRegistry(Class<T> clz) {
@SuppressWarnings("unchecked")
Class<T> impl = (Class<T>) implementationRegistry.get(clz);
return impl;
}
// find implementation class name according to programming convention
@SuppressWarnings("unchecked")
private <T> Class<T> findImplementationClass(Class<T> interfaceClass) {
Class<?> implementationClass = null;
String implClassName = interfaceClass.getName() + "Impl";
try {
implementationClass = Class.forName(implClassName);
} catch (ClassNotFoundException e) {
implClassName = ReflectUtils.getPackageName(interfaceClass.getName())+
".impl."+interfaceClass.getSimpleName()+"Impl";
implementationClass = ReflectUtils.getClass(implClassName);
}
return (Class<T>) implementationClass;
}
}