blob: 706e4c8f82ffaf1ad80cbcc3340fc6c28b4f4205 [file] [log] [blame]
/*
* Copyright (c) 2007-2015 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
* Simon McDuff - maintenance
* Christian W. Damus - partial/conditional persistence of features
*/
package org.eclipse.emf.internal.cdo;
import org.eclipse.emf.cdo.CDOLock;
import org.eclipse.emf.cdo.CDOObject;
import org.eclipse.emf.cdo.CDOObjectHistory;
import org.eclipse.emf.cdo.CDOState;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.lock.CDOLockState;
import org.eclipse.emf.cdo.common.model.CDOModelUtil;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.security.CDOPermission;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOClassInfo;
import org.eclipse.emf.cdo.spi.common.model.InternalCDOClassInfo.PersistenceFilter;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.util.CDOUtil;
import org.eclipse.emf.cdo.view.CDOView;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.messages.Messages;
import org.eclipse.emf.internal.cdo.object.CDOLockImpl;
import org.eclipse.emf.internal.cdo.view.CDOStateMachine;
import org.eclipse.net4j.util.ObjectUtil;
import org.eclipse.net4j.util.concurrent.IRWLockManager.LockType;
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.NotificationChain;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.notify.impl.BasicNotifierImpl.EObservableAdapterList.Listener;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.BasicEMap;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Internal.DynamicValueHolder;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.BasicEObjectImpl;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EStoreEObjectImpl;
import org.eclipse.emf.ecore.impl.EStoreEObjectImpl.BasicEStoreFeatureMap;
import org.eclipse.emf.ecore.impl.MinimalEStoreEObjectImpl;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.Resource.Internal;
import org.eclipse.emf.ecore.util.EcoreEMap;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
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.InternalCDOLoadable;
import org.eclipse.emf.spi.cdo.InternalCDOObject;
import org.eclipse.emf.spi.cdo.InternalCDOView;
import org.eclipse.emf.spi.cdo.InternalCDOView.ViewAndState;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* The base class of all <em>native</em> {@link CDOObject objects}.
*
* @author Eike Stepper
*/
public class CDOObjectImpl extends MinimalEStoreEObjectImpl implements InternalCDOObject
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_OBJECT, CDOObjectImpl.class);
private static final EObservableAdapterList.Listener ADAPTERS_LISTENER = new EObservableAdapterList.Listener()
{
public void added(Notifier notifier, Adapter adapter)
{
CDOObjectImpl object = (CDOObjectImpl)notifier;
object.eAdapterAdded(adapter);
}
public void removed(Notifier notifier, Adapter adapter)
{
CDOObjectImpl object = (CDOObjectImpl)notifier;
object.eAdapterRemoved(adapter);
}
};
private static final EObservableAdapterList.Listener[] ADAPTERS_LISTENERS = { ADAPTERS_LISTENER };
/**
* Optimized storage of {@link CDOObject#cdoView()} and {@link CDOObject#cdoState()}.
*
* @see ViewAndState
*/
private ViewAndState viewAndState = ViewAndState.TRANSIENT;
/**
* Optimized storage of {@link CDOObject#cdoID()} and {@link CDOObject#cdoRevision()}.
* The idea is that, if a revision is set, the object's ID is equal to the {@link CDORevision revision's} ID.
* The same is true for the classInfo field.
*/
private InternalCDORevision revision;
/**
* Don't use the optional slot in MinimalEObject because a CDOObject always needs eSettings to store:
* <ul>
* <li>the values of all features of transient objects,
* <li>the values of transient features of persistent objects and
* <li>the list wrappers of all many-valued features of all objects.
* </ul>
*/
private Object[] eSettings;
public CDOObjectImpl()
{
initClassInfo(eStaticClass());
}
/**
* @since 4.2
*/
public final InternalCDOClassInfo cdoClassInfo()
{
return revision.getClassInfo();
}
public final CDOState cdoState()
{
return viewAndState.state;
}
/**
* @since 2.0
*/
public final InternalCDOView cdoView()
{
return viewAndState.view;
}
public final CDOID cdoID()
{
return revision.getID();
}
/**
* @since 2.0
*/
public final InternalCDORevision cdoRevision()
{
return revision.getProperRevision();
}
/**
* @since 4.3
*/
public final InternalCDORevision cdoRevision(boolean loadOnDemand)
{
InternalCDORevision revision = cdoRevision();
if (revision == null && loadOnDemand)
{
revision = CDOStateMachine.INSTANCE.read(this);
}
return revision;
}
/**
* @since 4.3
*/
public final CDOPermission cdoPermission()
{
if (FSMUtil.isTransient(this))
{
return CDOPermission.WRITE;
}
return cdoRevision(true).getPermission();
}
public final CDOResource cdoResource()
{
Resource resource = eResource();
if (resource instanceof CDOResource)
{
return (CDOResource)resource;
}
return null;
}
/**
* @since 2.0
*/
public final CDOResource cdoDirectResource()
{
Resource.Internal resource = eDirectResource();
if (resource instanceof CDOResource)
{
return (CDOResource)resource;
}
return null;
}
/**
* @since 3.0
*/
public final void cdoPrefetch(int depth)
{
CDOID id = cdoID();
viewAndState.view.prefetchRevisions(id, depth);
}
@Deprecated
public final void cdoReload()
{
CDOStateMachine.INSTANCE.reload(this);
}
/**
* @since 4.2
*/
public final CDOObjectHistory cdoHistory()
{
return viewAndState.view.getHistory(this);
}
/**
* @since 2.0
*/
public final boolean cdoConflict()
{
return FSMUtil.isConflict(this);
}
/**
* @since 2.0
*/
public final boolean cdoInvalid()
{
return FSMUtil.isInvalid(this);
}
/**
* @since 2.0
*/
public final CDOLock cdoReadLock()
{
return createLock(this, LockType.READ);
}
/**
* @since 2.0
*/
public final CDOLock cdoWriteLock()
{
return createLock(this, LockType.WRITE);
}
/**
* @since 4.1
*/
public final CDOLock cdoWriteOption()
{
return createLock(this, LockType.OPTION);
}
/**
* @since 4.1
*/
public final CDOLockState cdoLockState()
{
return getLockState(this);
}
public final CDOState cdoInternalSetState(CDOState state)
{
CDOState oldState = viewAndState.state;
if (oldState != state)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting state {0} for {1}", state, this); //$NON-NLS-1$
}
viewAndState = viewAndState.getViewAndState(state);
if (viewAndState.view != null)
{
viewAndState.view.handleObjectStateChanged(this, oldState, state);
}
return oldState;
}
return null;
}
/**
* @since 2.0
*/
public final void cdoInternalSetView(CDOView view)
{
InternalCDOView newView = (InternalCDOView)view;
if (newView != null)
{
viewAndState = newView.getViewAndState(viewAndState.state);
}
else
{
viewAndState = ViewAndState.TRANSIENT.getViewAndState(viewAndState.state);
}
}
public final void cdoInternalSetID(CDOID id)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting ID: {0}", id); //$NON-NLS-1$
}
if (id == null)
{
revision = cdoClassInfo().getRevisionForID(null);
}
else
{
revision = revision.getRevisionForID(id);
}
}
/**
* @since 2.0
*/
public final void cdoInternalSetRevision(CDORevision revision)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting revision: {0}", revision); //$NON-NLS-1$
}
if (revision == null)
{
InternalCDOClassInfo classInfo = cdoClassInfo();
CDOID id = this.revision.getID();
this.revision = classInfo.getRevisionForID(id);
}
else
{
if (this.revision != null)
{
CDOID objectID = this.revision.getID();
if (objectID != null && objectID != revision.getID())
{
throw new IllegalArgumentException("The revision " + revision + " does not match the object " + objectID);
}
}
this.revision = (InternalCDORevision)revision;
}
}
public final void cdoInternalSetResource(CDOResource resource)
{
// Unsets direct resource and/or eContainer.
// Only intended to be called by CDOTransactionImpl.removeObject(CDOID, CDOObject).
// See bug 383370.
if (resource != null)
{
throw new IllegalArgumentException(
"Only intended to be called by CDOTransactionImpl.removeObject(CDOID, CDOObject");
}
super.eSetDirectResource(null);
eBasicSetContainer(null);
eBasicSetContainerFeatureID(0);
}
/**
* @since 2.0
*/
public void cdoInternalPreLoad()
{
// Do nothing
}
public final void cdoInternalPostLoad()
{
// Reset EMAP objects
Object[] eSettings = eBasicSettings();
if (eSettings != null)
{
// Make sure transient features are kept but persisted values are not cached.
InternalCDOClassInfo classInfo = cdoClassInfo();
int featureCount = classInfo.getEClass().getFeatureCount();
for (int i = 0; i < featureCount; i++)
{
// We need to keep the existing list if possible.
int index = classInfo.getSettingsFeatureIndex(i);
if (index != InternalCDOClassInfo.NO_SLOT)
{
Object value = eSettings[index];
if (value instanceof InternalCDOLoadable)
{
((InternalCDOLoadable)value).cdoInternalPostLoad();
}
}
}
}
}
/**
* @since 2.0
*/
public final void cdoInternalPostInvalidate()
{
cdoInternalSetRevision(null);
}
public final void cdoInternalPostAttach()
{
if (TRACER.isEnabled())
{
TRACER.format("Populating revision for {0}", this); //$NON-NLS-1$
}
InternalEObject eContainer = eBasicInternalContainer();
int eContainerFeatureID = eBasicContainerFeatureID();
InternalCDORevision revision = cdoRevision();
revision.setContainerID(eContainer == null ? CDOID.NULL : viewAndState.view.convertObjectToID(eContainer, true));
revision.setContainingFeatureID(eContainerFeatureID);
Resource directResource = eDirectResource();
if (directResource instanceof CDOResource)
{
CDOResource cdoResource = (CDOResource)directResource;
revision.setResourceID(cdoResource.cdoID());
}
if (eSettings != null)
{
InternalCDOClassInfo classInfo = cdoClassInfo();
EStructuralFeature[] allPersistentFeatures = classInfo.getAllPersistentFeatures();
int length = allPersistentFeatures.length;
for (int i = 0; i < length; i++)
{
EStructuralFeature eFeature = allPersistentFeatures[i];
int transientIndex = classInfo.getTransientFeatureIndex(eFeature);
Object setting = eSettings[transientIndex]; // Can be safely accessed directly because we come from TRANSIENT
instanceToRevisionFeature(viewAndState.view, this, eFeature, setting);
}
cdoRevision().setUnchunked();
int newSize = classInfo.getSettingsFeatureCount();
if (newSize != eSettings.length)
{
Object[] newSettings = new Object[newSize];
System.arraycopy(eSettings, 0, newSettings, 0, newSize);
eSettings = newSettings;
}
}
}
/**
* It is really important for accessing the data to go through {@link #cdoStore()}. {@link #eStore()} will redirect
* you to the transient data.
*
* @since 2.0
*/
public void cdoInternalPostDetach(boolean remote)
{
if (remote)
{
eSetDeliver(false);
eBasicSetContainer(null, eContainerFeatureID());
eSetDeliver(true);
return;
}
if (TRACER.isEnabled())
{
TRACER.format("Depopulating revision for {0}", this); //$NON-NLS-1$
}
InternalCDOClassInfo classInfo = cdoClassInfo();
CDOStore store = cdoStore();
super.eSetDirectResource((Resource.Internal)store.getResource(this));
eBasicSetContainer(store.getContainer(this));
eBasicSetContainerFeatureID(store.getContainingFeatureID(this));
if (eSettings != null)
{
int newSize = classInfo.getSettingsFeatureCount() + classInfo.getTransientFeatureCount();
if (newSize != eSettings.length)
{
Object[] newSettings = new Object[newSize];
System.arraycopy(eSettings, 0, newSettings, 0, eSettings.length);
eSettings = newSettings;
}
}
InternalCDORevision revision = cdoRevision();
EStructuralFeature[] allPersistentFeatures = classInfo.getAllPersistentFeatures();
for (EStructuralFeature eFeature : allPersistentFeatures)
{
revisionToInstanceFeature(this, revision, eFeature);
}
}
/**
* @since 3.0
*/
public final void cdoInternalPostRollback()
{
// Do nothing
}
public final void cdoInternalPreCommit()
{
// Do nothing
}
public final InternalEObject cdoInternalInstance()
{
return this;
}
@Deprecated
public final EStructuralFeature cdoInternalDynamicFeature(int dynamicFeatureID)
{
throw new UnsupportedOperationException();
}
private CDOStore cdoStore()
{
return viewAndState.view.getStore();
}
/**
* @since 2.0
*/
@Override
public final EStore eStore()
{
if (FSMUtil.isTransient(this))
{
return TransientStore.INSTANCE;
}
return cdoStore();
}
@Override
protected final EClass eDynamicClass()
{
EClass eClass = eClass();
if (eClass == eStaticClass())
{
return null;
}
return eClass;
}
@Override
public final EClass eClass()
{
return revision.getEClass();
}
@Override
public void eSetClass(EClass eClass)
{
initClassInfo(eClass);
}
/**
* @since 2.0
*/
@Override
public Resource.Internal eDirectResource()
{
if (FSMUtil.isTransient(this))
{
return super.eDirectResource();
}
return (Resource.Internal)cdoStore().getResource(this);
}
@Override
public final Resource.Internal eInternalResource()
{
CDOView view = cdoView();
if (view != null && view.isClosed())
{
return null;
}
if (FSMUtil.isInvalid(this))
{
return null;
}
return super.eInternalResource();
}
@Override
public final Object dynamicGet(int dynamicFeatureID)
{
InternalCDOClassInfo classInfo = cdoClassInfo();
int index = classInfo.getSettingsFeatureIndex(dynamicFeatureID);
if (index == InternalCDOClassInfo.NO_SLOT)
{
// The feature has no slot in eSettings, i.e., it's persistent or single-valued.
// Delegate to the store. TransientStore delegates back to eSettings.
EStructuralFeature eStructuralFeature = eDynamicFeature(dynamicFeatureID);
EStore eStore = eStore();
return eStore.get(this, eStructuralFeature, EStore.NO_INDEX);
}
// Here we know that the feature is transient or many-valued, hence it has a slot in eSettings.
Object[] eSettings = eBasicSettings();
Object result = eSettings[index];
if (result == null)
{
EStructuralFeature eStructuralFeature = eDynamicFeature(dynamicFeatureID);
if (classInfo.isPersistent(dynamicFeatureID))
{
if (FeatureMapUtil.isFeatureMap(eStructuralFeature))
{
eSettings[index] = result = createFeatureMap(eStructuralFeature);
}
else if (eStructuralFeature.isMany())
{
eSettings[index] = result = createList(eStructuralFeature);
}
}
}
return result;
}
@Override
public final void dynamicSet(int dynamicFeatureID, Object value)
{
InternalCDOClassInfo classInfo = cdoClassInfo();
int index = classInfo.getSettingsFeatureIndex(dynamicFeatureID);
if (index == InternalCDOClassInfo.NO_SLOT)
{
// The feature has no slot in eSettings, i.e., it's persistent or single-valued.
// Delegate to the store. TransientStore delegates back to eSettings.
EStructuralFeature eStructuralFeature = eDynamicFeature(dynamicFeatureID);
EStore eStore = eStore();
eStore.set(this, eStructuralFeature, EStore.NO_INDEX, value);
}
else
{
Object[] eSettings = eBasicSettings();
eSettings[index] = value;
}
}
@Override
public final void dynamicUnset(int dynamicFeatureID)
{
InternalCDOClassInfo classInfo = cdoClassInfo();
int index = classInfo.getSettingsFeatureIndex(dynamicFeatureID);
if (index == InternalCDOClassInfo.NO_SLOT)
{
// The feature has no slot in eSettings, i.e., it's persistent or single-valued.
// Delegate to the store. TransientStore delegates back to eSettings.
EStructuralFeature eStructuralFeature = eDynamicFeature(dynamicFeatureID);
EStore eStore = eStore();
eStore.unset(this, eStructuralFeature);
}
else
{
Object[] eSettings = eBasicSettings();
Object oldValue = eSettings[index];
if (oldValue != null)
{
if (oldValue instanceof InternalEList)
{
InternalEList<?> list = (InternalEList<?>)oldValue;
if (list instanceof InternalEList.Unsettable)
{
((InternalEList.Unsettable<?>)list).unset();
}
else
{
list.clear();
}
}
else
{
eSettings[index] = null;
}
}
}
}
/**
* @since 2.0
*/
@Override
protected final boolean eDynamicIsSet(int dynamicFeatureID, EStructuralFeature eFeature)
{
if (dynamicFeatureID < 0)
{
return eOpenIsSet(eFeature);
}
InternalCDOClassInfo classInfo = cdoClassInfo();
if (classInfo.isPersistent(dynamicFeatureID))
{
return eStore().isSet(this, eFeature);
}
return eSettingDelegate(eFeature).dynamicIsSet(this, eSettings(), dynamicFeatureID);
}
@Override
public final InternalEObject eInternalContainer()
{
if (FSMUtil.isTransient(this) || FSMUtil.isInvalid(this))
{
return eBasicInternalContainer();
}
return cdoStore().getContainer(this);
}
@Override
public final int eContainerFeatureID()
{
if (FSMUtil.isTransient(this) || FSMUtil.isInvalid(this))
{
return eBasicContainerFeatureID();
}
return cdoStore().getContainingFeatureID(this);
}
/**
* Code taken from {@link BasicEObjectImpl#eBasicSetContainer} and modified to detect whether the object is moved in the
* same context. (E.g.: An object is moved from resA to resB. resA and resB belongs to the same CDORepositoryInfo.
* Without this special handling, a detach and newObject will be generated for the object moved)
*
* @since 2.0
*/
@Override
public final NotificationChain eBasicSetContainer(InternalEObject newContainer, int newContainerFeatureID,
NotificationChain msgs)
{
boolean isResource = this instanceof CDOResource;
boolean isRootResource = isResource && ((CDOResource)this).isRoot();
InternalEObject oldContainer = eInternalContainer();
Resource.Internal oldResource = eDirectResource();
Resource.Internal newResource = null;
if (oldResource != null)
{
if (newContainer != null && !eContainmentFeature(this, newContainer, newContainerFeatureID).isResolveProxies())
{
msgs = ((InternalEList<?>)oldResource.getContents()).basicRemove(this, msgs);
eSetDirectResource(null);
newResource = newContainer.eInternalResource();
}
else
{
oldResource = null;
}
}
else
{
if (oldContainer != null)
{
oldResource = oldContainer.eInternalResource();
}
if (newContainer != null)
{
newResource = newContainer.eInternalResource();
}
}
CDOView oldView = viewAndState.view;
CDOView newView = newResource != null && newResource instanceof CDOResource ? ((CDOResource)newResource).cdoView()
: null;
boolean moved = oldView != null && oldView == newView;
if (!moved && oldResource != null && oldResource != newResource && !isRootResource)
{
oldResource.detached(this);
}
int oldContainerFeatureID = eContainerFeatureID();
eBasicSetContainer(newContainer, newContainerFeatureID);
if (!moved && newResource != null && newResource != oldResource)
{
newResource.attached(this);
}
if (eNotificationRequired())
{
if (oldContainer != null && oldContainerFeatureID >= 0 && oldContainerFeatureID != newContainerFeatureID)
{
ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, oldContainerFeatureID,
oldContainer, null);
if (msgs == null)
{
msgs = notification;
}
else
{
msgs.add(notification);
}
}
if (newContainerFeatureID >= 0)
{
ENotificationImpl notification = new ENotificationImpl(this, Notification.SET, newContainerFeatureID,
oldContainerFeatureID == newContainerFeatureID ? oldContainer : null, newContainer);
if (msgs == null)
{
msgs = notification;
}
else
{
msgs.add(notification);
}
}
}
return msgs;
}
/**
* Code took from {@link BasicEObjectImpl#eSetResource} and modify it to detect when object are moved in the same
* context.
*
* @since 2.0
*/
@Override
public final NotificationChain eSetResource(Resource.Internal resource, NotificationChain notifications)
{
Resource.Internal oldResource = eDirectResource();
CDOView oldView = viewAndState.view;
CDOView newView = resource != null && resource instanceof CDOResource ? ((CDOResource)resource).cdoView() : null;
boolean isSameView;
if (viewAndState.state == CDOState.NEW)
{
isSameView = false;
}
else
{
isSameView = oldView != null && oldView == newView;
}
if (oldResource != null && resource != null)
{
notifications = ((InternalEList<?>)oldResource.getContents()).basicRemove(this, notifications);
// When setting the resource to null we assume that detach has already been called in the resource implementation
if (!isSameView)
{
oldResource.detached(this);
}
}
InternalEObject oldContainer = eInternalContainer();
if (oldContainer != null && !isSameView)
{
if (eContainmentFeature().isResolveProxies())
{
Resource.Internal oldContainerResource = oldContainer.eInternalResource();
if (oldContainerResource != null)
{
// If we're not setting a new resource, attach it to the old container's resource.
if (resource == null)
{
oldContainerResource.attached(this);
}
// If we didn't detach it from an old resource already, detach it from the old container's resource.
//
else if (oldResource == null)
{
oldContainerResource.detached(this);
}
}
}
else
{
notifications = eBasicRemoveFromContainer(notifications);
notifications = eBasicSetContainer(null, -1, notifications);
}
}
eSetDirectResource(resource);
return notifications;
}
@Override
protected final void eSetDirectResource(Internal resource)
{
if (FSMUtil.isTransient(this))
{
super.eSetDirectResource(resource);
}
else if (resource == null || resource instanceof CDOResource)
{
cdoStore().setContainer(this, (CDOResource)resource, eInternalContainer(), eContainerFeatureID());
}
else
{
throw new IllegalArgumentException(Messages.getString("CDOObjectImpl.8")); //$NON-NLS-1$
}
}
@Override
protected final void eBasicSetContainer(InternalEObject newEContainer, int newContainerFeatureID)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting container: {0}, featureID={1}", newEContainer, newContainerFeatureID); //$NON-NLS-1$
}
if (FSMUtil.isTransient(this) || FSMUtil.isInvalid(this))
{
super.eBasicSetContainer(newEContainer, newContainerFeatureID);
}
else
{
CDOStore cdoStore = cdoStore();
cdoStore.setContainer(this, cdoDirectResource(), newEContainer, newContainerFeatureID);
}
}
@Override
protected final int eDynamicFeatureID(EStructuralFeature eStructuralFeature)
{
// CDOObjectImpl has no static features, so don't subract their count here:
EClass eClass = eClass();
return eClass.getFeatureID(eStructuralFeature);
}
@Override
protected final EStructuralFeature eDynamicFeature(int dynamicFeatureID)
{
// CDOObjectImpl has no static features, so don't add their count here:
EClass eClass = eClass();
return eClass.getEStructuralFeature(dynamicFeatureID);
}
@Override
protected final DynamicValueHolder eSettings()
{
if (!eHasSettings())
{
InternalCDOClassInfo classInfo = cdoClassInfo();
int size = classInfo.getSettingsFeatureCount();
if (FSMUtil.isTransient(this))
{
size += classInfo.getTransientFeatureCount();
}
if (size != 0)
{
eBasicSetSettings(new Object[size]);
}
}
return this;
}
@Override
protected final boolean eHasSettings()
{
return eSettings != null;
}
@Override
protected final Object[] eBasicSettings()
{
return eSettings;
}
@Override
protected final void eBasicSetSettings(Object[] settings)
{
eSettings = settings;
}
/**
* Don't cache non-transient features in this CDOObject's {@link #eSettings()}.
*/
@Override
protected final boolean eIsCaching()
{
return false;
}
@Override
protected final Adapter[] eContainerAdapterArray()
{
if (FSMUtil.isTransient(this))
{
return super.eContainerAdapterArray();
}
InternalCDOView view = cdoView();
if (view.isClosed())
{
return null;
}
CDOObject container;
InternalCDORevision revision = cdoRevision();
if (revision != null)
{
Object containerID = revision.getContainerID();
if (containerID instanceof CDOID)
{
container = view.getObject((CDOID)containerID, false);
}
else
{
container = (CDOObject)containerID;
}
if (container != null)
{
if (container instanceof CDOObjectImpl)
{
return ((CDOObjectImpl)container).eBasicAdapterArray();
}
if (container instanceof BasicEObjectImpl)
{
return super.eContainerAdapterArray();
}
}
}
return null;
}
/**
* This method is not called by the MinimalEStoreEObjectImpl in CDO's ecore.minimal (retrofitting) fragment
* but it is called by the normal MinimalEObjectImpl as of EMF 2.9.
*
* @since 4.2
*/
@Override
protected final Listener[] eBasicAdapterListeners()
{
Listener[] listeners = super.eBasicAdapterListeners();
if (listeners == null)
{
return ADAPTERS_LISTENERS;
}
return listeners;
}
/**
* This method is not called by the MinimalEStoreEObjectImpl in CDO's ecore.minimal (retrofitting) fragment
* but it is called by the normal MinimalEObjectImpl as of EMF 2.9.
*
* @since 4.2
*/
@Override
protected final void eBasicSetAdapterListeners(Listener[] eAdapterListeners)
{
if (eAdapterListeners != null)
{
if (eAdapterListeners.length == 1)
{
// Because noone else can remove ADAPTERS_LISTENER this must be it
eAdapterListeners = null;
}
}
super.eBasicSetAdapterListeners(eAdapterListeners);
}
/**
* This method must not be private because the MinimalEStoreEObjectImpl in CDO's ecore.minimal (retrofitting) fragment
* introduces it as a protected method and calls it.
*
* @since 4.2
*/
protected final void eAdapterAdded(Adapter adapter)
{
if (!FSMUtil.isTransient(this))
{
viewAndState.view.handleAddAdapter(this, adapter);
}
}
/**
* This method must not be private because the MinimalEStoreEObjectImpl in CDO's ecore.minimal (retrofitting) fragment
* introduces it as a protected method and calls it.
*
* @since 4.2
*/
protected final void eAdapterRemoved(Adapter adapter)
{
if (!FSMUtil.isTransient(this))
{
viewAndState.view.handleRemoveAdapter(this, adapter);
}
}
/**
* Specializing the behaviour of {@link #hashCode()} is not permitted as per {@link EObject} specification.
*/
@Override
public final int hashCode()
{
return super.hashCode();
}
/**
* Specializing the behaviour of {@link #equals(Object)} is not permitted as per {@link EObject} specification.
*/
@Override
public final boolean equals(Object obj)
{
return super.equals(obj);
}
@Override
public String toString()
{
String str = eClass().getName();
CDOID id = cdoID();
if (id != null)
{
str += "@" + id;
}
CDOState cdoState = cdoState();
if (cdoState != CDOState.CLEAN)
{
str += "[" + cdoState + "]";
}
return str;
}
@Override
protected final FeatureMap createFeatureMap(EStructuralFeature eStructuralFeature)
{
return new CDOStoreFeatureMap(eStructuralFeature);
}
/**
* @since 4.1
*/
protected final CDOStoreEcoreEMap createMap(EStructuralFeature eStructuralFeature)
{
return new CDOStoreEcoreEMap(eStructuralFeature);
}
/**
* @since 4.1
*/
protected final CDOStoreUnorderedEList<Object> createUnorderedList(EStructuralFeature eStructuralFeature)
{
return new CDOStoreUnorderedEList<Object>(eStructuralFeature);
}
@Override
protected EList<?> createList(EStructuralFeature eStructuralFeature)
{
if (isMap(eStructuralFeature))
{
return createMap(eStructuralFeature);
}
if (!eStructuralFeature.isOrdered())
{
return createUnorderedList(eStructuralFeature);
}
return super.createList(eStructuralFeature);
}
private boolean isMap(EStructuralFeature eStructuralFeature)
{
// Answer from Christian Damus:
// Java ensures that string constants are interned, so == is actually more efficient than equals() and it's correct
return eStructuralFeature.getEType().getInstanceClassName() == "java.util.Map$Entry"; //$NON-NLS-1$
}
private void initClassInfo(EClass eClass)
{
InternalCDOClassInfo classInfo = (InternalCDOClassInfo)CDOModelUtil.getClassInfo(eClass);
revision = classInfo.getRevisionForID(null);
}
/**
* Adjust the reference ONLY if the opposite reference used CDOID. This is true ONLY if the state of <cdo>this</code>
* was not {@link CDOState#NEW}.
*/
private static void adjustOppositeReference(InternalCDOObject instance, InternalEObject opposite,
EReference oppositeReference)
{
if (opposite != null)
{
InternalCDOObject cdoObject = (InternalCDOObject)CDOUtil.getCDOObject(opposite);
if (cdoObject != null && !FSMUtil.isTransient(cdoObject))
{
if (oppositeReference.isMany())
{
EStore eStore = cdoObject.eStore();
int index = eStore.indexOf(cdoObject, oppositeReference, instance.cdoID());
if (index != -1)
{
eStore.set(cdoObject, oppositeReference, index, instance);
}
}
else
{
EStore eStore = cdoObject.eStore();
eStore.set(cdoObject, oppositeReference, 0, instance);
}
}
else
{
if (oppositeReference.isResolveProxies())
{
// We should not trigger events. But we have no choice :-(.
if (oppositeReference.isMany())
{
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>)opposite.eGet(oppositeReference);
int index = list.indexOf(instance);
if (index != -1)
{
list.set(index, instance);
}
}
else
{
opposite.eSet(oppositeReference, instance);
}
}
}
}
}
/**
* @since 3.0
*/
public static void instanceToRevisionFeature(InternalCDOView view, InternalCDOObject object,
EStructuralFeature feature, Object setting)
{
if (TRACER.isEnabled())
{
TRACER.format("Populating feature {0}", feature); //$NON-NLS-1$
}
PersistenceFilter filter = ((InternalCDOClassInfo)CDOModelUtil.getClassInfo(feature.getEContainingClass()))
.getPersistenceFilter(feature);
if (filter != null)
{
if (TRACER.isEnabled())
{
TRACER.format("Filtering value of feature {0}", feature); //$NON-NLS-1$
}
setting = filter.getPersistableValue(object, setting);
}
CDOStore cdoStore = view.getStore();
InternalCDORevision revision = object.cdoRevision();
if (feature.isMany())
{
if (setting != null)
{
int index = 0;
@SuppressWarnings("unchecked")
EList<Object> list = (EList<Object>)setting;
for (Object value : list)
{
Object cdoValue = cdoStore.convertToCDO(object, feature, value);
revision.add(feature, index++, cdoValue);
}
}
}
else
{
Object cdoValue = cdoStore.convertToCDO(object, feature, setting);
revision.set(feature, 0, cdoValue);
}
}
/**
* @since 2.0
*/
public static void revisionToInstanceFeature(InternalCDOObject instance, InternalCDORevision revision,
EStructuralFeature eFeature)
{
if (TRACER.isEnabled())
{
TRACER.format("Depopulating feature {0}", eFeature); //$NON-NLS-1$
}
EStructuralFeature.Internal internalFeature = (EStructuralFeature.Internal)eFeature;
InternalCDOView view = instance.cdoView();
EReference oppositeReference = view.isObjectNew(instance.cdoID()) ? null : internalFeature.getEOpposite();
CDOStore cdoStore = view.getStore();
EStore eStore = instance.eStore();
if (eFeature.isMany())
{
int size = cdoStore.size(instance, eFeature);
for (int index = 0; index < size; index++)
{
// Do not trigger events
// Do not trigger inverse updates
Object opposite = cdoStore.get(instance, eFeature, index);
eStore.add(instance, eFeature, index, opposite);
if (oppositeReference != null)
{
adjustOppositeReference(instance, (InternalEObject)opposite, oppositeReference);
}
}
}
else
{
Object opposite = cdoStore.get(instance, eFeature, EStore.NO_INDEX);
eStore.set(instance, eFeature, EStore.NO_INDEX, opposite);
if (oppositeReference != null)
{
adjustOppositeReference(instance, (InternalEObject)opposite, oppositeReference);
}
}
}
/**
* @since 4.1
*/
public static CDOLock createLock(InternalCDOObject object, LockType type)
{
if (FSMUtil.isTransient(object))
{
throw new IllegalStateException("Call CDOView.lockObjects() for transient object " + object);
}
return new CDOLockImpl(object, type);
}
/**
* @since 4.1
*/
public static CDOLockState getLockState(InternalCDOObject object)
{
if (!FSMUtil.isTransient(object))
{
InternalCDOView view = object.cdoView();
CDOID id = object.cdoID();
return view.getLockStates(Collections.singletonList(id))[0];
}
return null;
}
/**
* Implements an internal EStore for TRANSIENT objects, where there is no view or {@link CDOStore}.
* <p>
* Feature values are actually stored in {@link CDOObjectImpl#cdoSettings}.
*
* @author Simon McDuff
* @since 2.0
*/
private static final class TransientStore implements InternalEObject.EStore
{
public static TransientStore INSTANCE = new TransientStore();
private TransientStore()
{
}
private Object getValue(InternalEObject eObject, int transientIndex)
{
Object[] settings = getSettings(eObject);
return settings[transientIndex];
}
private EList<Object> getValueAsList(InternalEObject eObject, int transientIndex)
{
Object[] settings = getSettings(eObject);
@SuppressWarnings("unchecked")
EList<Object> result = (EList<Object>)settings[transientIndex];
if (result == null)
{
result = new BasicEList<Object>();
settings[transientIndex] = result;
}
return result;
}
private Object setValue(InternalEObject eObject, int transientIndex, Object newValue)
{
Object[] settings = getSettings(eObject);
Object oldValue = settings[transientIndex];
settings[transientIndex] = newValue;
return oldValue;
}
private Object[] getSettings(InternalEObject eObject)
{
CDOObjectImpl object = (CDOObjectImpl)eObject;
object.eSettings(); // Ensure that the array is created
return object.eSettings;
}
private int getTransientFeatureIndex(InternalEObject eObject, EStructuralFeature feature)
{
CDOObjectImpl object = (CDOObjectImpl)eObject;
InternalCDOClassInfo classInfo = object.cdoClassInfo();
return classInfo.getTransientFeatureIndex(feature);
}
public Object get(InternalEObject eObject, EStructuralFeature feature, int index)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
if (index != NO_INDEX)
{
return getValueAsList(eObject, transientIndex).get(index);
}
return getValue(eObject, transientIndex);
}
public Object set(InternalEObject eObject, EStructuralFeature feature, int index, Object value)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
if (index != NO_INDEX)
{
return getValueAsList(eObject, transientIndex).set(index, value);
}
return setValue(eObject, transientIndex, value);
}
public void add(InternalEObject eObject, EStructuralFeature feature, int index, Object value)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
getValueAsList(eObject, transientIndex).add(index, value);
}
public Object remove(InternalEObject eObject, EStructuralFeature feature, int index)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).remove(index);
}
public Object move(InternalEObject eObject, EStructuralFeature feature, int targetIndex, int sourceIndex)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).move(targetIndex, sourceIndex);
}
public void clear(InternalEObject eObject, EStructuralFeature feature)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
if (feature.isMany())
{
getValueAsList(eObject, transientIndex).clear();
}
setValue(eObject, transientIndex, null);
}
public int size(InternalEObject eObject, EStructuralFeature feature)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).size();
}
public int indexOf(InternalEObject eObject, EStructuralFeature feature, Object value)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).indexOf(value);
}
public int lastIndexOf(InternalEObject eObject, EStructuralFeature feature, Object value)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).lastIndexOf(value);
}
public Object[] toArray(InternalEObject eObject, EStructuralFeature feature)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).toArray();
}
public <T> T[] toArray(InternalEObject eObject, EStructuralFeature feature, T[] array)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).toArray(array);
}
public boolean isEmpty(InternalEObject eObject, EStructuralFeature feature)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).isEmpty();
}
public boolean contains(InternalEObject eObject, EStructuralFeature feature, Object value)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).contains(value);
}
public int hashCode(InternalEObject eObject, EStructuralFeature feature)
{
int transientIndex = getTransientFeatureIndex(eObject, feature);
return getValueAsList(eObject, transientIndex).hashCode();
}
public InternalEObject getContainer(InternalEObject eObject)
{
return null;
}
public EStructuralFeature getContainingFeature(InternalEObject eObject)
{
throw new UnsupportedOperationException("Should never be called");
}
public EObject create(EClass eClass)
{
throw new UnsupportedOperationException("Should never be called");
}
public boolean isSet(InternalEObject eObject, EStructuralFeature feature)
{
if (!feature.isUnsettable())
{
if (feature.isMany())
{
@SuppressWarnings("unchecked")
List<Object> list = (InternalEList<Object>)eObject.eGet(feature, false);
return list != null && !list.isEmpty();
}
return !ObjectUtil.equals(eObject.eGet(feature, false), feature.getDefaultValue());
}
Object[] settings = ((CDOObjectImpl)eObject).eSettings;
if (settings == null)
{
return false;
}
int transientIndex = getTransientFeatureIndex(eObject, feature);
return settings[transientIndex] != null;
}
public void unset(InternalEObject eObject, EStructuralFeature feature)
{
CDOObjectImpl cdoObject = (CDOObjectImpl)eObject;
if (feature.isMany())
{
// Object object = get(eObject, feature, NO_INDEX);
Object object = cdoObject.eGet(feature, false);
if (object instanceof List<?>)
{
List<?> list = (List<?>)object;
list.clear();
}
}
else
{
Object[] settings = cdoObject.eSettings;
if (settings == null)
{
// Is already unset
return;
}
int transientIndex = getTransientFeatureIndex(eObject, feature);
settings[transientIndex] = null;
}
}
}
/**
* For internal use only.
*
* @author Eike Stepper
* @since 4.1
*/
private final class CDOStoreEcoreEMap extends EcoreEMap<Object, Object>implements InternalCDOLoadable
{
private static final long serialVersionUID = 1L;
public CDOStoreEcoreEMap(EStructuralFeature eStructuralFeature)
{
super((EClass)eStructuralFeature.getEType(), BasicEMap.Entry.class, null);
delegateEList = new EStoreEObjectImpl.BasicEStoreEList<BasicEMap.Entry<Object, Object>>(CDOObjectImpl.this,
eStructuralFeature)
{
private static final long serialVersionUID = 1L;
@Override
public void unset()
{
super.unset();
doClear();
}
@Override
protected void didAdd(int index, BasicEMap.Entry<Object, Object> newObject)
{
CDOStoreEcoreEMap.this.doPut(newObject);
}
@Override
protected void didSet(int index, BasicEMap.Entry<Object, Object> newObject,
BasicEMap.Entry<Object, Object> oldObject)
{
didRemove(index, oldObject);
didAdd(index, newObject);
}
@Override
protected void didRemove(int index, BasicEMap.Entry<Object, Object> oldObject)
{
CDOStoreEcoreEMap.this.doRemove(oldObject);
}
@Override
protected void didClear(int size, Object[] oldObjects)
{
CDOStoreEcoreEMap.this.doClear();
}
@Override
protected void didMove(int index, BasicEMap.Entry<Object, Object> movedObject, int oldIndex)
{
CDOStoreEcoreEMap.this.doMove(movedObject);
}
};
size = delegateEList.size();
}
private void checkListForReading()
{
if (!FSMUtil.isTransient(CDOObjectImpl.this))
{
CDOStateMachine.INSTANCE.read(CDOObjectImpl.this);
}
}
/**
* Ensures that the entry data is created and is populated with contents of the delegate list.
*/
@Override
protected synchronized void ensureEntryDataExists()
{
checkListForReading();
super.ensureEntryDataExists();
}
@Override
public int size()
{
checkListForReading();
return size;
}
@Override
public boolean isEmpty()
{
checkListForReading();
return size == 0;
}
@Override
public boolean contains(Object object)
{
checkListForReading();
return super.contains(object);
}
@Override
public boolean containsAll(Collection<?> collection)
{
checkListForReading();
return super.containsAll(collection);
}
@Override
public boolean containsKey(Object key)
{
checkListForReading();
return super.containsKey(key);
}
@Override
public boolean containsValue(Object value)
{
checkListForReading();
return super.containsValue(value);
}
public void cdoInternalPreLoad()
{
}
public void cdoInternalPostLoad()
{
entryData = null;
size = delegateEList.size();
}
}
/**
* For internal use only.
*
* @author Andras Peteri
* @since 4.1
*/
private final class CDOStoreUnorderedEList<E> extends EStoreEObjectImpl.BasicEStoreEList<E>
{
private static final long serialVersionUID = 1L;
public CDOStoreUnorderedEList(EStructuralFeature feature)
{
super(CDOObjectImpl.this, feature);
}
@Override
public E remove(int index)
{
boolean oldObjectIsLast = index == size() - 1;
E oldObject = super.remove(index);
if (!oldObjectIsLast)
{
move(index, size() - 1);
}
return oldObject;
}
}
/**
* For internal use only.
*
* @author Eike Stepper
*/
private final class CDOStoreFeatureMap extends BasicEStoreFeatureMap
{
private static final long serialVersionUID = 1L;
public CDOStoreFeatureMap(EStructuralFeature eStructuralFeature)
{
super(CDOObjectImpl.this, eStructuralFeature);
}
}
}