/** * @return the result feature delta, or <code>null</code> to indicate an unresolved conflict. */ protected CDOFeatureDelta changedInSourceAndTarget( CDOFeatureDelta targetFeatureDelta, CDOFeatureDelta sourceFeatureDelta) { EStructuralFeature feature = targetFeatureDelta.getFeature(); if (feature.isMany()) { return changedInSourceAndTargetManyValued(feature, targetFeatureDelta, sourceFeatureDelta); } return changedInSourceAndTargetSingleValued(feature, targetFeatureDelta, sourceFeatureDelta); }
protected boolean hasFeatureConflicts( CDORevisionDelta localDelta, List<CDORevisionDelta> remoteDeltas) { Set<EStructuralFeature> features = new HashSet<EStructuralFeature>(); for (CDOFeatureDelta localFeatureDelta : localDelta.getFeatureDeltas()) { features.add(localFeatureDelta.getFeature()); } for (CDORevisionDelta remoteDelta : remoteDeltas) { for (CDOFeatureDelta remoteFeatureDelta : remoteDelta.getFeatureDeltas()) { EStructuralFeature feature = remoteFeatureDelta.getFeature(); if (features.contains(feature)) { return true; } } } return false; }
/** * @return the result feature delta, or <code>null</code> to indicate an unresolved conflict. */ protected CDOFeatureDelta changedInSourceAndTargetSingleValued( EStructuralFeature feature, CDOFeatureDelta targetFeatureDelta, CDOFeatureDelta sourceFeatureDelta) { if (targetFeatureDelta.isStructurallyEqual(sourceFeatureDelta)) { return targetFeatureDelta; } return null; }
@Override protected CDOFeatureDelta changedInSourceAndTargetManyValued( EStructuralFeature feature, CDOFeatureDelta targetFeatureDelta, CDOFeatureDelta sourceFeatureDelta) { if (targetFeatureDelta instanceof CDOListFeatureDelta && sourceFeatureDelta instanceof CDOListFeatureDelta) { // Initialize work lists with virtual elements int originSize = ((CDOListFeatureDelta) sourceFeatureDelta.copy()).getOriginSize(); BasicEList<Element> ancestorList = new BasicEList<Element>(originSize); PerSide<BasicEList<Element>> listPerSide = new PerSide<BasicEList<Element>>(); initWorkLists(originSize, ancestorList, listPerSide); // Apply list changes to source and target work lists PerSide<List<CDOFeatureDelta>> changesPerSide = new PerSide<List<CDOFeatureDelta>>( copyListChanges(sourceFeatureDelta), copyListChanges(targetFeatureDelta)); Map<Object, List<Element>> additions = new HashMap<Object, List<Element>>(); Map<CDOFeatureDelta, Element> allElements = new HashMap<CDOFeatureDelta, Element>(); applyChangesToWorkList(Side.SOURCE, listPerSide, changesPerSide, allElements, additions); applyChangesToWorkList(Side.TARGET, listPerSide, changesPerSide, allElements, additions); // Pick changes from source and target sides into the merge result CDOListFeatureDelta result = new CDOListFeatureDeltaImpl(feature, originSize); List<CDOFeatureDelta> resultChanges = result.getListChanges(); pickChangesIntoResult( Side.SOURCE, feature, ancestorList, changesPerSide, allElements, additions, resultChanges); pickChangesIntoResult( Side.TARGET, feature, ancestorList, changesPerSide, allElements, additions, resultChanges); return result; } return super.changedInSourceAndTargetManyValued( feature, targetFeatureDelta, sourceFeatureDelta); }
private List<CDOFeatureDelta> copyListChanges(CDOFeatureDelta featureDelta) { CDOListFeatureDelta listFeatureDelta = (CDOListFeatureDelta) featureDelta.copy(); List<CDOFeatureDelta> copy = listFeatureDelta.getListChanges(); if (!copy.isEmpty()) { CDOFeatureDelta.Type firstType = copy.get(0).getType(); if (firstType == Type.CLEAR || firstType == Type.UNSET) { copy.remove(0); List<CDOFeatureDelta> expandedDeltas = expandClearDelta(listFeatureDelta); copy.addAll(0, expandedDeltas); } } return copy; }
private void pickChangesIntoResult( Side side, EStructuralFeature feature, BasicEList<Element> ancestorList, PerSide<List<CDOFeatureDelta>> changesPerSide, Map<CDOFeatureDelta, Element> allElements, Map<Object, List<Element>> additions, List<CDOFeatureDelta> result) { List<CDOFeatureDelta> changes = changesPerSide.get(side); for (CDOFeatureDelta change : changes) { Type changeType = change.getType(); switch (changeType) { case ADD: { CDOAddFeatureDeltaImpl addChange = (CDOAddFeatureDeltaImpl) change; result.add(addChange); int sideIndex = addChange.getIndex(); int ancestorIndex = sideIndex; int ancestorEnd = ancestorList.size(); if (ancestorIndex > ancestorEnd) { // TODO Better way to adjust ancestor indexes? ancestorIndex = ancestorEnd; addChange.setIndex(ancestorIndex); } Element newElement = allElements.get(addChange); ancestorList.add(ancestorIndex, newElement); if (treatAsUnique(feature)) { // Detect and remove corresponding AddDeltas from the other side Object value = addChange.getValue(); List<Element> elementsToAdd = additions.get(value); if (elementsToAdd != null) { for (Element element : elementsToAdd) { CDOAddFeatureDelta otherAdd = (CDOAddFeatureDelta) element.get(other(side)); if (otherAdd != null) { element.set(other(side), null); // Not taking an AddDelta has the same effect on indexes as a removal of the // element List<CDOFeatureDelta> otherChanges = changesPerSide.get(other(side)); int otherIndex = otherAdd.getIndex(); adjustAfterRemoval(otherChanges, otherIndex, addChange); } } } } break; } case REMOVE: { CDORemoveFeatureDeltaImpl removeChange = (CDORemoveFeatureDeltaImpl) change; result.add(removeChange); Element removedElement = allElements.get(removeChange); int ancestorIndex = ancestorList.indexOf(removedElement); removeChange.setIndex(ancestorIndex); ancestorList.remove(ancestorIndex); // Detect and remove a potential duplicate RemoveDelta from the other side CDOFeatureDelta otherChange = removedElement.get(other(side)); if (otherChange != null) { Type otherChangeType = otherChange.getType(); switch (otherChangeType) { case REMOVE: { CDORemoveFeatureDelta otherRemove = (CDORemoveFeatureDelta) otherChange; removedElement.set(other(side), null); // Not taking a RemoveDelta has the same effect on indexes as an addition of // the element List<CDOFeatureDelta> otherChanges = changesPerSide.get(other(side)); int otherIndex = otherRemove.getIndex(); adjustAfterAddition(otherChanges, otherIndex, otherRemove); break; } case MOVE: { CDOMoveFeatureDelta otherMove = (CDOMoveFeatureDelta) otherChange; removedElement.set(other(side), null); // Not taking a MoveDelta has the same effect on indexes as a reverse move // of the element List<CDOFeatureDelta> otherChanges = changesPerSide.get(other(side)); int otherOldPosition = otherMove.getOldPosition(); int otherNewPosition = otherMove.getNewPosition(); adjustAfterMove( otherChanges, otherOldPosition, otherNewPosition, otherMove); break; } default: throw new IllegalStateException("Unexpected change type: " + otherChangeType); } } break; } case SET: { throw new IllegalStateException("Unhandled change type: " + changeType); // CDOSetFeatureDelta setChange = (CDOSetFeatureDelta)change; // break; } case MOVE: { CDOMoveFeatureDeltaImpl moveChange = (CDOMoveFeatureDeltaImpl) change; int sideOldPosition = moveChange.getOldPosition(); int sideNewPosition = moveChange.getNewPosition(); Element movedElement = allElements.get(moveChange); CDOFeatureDelta otherChange = movedElement.get(other(side)); if (otherChange != null) { Type otherChangeType = otherChange.getType(); switch (otherChangeType) { case REMOVE: { // Prioritize the RemoveDelta of the other side, delete the MoveDelta from // this side adjustAfterMove(changes, sideOldPosition, sideNewPosition, moveChange); movedElement.set(side, null); return; } case MOVE: { CDOMoveFeatureDelta otherMove = (CDOMoveFeatureDelta) otherChange; movedElement.set(other(side), null); // Not taking a MoveDelta has the same effect on indexes as a reverse move // of the element List<CDOFeatureDelta> otherChanges = changesPerSide.get(other(side)); int otherOldPosition = otherMove.getOldPosition(); int otherNewPosition = otherMove.getNewPosition(); adjustAfterMove( otherChanges, otherOldPosition, otherNewPosition, otherMove); movedElement.set(other(side), null); break; } default: throw new IllegalStateException("Unexpected change type: " + otherChangeType); } } int positionDelta = sideNewPosition - sideOldPosition; int ancestorOldPosition = ancestorList.indexOf(movedElement); int ancestorNewPosition = ancestorOldPosition + positionDelta; if (ancestorNewPosition < 0) { ancestorNewPosition = 0; } int ancestorEnd = ancestorList.size() - 1; if (ancestorNewPosition > ancestorEnd) { ancestorNewPosition = ancestorEnd; } moveChange.setOldPosition(ancestorOldPosition); moveChange.setNewPosition(ancestorNewPosition); result.add(moveChange); ancestorList.move(ancestorNewPosition, ancestorOldPosition); break; } case CLEAR: case UNSET: default: throw new IllegalStateException("Illegal change type: " + changeType); } } }
private void applyChangesToWorkList( Side side, PerSide<BasicEList<Element>> listPerSide, PerSide<List<CDOFeatureDelta>> changesPerSide, Map<CDOFeatureDelta, Element> allElements, Map<Object, List<Element>> additions) { BasicEList<Element> list = listPerSide.get(side); List<CDOFeatureDelta> changes = changesPerSide.get(side); for (CDOFeatureDelta change : changes) { Type changeType = change.getType(); switch (changeType) { case ADD: { CDOAddFeatureDelta addChange = (CDOAddFeatureDelta) change; Element element = new Element(-1); element.set(side, addChange); allElements.put(addChange, element); list.add(addChange.getIndex(), element); rememberAddition(addChange.getValue(), element, additions); break; } case REMOVE: { CDORemoveFeatureDelta removeChange = (CDORemoveFeatureDelta) change; Element element = list.remove(removeChange.getIndex()); element.set(side, removeChange); allElements.put(removeChange, element); break; } case SET: { CDOSetFeatureDelta setChange = (CDOSetFeatureDelta) change; Element newElement = new Element(-1); newElement.set(side, setChange); rememberAddition(setChange.getValue(), newElement, additions); Element oldElement = list.set(setChange.getIndex(), newElement); oldElement.set(side, setChange); allElements.put(setChange, oldElement); break; } case MOVE: { CDOMoveFeatureDelta moveChange = (CDOMoveFeatureDelta) change; Element element = list.move(moveChange.getNewPosition(), moveChange.getOldPosition()); element.set(side, moveChange); allElements.put(moveChange, element); break; } case CLEAR: case UNSET: // These deltas should have been replaced by multiple REMOVE deltas in // copyListChanges() throw new IllegalStateException("Unhandled change type: " + changeType); default: throw new IllegalStateException("Illegal change type: " + changeType); } } }
@Override protected Object changedInSourceAndTarget( CDORevisionDelta targetDelta, CDORevisionDelta sourceDelta) { InternalCDORevisionDelta result = new CDORevisionDeltaImpl(targetDelta, false); ChangedInSourceAndTargetConflict conflict = null; Map<EStructuralFeature, CDOFeatureDelta> targetMap = ((InternalCDORevisionDelta) targetDelta).getFeatureDeltaMap(); Map<EStructuralFeature, CDOFeatureDelta> sourceMap = ((InternalCDORevisionDelta) sourceDelta).getFeatureDeltaMap(); for (CDOFeatureDelta targetFeatureDelta : targetMap.values()) { EStructuralFeature feature = targetFeatureDelta.getFeature(); CDOFeatureDelta sourceFeatureDelta = sourceMap.get(feature); if (sourceFeatureDelta == null) { CDOFeatureDelta featureDelta = changedInTarget(targetFeatureDelta); if (featureDelta != null) { result.addFeatureDelta(featureDelta, null); } } else { CDOFeatureDelta featureDelta = changedInSourceAndTarget(targetFeatureDelta, sourceFeatureDelta); if (featureDelta != null) { result.addFeatureDelta(featureDelta, null); } else { if (conflict == null) { ResolutionPreference resolutionPreference = getResolutionPreference(); switch (resolutionPreference) { case SOURCE_OVER_TARGET: // TODO: implement // DefaultCDOMerger.PerFeature.changedInSourceAndTarget(targetDelta, sourceDelta) throw new UnsupportedOperationException(); case TARGET_OVER_SOURCE: // TODO: implement // DefaultCDOMerger.PerFeature.changedInSourceAndTarget(targetDelta, sourceDelta) throw new UnsupportedOperationException(); case NONE: conflict = new ChangedInSourceAndTargetConflict( new CDORevisionDeltaImpl(targetDelta, false), new CDORevisionDeltaImpl(sourceDelta, false)); break; default: throw new IllegalStateException( "Illegal resolution preference: " + resolutionPreference); } } ((InternalCDORevisionDelta) conflict.getTargetDelta()) .addFeatureDelta(targetFeatureDelta, null); ((InternalCDORevisionDelta) conflict.getSourceDelta()) .addFeatureDelta(sourceFeatureDelta, null); } } } for (CDOFeatureDelta sourceFeatureDelta : sourceMap.values()) { EStructuralFeature feature = sourceFeatureDelta.getFeature(); CDOFeatureDelta targetFeatureDelta = targetMap.get(feature); if (targetFeatureDelta == null) { CDOFeatureDelta featureDelta = changedInSource(sourceFeatureDelta); if (featureDelta != null) { result.addFeatureDelta(featureDelta, null); } } } if (result.isEmpty()) { return conflict; } if (conflict != null) { return Pair.create(result, conflict); } return result; }