package net.sourceforge.javautil.poly;
import java.lang.annotation.Annotation;
import java.lang.reflect.Proxy;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassField;
import net.sourceforge.javautil.interceptor.IInterceptorManager;
import net.sourceforge.javautil.interceptor.IInterceptorManipulator;
import net.sourceforge.javautil.interceptor.InterceptorUtil;
import net.sourceforge.javautil.interceptor.type.InterceptorInvocationHandler;
import net.sourceforge.javautil.interceptor.type.cjc.InterceptorProxyConstructor;
import net.sourceforge.javautil.interceptor.type.cjcw.InterceptorProxyTypeCJCW;
/**
* The central place for generating and accessing SOJO based views.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public final class PojoView {
/**
* @param instance The instance in question
* @return True if the instance is a Super POJO (SOJO), otherwise false
*
* @see #isSojoType(Class)
*/
public static boolean isSojo (Object instance) {
return getViewMap(instance) != null;
}
/**
* @param type The type in question
* @return True if instances of the type can become Super POJO's (SOJO's), otherwise false
*
* @see #isSojo(Object)
*/
public static boolean isSojoType (Class type) {
return getViewDefinitions(type).size() != 0;
}
/**
* This can be used to determine if a particular Super POJO supports a particular view.
*
* @param instance The instance in question.
* @param view The view type (usually an interface)
* @return True if the instance that was passed supports the specified view.
*/
public static <T> boolean isSupportsView (Object instance, Class<T> view) {
Map<Class, Object> viewMap = getViewMap(instance);
return viewMap == null ? false : viewMap.containsKey(view);
}
/**
* @param <T> The type of view in question
* @param instance The instance for which a view is being requested
* @param view The class of the view type
* @return The view related to the instance
* @throws IllegalArgumentException If the instance does not really support the view
*
* @see #isSupportsView(Object, Class)
*/
public static <T> T getView (Object instance, Class<T> view) {
Map<Class, Object> viewMap = getViewMap(instance);
if (viewMap != null && viewMap.containsKey(view)) {
Object value = viewMap.get(view);
if (value instanceof PojoViewDefinition) {
PojoViewDefinition definition = (PojoViewDefinition) value;
value = createProxy(definition, instance);
for (Class iface : definition.interfaces()) {
viewMap.put(iface, value);
}
}
return (T) value;
}
throw new IllegalArgumentException(instance + " does not support view: " + view);
}
/**
* @param instance The instance for which a list of views is desired
* @return An array, possibly empty, of views available for the instance
*/
public static Class[] getViewsFor (Object instance) {
Map<Class, Object> viewMap = getViewMap(instance);
return viewMap == null ? new Class[0] : viewMap.keySet().toArray(new Class[viewMap.size()]);
}
/**
* @param instance The instance to wrap
* @param interceptor The interceptor related to the instance
* @param view The view definition
* @return The view proxy object
*/
public static Object createView (Object instance, IInterceptorManager interceptor, PojoViewDefinition view) {
return Proxy.newProxyInstance(instance.getClass().getClassLoader(), view.interfaces(), new PojoViewInvocationHandler(interceptor,
instance, ClassCache.getFor(view.wrapper()).newInstance(instance)));
}
/**
* This will use the view map extracted from the instance.
*
* @see #storeViewDefinitions(Class, Object, Map)
*/
public static void storeViewDefinitions (Class type, Object instance) {
storeViewDefinitions(type, instance, getViewMap(instance));
}
/**
* @param type The type for which definitions will be stored
* @param instance The instance related to the views
* @param viewMap The view map in which to store the view information
*/
public static void storeViewDefinitions (Class type, Object instance, Map<Class, Object> viewMap) {
if (viewMap == null) throw new IllegalArgumentException("Must provide view map");
for (PojoViewDefinition definition : getViewDefinitions(type)) {
for (Class iface : definition.interfaces()) {
viewMap.put(iface, definition.lazyInitialization() ? definition : createProxy(definition, instance));
}
}
}
/**
* @param definition The definition from which to create a proxy
* @param instance The instance for which this proxy is being made
* @return A proxy for the specified instance
*/
public static Object createProxy (PojoViewDefinition definition, Object instance) {
return Proxy.newProxyInstance(definition.wrapper().getClassLoader(), definition.interfaces(),
new PojoViewInvocationHandler(InterceptorUtil.getManager(instance), instance,
ClassCache.getFor(definition.wrapper()).newInstance(instance)));
}
/**
* @param type The type for which to retreive {@link PojoViewDefinition}'s.
* @return The unique set of view definitions
*/
public static Set<PojoViewDefinition> getViewDefinitions (Class type) {
Set<PojoViewDefinition> svd = new LinkedHashSet<PojoViewDefinition>();
findViews(type, svd);
return svd;
}
/**
* @param instance The instance from which to extract the view map
* @return The view map for the instance
*
* @see PojoViewManipulator
*/
protected static Map<Class, Object> getViewMap (Object instance) {
Map<String, Object> stateMap = null;
if (Proxy.isProxyClass(instance.getClass())) {
InterceptorInvocationHandler svih = (InterceptorInvocationHandler) Proxy.getInvocationHandler(instance);
stateMap = InterceptorInvocationHandler.getStateMap(svih);
if (stateMap == null) return null;
} else {
ClassField field = ClassCache.getFor(instance.getClass()).getField(InterceptorProxyTypeCJCW.ABILITIES_FIELD_NAME);
if (field != null && field.getFieldType() == Map.class) {
field.getJavaMember().setAccessible(true);
stateMap = (Map) field.getValue(instance);
} else {
return null;
}
}
return (Map) stateMap.get(PojoViewManipulator.class.getName());
}
/**
* Recursive loop based search for annotations that added {@link PojoViewDefinition}'s to a particular proxy class.
*
* @param type The type in question
* @param abilities The unique set of abilities for the particular type
*/
protected static void findViews (Class type, Set<PojoViewDefinition> svd) {
for (Annotation annotation : type.getAnnotations()) {
if (annotation.annotationType().getPackage().getName().startsWith("java")) continue;
PojoViewDefinition view = annotation.annotationType().getAnnotation(PojoViewDefinition.class);
if (view != null) svd.add(view);
findViews(annotation.annotationType(), svd);
}
}
}