--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.diagram.handler;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Point2D;\r
+import java.util.Collection;\r
+import java.util.EnumSet;\r
+import java.util.HashSet;\r
+import java.util.Set;\r
+\r
+import org.simantics.Simantics;\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.RequestProcessor;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Session;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.request.UniqueRead;\r
+import org.simantics.db.common.request.WriteRequest;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.layer0.util.Layer0Utils;\r
+import org.simantics.diagram.flag.DiagramFlagPreferences;\r
+import org.simantics.diagram.flag.FlagLabelingScheme;\r
+import org.simantics.diagram.flag.FlagUtil;\r
+import org.simantics.diagram.flag.IOTableUtil;\r
+import org.simantics.diagram.flag.IOTablesInfo;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.CopyAdvisor;\r
+import org.simantics.diagram.synchronization.IModifiableSynchronizationContext;\r
+import org.simantics.diagram.synchronization.SynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.AddElement;\r
+import org.simantics.diagram.synchronization.graph.CopyAdvisorUtil;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.diagram.synchronization.graph.GraphSynchronizationHints;\r
+import org.simantics.diagram.synchronization.graph.MoveRouteGraphConnection;\r
+import org.simantics.diagram.synchronization.graph.layer.GraphLayerManager;\r
+import org.simantics.diagram.ui.DiagramModelHints;\r
+import org.simantics.g2d.canvas.ICanvasContext;\r
+import org.simantics.g2d.diagram.DiagramHints;\r
+import org.simantics.g2d.diagram.IDiagram;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.elementclass.FlagClass.Type;\r
+import org.simantics.g2d.elementclass.FlagHandler;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;\r
+import org.simantics.scl.commands.Command;\r
+import org.simantics.scl.commands.Commands;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class CopyPasteUtil {\r
+\r
+ static final EnumSet<ElementType> NODES = EnumSet.of(ElementType.Node);\r
+ static final EnumSet<ElementType> CONNECTIONS = EnumSet.of(ElementType.Connection);\r
+ public static final EnumSet<ElementType> CONNECTION_PARTS = EnumSet.of(ElementType.Edge, ElementType.BranchPoint);\r
+ public static final EnumSet<ElementType> FLAGS = EnumSet.of(ElementType.Flag);\r
+ static final EnumSet<ElementType> MONITORS = EnumSet.of(ElementType.Monitor);\r
+ static final EnumSet<ElementType> OTHERS = EnumSet.of(ElementType.Other);\r
+ static final EnumSet<ElementType> NODES_AND_EDGES = EnumSet.of(ElementType.Node);\r
+ static final EnumSet<ElementType> NOT_FLAGS = EnumSet.complementOf(FLAGS);\r
+\r
+ public static boolean isFlagsOnlySelection(IElementAssortment ea) {\r
+ return !ea.containsAny(NOT_FLAGS)\r
+ && ea.contains(FLAGS)\r
+ && ea.count(ElementType.Flag) > 0;\r
+ }\r
+\r
+ public static boolean onlyFlagsWithoutCorrespondence(RequestProcessor processor, ElementObjectAssortment ea)\r
+ throws DatabaseException {\r
+ return isFlagsOnlySelection(ea)\r
+ && checkFlagsCorrespondences(processor, ea.flags, false);\r
+ }\r
+\r
+ public static boolean onlyFlagsWithoutCorrespondence(ElementAssortment ea) {\r
+ return isFlagsOnlySelection(ea)\r
+ && checkFlagsCorrespondences(ea.flags, false);\r
+ }\r
+\r
+ /**\r
+ * Check that all specified flag elements either have a correspondence or\r
+ * don't. Flag collections with both connected and disconnected flags will\r
+ * always return false;\r
+ * \r
+ * @param flags\r
+ * @param expectedValue\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static boolean checkFlagsCorrespondences(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {\r
+ return processor.syncRequest(new UniqueRead<Boolean>() {\r
+ @Override\r
+ public Boolean perform(ReadGraph graph) throws DatabaseException {\r
+ return checkFlagsCorrespondences(graph, flags, expectedValue);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * Check that all specified flag elements either have a correspondence or\r
+ * don't. Flag collections with both connected and disconnected flags will\r
+ * always return false;\r
+ * \r
+ * @param flags\r
+ * @param expectedValue\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static boolean checkFlagsCorrespondences(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {\r
+ for (Resource flag : flags) {\r
+ if (FlagUtil.isJoined(graph, flag) != expectedValue) {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Check that all specified flag elements either have a correspondence or\r
+ * don't. Flag collections with both connected and disconnected flags will\r
+ * always return false;\r
+ * \r
+ * @param flags\r
+ * @param expectedValue\r
+ * @return\r
+ */\r
+ public static boolean checkFlagsCorrespondences(Iterable<IElement> flags, boolean expectedValue) {\r
+ for (IElement flag : flags) {\r
+ if (flagHasCorrespondence(flag) != expectedValue) {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @param flags flags to test\r
+ * @param expectedValue <code>true</code> to return <code>true</code> only\r
+ * if all flags are external, <code>false</code> to return\r
+ * <code>true</code> only if all flags are not external\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static boolean checkFlagExternality(RequestProcessor processor, final Iterable<Resource> flags, final boolean expectedValue) throws DatabaseException {\r
+ return processor.syncRequest(new UniqueRead<Boolean>() {\r
+ @Override\r
+ public Boolean perform(ReadGraph graph) throws DatabaseException {\r
+ return checkFlagExternality(graph, flags, expectedValue);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * @param flags flags to test\r
+ * @param expectedValue <code>true</code> to return <code>true</code> only\r
+ * if all flags are external, <code>false</code> to return\r
+ * <code>true</code> only if all flags are not external\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static boolean checkFlagExternality(ReadGraph graph, Iterable<Resource> flags, boolean expectedValue) throws DatabaseException {\r
+ for (Resource flag : flags)\r
+ if (FlagUtil.isExternal(graph, flag) != expectedValue)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * @param flags flags to test\r
+ * @param expectedValue <code>true</code> to return <code>true</code> only\r
+ * if all flags are external, <code>false</code> to return\r
+ * <code>true</code> only if all flags are not external\r
+ * @return\r
+ */\r
+ public static boolean checkFlagExternality(Iterable<IElement> flags, boolean expectedValue) {\r
+ for (IElement flag : flags)\r
+ if (flagIsExternal(flag) != expectedValue)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ public static boolean flagHasCorrespondence(IElement flag) {\r
+ FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
+ if (fh == null)\r
+ throw new IllegalArgumentException("Not a flag element: " + flag);\r
+ //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);\r
+ return fh.getConnectionData(flag) != null;\r
+ }\r
+\r
+ public static boolean flagIsExternal(IElement flag) {\r
+ FlagHandler fh = flag.getElementClass().getSingleItem(FlagHandler.class);\r
+ if (fh == null)\r
+ throw new IllegalArgumentException("Not a flag element: " + flag);\r
+ //return (fh.getConnection(flag) != null || fh.getConnectionData(flag) != null);\r
+ return fh.isExternal(flag);\r
+ }\r
+\r
+ /**\r
+ * @param graph \r
+ * @param connection\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static Set<Resource> gatherBranchPoints(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {\r
+ Set<Resource> bps = new HashSet<Resource>();\r
+ bps.addAll(ea.branchPoints);\r
+ for (Resource connection : ea.connections)\r
+ bps.addAll( getBranchPoints(graph, connection) );\r
+ return bps;\r
+ }\r
+\r
+ /**\r
+ * @param connection\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static Set<Resource> gatherRouteGraphConnections(ReadGraph graph, ElementObjectAssortment ea) throws DatabaseException {\r
+ Set<Resource> rgcs = new HashSet<Resource>();\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ for (Resource connection : ea.connections) {\r
+ if (graph.isInstanceOf(connection, DIA.RouteGraphConnection))\r
+ rgcs.add(connection);\r
+ }\r
+ return rgcs;\r
+ }\r
+\r
+ /**\r
+ * @param connection\r
+ * @return\r
+ * @throws DatabaseException \r
+ */\r
+ public static Collection<Resource> getBranchPoints(ReadGraph graph, Resource connection) throws DatabaseException {\r
+ return graph.getObjects(connection, DiagramResource.getInstance(graph).HasBranchPoint);\r
+ }\r
+\r
+ /**\r
+ * @param m\r
+ * @param elements\r
+ * @param xoffset\r
+ * @param yoffset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveElements(WriteGraph graph, Set<Resource> elements, double xoffset, double yoffset)\r
+ throws DatabaseException {\r
+ for (Resource e : elements) {\r
+ AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);\r
+ at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),\r
+ at.getTranslateX() + xoffset,\r
+ at.getTranslateY() + yoffset);\r
+ DiagramGraphUtil.setTransform(graph, e, at);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param m\r
+ * @param elements\r
+ * @param offset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveElements(WriteGraph graph, Set<Resource> elements, Point2D offset) throws DatabaseException {\r
+ moveElements(graph, elements, offset.getX(), offset.getY());\r
+ }\r
+\r
+ /**\r
+ * @param m\r
+ * @param elements\r
+ * @param xoffset\r
+ * @param yoffset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveParentedElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, Resource parentRelation, double xoffset, double yoffset)\r
+ throws DatabaseException {\r
+ ModelingResources MOD = ModelingResources.getInstance(graph);\r
+ for (Resource e : elements) {\r
+ Resource referencedParentComponent = graph.getPossibleObject(e, parentRelation);\r
+ if (referencedParentComponent == null)\r
+ continue;\r
+ Resource referencedElement = graph.getPossibleObject(referencedParentComponent, MOD.ComponentToElement);\r
+ // Don't move the element if it's parent element is also included in the moved set of elements.\r
+ if (referencedElement != null && op.ea.all.contains(referencedElement))\r
+ continue;\r
+\r
+ AffineTransform at = DiagramGraphUtil.getAffineTransform(graph, e);\r
+ at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(),\r
+ at.getTranslateX() + xoffset,\r
+ at.getTranslateY() + yoffset);\r
+ DiagramGraphUtil.setTransform(graph, e, at);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param m\r
+ * @param elements\r
+ * @param xoffset\r
+ * @param yoffset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveMonitors(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)\r
+ throws DatabaseException {\r
+ moveParentedElements(graph, op, elements, DiagramResource.getInstance(graph).HasMonitorComponent, xoffset, yoffset);\r
+ }\r
+\r
+\r
+ /**\r
+ * @param m\r
+ * @param elements\r
+ * @param xoffset\r
+ * @param yoffset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveReferenceElements(WriteGraph graph, PasteOperation op, Set<Resource> elements, double xoffset, double yoffset)\r
+ throws DatabaseException {\r
+ moveParentedElements(graph, op, elements, ModelingResources.getInstance(graph).HasParentComponent, xoffset, yoffset);\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param connections\r
+ * @param xoffset\r
+ * @param yoffset\r
+ * @throws DatabaseException \r
+ */\r
+ public static void moveRouteGraphConnections(WriteGraph graph, Set<Resource> connections, Point2D offset) throws DatabaseException {\r
+ if(!connections.isEmpty()) {\r
+ Command command = Commands.get(graph, "Simantics/Diagram/moveConnection");\r
+ Resource root = graph.syncRequest(new IndexRoot(connections.iterator().next()));\r
+ for (Resource r : connections)\r
+ command.execute(graph, root, r, offset.getX(), offset.getY());\r
+ }\r
+ }\r
+ \r
+ public static void moveConnection(WriteGraph graph, Resource connection, double offsetX, double offsetY) throws DatabaseException {\r
+ new MoveRouteGraphConnection(connection, offsetX, offsetY).perform(graph);\r
+ }\r
+\r
+ /**\r
+ * @param ctx\r
+ * @param source\r
+ * @param target\r
+ * @param offset\r
+ */\r
+ public static void copyElementPosition(ICanvasContext ctx, IElement source, IElement target, Point2D offset) {\r
+ Point2D pos = ElementUtils.getPos(source);\r
+ double x = pos.getX() + offset.getX();\r
+ double y = pos.getY() + offset.getY();\r
+ ElementUtils.setPos(target, snap(ctx, new Point2D.Double(x, y)));\r
+ }\r
+\r
+ /**\r
+ * @param ctx\r
+ * @param source\r
+ * @param target\r
+ * @param offset\r
+ * @throws DatabaseException \r
+ */\r
+ public static AffineTransform copyElementPosition(WriteGraph graph, ICanvasContext ctx, Resource sourceElement, Resource targetElement, Point2D offset) throws DatabaseException {\r
+ AffineTransform at = getCopyTransform(graph, ctx, sourceElement);\r
+ Point2D snapped = snap(ctx, new Point2D.Double(at.getTranslateX() + offset.getX(), at.getTranslateY() + offset.getY()));\r
+ at.setTransform(at.getScaleX(), at.getShearY(), at.getShearX(), at.getScaleY(), snapped.getX(), snapped.getY());\r
+ DiagramGraphUtil.setTransform(graph, targetElement, at);\r
+ return at;\r
+ }\r
+ \r
+ private static AffineTransform getCopyTransform(ReadGraph graph, ICanvasContext ctx, Resource sourceElement) throws DatabaseException {\r
+ if(ctx != null){\r
+ Resource runtimeDiagram = (Resource)((IDiagram)ctx.getDefaultHintContext().getHint(DiagramHints.KEY_DIAGRAM)).getHint(DiagramModelHints.KEY_DIAGRAM_RUNTIME_RESOURCE);\r
+ return DiagramGraphUtil.getDynamicAffineTransform(graph, runtimeDiagram, sourceElement);\r
+ } else {\r
+ return DiagramGraphUtil.getAffineTransform(graph, sourceElement);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param ctx\r
+ * @param p\r
+ * @return\r
+ */\r
+ public static Point2D snap(ICanvasContext ctx, Point2D p) {\r
+ if (ctx != null) {\r
+ ISnapAdvisor snapAdvisor = ctx.getHintStack().getHint(DiagramHints.SNAP_ADVISOR);\r
+ if (snapAdvisor != null)\r
+ snapAdvisor.snap(p);\r
+ }\r
+ return p;\r
+ }\r
+\r
+ // ------------------------------------------------------------------------\r
+\r
+ /**\r
+ * Performs the operations related to a diagram-local cut-paste operation.\r
+ * This default implementation will merely translate the selection specified\r
+ * by the operation.\r
+ * \r
+ * @param op\r
+ * @throws DatabaseException \r
+ */\r
+ public static void localCutPaste(final PasteOperation op) throws DatabaseException {\r
+ Simantics.getSession().sync(new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ graph.markUndoPoint();\r
+ localCutPaste(graph, op);\r
+ Layer0Utils.addCommentMetadata(graph, "Cutted " + op + " to local target");\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * Performs the operations related to a diagram-local cut-paste operation.\r
+ * This default implementation will merely translate the selection specified\r
+ * by the operation.\r
+ * \r
+ * @param graph\r
+ * @param op\r
+ * @throws DatabaseException \r
+ */\r
+ public static void localCutPaste(WriteGraph graph, PasteOperation op) throws DatabaseException {\r
+ if (op.sameDiagram() && op.cut) {\r
+ CopyPasteUtil.moveElements(graph, op.ea.nodes, op.offset);\r
+ CopyPasteUtil.moveElements(graph, op.ea.flags, op.offset);\r
+ if(!op.ea.flags.isEmpty()) {\r
+ IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ for(Resource flag : op.ea.flags) {\r
+ double[] transform = graph.getRelatedValue(flag, DIA.HasTransform, Bindings.DOUBLE_ARRAY);\r
+ ioTablesInfo.updateBinding(graph, DIA, flag, transform[4], transform[5]);\r
+ }\r
+ }\r
+ CopyPasteUtil.moveElements(graph, CopyPasteUtil.gatherBranchPoints(graph, op.ea), op.offset);\r
+ CopyPasteUtil.moveRouteGraphConnections(graph, CopyPasteUtil.gatherRouteGraphConnections(graph, op.ea), op.offset);\r
+ CopyPasteUtil.moveElements(graph, op.ea.others, op.offset);\r
+ CopyPasteUtil.moveMonitors(graph, op, op.ea.monitors, op.offset.getX(), op.offset.getY());\r
+ CopyPasteUtil.moveReferenceElements(graph, op, op.ea.references, op.offset.getX(), op.offset.getY());\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param op\r
+ * @throws DatabaseException \r
+ */\r
+ public static void continueFlags(final PasteOperation op) throws DatabaseException {\r
+ Simantics.getSession().sync(new WriteRequest() {\r
+ @Override\r
+ public void perform(WriteGraph graph) throws DatabaseException {\r
+ continueFlags(graph, op);\r
+ }\r
+ });\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param op\r
+ * @throws DatabaseException \r
+ */\r
+ public static void continueFlags(WriteGraph graph, final PasteOperation op) throws DatabaseException {\r
+ IModifiableSynchronizationContext targetContext = (IModifiableSynchronizationContext) op.target.getHint(SynchronizationHints.CONTEXT);\r
+ if (targetContext == null)\r
+ throw new IllegalArgumentException("target diagram has no synchronization context");\r
+\r
+ CopyAdvisor ca = op.target.getHint(SynchronizationHints.COPY_ADVISOR);\r
+ if (ca == null)\r
+ throw new IllegalArgumentException("no copy advisor");\r
+\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+\r
+ FlagLabelingScheme scheme = DiagramFlagPreferences.getActiveFlagLabelingScheme(graph);\r
+ int joinedFlags = 0;\r
+\r
+ for (Resource src : op.ea.flags) {\r
+ Resource sourceDiagram = graph.getPossibleObject(src, L0.PartOf);\r
+ Resource copy = CopyAdvisorUtil.copy(targetContext, graph, ca, src, sourceDiagram, op.targetDiagram);\r
+ if(copy == null)\r
+ continue;\r
+ OrderedSetUtils.add(graph, op.targetDiagram, copy);\r
+ graph.claim(op.targetDiagram, L0.ConsistsOf, copy);\r
+ AddElement.claimFreshElementName(graph, op.targetDiagram, copy);\r
+\r
+ GraphLayerManager glm = targetContext.get(GraphSynchronizationHints.GRAPH_LAYER_MANAGER);\r
+ if (glm != null) {\r
+ glm.removeFromAllLayers(graph, copy);\r
+ glm.putElementOnVisibleLayers(op.target, graph, copy);\r
+ }\r
+\r
+ AffineTransform at = CopyPasteUtil.copyElementPosition(graph, op.ctx, src, copy, op.offset);\r
+ Type type = FlagUtil.getFlagType(graph, src, Type.Out);\r
+ FlagUtil.setFlagType(graph, copy, type.other());\r
+\r
+ FlagUtil.join(graph, src, copy);\r
+\r
+ if (scheme != null) {\r
+ String label = scheme.generateLabel(graph, op.targetDiagram);\r
+ if (label != null) {\r
+ graph.claimLiteral(src, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);\r
+ graph.claimLiteral(copy, L0.HasLabel, DIA.FlagLabel, label, Bindings.STRING);\r
+ }\r
+ }\r
+\r
+ // Update flag table binding\r
+ IOTablesInfo ioTablesInfo = IOTableUtil.getIOTablesInfo(graph, op.targetDiagram);\r
+ ioTablesInfo.updateBinding(graph, DIA, copy, at.getTranslateX(), at.getTranslateY());\r
+\r
+ ++joinedFlags;\r
+ }\r
+\r
+ if (joinedFlags > 0) {\r
+ // Add comment to change set.\r
+ CommentMetadata cm = graph.getMetadata(CommentMetadata.class);\r
+ graph.addMetadata(cm.add("Continued " + joinedFlags + " flag(s)"));\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @param op\r
+ * @throws DatabaseException\r
+ */\r
+ public static void performDefaultPaste(PasteOperation op) throws DatabaseException {\r
+ Session session = Simantics.getSession();\r
+ new Paster(session, op).perform();\r
+ }\r
+\r
+}\r