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);
          }
        }
      }