package jetbrains.mps.vcs.diff.merge; /*Generated by MPS */ import jetbrains.mps.vcs.diff.ChangeSet; import java.util.Map; import jetbrains.mps.vcs.diff.changes.ModelChange; import java.util.List; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import java.util.Set; import jetbrains.mps.internal.collections.runtime.SetSequence; import java.util.HashSet; import jetbrains.mps.vcs.diff.changes.NodeCopier; import org.jetbrains.mps.openapi.model.SModel; import jetbrains.mps.smodel.CopyUtil; import jetbrains.mps.extapi.model.SModelBase; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.vcs.diff.changes.MetadataChange; import jetbrains.mps.vcs.diff.changes.NodeGroupChange; import jetbrains.mps.vcs.diff.changes.NodeChange; import jetbrains.mps.vcs.diff.changes.AddRootChange; import jetbrains.mps.vcs.diff.changes.DeleteRootChange; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import java.util.Arrays; import jetbrains.mps.internal.collections.runtime.ITranslator2; import java.util.Collections; import org.jetbrains.annotations.NotNull; import jetbrains.mps.internal.collections.runtime.IVisitor; import java.util.Comparator; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations; import jetbrains.mps.smodel.references.UnregisteredNodes; import jetbrains.mps.persistence.PersistenceVersionAware; import jetbrains.mps.smodel.SModelAdapter; import jetbrains.mps.smodel.event.SModelEvent; import jetbrains.mps.smodel.event.SModelReferenceEvent; import jetbrains.mps.vcs.diff.changes.SetReferenceChange; import jetbrains.mps.util.IterableUtil; import jetbrains.mps.baseLanguage.closures.runtime.Wrappers; import jetbrains.mps.smodel.event.SModelChildEvent; import jetbrains.mps.smodel.event.SModelPropertyEvent; import jetbrains.mps.vcs.diff.changes.SetPropertyChange; import jetbrains.mps.smodel.event.SModelRootEvent; import org.jetbrains.mps.openapi.model.SNodeReference; import jetbrains.mps.smodel.MPSModuleRepository; public final class MergeSession { private ChangeSet myMineChangeSet; private ChangeSet myRepositoryChangeSet; private Map<ModelChange, List<ModelChange>> myConflictingChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>()); private Map<ModelChange, List<ModelChange>> mySymmetricChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>()); private Map<SNodeId, List<ModelChange>> myRootToChanges = MapSequence.fromMap(new HashMap<SNodeId, List<ModelChange>>()); private Map<SNodeId, List<ModelChange>> myNodeToChanges = MapSequence.fromMap(new HashMap<SNodeId, List<ModelChange>>()); private List<ModelChange> myMetadataChanges = ListSequence.fromList(new ArrayList<ModelChange>()); private MergeTemporaryModel myResultModel; private Set<ModelChange> myResolvedChanges = SetSequence.fromSet(new HashSet<ModelChange>()); private NodeCopier myNodeCopier; private MergeSession.MyResultModelListener myModelListener = new MergeSession.MyResultModelListener(); private MergeSession.ChangesInvalidateHandler myChangesInvalidateHandler; public static MergeSession createMergeSession(SModel base, SModel mine, SModel repository) { // TODO generalize merge for any SModel jetbrains.mps.smodel.SModel resModel = CopyUtil.copyModel(((SModelBase) base).getSModel()); MergeTemporaryModel result = new MergeTemporaryModel(resModel, false); int pv = Math.max(getPersistenceVersion(base), Math.max(getPersistenceVersion(mine), getPersistenceVersion(repository))); result.setPersistenceVersion(pv); return new MergeSession(base, mine, repository, result); } private MergeSession(SModel base, SModel mine, SModel repository, MergeTemporaryModel result) { MergeConflictsBuilder conflictsBuilder = new MergeConflictsBuilder(base, mine, repository); myMineChangeSet = conflictsBuilder.myMineChangeSet; myRepositoryChangeSet = conflictsBuilder.myRepositoryChangeSet; myConflictingChanges = conflictsBuilder.myConflictingChanges; mySymmetricChanges = conflictsBuilder.mySymmetricChanges; fillRootToChangesMap(); fillNodeToChangesMap(); myResultModel = result; myNodeCopier = new NodeCopier(myResultModel); } private void fillRootToChangesMap() { for (ModelChange change : Sequence.fromIterable(getAllChanges())) { SNodeId rootId = change.getRootId(); if (rootId == null) { assert change instanceof MetadataChange; ListSequence.fromList(myMetadataChanges).addElement(change); } else { if (MapSequence.fromMap(myRootToChanges).get(rootId) == null) { MapSequence.fromMap(myRootToChanges).put(rootId, ListSequence.fromList(new ArrayList<ModelChange>())); } ListSequence.fromList(MapSequence.fromMap(myRootToChanges).get(rootId)).addElement(change); } } } public void installResultModelListener() { myResultModel.addModelListener(myModelListener); } private void fillNodeToChangesMap() { for (ModelChange change : Sequence.fromIterable(getAllChanges())) { SNodeId nodeId = null; if (change instanceof NodeGroupChange) { nodeId = ((NodeGroupChange) change).getParentNodeId(); } else if (change instanceof NodeChange) { nodeId = ((NodeChange) change).getAffectedNodeId(); } else if (change instanceof AddRootChange || change instanceof DeleteRootChange) { nodeId = change.getRootId(); } if (nodeId != null) { if (MapSequence.fromMap(myNodeToChanges).get(nodeId) == null) { MapSequence.fromMap(myNodeToChanges).put(nodeId, ListSequence.fromList(new ArrayList<ModelChange>())); } ListSequence.fromList(MapSequence.fromMap(myNodeToChanges).get(nodeId)).addElement(change); } } } public Iterable<ModelChange> getApplicableChangesForRoot(SNodeId rootId) { return ListSequence.fromList(MapSequence.fromMap(myRootToChanges).get(rootId)).where(new IWhereFilter<ModelChange>() { public boolean accept(ModelChange ch) { return !(SetSequence.fromSet(myResolvedChanges).contains(ch)) && Sequence.fromIterable(getConflictedWith(ch)).isEmpty(); } }); } public Iterable<ModelChange> getApplicableChangesInNonConflictingRoots() { return Sequence.fromIterable((Sequence.fromIterable(MapSequence.fromMap(myRootToChanges).values()).concat(ListSequence.fromList(Arrays.asList(myMetadataChanges))))).translate(new ITranslator2<List<ModelChange>, ModelChange>() { public Iterable<ModelChange> translate(List<ModelChange> changes) { Iterable<ModelChange> unresolvedForRoot = ListSequence.fromList(changes).where(new IWhereFilter<ModelChange>() { public boolean accept(ModelChange ch) { return !(SetSequence.fromSet(myResolvedChanges).contains(ch)); } }); if (Sequence.fromIterable(unresolvedForRoot).all(new IWhereFilter<ModelChange>() { public boolean accept(ModelChange ch) { return Sequence.fromIterable(getConflictedWith(ch)).isEmpty(); } })) { return unresolvedForRoot; } else { return Sequence.fromIterable(Collections.<ModelChange>emptyList()); } } }); } public Iterable<ModelChange> getAllChanges() { return ListSequence.fromList(myMineChangeSet.getModelChanges()).concat(ListSequence.fromList(myRepositoryChangeSet.getModelChanges())); } public Iterable<SNodeId> getAffectedRoots() { return (ListSequence.fromList(myMetadataChanges).isEmpty() ? MapSequence.fromMap(myRootToChanges).keySet() : SetSequence.fromSet(MapSequence.fromMap(myRootToChanges).keySet()).concat(ListSequence.fromList(ListSequence.fromListAndArray(new ArrayList<SNodeId>(), null)))); } public List<ModelChange> getChangesForRoot(@NotNull SNodeId rootId) { return MapSequence.fromMap(myRootToChanges).get(rootId); } public List<ModelChange> getMetadataChanges() { return myMetadataChanges; } public Iterable<ModelChange> getConflictedWith(ModelChange change) { return ListSequence.fromList(MapSequence.fromMap(myConflictingChanges).get(change)).where(new IWhereFilter<ModelChange>() { public boolean accept(ModelChange other) { return !(SetSequence.fromSet(myResolvedChanges).contains(other)); } }); } public boolean isChangeResolved(ModelChange change) { return SetSequence.fromSet(myResolvedChanges).contains(change); } public void applyChanges(Iterable<ModelChange> changes) { applyChangesNoRestoreIds(changes); myNodeCopier.restoreIds(false); } public void excludeChanges(Iterable<ModelChange> changes) { excludeChangesNoRestoreIds(changes); myNodeCopier.restoreIds(false); } private void applyChangesNoRestoreIds(Iterable<ModelChange> changes) { Sequence.fromIterable(changes).where(new IWhereFilter<ModelChange>() { public boolean accept(ModelChange ch) { return ch instanceof NodeGroupChange; } }).visitAll(new IVisitor<ModelChange>() { public void visit(ModelChange ch) { ((NodeGroupChange) ch).prepare(); } }); for (ModelChange c : Sequence.fromIterable(changes).sort(new Comparator<ModelChange>() { public int compare(ModelChange a, ModelChange b) { // sort out nonconflicting changes to the end of list, so they will be ignored if other connected changes exists boolean aa = a.isNonConflicting(); boolean bb = b.isNonConflicting(); int result = (aa == bb ? 0 : (aa ? 1 : -1)); return result; } }, true)) { applyChange(c); } } private void excludeChangesNoRestoreIds(Iterable<ModelChange> changes) { for (ModelChange c : Sequence.fromIterable(changes)) { excludeChange(c); } } private void applyChange(ModelChange change) { if (SetSequence.fromSet(myResolvedChanges).contains(change)) { return; } // for nonconflicting change we can execute symmetric if it suits better if (change.isNonConflicting()) { ModelChange symmChange = ListSequence.fromList(MapSequence.fromMap(mySymmetricChanges).get(change)).subtract(SetSequence.fromSet(myResolvedChanges)).first(); if (symmChange != null) { boolean isMineChange = change.getChangeSet() == myMineChangeSet; SNode mergeHint = SNodeOperations.as(((SNode) check_bow6nj_a0a0a1a1a3a13(change.getMergeHint())), MetaAdapterFactory.getConcept(0x37e03aa1728949bcL, 0x826930de5eceec76L, 0x657f08af7deb331aL, "jetbrains.mps.vcs.mergehints.structure.MergeHint")); if ((mergeHint != null) && (SPropertyOperations.hasValue(mergeHint, MetaAdapterFactory.getProperty(0x37e03aa1728949bcL, 0x826930de5eceec76L, 0x657f08af7deb331aL, 0x75c17d085c8e0dbaL, "hint"), "1", "1") != isMineChange)) { // execute more appropriate symmetric change, original change will be excluded change = symmChange; } } } change.apply(myResultModel, myNodeCopier); SetSequence.fromSet(myResolvedChanges).addElement(change); SetSequence.fromSet(myResolvedChanges).addSequence(ListSequence.fromList(MapSequence.fromMap(mySymmetricChanges).get(change))); excludeChangesNoRestoreIds(getConflictedWith(change)); } private void excludeChange(ModelChange change) { if (SetSequence.fromSet(myResolvedChanges).contains(change)) { return; } SetSequence.fromSet(myResolvedChanges).addElement(change); SetSequence.fromSet(myResolvedChanges).addSequence(ListSequence.fromList(MapSequence.fromMap(mySymmetricChanges).get(change))); } public boolean hasIdsToRestore() { return myNodeCopier.hasIdsToRestore(); } public SNodeId getReplacementId(SNodeId originalId) { return myNodeCopier.getReplacementId(originalId); } public SModel getResultModel() { return myResultModel; } public SModel getBaseModel() { return myMineChangeSet.getOldModel(); } public SModel getMyModel() { return myMineChangeSet.getNewModel(); } public SModel getRepositoryModel() { return myRepositoryChangeSet.getNewModel(); } public ChangeSet getMyChangeSet() { return myMineChangeSet; } public ChangeSet getRepositoryChangeSet() { return myRepositoryChangeSet; } public boolean isMyChange(ModelChange change) { return change.getChangeSet() == myMineChangeSet; } public MergeSessionState getCurrentState() { return new MergeSessionState(myResultModel, myResolvedChanges, myNodeCopier.getState()); } public void restoreState(MergeSessionState state) { MergeSessionState stateCopy = new MergeSessionState(state); myResultModel.setSModelInternal(stateCopy.myResultModel.getSModelInternal()); // clear UnregisteredNodes pool to avoid a lot of ERRORs in log: UnregisteredNodes.instance().clear(); myResolvedChanges = stateCopy.myResolvedChanges; myNodeCopier.setState(stateCopy.myIdReplacementCache, myResultModel); } public void setChangesInvalidateHandler(MergeSession.ChangesInvalidateHandler changesInvalidateHandler) { myChangesInvalidateHandler = changesInvalidateHandler; } private void invalidateChanges(Iterable<? extends ModelChange> changes) { if (Sequence.fromIterable(changes).isNotEmpty()) { SetSequence.fromSet(myResolvedChanges).addSequence(Sequence.fromIterable(changes)); check_bow6nj_a1a0a54(myChangesInvalidateHandler); } } private static int getPersistenceVersion(SModel model) { if (model instanceof PersistenceVersionAware) { return ((PersistenceVersionAware) model).getPersistenceVersion(); } return -1; } public interface ChangesInvalidateHandler { void someChangesInvalidated(); } private class MyResultModelListener extends SModelAdapter { private MyResultModelListener() { } private void invalidateDeletedRoot(SModelEvent event) { assert event.getAffectedRoot() != null; List<ModelChange> nodeChanges = MapSequence.fromMap(myNodeToChanges).get(event.getAffectedRoot().getNodeId()); invalidateChanges(ListSequence.fromList(nodeChanges).ofType(DeleteRootChange.class)); } private void beforeNodeRemovedRecursively(SNode node) { for (SNode child : ListSequence.fromList(SNodeOperations.getChildren(node))) { beforeNodeRemovedRecursively(child); } // process child invalidateChanges(MapSequence.fromMap(myNodeToChanges).get(node.getNodeId())); } private void referenceModified(final SModelReferenceEvent event) { List<ModelChange> nodeChanges = MapSequence.fromMap(myNodeToChanges).get(event.getReference().getSourceNode().getNodeId()); invalidateChanges(ListSequence.fromList(nodeChanges).ofType(SetReferenceChange.class).where(new IWhereFilter<SetReferenceChange>() { public boolean accept(SetReferenceChange ch) { return eq_bow6nj_a0a0a0a0a0a1a3wb(ch.getRole(), event.getReference().getRole()); } })); invalidateDeletedRoot(event); } @Override public void referenceRemoved(SModelReferenceEvent event) { referenceModified(event); } @Override public void referenceAdded(SModelReferenceEvent event) { referenceModified(event); } private List<NodeGroupChange> getRelevantNodeGroupChanges(SNode parent, final String role) { List<ModelChange> nodeChanges = MapSequence.fromMap(myNodeToChanges).get(parent.getNodeId()); Iterable<NodeGroupChange> allNodeGroupChanges = ListSequence.fromList(nodeChanges).ofType(NodeGroupChange.class); return Sequence.fromIterable(allNodeGroupChanges).where(new IWhereFilter<NodeGroupChange>() { public boolean accept(NodeGroupChange ngc) { return role.equals(ngc.getRole()); } }).toListSequence(); } private void invalidateChildrenChanges(SNode parent, String role, int index, final int beginOffset, final int endOffset) { List<? extends SNode> currentChildren = IterableUtil.asList(parent.getChildren(role)); List<NodeGroupChange> relevantChanges = getRelevantNodeGroupChanges(parent, role); if (ListSequence.fromList(relevantChanges).isEmpty()) { return; } SNode baseParent = myMineChangeSet.getOldModel().getNode(parent.getNodeId()); if (baseParent == null) { return; } List<SNode> baseChildren = IterableUtil.asList(baseParent.getChildren(role)); final Wrappers._int baseIndex = new Wrappers._int(); if (0 <= index && index < currentChildren.size()) { final SNodeId currentChildId = currentChildren.get(index).getNodeId(); SNode baseChild = ListSequence.fromList(baseChildren).findFirst(new IWhereFilter<SNode>() { public boolean accept(SNode c) { return currentChildId.equals(c.getNodeId()); } }); if (baseChild == null) { return; } baseIndex.value = SNodeOperations.getIndexInParent(baseChild); } else if (index == 0) { baseIndex.value = 0; } else if (index == currentChildren.size()) { baseIndex.value = ListSequence.fromList(baseChildren).count(); } else { return; } invalidateChanges(ListSequence.fromList(relevantChanges).where(new IWhereFilter<NodeGroupChange>() { public boolean accept(NodeGroupChange ch) { return ch.getBegin() + beginOffset <= baseIndex.value && baseIndex.value < ch.getEnd() + endOffset; } })); } private void invalidateChildrenChanges(SModelChildEvent event, int offset) { int index = SNodeOperations.getIndexInParent(event.getChild()) + offset; int beginOffset = (offset == 1 ? 0 : -1); int endOffset = (offset == -1 ? 0 : 1); invalidateChildrenChanges(event.getParent(), event.getChildRole(), index, beginOffset, endOffset); } @Override public void beforeChildRemoved(SModelChildEvent event) { beforeNodeRemovedRecursively(event.getChild()); invalidateDeletedRoot(event); invalidateChildrenChanges(event, 0); } @Override public void childAdded(SModelChildEvent event) { invalidateDeletedRoot(event); invalidateChildrenChanges(event, -1); invalidateChildrenChanges(event, 1); } @Override public void propertyChanged(final SModelPropertyEvent event) { List<ModelChange> nodeChanges = MapSequence.fromMap(myNodeToChanges).get(event.getNode().getNodeId()); invalidateChanges(ListSequence.fromList(nodeChanges).ofType(SetPropertyChange.class).where(new IWhereFilter<SetPropertyChange>() { public boolean accept(SetPropertyChange ch) { return eq_bow6nj_a0a0a0a0a0a1a11wb(ch.getPropertyName(), event.getPropertyName()); } })); invalidateDeletedRoot(event); } @Override public void beforeRootRemoved(SModelRootEvent event) { beforeNodeRemovedRecursively(event.getRoot()); invalidateDeletedRoot(event); } } private static SNode check_bow6nj_a0a0a1a1a3a13(SNodeReference checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.resolve(MPSModuleRepository.getInstance()); } return null; } private static void check_bow6nj_a1a0a54(MergeSession.ChangesInvalidateHandler checkedDotOperand) { if (null != checkedDotOperand) { checkedDotOperand.someChangesInvalidated(); } } private static boolean eq_bow6nj_a0a0a0a0a0a1a3wb(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } private static boolean eq_bow6nj_a0a0a0a0a0a1a11wb(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } }