package org.archstudio.xarchadt.internal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import org.archstudio.xarchadt.IXArchADT;
import org.archstudio.xarchadt.ObjRef;
import org.archstudio.xarchadt.XArchADTProxy;
import org.eclipse.emf.ecore.EObject;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
public class AbstractProxy {
static final class NameContext {
String name;
public NameContext(String name) {
String suffix;
if (name.endsWith(suffix = "_")) {
name = name.substring(0, name.length() - suffix.length());
}
this.name = name;
}
@Override
public String toString() {
return "NamedContext[name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
NameContext other = (NameContext) obj;
if (name == null) {
if (other.name != null) {
return false;
}
}
else if (!name.equals(other.name)) {
return false;
}
return true;
}
}
protected static abstract class Handler<Context, Proxy extends InvocationHandler> {
final Context context;
public Handler() {
this(null);
}
public Handler(Context context) {
this.context = context;
}
public abstract Object invoke(Proxy proxy, Method method, Object[] args) throws Throwable;
}
private static final class DefaultHandler extends Handler<Void, InvocationHandler> {
public DefaultHandler() {
}
@Override
public Object invoke(InvocationHandler proxy, Method method, Object[] args) throws Throwable {
/*
* This is a last resort handler to call the basic Object methods on the actual proxy object itself, e.g.:
* #toString(), #equals(Object), and #hashCode()
*
* Note: an IllegalArgumentException with the message of 'object is not an instance of declaring class' may
* occur if the methods map does not create a specialized handler for the method being called.
*/
return method.invoke(proxy, args);
}
}
private static final LoadingCache<Class<? extends Handler<?, ?>>, LoadingCache<Object, Handler<?, ?>>> handlerCache = CacheBuilder
.newBuilder().build(new CacheLoader<Class<? extends Handler<?, ?>>, LoadingCache<Object, Handler<?, ?>>>() {
@Override
public LoadingCache<Object, Handler<?, ?>> load(final Class<? extends Handler<?, ?>> handlerClass)
throws Exception {
return CacheBuilder.newBuilder().build(new CacheLoader<Object, Handler<?, ?>>() {
@Override
public AbstractProxy.Handler<?, ?> load(Object context) throws Exception {
if (context == VOID_CONTEXT) {
return handlerClass.getConstructor().newInstance();
}
return handlerClass.getConstructor(context.getClass()).newInstance(context);
};
});
};
});
@SuppressWarnings("unchecked")
protected static final <H extends Handler<C, P>, C, P extends InvocationHandler> H getHandler(
Class<H> handlerClass, C context) {
return (H) handlerCache.getUnchecked(handlerClass).getUnchecked(context);
}
private static final Object VOID_CONTEXT = new Object();
@SuppressWarnings("unchecked")
protected static final <H extends Handler<C, P>, C, P extends InvocationHandler> H getHandler(Class<H> handlerClass) {
return getHandler(handlerClass, (C) VOID_CONTEXT);
}
private static final DefaultHandler DEFAULT_HANDLER = new DefaultHandler();
@SuppressWarnings("unchecked")
protected static final <H extends Handler<?, ?>> H getDefaultHandler() {
return (H) DEFAULT_HANDLER;
}
@SuppressWarnings("unchecked")
protected static <T> T unproxy(Object o) {
if (o instanceof EObject) {
return (T) XArchADTProxy.unproxy((EObject) o);
}
return (T) o;
}
@SuppressWarnings("unchecked")
protected static <T> T proxy(IXArchADT xarch, Object o) {
if (o instanceof ObjRef) {
return (T) XArchADTProxy.proxy(xarch, (ObjRef) o);
}
return (T) o;
}
}