package jetbrains.mps.ide.datatransfer;
/*Generated by MPS */
import org.apache.log4j.Logger;
import org.apache.log4j.LogManager;
import java.util.Set;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.mps.openapi.language.SLanguage;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.datatransfer.PasteNodeData;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import jetbrains.mps.datatransfer.DataTransferManager;
import jetbrains.mps.internal.collections.runtime.ISelector;
import java.util.ArrayList;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.mps.openapi.language.SProperty;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.AttributeOperations;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import jetbrains.mps.smodel.StaticReference;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import com.intellij.ide.CopyPasteManagerEx;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.awt.datatransfer.DataFlavor;
import org.apache.log4j.Level;
import java.util.Collection;
import jetbrains.mps.project.Project;
import jetbrains.mps.smodel.SModelOperations;
import jetbrains.mps.smodel.language.LanguageRegistry;
import jetbrains.mps.smodel.SLanguageHierarchy;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.smodel.SModelInternal;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.project.AbstractModule;
public final class CopyPasteUtil {
private static final Logger LOG = LogManager.getLogger(CopyPasteUtil.class);
public CopyPasteUtil() {
}
private static void processImportsAndLanguages(Set<SModelReference> necessaryImports, Set<SLanguage> necessaryLanguages, Map<SNode, SNode> sourceNodesToNewNodes, Set<SReference> allReferences) {
necessaryImports.clear();
necessaryLanguages.clear();
Set<SNode> sourceNodes = sourceNodesToNewNodes.keySet();
for (SNode node : sourceNodes) {
necessaryLanguages.add(node.getConcept().getLanguage());
}
for (SReference ref : allReferences) {
if (sourceNodesToNewNodes.get(ref.getTargetNode()) == null) {
SModelReference targetModelReference = ref.getTargetSModelReference();
if (targetModelReference != null) {
necessaryImports.add(targetModelReference);
}
}
}
}
public static PasteNodeData createNodeDataIn(List<SNode> sourceNodes, Map<SNode, Set<SNode>> sourceNodesAndAttributes) {
if (ListSequence.fromList(sourceNodes).isEmpty()) {
return PasteNodeData.emptyPasteNodeData(null);
}
SModel model = ListSequence.fromList(sourceNodes).first().getModel();
final Map<SNode, SNode> sourceNodesToNewNodes = MapSequence.fromMap(new HashMap<SNode, SNode>());
Set<SReference> allReferences = SetSequence.fromSet(new HashSet<SReference>());
for (SNode sourceNode : ListSequence.fromList(sourceNodes)) {
assert sourceNode.getModel() == model;
CopyPasteUtil.copyNode_internal(sourceNode, sourceNodesAndAttributes, sourceNodesToNewNodes, allReferences);
}
Set<SModelReference> necessaryModels = SetSequence.fromSet(new HashSet<SModelReference>());
Set<SLanguage> necessaryLanguages = SetSequence.fromSet(new HashSet<SLanguage>());
CopyPasteUtil.processImportsAndLanguages(necessaryModels, necessaryLanguages, sourceNodesToNewNodes, allReferences);
CopyPasteUtil.processReferencesIn(sourceNodesToNewNodes, allReferences);
for (SNode source : ListSequence.fromList(sourceNodes)) {
DataTransferManager.getInstance().preProcessNode(MapSequence.fromMap(sourceNodesToNewNodes).get(source), source);
}
return new PasteNodeData(ListSequence.fromList(sourceNodes).select(new ISelector<SNode, SNode>() {
public SNode select(SNode it) {
return MapSequence.fromMap(sourceNodesToNewNodes).get(it);
}
}).toListSequence(), null, check_lwiaog_c0a01a2(model), necessaryLanguages, necessaryModels);
}
public static PasteNodeData createNodeDataOut(List<SNode> sourceNodes, SModelReference sourceModel, Set<SLanguage> necessaryLanguages, Set<SModelReference> necessaryModels) {
if (sourceNodes.isEmpty()) {
return PasteNodeData.emptyPasteNodeData(null);
}
List<SNode> result = new ArrayList<SNode>();
Map<SNode, SNode> sourceNodesToNewNodes = new HashMap<SNode, SNode>();
Set<SReference> allReferences = new HashSet<SReference>();
SModel originalModel = sourceNodes.get(0).getModel();
for (SNode sourceNode : sourceNodes) {
assert sourceNode.getModel() == originalModel;
SNode nodeToPaste = CopyPasteUtil.copyNode_internal(sourceNode, null, sourceNodesToNewNodes, allReferences);
result.add(nodeToPaste);
}
Set<SReference> referencesRequireResolve = CopyPasteUtil.processReferencesOut(sourceNodesToNewNodes, allReferences);
return new PasteNodeData(result, referencesRequireResolve, sourceModel, necessaryLanguages, necessaryModels);
}
private static SNode copyNode_internal(SNode sourceNode, @Nullable Map<SNode, Set<SNode>> nodesAndAttributes, Map<SNode, SNode> sourceNodesToNewNodes, Set<SReference> allReferences) {
SNode targetNode = new jetbrains.mps.smodel.SNode(sourceNode.getConcept(), sourceNode.getNodeId());
for (SProperty name : Sequence.fromIterable(sourceNode.getProperties())) {
targetNode.setProperty(name, sourceNode.getProperty(name));
}
sourceNodesToNewNodes.put(sourceNode, targetNode);
for (SReference reference : sourceNode.getReferences()) {
allReferences.add(reference);
}
for (SNode sourceChild : sourceNode.getChildren()) {
if (nodesAndAttributes != null) {
if (AttributeOperations.isAttribute(sourceChild)) {
Set<SNode> nodes = nodesAndAttributes.get(sourceNode);
if (nodes != null && !(nodes.contains(sourceChild))) {
continue;
}
}
}
SNode targetChild = CopyPasteUtil.copyNode_internal(sourceChild, nodesAndAttributes, sourceNodesToNewNodes, allReferences);
SContainmentLink role = sourceChild.getContainmentLink();
assert role != null;
targetNode.addChild(role, targetChild);
}
return targetNode;
}
private static void processReferencesIn(Map<SNode, SNode> sourceNodesToNewNodes, Set<SReference> allReferences) {
for (SReference sourceReference : allReferences) {
SNode oldSourceNode = sourceReference.getSourceNode();
SNode newSourceNode = sourceNodesToNewNodes.get(oldSourceNode);
SNode oldTargetNode = sourceReference.getTargetNode();
SNode newTargetNode = sourceNodesToNewNodes.get(oldTargetNode);
SReference newReference;
if (newTargetNode != null) {
newReference = jetbrains.mps.smodel.SReference.create(sourceReference.getLink(), newSourceNode, newTargetNode);
} else {
if (oldTargetNode != null) {
// model can be null in case it's generation process and the target node was removed due to in-place transformation
// see MPS-24188, this may be fixed when MPS-23902 is fixed
SModel model = oldTargetNode.getModel();
newReference = jetbrains.mps.smodel.SReference.create(sourceReference.getLink(), newSourceNode, (model == null ? null : model.getReference()), oldTargetNode.getNodeId());
} else
if (((jetbrains.mps.smodel.SReference) sourceReference).getResolveInfo() != null) {
newReference = new StaticReference(sourceReference.getLink(), newSourceNode, null, null, ((jetbrains.mps.smodel.SReference) sourceReference).getResolveInfo());
} else {
continue;
}
}
newSourceNode.setReference(newReference.getLink(), newReference);
}
}
private static Set<SReference> processReferencesOut(Map<SNode, SNode> sourceNodesToNewNodes, Set<SReference> allReferences) {
Set<SReference> referencesRequireResolve = new HashSet<SReference>();
for (SReference sourceReference : allReferences) {
SNode oldSourceNode = sourceReference.getSourceNode();
SNode newSourceNode = sourceNodesToNewNodes.get(oldSourceNode);
// XXX sourceReference.getTargetNodeReference would suffice, with a bit of refactoring
SNode oldTargetNode = sourceReference.getTargetNode();
SNode newTargetNode = sourceNodesToNewNodes.get(oldTargetNode);
SReference newReference;
if (newTargetNode != null) {
newReference = jetbrains.mps.smodel.SReference.create(sourceReference.getLink(), newSourceNode, newTargetNode);
} else {
// XXX special hack for BL, oh, really?
if ((SNodeOperations.isInstanceOf(newSourceNode, MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x11857355952L, "jetbrains.mps.baseLanguage.structure.IMethodCall")) || SNodeOperations.isInstanceOf(newSourceNode, MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x101de48bf9eL, "jetbrains.mps.baseLanguage.structure.ClassifierType"))) && oldTargetNode != null) {
newReference = jetbrains.mps.smodel.SReference.create(sourceReference.getLink(), newSourceNode, oldTargetNode);
} else {
// XXX the code below is quite suspicious and deserves a refactoring. It seems the point here is to keep resolveInfo of original link, otherwise
// SReference.create(newSource, oldTarget) would suffice. Is it our true intention, and is it the smart way to do? If it's common scenario,
// why don't we expose it as a distinct #create factory method?
String resolveInfo = (oldTargetNode == null ? ((jetbrains.mps.smodel.SReference) sourceReference).getResolveInfo() : oldTargetNode.getName());
if (resolveInfo != null) {
if (oldTargetNode != null) {
newReference = new StaticReference(sourceReference.getLink(), newSourceNode, oldTargetNode.getReference().getModelReference(), oldTargetNode.getNodeId(), resolveInfo);
} else {
newReference = new StaticReference(sourceReference.getLink(), newSourceNode, null, null, resolveInfo);
}
referencesRequireResolve.add(newReference);
} else {
if (oldTargetNode != null) {
newReference = jetbrains.mps.smodel.SReference.create(sourceReference.getLink(), newSourceNode, oldTargetNode);
} else {
continue;
}
}
}
}
newSourceNode.setReference(newReference.getLink(), newReference);
}
return referencesRequireResolve;
}
public static void copyTextToClipboard(String text) {
CopyPasteManagerEx.getInstanceEx().setContents(new StringSelection(text));
}
public static void copyTextAndNodeToClipboard(String text, SNode node) {
setClipboardContents(new SNodeTransferable(text, node));
}
public static void copyNodesAndTextToClipboard(List<SNode> nodes, Map<SNode, Set<SNode>> nodesAndAttributes, String text) {
setClipboardContents(new SNodeTransferable(nodes, text, nodesAndAttributes));
}
/**
* A workaround for the following problem with CopyPasteManagerEx:
*
* if stringContent of one of existing Transferable instances stored inside CopyPasteManagerEx.myDatas
* collection is equals to the stringContent of Transferable we are trying to "push" there (used as a parameter
* of this method) then existing element will "float up" inside CopyPasteManagerEx.myDatas collection and will
* be used next on next paste operation instead of passed Transferable.
*
* In case of MPS precondition that string equality of clipboard ontent meant actual equality of passed Trabsferables
* (SNodeTransferables) is generally wrong, so we have to work around this logic by deleting all exiting Transferables
* to avoid possible collisions between copied elements preventing user from copying actual node under mouse in editor.
*/
private static void setClipboardContents(Transferable content) {
try {
String stringContent = getStringContent(content);
if (stringContent != null) {
for (Transferable existingContent : CopyPasteManagerEx.getInstanceEx().getAllContents()) {
if (stringContent.equals(getStringContent(existingContent))) {
CopyPasteManagerEx.getInstanceEx().removeContent(existingContent);
}
}
}
} catch (UnsupportedFlavorException e) {
} catch (IOException ex) {
}
CopyPasteManagerEx.getInstanceEx().setContents(content);
}
private static String getStringContent(Transferable content) throws UnsupportedFlavorException, IOException {
return (String) content.getTransferData(DataFlavor.stringFlavor);
}
public static void copyNodesToClipboard(List<SNode> nodes) {
StringBuilder stringBuilder = new StringBuilder();
int i = 1;
int size = nodes.size();
for (SNode node : nodes) {
stringBuilder.append(jetbrains.mps.util.SNodeOperations.getDebugText(node));
if (i < size) {
stringBuilder.append("\n");
}
i++;
}
setClipboardContents(new SNodeTransferable(nodes, stringBuilder.toString()));
}
public static void copyNodeToClipboard(SNode node) {
List<SNode> list = new ArrayList<SNode>();
list.add(node);
CopyPasteUtil.copyNodesToClipboard(list);
}
public static List<SNode> getNodesFromClipboard(SModel model) {
return CopyPasteUtil.getPasteNodeDataFromClipboard(model).getNodes();
}
public static PasteNodeData getPasteNodeDataFromClipboard(SModel model) {
Transferable content = null;
for (Transferable trf : CopyPasteManagerEx.getInstanceEx().getAllContents()) {
if (trf != null && trf.isDataFlavorSupported(SModelDataFlavor.sNode)) {
content = trf;
}
break;
}
if (content == null) {
return PasteNodeData.emptyPasteNodeData(model.getReference());
}
if (content.isDataFlavorSupported(SModelDataFlavor.sNode)) {
SNodeTransferable nodeTransferable;
try {
nodeTransferable = (SNodeTransferable) content.getTransferData(SModelDataFlavor.sNode);
return nodeTransferable.createNodeData();
} catch (UnsupportedFlavorException e) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Exception", e);
}
} catch (IOException e) {
if (LOG.isEnabledFor(Level.ERROR)) {
LOG.error("Exception", e);
}
}
}
return PasteNodeData.emptyPasteNodeData(model.getReference());
}
public static SNode getNodeFromClipboard(SModel model) {
return CopyPasteUtil.getNodesFromClipboard(model).get(0);
}
@Nullable
public static Runnable addImportsWithDialog(final SModel targetModel, final Collection<SLanguage> necessaryLanguages, final Collection<SModelReference> necessaryImports, final Project mpsProject) {
if (targetModel.getModule() == null) {
return null;
}
if (mpsProject == null) {
return null;
}
final List<SLanguage> additionalLanguages = new ArrayList<SLanguage>();
final List<SModelReference> additionalModels = new ArrayList<SModelReference>();
mpsProject.getModelAccess().runReadAction(new Runnable() {
@Override
public void run() {
List<SModelReference> allImportedModels = new ArrayList<SModelReference>();
// XXX in fact, allImportedModels doesn't give us implicit imports, while one in necessaryImports may actually be imported already as implicit
// need better way to deal with implicit imports.
for (SModel sm : SModelOperations.allImportedModels(targetModel)) {
allImportedModels.add(sm.getReference());
}
// no idea why allImportedModels explicitly removes models from its imports
// it's handy for us, though
allImportedModels.add(targetModel.getReference());
for (SModelReference modelReference : necessaryImports) {
assert modelReference != null;
if (!(allImportedModels.contains(modelReference))) {
additionalModels.add(modelReference);
}
}
LanguageRegistry langReg = LanguageRegistry.getInstance(mpsProject.getRepository());
Set<SLanguage> allVisibleLanguages = new SLanguageHierarchy(langReg, SModelOperations.getAllLanguageImports(targetModel)).getExtended();
for (SLanguage lang : necessaryLanguages) {
if (!(allVisibleLanguages.contains(lang))) {
additionalLanguages.add(lang);
}
}
}
});
if (additionalModels.isEmpty() && additionalLanguages.isEmpty()) {
return null;
}
AddRequiredImportsDialog dialog = new AddRequiredImportsDialog(mpsProject, additionalModels.toArray(new SModelReference[additionalModels.size()]), additionalLanguages.toArray(new SLanguage[additionalLanguages.size()]));
dialog.show();
if (dialog.isOK()) {
return addImports(mpsProject, targetModel, dialog.getSelectedLanguages(), dialog.getSelectedImports());
} else {
return null;
}
}
@Nullable
public static Runnable addImportsWithDialog(PasteNodeData pasteNodeData, SModel targetModel, Project mpsProject) {
// shows dialog if necessary and pasted nodes were taken not from the same model
SModelReference oldModel = pasteNodeData.getSourceModel();
// no dialog if copying from the same model
if (oldModel != null && targetModel.getReference().equals(oldModel)) {
return null;
}
return CopyPasteUtil.addImportsWithDialog(targetModel, pasteNodeData.getNecessaryLanguages(), pasteNodeData.getNecessaryModels(), mpsProject);
}
private static Runnable addImports(final Project p, final SModel targetModel, @NotNull final SLanguage[] requiredLanguages, @NotNull final SModelReference[] requiredImports) {
if (requiredLanguages.length == 0 && requiredImports.length == 0) {
return null;
}
return new Runnable() {
@Override
public void run() {
// model properties
for (SModelReference imported : requiredImports) {
((SModelInternal) targetModel).addModelImport(imported, false);
}
for (SLanguage language : requiredLanguages) {
((SModelInternal) targetModel).addLanguage(language);
}
// model's module properties
SModule targetModule = targetModel.getModule();
if (targetModule == null) {
return;
}
for (SModelReference modelRef : requiredImports) {
SModel model = modelRef.resolve(p.getRepository());
if (model == null) {
continue;
}
SModule module = model.getModule();
if (module == null || module == targetModule) {
continue;
}
((AbstractModule) targetModule).addDependency(module.getModuleReference(), false);
}
}
};
}
public static boolean isStringOnTopOfClipboard() {
// This method was created in accordance with TextPasteUtil.hasStringInClipboard()/.getStringFromClipboard()
// methods we should consider reimplementing these methods in order to iterrate over .getAllContents() collection
// in case first available Transferable does not support neither stringFlavor nor sNode one.
for (Transferable trf : CopyPasteManagerEx.getInstanceEx().getAllContents()) {
if (trf != null) {
for (DataFlavor nextFlavor : trf.getTransferDataFlavors()) {
if (nextFlavor == SModelDataFlavor.stringFlavor) {
return true;
}
if (nextFlavor == SModelDataFlavor.sNode) {
return false;
}
}
}
break;
}
return false;
}
private static SModelReference check_lwiaog_c0a01a2(SModel checkedDotOperand) {
if (null != checkedDotOperand) {
return checkedDotOperand.getReference();
}
return null;
}
}