package com.sap.runlet.abstractinterpreter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import com.sap.runlet.abstractinterpreter.objects.RunletObject;
/**
* For an object, such as a model element or a {@link RunletObject}, finds the
* most specific {@link Class} registered with this factory to handle the
* object.
* <p>
*
* Handling classes are registered for one or more concrete types. If this would
* lead to an ambiguity for resolving handlers, an exception is thrown.
* <p>
*
* The factory chooses the handler that can handle the actual type of the object
* passed to the factory and for which no other handler is registered for a type
* to which the object conforms and that conforms to the type the chosen handler
* can handle. In other words, the most specific handler is selected.
* <p>
*
* The specialization relation over the objects passed to this factory is
* implemented by the {@link #getDirectSupertypes(Object)} method in subclasses
* of this class.
*
* @param <C>
* the class of which instances are the types of objects of type
* <tt>T</tt>. of the objects passed to this factory to determine a
* handler class for; on objects of this class, direct supertypes can
* be computed which defines a conformance relation for finding the
* most specific interpreter
* @param <T>
* the type of the objects that handling classes can handle and of
* which the type can be determined by {@link #getType(Object)},
* returning a <tt>C</tt> object describing the <tt>T</tt> object's
* type. Example: <tt>Expression</tt> as the abstract superclass of
* all expressions (which would require <tt>C</tt> to be
* {@link MofClass} because it's the type description based on which
* the specialization hierarchy is constructed. In particular,
* elements passed to {@link #getHandlerFor(Object)} have to be
* at least of that type.
*
* @param <H>
* the handler class's common base class; used as the return type of
* {@link #getHandlerClassFor(Object)}. All handler classes must have a
* public single-argument constructor with the argument's type being
* <tt>T</tt> or the specific subtype of <tt>T</tt>
*
* @author Axel Uhl (D043530)
*/
public abstract class GenericFactory<C, T, H> {
private Map<C, Class<?>> registeredHandlerClasses;
public GenericFactory() {
registeredHandlerClasses = new HashMap<C, Class<?>>();
}
public void registerHandlerClass(Class<? extends H> handlerClass, C... classes) {
// TODO ambiguity check
for (C c:classes) {
registeredHandlerClasses.put(c, handlerClass);
}
}
public void registerHandlerClass(Class<? extends H> handlerClass, C clazz) {
// TODO ambiguity check
registeredHandlerClasses.put(clazz, handlerClass);
}
@SuppressWarnings("unchecked")
public H getHandlerFor(T object) throws SecurityException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
H result = null;
C clazz = getType(object);
Class<?> interpreterClass = getHandlingClassForClass(clazz);
if (interpreterClass != null) {
Constructor<? extends H> constructor =
(Constructor<? extends H>) interpreterClass.getConstructors()[0];
result = constructor.newInstance(object);
}
return result;
}
abstract protected C getType(T object);
protected Class<?> getHandlingClassForClass(C clazz) {
Class<?> resultClass = registeredHandlerClasses.get(clazz);
if (resultClass == null) {
for (C superclass:getDirectSupertypes(clazz)) {
resultClass = getHandlingClassForClass(superclass);
if (resultClass != null) {
return resultClass;
}
}
}
return resultClass;
}
abstract protected Iterable<C> getDirectSupertypes(C c);
}