package org.archstudio.xarchadt.internal; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import java.util.concurrent.ConcurrentMap; import org.archstudio.sysutils.SystemUtils; import org.archstudio.xarchadt.IXArchADT; import org.archstudio.xarchadt.IXArchADTTypeMetadata; import org.archstudio.xarchadt.ObjRef; import org.archstudio.xarchadt.XArchADTProxy; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EClassifier; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EPackage; import org.eclipse.emf.ecore.EStructuralFeature; import com.google.common.base.Function; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.MapMaker; public class EObjectProxy extends AbstractProxy { private static final LoadingCache<String, EPackage> ePackageCache = CacheBuilder.newBuilder().build( new CacheLoader<String, EPackage>() { @Override public synchronized EPackage load(String nsURI) throws Exception { EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); if (ePackage != null) { return ePackage; } throw new NullPointerException(SystemUtils.message("No EPackage with nsURI: $0", nsURI)); } }); static final class Get_EObject extends Handler<NameContext, ProxyImpl> { public Get_EObject(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { return proxy(proxy.xarch, (ObjRef) proxy.xarch.get(proxy.objRef, context.name)); } } static final class Get_EList extends Handler<NameContext, ProxyImpl> { public Get_EList(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { return EListProxy.proxy(proxy.xarch, proxy.objRef, context.name); } } static final class Get_Object extends Handler<NameContext, ProxyImpl> { public Get_Object(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { return proxy.xarch.get(proxy.objRef, context.name); } } static final class Get_EClass extends Handler<NameContext, ProxyImpl> { public Get_EClass(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { IXArchADTTypeMetadata typeMetadata = proxy.xarch.getTypeMetadata(proxy.objRef); EPackage ePackage = ePackageCache.getUnchecked(typeMetadata.getNsURI()); return ePackage.getEClassifier(typeMetadata.getTypeName()); } } static final class Get_EContainer extends Handler<NameContext, ProxyImpl> { public Get_EContainer(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { return XArchADTProxy.proxy(proxy.xarch, proxy.xarch.getParent(proxy.objRef)); } } static final class Get_EGet extends Handler<NameContext, ProxyImpl> { public Get_EGet(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { EStructuralFeature feature = (EStructuralFeature) args[0]; if (feature.isMany()) { return EListProxy.proxy(proxy.xarch, proxy.objRef, feature.getName()); } Serializable result = proxy.xarch.get(proxy.objRef, feature.getName()); return result instanceof ObjRef ? XArchADTProxy.proxy(proxy.xarch, (ObjRef) result) : result; } } static final class Set_ESet extends Handler<NameContext, ProxyImpl> { public Set_ESet(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { EStructuralFeature feature = (EStructuralFeature) args[0]; proxy.xarch.set(proxy.objRef, feature.getName(), (Serializable) args[1]); return null; } } static final class Set_EObject extends Handler<NameContext, ProxyImpl> { public Set_EObject(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { proxy.xarch.set(proxy.objRef, context.name, XArchADTProxy.unproxy((EObject) args[0])); return null; } } static final class Set_EList extends Handler<NameContext, ProxyImpl> { public Set_EList(NameContext context) { super(context); } @Override public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { return EListProxy.proxy(proxy.xarch, proxy.objRef, context.name); } } static final class Set_Serializable extends Handler<NameContext, ProxyImpl> { public Set_Serializable(NameContext context) { super(context); } @Override @SuppressWarnings("cast") public Object invoke(ProxyImpl proxy, Method method, Object[] args) throws Throwable { proxy.xarch.set(proxy.objRef, context.name, (Serializable) args[0]); return null; } } static final class ProxyImpl implements InvocationHandler { final IXArchADT xarch; final ObjRef objRef; ProxyImpl(IXArchADT xarch, ObjRef objRef) { this.xarch = xarch; this.objRef = objRef; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return methodsCache.get(method).invoke(this, method, args); } @Override public String toString() { IXArchADTTypeMetadata typeMetadata = xarch.getTypeMetadata(objRef); List<String> features = Lists.newArrayList("name", "id"); String info = null; for (String feature : features) { if (typeMetadata.getFeatures().get(feature) != null) { Object value = xarch.get(objRef, feature); if (value != null) { String string = value.toString(); if (string.length() > 0) { info = "[" + feature + "=" + string + "]"; break; } } } } if (info == null) { info = "[type=" + typeMetadata.getTypeName() + "]"; } return objRef.toString() + info; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (objRef == null ? 0 : objRef.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (obj instanceof Proxy) { obj = Proxy.getInvocationHandler(obj); } if (getClass() != obj.getClass()) { return false; } ProxyImpl other = (ProxyImpl) obj; if (objRef == null) { if (other.objRef != null) { return false; } } else if (!objRef.equals(other.objRef)) { 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 = "e")) { if (name.equals("eClass")) { return getHandler(Get_EClass.class, new NameContext(name)); } if (name.equals("eContainer")) { return getHandler(Get_EContainer.class, new NameContext(name)); } if (name.equals("eGet")) { return getHandler(Get_EGet.class, new NameContext(name)); } if (name.equals("eSet")) { return getHandler(Set_ESet.class, new NameContext(name)); } } if (name.startsWith(prefix = "get")) { if (EObject.class.isAssignableFrom(method.getReturnType())) { return getHandler(Get_EObject.class, new NameContext(name.substring(prefix.length()))); } if (EList.class.isAssignableFrom(method.getReturnType())) { return getHandler(Get_EList.class, new NameContext(name.substring(prefix.length()))); } return getHandler(Get_Object.class, new NameContext(name.substring(prefix.length()))); } if (name.startsWith(prefix = "is")) { return getHandler(Get_Object.class, new NameContext(name.substring(prefix.length()))); } if (name.startsWith(prefix = "set")) { if (EObject.class.isAssignableFrom(method.getParameterTypes()[0])) { return getHandler(Set_EObject.class, new NameContext(name.substring(prefix.length()))); } if (EList.class.isAssignableFrom(method.getParameterTypes()[0])) { return getHandler(Set_EList.class, new NameContext(name.substring(prefix.length()))); } // set methods can take many different object types, not just Strings return getHandler(Set_Serializable.class, new NameContext(name.substring(prefix.length()))); } return getDefaultHandler(); } }); static final LoadingCache<IXArchADT, ConcurrentMap<ObjRef, EObject>> xArchProxiesCache = CacheBuilder.newBuilder() .weakKeys().build(new CacheLoader<IXArchADT, ConcurrentMap<ObjRef, EObject>>() { @Override public ConcurrentMap<ObjRef, EObject> load(IXArchADT xarch) throws Exception { return new MapMaker().softValues().makeMap(); } }); @SuppressWarnings("unchecked") public static final <T extends EObject> T proxy(IXArchADT xarch, ObjRef objRef) { if (objRef == null) { return null; } ConcurrentMap<ObjRef, EObject> proxies = xArchProxiesCache.getUnchecked(xarch); T proxy = (T) proxies.get(objRef); if (proxy == null) { IXArchADTTypeMetadata typeMetadata = xarch.getTypeMetadata(objRef); String nsURI = typeMetadata.getNsURI(); EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsURI); if (ePackage == null) { throw new NullPointerException(SystemUtils.message(// "EPackage not registered: $0", nsURI)); } String typeName = typeMetadata.getTypeName(); EClassifier eClassifier = ePackage.getEClassifier(typeName); if (!(eClassifier instanceof EClass)) { throw new NullPointerException(SystemUtils.message(// "EPackage does not contain type '$1': $0", nsURI, typeName)); } EClass eClass = (EClass) eClassifier; Class<T> instanceClass = (Class<T>) eClass.getInstanceClass(); proxies.putIfAbsent(// objRef, // proxy = (T) Proxy.newProxyInstance(// instanceClass.getClassLoader(), // new Class<?>[] { instanceClass }, // new ProxyImpl(xarch, objRef))); } return proxy; } public static final <T extends EObject> Iterable<T> proxy(final IXArchADT xarch, Iterable<ObjRef> objRefs) { return Iterables.transform(objRefs, new Function<ObjRef, T>() { @Override @SuppressWarnings("unchecked") public T apply(ObjRef objRef) { return (T) proxy(xarch, objRef); } }); } public static final ObjRef unproxy(EObject eObject) { if (eObject == null) { return null; } return ((ProxyImpl) Proxy.getInvocationHandler(eObject)).objRef; } public static final Iterable<ObjRef> unproxy(Iterable<EObject> eObjects) { return Iterables.transform(eObjects, new Function<EObject, ObjRef>() { @Override public ObjRef apply(EObject eObject) { return unproxy(eObject); } }); } public static IXArchADT getXArchADT(EObject eObject) { return ((ProxyImpl) Proxy.getInvocationHandler(eObject)).xarch; } }