package jetbrains.mps.vcs.diff.merge; /*Generated by MPS */ import org.jetbrains.mps.openapi.model.SModel; 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 jetbrains.mps.vcs.diff.ChangeSetBuilder; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import jetbrains.mps.baseLanguage.closures.runtime._FunctionTypes; import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple; import jetbrains.mps.internal.collections.runtime.SetSequence; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.vcs.diff.changes.NodeGroupChange; import jetbrains.mps.vcs.diff.changes.DeleteRootChange; import jetbrains.mps.internal.collections.runtime.ListSequence; import jetbrains.mps.vcs.diff.changes.NodeChange; import org.jetbrains.mps.openapi.model.SNode; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import jetbrains.mps.internal.collections.runtime.IWhereFilter; import jetbrains.mps.vcs.diff.changes.SetPropertyChange; import jetbrains.mps.util.EqualUtil; import jetbrains.mps.vcs.diff.changes.SetReferenceChange; import jetbrains.mps.vcs.diff.changes.AddRootChange; import jetbrains.mps.util.SNodeCompare; import jetbrains.mps.util.IterableUtil; import org.jetbrains.mps.openapi.model.SModelReference; import jetbrains.mps.vcs.diff.changes.ImportedModelChange; import org.jetbrains.mps.openapi.module.SModuleReference; import jetbrains.mps.vcs.diff.changes.ModuleDependencyChange; import org.jetbrains.mps.openapi.language.SLanguage; import jetbrains.mps.vcs.diff.changes.UsedLanguageChange; import jetbrains.mps.internal.collections.runtime.Sequence; import java.util.ArrayList; import jetbrains.mps.internal.collections.runtime.IVisitor; import org.jetbrains.mps.openapi.language.SContainmentLink; public class MergeConflictsBuilder { private SModel myBaseModel; private SModel myMyModel; private SModel myRepositoryModel; /*package*/ ChangeSet myMineChangeSet; /*package*/ ChangeSet myRepositoryChangeSet; /*package*/ Map<ModelChange, List<ModelChange>> myConflictingChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>()); /*package*/ Map<ModelChange, List<ModelChange>> mySymmetricChanges = MapSequence.fromMap(new HashMap<ModelChange, List<ModelChange>>()); public MergeConflictsBuilder(SModel base, SModel mine, SModel repository) { // should be invoked from read action myBaseModel = base; myMyModel = mine; myRepositoryModel = repository; myMineChangeSet = ChangeSetBuilder.buildChangeSet(base, mine); myRepositoryChangeSet = ChangeSetBuilder.buildChangeSet(base, repository); collectConflicts(); } private void addPossibleConflict(ModelChange a, ModelChange b) { if (a.isNonConflicting() || b.isNonConflicting()) { addSymmetric(a, b); } else { addConflict(a, b); } } private void addConflict(ModelChange a, ModelChange b) { addChangeLink(myConflictingChanges, a, b); } private void addSymmetric(ModelChange a, ModelChange b) { addChangeLink(mySymmetricChanges, a, b); } private <K, C extends ModelChange> Tuples._2<Map<K, C>, Map<K, C>> arrangeChanges(_FunctionTypes._return_P1_E0<? extends K, ? super C> changeToKey, Class<C> changeClass) { return MultiTuple.<Map<K, C>,Map<K, C>>from(MergeConflictsBuilder.<K,C>arrangeChanges(myMineChangeSet, changeToKey, changeClass), MergeConflictsBuilder.<K,C>arrangeChanges(myRepositoryChangeSet, changeToKey, changeClass)); } private <K, C extends ModelChange> void collectSymmetricChanges(Map<K, C> mine, Map<K, C> repo) { for (K key : SetSequence.fromSet(MapSequence.fromMap(mine).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(repo).keySet()))) { addSymmetric(MapSequence.fromMap(mine).get(key), MapSequence.fromMap(repo).get(key)); } } private <K, C extends ModelChange> void collectSymmetricChanges(Tuples._2<Map<K, C>, Map<K, C>> mineAndRepo) { collectSymmetricChanges(mineAndRepo._0(), mineAndRepo._1()); } private <K, C extends ModelChange> void collectSymmetricChanges(_FunctionTypes._return_P1_E0<? extends K, ? super C> changeToKey, Class<C> changeClass) { collectSymmetricChanges(arrangeChanges(changeToKey, changeClass)); } private void collectGroupChangesWithOthersConflicts(Map<Tuples._2<SNodeId, String>, List<NodeGroupChange>> arrangedChanges, ChangeSet thisChangeSet, ChangeSet otherChangeSet) { Map<SNodeId, DeleteRootChange> deleteRootChanges = MergeConflictsBuilder.<SNodeId,DeleteRootChange>arrangeChanges(thisChangeSet, new _FunctionTypes._return_P1_E0<SNodeId, DeleteRootChange>() { public SNodeId invoke(DeleteRootChange drc) { return drc.getRootId(); } }, DeleteRootChange.class); for (ModelChange change : ListSequence.fromList(otherChangeSet.getModelChanges())) { if (MapSequence.fromMap(myConflictingChanges).containsKey(change)) { continue; } SNodeId nodeId = null; if (change instanceof NodeChange) { nodeId = ((NodeChange) change).getAffectedNodeId(); } else if (change instanceof NodeGroupChange) { nodeId = ((NodeGroupChange) change).getParentNodeId(); } if (nodeId == null) { continue; } SNode node = myBaseModel.getNode(nodeId); while (node != null) { if (SNodeOperations.getParent(node) == null) { DeleteRootChange conflicting = MapSequence.fromMap(deleteRootChanges).get(node.getNodeId()); if (conflicting != null) { addPossibleConflict(change, conflicting); } } else { Tuples._2<SNodeId, String> nodeRole = MultiTuple.<SNodeId,String>from(SNodeOperations.getParent(node).getNodeId(), check_thl6ft_b0a0a0a0g0b0p(SNodeOperations.getContainingLink(node))); final int index = SNodeOperations.getIndexInParent(node); NodeGroupChange conflicting = ListSequence.fromList(MapSequence.fromMap(arrangedChanges).get(nodeRole)).findFirst(new IWhereFilter<NodeGroupChange>() { public boolean accept(NodeGroupChange ch) { return ch.getBegin() <= index && index < ch.getEnd(); } }); if (conflicting != null) { addPossibleConflict(change, conflicting); break; } } node = SNodeOperations.getParent(node); } } } private void collectPropertyConflicts() { Tuples._2<Map<Tuples._2<SNodeId, String>, SetPropertyChange>, Map<Tuples._2<SNodeId, String>, SetPropertyChange>> arranged; arranged = this.<Tuples._2<SNodeId, String>,SetPropertyChange>arrangeChanges(new _FunctionTypes._return_P1_E0<Tuples._2<SNodeId, String>, SetPropertyChange>() { public Tuples._2<SNodeId, String> invoke(SetPropertyChange spc) { return MultiTuple.<SNodeId,String>from(spc.getAffectedNodeId(), spc.getPropertyName()); } }, SetPropertyChange.class); for (Tuples._2<SNodeId, String> nodeName : SetSequence.fromSet(MapSequence.fromMap(arranged._0()).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(arranged._1()).keySet()))) { SetPropertyChange mineChange = MapSequence.fromMap(arranged._0()).get(nodeName); SetPropertyChange repositoryChange = MapSequence.fromMap(arranged._1()).get(nodeName); if (EqualUtil.equals(mineChange.getNewValue(), repositoryChange.getNewValue())) { addSymmetric(mineChange, repositoryChange); } else { addPossibleConflict(mineChange, repositoryChange); } } } private void collectReferenceConflicts() { Tuples._2<Map<Tuples._2<SNodeId, String>, SetReferenceChange>, Map<Tuples._2<SNodeId, String>, SetReferenceChange>> arranged; arranged = this.<Tuples._2<SNodeId, String>,SetReferenceChange>arrangeChanges(new _FunctionTypes._return_P1_E0<Tuples._2<SNodeId, String>, SetReferenceChange>() { public Tuples._2<SNodeId, String> invoke(SetReferenceChange src) { return MultiTuple.<SNodeId,String>from(src.getAffectedNodeId(), src.getRole()); } }, SetReferenceChange.class); for (Tuples._2<SNodeId, String> nodeName : SetSequence.fromSet(MapSequence.fromMap(arranged._0()).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(arranged._1()).keySet()))) { SetReferenceChange mineChange = MapSequence.fromMap(arranged._0()).get(nodeName); SetReferenceChange repositoryChange = MapSequence.fromMap(arranged._1()).get(nodeName); if (EqualUtil.equals(mineChange.getTargetNodeId(), repositoryChange.getTargetNodeId()) && EqualUtil.equals(mineChange.getTargetModelReference(), repositoryChange.getTargetModelReference()) && EqualUtil.equals(mineChange.getResolveInfo(), repositoryChange.getResolveInfo())) { addSymmetric(mineChange, repositoryChange); } else { addPossibleConflict(mineChange, repositoryChange); } } } private void collectSymmetricRootDeletes() { collectSymmetricChanges(new _FunctionTypes._return_P1_E0<SNodeId, DeleteRootChange>() { public SNodeId invoke(DeleteRootChange drc) { return drc.getRootId(); } }, DeleteRootChange.class); } private void collectConflictingRootAdds() { Tuples._2<Map<SNodeId, AddRootChange>, Map<SNodeId, AddRootChange>> arranged; arranged = this.<SNodeId,AddRootChange>arrangeChanges(new _FunctionTypes._return_P1_E0<SNodeId, AddRootChange>() { public SNodeId invoke(AddRootChange drc) { return drc.getRootId(); } }, AddRootChange.class); for (SNodeId addedRoot : SetSequence.fromSet(MapSequence.fromMap(arranged._0()).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(arranged._1()).keySet()))) { AddRootChange mine = MapSequence.fromMap(arranged._0()).get(addedRoot); AddRootChange repository = MapSequence.fromMap(arranged._1()).get(addedRoot); if (SNodeCompare.nodeEquals(myMyModel.getNode(mine.getRootId()), myRepositoryModel.getNode(repository.getRootId()))) { addSymmetric(mine, repository); } else { addPossibleConflict(mine, repository); } } } private boolean nodeGroupChangesSymmetric(NodeGroupChange mine, NodeGroupChange repository) { if (mine.getBegin() == repository.getBegin() && mine.getEnd() == repository.getEnd()) { if (mine.getResultEnd() - mine.getResultBegin() == repository.getResultEnd() - repository.getResultBegin()) { List<? extends SNode> myChildren = IterableUtil.asList(myMyModel.getNode(mine.getParentNodeId()).getChildren(mine.getRole())); List<? extends SNode> repositoryChildren = IterableUtil.asList(myRepositoryModel.getNode(repository.getParentNodeId()).getChildren(repository.getRole())); for (int o = 0; o < mine.getResultEnd() - mine.getResultBegin(); o++) { if (!(SNodeCompare.nodeEquals(myChildren.get(mine.getResultBegin() + o), repositoryChildren.get(repository.getResultBegin() + o)))) { return false; } } return true; } } return false; } private void collectSymmetricImportedModelChanges() { collectSymmetricChanges(new _FunctionTypes._return_P1_E0<SModelReference, ImportedModelChange>() { public SModelReference invoke(ImportedModelChange imc) { return imc.getModelReference(); } }, ImportedModelChange.class); } private void collectSymmetricModuleDependencyChanges() { collectSymmetricChanges(new _FunctionTypes._return_P1_E0<Tuples._2<SModuleReference, ModuleDependencyChange.DependencyType>, ModuleDependencyChange>() { public Tuples._2<SModuleReference, ModuleDependencyChange.DependencyType> invoke(ModuleDependencyChange mdc) { return MultiTuple.<SModuleReference,ModuleDependencyChange.DependencyType>from(mdc.getModuleReference(), mdc.getDependencyType()); } }, ModuleDependencyChange.class); } private void collectConflicts() { Map<Tuples._2<SNodeId, String>, List<NodeGroupChange>> mineGroupChanges = arrangeNodeGroupChanges(myMineChangeSet); Map<Tuples._2<SNodeId, String>, List<NodeGroupChange>> repositoryGroupChanges = arrangeNodeGroupChanges(myRepositoryChangeSet); for (Tuples._2<SNodeId, String> nodeRole : SetSequence.fromSet(MapSequence.fromMap(mineGroupChanges).keySet()).intersect(SetSequence.fromSet(MapSequence.fromMap(repositoryGroupChanges).keySet()))) { List<NodeGroupChange> mine = MapSequence.fromMap(mineGroupChanges).get(nodeRole); List<NodeGroupChange> repository = MapSequence.fromMap(repositoryGroupChanges).get(nodeRole); // This is a quadratic algorithm, it can be optimized to linear, // but it is left for simplicity for (NodeGroupChange m : ListSequence.fromList(mine)) { for (NodeGroupChange r : ListSequence.fromList(repository)) { if (m.getEnd() < r.getBegin() || m.getBegin() > r.getEnd()) { // ok } else { if (nodeGroupChangesSymmetric(m, r)) { addSymmetric(m, r); } else { addPossibleConflict(m, r); } } } } } collectGroupChangesWithOthersConflicts(mineGroupChanges, myMineChangeSet, myRepositoryChangeSet); collectGroupChangesWithOthersConflicts(repositoryGroupChanges, myRepositoryChangeSet, myMineChangeSet); collectPropertyConflicts(); collectReferenceConflicts(); collectSymmetricRootDeletes(); collectConflictingRootAdds(); collectSymmetricImportedModelChanges(); collectSymmetricChanges(new _FunctionTypes._return_P1_E0<SLanguage, UsedLanguageChange>() { public SLanguage invoke(UsedLanguageChange udc) { return udc.getLanguage(); } }, UsedLanguageChange.class); collectSymmetricModuleDependencyChanges(); } private static Map<Tuples._2<SNodeId, String>, List<NodeGroupChange>> arrangeNodeGroupChanges(ChangeSet changeSet) { Map<Tuples._2<SNodeId, String>, List<NodeGroupChange>> nodeRoleToGroupChanges = MapSequence.fromMap(new HashMap<Tuples._2<SNodeId, String>, List<NodeGroupChange>>()); for (NodeGroupChange change : Sequence.fromIterable(changeSet.getModelChanges(NodeGroupChange.class))) { Tuples._2<SNodeId, String> nodeRole = MultiTuple.<SNodeId,String>from(change.getParentNodeId(), change.getRole()); if (!(MapSequence.fromMap(nodeRoleToGroupChanges).containsKey(nodeRole))) { MapSequence.fromMap(nodeRoleToGroupChanges).put(nodeRole, ListSequence.fromList(new ArrayList<NodeGroupChange>())); } ListSequence.fromList(MapSequence.fromMap(nodeRoleToGroupChanges).get(nodeRole)).addElement(change); } return nodeRoleToGroupChanges; } private static void addOneWayChangeLink(Map<ModelChange, List<ModelChange>> map, ModelChange a, ModelChange b) { if (MapSequence.fromMap(map).get(a) == null) { MapSequence.fromMap(map).put(a, ListSequence.fromList(new ArrayList<ModelChange>())); } ListSequence.fromList(MapSequence.fromMap(map).get(a)).addElement(b); } private static void addChangeLink(Map<ModelChange, List<ModelChange>> map, ModelChange a, ModelChange b) { assert a.getChangeSet() != b.getChangeSet(); addOneWayChangeLink(map, a, b); addOneWayChangeLink(map, b, a); } private static <K, C extends ModelChange> Map<K, C> arrangeChanges(ChangeSet changeSet, final _FunctionTypes._return_P1_E0<? extends K, ? super C> changeToKey, Class<C> changeClass) { final Map<K, C> map = MapSequence.fromMap(new HashMap<K, C>()); Sequence.fromIterable(changeSet.getModelChanges(changeClass)).visitAll(new IVisitor<C>() { public void visit(C ch) { MapSequence.fromMap(map).put(changeToKey.invoke(ch), ch); } }); return map; } private static String check_thl6ft_b0a0a0a0g0b0p(SContainmentLink checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getName(); } return null; } }