| /*************************************************************************** |
| * Copyright (c) 2004 - 2008 Eike Stepper, Germany. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Eike Stepper - initial API and implementation |
| **************************************************************************/ |
| package org.eclipse.emf.internal.cdo; |
| |
| import org.eclipse.emf.cdo.CDOObject; |
| import org.eclipse.emf.cdo.CDOState; |
| import org.eclipse.emf.cdo.common.CDOProtocolConstants; |
| import org.eclipse.emf.cdo.common.id.CDOID; |
| import org.eclipse.emf.cdo.common.model.CDOClass; |
| import org.eclipse.emf.cdo.common.model.CDOFeature; |
| import org.eclipse.emf.cdo.common.model.CDOType; |
| import org.eclipse.emf.cdo.common.revision.CDORevision; |
| import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta; |
| import org.eclipse.emf.cdo.eresource.CDOResource; |
| import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision; |
| import org.eclipse.emf.cdo.util.CDOPackageRegistry; |
| |
| import org.eclipse.emf.internal.cdo.bundle.OM; |
| import org.eclipse.emf.internal.cdo.util.FSMUtil; |
| import org.eclipse.emf.internal.cdo.util.GenUtil; |
| import org.eclipse.emf.internal.cdo.util.ModelUtil; |
| |
| import org.eclipse.net4j.util.ImplementationError; |
| import org.eclipse.net4j.util.ReflectUtil; |
| import org.eclipse.net4j.util.om.trace.ContextTracer; |
| |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.impl.NotifyingListImpl; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.EClassifier; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.impl.EAttributeImpl; |
| import org.eclipse.emf.ecore.impl.EClassImpl; |
| import org.eclipse.emf.ecore.impl.EDataTypeImpl; |
| import org.eclipse.emf.ecore.impl.EObjectImpl; |
| import org.eclipse.emf.ecore.impl.EReferenceImpl; |
| import org.eclipse.emf.ecore.impl.EStructuralFeatureImpl; |
| import org.eclipse.emf.ecore.impl.ETypedElementImpl; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.util.InternalEList; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationHandler; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Proxy; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| /** |
| * @author Eike Stepper |
| * @since 2.0 |
| */ |
| /* |
| * IMPORTANT: Compile errors in this class might indicate an old version of EMF. Legacy support is only enabled for EMF |
| * with fixed bug #247130. These compile errors do not affect native models! |
| */ |
| public final class CDOLegacyWrapper extends CDOObjectWrapper |
| // TODO LEGACY |
| // implements InternalEObject.EReadListener, InternalEObject.EWriteListener |
| { |
| private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOLegacyWrapper.class); |
| |
| private static final Method eSetDirectResourceMethod = ReflectUtil.getMethod(EObjectImpl.class, "eSetDirectResource", |
| Resource.Internal.class); |
| |
| private static final Method eBasicSetContainerMethod = ReflectUtil.getMethod(EObjectImpl.class, "eBasicSetContainer", |
| InternalEObject.class, int.class); |
| |
| private CDOState state; |
| |
| private InternalCDORevision revision; |
| |
| private boolean allProxiesResolved; |
| |
| private boolean handlingCallback; |
| |
| public CDOLegacyWrapper(InternalEObject instance) |
| { |
| this.instance = instance; |
| state = CDOState.TRANSIENT; |
| } |
| |
| public CDOClass cdoClass() |
| { |
| return CDOObjectImpl.getCDOClass(this); |
| } |
| |
| public CDOState cdoState() |
| { |
| return state; |
| } |
| |
| public InternalCDORevision cdoRevision() |
| { |
| return revision; |
| } |
| |
| public void cdoReload() |
| { |
| CDOStateMachine.INSTANCE.reload(this); |
| } |
| |
| public CDOState cdoInternalSetState(CDOState state) |
| { |
| if (this.state != state) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting state {0} for {1}", state, this); |
| } |
| |
| CDOState tmp = this.state; |
| this.state = state; |
| adjustEProxy(); |
| return tmp; |
| } |
| |
| // TODO Detect duplicate cdoInternalSetState() calls |
| return null; |
| } |
| |
| public void cdoInternalSetRevision(CDORevision revision) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting revision: {0}", revision); |
| } |
| |
| this.revision = (InternalCDORevision)revision; |
| } |
| |
| public void cdoInternalPostAttach() |
| { |
| // TODO Avoid if no adapters in list (eBasicAdapters?) |
| // TODO LEGACY Clarify how to intercept adapter addition in the legacy instance |
| for (Adapter adapter : eAdapters()) |
| { |
| view.subscribe(this, adapter); |
| } |
| } |
| |
| public void cdoInternalPostDetach() |
| { |
| // Do nothing |
| } |
| |
| public void cdoInternalPreCommit() |
| { |
| instanceToRevision(); |
| if (cdoState() == CDOState.DIRTY) // NEW is handled in PrepareTransition |
| { |
| CDORevisionManagerImpl revisionManager = (CDORevisionManagerImpl)cdoView().getSession().getRevisionManager(); |
| InternalCDORevision originRevision = revisionManager.getRevisionByVersion(revision.getID(), |
| CDORevision.UNCHUNKED, revision.getVersion() - 1, false); |
| CDORevisionDelta delta = revision.compare(originRevision); |
| |
| // TODO LEGACY Consider to gather the deltas on the fly with noremal EMF change notifications |
| cdoView().toTransaction().registerRevisionDelta(delta); |
| } |
| } |
| |
| public void cdoInternalPostLoad() |
| { |
| // TODO Consider not remembering the revisin after copying it to the instance (spare 1/2 of the space) |
| revisionToInstance(); |
| } |
| |
| public void cdoInternalPostInvalid() |
| { |
| } |
| |
| public void cdoInternalCleanup() |
| { |
| } |
| |
| public synchronized void handleRead(InternalEObject object, int featureID) |
| { |
| if (!handlingCallback) |
| { |
| try |
| { |
| handlingCallback = true; |
| CDOStateMachine.INSTANCE.read(this); |
| |
| // TODO Optimize this when the list position index is added to the new callbacks |
| resolveAllProxies(); |
| } |
| finally |
| { |
| handlingCallback = false; |
| } |
| } |
| } |
| |
| public synchronized void handleWrite(InternalEObject object, int featureID) |
| { |
| if (!handlingCallback) |
| { |
| try |
| { |
| handlingCallback = true; |
| CDOStateMachine.INSTANCE.write(this); |
| |
| // TODO Optimize this when the list position index is added to the new callbacks |
| resolveAllProxies(); |
| } |
| finally |
| { |
| handlingCallback = false; |
| } |
| } |
| } |
| |
| @Override |
| public boolean equals(Object obj) |
| { |
| return obj == this || obj == instance; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| if (instance != null) |
| { |
| return instance.hashCode(); |
| } |
| |
| return super.hashCode(); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "CDOLegacyWrapper[" + id + "]"; |
| } |
| |
| private void instanceToRevision() |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Transfering instance to revision: {0} --> {1}", instance, revision); |
| } |
| |
| // Handle containment |
| instanceToRevisionContainment(); |
| |
| // Handle values |
| CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry(); |
| CDOClass cdoClass = revision.getCDOClass(); |
| for (CDOFeature feature : cdoClass.getAllFeatures()) |
| { |
| instanceToRevisionFeature(feature, packageRegistry); |
| } |
| } |
| |
| private void instanceToRevisionContainment() |
| { |
| CDOResource resource = (CDOResource)getInstanceResource(instance); |
| revision.setResourceID(resource == null ? CDOID.NULL : resource.cdoID()); |
| |
| InternalEObject eContainer = getInstanceContainer(instance); |
| if (eContainer == null) |
| { |
| revision.setContainerID(CDOID.NULL); |
| revision.setContainingFeatureID(0); |
| } |
| else |
| { |
| CDOObject cdoContainer = FSMUtil.adapt(eContainer, view); |
| revision.setContainerID(cdoContainer.cdoID()); |
| revision.setContainingFeatureID(getInstanceContainerFeatureID(instance)); |
| } |
| } |
| |
| private void instanceToRevisionFeature(CDOFeature feature, CDOPackageRegistry packageRegistry) |
| { |
| Object instanceValue = getInstanceValue(instance, feature, packageRegistry); |
| if (feature.isMany()) |
| { |
| List<Object> revisionList = revision.getList(feature); // TODO lazy? |
| revisionList.clear(); |
| |
| if (instanceValue != null) |
| { |
| InternalEList<?> instanceList = (InternalEList<?>)instanceValue; |
| if (!instanceList.isEmpty()) |
| { |
| for (Iterator<?> it = instanceList.basicIterator(); it.hasNext();) |
| { |
| Object instanceElement = it.next(); |
| if (instanceElement != null && feature.isReference()) |
| { |
| instanceElement = view.convertObjectToID(instanceElement); |
| } |
| |
| revisionList.add(instanceElement); |
| } |
| } |
| } |
| } |
| else |
| { |
| if (instanceValue != null && feature.isReference()) |
| { |
| instanceValue = view.convertObjectToID(instanceValue); |
| } |
| |
| revision.setValue(feature, instanceValue); |
| } |
| } |
| |
| /** |
| * TODO Simon: Fix this whole mess ;-) |
| */ |
| private void revisionToInstance() |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Transfering revision to instance: {0} --> {1}", revision, instance); |
| } |
| |
| boolean deliver = instance.eDeliver(); |
| if (deliver) |
| { |
| instance.eSetDeliver(false); |
| } |
| |
| try |
| { |
| // Handle containment |
| revisionToInstanceContainment(); |
| |
| // Handle values |
| CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry(); |
| CDOClass cdoClass = revision.getCDOClass(); |
| for (CDOFeature feature : cdoClass.getAllFeatures()) |
| { |
| revisionToInstanceFeature(feature, packageRegistry); |
| } |
| } |
| finally |
| { |
| if (deliver) |
| { |
| instance.eSetDeliver(true); |
| } |
| } |
| } |
| |
| private void revisionToInstanceContainment() |
| { |
| CDOID resourceID = revision.getResourceID(); |
| InternalEObject resource = getEObjectFromPotentialID(view, null, resourceID); |
| setInstanceResource((Resource.Internal)resource); |
| |
| Object containerID = revision.getContainerID(); |
| InternalEObject container = getEObjectFromPotentialID(view, null, containerID); |
| setInstanceContainer(container, revision.getContainingFeatureID()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void revisionToInstanceFeature(CDOFeature feature, CDOPackageRegistry packageRegistry) |
| { |
| Object value = revision.getValue(feature); |
| if (feature.isMany()) |
| { |
| InternalEList<Object> instanceList = (InternalEList<Object>)getInstanceValue(instance, feature, packageRegistry); |
| if (instanceList != null) |
| { |
| clearEList(instanceList); |
| if (value != null) |
| { |
| List<?> revisionList = (List<?>)value; |
| if (feature.isReference()) |
| { |
| for (Object element : revisionList) |
| { |
| element = getEObjectFromPotentialID(view, feature, element); |
| instanceList.basicAdd(element, null); |
| } |
| } |
| else |
| { |
| // TODO Is this only for multi-valued attributes?? |
| for (Object element : revisionList) |
| { |
| instanceList.basicAdd(element, null); |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| if (feature.isReference()) |
| { |
| value = getEObjectFromPotentialID(view, feature, value); |
| } |
| |
| setInstanceValue(instance, feature, value); |
| } |
| } |
| |
| private Resource.Internal getInstanceResource(InternalEObject instance) |
| { |
| return instance.eDirectResource(); |
| } |
| |
| private InternalEObject getInstanceContainer(InternalEObject instance) |
| { |
| return instance.eInternalContainer(); |
| } |
| |
| private int getInstanceContainerFeatureID(InternalEObject instance) |
| { |
| return instance.eContainerFeatureID(); |
| } |
| |
| private Object getInstanceValue(InternalEObject instance, CDOFeature feature, CDOPackageRegistry packageRegistry) |
| { |
| EStructuralFeature eFeature = ModelUtil.getEFeature(feature, packageRegistry); |
| return instance.eGet(eFeature); |
| } |
| |
| private void setInstanceResource(Resource.Internal resource) |
| { |
| ReflectUtil.invokeMethod(eSetDirectResourceMethod, instance, resource); |
| } |
| |
| private void setInstanceContainer(InternalEObject container, int containerFeatureID) |
| { |
| ReflectUtil.invokeMethod(eBasicSetContainerMethod, instance, container, containerFeatureID); |
| } |
| |
| /** |
| * TODO Ed: Help to fix whole mess (avoid inverse updates) |
| */ |
| private void setInstanceValue(InternalEObject instance, CDOFeature feature, Object value) |
| { |
| // TODO Consider EStoreEObjectImpl based objects as well! |
| // TODO Don't use Java reflection |
| Class<?> instanceClass = instance.getClass(); |
| String featureName = feature.getName(); |
| String fieldName = featureName;// TODO safeName() |
| Field field = ReflectUtil.getField(instanceClass, fieldName); |
| if (field == null && feature.getType() == CDOType.BOOLEAN) |
| { |
| if (instanceClass.isAssignableFrom(EAttributeImpl.class) || instanceClass.isAssignableFrom(EClassImpl.class) |
| || instanceClass.isAssignableFrom(EDataTypeImpl.class) |
| || instanceClass.isAssignableFrom(EReferenceImpl.class) |
| || instanceClass.isAssignableFrom(EStructuralFeatureImpl.class) |
| || instanceClass.isAssignableFrom(ETypedElementImpl.class)) |
| { |
| // ******************************************* |
| // ID_EFLAG = 1 << 15; |
| // ******************************************* |
| // ABSTRACT_EFLAG = 1 << 8; |
| // INTERFACE_EFLAG = 1 << 9; |
| // ******************************************* |
| // SERIALIZABLE_EFLAG = 1 << 8; |
| // ******************************************* |
| // CONTAINMENT_EFLAG = 1 << 15; |
| // RESOLVE_PROXIES_EFLAG = 1 << 16; |
| // ******************************************* |
| // CHANGEABLE_EFLAG = 1 << 10; |
| // VOLATILE_EFLAG = 1 << 11; |
| // TRANSIENT_EFLAG = 1 << 12; |
| // UNSETTABLE_EFLAG = 1 << 13; |
| // DERIVED_EFLAG = 1 << 14; |
| // ******************************************* |
| // ORDERED_EFLAG = 1 << 8; |
| // UNIQUE_EFLAG = 1 << 9; |
| // ******************************************* |
| |
| String flagName = GenUtil.getFeatureUpperName(featureName) + "_EFLAG"; |
| int flagsMask = getEFlagMask(instanceClass, flagName); |
| |
| field = ReflectUtil.getField(instanceClass, "eFlags"); |
| int flags = (Integer)ReflectUtil.getValue(field, instance); |
| boolean on = (Boolean)value; |
| if (on) |
| { |
| flags |= flagsMask; // Add EFlag |
| } |
| else |
| { |
| flags &= ~flagsMask; // Remove EFlag |
| } |
| |
| ReflectUtil.setValue(field, instance, flags); |
| return; |
| } |
| } |
| |
| if (field == null) |
| { |
| throw new ImplementationError("Field not found: " + fieldName); |
| } |
| |
| ReflectUtil.setValue(field, instance, value); |
| } |
| |
| /** |
| * @param feature |
| * in case that a proxy has to be created the feature that will determine the interface type of the proxy and |
| * that will be used later to resolve the proxy. <code>null</code> indicates that proxy creation will be |
| * avoided! |
| */ |
| private InternalEObject getEObjectFromPotentialID(InternalCDOView view, CDOFeature feature, Object potentialID) |
| { |
| if (potentialID instanceof CDOID) |
| { |
| CDOID id = (CDOID)potentialID; |
| if (id.isNull()) |
| { |
| return null; |
| } |
| |
| boolean loadOnDemand = feature == null; |
| potentialID = view.getObject(id, loadOnDemand); |
| if (potentialID == null && !loadOnDemand) |
| { |
| return createProxy(view, feature, id); |
| } |
| } |
| |
| if (potentialID instanceof InternalCDOObject) |
| { |
| return ((InternalCDOObject)potentialID).cdoInternalInstance(); |
| } |
| |
| return (InternalEObject)potentialID; |
| } |
| |
| /** |
| * Creates and returns a <em>proxy</em> object. The usage of a proxy object is strongly limited. The only guarantee |
| * that can be made is that the following methods are callable and will behave in the expected way: |
| * <ul> |
| * <li>{@link CDOObject#cdoID()} will return the {@link CDOID} of the target object |
| * <li>{@link CDOObject#cdoState()} will return {@link CDOState#PROXY PROXY} |
| * <li>{@link InternalEObject#eIsProxy()} will return <code>true</code> |
| * <li>{@link InternalEObject#eProxyURI()} will return the EMF proxy URI of the target object |
| * </ul> |
| * Calling any other method on the proxy object will result in an {@link UnsupportedOperationException} being thrown |
| * at runtime. Note also that the proxy object might even not be cast to the concrete type of the target object. The |
| * proxy can only guaranteed to be of <em>any</em> concrete subtype of the declared type of the given feature. |
| * <p> |
| * TODO {@link InternalEObject#eResolveProxy(InternalEObject) |
| */ |
| private InternalEObject createProxy(InternalCDOView view, CDOFeature feature, CDOID id) |
| { |
| CDOPackageRegistry packageRegistry = view.getSession().getPackageRegistry(); |
| EStructuralFeature eFeature = ModelUtil.getEFeature(feature, packageRegistry); |
| EClassifier eType = eFeature.getEType(); |
| Class<?> instanceClass = eType.getInstanceClass(); |
| |
| Class<?>[] interfaces = { instanceClass, InternalEObject.class, LegacyProxy.class }; |
| ClassLoader classLoader = CDOLegacyWrapper.class.getClassLoader(); |
| LegacyProxyInvocationHandler handler = new LegacyProxyInvocationHandler(this, id); |
| return (InternalEObject)Proxy.newProxyInstance(classLoader, interfaces, handler); |
| } |
| |
| /** |
| * TODO Consider using only EMF concepts for resolving proxies! |
| */ |
| private void resolveAllProxies() |
| { |
| // if (!allProxiesResolved) |
| { |
| CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry(); |
| CDOClass cdoClass = revision.getCDOClass(); |
| for (CDOFeature feature : cdoClass.getAllFeatures()) |
| { |
| if (feature.isReference()) |
| { |
| resolveProxies(feature, packageRegistry); |
| } |
| } |
| |
| // allProxiesResolved = true; |
| } |
| } |
| |
| /* |
| * IMPORTANT: Compile errors in this method might indicate an old version of EMF. Legacy support is only enabled for |
| * EMF with fixed bug #247130. These compile errors do not affect native models! |
| */ |
| @SuppressWarnings("unchecked") |
| private void resolveProxies(CDOFeature feature, CDOPackageRegistry packageRegistry) |
| { |
| Object value = getInstanceValue(instance, feature, packageRegistry); |
| if (value != null) |
| { |
| if (feature.isMany()) |
| { |
| InternalEList<Object> list = (InternalEList<Object>)value; |
| int size = list.size(); |
| for (int i = 0; i < size; i++) |
| { |
| Object element = list.get(i); |
| if (element instanceof LegacyProxy) |
| { |
| CDOID id = ((LegacyProxy)element).getID(); |
| InternalCDOObject resolved = (InternalCDOObject)view.getObject(id); |
| InternalEObject instance = resolved.cdoInternalInstance(); |
| |
| // TODO LEGACY |
| // // TODO Is InternalEList.basicSet() needed??? |
| // if (list instanceof org.eclipse.emf.ecore.util.DelegatingInternalEList) |
| // { |
| // list = ((org.eclipse.emf.ecore.util.DelegatingInternalEList)list).getDelegateInternalEList(); |
| // } |
| |
| if (list instanceof NotifyingListImpl) |
| { |
| ((NotifyingListImpl)list).basicSet(i, instance, null); |
| } |
| else |
| { |
| list.set(i, instance); |
| } |
| } |
| } |
| } |
| else |
| { |
| if (value instanceof LegacyProxy) |
| { |
| CDOID id = ((LegacyProxy)value).getID(); |
| InternalCDOObject resolved = (InternalCDOObject)view.getObject(id); |
| InternalEObject instance = resolved.cdoInternalInstance(); |
| setInstanceValue(instance, feature, instance); |
| } |
| } |
| } |
| } |
| |
| private void adjustEProxy() |
| { |
| // Setting eProxyURI is necessary to prevent content adapters from |
| // loading the whole content tree. |
| // TODO Does not have the desired effect ;-( see CDOEditor.createModel() |
| if (state == CDOState.PROXY) |
| { |
| if (!instance.eIsProxy()) |
| { |
| URI uri = URI.createURI(CDOProtocolConstants.PROTOCOL_NAME + ":proxy#" + id); |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Setting proxyURI {0} for {1}", uri, instance); |
| } |
| |
| instance.eSetProxyURI(uri); |
| } |
| } |
| else |
| { |
| if (instance.eIsProxy()) |
| { |
| if (TRACER.isEnabled()) |
| { |
| TRACER.format("Unsetting proxyURI for {0}", instance); |
| } |
| |
| instance.eSetProxyURI(null); |
| } |
| } |
| } |
| |
| /** |
| * TODO Ed: Fix whole mess ;-) |
| */ |
| private void clearEList(InternalEList<Object> list) |
| { |
| while (!list.isEmpty()) |
| { |
| Object toBeRemoved = list.basicGet(0); |
| list.basicRemove(toBeRemoved, null); |
| } |
| } |
| |
| private static int getEFlagMask(Class<?> instanceClass, String flagName) |
| { |
| Field field = ReflectUtil.getField(instanceClass, flagName); |
| if (!field.isAccessible()) |
| { |
| field.setAccessible(true); |
| } |
| |
| try |
| { |
| return (Integer)field.get(null); |
| } |
| catch (IllegalAccessException ex) |
| { |
| throw new ImplementationError(ex); |
| } |
| } |
| |
| public static boolean isLegacyProxy(Object object) |
| { |
| return object instanceof LegacyProxy; |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static interface LegacyProxy |
| { |
| public CDOID getID(); |
| } |
| |
| /** |
| * @author Eike Stepper |
| */ |
| private static final class LegacyProxyInvocationHandler implements InvocationHandler, LegacyProxy |
| { |
| private static final Method getIDMethod = ReflectUtil.getMethod(LegacyProxy.class, "getID"); |
| |
| private static final Method eIsProxyMethod = ReflectUtil.getMethod(EObject.class, "eIsProxy"); |
| |
| private static final Method eProxyURIMethod = ReflectUtil.getMethod(InternalEObject.class, "eProxyURI"); |
| |
| private CDOLegacyWrapper wrapper; |
| |
| private CDOID id; |
| |
| public LegacyProxyInvocationHandler(CDOLegacyWrapper wrapper, CDOID id) |
| { |
| this.wrapper = wrapper; |
| this.id = id; |
| } |
| |
| public CDOID getID() |
| { |
| return id; |
| } |
| |
| public Object invoke(Object proxy, Method method, Object[] args) throws Throwable |
| { |
| if (method.equals(getIDMethod)) |
| { |
| return id; |
| } |
| |
| if (method.equals(eIsProxyMethod)) |
| { |
| return true; |
| } |
| |
| if (method.equals(eProxyURIMethod)) |
| { |
| // Use the resource of the container because it's guaranteed to be in the same CDOView as the resource |
| // of the target! |
| Resource resource = wrapper.eResource(); |
| |
| // TODO Consider using a "fake" Resource implementation. See Resource.getEObject(...) |
| return resource.getURI().appendFragment(id.toURIFragment()); |
| } |
| |
| // A client must have invoked the proxy while being told not to do so! |
| throw new UnsupportedOperationException(method.getName()); |
| } |
| } |
| } |