package jetbrains.mps.vcs.diff; /*Generated by MPS */ import java.util.List; import jetbrains.mps.vcs.diff.changes.ModelChange; import jetbrains.mps.internal.collections.runtime.ListSequence; import java.util.ArrayList; import java.util.Map; import org.jetbrains.mps.openapi.model.SNodeId; import jetbrains.mps.internal.collections.runtime.MapSequence; import java.util.HashMap; import org.jetbrains.mps.openapi.model.SNode; import org.jetbrains.mps.openapi.language.SProperty; import jetbrains.mps.internal.collections.runtime.Sequence; import jetbrains.mps.vcs.diff.changes.SetPropertyStructChange; import org.jetbrains.mps.openapi.model.SReference; import org.jetbrains.mps.openapi.language.SReferenceLink; import jetbrains.mps.internal.collections.runtime.ISelector; import org.jetbrains.mps.openapi.model.SModelReference; import jetbrains.mps.smodel.DynamicReference; import jetbrains.mps.vcs.diff.changes.SetReferenceStructChange; import org.jetbrains.annotations.NotNull; import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations; import org.jetbrains.mps.openapi.language.SContainmentLink; import jetbrains.mps.util.IterableUtil; import jetbrains.mps.internal.collections.runtime.SetSequence; import jetbrains.mps.util.LongestCommonSubsequenceFinder; import jetbrains.mps.baseLanguage.tuples.runtime.Tuples; import org.jetbrains.mps.openapi.language.SConcept; import jetbrains.mps.vcs.diff.changes.NodeGroupStructChange; import java.util.Iterator; import jetbrains.mps.internal.collections.runtime.IMapping; import jetbrains.mps.internal.collections.runtime.IVisitor; import jetbrains.mps.smodel.PropertySupport; import jetbrains.mps.RuntimeFlags; import jetbrains.mps.util.EqualUtil; public class StructChangeSetBuilder { private StructChangeSetImpl myChangeSet; private List<ModelChange> myNewChanges = ListSequence.fromList(new ArrayList<ModelChange>()); private Map<SNodeId, SNodeId> myOldToNewMap = MapSequence.fromMap(new HashMap<SNodeId, SNodeId>()); private StructChangeSetBuilder(SNode oldNode, SNode newNode) { this(new StructChangeSetImpl(oldNode, newNode)); } private StructChangeSetBuilder(StructChangeSetImpl changeSet) { myChangeSet = changeSet; } private void buildForProperties(SNode oldNode, SNode newNode) { Iterable<SProperty> oldProperties = oldNode.getProperties(); Iterable<SProperty> newProperties = newNode.getProperties(); for (SProperty p : Sequence.fromIterable(oldProperties).union(Sequence.fromIterable(newProperties))) { buildForProperty(oldNode, newNode, p); } } private void buildForProperty(SNode oldNode, SNode newNode, SProperty property) { if (!(equalsProperty(oldNode, newNode, property))) { ListSequence.fromList(myNewChanges).addElement(new SetPropertyStructChange(myChangeSet, oldNode.getNodeId(), newNode.getNodeId(), property, newNode.getProperty(property))); } } private void buildForReferences(Map<SNode, SNode> olToNewMap, SNode oldNode, SNode newNode) { List<SReference> oldReferences = (List<SReference>) oldNode.getReferences(); List<SReference> newReferences = (List<SReference>) newNode.getReferences(); for (SReferenceLink role : ListSequence.fromList(oldReferences).concat(ListSequence.fromList(newReferences)).select(new ISelector<SReference, SReferenceLink>() { public SReferenceLink select(SReference r) { return r.getLink(); } }).distinct()) { buildForReference(olToNewMap, oldNode, newNode, role); } } private void buildForReference(Map<SNode, SNode> oldToNewMap, SNode oldNode, SNode newNode, SReferenceLink role) { SReference oldReference = oldNode.getReference(role); SReference newReference = newNode.getReference(role); if (!(equalsReference(oldReference, newReference, oldToNewMap, false))) { SModelReference targetModel = check_okvhpb_a0a0c0i(newReference); SNodeId targetId = (newReference instanceof DynamicReference ? null : check_okvhpb_a0a1a2a8(newReference)); ListSequence.fromList(myNewChanges).addElement(new SetReferenceStructChange(myChangeSet, oldNode.getNodeId(), newNode.getNodeId(), role, targetModel, targetId, check_okvhpb_g0a0a2a2a8(((jetbrains.mps.smodel.SReference) newReference)))); } } private void buildForNode(Map<SNode, SNode> oldToNewMap, @NotNull SNode oldNode, @NotNull SNode newNode) { // updates oldToNewMap with new mappings if (neq_okvhpb_a0b0j(SNodeOperations.getConcept(oldNode), SNodeOperations.getConcept(newNode))) { // todo: should be whole node change instead of going into details... } MapSequence.fromMap(oldToNewMap).put(oldNode, newNode); buildForProperties(oldNode, newNode); buildForReferences(oldToNewMap, oldNode, newNode); for (SContainmentLink role : ListSequence.fromList(SNodeOperations.getChildren(oldNode)).concat(ListSequence.fromList(SNodeOperations.getChildren(newNode))).select(new ISelector<SNode, SContainmentLink>() { public SContainmentLink select(SNode ch) { return ch.getContainmentLink(); } }).distinct()) { buildForNodeRole(oldToNewMap, oldNode, newNode, role); } } private void buildForNodeRole(Map<SNode, SNode> oldToNewMap, SNode oldNode, SNode newNode, SContainmentLink role) { // updates oldToNewMap with new mappings buildForNodeRole(oldToNewMap, IterableUtil.asList(oldNode.getChildren(role)), IterableUtil.asList(newNode.getChildren(role)), oldNode.getNodeId(), newNode.getNodeId(), role); } private void buildForNodeRole(Map<SNode, SNode> oldToNewMap, List<SNode> oldChildren, List<SNode> newChildren, SNodeId parentId, SNodeId newParentId, SContainmentLink role) { // updates oldToNewMap with new mappings final Map<SNode, Integer> nodeClasses = MapSequence.fromMap(new HashMap<SNode, Integer>()); int i = 1; Map<SNode, SNode> old2NewMap = MapSequence.fromMap(new HashMap<SNode, SNode>()); MapSequence.fromMap(old2NewMap).putAll(oldToNewMap); outer: for (SNode node : ListSequence.fromList(oldChildren).concat(ListSequence.fromList(newChildren))) { for (SNode nodeClass : SetSequence.fromSet(MapSequence.fromMap(nodeClasses).keySet())) { if (equalsNodeStructure(node, nodeClass, old2NewMap, false)) { MapSequence.fromMap(nodeClasses).put(node, MapSequence.fromMap(nodeClasses).get(nodeClass)); continue outer; } } MapSequence.fromMap(nodeClasses).put(node, i++); } List<Integer> oldClasses = ListSequence.fromList(oldChildren).select(new ISelector<SNode, Integer>() { public Integer select(SNode n) { return MapSequence.fromMap(nodeClasses).get(n); } }).toListSequence(); List<Integer> newClasses = ListSequence.fromList(newChildren).select(new ISelector<SNode, Integer>() { public Integer select(SNode n) { return MapSequence.fromMap(nodeClasses).get(n); } }).toListSequence(); LongestCommonSubsequenceFinder<Integer> finder = new LongestCommonSubsequenceFinder<Integer>(oldClasses, newClasses); // add matched Nodes List<Tuples._2<Integer, Integer>> commonIndices = finder.getCommonIndices(); for (Tuples._2<Integer, Integer> ix : ListSequence.fromList(commonIndices)) { addMatchedNodes(oldToNewMap, ListSequence.fromList(oldChildren).getElement((int) ix._0()), ListSequence.fromList(newChildren).getElement((int) ix._1())); } // Finding insertings, deletings and replacings List<Tuples._2<Tuples._2<Integer, Integer>, Tuples._2<Integer, Integer>>> differentIndices = finder.getDifferentIndices(); for (Tuples._2<Tuples._2<Integer, Integer>, Tuples._2<Integer, Integer>> indices : ListSequence.fromList(differentIndices)) { Tuples._2<Integer, Integer> oldIndices = indices._0(); Tuples._2<Integer, Integer> newIndices = indices._1(); List<SConcept> oldC = ListSequence.fromList(oldChildren).page((int) oldIndices._0(), (int) oldIndices._1()).select(new ISelector<SNode, SConcept>() { public SConcept select(SNode n) { return n.getConcept(); } }).toListSequence(); List<SConcept> newC = ListSequence.fromList(newChildren).page((int) newIndices._0(), (int) newIndices._1()).select(new ISelector<SNode, SConcept>() { public SConcept select(SNode n) { return n.getConcept(); } }).toListSequence(); LongestCommonSubsequenceFinder<SConcept> finder2 = new LongestCommonSubsequenceFinder<SConcept>(oldC, newC); // concepts were not matched: for (Tuples._2<Tuples._2<Integer, Integer>, Tuples._2<Integer, Integer>> ixs : ListSequence.fromList(finder2.getDifferentIndices())) { ListSequence.fromList(myNewChanges).addElement(new NodeGroupStructChange(myChangeSet, parentId, newParentId, role, (int) oldIndices._0() + (int) ixs._0()._0(), (int) oldIndices._0() + (int) ixs._0()._1(), (int) newIndices._0() + (int) ixs._1()._0(), (int) newIndices._0() + (int) ixs._1()._1())); } // Finding changes for "matched" children for (Tuples._2<Integer, Integer> ixs : ListSequence.fromList(finder2.getCommonIndices())) { buildForNode(oldToNewMap, ListSequence.fromList(oldChildren).getElement((int) oldIndices._0() + (int) ixs._0()), ListSequence.fromList(newChildren).getElement((int) newIndices._0() + (int) ixs._1())); } } } private void addMatchedNodes(Map<SNode, SNode> oldToNewNodes, SNode oldNode, SNode newNode) { MapSequence.fromMap(oldToNewNodes).put(oldNode, newNode); for (SContainmentLink role : ListSequence.fromList(SNodeOperations.getChildren(oldNode)).concat(ListSequence.fromList(SNodeOperations.getChildren(newNode))).select(new ISelector<SNode, SContainmentLink>() { public SContainmentLink select(SNode ch) { return ch.getContainmentLink(); } }).distinct()) { List<SNode> ch1List = IterableUtil.asList(oldNode.getChildren(role)); List<SNode> ch2List = IterableUtil.asList(newNode.getChildren(role)); { Iterator<SNode> ch1_it = ListSequence.fromList(ch1List).iterator(); Iterator<SNode> ch2_it = ListSequence.fromList(ch2List).iterator(); SNode ch1_var; SNode ch2_var; while (ch1_it.hasNext() && ch2_it.hasNext()) { ch1_var = ch1_it.next(); ch2_var = ch2_it.next(); addMatchedNodes(oldToNewNodes, ch1_var, ch2_var); } } } } private SNode getOldNode() { return myChangeSet.getOldModel().getNode(myChangeSet.getOldNodeId()); } private SNode getNewNode() { return myChangeSet.getNewModel().getNode(myChangeSet.getNewNodeId()); } private void removeMatchedRefChanges() { for (SetReferenceStructChange ch : ListSequence.fromList(myNewChanges).ofType(SetReferenceStructChange.class).toListSequence()) { SReference ref = myChangeSet.getOldModel().getNode(ch.getAffectedNodeId(false)).getReference(ch.getRoleLink()); if (eq_okvhpb_a0a0b0a0q(ref.getTargetSModelReference(), myChangeSet.getOldModel().getReference()) && eq_okvhpb_a0a0b0a0q_0(ch.getTargetModelReference(), myChangeSet.getNewModel().getReference()) && eq_okvhpb_a0a1a0a61(MapSequence.fromMap(myOldToNewMap).get(ref.getTargetNodeId()), ch.getTargetNodeId())) { // ?? should the resolveInfo be the same ?? ListSequence.fromList(myNewChanges).removeElement(ch); } } } private void build(boolean withOpposite) { Map<SNode, SNode> oldToNewMap = MapSequence.fromMap(new HashMap<SNode, SNode>()); buildForNode(oldToNewMap, getOldNode(), getNewNode()); for (IMapping<SNode, SNode> node : MapSequence.fromMap(oldToNewMap)) { MapSequence.fromMap(myOldToNewMap).put(node.key().getNodeId(), node.value().getNodeId()); } removeMatchedRefChanges(); commit(); if (withOpposite) { myChangeSet.buildOppositeChangeSet(); } } private void commit() { ListSequence.fromList(myNewChanges).visitAll(new IVisitor<ModelChange>() { public void visit(ModelChange it) { myChangeSet.add(it); } }); myChangeSet.buildNodeMaps(myOldToNewMap); ListSequence.fromList(myNewChanges).clear(); MapSequence.fromMap(myOldToNewMap).clear(); } public static StructChangeSet buildChangeSet(SNode oldNode, SNode newNode, boolean withOpposite) { StructChangeSetBuilder builder = new StructChangeSetBuilder(oldNode, newNode); builder.build(withOpposite); return builder.myChangeSet; } public static void rebuildChangeSet(StructChangeSet changeSet) { StructChangeSetImpl impl = (StructChangeSetImpl) changeSet; impl.clear(); impl.clearOppositeChangeSet(); StructChangeSetBuilder builder = new StructChangeSetBuilder(impl); builder.build(true); } private static boolean equalsProperty(SNode n1, SNode n2, SProperty property) { PropertySupport propertySupport = new ChangeSetBuilder.DefaultPropertySupport(); if (!(RuntimeFlags.isMergeDriverMode())) { propertySupport = PropertySupport.getPropertySupport(property); } String n1PresentableValue = propertySupport.fromInternalValue(n1.getProperty(property)); String n2PresentableValue = propertySupport.fromInternalValue(n2.getProperty(property)); return EqualUtil.equals(n1PresentableValue, n2PresentableValue); } private static boolean equalsReference(SReference ref1, SReference ref2, final Map<SNode, SNode> oldToNewMap, boolean easy) { SNode target1 = ref1.getTargetNode(); SNode target2 = ref2.getTargetNode(); return target1 == target2 || MapSequence.fromMap(oldToNewMap).get(target1) == target2 || easy && ListSequence.fromList(SNodeOperations.getNodeAncestors(target1, null, true)).select(new ISelector<SNode, SNode>() { public SNode select(SNode it) { return MapSequence.fromMap(oldToNewMap).get(it); } }).intersect(ListSequence.fromList(SNodeOperations.getNodeAncestors(target2, null, true))).isNotEmpty(); } private static boolean equalsNodeStructure(@NotNull SNode n1, @NotNull SNode n2, Map<SNode, SNode> oldToNewMap, boolean easyRef) { // updates oldToNewMap with new mapping if matched if (neq_okvhpb_a0b0bb(SNodeOperations.getConcept(n1), SNodeOperations.getConcept(n2))) { return false; } Map<SNode, SNode> tempMap = MapSequence.fromMap(new HashMap<SNode, SNode>()); MapSequence.fromMap(tempMap).putAll(oldToNewMap); MapSequence.fromMap(tempMap).put(n1, n2); Iterable<SProperty> n1Properties = n1.getProperties(); Iterable<SProperty> n2Properties = n2.getProperties(); for (SProperty prop : Sequence.fromIterable(n1Properties).union(Sequence.fromIterable(n2Properties))) { if (!((equalsProperty(n1, n2, prop)))) { return false; } } List<SReference> n1References = (List<SReference>) n1.getReferences(); List<SReference> n2References = (List<SReference>) n2.getReferences(); for (SReferenceLink role : ListSequence.fromList(n1References).concat(ListSequence.fromList(n2References)).select(new ISelector<SReference, SReferenceLink>() { public SReferenceLink select(SReference r) { return r.getLink(); } }).distinct()) { if (!(equalsReference(n1.getReference(role), n2.getReference(role), tempMap, easyRef))) { return false; } } for (SContainmentLink role : ListSequence.fromList(SNodeOperations.getChildren(n1)).concat(ListSequence.fromList(SNodeOperations.getChildren(n2))).select(new ISelector<SNode, SContainmentLink>() { public SContainmentLink select(SNode ch) { return ch.getContainmentLink(); } }).distinct()) { List<SNode> ch1List = IterableUtil.asList(n1.getChildren(role)); List<SNode> ch2List = IterableUtil.asList(n2.getChildren(role)); if (ListSequence.fromList(ch1List).count() != ListSequence.fromList(ch2List).count()) { return false; } { Iterator<SNode> ch1_it = ListSequence.fromList(ch1List).iterator(); Iterator<SNode> ch2_it = ListSequence.fromList(ch2List).iterator(); SNode ch1_var; SNode ch2_var; while (ch1_it.hasNext() && ch2_it.hasNext()) { ch1_var = ch1_it.next(); ch2_var = ch2_it.next(); if (!(equalsNodeStructure(ch1_var, ch2_var, tempMap, easyRef))) { return false; } } } } MapSequence.fromMap(oldToNewMap).putAll(tempMap); return true; } private static SModelReference check_okvhpb_a0a0c0i(SReference checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getTargetSModelReference(); } return null; } private static SNodeId check_okvhpb_a0a1a2a8(SReference checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getTargetNodeId(); } return null; } private static String check_okvhpb_g0a0a2a2a8(jetbrains.mps.smodel.SReference checkedDotOperand) { if (null != checkedDotOperand) { return checkedDotOperand.getResolveInfo(); } return null; } private static boolean neq_okvhpb_a0b0j(Object a, Object b) { return !(((a != null ? a.equals(b) : a == b))); } private static boolean eq_okvhpb_a0a1a0a61(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } private static boolean eq_okvhpb_a0a0b0a0q(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } private static boolean eq_okvhpb_a0a0b0a0q_0(Object a, Object b) { return (a != null ? a.equals(b) : a == b); } private static boolean neq_okvhpb_a0b0bb(Object a, Object b) { return !(((a != null ? a.equals(b) : a == b))); } }