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