/*
* Copyright 2003-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.smodel;
import jetbrains.mps.RuntimeFlags;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import org.jetbrains.mps.openapi.model.SReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public final class CopyUtil {
private CopyUtil() {
}
public static void copyModelContent(SModel from, SModel to) {
for (SNode root : from.getRootNodes()) {
to.addRootNode(copy(root));
}
}
public static void copyModelContentAndPreserveIds(SModel from, SModel to) {
for (SNode root : from.getRootNodes()) {
to.addRootNode(copyAndPreserveId(root, true));
}
}
private static void copyModelContentAndPreserveIds(jetbrains.mps.smodel.SModel from, jetbrains.mps.smodel.SModel to) {
for (SNode root : from.getRootNodes()) {
to.addRootNode(copyAndPreserveId(root, true));
}
}
public static void copyModelProperties(jetbrains.mps.smodel.SModel from, jetbrains.mps.smodel.SModel to) {
from.copyPropertiesTo(to);
}
public static jetbrains.mps.smodel.SModel copyModel(jetbrains.mps.smodel.SModel model) {
jetbrains.mps.smodel.SModel copy = model.createEmptyCopy();
copyModelContentAndPreserveIds(model, copy);
copyModelProperties(model, copy);
return copy;
}
public static List<SNode> copy(List<SNode> nodes) {
return copy(nodes, new HashMap<SNode, SNode>());
}
public static List<SNode> copy(List<SNode> nodes, Map<SNode, SNode> mapping) {
List<SNode> result = clone(nodes, mapping);
for (SNode node : nodes) {
addReferences(node, mapping, false);
}
return result;
}
public static SNode copy(SNode node) {
return copy(node, new HashMap<SNode, SNode>(), true);
}
public static SNode copyAndPreserveId(SNode node) {
return copyAndPreserveId(node, true);
}
public static SNode copyAndPreserveId(SNode node, boolean cloneRefs) {
HashMap<SNode, SNode> mapping = new HashMap<SNode, SNode>();
SNode result = clone(node, mapping, true);
for (SNode sourceNode : mapping.keySet()) {
((jetbrains.mps.smodel.SNode) mapping.get(sourceNode)).setId(sourceNode.getNodeId());
}
addReferences(node, mapping, cloneRefs);
return result;
}
public static SNode copy(SNode node, boolean copyAttributes) {
return copy(node, new HashMap<SNode, SNode>(), copyAttributes);
}
public static SNode copy(SNode node, Map<SNode, SNode> mapping, boolean copyAttributes) {
SNode result = clone(node, mapping, copyAttributes);
addReferences(node, mapping, false);
return result;
}
public static List<SNode> copyAndPreserveId(List<SNode> nodes, Map<SNode, SNode> mapping) {
List<SNode> result = clone(nodes, mapping);
for (SNode sourceNode : mapping.keySet()) {
((jetbrains.mps.smodel.SNode) mapping.get(sourceNode)).setId(sourceNode.getNodeId());
}
for (SNode node : nodes) {
addReferences(node, mapping, false);
}
return result;
}
private static SNode clone(SNode node, Map<SNode, SNode> mapping, boolean copyAttributes) {
if (node == null) return null;
jetbrains.mps.smodel.SNode result = new jetbrains.mps.smodel.SNode(node.getConcept());
mapping.put(node, result);
copyProperties(node, result);
copyUserObjects(node, result);
for (SNode child : node.getChildren()) {
if (!copyAttributes && AttributeOperations.isAttribute(child)) continue;
SContainmentLink role = child.getContainmentLink();
assert role != null;
result.addChild(role, clone(child, mapping, copyAttributes));
}
return result;
}
private static List<SNode> clone(List<? extends SNode> nodes, Map<SNode, SNode> mapping) {
List<SNode> results = new ArrayList<SNode>();
for (SNode node : nodes) {
results.add(clone(node, mapping, true));
}
return results;
}
public static void copyProperties(SNode from, SNode to) {
for (SProperty p : from.getProperties()) {
to.setProperty(p, from.getProperty(p));
}
}
public static void copyUserObjects(SNode from, final SNode to) {
for (Object key : from.getUserObjectKeys()) {
to.putUserObject(key, from.getUserObject(key));
}
}
public static void addReferences(SNode root, Map<SNode, SNode> mapping, boolean forceCloneRefs) {
if (root == null) return;
Iterable<SNode> thisAndDesc = SNodeUtil.getDescendants(root);
for (SNode inputNode : thisAndDesc) {
SNode outputNode = mapping.get(inputNode);
if (outputNode == null) continue;
for (SReference ref : inputNode.getReferences()) {
boolean cloneRefs = forceCloneRefs || RuntimeFlags.isMergeDriverMode();
SNode inputTargetNode = cloneRefs ? null : jetbrains.mps.util.SNodeOperations.getTargetNodeSilently(ref);
if (inputTargetNode == null) { //broken reference or need to clone
if (ref instanceof StaticReference) {
StaticReference statRef = (StaticReference) ref;
SReference reference = new StaticReference(
statRef.getLink(),
outputNode,
statRef.getTargetSModelReference(),
statRef.getTargetNodeId(),
statRef.getResolveInfo());
outputNode.setReference(reference.getLink(), reference);
} else if (ref instanceof DynamicReference && cloneRefs) {
DynamicReference dynRef = (DynamicReference) ref;
DynamicReference output = new DynamicReference(dynRef.getLink(), outputNode, dynRef.getTargetSModelReference(), dynRef.getResolveInfo());
output.setOrigin(dynRef.getOrigin());
outputNode.setReference(output.getLink(), output);
}
} else if (mapping.containsKey(inputTargetNode)) {
outputNode.setReference(ref.getLink(), jetbrains.mps.smodel.SReference.create(ref.getLink(), outputNode, mapping.get(inputTargetNode)));
} else {
outputNode.setReference(ref.getLink(), jetbrains.mps.smodel.SReference.create(ref.getLink(), outputNode, inputTargetNode));
}
}
}
}
}