/**
* Copyright (c) 2004 - 2009 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
*/
package org.eclipse.emf.internal.cdo;
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.protocol.CDOProtocolConstants;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.internal.common.model.GenUtil;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.util.FSMUtil;
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.Notification;
import org.eclipse.emf.common.notify.impl.NotifyingListImpl;
import org.eclipse.emf.common.util.URI;
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.EcorePackage;
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.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 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;
/**
* @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 abstract class CDOLegacyWrapper extends CDOObjectWrapper
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOLegacyWrapper.class);
protected CDOState state;
protected InternalCDORevision revision;
private boolean allProxiesResolved;
public CDOLegacyWrapper(InternalEObject instance)
{
this.instance = instance;
state = CDOState.TRANSIENT;
}
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); //$NON-NLS-1$
}
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); //$NON-NLS-1$
}
this.revision = (InternalCDORevision)revision;
}
public void cdoInternalPostAttach()
{
instanceToRevision();
// 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(boolean remote)
{
// 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 cdoInternalPreLoad()
{
}
public void cdoInternalPostLoad()
{
// TODO Consider not remembering the revisin after copying it to the instance (spare 1/2 of the space)
revisionToInstance();
}
public void cdoInternalPostInvalidate()
{
}
public void cdoInternalCleanup()
{
}
@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 + "]"; //$NON-NLS-1$ //$NON-NLS-2$
}
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.cdoID());
revision.setContainingFeatureID(getInstanceContainerFeatureID(instance));
}
}
protected void instanceToRevisionFeature(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
{
Object instanceValue = getInstanceValue(instance, feature, packageRegistry);
CDOObjectImpl.instanceToRevisionFeature(view, revision, feature, instanceValue);
}
/**
* TODO Simon: Fix this whole mess ;-)
*/
protected void revisionToInstance()
{
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);
}
try
{
// Handle containment
revisionToInstanceContainment();
// Handle values
CDOPackageRegistry packageRegistry = cdoView().getSession().getPackageRegistry();
EClass eClass = revision.getEClass();
for (EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
{
revisionToInstanceFeature(feature, packageRegistry);
}
}
finally
{
if (deliver)
{
instance.eSetDeliver(true);
}
}
}
protected 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());
}
protected void revisionToInstanceFeature(EStructuralFeature feature, CDOPackageRegistry packageRegistry)
{
// Attempt 4
Object value = revision.getValue(feature);
view.getStore().set(instance, feature, Notification.NO_INDEX, value);
}
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);
}
/**
* TODO Ed: Help to fix whole mess (avoid inverse updates)
*/
private void setInstanceValueOLD(InternalEObject instance, EStructuralFeature 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.getEType() == EcorePackage.eINSTANCE.getEBoolean())
{
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"; //$NON-NLS-1$
int flagsMask = getEFlagMask(instanceClass, flagName);
field = ReflectUtil.getField(instanceClass, "eFlags"); //$NON-NLS-1$
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); //$NON-NLS-1$
}
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. null
indicates that proxy creation will be
* avoided!
*/
protected InternalEObject getEObjectFromPotentialID(InternalCDOView view, EStructuralFeature 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 proxy 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:
*
true
* * 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); } /** * TODO Ed: Fix whole mess ;-) */ protected void clearEList(InternalEList