blob: fefb2b9548519c996260aa4b68ea7e98a89b1e11 [file] [log] [blame]
/*
* Copyright (c) 2010-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 - bug 201266
* Simon McDuff - bug 212958
* Simon McDuff - bug 213402
*/
package org.eclipse.emf.cdo.spi.common.revision;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.branch.CDOBranchManager;
import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDProvider;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.id.CDOIDUtil;
import org.eclipse.emf.cdo.common.model.CDOClassifierRef;
import org.eclipse.emf.cdo.common.model.CDOModelUtil;
import org.eclipse.emf.cdo.common.model.CDOType;
import org.eclipse.emf.cdo.common.protocol.CDODataInput;
import org.eclipse.emf.cdo.common.protocol.CDODataOutput;
import org.eclipse.emf.cdo.common.revision.CDOElementProxy;
import org.eclipse.emf.cdo.common.revision.CDOList;
import org.eclipse.emf.cdo.common.revision.CDOListFactory;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionData;
import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.security.CDOPermission;
import org.eclipse.emf.cdo.common.security.CDOPermissionProvider;
import org.eclipse.emf.cdo.common.security.NoPermissionException;
import org.eclipse.emf.cdo.common.util.CDOCommonUtil;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.internal.common.messages.Messages;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionDeltaImpl;
import org.eclipse.emf.cdo.spi.common.branch.CDOBranchUtil;
import org.eclipse.emf.cdo.spi.common.branch.InternalCDOBranch;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDOList.ConfigurableEquality;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.net4j.util.om.trace.PerfTracer;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.ecore.util.FeatureMap.Entry;
import org.eclipse.emf.ecore.util.FeatureMapUtil;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.Map;
/**
* If the meaning of this type isn't clear, there really should be more of a description here...
*
* @author Eike Stepper
* @since 3.0
* @noextend This class is not intended to be subclassed by clients.
*/
public abstract class BaseCDORevision extends AbstractCDORevision
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, BaseCDORevision.class);
private static final PerfTracer READING = new PerfTracer(OM.PERF_REVISION_READING, BaseCDORevision.class);
private static final PerfTracer WRITING = new PerfTracer(OM.PERF_REVISION_WRITING, BaseCDORevision.class);
private static final int RESOURCE_NODE_NAME_INDEX = 1;
private static final int RESOURCE_FOLDER_NODES_INDEX = 2;
private static final byte UNSET_OPCODE = 0;
private static final byte SET_NULL_OPCODE = 1;
private static final byte SET_NOT_NULL_OPCODE = 2;
private static final byte READ_PERMISSION_FLAG = 1 << 0; // 1
private static final byte WRITE_PERMISSION_FLAG = 1 << 1; // 2
private static final byte FROZEN_FLAG = 1 << 2; // 4
private static final byte UNCHUNKED_FLAG = 1 << 3; // 8
private static final byte BYPASS_PERMISSION_CHECKS_FLAG = 1 << 4; // 16
private static final byte LIST_PRESERVING_FLAG = 1 << 5; // 32;
private static final byte PERMISSION_MASK = READ_PERMISSION_FLAG | WRITE_PERMISSION_FLAG; // 3
private CDOID id;
private CDOBranchPoint branchPoint;
private int version;
private long revised;
private CDOID resourceID;
/**
* On a client, between a local modification and the commit the value of this <i>ID</i> can be an EObject.
*/
private Object containerID;
private int containingFeatureID;
private transient byte flags;
/**
* @since 3.0
*/
public BaseCDORevision(EClass eClass)
{
super(eClass);
if (eClass != null)
{
version = UNSPECIFIED_VERSION;
revised = UNSPECIFIED_DATE;
resourceID = CDOID.NULL;
containerID = CDOID.NULL;
containingFeatureID = 0;
initValues(getAllPersistentFeatures());
}
flags = CDOPermission.WRITE.getBits();
}
protected BaseCDORevision(BaseCDORevision source)
{
super(source.getEClass());
id = source.id;
branchPoint = source.branchPoint;
version = source.version;
revised = source.revised;
resourceID = source.resourceID;
containerID = source.containerID;
containingFeatureID = source.containingFeatureID;
flags = (byte)(source.flags & ~FROZEN_FLAG);
}
/**
* @since 3.0
*/
public void read(CDODataInput in) throws IOException
{
if (READING.isEnabled())
{
READING.start(this);
}
readSystemValues(in);
flags = in.readByte(); // Don't set permissions into this.falgs before readValues()
flags |= UNCHUNKED_FLAG; // First assume all lists are unchunked; may be revised below
flags |= BYPASS_PERMISSION_CHECKS_FLAG; // Temporarily disable permission checking to be able to set the read values
if ((flags & PERMISSION_MASK) == CDOPermission.NONE.ordinal())
{
if (getClassInfo().isResourceNode())
{
clearValues();
EClass eClass = getEClass();
EStructuralFeature[] features = getAllPersistentFeatures();
readValue(in, eClass, features, RESOURCE_NODE_NAME_INDEX, true);
if (getClassInfo().isResourceFolder())
{
if (!readValue(in, eClass, features, RESOURCE_FOLDER_NODES_INDEX, true))
{
flags &= ~UNCHUNKED_FLAG;
}
}
}
}
else
{
if (!readValues(in))
{
flags &= ~UNCHUNKED_FLAG;
}
}
// Enable permission checking
flags &= ~BYPASS_PERMISSION_CHECKS_FLAG;
if (READING.isEnabled())
{
READING.stop(this);
}
}
/**
* @since 4.0
*/
protected void readSystemValues(CDODataInput in) throws IOException
{
EClassifier classifier = in.readCDOClassifierRefAndResolve();
initClassInfo((EClass)classifier);
id = in.readCDOID();
branchPoint = in.readCDOBranchPoint();
version = in.readInt();
if (!id.isTemporary())
{
revised = in.readLong();
}
resourceID = in.readCDOID();
containerID = in.readCDOID();
containingFeatureID = in.readInt();
if (TRACER.isEnabled())
{
TRACER.format(
"Reading revision: ID={0}, className={1}, version={2}, branchPoint={3}, revised={4}, resource={5}, container={6}, featureID={7}", //$NON-NLS-1$
id, getEClass().getName(), version, branchPoint, revised, resourceID, containerID, containingFeatureID);
}
}
/**
* @since 4.3
*/
public boolean readValues(CDODataInput in) throws IOException
{
EClass owner = getEClass();
EStructuralFeature[] features = getAllPersistentFeatures();
initValues(features);
boolean unchunked = true;
for (int i = 0; i < features.length; i++)
{
unchunked = readValue(in, owner, features, i, unchunked);
}
return unchunked;
}
private boolean readValue(CDODataInput in, EClass owner, EStructuralFeature[] features, int i, boolean unchunked)
throws IOException
{
Object value;
byte unsetState = in.readByte();
switch (unsetState)
{
case UNSET_OPCODE:
return unchunked;
case SET_NULL_OPCODE:
setValue(i, CDORevisionData.NIL);
return unchunked;
}
EStructuralFeature feature = features[i];
if (feature.isMany())
{
CDOList list = in.readCDOList(owner, feature);
if (unchunked)
{
int size = list.size();
if (size != 0)
{
Object lastElement = list.get(size - 1);
if (lastElement == InternalCDOList.UNINITIALIZED || lastElement instanceof CDOElementProxy)
{
unchunked = false;
}
}
}
value = list;
}
else
{
value = in.readCDOFeatureValue(feature);
if (TRACER.isEnabled())
{
TRACER.format("Read feature {0}: {1}", feature.getName(), value);
}
}
setValue(i, value);
return unchunked;
}
/**
* @since 4.0
*/
public void write(CDODataOutput out, int referenceChunk) throws IOException
{
write(out, referenceChunk, null);
}
/**
* @since 4.1
*/
public void write(CDODataOutput out, int referenceChunk, CDOBranchPoint securityContext) throws IOException
{
if (WRITING.isEnabled())
{
WRITING.start(this);
}
writeSystemValues(out);
CDOPermissionProvider permissionProvider = out.getPermissionProvider();
CDOPermission permission = permissionProvider.getPermission(this, securityContext);
out.writeByte(permission.getBits());
if (permission == CDOPermission.NONE)
{
if (getClassInfo().isResourceNode())
{
EClass eClass = getEClass();
EStructuralFeature[] features = getAllPersistentFeatures();
writeValue(out, eClass, features, RESOURCE_NODE_NAME_INDEX, referenceChunk);
if (getClassInfo().isResourceFolder())
{
writeValue(out, eClass, features, RESOURCE_FOLDER_NODES_INDEX, referenceChunk);
}
}
}
else
{
if (!isUnchunked() && referenceChunk != 0)
{
CDORevisionUnchunker unchunker = out.getRevisionUnchunker();
if (unchunker != null)
{
unchunker.ensureChunks(this, referenceChunk);
}
}
writeValues(out, referenceChunk);
}
if (WRITING.isEnabled())
{
WRITING.stop(this);
}
}
/**
* @since 4.0
*/
protected void writeSystemValues(CDODataOutput out) throws IOException
{
EClass eClass = getEClass();
CDOClassifierRef classRef = new CDOClassifierRef(eClass);
if (TRACER.isEnabled())
{
TRACER.format(
"Writing revision: ID={0}, className={1}, version={2}, branchPoint={3}, revised={4}, resource={5}, container={6}, featureID={7}", //$NON-NLS-1$
id, eClass.getName(), getVersion(), branchPoint, revised, resourceID, containerID, containingFeatureID);
}
out.writeCDOClassifierRef(classRef);
out.writeCDOID(id);
out.writeCDOBranchPoint(branchPoint);
out.writeInt(getVersion());
if (!id.isTemporary())
{
out.writeLong(revised);
}
out.writeCDOID(resourceID);
out.writeCDOID(out.getIDProvider().provideCDOID(containerID));
out.writeInt(containingFeatureID);
}
/**
* @since 4.3
*/
public void writeValues(CDODataOutput out, int referenceChunk) throws IOException
{
EClass owner = getEClass();
EStructuralFeature[] features = getAllPersistentFeatures();
for (int i = 0; i < features.length; i++)
{
writeValue(out, owner, features, i, referenceChunk);
}
}
private void writeValue(CDODataOutput out, EClass owner, EStructuralFeature[] features, int i, int referenceChunk)
throws IOException
{
EStructuralFeature feature = features[i];
Object value = getValue(i);
if (value == null)
{
// Feature is NOT set
out.writeByte(UNSET_OPCODE);
return;
}
// Feature IS set
if (value == CDORevisionData.NIL)
{
// Feature IS null
out.writeByte(SET_NULL_OPCODE);
return;
}
// Feature is NOT null
out.writeByte(SET_NOT_NULL_OPCODE);
if (feature.isMany())
{
CDOList list = (CDOList)value;
out.writeCDOList(owner, feature, list, referenceChunk);
}
else
{
checkNoFeatureMap(feature);
if (feature instanceof EReference)
{
value = out.getIDProvider().provideCDOID(value);
}
if (TRACER.isEnabled())
{
TRACER.format("Writing feature {0}: {1}", feature.getName(), value);
}
out.writeCDOFeatureValue(feature, value);
}
}
/**
* @see #write(CDODataOutput, int)
* @since 3.0
*/
public void convertEObjects(CDOIDProvider idProvider)
{
if (!(containerID instanceof CDOID))
{
containerID = idProvider.provideCDOID(containerID);
}
EStructuralFeature[] features = getAllPersistentFeatures();
for (int i = 0; i < features.length; i++)
{
EStructuralFeature feature = features[i];
if (feature.isMany())
{
CDOList list = getValueAsList(i);
if (list != null)
{
boolean isFeatureMap = FeatureMapUtil.isFeatureMap(feature);
for (int j = 0; j < list.size(); j++)
{
Object value = list.get(j, false);
EStructuralFeature innerFeature = feature; // Prepare for possible feature map
if (isFeatureMap)
{
Entry entry = (FeatureMap.Entry)value;
innerFeature = entry.getEStructuralFeature();
value = entry.getValue();
}
if (value != null && innerFeature instanceof EReference)
{
CDOID newValue = idProvider.provideCDOID(value);
if (newValue != value)
{
list.set(j, newValue);
}
}
}
}
}
else
{
checkNoFeatureMap(feature);
Object value = getValue(i);
if (value != null && feature instanceof EReference)
{
CDOID newValue = idProvider.provideCDOID(value);
if (newValue != value)
{
setValue(i, newValue);
}
}
}
}
}
public CDOID getID()
{
return id;
}
public void setID(CDOID id)
{
if (CDOIDUtil.isNull(id))
{
throw new IllegalArgumentException(Messages.getString("AbstractCDORevision.1")); //$NON-NLS-1$
}
if (TRACER.isEnabled())
{
TRACER.format("Setting ID: {0}", id);
}
this.id = id;
}
/**
* @since 4.2
*/
public InternalCDOBranch getBranch()
{
if (branchPoint == null)
{
return null;
}
return (InternalCDOBranch)branchPoint.getBranch();
}
/**
* @since 3.0
*/
public long getTimeStamp()
{
if (branchPoint == null)
{
return UNSPECIFIED_DATE;
}
return branchPoint.getTimeStamp();
}
/**
* @since 3.0
*/
public void setBranchPoint(CDOBranchPoint branchPoint)
{
branchPoint = CDOBranchUtil.copyBranchPoint(branchPoint);
if (TRACER.isEnabled())
{
TRACER.format("Setting branchPoint {0}: {1}", this, branchPoint);
}
this.branchPoint = branchPoint;
}
public int getVersion()
{
return version;
}
public void setVersion(int version)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting version for {0}: v{1}", this, version);
}
this.version = version;
}
public long getRevised()
{
return revised;
}
public void setRevised(long revised)
{
long created = branchPoint.getTimeStamp();
if (revised != UNSPECIFIED_DATE && revised < Math.max(0, created))
{
throw new IllegalArgumentException("revision=" + this + ", created=" + CDOCommonUtil.formatTimeStamp(created)
+ ", revised=" + CDOCommonUtil.formatTimeStamp(revised));
}
if (TRACER.isEnabled())
{
TRACER.format("Setting revised {0}: {1}", this, CDOCommonUtil.formatTimeStamp(revised));
}
this.revised = revised;
}
public InternalCDORevisionDelta compare(CDORevision origin)
{
return new CDORevisionDeltaImpl(origin, this);
}
public void merge(CDORevisionDelta delta)
{
CDORevisionMerger applier = new CDORevisionMerger();
applier.merge(this, delta);
}
public CDOID getResourceID()
{
return resourceID;
}
public void setResourceID(CDOID resourceID)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting resourceID {0}: {1}", this, resourceID);
}
this.resourceID = resourceID;
}
public Object getContainerID()
{
return containerID;
}
public void setContainerID(Object containerID)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting containerID {0}: {1}", this, containerID);
}
this.containerID = containerID;
}
public int getContainingFeatureID()
{
return containingFeatureID;
}
public void setContainingFeatureID(int containingFeatureID)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting containingFeatureID {0}: {1}", this, containingFeatureID);
}
this.containingFeatureID = containingFeatureID;
}
public int hashCode(EStructuralFeature feature)
{
Object value = getValue(feature);
if (value == null)
{
// See how AbstractEList.hashCode() returns 1 for an empty list.
return 1;
}
return value.hashCode();
}
public Object get(EStructuralFeature feature, int index)
{
if (feature.isMany())
{
CDOList list = getList(feature);
return list.get(index);
}
return getValue(feature);
}
public boolean contains(EStructuralFeature feature, Object value)
{
CDOList list = getList(feature);
return list.contains(value);
}
public int indexOf(EStructuralFeature feature, Object value)
{
CDOList list = getList(feature);
return list.indexOf(value);
}
public int lastIndexOf(EStructuralFeature feature, Object value)
{
CDOList list = getList(feature);
return list.lastIndexOf(value);
}
public boolean isEmpty(EStructuralFeature feature)
{
CDOList list = getList(feature);
return list.isEmpty();
}
public int size(EStructuralFeature feature)
{
CDOList list = getList(feature);
return list.size();
}
public Object[] toArray(EStructuralFeature feature)
{
if (!feature.isMany())
{
throw new IllegalStateException("!feature.isMany()");
}
CDOList list = getList(feature);
return list.toArray();
}
public <T> T[] toArray(EStructuralFeature feature, T[] array)
{
if (!feature.isMany())
{
throw new IllegalStateException("!feature.isMany()");
}
CDOList list = getList(feature);
return list.toArray(array);
}
public void add(EStructuralFeature feature, int index, Object value)
{
CDOList list = getList(feature);
list.add(index, value);
}
public void clear(EStructuralFeature feature)
{
if (feature.isMany() && isListPreserving())
{
getList(feature).clear();
}
else
{
setValue(feature, null);
}
}
public Object move(EStructuralFeature feature, int targetIndex, int sourceIndex)
{
CDOList list = getList(feature);
return list.move(targetIndex, sourceIndex);
}
public Object remove(EStructuralFeature feature, int index)
{
CDOList list = getList(feature);
return list.remove(index);
}
public Object set(EStructuralFeature feature, int index, Object value)
{
if (feature.isMany())
{
CDOList list = getList(feature);
return list.set(index, value);
}
return setValue(feature, value);
}
public void unset(EStructuralFeature feature)
{
if (feature.isMany() && isListPreserving())
{
getList(feature).clear();
}
else
{
setValue(feature, null);
}
}
/**
* @since 4.0
*/
public boolean adjustReferences(CDOReferenceAdjuster referenceAdjuster)
{
if (TRACER.isEnabled())
{
TRACER.format("Adjusting references for revision {0}", this);
}
boolean changed = false;
CDOID id1 = (CDOID)referenceAdjuster.adjustReference(resourceID, CDOContainerFeatureDelta.CONTAINER_FEATURE,
CDOFeatureDelta.NO_INDEX);
if (id1 != resourceID)
{
resourceID = id1;
changed = true;
}
Object id2 = referenceAdjuster.adjustReference(containerID, CDOContainerFeatureDelta.CONTAINER_FEATURE,
CDOFeatureDelta.NO_INDEX);
if (id2 != containerID)
{
containerID = id2;
changed = true;
}
EStructuralFeature[] features = getAllPersistentFeatures();
for (int i = 0; i < features.length; i++)
{
EStructuralFeature feature = features[i];
if (feature instanceof EReference || FeatureMapUtil.isFeatureMap(feature))
{
if (feature.isMany())
{
InternalCDOList list = (InternalCDOList)getValueAsList(i);
if (list != null)
{
changed |= list.adjustReferences(referenceAdjuster, feature);
}
}
else
{
CDOType type = CDOModelUtil.getType(feature);
Object oldValue = getValue(i);
Object newValue = type.adjustReferences(referenceAdjuster, oldValue, feature, CDOFeatureDelta.NO_INDEX);
if (oldValue != newValue) // Just an optimization for NOOP adjusters
{
setValue(i, newValue);
changed = true;
}
}
}
}
return changed;
}
/**
* @since 4.3
*/
public void adjustBranches(CDOBranchManager newBranchManager)
{
if (branchPoint != null)
{
CDOBranch branch = branchPoint.getBranch();
if (branch != null)
{
branch = newBranchManager.getBranch(branch.getID());
branchPoint = branch.getPoint(branchPoint.getTimeStamp());
}
}
}
public Object getValue(EStructuralFeature feature)
{
checkReadable(feature);
int featureIndex = getFeatureIndex(feature);
return getValue(featureIndex);
}
public Object setValue(EStructuralFeature feature, Object value)
{
int featureIndex = getFeatureIndex(feature);
try
{
Object old = getValue(featureIndex);
setValue(featureIndex, value);
return old;
}
catch (ArrayIndexOutOfBoundsException ex)
{
throw new IllegalArgumentException(
MessageFormat.format(Messages.getString("AbstractCDORevision.20"), feature, getClassInfo()), ex);
}
}
public CDOList getList(EStructuralFeature feature)
{
return getList(feature, 0);
}
public CDOList getList(EStructuralFeature feature, int size)
{
checkReadable(feature);
int featureIndex = getFeatureIndex(feature);
InternalCDOList list = (InternalCDOList)getValue(featureIndex);
if (list == null && size != -1)
{
list = (InternalCDOList)CDOListFactory.DEFAULT.createList(size, 0, 0);
if (feature instanceof EReference && list instanceof ConfigurableEquality)
{
((ConfigurableEquality)list).setUseEquals(false);
}
synchronized (this)
{
boolean bypassPermissionChecks = bypassPermissionChecks(true);
try
{
setValue(featureIndex, list);
}
finally
{
bypassPermissionChecks(bypassPermissionChecks);
}
}
}
return list;
}
public void setList(EStructuralFeature feature, InternalCDOList list)
{
int featureIndex = getFeatureIndex(feature);
setValue(featureIndex, list);
}
/**
* @since 4.2
*/
public EStructuralFeature[] clearValues()
{
EStructuralFeature[] features = getClassInfo().getAllPersistentFeatures();
initValues(features);
return features;
}
/**
* @since 4.3
*/
public String getResourceNodeName()
{
return (String)doGetValue(RESOURCE_NODE_NAME_INDEX);
}
/**
* @since 4.1
*/
public CDOPermission getPermission()
{
return CDOPermission.get(flags & PERMISSION_MASK);
}
/**
* @since 4.1
*/
public void setPermission(CDOPermission permission)
{
flags = (byte)(flags & ~PERMISSION_MASK | permission.getBits() & PERMISSION_MASK);
}
/**
* @since 4.3
*/
public boolean bypassPermissionChecks(boolean on)
{
boolean old = (flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0;
if (on)
{
flags |= BYPASS_PERMISSION_CHECKS_FLAG;
}
else
{
flags &= ~BYPASS_PERMISSION_CHECKS_FLAG;
}
return old;
}
/**
* @since 4.3
*/
public boolean isListPreserving()
{
return (flags & LIST_PRESERVING_FLAG) != 0;
}
/**
* @since 4.3
*/
public void setListPreserving()
{
flags |= LIST_PRESERVING_FLAG;
}
/**
* @since 4.1
*/
public void freeze()
{
flags |= FROZEN_FLAG;
if (isReadable())
{
EStructuralFeature[] features = getAllPersistentFeatures();
for (int i = 0; i < features.length; i++)
{
EStructuralFeature feature = features[i];
if (feature.isMany())
{
InternalCDOList list = (InternalCDOList)doGetValue(i);
if (list != null)
{
list.freeze();
}
}
}
}
}
/**
* @since 4.2
*/
public boolean isFrozen()
{
return (flags & FROZEN_FLAG) != 0;
}
/**
* @since 4.1
*/
public boolean isUnchunked()
{
return (flags & UNCHUNKED_FLAG) != 0;
}
/**
* @since 4.1
*/
public void setUnchunked()
{
flags |= UNCHUNKED_FLAG;
}
protected Object getValue(int featureIndex)
{
return doGetValue(featureIndex);
}
protected void setValue(int featureIndex, Object value)
{
checkUnfrozen(featureIndex, value);
checkWritable();
doSetValue(featureIndex, value);
}
protected abstract void initValues(EStructuralFeature[] allPersistentFeatures);
/**
* @since 4.1
*/
protected abstract Object doGetValue(int featureIndex);
/**
* @since 4.1
*/
protected abstract void doSetValue(int featureIndex, Object value);
private CDOList getValueAsList(int i)
{
return (CDOList)getValue(i);
}
private void checkUnfrozen(int featureIndex, Object value)
{
if ((flags & FROZEN_FLAG) != 0)
{
// Exception 1: LoadPermissionsRequest needs to "reload" revision values in case the original permission was NONE.
// In this case BYPASS_PERMISSION_CHECKS_FLAG is set.
if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0)
{
return;
}
Object oldValue = getValue(featureIndex);
// Exception 2: Setting an empty list as the value for an isMany feature, is allowed if the old value is null.
// This is a case of lazy initialization.
boolean newIsEmptyList = value instanceof EList<?> && ((EList<?>)value).size() == 0;
if (newIsEmptyList && oldValue == null)
{
return;
}
// Exception 3a: Replacing a temp ID with a regular ID is allowed (happens during postCommit of new objects)
// Exception 3b: Replacing a temp ID with another temp ID is also allowed (happens when changes are imported in a
// PushTx).
if (oldValue instanceof CDOIDTemp && value instanceof CDOID)
{
return;
}
throw new IllegalStateException("Cannot modify a frozen revision");
}
}
private void checkReadable(EStructuralFeature feature)
{
if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0)
{
return;
}
if (CDOModelUtil.isResourcePathFeature(feature))
{
return;
}
if ((flags & READ_PERMISSION_FLAG) == 0)
{
throw new NoPermissionException(this);
}
}
private void checkWritable()
{
if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0)
{
return;
}
if ((flags & WRITE_PERMISSION_FLAG) == 0)
{
throw new NoPermissionException(this);
}
}
public static void checkNoFeatureMap(EStructuralFeature feature)
{
if (FeatureMapUtil.isFeatureMap(feature))
{
throw new UnsupportedOperationException("Single-valued feature maps not yet handled");
}
}
public static Object remapID(Object value, Map<CDOID, CDOID> idMappings, boolean allowUnmappedTempIDs)
{
if (value instanceof CDOID)
{
CDOID oldID = (CDOID)value;
if (!oldID.isNull())
{
CDOID newID = idMappings.get(oldID);
if (newID != null)
{
if (TRACER.isEnabled())
{
TRACER.format("Adjusting ID: {0} --> {1}", oldID, newID);
}
return newID;
}
if (oldID instanceof CDOIDTemp)
{
throw new IllegalStateException(MessageFormat.format(Messages.getString("AbstractCDORevision.2"), oldID));
}
}
}
return value;
}
/**
* @since 4.3
*/
public static String formatFlags(BaseCDORevision revision)
{
int flags = revision.flags;
StringBuilder builder = new StringBuilder();
if ((flags & UNCHUNKED_FLAG) != 0)
{
builder.append("UNCHUNKED");
}
if ((flags & FROZEN_FLAG) != 0)
{
if (builder.length() != 0)
{
builder.append("|");
}
builder.append("FROZEN");
}
if ((flags & READ_PERMISSION_FLAG) != 0)
{
if (builder.length() != 0)
{
builder.append("|");
}
builder.append("READ");
}
if ((flags & WRITE_PERMISSION_FLAG) != 0)
{
if (builder.length() != 0)
{
builder.append("|");
}
builder.append("WRITE");
}
if ((flags & BYPASS_PERMISSION_CHECKS_FLAG) != 0)
{
if (builder.length() != 0)
{
builder.append("|");
}
builder.append("BYPASS_PERMISSION_CHECKS");
}
return builder.toString();
}
}