--- /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.content;\r
+\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.Line2D;\r
+import java.awt.geom.Point2D;\r
+import java.util.ArrayList;\r
+import java.util.Collection;\r
+import java.util.Collections;\r
+import java.util.HashSet;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+import java.util.Set;\r
+import java.util.Stack;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.db.ReadGraph;\r
+import org.simantics.db.Resource;\r
+import org.simantics.db.Statement;\r
+import org.simantics.db.WriteGraph;\r
+import org.simantics.db.WriteOnlyGraph;\r
+import org.simantics.db.common.CommentMetadata;\r
+import org.simantics.db.common.request.IndexRoot;\r
+import org.simantics.db.common.utils.NameUtils;\r
+import org.simantics.db.common.utils.OrderedSetUtils;\r
+import org.simantics.db.exception.AssumptionException;\r
+import org.simantics.db.exception.DatabaseException;\r
+import org.simantics.db.exception.ValidationException;\r
+import org.simantics.db.layer0.adapter.impl.EntityRemover;\r
+import org.simantics.db.layer0.util.RemoverUtil;\r
+import org.simantics.diagram.connection.ConnectionSegmentEnd;\r
+import org.simantics.diagram.stubs.DiagramResource;\r
+import org.simantics.diagram.synchronization.graph.BasicResources;\r
+import org.simantics.diagram.synchronization.graph.DiagramGraphUtil;\r
+import org.simantics.g2d.connection.handler.ConnectionHandler;\r
+import org.simantics.g2d.diagram.handler.PickRequest.PickFilter;\r
+import org.simantics.g2d.diagram.handler.Topology.Terminal;\r
+import org.simantics.g2d.diagram.participant.pointertool.TerminalUtil.TerminalInfo;\r
+import org.simantics.g2d.element.ElementUtils;\r
+import org.simantics.g2d.element.IElement;\r
+import org.simantics.g2d.element.handler.BendsHandler;\r
+import org.simantics.g2d.element.handler.EdgeVisuals.EdgeEnd;\r
+import org.simantics.g2d.element.handler.impl.BranchPointTerminal;\r
+import org.simantics.g2d.elementclass.BranchPoint;\r
+import org.simantics.g2d.elementclass.BranchPoint.Direction;\r
+import org.simantics.layer0.Layer0;\r
+import org.simantics.modeling.ModelingResources;\r
+import org.simantics.scenegraph.utils.GeometryUtils;\r
+import org.simantics.scl.commands.Commands;\r
+import org.simantics.structural.stubs.StructuralResource2;\r
+import org.simantics.structural2.modelingRules.CPConnection;\r
+import org.simantics.structural2.modelingRules.CPConnectionJoin;\r
+import org.simantics.structural2.modelingRules.CPTerminal;\r
+import org.simantics.structural2.modelingRules.IConnectionPoint;\r
+import org.simantics.utils.Development;\r
+import org.simantics.utils.datastructures.Pair;\r
+import org.simantics.utils.datastructures.Triple;\r
+import org.simantics.utils.ui.AdaptionUtils;\r
+\r
+/**\r
+ * @author Tuukka Lehtonen\r
+ */\r
+public final class ConnectionUtil {\r
+\r
+ private final ReadGraph rg;\r
+ private final WriteGraph g;\r
+ private final BasicResources br;\r
+\r
+ /**\r
+ * Construct utility with read-only support.\r
+ * @param g\r
+ */\r
+ public ConnectionUtil(ReadGraph g) {\r
+ this.rg = g;\r
+ this.g = (g instanceof WriteGraph) ? (WriteGraph) g : null;\r
+ this.br = BasicResources.getInstance(g);\r
+ }\r
+\r
+ /**\r
+ * Construct utility with read-write support.\r
+ * @param g\r
+ */\r
+ public ConnectionUtil(WriteGraph g) {\r
+ this.rg = g;\r
+ this.g = g;\r
+ this.br = BasicResources.getInstance(g);\r
+ }\r
+\r
+ void assertWriteSupport() {\r
+ if (g == null)\r
+ throw new UnsupportedOperationException("no write support, this ConnectionUtil is read-only");\r
+ }\r
+\r
+ /**\r
+ * Creates a new connection element of the specified type and attaches it to\r
+ * the specified diagram composite.\r
+ * \r
+ * @param composite diagram composite\r
+ * @param type connection element type\r
+ * @return created connection\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource newConnection(Resource composite, Resource type) throws DatabaseException {\r
+ assertWriteSupport();\r
+ Resource connection = newConnection(type);\r
+ // By default, add connections to the front of the diagram since most\r
+ // often it is visually the expected result.\r
+ OrderedSetUtils.addFirst(g, composite, connection);\r
+ g.claim(composite, br.L0.ConsistsOf, br.L0.PartOf, connection);\r
+ return connection;\r
+ }\r
+\r
+ /**\r
+ * Creates a new connection element of the specified type without attaching\r
+ * it to any diagram composite.\r
+ * \r
+ * @param type connection element type\r
+ * @return created connection\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource newConnection(Resource type) throws DatabaseException {\r
+ assertWriteSupport();\r
+ return newInstance(g, type);\r
+ }\r
+\r
+ /**\r
+ * Creates a new element terminal connector (DiagramResource) for the specified connector\r
+ * @param connection\r
+ * @param hasConnector\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource newConnector(Resource connection, Resource hasConnector) throws DatabaseException {\r
+ assertWriteSupport();\r
+ Resource connector = newInstance(g, br.DIA.Connector);\r
+ g.claim(connection, hasConnector, connector);\r
+ return connector;\r
+ }\r
+\r
+ public Resource newBranchPoint(Resource connection, AffineTransform tr) throws DatabaseException {\r
+ return newBranchPoint(connection, tr, null);\r
+ }\r
+\r
+ public Resource newBranchPoint(Resource connection, AffineTransform tr, Direction direction) throws DatabaseException {\r
+ assertWriteSupport();\r
+ Resource bp = g.newResource();\r
+ g.claim(bp, br.L0.InstanceOf, null, br.DIA.BranchPoint);\r
+ if (tr != null) {\r
+ double[] mat = new double[6];\r
+ tr.getMatrix(mat);\r
+ Resource transform = g.newResource();\r
+ g.claim(transform, br.L0.InstanceOf, null, br.G2D.Transform);\r
+ g.claimValue(transform, mat);\r
+ g.claim(bp, br.DIA.HasTransform, transform);\r
+ }\r
+ Resource tag = toDirectionTag(g, direction);\r
+ if (tag != null) {\r
+ g.claim(bp, tag, tag, bp);\r
+ }\r
+ g.claim(connection, br.DIA.HasBranchPoint, bp);\r
+ return bp;\r
+ }\r
+\r
+ /**\r
+ * @param connection\r
+ * @param position\r
+ * @param isHorizontal\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource newRouteLine(Resource connection, Double position, Boolean isHorizontal) throws DatabaseException {\r
+ assertWriteSupport();\r
+ Resource rl = newInstance(g, br.DIA.RouteLine);\r
+ if (position != null) {\r
+ g.addLiteral(rl, br.DIA.HasPosition, br.DIA.HasPosition_Inverse, br.L0.Double, position, Bindings.DOUBLE);\r
+ }\r
+ if (isHorizontal != null) {\r
+ g.addLiteral(rl, br.DIA.IsHorizontal, br.DIA.IsHorizontal_Inverse, br.L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
+ }\r
+ g.claim(connection, br.DIA.HasInteriorRouteNode, br.DIA.HasInteriorRouteNode_Inverse, rl);\r
+ return rl;\r
+ }\r
+\r
+ ConnectionSegmentEnd getTerminalType(Terminal terminal, ConnectionSegmentEnd defaultValue) {\r
+ ConnectionSegmentEnd type = getTerminalType(terminal);\r
+ return type != null ? type : defaultValue;\r
+ }\r
+\r
+ ConnectionSegmentEnd getTerminalType(Terminal t) {\r
+ if (t == null)\r
+ return null;\r
+\r
+ if (t instanceof ResourceTerminal) {\r
+ return ConnectionSegmentEnd.CONNECTOR;\r
+ } else if (t instanceof BranchPointTerminal) {\r
+ return ConnectionSegmentEnd.BRANCH;\r
+ } else {\r
+ throw new IllegalArgumentException("unsupported terminal '" + t + "'");\r
+ }\r
+ }\r
+\r
+ Resource resolveTerminalRelation(ReadGraph g, Terminal t) throws DatabaseException {\r
+ if (t == null)\r
+ return null;\r
+ if (t instanceof ResourceTerminal) {\r
+ ResourceTerminal rt = (ResourceTerminal) t;\r
+ Resource terminalRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, rt.getResource());\r
+ if (!g.isSubrelationOf(terminalRelation, br.STR.IsConnectedTo)) {\r
+ // debug...\r
+ }\r
+ return terminalRelation;\r
+ } else {\r
+ throw new IllegalArgumentException("unsupported terminal '" + t + "' for terminal relation resolution");\r
+ }\r
+ }\r
+\r
+ public Resource toHasConnectorRelation(EdgeEnd end) {\r
+ switch (end) {\r
+ case Begin: return br.DIA.HasPlainConnector;\r
+ case End: return br.DIA.HasArrowConnector;\r
+ default: throw new IllegalArgumentException("unsupported edge end: " + end);\r
+ }\r
+ }\r
+\r
+ public EdgeEnd toEdgeEnd(Resource attachmentRelation, EdgeEnd defaultValue) throws DatabaseException {\r
+ if (g.isSubrelationOf(attachmentRelation, br.DIA.HasPlainConnector))\r
+ return EdgeEnd.Begin;\r
+ if (g.isSubrelationOf(attachmentRelation, br.DIA.HasArrowConnector))\r
+ return EdgeEnd.End;\r
+ return defaultValue;\r
+ }\r
+\r
+ public Resource getAttachmentRelationForConnector(Resource connector) throws DatabaseException {\r
+ Statement connection = g.getPossibleStatement(connector, br.DIA.IsConnectorOf);\r
+ if (connection == null)\r
+ return null;\r
+ Resource attachment = g.getInverse(connection.getPredicate());\r
+ return attachment;\r
+ }\r
+\r
+ /**\r
+ * @param connection\r
+ * @param node\r
+ * @param c\r
+ * @param end\r
+ * @param attachmentRelation the relation used for attaching the connector to the connector\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource getOrCreateConnector(Resource connection, Resource node, Terminal terminal, EdgeEnd end, Resource attachmentRelation) throws DatabaseException {\r
+ assertWriteSupport();\r
+ ConnectionSegmentEnd connectorType = getTerminalType(terminal, null);\r
+ if (connectorType == null)\r
+ throw new AssumptionException("Invalid connection node", connection, node);\r
+\r
+ switch (connectorType) {\r
+ case BRANCH:\r
+ // NOTE: should we ensure here that (connection, br.dr.HasBranchPoint, node) exists?\r
+ return node;\r
+\r
+ case CONNECTOR: {\r
+ Resource terminalRelation = resolveTerminalRelation(g, terminal);\r
+\r
+ if (attachmentRelation == null)\r
+ attachmentRelation = toHasConnectorRelation(end);\r
+\r
+ if (!g.isSubrelationOf(attachmentRelation, br.DIA.HasConnector))\r
+ throw new AssumptionException("attachment relation not a subrelation of Has Connector", attachmentRelation);\r
+\r
+ // Create new connector for the specified node terminal\r
+ Resource terminalConnector = newConnector(connection, attachmentRelation);\r
+ g.claim(node, terminalRelation, terminalConnector);\r
+ return terminalConnector;\r
+ }\r
+ default:\r
+ throw new Error("this should be unreachable code");\r
+ }\r
+ }\r
+\r
+ public void connect(Resource connector1, Resource connector2) throws DatabaseException {\r
+ assertWriteSupport();\r
+ g.claim(connector1, br.DIA.AreConnected, br.DIA.AreConnected, connector2);\r
+ }\r
+\r
+ public void disconnect(Resource connector1, Resource connector2) throws DatabaseException {\r
+ assertWriteSupport();\r
+ g.denyStatement(connector1, br.DIA.AreConnected, connector2);\r
+ }\r
+\r
+ public void disconnectFromAllRouteNodes(Resource connector) throws DatabaseException {\r
+ assertWriteSupport();\r
+ g.deny(connector, br.DIA.AreConnected);\r
+ }\r
+\r
+ public void disconnect(EdgeResource segment) throws DatabaseException {\r
+ assertWriteSupport();\r
+ disconnect(segment.first(), segment.second());\r
+ }\r
+\r
+ public boolean isConnected(Resource connector) throws DatabaseException {\r
+ return rg.hasStatement(connector, br.DIA.AreConnected);\r
+ }\r
+\r
+ public boolean isConnected(Resource connector, Resource toConnector) throws DatabaseException {\r
+ return rg.hasStatement(connector, br.DIA.AreConnected, toConnector);\r
+ }\r
+\r
+ public boolean isConnectionEmpty(Resource connection) throws DatabaseException {\r
+ return !rg.hasStatement(connection, br.DIA.HasConnector)\r
+ && !rg.hasStatement(connection, br.DIA.HasInteriorRouteNode);\r
+ }\r
+\r
+ private void removeConnectorOrBranchPoint(Resource connectorOrBranchPoint) throws DatabaseException {\r
+\r
+// // Handle correct removal of route points\r
+// if(g.isInstanceOf(connectorOrBranchPoint, dr.BranchPoint)) {\r
+// Collection<Resource> connectedConnectors = g.getObjects(connectorOrBranchPoint, dr.AreConnected);\r
+// if(connectedConnectors.size() == 2) {\r
+// Iterator<Resource> it = connectedConnectors.iterator();\r
+// g.claim(it.next(), dr.AreConnected, it.next());\r
+// }\r
+// }\r
+\r
+ g.deny(connectorOrBranchPoint, br.DIA.AreConnected);\r
+\r
+ // Removes both the terminal relation and the HasConnector relation\r
+ // to the :Connection\r
+ g.deny(connectorOrBranchPoint, br.STR.Connects);\r
+\r
+ // If this is a branch point/route node, remove it from the connection too.\r
+ g.deny(connectorOrBranchPoint, br.DIA.IsBranchPointOf);\r
+ g.deny(connectorOrBranchPoint, br.DIA.HasInteriorRouteNode_Inverse);\r
+ }\r
+\r
+ /**\r
+ * Removes a complete connection along with all its branch points and terminal connectors.\r
+ * \r
+ * @param connection the connection to remove\r
+ */\r
+ public void removeConnection(Resource connection) throws DatabaseException {\r
+ assertWriteSupport();\r
+\r
+ // Add comment to change set.\r
+ CommentMetadata cm = g.getMetadata(CommentMetadata.class);\r
+ g.addMetadata(cm.add("Remove connection " + connection));\r
+\r
+ // 1. Get all connectors/branch points\r
+ Collection<Resource> connectors = new ArrayList<Resource>();\r
+ connectors.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
+ connectors.addAll(rg.getObjects(connection, br.DIA.HasInteriorRouteNode));\r
+\r
+ // 2. Remove all connectors/branch points\r
+ for (Resource connector : connectors) {\r
+ removeConnectorOrBranchPoint(connector);\r
+ RemoverUtil.remove(g, connector);\r
+ }\r
+\r
+ // 3. Remove whole connection\r
+ for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, br.DIA.Diagram))\r
+ OrderedSetUtils.remove(g, owner, connection);\r
+ EntityRemover.remove(g, connection);\r
+ }\r
+\r
+ /**\r
+ * Removes a single connector part from the graph. A connection part can be\r
+ * either a branch point or a terminal connector.\r
+ * \r
+ * @param connectorOrBranchPoint\r
+ * @throws DatabaseException\r
+ */\r
+ public void removeConnectionPart(Resource connectorOrBranchPoint) throws DatabaseException {\r
+ removeConnectorOrBranchPoint(connectorOrBranchPoint);\r
+ RemoverUtil.remove(g, connectorOrBranchPoint);\r
+ }\r
+\r
+ /**\r
+ * Removes the specified connection segment. Checks that both ends of the\r
+ * edge segment are part of to the same connection. Steps taken:\r
+ * <ul>\r
+ * <li>Minimally this only disconnects the connection segment ends from each\r
+ * other and nothing more.</li>\r
+ * <li>After disconnecting, we check whether the segment ends are still\r
+ * connected to something. If not, the :Connector/:BranchPoint at the\r
+ * segment's end is destroyed and detached from the connection entity.</li>\r
+ * <li>Finally, if the connection entity is empty (has no connectors/branch\r
+ * points), it is also destroyed and removed from the diagram.</li>\r
+ * \r
+ * @param segment the connection segment to remove\r
+ */\r
+ public void remove(EdgeResource segment) throws DatabaseException {\r
+ remove(segment, false);\r
+ }\r
+\r
+ /**\r
+ * Removes the specified connection segment. Steps taken:\r
+ * <ul>\r
+ * <li>Minimally this only disconnects the connection segment ends from each\r
+ * other and nothing more.</li>\r
+ * <li>After disconnecting, we check whether the segment ends are still\r
+ * connected to something. If not, the :Connector/:BranchPoint at the\r
+ * segment's end is destroyed and detached from the connection entity.</li>\r
+ * <li>Finally, if the connection entity is empty (has no connectors/branch\r
+ * points), it is also destroyed and removed from the diagram.</li>\r
+ * \r
+ * @param segment the connection segment to remove\r
+ * @param unchecked <code>false</code> to check that both ends of the\r
+ * segment are part of the same connection before removing,\r
+ * <code>true</code> to just remove the segment without checking\r
+ * this. Using <code>true</code> may help in cases where the\r
+ * connection model has become corrupted for some reason, e.g. the\r
+ * other end of the edge has lost its link to the connection while\r
+ * the other has not,\r
+ */\r
+ public void remove(EdgeResource segment, boolean unchecked) throws DatabaseException {\r
+ assertWriteSupport();\r
+\r
+ if (!unchecked) {\r
+ @SuppressWarnings("unused")\r
+ Resource connection = getConnection(g, segment);\r
+ }\r
+\r
+ // 1. disconnect segment ends\r
+ disconnect(segment);\r
+\r
+ // 2. Remove connectors/branch points if they become fully disconnected\r
+ if (!isConnected(segment.first())) {\r
+ removeConnectorOrBranchPoint(segment.first());\r
+ }\r
+ if (!isConnected(segment.second())) {\r
+ removeConnectorOrBranchPoint(segment.second());\r
+ }\r
+\r
+ // 3. Remove whole connection entity if it becomes empty\r
+// if (isConnectionEmpty(connection)) {\r
+// for (Resource owner : OrderedSetUtils.getOwnerLists(g, connection, dr.Diagram))\r
+// OrderedSetUtils.remove(g, owner, connection);\r
+// RemoverUtil.remove(g, connection);\r
+// }\r
+\r
+ }\r
+\r
+ /**\r
+ * Removes all DIA.Connector instances from the specified connection that\r
+ * are not used for anything.\r
+ * \r
+ * @param connection connection to examine\r
+ * @return the amount of unused connectors removed\r
+ */\r
+ public int removeUnusedConnectors(Resource connection) throws DatabaseException {\r
+ int result = 0;\r
+ for (Resource connector : getConnectors(connection, null)) {\r
+ if (!g.getObjects(connector, br.DIA.AreConnected).isEmpty())\r
+ continue;\r
+ Collection<Resource> connects = g.getObjects(connector, br.STR.Connects);\r
+ if (connects.size() > 1)\r
+ continue;\r
+\r
+ removeConnectionPart(connector);\r
+ ++result;\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Removes all DIA.InteriorRouteNode instances from the specified connection that\r
+ * are not used for anything, i.e. connect to less than 2 other route nodes.\r
+ * \r
+ * @param connection connection to examine\r
+ * @return the amount of unused connectors removed\r
+ */\r
+ public int removeExtraInteriorRouteNodes(Resource connection) throws DatabaseException {\r
+ int result = 0;\r
+ for (Resource interiorRouteNode : g.getObjects(connection, br.DIA.HasInteriorRouteNode)) {\r
+ Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
+ if (connectedTo.size() > 1)\r
+ continue;\r
+\r
+ removeConnectionPart(interiorRouteNode);\r
+ ++result;\r
+ }\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * Splits the specified connection segment by adding a new branch point in\r
+ * between the segment ends.\r
+ * \r
+ * @param segment\r
+ * @return the branch (route) point created by the split operation.\r
+ */\r
+ public Resource split(EdgeResource segment, AffineTransform splitPos) throws DatabaseException {\r
+ assertWriteSupport();\r
+\r
+ Resource connection = getConnection(g, segment);\r
+ disconnect(segment);\r
+ Resource bp = newBranchPoint(connection, splitPos);\r
+ connect(segment.first(), bp);\r
+ connect(bp, segment.second());\r
+ return bp;\r
+ }\r
+\r
+ /**\r
+ * Joins the connection at the selected branch point if and only if the\r
+ * branch point is a route point, i.e. it is connected to two other branch\r
+ * points or connector.\r
+ * \r
+ * @param interiorRouteNode\r
+ * @return\r
+ */\r
+ public void join(Resource interiorRouteNode) throws DatabaseException {\r
+ assertWriteSupport();\r
+\r
+ if (!g.isInstanceOf(interiorRouteNode, br.DIA.InteriorRouteNode))\r
+ throw new ValidationException("'" + NameUtils.getSafeName(g, interiorRouteNode) + "' is not an instance of DIA.InteriorRouteNode");\r
+ @SuppressWarnings("unused")\r
+ Resource connection = getConnection(g, interiorRouteNode);\r
+ Collection<Resource> connectedTo = g.getObjects(interiorRouteNode, br.DIA.AreConnected);\r
+ if (connectedTo.size() != 2)\r
+ throw new ValidationException("Interior route node is not a discardable route line/point. It is not connected to 2 route nodes, but " + connectedTo.size() + ".");\r
+ Iterator<Resource> it = connectedTo.iterator();\r
+ Resource connector1 = it.next();\r
+ Resource connector2 = it.next();\r
+ //System.out.println("removing branch point " + GraphUtils.getReadableName(g, routeBranchPoint) + " which is connected to " + GraphUtils.getReadableName(g, connector1) + " and " + GraphUtils.getReadableName(g, connector2));\r
+ removeConnectorOrBranchPoint(interiorRouteNode);\r
+ connect(connector1, connector2);\r
+ }\r
+\r
+ public void getConnectionSegments(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
+\r
+ ArrayList<EdgeResource> edges = new ArrayList<EdgeResource>();\r
+ Set<Resource> visited = new HashSet<Resource>();\r
+ Stack<Resource> todo = new Stack<Resource>();\r
+\r
+ // Try to select input as root, this ensures correct order for simple paths\r
+ Collection<Resource> seeds = rg.getObjects(connection, br.DIA.HasArrowConnector);\r
+ if(seeds.isEmpty()) seeds = rg.getObjects(connection, br.DIA.HasPlainConnector);\r
+\r
+ assert(!seeds.isEmpty());\r
+\r
+ Resource seed = seeds.iterator().next();\r
+\r
+ todo.push(seed);\r
+\r
+ while(!todo.isEmpty()) {\r
+ Resource location = todo.pop();\r
+ if(!visited.contains(location)) {\r
+ visited.add(location);\r
+ for (Resource connectedTo : rg.getObjects(location, br.DIA.AreConnected)) {\r
+ todo.add(connectedTo);\r
+ EdgeResource edge = new EdgeResource(location, connectedTo);\r
+ if(!edges.contains(edge)) edges.add(edge);\r
+ }\r
+ }\r
+ }\r
+\r
+ for(EdgeResource uer : edges) {\r
+// System.out.println("loaded edge " + uer.first() + " " + uer.second());\r
+ result.add(uer);\r
+ }\r
+\r
+ }\r
+\r
+ public Collection<Resource> getBranchPoints(Resource connection, Collection<Resource> result) throws DatabaseException {\r
+ if (result == null)\r
+ result = new ArrayList<Resource>();\r
+ result.addAll(rg.getObjects(connection, br.DIA.HasBranchPoint));\r
+ return result;\r
+ }\r
+\r
+ public Collection<Resource> getConnectors(Resource connection, Collection<Resource> result) throws DatabaseException {\r
+ if (result == null)\r
+ result = new ArrayList<Resource>();\r
+ result.addAll(rg.getObjects(connection, br.DIA.HasConnector));\r
+ return result;\r
+ }\r
+\r
+ public Resource getConnectedComponent(Resource connection, Resource connector) throws DatabaseException {\r
+ for (Resource connects : rg.getObjects(connector, br.STR.Connects))\r
+ if (!connects.equals(connection))\r
+ return connects;\r
+ return null;\r
+ }\r
+\r
+ public Statement getConnectedComponentStatement(Resource connection, Resource connector) throws DatabaseException {\r
+ for (Statement connects : rg.getStatements(connector, br.STR.Connects))\r
+ if (!connects.getObject().equals(connection))\r
+ return connects;\r
+ return null;\r
+ }\r
+\r
+ public void gatherEdges(Resource connection, Collection<EdgeResource> result) throws DatabaseException {\r
+ Set<Object> visited = new HashSet<Object>();\r
+ for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+ for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
+ EdgeResource p = new EdgeResource(connector, connectedTo);\r
+ if (visited.add(p)) {\r
+ result.add(p);\r
+ }\r
+ }\r
+ }\r
+ Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
+ for (Resource routeNode : routeNodes) {\r
+ for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
+ EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
+ if (visited.add(p)) {\r
+ result.add(p);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public void gatherConnectionParts(Resource connection, Collection<Object> result) throws DatabaseException {\r
+ Set<Object> visited = new HashSet<Object>();\r
+ for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+ for (Resource connectedTo : rg.getObjects(connector, br.DIA.AreConnected)) {\r
+ EdgeResource p = new EdgeResource(connector, connectedTo);\r
+ if (visited.add(p)) {\r
+ result.add(p);\r
+ }\r
+ }\r
+ }\r
+ Collection<Resource> routeNodes = rg.getObjects(connection, br.DIA.HasInteriorRouteNode);\r
+ for (Resource routeNode : routeNodes) {\r
+ result.add(routeNode);\r
+ for (Resource connectedTo : rg.getObjects(routeNode, br.DIA.AreConnected)) {\r
+ EdgeResource p = new EdgeResource(routeNode, connectedTo);\r
+ if (visited.add(p)) {\r
+ result.add(p);\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ public Collection<Resource> getConnectedComponents(Resource connection, Collection<Resource> result)\r
+ throws DatabaseException {\r
+ if (result == null)\r
+ result = new ArrayList<Resource>(2);\r
+ for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+ for (Resource connects : rg.getObjects(connector, br.STR.Connects)) {\r
+ if (connects.equals(connection))\r
+ continue;\r
+ result.add(connects);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public Collection<Resource> getConnectedConnectors(Resource connection, Collection<Resource> result)\r
+ throws DatabaseException {\r
+ if (result == null)\r
+ result = new ArrayList<Resource>(2);\r
+ for (Resource connector : rg.getObjects(connection, br.DIA.HasConnector)) {\r
+ Resource connects = getConnectedComponent(connection, connector);\r
+ if (connects != null)\r
+ result.add( connector );\r
+ }\r
+ return result;\r
+ }\r
+\r
+ public Collection<Resource> getTerminalConnectors(Resource node, Collection<Resource> result)\r
+ throws DatabaseException {\r
+ if (result == null)\r
+ result = new ArrayList<Resource>(2);\r
+ result.addAll( rg.getObjects(node, br.STR.IsConnectedTo) );\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * @param g\r
+ * @param routeNode\r
+ * @return <code>null</code> if the specified resource is not part of any\r
+ * connection\r
+ */\r
+ public static Resource tryGetConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
+ DiagramResource dr = DiagramResource.getInstance(g);\r
+ Resource conn = g.getPossibleObject(routeNode, dr.IsConnectorOf);\r
+ if (conn == null)\r
+ conn = g.getPossibleObject(routeNode, dr.HasInteriorRouteNode_Inverse);\r
+ return conn;\r
+ }\r
+\r
+ public static Resource tryGetConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
+ Resource first = tryGetConnection(g, segment.first());\r
+ Resource second = tryGetConnection(g, segment.second());\r
+ if (first == null || second == null || !first.equals(second))\r
+ return null;\r
+ return first;\r
+ }\r
+\r
+ public static Resource tryGetMappedConnection(ReadGraph g, Resource connector) throws DatabaseException {\r
+ ModelingResources MOD = ModelingResources.getInstance(g);\r
+ return g.getPossibleObject(connector, MOD.ConnectorToConnection);\r
+ }\r
+ \r
+ public static Resource getConnection(ReadGraph g, EdgeResource segment) throws DatabaseException {\r
+ Resource first = tryGetConnection(g, segment.first());\r
+ Resource second = tryGetConnection(g, segment.second());\r
+ if (first == null && second == null)\r
+ throw new ValidationException(\r
+ "neither connection segment end is attached to a Connection entity instance: "\r
+ + segment.toString(g) + " - " + segment.toString());\r
+ if (first != null ^ second != null)\r
+ throw new ValidationException("both ends of connection segment "\r
+ + segment.toString(g) + " - " + segment.toString() + " are not connected (first=" + first\r
+ + ", second=" + second + ")");\r
+ if (!first.equals(second))\r
+ throw new ValidationException("connectors of connection segment "\r
+ + segment.toString(g) + " - " + segment.toString() + " are part of different connections: " + first\r
+ + " vs. " + second);\r
+ return first;\r
+ }\r
+\r
+ public static Resource getConnection(ReadGraph g, Resource routeNode) throws DatabaseException {\r
+ Resource connection = tryGetConnection(g, routeNode);\r
+ if (connection == null)\r
+ throw new ValidationException("route node '"\r
+ + NameUtils.getSafeName(g, routeNode) + "' is not part of any connection");\r
+ return connection;\r
+ }\r
+\r
+ public static IConnectionPoint toConnectionPoint(ReadGraph g, Resource element, Terminal terminal) throws DatabaseException {\r
+ if (terminal instanceof ResourceTerminal) {\r
+ Resource t = ((ResourceTerminal) terminal).getResource();\r
+\r
+ // TODO: remove this hack\r
+ if (element == null) { // Flag?\r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ Resource join = g.getPossibleObject(t, DIA.FlagIsJoinedBy);\r
+ if (join == null)\r
+ return null;\r
+ return new CPConnectionJoin(join);\r
+ }\r
+ // element should be :Element\r
+ Resource bindingRelation = DiagramGraphUtil.getConnectionPointOfTerminal(g, t);\r
+ return new CPTerminal(element, bindingRelation);\r
+ } else if (terminal instanceof BranchPointTerminal) {\r
+ // element should be either : DIA.InteriorRouteNode or DIA.Connection\r
+ Resource connection = null;\r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ if (g.isInstanceOf(element, DIA.Connection))\r
+ connection = element;\r
+ else\r
+ connection = getConnection(g, element);\r
+ return new CPConnection(connection);\r
+ }\r
+ throw new IllegalArgumentException("Unrecognized Terminal class: " + terminal);\r
+ }\r
+\r
+ public static IConnectionPoint toConnectionPoint(ReadGraph graph, IElement element, Terminal terminal) throws DatabaseException {\r
+ Resource r = (Resource) ElementUtils.getObject(element);\r
+ return ConnectionUtil.toConnectionPoint(graph, r, terminal);\r
+ }\r
+\r
+ public static IConnectionPoint toConnectionPoint(ReadGraph graph, TerminalInfo ti) throws DatabaseException {\r
+ return ConnectionUtil.toConnectionPoint(graph, ti.e, ti.t);\r
+ }\r
+\r
+ public static IConnectionPoint toConnectionPoint(ReadGraph graph, DesignatedTerminal t) throws DatabaseException {\r
+ return toConnectionPoint(graph, t.element, t.terminal);\r
+ }\r
+\r
+ public static Resource toDirectionTag(ReadGraph graph, BranchPoint.Direction direction) {\r
+ if (direction == null)\r
+ return null;\r
+\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ switch (direction) {\r
+ case Horizontal: return DIA.Horizontal;\r
+ case Vertical: return DIA.Vertical;\r
+ default: return null;\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Copied from ConnectionCommandHandler#splitConnection, duplicate code.\r
+ * \r
+ * @param toCanvasPos\r
+ * @param onEdge\r
+ * @return\r
+ */\r
+ public static Line2D resolveNearestEdgeLineSegment(Point2D toCanvasPos, IElement onEdge) {\r
+ // Try to find an initial preferred direction for the new\r
+ // branch point based on the direction of the edge's line\r
+ // segment on which the split is done.\r
+ List<Point2D> points = new ArrayList<Point2D>();\r
+ BendsHandler bh = onEdge.getElementClass().getAtMostOneItemOfClass(BendsHandler.class);\r
+ if (bh != null)\r
+ org.simantics.g2d.utils.GeometryUtils.getPoints(bh.getPath(onEdge), points);\r
+ Line2D nearestLine = null;\r
+ double nearestDistanceToLine = Double.MAX_VALUE;\r
+ for (int i = 0; i < points.size() - 1; ++i) {\r
+ Point2D p1 = points.get(i);\r
+ Point2D p2 = points.get(i+1);\r
+ double distanceToLine = GeometryUtils.distanceFromLine(toCanvasPos, p1, p2);\r
+ if (distanceToLine < nearestDistanceToLine) {\r
+ nearestDistanceToLine = distanceToLine;\r
+ if (nearestLine == null)\r
+ nearestLine = new Line2D.Double();\r
+ nearestLine.setLine(p1, p2);\r
+ }\r
+ }\r
+ return nearestLine;\r
+ }\r
+\r
+ /**\r
+ * @param object\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public static IElement getSingleEdge(Object object) throws DatabaseException {\r
+ IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
+ if (e == null)\r
+ return null;\r
+\r
+ if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
+ return e;\r
+\r
+ if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
+ ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
+ Collection<IElement> bps = ch.getBranchPoints(e, null);\r
+ Collection<IElement> segs = ch.getSegments(e, null);\r
+ if (bps.isEmpty() && segs.size() == 1)\r
+ return segs.iterator().next();\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @param object\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public static Collection<IElement> getEdges(Object object) throws DatabaseException {\r
+ IElement e = AdaptionUtils.adaptToSingle(object, IElement.class);\r
+ if (e == null)\r
+ return null;\r
+\r
+ if (PickFilter.FILTER_CONNECTION_EDGES.accept(e))\r
+ return Collections.singleton(e);\r
+\r
+ if (PickFilter.FILTER_CONNECTIONS.accept(e)) {\r
+ ConnectionHandler ch = e.getElementClass().getSingleItem(ConnectionHandler.class);\r
+ return ch.getSegments(e, null);\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param connection\r
+ * @return\r
+ * @throws DatabaseException\r
+ */\r
+ public static Resource getConnectionTailNode(ReadGraph graph, Resource connection) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
+ for (Resource node : graph.getObjects(connector, STR.Connects)) {\r
+ if (node.equals(connection))\r
+ continue;\r
+ return node;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param connection\r
+ * @return the STR.Connects statement from the tail DIA.Connector to the\r
+ * tail node (DIA.Element) or <code>null</code> if no tail node\r
+ * exists\r
+ * @throws DatabaseException\r
+ */\r
+ public static Statement getConnectionTailNodeStatement(ReadGraph graph, Resource connection) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ StructuralResource2 STR = StructuralResource2.getInstance(graph);\r
+ for (Resource connector : graph.getObjects(connection, DIA.HasTailConnector)) {\r
+ for (Statement connects : graph.getStatements(connector, STR.Connects)) {\r
+ if (connects.getObject().equals(connection))\r
+ continue;\r
+ return connects;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * @param connection\r
+ * @param dx\r
+ * @param dy\r
+ * @throws DatabaseException\r
+ */\r
+ public void translateRouteNodes(Resource connection, double dx, double dy) throws DatabaseException {\r
+ Commands.get(g, "Simantics/Diagram/translateRouteNodes")\r
+ .execute(g, g.syncRequest(new IndexRoot(connection)), connection, dx, dy);\r
+ }\r
+ \r
+ public static void translateRouteNodes(WriteGraph g, Resource connection, double dx, double dy) throws DatabaseException {\r
+ DiagramResource DIA = DiagramResource.getInstance(g);\r
+ for (Resource routeNode : g.getObjects(connection, DIA.HasInteriorRouteNode)) {\r
+ if (g.isInstanceOf(routeNode, DIA.RouteLine)) {\r
+ Double pos = g.getRelatedValue(routeNode, DIA.HasPosition, Bindings.DOUBLE);\r
+ Boolean isHorizontal = g.getRelatedValue(routeNode, DIA.IsHorizontal, Bindings.BOOLEAN);\r
+ pos += isHorizontal ? dy : dx;\r
+ g.claimLiteral(routeNode, DIA.HasPosition, pos);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Creates a route graph connection which has corners at the specified\r
+ * locations, starting/ending from/at the specified element terminals.\r
+ * \r
+ * if {@link Development#DEVELOPMENT} is <code>true</code> the code will\r
+ * verify that both element end-points are part of the same diagram.\r
+ * \r
+ * @param graph\r
+ * database write access\r
+ * @param startElement\r
+ * element to start connecting from\r
+ * @param startConnectionPoint\r
+ * STR.ConnectedTo relation of the start element terminal\r
+ * @param endElement\r
+ * element to end the connection at\r
+ * @param endConnectionPoint\r
+ * STR.ConnectedTo relation of the end element terminal\r
+ * @param corners\r
+ * the corners to create for the connection\r
+ * @return the created diagram connection resource\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource createConnectionWithCorners(WriteGraph graph, Resource startElement,\r
+ Resource startConnectionPoint, Resource endElement, Resource endConnectionPoint, List<Point2D> corners)\r
+ throws DatabaseException {\r
+ DiagramResource DIA = br.DIA;\r
+\r
+ // Verify that both elements are part of the same diagram before connecting.\r
+ if (Development.DEVELOPMENT) {\r
+ Collection<Resource> startDiagram = OrderedSetUtils.getOwnerLists(graph, startElement, DIA.Diagram);\r
+ Collection<Resource> endDiagram = OrderedSetUtils.getOwnerLists(graph, endElement, DIA.Diagram);\r
+ if (Collections.disjoint(startDiagram, endDiagram))\r
+ throw new IllegalArgumentException("start element " + startElement\r
+ + " is not on same diagram as end element " + endElement + ". start diagram: " + startDiagram\r
+ + ", end diagram: " + endDiagram);\r
+ }\r
+\r
+ return createConnectionWithCorners(graph, DIA.RouteGraphConnection, startElement,\r
+ startConnectionPoint, DIA.HasPlainConnector, endElement, endConnectionPoint, DIA.HasArrowConnector,\r
+ corners);\r
+ }\r
+\r
+ /**\r
+ * Creates a route graph connection which has corners at the specified\r
+ * locations, starting/ending from/at the specified element terminals.\r
+ * Verifies that both element end-points are part of the same diagram.\r
+ * \r
+ * @param graph database write access\r
+ * @param connectionType type of created connection (e.g. DIA.RouteGraphConnection)\r
+ * @param element1 element to start connecting from\r
+ * @param connectionPoint1 STR.ConnectedTo relation of the start element terminal \r
+ * @param hasConnector1 connector to connection attachment relation to use for element1 and connectionPoint1\r
+ * @param element2 element to end the connection at\r
+ * @param connectionPoint2 STR.ConnectedTo relation of the end element terminal\r
+ * @param hasConnector2 connector to connection attachment relation to use for element2 and connectionPoint2\r
+ * @param corners the corners to create for the connection\r
+ * @return the created diagram connection resource\r
+ * @throws DatabaseException\r
+ */\r
+ public Resource createConnectionWithCorners(WriteGraph graph, Resource connectionType,\r
+ Resource element1, Resource connectionPoint1, Resource hasConnector1, Resource element2,\r
+ Resource connectionPoint2, Resource hasConnector2, List<Point2D> corners)\r
+ throws DatabaseException {\r
+ DiagramResource DIA = br.DIA;\r
+\r
+ if (corners.size() == 1)\r
+ throw new UnsupportedOperationException("1 corner currently not supported");\r
+\r
+ Resource connection = newInstance(g, connectionType);\r
+ Resource connector1 = newConnector(connection, hasConnector1);\r
+ Resource connector2 = newConnector(connection, hasConnector2);\r
+ graph.claim(element1, connectionPoint1, connector1);\r
+ graph.claim(element2, connectionPoint2, connector2);\r
+\r
+ if (corners.size() > 1) {\r
+ boolean horizontal;\r
+ Resource previousRouteNode = connector1;\r
+ for (int i=0; i<corners.size()-1; ++i) {\r
+ Point2D p = corners.get(i);\r
+ Point2D p1 = corners.get(i+1);\r
+ horizontal = Math.abs(p1.getY() - p.getY()) < Math.abs(p1.getX() - p.getX());\r
+ Resource routeLine = ConnectionUtil.createRouteline(graph, connection, horizontal ? p.getY() : p.getX(), horizontal);\r
+ graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, routeLine);\r
+ previousRouteNode = routeLine;\r
+ }\r
+ graph.claim(previousRouteNode, DIA.AreConnected, DIA.AreConnected, connector2);\r
+ } else {\r
+ graph.claim(connector1, DIA.AreConnected, DIA.AreConnected, connector2);\r
+ }\r
+\r
+ return connection;\r
+ }\r
+\r
+ public Resource createConnectionWithSingleLine(WriteGraph graph, Resource connectionType,\r
+ Collection<Triple<Resource,Resource,Resource>> endpoints,\r
+ double coordinate, boolean horizontal)\r
+ throws DatabaseException {\r
+\r
+ DiagramResource DIA = br.DIA;\r
+\r
+ Resource connection = newInstance(g, connectionType);\r
+\r
+ Resource routeLine = ConnectionUtil.createRouteline(graph, connection, coordinate, horizontal);\r
+\r
+ for(Triple<Resource,Resource,Resource> endpoint : endpoints) {\r
+ Resource connector = newConnector(connection, endpoint.third); \r
+ graph.claim(endpoint.first, endpoint.second, connector);\r
+ graph.claim(routeLine, DIA.AreConnected, DIA.AreConnected, connector);\r
+ }\r
+\r
+ return connection;\r
+ \r
+ }\r
+\r
+ public Resource createConnection(WriteGraph graph, Resource connectionType,\r
+ List<Triple<Resource,Resource,Resource>> terminals,\r
+ List<Pair<Double, Boolean>> routeLines,\r
+ List<Pair<Integer,Integer>> connections) throws DatabaseException {\r
+\r
+ DiagramResource DIA = br.DIA;\r
+\r
+ Resource connection = newInstance(g, connectionType);\r
+\r
+ Resource[] parts = new Resource[terminals.size() + routeLines.size()];\r
+ \r
+ int index = 0;\r
+ \r
+ for(Triple<Resource,Resource,Resource> terminal : terminals) {\r
+ Resource connector = newConnector(connection, terminal.third); \r
+ graph.claim(terminal.first, terminal.second, connector);\r
+ parts[index++] = connector;\r
+ }\r
+\r
+ for(Pair<Double, Boolean> routeLine : routeLines) {\r
+ Resource r = ConnectionUtil.createRouteline(graph, connection, routeLine.first, routeLine.second);\r
+ parts[index++] = r;\r
+ }\r
+ \r
+// System.err.println("Connect " + parts.length + " parts.");\r
+ \r
+ for(Pair<Integer,Integer> conn : connections) {\r
+// System.err.println("-" + conn.first + " " + conn.second);\r
+ Resource part1 = parts[conn.first];\r
+ Resource part2 = parts[conn.second];\r
+ graph.claim(part1, DIA.AreConnected, DIA.AreConnected, part2);\r
+ }\r
+\r
+ return connection;\r
+ \r
+ }\r
+\r
+ /**\r
+ * @param graph\r
+ * @param connection\r
+ * @param pos\r
+ * @param isHorizontal\r
+ * @return new route line that is attached to the specified diagram connection\r
+ * @throws DatabaseException\r
+ */\r
+ public static Resource createRouteline(WriteGraph graph, Resource connection, double pos, boolean isHorizontal) throws DatabaseException {\r
+ Layer0 L0 = Layer0.getInstance(graph);\r
+ DiagramResource DIA = DiagramResource.getInstance(graph);\r
+ Resource routeLine = graph.newResource();\r
+ graph.claim(routeLine, L0.InstanceOf, null, DIA.RouteLine);\r
+ graph.addLiteral(routeLine, DIA.HasPosition, DIA.HasPosition_Inverse, L0.Double, pos, Bindings.DOUBLE);\r
+ graph.addLiteral(routeLine, DIA.IsHorizontal, DIA.IsHorizontal_Inverse, L0.Boolean, isHorizontal, Bindings.BOOLEAN);\r
+ graph.claim(connection, DIA.HasInteriorRouteNode, DIA.HasInteriorRouteNode_Inverse, routeLine);\r
+ return routeLine;\r
+ }\r
+\r
+ /**\r
+ * @param graph database write-only access\r
+ * @param type type of the created resource\r
+ * @return new instance of type\r
+ * @throws DatabaseException\r
+ */\r
+ private Resource newInstance(WriteOnlyGraph graph, Resource type) throws DatabaseException {\r
+ Resource connection = graph.newResource();\r
+ g.claim(connection, br.L0.InstanceOf, null, type);\r
+ return connection;\r
+ }\r
+\r
+}\r