package org.archstudio.xarchadt.internal;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.ConcurrentMap;
import org.archstudio.sysutils.SystemUtils;
import org.archstudio.xarchadt.IXArchADT;
import org.eclipse.emf.ecore.EFactory;
import org.eclipse.emf.ecore.EPackage;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.MapMaker;
public class EFactoryProxy extends AbstractProxy {
static final class Create_EObject extends Handler<NameContext, ProxyImpl> {
public Create_EObject(NameContext context) {
super(context);
}
@Override
public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable {
return EObjectProxy.proxy(proxy.xarch, proxy.xarch.create(proxy.nsURI, context.name));
}
}
static final class ProxyImpl implements InvocationHandler {
private final IXArchADT xarch;
private final String nsURI;
public ProxyImpl(IXArchADT xarch, String nsURI) {
this.xarch = xarch;
this.nsURI = nsURI;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return methodsCache.get(method).invoke(this, method, args);
}
@Override
public String toString() {
return "Factory for " + nsURI;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (nsURI == null ? 0 : nsURI.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;
}
ProxyImpl other = (ProxyImpl) obj;
if (nsURI == null) {
if (other.nsURI != null) {
return false;
}
}
else if (!nsURI.equals(other.nsURI)) {
return false;
}
return true;
}
}
static final LoadingCache<Method, Handler<NameContext, ProxyImpl>> methodsCache = CacheBuilder.newBuilder().build(
new CacheLoader<Method, Handler<NameContext, ProxyImpl>>() {
@Override
public Handler<NameContext, ProxyImpl> load(Method method) throws Exception {
String name = method.getName();
String prefix;
if (name.startsWith(prefix = "create")) {
return getHandler(Create_EObject.class, new NameContext(name.substring(prefix.length())));
}
return getDefaultHandler();
};
});
static final LoadingCache<IXArchADT, ConcurrentMap<String, EFactory>> xArchProxiesCache = CacheBuilder.newBuilder()
.weakKeys().build(new CacheLoader<IXArchADT, ConcurrentMap<String, EFactory>>() {
@Override
public ConcurrentMap<String, EFactory> load(IXArchADT key) throws Exception {
return new MapMaker().softValues().makeMap();
}
});
@SuppressWarnings("unchecked")
public static final <T extends EFactory> T proxy(IXArchADT xarch, String nsURI) {
ConcurrentMap<String, EFactory> proxies = xArchProxiesCache.getUnchecked(xarch);
T proxy = (T) proxies.get(nsURI);
if (proxy == null) {
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI);
if (ePackage == null) {
throw new NullPointerException(SystemUtils.message(//
"EPackage not registered: $0", nsURI));
}
Class<T> instanceClass = (Class<T>) ePackage.getEFactoryInstance().getClass().getInterfaces()[0];
proxies.putIfAbsent(//
nsURI, //
proxy = (T) Proxy.newProxyInstance(//
instanceClass.getClassLoader(), //
new Class<?>[] { instanceClass }, //
new ProxyImpl(xarch, nsURI)));
}
return proxy;
}
public static final String unproxy(EFactory eFactory) {
return ((ProxyImpl) Proxy.getInvocationHandler(eFactory)).nsURI;
}
}