package jetbrains.mps.vcs.diff.changes;
/*Generated by MPS */
import org.jetbrains.mps.openapi.model.SNodeId;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.vcs.diff.ChangeSet;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.annotations.Nullable;
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.AttributeOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.IAttributeDescriptor;
import jetbrains.mps.smodel.SNodePointer;
import jetbrains.mps.util.IterableUtil;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.internal.collections.runtime.IVisitor;
import jetbrains.mps.internal.collections.runtime.IterableUtils;
import jetbrains.mps.internal.collections.runtime.ISelector;
import jetbrains.mps.util.NameUtil;
public class NodeGroupChange extends ModelChange {
private final SNodeId myParentNodeId;
private final SContainmentLink myRole;
private final int myBegin;
private final int myEnd;
private final int myResultBegin;
private final int myResultEnd;
private List<SNodeId> myPreparedIdsToDelete = null;
private SNodeId myPreparedAnchorId = null;
public NodeGroupChange(@NotNull ChangeSet changeSet, @NotNull SNodeId parentNodeId, @NotNull SContainmentLink role, int begin, int end, int resultBegin, int resultEnd) {
super(changeSet);
myParentNodeId = parentNodeId;
myRole = role;
myBegin = begin;
myEnd = end;
myResultBegin = resultBegin;
myResultEnd = resultEnd;
}
@NotNull
public SNodeId getParentNodeId() {
return myParentNodeId;
}
@NotNull
public SNodeId getParentNodeId(boolean isNewModel) {
return myParentNodeId;
}
@NotNull
public String getRole() {
return myRole.getRoleName();
}
@NotNull
public SContainmentLink getRoleLink() {
return myRole;
}
public boolean isAbout(SContainmentLink link) {
return myRole.equals(link);
}
public int getBegin() {
return myBegin;
}
public int getEnd() {
return myEnd;
}
public int getResultEnd() {
return myResultEnd;
}
public int getResultBegin() {
return myResultBegin;
}
private SNodeReference myMergeHint = null;
private boolean myMergeHintLoaded = false;
@Nullable
@Override
public SNodeReference getMergeHint() {
// get "nonconflicting" attribute in metamodel
if (!(myMergeHintLoaded)) {
myMergeHintLoaded = true;
SNode n = getParent(false);
SNode c = SNodeOperations.getConceptDeclaration(n);
SNode linkDecl = SNodeOperations.as(myRole.getDeclarationNode(), MetaAdapterFactory.getConcept(0xc72da2b97cce4447L, 0x8389f407dc1158b7L, 0xf979bd086aL, "jetbrains.mps.lang.structure.structure.LinkDeclaration"));
SNode hint = AttributeOperations.getAttribute(linkDecl, new IAttributeDescriptor.NodeAttribute(MetaAdapterFactory.getConcept(0x37e03aa1728949bcL, 0x826930de5eceec76L, 0x657f08af7deb331aL, "jetbrains.mps.vcs.mergehints.structure.MergeHint")));
if ((hint == null)) {
hint = AttributeOperations.getAttribute(c, new IAttributeDescriptor.NodeAttribute(MetaAdapterFactory.getConcept(0x37e03aa1728949bcL, 0x826930de5eceec76L, 0x657f08af7deb331aL, "jetbrains.mps.vcs.mergehints.structure.MergeHint")));
}
if ((hint != null)) {
myMergeHint = new SNodePointer(hint);
}
}
return myMergeHint;
}
private SNode getParent(boolean isNewModel) {
return ((isNewModel ? getChangeSet().getNewModel() : getChangeSet().getOldModel())).getNode(getParentNodeId(isNewModel));
}
public void prepare() {
if (myPreparedIdsToDelete == null) {
SNode parent = getParent(false);
assert parent != null;
List<? extends SNode> children = IterableUtil.asList(parent.getChildren(myRole));
myPreparedIdsToDelete = ListSequence.fromList(new ArrayList<SNodeId>());
for (int i = myBegin; i < myEnd; i++) {
ListSequence.fromList(myPreparedIdsToDelete).addElement(children.get(i).getNodeId());
}
myPreparedAnchorId = (myBegin == 0 ? null : children.get(myBegin - 1).getNodeId());
}
}
@Override
public void apply(@NotNull final SModel model, @NotNull NodeCopier nodeCopier) {
// delete old nodes
prepare();
ListSequence.fromList(myPreparedIdsToDelete).visitAll(new IVisitor<SNodeId>() {
public void visit(SNodeId id) {
model.getNode(id).delete();
}
});
myPreparedIdsToDelete = null;
// copy nodes to insert
List<SNode> nodesToAdd = ListSequence.fromList(new ArrayList<SNode>());
List<? extends SNode> newChildren = IterableUtil.asList(getParent(true).getChildren(myRole));
for (int i = myResultBegin; i < myResultEnd; i++) {
ListSequence.fromList(nodesToAdd).addElement(nodeCopier.copyNode(newChildren.get(i)));
}
// insert new nodes
SNode anchor = (myPreparedAnchorId == null ? null : model.getNode(myPreparedAnchorId));
SNode parent = model.getNode(myParentNodeId);
if (anchor != null) {
// can't use anchor.next-sibling here as it looks for sibling with respect to node's containment role
// while there are tests (MergeCoreTest) that expect ordering according overall children list
anchor = anchor.getNextSibling();
} else {
// MergeCoreTest expects nodes without anchor to go first
anchor = parent.getFirstChild();
}
for (SNode newNode : ListSequence.fromList(nodesToAdd)) {
parent.insertChildBefore(myRole, newNode, anchor);
}
}
@Nullable
@Override
public SNodeId getRootId() {
return SNodeOperations.getContainingRoot(getParent(false)).getNodeId();
}
@NotNull
@Override
public ChangeType getType() {
if (myBegin == myEnd) {
return ChangeType.ADD;
}
if (myResultBegin == myResultEnd) {
return ChangeType.DELETE;
}
return ChangeType.CHANGE;
}
@Override
public String toString() {
if (myEnd == myBegin) {
return String.format("Insert %s into position #%d in role %s of node %s", nodeRange(myResultBegin, myResultEnd), myBegin, myRole, myParentNodeId);
}
if (myResultEnd == myResultBegin) {
return String.format("Delete %s in role %s of node %s", nodeRange(myBegin, myEnd), myRole, myParentNodeId);
}
return String.format("Replace %s with nodes %s in role %s of node %s", nodeRange(myBegin, myEnd), nodeRange(myResultBegin, myResultEnd), myRole, myParentNodeId);
}
@Override
public String getDescription() {
return getDescription(true);
}
public String getDescription(boolean verbose) {
List<SNode> newChildren = null;
String newIds = null;
if (verbose) {
newChildren = IterableUtil.asList(getParent(true).getChildren(myRole));
newIds = IterableUtils.join(ListSequence.fromList(newChildren).page(myResultBegin, myResultEnd).select(new ISelector<SNode, String>() {
public String select(SNode n) {
return "#" + n.getNodeId();
}
}), ", ");
}
String oldStuff = (myEnd - myBegin == 1 ? getRole() : NameUtil.formatNumericalString(myEnd - myBegin, getRole()));
String newStuff = (myResultEnd - myResultBegin == 1 ? getRole() : NameUtil.formatNumericalString(myResultEnd - myResultBegin, getRole()));
// FIXME get rid of this dirty magic with role names "pluralization". PLEASE!!!
if (eq_yjf6x2_a0a7a82(newStuff, getRole()) && eq_yjf6x2_a0a7a82_0(oldStuff, getRole())) {
newStuff = "another";
} else if (myEnd != myBegin) {
newStuff = "another " + newStuff;
}
if (myEnd == myBegin) {
if (verbose) {
String addedOrInserted = (myResultEnd == ListSequence.fromList(newChildren).count() ? "Added" : "Inserted");
return String.format("%s %s: %s", addedOrInserted, newStuff, newIds);
} else {
return String.format("Added %s", newStuff);
}
}
if (myResultEnd == myResultBegin) {
return String.format("Removed %s", oldStuff);
}
if (verbose) {
return String.format("Replaced %s with %s: %s", oldStuff, newStuff, newIds);
} else {
return String.format("Replaced %s with %s", oldStuff, newStuff);
}
}
@NotNull
@Override
protected ModelChange createOppositeChange() {
return new NodeGroupChange(getChangeSet().getOppositeChangeSet(), myParentNodeId, myRole, myResultBegin, myResultEnd, myBegin, myEnd);
}
private static String nodeRange(int begin, int end) {
return (begin + 1 == end ? String.format("node #%d", begin) : String.format("nodes #%d-%d", begin, end - 1));
}
private static boolean eq_yjf6x2_a0a7a82(Object a, Object b) {
return (a != null ? a.equals(b) : a == b);
}
private static boolean eq_yjf6x2_a0a7a82_0(Object a, Object b) {
return (a != null ? a.equals(b) : a == b);
}
}