blob: a5265ea4b0b0725298a6e183320886a4fcd8065c [file] [log] [blame]
/*
* Copyright (c) 2004 - 2012 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 204890
*/
package org.eclipse.emf.cdo.internal.common.revision.delta;
import org.eclipse.emf.cdo.common.branch.CDOBranch;
import org.eclipse.emf.cdo.common.id.CDOID;
import org.eclipse.emf.cdo.common.id.CDOWithID;
import org.eclipse.emf.cdo.common.model.CDOModelUtil;
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.CDORevisable;
import org.eclipse.emf.cdo.common.revision.CDORevision;
import org.eclipse.emf.cdo.common.revision.CDORevisionData;
import org.eclipse.emf.cdo.common.revision.CDORevisionUtil;
import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDeltaVisitor;
import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
import org.eclipse.emf.cdo.common.util.PartialCollectionLoadingNotSupportedException;
import org.eclipse.emf.cdo.internal.common.revision.CDOListImpl;
import org.eclipse.emf.cdo.spi.common.revision.CDOReferenceAdjuster;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDOFeatureDelta;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevisionDelta;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.change.ListChange;
import org.eclipse.emf.ecore.change.util.ListDifferenceAnalyzer;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
* @author Eike Stepper
*/
public class CDORevisionDeltaImpl implements InternalCDORevisionDelta
{
private EClass eClass;
private CDOID id;
private CDOBranch branch;
private int version;
private CDORevisable target;
private Map<EStructuralFeature, CDOFeatureDelta> featureDeltas = new HashMap<EStructuralFeature, CDOFeatureDelta>();
public CDORevisionDeltaImpl(CDORevision revision)
{
eClass = revision.getEClass();
id = revision.getID();
branch = revision.getBranch();
version = revision.getVersion();
}
public CDORevisionDeltaImpl(CDORevisionDelta revisionDelta, boolean copyFeatureDeltas)
{
eClass = revisionDelta.getEClass();
id = revisionDelta.getID();
branch = revisionDelta.getBranch();
version = revisionDelta.getVersion();
if (copyFeatureDeltas)
{
for (CDOFeatureDelta delta : revisionDelta.getFeatureDeltas())
{
addFeatureDelta(((InternalCDOFeatureDelta)delta).copy());
}
}
}
public CDORevisionDeltaImpl(CDORevision sourceRevision, CDORevision targetRevision)
{
if (sourceRevision.getEClass() != targetRevision.getEClass())
{
throw new IllegalArgumentException();
}
eClass = sourceRevision.getEClass();
id = sourceRevision.getID();
branch = sourceRevision.getBranch();
version = sourceRevision.getVersion();
target = CDORevisionUtil.copyRevisable(targetRevision);
compare(sourceRevision, targetRevision);
CDORevisionData originData = sourceRevision.data();
CDORevisionData dirtyData = targetRevision.data();
Object dirtyContainerID = dirtyData.getContainerID();
if (dirtyContainerID instanceof CDOWithID)
{
dirtyContainerID = ((CDOWithID)dirtyContainerID).cdoID();
}
if (!compare(originData.getContainerID(), dirtyContainerID)
|| !compare(originData.getContainingFeatureID(), dirtyData.getContainingFeatureID())
|| !compare(originData.getResourceID(), dirtyData.getResourceID()))
{
addFeatureDelta(new CDOContainerFeatureDeltaImpl(dirtyData.getResourceID(), dirtyContainerID,
dirtyData.getContainingFeatureID()));
}
}
public CDORevisionDeltaImpl(CDODataInput in) throws IOException
{
eClass = (EClass)in.readCDOClassifierRefAndResolve();
id = in.readCDOID();
branch = in.readCDOBranch();
version = in.readInt();
if (version < 0)
{
version = -version;
target = in.readCDORevisable();
}
int size = in.readInt();
for (int i = 0; i < size; i++)
{
CDOFeatureDelta featureDelta = in.readCDOFeatureDelta(eClass);
featureDeltas.put(featureDelta.getFeature(), featureDelta);
}
}
public void write(CDODataOutput out) throws IOException
{
out.writeCDOClassifierRef(eClass);
out.writeCDOID(id);
out.writeCDOBranch(branch);
if (target == null)
{
out.writeInt(version);
}
else
{
out.writeInt(-version);
out.writeCDORevisable(target);
}
out.writeInt(featureDeltas.size());
for (CDOFeatureDelta featureDelta : featureDeltas.values())
{
out.writeCDOFeatureDelta(eClass, featureDelta);
}
}
public EClass getEClass()
{
return eClass;
}
public CDOID getID()
{
return id;
}
public CDOBranch getBranch()
{
return branch;
}
public void setBranch(CDOBranch branch)
{
this.branch = branch;
}
public int getVersion()
{
return version;
}
public void setVersion(int version)
{
this.version = version;
}
public CDORevisable getTarget()
{
return target;
}
public void setTarget(CDORevisable target)
{
this.target = target;
}
public boolean isEmpty()
{
return featureDeltas.isEmpty();
}
public CDORevisionDelta copy()
{
return new CDORevisionDeltaImpl(this, true);
}
public Map<EStructuralFeature, CDOFeatureDelta> getFeatureDeltaMap()
{
return featureDeltas;
}
public CDOFeatureDelta getFeatureDelta(EStructuralFeature feature)
{
return featureDeltas.get(feature);
}
public List<CDOFeatureDelta> getFeatureDeltas()
{
return new ArrayList<CDOFeatureDelta>(featureDeltas.values());
}
public void apply(CDORevision revision)
{
for (CDOFeatureDelta featureDelta : featureDeltas.values())
{
((CDOFeatureDeltaImpl)featureDelta).apply(revision);
}
}
public void addFeatureDelta(CDOFeatureDelta delta)
{
if (delta instanceof CDOListFeatureDelta)
{
CDOListFeatureDelta deltas = (CDOListFeatureDelta)delta;
for (CDOFeatureDelta childDelta : deltas.getListChanges())
{
addFeatureDelta(childDelta);
}
}
else
{
addSingleFeatureDelta(delta);
}
}
private void addSingleFeatureDelta(CDOFeatureDelta delta)
{
EStructuralFeature feature = delta.getFeature();
if (feature.isMany())
{
CDOListFeatureDeltaImpl listDelta = (CDOListFeatureDeltaImpl)featureDeltas.get(feature);
if (listDelta == null)
{
listDelta = new CDOListFeatureDeltaImpl(feature);
featureDeltas.put(listDelta.getFeature(), listDelta);
}
// Remove all previous changes
if (delta instanceof CDOClearFeatureDelta || delta instanceof CDOUnsetFeatureDelta)
{
listDelta.getListChanges().clear();
}
listDelta.add(delta);
}
else
{
featureDeltas.put(feature, delta);
}
}
public boolean adjustReferences(CDOReferenceAdjuster referenceAdjuster)
{
boolean changed = false;
for (CDOFeatureDelta featureDelta : featureDeltas.values())
{
changed |= ((CDOFeatureDeltaImpl)featureDelta).adjustReferences(referenceAdjuster);
}
return changed;
}
public void accept(CDOFeatureDeltaVisitor visitor)
{
for (CDOFeatureDelta featureDelta : featureDeltas.values())
{
((CDOFeatureDeltaImpl)featureDelta).accept(visitor);
}
}
private void compare(CDORevision originRevision, CDORevision dirtyRevision)
{
CDORevisionData originData = originRevision.data();
CDORevisionData dirtyData = dirtyRevision.data();
for (final EStructuralFeature feature : CDOModelUtil.getAllPersistentFeatures(eClass))
{
if (feature.isMany())
{
if (originData.size(feature) > 0 && dirtyData.size(feature) == 0)
{
addFeatureDelta(new CDOClearFeatureDeltaImpl(feature));
}
else
{
CDOListFeatureDelta listFeatureDelta = new CDOListFeatureDeltaImpl(feature);
final List<CDOFeatureDelta> changes = listFeatureDelta.getListChanges();
ListDifferenceAnalyzer analyzer = new ListDifferenceAnalyzer()
{
@Override
public void analyzeLists(EList<Object> oldList, EList<?> newList, EList<ListChange> listChanges)
{
checkNoProxies(oldList);
checkNoProxies(newList);
super.analyzeLists(oldList, newList, listChanges);
}
@Override
protected void createAddListChange(EList<Object> oldList, EList<ListChange> listChanges, Object value,
int index)
{
CDOFeatureDelta delta = new CDOAddFeatureDeltaImpl(feature, index, value);
changes.add(delta);
oldList.add(index, value);
}
@Override
protected void createRemoveListChange(EList<?> oldList, EList<ListChange> listChanges, Object value,
int index)
{
CDORemoveFeatureDeltaImpl delta = new CDORemoveFeatureDeltaImpl(feature, index);
// fix until ListDifferenceAnalyzer delivers the correct value (bug #308618).
delta.setValue(oldList.get(index));
changes.add(delta);
oldList.remove(index);
}
@Override
protected void createMoveListChange(EList<?> oldList, EList<ListChange> listChanges, Object value,
int index, int toIndex)
{
CDOMoveFeatureDeltaImpl delta = new CDOMoveFeatureDeltaImpl(feature, toIndex, index);
// fix until ListDifferenceAnalyzer delivers the correct value (same problem as bug #308618).
delta.setValue(oldList.get(index));
changes.add(delta);
oldList.move(toIndex, index);
}
private void checkNoProxies(EList<?> list)
{
for (Object element : list)
{
if (element instanceof CDOElementProxy || element == CDOListImpl.UNINITIALIZED)
{
throw new PartialCollectionLoadingNotSupportedException("List contains proxy elements");
}
}
}
};
CDOList originList = ((InternalCDORevision)originRevision).getList(feature);
CDOList dirtyList = ((InternalCDORevision)dirtyRevision).getList(feature);
analyzer.analyzeLists(originList, dirtyList, new NOOPList());
if (!changes.isEmpty())
{
featureDeltas.put(feature, listFeatureDelta);
}
}
}
else
{
Object originValue = originData.get(feature, 0);
Object dirtyValue = dirtyData.get(feature, 0);
if (!compare(originValue, dirtyValue))
{
if (dirtyValue == null)
{
addFeatureDelta(new CDOUnsetFeatureDeltaImpl(feature));
}
else
{
addFeatureDelta(new CDOSetFeatureDeltaImpl(feature, 0, dirtyValue, originValue));
}
}
}
}
}
private boolean compare(Object originValue, Object dirtyValue)
{
return originValue == dirtyValue || originValue != null && dirtyValue != null && originValue.equals(dirtyValue);
}
@Override
public String toString()
{
return MessageFormat.format("CDORevisionDelta[{0}@{1}:{2}v{3} --> {4}]", eClass.getName(), id, branch.getID(),
version, featureDeltas.values());
}
/**
* @author Eike Stepper
*/
public static class NOOPList implements EList<ListChange>
{
private static final EList<ListChange> LIST = ECollections.emptyEList();
public NOOPList()
{
}
public int size()
{
return 0;
}
public boolean isEmpty()
{
return true;
}
public boolean contains(Object o)
{
return false;
}
public Iterator<ListChange> iterator()
{
return LIST.iterator();
}
public Object[] toArray()
{
return LIST.toArray();
}
public <T> T[] toArray(T[] a)
{
return LIST.toArray(a);
}
public boolean add(ListChange o)
{
return false;
}
public boolean remove(Object o)
{
return false;
}
public boolean containsAll(Collection<?> c)
{
return false;
}
public boolean addAll(Collection<? extends ListChange> c)
{
return false;
}
public boolean addAll(int index, Collection<? extends ListChange> c)
{
return false;
}
public boolean removeAll(Collection<?> c)
{
return false;
}
public boolean retainAll(Collection<?> c)
{
return false;
}
public void clear()
{
}
public ListChange get(int index)
{
return LIST.get(index);
}
public ListChange set(int index, ListChange element)
{
return null;
}
public void add(int index, ListChange element)
{
}
public ListChange remove(int index)
{
return null;
}
public int indexOf(Object o)
{
return LIST.indexOf(o);
}
public int lastIndexOf(Object o)
{
return LIST.lastIndexOf(o);
}
public ListIterator<ListChange> listIterator()
{
return LIST.listIterator();
}
public ListIterator<ListChange> listIterator(int index)
{
return LIST.listIterator(index);
}
public List<ListChange> subList(int fromIndex, int toIndex)
{
return LIST.subList(fromIndex, toIndex);
}
public void move(int newPosition, ListChange object)
{
}
public ListChange move(int newPosition, int oldPosition)
{
return null;
}
}
}