blob: 397e0559d175a9e151c0e0b39116338e8a1bbe56 [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
* Simon McDuff - http://bugs.eclipse.org/212958
* Simon McDuff - http://bugs.eclipse.org/213402
**************************************************************************/
package org.eclipse.emf.cdo.internal.common.revision;
import org.eclipse.emf.cdo.common.CDODataInput;
import org.eclipse.emf.cdo.common.CDODataOutput;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOIDTemp;
import org.eclipse.emf.cdo.common.model.CDOClass;
import org.eclipse.emf.cdo.common.model.CDOClassRef;
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.CDOListFactory;
import org.eclipse.emf.cdo.common.revision.CDOReferenceAdjuster;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionData;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDeltaUtil;
import org.eclipse.emf.cdo.internal.common.bundle.OM;
import org.eclipse.emf.cdo.internal.common.revision.delta.CDORevisionMerger;
import org.eclipse.emf.cdo.spi.common.InternalCDOList;
import org.eclipse.emf.cdo.spi.common.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.InternalCDORevisionDelta;
import org.eclipse.net4j.util.ImplementationError;
import org.eclipse.net4j.util.collection.MoveableList;
import org.eclipse.net4j.util.om.trace.ContextTracer;
import org.eclipse.net4j.util.om.trace.PerfTracer;
import java.io.IOException;
import java.util.Map;
/**
* @author Eike Stepper
*/
public class CDORevisionImpl implements InternalCDORevision
{
private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_REVISION, CDORevisionImpl.class);
private static final PerfTracer READING = new PerfTracer(OM.PERF_REVISION_READING, CDORevisionImpl.class);
private static final PerfTracer WRITING = new PerfTracer(OM.PERF_REVISION_WRITING, CDORevisionImpl.class);
private CDOClass cdoClass;
private CDOID id;
private int version;
private long created;
private long revised;
private CDOID resourceID;
private Object containerID;
private int containingFeatureID;
private Object[] values;
public CDORevisionImpl(CDOClass cdoClass, CDOID id)
{
if (cdoClass.isAbstract())
{
throw new IllegalArgumentException("CDOClass is abstract: " + cdoClass);
}
if (id == null || id.isNull())
{
throw new IllegalArgumentException("CDIID is null");
}
this.cdoClass = cdoClass;
this.id = id;
version = 0;
created = UNSPECIFIED_DATE;
revised = UNSPECIFIED_DATE;
resourceID = CDOID.NULL;
containerID = CDOID.NULL;
containingFeatureID = 0;
values = new Object[cdoClass.getAllFeatures().length];
}
public CDORevisionImpl(CDORevisionImpl source)
{
cdoClass = source.cdoClass;
id = source.id;
version = source.version;
created = source.created;
revised = source.revised; // == UNSPECIFIED
resourceID = source.resourceID;
containerID = source.containerID;
containingFeatureID = source.containingFeatureID;
copyValues(source.values);
}
public CDORevisionImpl(CDODataInput in) throws IOException
{
READING.start(this);
cdoClass = in.readCDOClassRefAndResolve();
id = in.readCDOID();
version = in.readInt();
if (!id.isTemporary())
{
created = in.readLong();
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}, created={3}, revised={4}, resource={5}, container={6}, feature={7}",
id, cdoClass.getName(), version, created, revised, resourceID, containerID, containingFeatureID);
}
readValues(in);
READING.stop(this);
}
public void write(CDODataOutput out, int referenceChunk) throws IOException
{
CDOClassRef classRef = cdoClass.createClassRef();
if (TRACER.isEnabled())
{
TRACER
.format(
"Writing revision: ID={0}, classRef={1}, className={2}, version={3}, created={4}, revised={5}, resource={6}, container={7}, feature={8}",
id, classRef, cdoClass.getName(), getVersion(), created, revised, resourceID, containerID,
containingFeatureID);
}
WRITING.start(this);
out.writeCDOClassRef(classRef);
out.writeCDOID(id);
out.writeInt(getVersion());
if (!id.isTemporary())
{
out.writeLong(created);
out.writeLong(revised);
}
out.writeCDOID(resourceID);
Object newContainerID = out.getIDProvider().provideCDOID(containerID);
out.writeCDOID((CDOID)newContainerID);
out.writeInt(containingFeatureID);
writeValues(out, referenceChunk);
WRITING.stop(this);
}
public CDOClass getCDOClass()
{
return cdoClass;
}
public CDOID getID()
{
return id;
}
public void setID(CDOID id)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting ID: {0}", id);
}
this.id = id;
}
public int getVersion()
{
return version < 0 ? -version : version;
}
public void setVersion(int version)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting version for {0}: v{1}", this, version);
}
this.version = version;
}
public boolean isTransactional()
{
return version < 0;
}
public int setTransactional()
{
if (TRACER.isEnabled())
{
TRACER.format("Setting transactional {0}: v{1}", this, -(version + 1));
}
version = -(version + 1);
return version;
}
public void setUntransactional()
{
if (TRACER.isEnabled())
{
TRACER.format("Setting untransactional {0}: v{1}", this, Math.abs(version));
}
version = Math.abs(version);
}
public long getCreated()
{
return created;
}
public void setCreated(long created)
{
if (TRACER.isEnabled())
{
TRACER.format("Setting created {0}: {1,date} {1,time}", this, created);
}
this.created = created;
}
public long getRevised()
{
return revised;
}
public void setRevised(long revised)
{
if (revised != UNSPECIFIED_DATE && revised < created)
{
throw new IllegalArgumentException("created=" + created + ", revised=" + revised);
}
if (TRACER.isEnabled())
{
TRACER.format("Setting revised {0}: {1,date} {1,time}", this, revised);
}
this.revised = revised;
}
public boolean isCurrent()
{
return revised == UNSPECIFIED_DATE;
}
public boolean isValid(long timeStamp)
{
return (revised == UNSPECIFIED_DATE || revised >= timeStamp) && timeStamp >= created;
}
public boolean isResourceNode()
{
return cdoClass.isResourceNode();
}
public boolean isResourceFolder()
{
return cdoClass.isResourceFolder();
}
public boolean isResource()
{
return cdoClass.isResource();
}
public CDORevisionData getData()
{
return this;
}
public CDORevision getRevision()
{
return this;
}
public InternalCDORevisionDelta compare(CDORevision origin)
{
return (InternalCDORevisionDelta)CDORevisionDeltaUtil.create(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(CDOFeature feature)
{
return getValue(feature).hashCode();
}
public Object get(CDOFeature feature, int index)
{
if (feature.isMany())
{
return getList(feature).get(index);
}
return getValue(feature);
}
public boolean contains(CDOFeature feature, Object value)
{
return getList(feature).contains(value);
}
public int indexOf(CDOFeature feature, Object value)
{
return getList(feature).indexOf(value);
}
public boolean isEmpty(CDOFeature feature)
{
return getList(feature).isEmpty();
}
public boolean isSet(CDOFeature feature)
{
return getValue(feature) != null;
}
public int lastIndexOf(CDOFeature feature, Object value)
{
return getList(feature).lastIndexOf(value);
}
public int size(CDOFeature feature)
{
return getList(feature).size();
}
public Object[] toArray(CDOFeature feature)
{
if (!feature.isMany())
{
throw new IllegalStateException("!feature.isMany()");
}
return getList(feature).toArray();
}
public <T> T[] toArray(CDOFeature feature, T[] array)
{
if (!feature.isMany())
{
throw new IllegalStateException("!feature.isMany()");
}
return getList(feature).toArray(array);
}
public void add(CDOFeature feature, int index, Object value)
{
getList(feature).add(index, value);
}
public void clear(CDOFeature feature)
{
setValue(feature, null);
}
public Object move(CDOFeature feature, int targetIndex, int sourceIndex)
{
return getList(feature).move(targetIndex, sourceIndex);
}
public Object remove(CDOFeature feature, int index)
{
return getList(feature).remove(index);
}
public Object set(CDOFeature feature, int index, Object value)
{
if (feature.isMany())
{
return getList(feature).set(index, value);
}
return setValue(feature, value);
}
public void unset(CDOFeature feature)
{
setValue(feature, null);
}
public void adjustReferences(CDOReferenceAdjuster revisionAdjuster)
{
if (TRACER.isEnabled())
{
TRACER.format("Adjusting references for revision {0}", this);
}
resourceID = (CDOID)revisionAdjuster.adjustReference(resourceID);
containerID = revisionAdjuster.adjustReference(containerID);
CDOFeature[] features = cdoClass.getAllFeatures();
for (int i = 0; i < features.length; i++)
{
CDOFeature feature = features[i];
if (feature.isReference())
{
if (feature.isMany())
{
InternalCDOList list = (InternalCDOList)getValueAsList(i);
if (list != null)
{
list.adjustReferences(revisionAdjuster);
}
}
else
{
values[i] = revisionAdjuster.adjustReference(values[i]);
}
}
}
}
private CDOList getValueAsList(int i)
{
return (CDOList)values[i];
}
@Override
public String toString()
{
return cdoClass.getName() + "@" + id + "v" + version;
}
public Object getValue(CDOFeature feature)
{
int i = cdoClass.getFeatureID(feature);
return values[i];
}
public Object setValue(CDOFeature feature, Object value)
{
int i = cdoClass.getFeatureID(feature);
try
{
Object old = values[i];
values[i] = value;
return old;
}
catch (ArrayIndexOutOfBoundsException ex)
{
throw new IllegalArgumentException("Could not find feature " + feature + " in class " + cdoClass, ex);
}
}
public CDOList getList(CDOFeature feature)
{
return getList(feature, 0);
}
public CDOList getList(CDOFeature feature, int size)
{
int i = cdoClass.getFeatureID(feature);
CDOList list = (CDOList)values[i];
if (list == null && size != -1)
{
list = CDOListFactory.DEFAULT.createList(size, 0, 0);
values[i] = list;
}
return list;
}
public void setList(CDOFeature feature, InternalCDOList list)
{
int i = cdoClass.getFeatureID(feature);
values[i] = list;
}
public void setListSize(CDOFeature feature, int size)
{
MoveableList<Object> list = getList(feature, size);
for (int j = list.size(); j < size; j++)
{
list.add(InternalCDORevision.UNINITIALIZED);
}
}
private void copyValues(Object[] sourceValues)
{
CDOFeature[] features = cdoClass.getAllFeatures();
values = new Object[features.length];
for (int i = 0; i < features.length; i++)
{
CDOFeature feature = features[i];
CDOType type = feature.getType();
if (feature.isMany())
{
InternalCDOList sourceList = (InternalCDOList)sourceValues[i];
if (sourceList != null)
{
values[i] = sourceList.clone(type);
}
}
else
{
values[i] = type.copyValue(sourceValues[i]);
}
}
}
private void readValues(CDODataInput in) throws IOException
{
CDOFeature[] features = cdoClass.getAllFeatures();
values = new Object[features.length];
for (int i = 0; i < features.length; i++)
{
CDOFeature feature = features[i];
CDOType type = feature.getType();
if (feature.isMany())
{
values[i] = in.readCDOList(this, feature);
}
else
{
values[i] = type.readValue(in);
if (TRACER.isEnabled())
{
TRACER.format("Read feature {0}: {1}", feature, values[i]);
}
}
}
}
private void writeValues(CDODataOutput out, int referenceChunk) throws IOException
{
CDOFeature[] features = cdoClass.getAllFeatures();
for (int i = 0; i < features.length; i++)
{
CDOFeature feature = features[i];
if (feature.isMany())
{
out.writeCDOList(getValueAsList(i), feature, referenceChunk);
}
else
{
Object value = values[i];
if (value != null && feature.isReference())
{
value = out.getIDProvider().provideCDOID(value);
}
if (TRACER.isEnabled())
{
TRACER.format("Writing feature {0}: {1}", feature, value);
}
feature.getType().writeValue(out, value);
}
}
}
public static Object remapID(Object value, Map<CDOIDTemp, CDOID> idMappings)
{
if (value instanceof CDOIDTemp)
{
CDOIDTemp oldID = (CDOIDTemp)value;
if (!oldID.isNull())
{
CDOID newID = idMappings.get(oldID);
if (newID == null)
{
throw new ImplementationError("Missing ID mapping for " + oldID);
}
if (TRACER.isEnabled())
{
TRACER.format("Adjusting ID: {0} --> {1}", oldID, newID);
}
return newID;
}
}
return value;
}
}