blob: 98afef0b97e5dbef2091ad52da5a7ad87dac2f79 [file] [log] [blame]
/***************************************************************************
* Copyright (c) 2004 - 2008 Eike Stepper, Germany.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Eike Stepper - initial API and implementation
* Simon McDuff - http://bugs.eclipse.org/201266
* Eike Stepper & Simon McDuff - http://bugs.eclipse.org/204890
* Simon McDuff - http://bugs.eclipse.org/246705
* Simon McDuff - http://bugs.eclipse.org/246622
**************************************************************************/
package org.eclipse.emf.internal.cdo;
import org.eclipse.emf.cdo.CDORevisionPrefetchingPolicy;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.model.CDOFeature;
import org.eclipse.emf.cdo.common.model.CDOType;
import org.eclipse.emf.cdo.common.revision.CDOList;
import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
import org.eclipse.emf.cdo.eresource.CDOResource;
import org.eclipse.emf.cdo.internal.common.model.core.CDOFeatureMapEntryDataTypeImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOAddFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOClearFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOContainerFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOMoveFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDORemoveFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOSetFeatureDeltaImpl;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDOUnsetFeatureDeltaImpl;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.internal.cdo.bundle.OM;
import org.eclipse.emf.internal.cdo.revision.CDOElementProxy;
import org.eclipse.emf.internal.cdo.util.FSMUtil;
import org.eclipse.net4j.util.ImplementationError;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EDataType;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.InternalEObject.EStore;
import org.eclipse.emf.ecore.impl.EStoreEObjectImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import java.text.MessageFormat;
import java.util.Collection;
/**
* CDORevision needs to follow these rules:<br>
* - Keep CDOID only when the object (!isNew && !isTransient) // Only when CDOID will not changed.<br>
* - Keep EObject for external reference, new, transient and that until commit time.<br>
* It is important since these objects could changed and we need to keep a reference to {@link EObject} until the end.
* It is the reason why {@link CDOStore} always call {@link InternalCDOView#convertObjectToID(Object, boolean)} with
* true.
*
* @author Eike Stepper
*/
public final class CDOStore implements EStore
{
private final ContextTracer TRACER = new ContextTracer(OM.DEBUG_STORE, CDOStore.class);
private InternalCDOView view;
// Used for optimization. Multiple call to CDStore will be sent like size and than add.
private EStructuralFeature lastLookupEFeature;
private CDOFeature lastLookupCDOFeature;
private Object lock = new Object();
/**
* @since 2.0
*/
public CDOStore(InternalCDOView view)
{
this.view = view;
}
/**
* @since 2.0
*/
public InternalCDOView getView()
{
return view;
}
/**
* @since 2.0
*/
public void setContainer(InternalEObject eObject, CDOResource newResource, InternalEObject newEContainer,
int newContainerFeatureID)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
if (TRACER.isEnabled())
{
TRACER.format("setContainer({0}, {1}, {2}, {3})", cdoObject, newResource, newEContainer, newContainerFeatureID);
}
Object newContainerID = newEContainer == null ? CDOID.NULL : cdoObject.cdoView().convertObjectToID(newEContainer,
true);
CDOID newResourceID = newResource == null ? CDOID.NULL : newResource.cdoID();
CDOFeatureDelta delta = new CDOContainerFeatureDeltaImpl(newResourceID, newContainerID, newContainerFeatureID);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
revision.setResourceID(newResourceID);
revision.setContainerID(newContainerID);
revision.setContainingFeatureID(newContainerFeatureID);
}
public InternalEObject getContainer(InternalEObject eObject)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
if (TRACER.isEnabled())
{
TRACER.format("getContainer({0})", cdoObject);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return (InternalEObject)cdoObject.cdoView().convertIDToObject(revision.getContainerID());
}
public int getContainingFeatureID(InternalEObject eObject)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
if (TRACER.isEnabled())
{
TRACER.format("getContainingFeatureID({0})", cdoObject);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.getContainingFeatureID();
}
/**
* @since 2.0
*/
public InternalEObject getResource(InternalEObject eObject)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
if (TRACER.isEnabled())
{
TRACER.format("getResource({0})", cdoObject);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return (InternalEObject)cdoObject.cdoView().convertIDToObject(revision.getResourceID());
}
@Deprecated
public EStructuralFeature getContainingFeature(InternalEObject eObject)
{
throw new UnsupportedOperationException("Use getContainingFeatureID() instead");
}
public Object get(InternalEObject eObject, EStructuralFeature eFeature, int index)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("get({0}, {1}, {2})", cdoObject, cdoFeature, index);
}
view.getFeatureAnalyzer().preTraverseFeature(cdoObject, cdoFeature, index);
InternalCDORevision revision = getRevisionForReading(cdoObject);
Object value = revision.basicGet(cdoFeature, index);
value = convertToEMF(view, eObject, revision, eFeature, cdoFeature, index, value);
view.getFeatureAnalyzer().postTraverseFeature(cdoObject, cdoFeature, index, value);
return value;
}
@Deprecated
public boolean isSet(InternalEObject eObject, EStructuralFeature eFeature)
{
// Should not be called
throw new ImplementationError();
}
public int size(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("size({0}, {1})", cdoObject, cdoFeature);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.size(cdoFeature);
}
public boolean isEmpty(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("isEmpty({0}, {1})", cdoObject, cdoFeature);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.isEmpty(cdoFeature);
}
public boolean contains(InternalEObject eObject, EStructuralFeature eFeature, Object value)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("contains({0}, {1}, {2})", cdoObject, cdoFeature, value);
}
if (cdoFeature.isReference())
{
value = cdoObject.cdoView().convertObjectToID(value, true);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.contains(cdoFeature, value);
}
public int indexOf(InternalEObject eObject, EStructuralFeature eFeature, Object value)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("indexOf({0}, {1}, {2})", cdoObject, cdoFeature, value);
}
if (cdoFeature.isReference())
{
value = cdoObject.cdoView().convertObjectToID(value, true);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.indexOf(cdoFeature, value);
}
public int lastIndexOf(InternalEObject eObject, EStructuralFeature eFeature, Object value)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("lastIndexOf({0}, {1}, {2})", cdoObject, cdoFeature, value);
}
if (cdoFeature.isReference())
{
value = cdoObject.cdoView().convertObjectToID(value, true);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.lastIndexOf(cdoFeature, value);
}
public int hashCode(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("hashCode({0}, {1})", cdoObject, cdoFeature);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
return revision.hashCode(cdoFeature);
}
public Object[] toArray(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("toArray({0}, {1})", cdoObject, cdoFeature);
}
InternalCDORevision revision = getRevisionForReading(cdoObject);
Object[] result = revision.toArray(cdoFeature);
if (cdoFeature.isReference())
{
for (int i = 0; i < result.length; i++)
{
result[i] = resolveProxy(revision, cdoFeature, i, result[i]);
result[i] = cdoObject.cdoView().convertIDToObject(result[i]);
}
}
return result;
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(InternalEObject eObject, EStructuralFeature eFeature, T[] a)
{
Object[] array = toArray(eObject, eFeature);
int size = array.length;
if (a.length < size)
{
a = (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
}
System.arraycopy(array, 0, a, 0, size);
if (a.length > size)
{
a[size] = null;
}
return a;
}
public Object set(InternalEObject eObject, EStructuralFeature eFeature, int index, Object value)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("set({0}, {1}, {2}, {3})", cdoObject, cdoFeature, index, value);
}
value = convertToCDO(getView(), eFeature, cdoFeature, value);
CDOFeatureDelta delta = new CDOSetFeatureDeltaImpl(cdoFeature, index, value);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
if (cdoFeature.isReference())
{
Object oldValue = revision.basicGet(cdoFeature, index);
oldValue = resolveProxy(revision, cdoFeature, index, oldValue);
value = cdoObject.cdoView().convertObjectToID(value, true);
}
Object oldValue = revision.basicSet(cdoFeature, index, value);
oldValue = convertToEMF(cdoObject.cdoView(), eObject, revision, eFeature, cdoFeature, index, oldValue);
return oldValue;
}
/**
* @since 2.0
*/
public Object convertToEMF(InternalCDOView view, EObject eObject, InternalCDORevision revision,
EStructuralFeature eFeature, CDOFeature cdoFeature, int index, Object value)
{
if (value != null)
{
if (value == InternalCDORevision.NIL)
{
return EStoreEObjectImpl.NIL;
}
if (cdoFeature.isMany() && EStore.NO_INDEX != index)
{
value = resolveProxy(revision, cdoFeature, index, value);
if (cdoFeature.isMany() && value instanceof CDOID)
{
CDOID id = (CDOID)value;
CDOList list = revision.getList(cdoFeature);
CDORevisionManagerImpl revisionManager = (CDORevisionManagerImpl)view.getSession().getRevisionManager();
CDORevisionPrefetchingPolicy policy = view.options().getRevisionPrefetchingPolicy();
Collection<CDOID> listOfIDs = policy.loadAhead(revisionManager, eObject, eFeature, list, index, id);
if (!listOfIDs.isEmpty())
{
revisionManager.getRevisions(listOfIDs, view.getSession().options().getCollectionLoadingPolicy()
.getInitialChunkSize());
}
}
}
if (cdoFeature.isReference())
{
value = view.convertIDToObject(value);
}
else if (cdoFeature.getType() == CDOType.CUSTOM)
{
value = EcoreUtil.createFromString((EDataType)eFeature.getEType(), (String)value);
}
else if (cdoFeature.getType() == CDOType.FEATURE_MAP_ENTRY)
{
CDOFeatureMapEntryDataTypeImpl entry = (CDOFeatureMapEntryDataTypeImpl)value;
EStructuralFeature feature = (EStructuralFeature)view.getResourceSet().getEObject(
URI.createURI(entry.getURI()), true);
Object object = view.convertIDToObject(entry.getObject());
value = FeatureMapUtil.createEntry(feature, object);
}
}
return value;
}
/**
* @since 2.0
*/
public Object convertToCDO(InternalCDOView view, EStructuralFeature eFeature, CDOFeature cdoFeature, Object value)
{
if (value != null)
{
if (value == EStoreEObjectImpl.NIL)
{
value = InternalCDORevision.NIL;
}
else if (cdoFeature.isReference())
{
value = view.convertObjectToID(value, true);
}
else if (cdoFeature.getType() == CDOType.FEATURE_MAP_ENTRY)
{
FeatureMap.Entry entry = (FeatureMap.Entry)value;
String uri = EcoreUtil.getURI(entry.getEStructuralFeature()).toString();
value = CDORevisionUtil.createFeatureMapEntry(uri, entry.getValue());
}
else if (cdoFeature.getType() == CDOType.CUSTOM)
{
value = EcoreUtil.convertToString((EDataType)eFeature.getEType(), value);
}
}
return value;
}
public void unset(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("unset({0}, {1})", cdoObject, cdoFeature);
}
CDOFeatureDelta delta = new CDOUnsetFeatureDeltaImpl(cdoFeature);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
// TODO Handle containment remove!!!
revision.set(cdoFeature, 0, null);
}
public void add(InternalEObject eObject, EStructuralFeature eFeature, int index, Object value)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("add({0}, {1}, {2}, {3})", cdoObject, cdoFeature, index, value);
}
value = convertToCDO(cdoObject.cdoView(), eFeature, cdoFeature, value);
CDOFeatureDelta delta = new CDOAddFeatureDeltaImpl(cdoFeature, index, value);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
revision.add(cdoFeature, index, value);
}
public Object remove(InternalEObject eObject, EStructuralFeature eFeature, int index)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("remove({0}, {1}, {2})", cdoObject, cdoFeature, index);
}
CDOFeatureDelta delta = new CDORemoveFeatureDeltaImpl(cdoFeature, index);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
Object result = revision.remove(cdoFeature, index);
result = convertToEMF(cdoObject.cdoView(), eObject, revision, eFeature, cdoFeature, index, result);
return result;
}
public void clear(InternalEObject eObject, EStructuralFeature eFeature)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("clear({0}, {1})", cdoObject, cdoFeature);
}
CDOFeatureDelta delta = new CDOClearFeatureDeltaImpl(cdoFeature);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
// TODO Handle containment remove!!!
revision.clear(cdoFeature);
}
public Object move(InternalEObject eObject, EStructuralFeature eFeature, int target, int source)
{
InternalCDOObject cdoObject = getCDOObject(eObject);
CDOFeature cdoFeature = getCDOFeature(cdoObject, eFeature);
if (TRACER.isEnabled())
{
TRACER.format("move({0}, {1}, {2}, {3})", cdoObject, cdoFeature, target, source);
}
CDOFeatureDelta delta = new CDOMoveFeatureDeltaImpl(cdoFeature, target, source);
InternalCDORevision revision = getRevisionForWriting(cdoObject, delta);
Object result = revision.move(cdoFeature, target, source);
result = convertToEMF(cdoObject.cdoView(), eObject, revision, eFeature, cdoFeature, EStore.NO_INDEX, result);
return result;
}
public EObject create(EClass eClass)
{
throw new UnsupportedOperationException("Use the generated factory to create objects");
}
@Override
public String toString()
{
return MessageFormat.format("CDOStore[{0}]", view);
}
private InternalCDOObject getCDOObject(Object object)
{
return FSMUtil.adapt(object, view);
}
/**
* @since 2.0
*/
public Object resolveProxy(InternalCDORevision revision, CDOFeature cdoFeature, int index, Object value)
{
if (value instanceof CDOElementProxy)
{
value = ((CDOElementProxy)value)
.resolve(getView().getSession().getRevisionManager(), revision, cdoFeature, index);
}
return value;
}
private CDOFeature getCDOFeature(InternalCDOObject cdoObject, EStructuralFeature eFeature)
{
synchronized (lock)
{
if (eFeature == lastLookupEFeature)
{
return lastLookupCDOFeature;
}
}
InternalCDOView view = cdoObject.cdoView();
if (view == null)
{
throw new IllegalStateException("view == null");
}
CDOSessionPackageManagerImpl packageManager = (CDOSessionPackageManagerImpl)view.getSession().getPackageManager();
CDOFeature cdoFeature = packageManager.getCDOFeature(eFeature);
synchronized (lock)
{
lastLookupEFeature = eFeature;
lastLookupCDOFeature = cdoFeature;
}
return cdoFeature;
}
private static InternalCDORevision getRevisionForReading(InternalCDOObject cdoObject)
{
CDOStateMachine.INSTANCE.read(cdoObject);
return getRevision(cdoObject);
}
private static InternalCDORevision getRevisionForWriting(InternalCDOObject cdoObject, CDOFeatureDelta delta)
{
CDOStateMachine.INSTANCE.write(cdoObject, delta);
return getRevision(cdoObject);
}
private static InternalCDORevision getRevision(InternalCDOObject cdoObject)
{
InternalCDORevision revision = cdoObject.cdoRevision();
if (revision == null)
{
throw new IllegalStateException("revision == null");
}
return revision;
}
}