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.AbstractList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import org.archstudio.sysutils.SystemUtils;
import org.archstudio.xarchadt.IXArchADT;
import org.archstudio.xarchadt.ObjRef;
import org.archstudio.xarchadt.XArchADTProxy;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
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.MapMaker;
public class EListProxy extends AbstractProxy {
static class ProxyImpl<E> extends AbstractList<E> implements EList<E>, InvocationHandler {
protected final IXArchADT xarch;
protected final ObjRef objRef;
protected final String name;
public ProxyImpl(IXArchADT xarch, ObjRef objRef, String name) {
this.xarch = xarch;
this.objRef = objRef;
this.name = name;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this, args);
}
@Override
public void move(int newPosition, E object) {
throw new UnsupportedOperationException();
}
@Override
public E move(int newPosition, int oldPosition) {
throw new UnsupportedOperationException();
}
@Override
@SuppressWarnings("unchecked")
public E get(int index) {
return (E) XArchADTProxy.proxy(xarch, (ObjRef) xarch.getAll(objRef, name).get(index));
}
@Override
public int size() {
return xarch.getAll(objRef, name).size();
}
@Override
@SuppressWarnings("unchecked")
public Iterator<E> iterator() {
final Iterator<ObjRef> i = Iterables.filter(xarch.getAll(objRef, name), ObjRef.class).iterator();
return new Iterator<E>() {
ObjRef current = null;
@Override
public boolean hasNext() {
return i.hasNext();
}
@Override
public E next() {
return (E) proxy(xarch, current = i.next());
}
@Override
public void remove() {
if (current == null) {
throw new IllegalStateException();
}
xarch.remove(objRef, name, current);
current = null;
}
};
}
@Override
public boolean add(E e) {
xarch.add(objRef, name, (Serializable) unproxy(e));
return true;
}
@Override
public boolean remove(Object o) {
if (o instanceof EObject) {
xarch.remove(objRef, name, (Serializable) unproxy(o));
return true;
}
return false;
}
@Override
public void clear() {
xarch.remove(objRef, name, xarch.getAll(objRef, name));
}
@Override
public String toString() {
return SystemUtils.message("EList of '$0' for $1", name, objRef);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + (objRef == null ? 0 : objRef.hashCode());
return result;
}
@Override
@SuppressWarnings("rawtypes")
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 (name == null) {
if (other.name != null) {
return false;
}
}
else if (!name.equals(other.name)) {
return false;
}
if (objRef == null) {
if (other.objRef != null) {
return false;
}
}
else if (!objRef.equals(other.objRef)) {
return false;
}
return true;
}
}
static final LoadingCache<IXArchADT, ConcurrentMap<List<Object>, EList<Object>>> xArchProxiesCache = CacheBuilder
.newBuilder().weakKeys().build(new CacheLoader<IXArchADT, ConcurrentMap<List<Object>, EList<Object>>>() {
@Override
public ConcurrentMap<List<Object>, EList<Object>> load(IXArchADT input) {
return new MapMaker().softValues().makeMap();
}
});
@SuppressWarnings("unchecked")
public static final <T extends EObject> EList<T> proxy(IXArchADT xarch, ObjRef objRef, String name) {
ConcurrentMap<List<Object>, EList<Object>> proxies = xArchProxiesCache.getUnchecked(xarch);
EList<Object> proxy = proxies.get(Arrays.asList(objRef, name));
if (proxy == null) {
@SuppressWarnings("rawtypes")
Class<EList> instanceClass = EList.class;
proxies.putIfAbsent(//
Arrays.<Object> asList(objRef, name), //
proxy = (EList<Object>) Proxy.newProxyInstance(//
instanceClass.getClassLoader(), //
new Class<?>[] { instanceClass }, //
new ProxyImpl<EObject>(xarch, objRef, name)));
}
return (EList<T>) proxy;
}
}