Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEike Stepper2012-05-01 09:53:32 +0000
committerEike Stepper2012-05-01 09:53:32 +0000
commitcbf2aed7453b7ac4e9282aba2910bce1bd1a75ff (patch)
treeeb14548773729d54d4cf1628fe39d0e565e3f7c0 /plugins/org.eclipse.emf.cdo
parent08ee9b77f614a70a477d1fc239937293aff17419 (diff)
downloadcdo-cbf2aed7453b7ac4e9282aba2910bce1bd1a75ff.tar.gz
cdo-cbf2aed7453b7ac4e9282aba2910bce1bd1a75ff.tar.xz
cdo-cbf2aed7453b7ac4e9282aba2910bce1bd1a75ff.zip
[343084] Add security infos to BaseCDORevision and adjust the framework accordingly
https://bugs.eclipse.org/bugs/show_bug.cgi?id=343084
Diffstat (limited to 'plugins/org.eclipse.emf.cdo')
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java2298
1 files changed, 1158 insertions, 1140 deletions
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
index cbb116fbf9..1d93cabcbd 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/object/CDOLegacyWrapper.java
@@ -1,1140 +1,1158 @@
-/*
- * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
- * 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
- * Martin Fluegge - bug 247226: Transparently support legacy models
- */
-package org.eclipse.emf.internal.cdo.object;
-
-import org.eclipse.emf.cdo.CDOObject;
-import org.eclipse.emf.cdo.CDOState;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.model.CDOModelUtil;
-import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
-import org.eclipse.emf.cdo.common.model.CDOType;
-import org.eclipse.emf.cdo.common.model.EMFUtil;
-import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
-import org.eclipse.emf.cdo.common.revision.CDOElementProxy;
-import org.eclipse.emf.cdo.common.revision.CDORevision;
-import org.eclipse.emf.cdo.common.revision.CDORevisionData;
-import org.eclipse.emf.cdo.common.util.CDOException;
-import org.eclipse.emf.cdo.eresource.CDOResource;
-import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.util.CDOUtil;
-
-import org.eclipse.emf.internal.cdo.CDOObjectImpl;
-import org.eclipse.emf.internal.cdo.bundle.OM;
-import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
-
-import org.eclipse.net4j.util.ReflectUtil;
-import org.eclipse.net4j.util.WrappedException;
-import org.eclipse.net4j.util.om.trace.ContextTracer;
-
-import org.eclipse.emf.common.notify.Adapter;
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.common.util.URI;
-import org.eclipse.emf.ecore.EAttribute;
-import org.eclipse.emf.ecore.EClass;
-import org.eclipse.emf.ecore.EClassifier;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.InternalEObject;
-import org.eclipse.emf.ecore.resource.Resource;
-import org.eclipse.emf.ecore.util.InternalEList;
-import org.eclipse.emf.spi.cdo.CDOStore;
-import org.eclipse.emf.spi.cdo.FSMUtil;
-import org.eclipse.emf.spi.cdo.InternalCDOObject;
-import org.eclipse.emf.spi.cdo.InternalCDOView;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- * @author Martin Fluegge
- * @since 2.0
- */
-public abstract class CDOLegacyWrapper extends CDOObjectWrapper
-{
- private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOLegacyWrapper.class);
-
- /**
- * This ThreadLocal map stores all pre-registered objects. This avoids a never-ending loop when setting the container
- * of an object.
- */
- private static ThreadLocal<Map<CDOID, CDOLegacyWrapper>> wrapperRegistry = new InheritableThreadLocal<Map<CDOID, CDOLegacyWrapper>>()
- {
- @Override
- protected Map<CDOID, CDOLegacyWrapper> initialValue()
- {
- return new HashMap<CDOID, CDOLegacyWrapper>();
- }
- };
-
- private static ThreadLocal<Counter> recursionCounter = new InheritableThreadLocal<Counter>()
- {
- @Override
- protected Counter initialValue()
- {
- return new Counter();
- }
- };
-
- protected CDOState state;
-
- protected InternalCDORevision revision;
-
- /**
- * It could happen that while <i>revisionToInstance()</i> is executed externally the <i>internalPostLoad()</i> method
- * will be called. This happens for example if <i>internalPostInvalidate()</i> is called. The leads to another
- * <i>revisionToInstance()</i> call while the first call has not finished. This is certainly not so cool. That's why
- * <b>underConstruction</b> will flag that <i>revisionToInstance()</i> is still running and avoid the second call.
- *
- * @since 3.0
- */
- private boolean underConstruction;
-
- public CDOLegacyWrapper(InternalEObject instance)
- {
- this.instance = instance;
- state = CDOState.TRANSIENT;
- }
-
- public CDOState cdoState()
- {
- return state;
- }
-
- public InternalCDORevision cdoRevision()
- {
- return revision;
- }
-
- @Override
- public CDOResourceImpl cdoResource()
- {
- revisionToInstanceResource();
- return super.cdoResource();
- }
-
- 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); //$NON-NLS-1$
- }
-
- CDOState oldState = this.state;
- this.state = state;
- adjustEProxy();
-
- if (view != null)
- {
- view.handleObjectStateChanged(this, oldState, state);
- }
-
- return oldState;
- }
-
- return null;
- }
-
- public void cdoInternalSetRevision(CDORevision revision)
- {
- if (TRACER.isEnabled())
- {
- TRACER.trace("Setting revision: " + revision); //$NON-NLS-1$
- }
-
- this.revision = (InternalCDORevision)revision;
- }
-
- public void cdoInternalPostAttach()
- {
- instanceToRevision();
-
- for (Adapter adapter : eAdapters())
- {
- if (!(adapter instanceof CDOObjectWrapper))
- {
- view.handleAddAdapter(this, adapter);
- view.subscribe(this, adapter);
- }
- }
- }
-
- public void cdoInternalPostDetach(boolean remote)
- {
- if (remote)
- {
- // Do nothing??
- return;
- }
-
- EClass eClass = revision.getEClass();
-
- // This loop adjusts the opposite wrapper objects to support dangling references. See Bugzilla_251263_Test
- for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
- {
- EReference oppositeReference = ((EStructuralFeature.Internal)feature).getEOpposite();
- if (oppositeReference != null && !oppositeReference.isContainment() && EMFUtil.isPersistent(oppositeReference))
- {
- if (feature.isMany())
- {
- int size = revision.size(feature);
- for (int i = 0; i < size; i++)
- {
- EObject object = (EObject)getValueFromRevision(feature, i);
- adjustPersistentOppositeReference(this, object, oppositeReference);
- }
- }
- else
- {
- EObject oppositeObject = (EObject)instance.eGet(feature);
- if (oppositeObject != null)
- {
- adjustPersistentOppositeReference(this, oppositeObject, oppositeReference);
- }
- }
- }
- }
- }
-
- /**
- * @since 3.0
- */
- public void cdoInternalPostRollback()
- {
- CDOStateMachine.INSTANCE.read(this);
- }
-
- /**
- * CDO persists the isUnset state of an eObject in the database. The indicator for this is that the feature is null in
- * the revision (see CDOStore.isSet()). When committing a legacy object all values in the instance for native
- * attributes are set with the java default values. So, these values will be stored in the revision and CDO cannot
- * distinguish whether the feature is set or not. This method must ensure that the value will be set to null if the
- * feature is not set.
- */
- public void cdoInternalPreCommit()
- {
- // We have to set this here because the CDOLegacyAdapter will not be notified when the instance is the target of a
- // single-directional containment reference.
- // If the container is not an legacy Object the system will get no information
- instanceToRevisionContainment();
-
- EClass eClass = revision.getEClass();
- for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
- {
- if (feature.isUnsettable())
- {
- if (!instance.eIsSet(feature))
- {
- if (feature.isMany())
- {
- @SuppressWarnings("unchecked")
- InternalEList<Object> list = (InternalEList<Object>)instance.eGet(feature);
- clearEList(list);
- }
- else
- {
- revision.set(feature, EStore.NO_INDEX, null);
- }
- }
- else if (instance.eGet(feature) == null)
- {
- // Must be single-valued!
- revision.set(feature, EStore.NO_INDEX, CDORevisionData.NIL);
- }
- }
- }
- }
-
- public void cdoInternalPreLoad()
- {
- // Do nothing
- }
-
- public void cdoInternalPostLoad()
- {
- // TODO Consider not remembering the revision after copying it to the instance (spare 1/2 of the space)
- revisionToInstance();
- }
-
- public void cdoInternalPostInvalidate()
- {
- if (cdoState() != CDOState.CLEAN)
- {
- InternalCDORevision revision = cdoView().getRevision(cdoID(), true);
- cdoInternalSetRevision(revision);
- }
-
- revisionToInstance();
- cdoInternalSetState(CDOState.CLEAN);
- }
-
- @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[" + instance.getClass().getSimpleName() + "@" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- protected void instanceToRevision()
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Transfering instance to revision: {0} --> {1}", instance, revision); //$NON-NLS-1$
- }
-
- // Handle containment
- instanceToRevisionContainment();
-
- // Handle values
- CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry();
- EClass eClass = revision.getEClass();
- for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
- {
- instanceToRevisionFeature(feature, packageRegistry);
- }
- }
-
- protected 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);
- revision.setContainingFeatureID(getInstanceContainerFeatureID(instance));
- }
- }
-
- protected void instanceToRevisionFeature(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
- {
- Object instanceValue = getInstanceValue(instance, feature, packageRegistry);
- CDOObjectImpl.instanceToRevisionFeature(view, this, feature, instanceValue);
- }
-
- protected void revisionToInstance()
- {
- if (underConstruction)
- {
- // Return if revisionToInstance was called before to avoid doubled calls
- return;
- }
-
- underConstruction = true;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Transfering revision to instance: {0} --> {1}", revision, instance); //$NON-NLS-1$
- }
-
- boolean deliver = instance.eDeliver();
- if (deliver)
- {
- instance.eSetDeliver(false);
- }
-
- Counter counter = recursionCounter.get();
- try
- {
- registerWrapper(this);
- counter.increment();
- view.registerObject(this);
-
- revisionToInstanceContainer();
-
- for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(revision.getEClass()))
- {
- revisionToInstanceFeature(feature);
- }
-
- revisionToInstanceResource();
- }
- catch (RuntimeException ex)
- {
- OM.LOG.error(ex);
- throw ex;
- }
- catch (Exception ex)
- {
- OM.LOG.error(ex);
- throw new CDOException(ex);
- }
- finally
- {
- if (deliver)
- {
- instance.eSetDeliver(true);
- }
-
- counter.decrement();
- unregisterWrapper(this);
- underConstruction = false;
- }
- }
-
- /**
- * @since 4.0
- */
- protected void revisionToInstanceContainer()
- {
- Object containerID = revision.getContainerID();
- InternalEObject container = getEObjectFromPotentialID(view, null, containerID);
-
- EObject oldContainer = instance.eContainer();
- if (oldContainer != container)
- {
- setInstanceContainer(container, revision.getContainingFeatureID());
- }
- }
-
- /**
- * @since 4.0
- */
- protected void revisionToInstanceResource()
- {
- if (revision != null)
- {
- CDOID resourceID = revision.getResourceID();
- InternalEObject resource = getEObjectFromPotentialID(view, null, resourceID);
- setInstanceResource((Resource.Internal)resource);
- if (resource != null)
- {
- view.registerObject((InternalCDOObject)resource);
- }
- }
- }
-
- /**
- * @since 3.0
- */
- protected void revisionToInstanceFeature(EStructuralFeature feature)
- {
- if (feature.isUnsettable() && !view.getStore().isSet(this, feature))
- {
- // Clarify if this is sufficient for bidirectional references
- instance.eUnset(feature);
- return;
- }
-
- if (feature.isMany())
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("State of Object (" + this + "/" + instance + ") is : " + state); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- if (state == CDOState.CLEAN || state == CDOState.PROXY || state == CDOState.NEW || state == CDOState.DIRTY)
- {
- int size = revision.size(feature);
-
- @SuppressWarnings("unchecked")
- InternalEList<Object> list = (InternalEList<Object>)instance.eGet(feature);
-
- clearEList(list);
- for (int i = 0; i < size; i++)
- {
- Object object = getValueFromRevision(feature, i);
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Adding " + object + " to feature " + feature + "in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- list.basicAdd(object, null);
- }
- }
- }
- else
- {
- // !feature.isMany()
- Object object = getValueFromRevision(feature, 0);
- if (feature instanceof EAttribute)
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Setting attribute value " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- // Just fake it for the store :(
- if (feature.isUnsettable() && object.equals(CDORevisionData.NIL))
- {
- eSet(feature, null);
- }
- else
- {
- eSet(feature, object);
- }
- }
- else
- {
- // EReferences
- if (TRACER.isEnabled())
- {
- TRACER.format("Adding object " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
-
- int featureID = instance.eClass().getFeatureID(feature);
- Class<? extends Object> baseClass = object == null ? null : object.getClass();
- EStructuralFeature.Internal internalFeature = (EStructuralFeature.Internal)feature;
- EReference oppositeReference = internalFeature.getEOpposite();
-
- if (oppositeReference != null)
- {
- if (object != null)
- {
- // If you have a containment reference but the container is not set, but the object is attached to a
- // resource
- // do not set the feature to null. Otherwise the object will be removed from the container which is the
- // resource instead of the original container. As a result the object will be detached. See
- // MapTest.testEObjectToEObjectValueContainedMap for more information
- if (object != instance.eContainer())
- {
- instance.eInverseAdd((InternalEObject)object, featureID, baseClass, null);
- }
-
- if (!EMFUtil.isPersistent(oppositeReference))
- {
- adjustTransientOppositeReference(instance, (InternalEObject)object, oppositeReference);
- }
- }
- }
- else
- {
- if (object != CDORevisionData.NIL)
- {
- EReference reference = (EReference)feature;
- if (reference.isContainment())
- {
- if (object != null)
- {
- // Calling eSet it not the optimal approach, but currently there is no other way to set the value here.
- // To avoid attaching already processed (clean) objects a check was introduced to
- // CDOResourceImpl.attached(EObject).
- // If we find a way to avoid the call of eSet and if we are able to only set the feature value directly
- // this check can be removed from CDOResourceImpl. See also Bug 352204.
- instance.eSet(feature, object);
- }
- else
- {
- instance.eSet(feature, null);
- }
- }
- else
- {
- instance.eSet(feature, object);
- }
- }
- else
- {
- instance.eSet(feature, null);
- }
- }
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Added object " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
- }
- }
- }
- }
-
- /**
- * This method retrieves the value from the feature at the given index. It retrieves the value either from the views's
- * store or the internal pre-registration Map.
- *
- * @param feature
- * the feature to retrieve the value from
- * @param index
- * the given index of the object in the feature
- * @return the value from the feature at the given index
- */
- private Object getValueFromRevision(EStructuralFeature feature, int index)
- {
- Object object = revision.get(feature, index);
- if (object == null)
- {
- return null;
- }
-
- if (object instanceof CDOElementProxy)
- {
- // Resolve proxy
- CDOElementProxy proxy = (CDOElementProxy)object;
- object = view.getSession().resolveElementProxy(revision, feature, index, proxy.getIndex());
- }
-
- if (object instanceof CDOLegacyWrapper)
- {
- return ((CDOLegacyWrapper)object).cdoInternalInstance();
- }
-
- CDOType type = CDOModelUtil.getType(feature.getEType());
- object = view.getStore().convertToEMF(instance, revision, feature, index, object);
-
- if (type == CDOType.OBJECT)
- {
- if (object instanceof CDOID)
- {
- CDOID id = (CDOID)object;
- if (id.isNull())
- {
- return null;
- }
-
- object = getRegisteredWrapper(id);
- if (object != null)
- {
- return ((CDOLegacyWrapper)object).cdoInternalInstance();
- }
-
- if (id.isExternal())
- {
- object = view.getResourceSet().getEObject(URI.createURI(id.toURIFragment()), true);
- }
- else
- {
- object = view.getObject(id);
- }
-
- if (object instanceof CDOObjectWrapper)
- {
- return ((CDOObjectWrapper)object).cdoInternalInstance();
- }
- }
- }
-
- return object;
- }
-
- protected Resource.Internal getInstanceResource(InternalEObject instance)
- {
- return instance.eDirectResource();
- }
-
- protected InternalEObject getInstanceContainer(InternalEObject instance)
- {
- return instance.eInternalContainer();
- }
-
- protected int getInstanceContainerFeatureID(InternalEObject instance)
- {
- return instance.eContainerFeatureID();
- }
-
- protected Object getInstanceValue(InternalEObject instance, EStructuralFeature feature,
- CDOPackageRegistry packageRegistry)
- {
- return instance.eGet(feature);
- }
-
- protected void setInstanceResource(Resource.Internal resource)
- {
- Method method = ReflectUtil.getMethod(instance.getClass(), "eSetDirectResource", Resource.Internal.class); //$NON-NLS-1$
- ReflectUtil.invokeMethod(method, instance, resource);
- }
-
- protected void setInstanceContainer(InternalEObject container, int containerFeatureID)
- {
- Method method = ReflectUtil.getMethod(instance.getClass(), "eBasicSetContainer", InternalEObject.class, int.class); //$NON-NLS-1$
- ReflectUtil.invokeMethod(method, instance, container, containerFeatureID);
- }
-
- protected void setInstanceValue(InternalEObject instance, EStructuralFeature feature, Object value)
- {
- instance.eSet(feature, 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!
- */
- protected InternalEObject getEObjectFromPotentialID(InternalCDOView view, EStructuralFeature feature,
- Object potentialID)
- {
- CDOLegacyWrapper wrapper;
- if (potentialID instanceof CDOID && (wrapper = getRegisteredWrapper((CDOID)potentialID)) != null)
- {
- potentialID = wrapper.instance;
-
- if (TRACER.isEnabled())
- {
- TRACER.format("Getting Object (" + potentialID + ") from localThread instead of the view"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- }
- else
- {
- if (potentialID instanceof CDOID)
- {
- CDOID id = (CDOID)potentialID;
- if (id.isNull())
- {
- return null;
- }
-
- if (id.isExternal())
- {
- URI uri = URI.createURI(id.toURIFragment());
- InternalEObject eObject = (InternalEObject)view.getResourceSet().getEObject(uri, true);
- return eObject;
- }
-
- 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)}
- */
- protected InternalEObject createProxy(InternalCDOView view, EStructuralFeature feature, CDOID id)
- {
- EClassifier eType = feature.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);
- }
-
- protected void clearEList(InternalEList<?> list)
- {
- for (int i = list.size() - 1; i >= 0; --i)
- {
- Object obj = list.get(i);
- list.basicRemove(obj, null);
- }
- }
-
- /**
- * TODO Consider using only EMF concepts for resolving proxies!
- */
- protected void resolveAllProxies()
- {
- CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry();
- EClass eClass = revision.getEClass();
- for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
- {
- if (feature instanceof EReference)
- {
- resolveProxies(feature, packageRegistry);
- }
- }
- }
-
- /**
- * 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!
- */
- protected void resolveProxies(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
- {
- Object value = getInstanceValue(instance, feature, packageRegistry);
- if (value != null)
- {
- if (feature.isMany())
- {
- @SuppressWarnings("unchecked")
- InternalEList<Object> list = (InternalEList<Object>)value;
- int size = list.size();
-
- boolean deliver = instance.eDeliver();
- if (deliver)
- {
- instance.eSetDeliver(false);
- }
-
- 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<Object>)list).basicSet(i, instance, null);
- // }
- // else
- // {
- list.set(i, instance);
- // }
- }
- }
-
- if (deliver)
- {
- instance.eSetDeliver(true);
- }
- }
- else
- {
- if (value instanceof LegacyProxy)
- {
- CDOID id = ((LegacyProxy)value).getID();
- InternalCDOObject resolved = (InternalCDOObject)view.getObject(id);
- InternalEObject instance = resolved.cdoInternalInstance();
- setInstanceValue(instance, feature, instance);
- }
- }
- }
- }
-
- protected 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); //$NON-NLS-1$
- if (TRACER.isEnabled())
- {
- TRACER.format("Setting proxyURI {0} for {1}", uri, instance); //$NON-NLS-1$
- }
-
- instance.eSetProxyURI(uri);
- }
- }
- else
- {
- if (instance.eIsProxy())
- {
- if (TRACER.isEnabled())
- {
- TRACER.format("Unsetting proxyURI for {0}", instance); //$NON-NLS-1$
- }
-
- instance.eSetProxyURI(null);
- }
- }
- }
-
- @Override
- public synchronized EList<Adapter> eAdapters()
- {
- EList<Adapter> adapters = instance.eAdapters();
- for (Adapter adapter : adapters)
- {
- if (!FSMUtil.isTransient(this) && !(adapter instanceof CDOLegacyWrapper))
- {
- cdoView().handleAddAdapter(this, adapter);
- }
- }
-
- return adapters;
- }
-
- public static boolean isLegacyProxy(Object object)
- {
- return object instanceof LegacyProxy;
- }
-
- protected 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 WrappedException.wrap(ex);
- }
- }
-
- /**
- * @since 3.0
- */
- protected static CDOLegacyWrapper getRegisteredWrapper(CDOID id)
- {
- return wrapperRegistry.get().get(id);
- }
-
- /**
- * Adds an object to the pre-registered objects list which hold all created objects even if they are not registered in
- * the view
- *
- * @since 3.0
- */
- protected static void registerWrapper(CDOLegacyWrapper wrapper)
- {
- wrapperRegistry.get().put(wrapper.cdoID(), wrapper);
- }
-
- /**
- * @since 3.0
- */
- protected static void unregisterWrapper(CDOLegacyWrapper wrapper)
- {
- wrapperRegistry.get().remove(wrapper.cdoID());
- }
-
- /**
- * @since 3.0
- */
- protected static boolean isRegisteredWrapper(CDOLegacyWrapper wrapper)
- {
- return wrapperRegistry.get().containsKey(wrapper.cdoID());
- }
-
- // TODO: Remove this method if it is ensured that ist is not needed anymore
- // private void adjustOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject, EReference
- // oppositeReference)
- // {
- // if (oppositeObject != null)
- // {
- // InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
- //
- // if (!FSMUtil.isTransient(oppositeCDOObject) && !EMFUtil.isPersistent(oppositeReference))
- // {
- // adjustPersistentOppositeReference(cdoObject, oppositeObject, oppositeReference);
- // }
- // else
- // {
- // if (oppositeReference.isResolveProxies())
- // {
- // adjustTransientOppositeReference(instance, (InternalEObject)oppositeObject, oppositeReference);
- // }
- // }
- // }
- // }
-
- private void adjustPersistentOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject,
- EReference oppositeReference)
- {
- InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
- if (oppositeCDOObject != null)
- {
- InternalCDOView view = oppositeCDOObject.cdoView();
- if (view != null)
- {
- CDOStore store = view.getStore();
- if (store != null)
- {
- if (oppositeReference.isMany())
- {
- EObject eObject = oppositeCDOObject.cdoInternalInstance();
-
- @SuppressWarnings("unchecked")
- EList<Object> list = (EList<Object>)eObject.eGet(oppositeReference);
- int index = list.indexOf(instance);
-
- if (!store.isEmpty(oppositeCDOObject, oppositeReference) && index != EStore.NO_INDEX)
- {
- store.set(oppositeCDOObject, oppositeReference, index, cdoObject);
- }
- }
- else
- {
- store.set(oppositeCDOObject, oppositeReference, 0, cdoObject);
- }
- }
- }
- }
- }
-
- private void adjustTransientOppositeReference(InternalEObject instance, InternalEObject object,
- EReference oppositeReference)
- {
- boolean wasDeliver = object.eDeliver(); // Disable notifications
- if (wasDeliver)
- {
- object.eSetDeliver(false);
- }
-
- try
- {
- if (oppositeReference.isMany())
- {
- @SuppressWarnings("unchecked")
- InternalEList<Object> list = (InternalEList<Object>)object.eGet(oppositeReference);
- list.basicAdd(instance, null);
- }
- else
- {
- if (object.eGet(oppositeReference) != instance)
- {
- object.eInverseAdd(instance, oppositeReference.getFeatureID(), ((EObject)instance).getClass(), null);
- }
- }
- }
- finally
- {
- if (wasDeliver)
- {
- object.eSetDeliver(true);
- }
- }
- }
-
- /**
- * @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"); //$NON-NLS-1$
-
- private static final Method eIsProxyMethod = ReflectUtil.getMethod(EObject.class, "eIsProxy"); //$NON-NLS-1$
-
- private static final Method eProxyURIMethod = ReflectUtil.getMethod(InternalEObject.class, "eProxyURI"); //$NON-NLS-1$
-
- 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 container's resource 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());
- }
- }
-
- /**
- * @author Martin Fluegge
- */
- private static final class Counter
- {
- private int value;
-
- public Counter()
- {
- }
-
- public void increment()
- {
- ++value;
- }
-
- public int decrement()
- {
- return --value;
- }
- }
-}
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * 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
+ * Martin Fluegge - bug 247226: Transparently support legacy models
+ */
+package org.eclipse.emf.internal.cdo.object;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.CDOState;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.model.CDOModelUtil;
+import org.eclipse.emf.cdo.common.model.CDOPackageRegistry;
+import org.eclipse.emf.cdo.common.model.CDOType;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.protocol.CDOProtocolConstants;
+import org.eclipse.emf.cdo.common.revision.CDOElementProxy;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+import org.eclipse.emf.cdo.common.revision.CDORevisionData;
+import org.eclipse.emf.cdo.common.security.CDOPermission;
+import org.eclipse.emf.cdo.common.util.CDOException;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.eresource.impl.CDOResourceImpl;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.util.CDOUtil;
+
+import org.eclipse.emf.internal.cdo.CDOObjectImpl;
+import org.eclipse.emf.internal.cdo.bundle.OM;
+import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
+
+import org.eclipse.net4j.util.ReflectUtil;
+import org.eclipse.net4j.util.WrappedException;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EAttribute;
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EClassifier;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.InternalEList;
+import org.eclipse.emf.spi.cdo.CDOStore;
+import org.eclipse.emf.spi.cdo.FSMUtil;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDOView;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Eike Stepper
+ * @author Martin Fluegge
+ * @since 2.0
+ */
+public abstract class CDOLegacyWrapper extends CDOObjectWrapper
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOLegacyWrapper.class);
+
+ /**
+ * This ThreadLocal map stores all pre-registered objects. This avoids a never-ending loop when setting the container
+ * of an object.
+ */
+ private static ThreadLocal<Map<CDOID, CDOLegacyWrapper>> wrapperRegistry = new InheritableThreadLocal<Map<CDOID, CDOLegacyWrapper>>()
+ {
+ @Override
+ protected Map<CDOID, CDOLegacyWrapper> initialValue()
+ {
+ return new HashMap<CDOID, CDOLegacyWrapper>();
+ }
+ };
+
+ private static ThreadLocal<Counter> recursionCounter = new InheritableThreadLocal<Counter>()
+ {
+ @Override
+ protected Counter initialValue()
+ {
+ return new Counter();
+ }
+ };
+
+ protected CDOState state;
+
+ protected InternalCDORevision revision;
+
+ /**
+ * It could happen that while <i>revisionToInstance()</i> is executed externally the <i>internalPostLoad()</i> method
+ * will be called. This happens for example if <i>internalPostInvalidate()</i> is called. The leads to another
+ * <i>revisionToInstance()</i> call while the first call has not finished. This is certainly not so cool. That's why
+ * <b>underConstruction</b> will flag that <i>revisionToInstance()</i> is still running and avoid the second call.
+ *
+ * @since 3.0
+ */
+ private boolean underConstruction;
+
+ public CDOLegacyWrapper(InternalEObject instance)
+ {
+ this.instance = instance;
+ state = CDOState.TRANSIENT;
+ }
+
+ public CDOState cdoState()
+ {
+ return state;
+ }
+
+ public InternalCDORevision cdoRevision()
+ {
+ return revision;
+ }
+
+ @Override
+ public CDOResourceImpl cdoResource()
+ {
+ revisionToInstanceResource();
+ return super.cdoResource();
+ }
+
+ 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); //$NON-NLS-1$
+ }
+
+ CDOState oldState = this.state;
+ this.state = state;
+ adjustEProxy();
+
+ if (view != null)
+ {
+ view.handleObjectStateChanged(this, oldState, state);
+ }
+
+ return oldState;
+ }
+
+ return null;
+ }
+
+ public void cdoInternalSetRevision(CDORevision revision)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.trace("Setting revision: " + revision); //$NON-NLS-1$
+ }
+
+ this.revision = (InternalCDORevision)revision;
+ }
+
+ public void cdoInternalPostAttach()
+ {
+ instanceToRevision();
+
+ for (Adapter adapter : eAdapters())
+ {
+ if (!(adapter instanceof CDOObjectWrapper))
+ {
+ view.handleAddAdapter(this, adapter);
+ view.subscribe(this, adapter);
+ }
+ }
+ }
+
+ public void cdoInternalPostDetach(boolean remote)
+ {
+ if (remote)
+ {
+ // Do nothing??
+ return;
+ }
+
+ EClass eClass = revision.getEClass();
+
+ // This loop adjusts the opposite wrapper objects to support dangling references. See Bugzilla_251263_Test
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
+ {
+ EReference oppositeReference = ((EStructuralFeature.Internal)feature).getEOpposite();
+ if (oppositeReference != null && !oppositeReference.isContainment() && EMFUtil.isPersistent(oppositeReference))
+ {
+ if (feature.isMany())
+ {
+ int size = revision.size(feature);
+ for (int i = 0; i < size; i++)
+ {
+ EObject object = (EObject)getValueFromRevision(feature, i);
+ adjustPersistentOppositeReference(this, object, oppositeReference);
+ }
+ }
+ else
+ {
+ EObject oppositeObject = (EObject)instance.eGet(feature);
+ if (oppositeObject != null)
+ {
+ adjustPersistentOppositeReference(this, oppositeObject, oppositeReference);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ public void cdoInternalPostRollback()
+ {
+ CDOStateMachine.INSTANCE.read(this);
+ }
+
+ /**
+ * CDO persists the isUnset state of an eObject in the database. The indicator for this is that the feature is null in
+ * the revision (see CDOStore.isSet()). When committing a legacy object all values in the instance for native
+ * attributes are set with the java default values. So, these values will be stored in the revision and CDO cannot
+ * distinguish whether the feature is set or not. This method must ensure that the value will be set to null if the
+ * feature is not set.
+ */
+ public void cdoInternalPreCommit()
+ {
+ // We have to set this here because the CDOLegacyAdapter will not be notified when the instance is the target of a
+ // single-directional containment reference.
+ // If the container is not an legacy Object the system will get no information
+ instanceToRevisionContainment();
+
+ EClass eClass = revision.getEClass();
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
+ {
+ if (feature.isUnsettable())
+ {
+ if (!instance.eIsSet(feature))
+ {
+ if (feature.isMany())
+ {
+ @SuppressWarnings("unchecked")
+ InternalEList<Object> list = (InternalEList<Object>)instance.eGet(feature);
+ clearEList(list);
+ }
+ else
+ {
+ revision.set(feature, EStore.NO_INDEX, null);
+ }
+ }
+ else if (instance.eGet(feature) == null)
+ {
+ // Must be single-valued!
+ revision.set(feature, EStore.NO_INDEX, CDORevisionData.NIL);
+ }
+ }
+ }
+ }
+
+ public void cdoInternalPreLoad()
+ {
+ // Do nothing
+ }
+
+ public void cdoInternalPostLoad()
+ {
+ // TODO Consider not remembering the revision after copying it to the instance (spare 1/2 of the space)
+ revisionToInstance();
+ }
+
+ public void cdoInternalPostInvalidate()
+ {
+ if (cdoState() != CDOState.CLEAN)
+ {
+ InternalCDORevision revision = cdoView().getRevision(cdoID(), true);
+ cdoInternalSetRevision(revision);
+ }
+
+ revisionToInstance();
+ cdoInternalSetState(CDOState.CLEAN);
+ }
+
+ @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[" + instance.getClass().getSimpleName() + "@" + id + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ protected void instanceToRevision()
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Transfering instance to revision: {0} --> {1}", instance, revision); //$NON-NLS-1$
+ }
+
+ // Handle containment
+ instanceToRevisionContainment();
+
+ // Handle values
+ CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry();
+ EClass eClass = revision.getEClass();
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
+ {
+ instanceToRevisionFeature(feature, packageRegistry);
+ }
+ }
+
+ protected 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);
+ revision.setContainingFeatureID(getInstanceContainerFeatureID(instance));
+ }
+ }
+
+ protected void instanceToRevisionFeature(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
+ {
+ Object instanceValue = getInstanceValue(instance, feature, packageRegistry);
+ CDOObjectImpl.instanceToRevisionFeature(view, this, feature, instanceValue);
+ }
+
+ protected void revisionToInstance()
+ {
+
+ if (underConstruction)
+ {
+ // Return if revisionToInstance was called before to avoid doubled calls
+ return;
+ }
+
+ underConstruction = true;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Transfering revision to instance: {0} --> {1}", revision, instance); //$NON-NLS-1$
+ }
+
+ boolean deliver = instance.eDeliver();
+ if (deliver)
+ {
+ instance.eSetDeliver(false);
+ }
+
+ Counter counter = recursionCounter.get();
+
+ try
+ {
+ registerWrapper(this);
+ counter.increment();
+ view.registerObject(this);
+
+ revisionToInstanceContainer();
+
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(revision.getEClass()))
+ {
+ revisionToInstanceFeature(feature);
+ }
+
+ revisionToInstanceResource();
+ }
+ catch (RuntimeException ex)
+ {
+ OM.LOG.error(ex);
+ throw ex;
+ }
+ catch (Exception ex)
+ {
+ OM.LOG.error(ex);
+ throw new CDOException(ex);
+ }
+ finally
+ {
+ if (deliver)
+ {
+ instance.eSetDeliver(true);
+ }
+
+ counter.decrement();
+ unregisterWrapper(this);
+ underConstruction = false;
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void revisionToInstanceContainer()
+ {
+ CDOPermission permission = revision.getPermission();
+ if (permission != CDOPermission.WRITE)
+ {
+ revision.setPermission(CDOPermission.WRITE);
+ }
+
+ try
+ {
+ Object containerID = revision.getContainerID();
+ InternalEObject container = getEObjectFromPotentialID(view, null, containerID);
+ EObject oldContainer = instance.eContainer();
+ if (oldContainer != container)
+ {
+ setInstanceContainer(container, revision.getContainingFeatureID());
+ }
+ }
+ finally
+ {
+ if (permission != CDOPermission.WRITE)
+ {
+ revision.setPermission(permission);
+ }
+ }
+ }
+
+ /**
+ * @since 4.0
+ */
+ protected void revisionToInstanceResource()
+ {
+ if (revision != null)
+ {
+ CDOID resourceID = revision.getResourceID();
+ InternalEObject resource = getEObjectFromPotentialID(view, null, resourceID);
+ setInstanceResource((Resource.Internal)resource);
+ if (resource != null)
+ {
+ view.registerObject((InternalCDOObject)resource);
+ }
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected void revisionToInstanceFeature(EStructuralFeature feature)
+ {
+ if (feature.isUnsettable() && !view.getStore().isSet(this, feature))
+ {
+ // Clarify if this is sufficient for bidirectional references
+ instance.eUnset(feature);
+ return;
+ }
+
+ if (feature.isMany())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("State of Object (" + this + "/" + instance + ") is : " + state); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ if (state == CDOState.CLEAN || state == CDOState.PROXY || state == CDOState.NEW || state == CDOState.DIRTY)
+ {
+ int size = revision.size(feature);
+
+ @SuppressWarnings("unchecked")
+ InternalEList<Object> list = (InternalEList<Object>)instance.eGet(feature);
+
+ clearEList(list);
+ for (int i = 0; i < size; i++)
+ {
+ Object object = getValueFromRevision(feature, i);
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Adding " + object + " to feature " + feature + "in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ list.basicAdd(object, null);
+ }
+ }
+ }
+ else
+ {
+ // !feature.isMany()
+ Object object = getValueFromRevision(feature, 0);
+ if (feature instanceof EAttribute)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Setting attribute value " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ // Just fake it for the store :(
+ if (feature.isUnsettable() && object.equals(CDORevisionData.NIL))
+ {
+ eSet(feature, null);
+ }
+ else
+ {
+ eSet(feature, object);
+ }
+ }
+ else
+ {
+ // EReferences
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Adding object " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ int featureID = instance.eClass().getFeatureID(feature);
+ Class<? extends Object> baseClass = object == null ? null : object.getClass();
+ EStructuralFeature.Internal internalFeature = (EStructuralFeature.Internal)feature;
+ EReference oppositeReference = internalFeature.getEOpposite();
+
+ if (oppositeReference != null)
+ {
+ if (object != null)
+ {
+ // If you have a containment reference but the container is not set, but the object is attached to a
+ // resource
+ // do not set the feature to null. Otherwise the object will be removed from the container which is the
+ // resource instead of the original container. As a result the object will be detached. See
+ // MapTest.testEObjectToEObjectValueContainedMap for more information
+ if (object != instance.eContainer())
+ {
+ instance.eInverseAdd((InternalEObject)object, featureID, baseClass, null);
+ }
+
+ if (!EMFUtil.isPersistent(oppositeReference))
+ {
+ adjustTransientOppositeReference(instance, (InternalEObject)object, oppositeReference);
+ }
+ }
+ }
+ else
+ {
+ if (object != CDORevisionData.NIL)
+ {
+ EReference reference = (EReference)feature;
+ if (reference.isContainment())
+ {
+ if (object != null)
+ {
+ // Calling eSet it not the optimal approach, but currently there is no other way to set the value here.
+ // To avoid attaching already processed (clean) objects a check was introduced to
+ // CDOResourceImpl.attached(EObject).
+ // If we find a way to avoid the call of eSet and if we are able to only set the feature value directly
+ // this check can be removed from CDOResourceImpl. See also Bug 352204.
+ instance.eSet(feature, object);
+ }
+ else
+ {
+ instance.eSet(feature, null);
+ }
+ }
+ else
+ {
+ instance.eSet(feature, object);
+ }
+ }
+ else
+ {
+ instance.eSet(feature, null);
+ }
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Added object " + object + " to feature " + feature + " in instance " + instance); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ }
+ }
+
+ /**
+ * This method retrieves the value from the feature at the given index. It retrieves the value either from the views's
+ * store or the internal pre-registration Map.
+ *
+ * @param feature
+ * the feature to retrieve the value from
+ * @param index
+ * the given index of the object in the feature
+ * @return the value from the feature at the given index
+ */
+ private Object getValueFromRevision(EStructuralFeature feature, int index)
+ {
+ Object object = revision.get(feature, index);
+ if (object == null)
+ {
+ return null;
+ }
+
+ if (object instanceof CDOElementProxy)
+ {
+ // Resolve proxy
+ CDOElementProxy proxy = (CDOElementProxy)object;
+ object = view.getSession().resolveElementProxy(revision, feature, index, proxy.getIndex());
+ }
+
+ if (object instanceof CDOLegacyWrapper)
+ {
+ return ((CDOLegacyWrapper)object).cdoInternalInstance();
+ }
+
+ CDOType type = CDOModelUtil.getType(feature.getEType());
+ object = view.getStore().convertToEMF(instance, revision, feature, index, object);
+
+ if (type == CDOType.OBJECT)
+ {
+ if (object instanceof CDOID)
+ {
+ CDOID id = (CDOID)object;
+ if (id.isNull())
+ {
+ return null;
+ }
+
+ object = getRegisteredWrapper(id);
+ if (object != null)
+ {
+ return ((CDOLegacyWrapper)object).cdoInternalInstance();
+ }
+
+ if (id.isExternal())
+ {
+ object = view.getResourceSet().getEObject(URI.createURI(id.toURIFragment()), true);
+ }
+ else
+ {
+ object = view.getObject(id);
+ }
+
+ if (object instanceof CDOObjectWrapper)
+ {
+ return ((CDOObjectWrapper)object).cdoInternalInstance();
+ }
+ }
+ }
+
+ return object;
+ }
+
+ protected Resource.Internal getInstanceResource(InternalEObject instance)
+ {
+ return instance.eDirectResource();
+ }
+
+ protected InternalEObject getInstanceContainer(InternalEObject instance)
+ {
+ return instance.eInternalContainer();
+ }
+
+ protected int getInstanceContainerFeatureID(InternalEObject instance)
+ {
+ return instance.eContainerFeatureID();
+ }
+
+ protected Object getInstanceValue(InternalEObject instance, EStructuralFeature feature,
+ CDOPackageRegistry packageRegistry)
+ {
+ return instance.eGet(feature);
+ }
+
+ protected void setInstanceResource(Resource.Internal resource)
+ {
+ Method method = ReflectUtil.getMethod(instance.getClass(), "eSetDirectResource", Resource.Internal.class); //$NON-NLS-1$
+ ReflectUtil.invokeMethod(method, instance, resource);
+ }
+
+ protected void setInstanceContainer(InternalEObject container, int containerFeatureID)
+ {
+ Method method = ReflectUtil.getMethod(instance.getClass(), "eBasicSetContainer", InternalEObject.class, int.class); //$NON-NLS-1$
+ ReflectUtil.invokeMethod(method, instance, container, containerFeatureID);
+ }
+
+ protected void setInstanceValue(InternalEObject instance, EStructuralFeature feature, Object value)
+ {
+ instance.eSet(feature, 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!
+ */
+ protected InternalEObject getEObjectFromPotentialID(InternalCDOView view, EStructuralFeature feature,
+ Object potentialID)
+ {
+ CDOLegacyWrapper wrapper;
+ if (potentialID instanceof CDOID && (wrapper = getRegisteredWrapper((CDOID)potentialID)) != null)
+ {
+ potentialID = wrapper.instance;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Getting Object (" + potentialID + ") from localThread instead of the view"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ else
+ {
+ if (potentialID instanceof CDOID)
+ {
+ CDOID id = (CDOID)potentialID;
+ if (id.isNull())
+ {
+ return null;
+ }
+
+ if (id.isExternal())
+ {
+ URI uri = URI.createURI(id.toURIFragment());
+ InternalEObject eObject = (InternalEObject)view.getResourceSet().getEObject(uri, true);
+ return eObject;
+ }
+
+ 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)}
+ */
+ protected InternalEObject createProxy(InternalCDOView view, EStructuralFeature feature, CDOID id)
+ {
+ EClassifier eType = feature.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);
+ }
+
+ protected void clearEList(InternalEList<?> list)
+ {
+ for (int i = list.size() - 1; i >= 0; --i)
+ {
+ Object obj = list.get(i);
+ list.basicRemove(obj, null);
+ }
+ }
+
+ /**
+ * TODO Consider using only EMF concepts for resolving proxies!
+ */
+ protected void resolveAllProxies()
+ {
+ CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry();
+ EClass eClass = revision.getEClass();
+ for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
+ {
+ if (feature instanceof EReference)
+ {
+ resolveProxies(feature, packageRegistry);
+ }
+ }
+ }
+
+ /**
+ * 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!
+ */
+ protected void resolveProxies(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
+ {
+ Object value = getInstanceValue(instance, feature, packageRegistry);
+ if (value != null)
+ {
+ if (feature.isMany())
+ {
+ @SuppressWarnings("unchecked")
+ InternalEList<Object> list = (InternalEList<Object>)value;
+ int size = list.size();
+
+ boolean deliver = instance.eDeliver();
+ if (deliver)
+ {
+ instance.eSetDeliver(false);
+ }
+
+ 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<Object>)list).basicSet(i, instance, null);
+ // }
+ // else
+ // {
+ list.set(i, instance);
+ // }
+ }
+ }
+
+ if (deliver)
+ {
+ instance.eSetDeliver(true);
+ }
+ }
+ else
+ {
+ if (value instanceof LegacyProxy)
+ {
+ CDOID id = ((LegacyProxy)value).getID();
+ InternalCDOObject resolved = (InternalCDOObject)view.getObject(id);
+ InternalEObject instance = resolved.cdoInternalInstance();
+ setInstanceValue(instance, feature, instance);
+ }
+ }
+ }
+ }
+
+ protected 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); //$NON-NLS-1$
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Setting proxyURI {0} for {1}", uri, instance); //$NON-NLS-1$
+ }
+
+ instance.eSetProxyURI(uri);
+ }
+ }
+ else
+ {
+ if (instance.eIsProxy())
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Unsetting proxyURI for {0}", instance); //$NON-NLS-1$
+ }
+
+ instance.eSetProxyURI(null);
+ }
+ }
+ }
+
+ @Override
+ public synchronized EList<Adapter> eAdapters()
+ {
+ EList<Adapter> adapters = instance.eAdapters();
+ for (Adapter adapter : adapters)
+ {
+ if (!FSMUtil.isTransient(this) && !(adapter instanceof CDOLegacyWrapper))
+ {
+ cdoView().handleAddAdapter(this, adapter);
+ }
+ }
+
+ return adapters;
+ }
+
+ public static boolean isLegacyProxy(Object object)
+ {
+ return object instanceof LegacyProxy;
+ }
+
+ protected 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 WrappedException.wrap(ex);
+ }
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected static CDOLegacyWrapper getRegisteredWrapper(CDOID id)
+ {
+ return wrapperRegistry.get().get(id);
+ }
+
+ /**
+ * Adds an object to the pre-registered objects list which hold all created objects even if they are not registered in
+ * the view
+ *
+ * @since 3.0
+ */
+ protected static void registerWrapper(CDOLegacyWrapper wrapper)
+ {
+ wrapperRegistry.get().put(wrapper.cdoID(), wrapper);
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected static void unregisterWrapper(CDOLegacyWrapper wrapper)
+ {
+ wrapperRegistry.get().remove(wrapper.cdoID());
+ }
+
+ /**
+ * @since 3.0
+ */
+ protected static boolean isRegisteredWrapper(CDOLegacyWrapper wrapper)
+ {
+ return wrapperRegistry.get().containsKey(wrapper.cdoID());
+ }
+
+ // TODO: Remove this method if it is ensured that ist is not needed anymore
+ // private void adjustOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject, EReference
+ // oppositeReference)
+ // {
+ // if (oppositeObject != null)
+ // {
+ // InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
+ //
+ // if (!FSMUtil.isTransient(oppositeCDOObject) && !EMFUtil.isPersistent(oppositeReference))
+ // {
+ // adjustPersistentOppositeReference(cdoObject, oppositeObject, oppositeReference);
+ // }
+ // else
+ // {
+ // if (oppositeReference.isResolveProxies())
+ // {
+ // adjustTransientOppositeReference(instance, (InternalEObject)oppositeObject, oppositeReference);
+ // }
+ // }
+ // }
+ // }
+
+ private void adjustPersistentOppositeReference(InternalCDOObject cdoObject, EObject oppositeObject,
+ EReference oppositeReference)
+ {
+ InternalCDOObject oppositeCDOObject = (InternalCDOObject)CDOUtil.getCDOObject(oppositeObject);
+ if (oppositeCDOObject != null)
+ {
+ InternalCDOView view = oppositeCDOObject.cdoView();
+ if (view != null)
+ {
+ CDOStore store = view.getStore();
+ if (store != null)
+ {
+ if (oppositeReference.isMany())
+ {
+ EObject eObject = oppositeCDOObject.cdoInternalInstance();
+
+ @SuppressWarnings("unchecked")
+ EList<Object> list = (EList<Object>)eObject.eGet(oppositeReference);
+ int index = list.indexOf(instance);
+
+ if (!store.isEmpty(oppositeCDOObject, oppositeReference) && index != EStore.NO_INDEX)
+ {
+ store.set(oppositeCDOObject, oppositeReference, index, cdoObject);
+ }
+ }
+ else
+ {
+ store.set(oppositeCDOObject, oppositeReference, 0, cdoObject);
+ }
+ }
+ }
+ }
+ }
+
+ private void adjustTransientOppositeReference(InternalEObject instance, InternalEObject object,
+ EReference oppositeReference)
+ {
+ boolean wasDeliver = object.eDeliver(); // Disable notifications
+ if (wasDeliver)
+ {
+ object.eSetDeliver(false);
+ }
+
+ try
+ {
+ if (oppositeReference.isMany())
+ {
+ @SuppressWarnings("unchecked")
+ InternalEList<Object> list = (InternalEList<Object>)object.eGet(oppositeReference);
+ list.basicAdd(instance, null);
+ }
+ else
+ {
+ if (object.eGet(oppositeReference) != instance)
+ {
+ object.eInverseAdd(instance, oppositeReference.getFeatureID(), ((EObject)instance).getClass(), null);
+ }
+ }
+ }
+ finally
+ {
+ if (wasDeliver)
+ {
+ object.eSetDeliver(true);
+ }
+ }
+ }
+
+ /**
+ * @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"); //$NON-NLS-1$
+
+ private static final Method eIsProxyMethod = ReflectUtil.getMethod(EObject.class, "eIsProxy"); //$NON-NLS-1$
+
+ private static final Method eProxyURIMethod = ReflectUtil.getMethod(InternalEObject.class, "eProxyURI"); //$NON-NLS-1$
+
+ 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 container's resource 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());
+ }
+ }
+
+ /**
+ * @author Martin Fluegge
+ */
+ private static final class Counter
+ {
+ private int value;
+
+ public Counter()
+ {
+ }
+
+ public void increment()
+ {
+ ++value;
+ }
+
+ public int decrement()
+ {
+ return --value;
+ }
+ }
+}

Back to the top