Repository: workcraft/workcraft
Issue: Multiple STG implicit places with the same name

Currently Workcraft allows creating several implicit place arcs between a pair of transitions. However, the implicit places are referenced as <from_transition,to_transition> in .g format. As a result it is impossible to distinguish implicit places on the arcs between the same pair of transitions.

To avoid confusion we need to forbid several connections between transitions.


Changed files:
PetriNetPlugin/src/org/workcraft/plugins/petri/PetriNetUtils.java, STGPlugin/src/org/workcraft/plugins/stg/STG.java, STGPlugin/src/org/workcraft/plugins/stg/tools/MakePlacesImplicitTool.java, STGPlugin/src/org/workcraft/plugins/stg/VisualSTG.java

diff --git a/PetriNetPlugin/src/org/workcraft/plugins/petri/PetriNetUtils.java b/PetriNetPlugin/src/org/workcraft/plugins/petri/PetriNetUtils.java
index 5a9e17827..23d453eaa 100644
--- a/PetriNetPlugin/src/org/workcraft/plugins/petri/PetriNetUtils.java
+++ b/PetriNetPlugin/src/org/workcraft/plugins/petri/PetriNetUtils.java
@@ -9,7 +9,6 @@ import java.util.LinkedList;
 import org.workcraft.dom.Connection;
 import org.workcraft.dom.Container;
 import org.workcraft.dom.Node;
-import org.workcraft.dom.visual.AbstractVisualModel;
 import org.workcraft.dom.visual.ConnectionHelper;
 import org.workcraft.dom.visual.Replica;
 import org.workcraft.dom.visual.TransformHelper;
@@ -312,7 +311,7 @@ public class PetriNetUtils {
         return positionInRootSpace;
     }
 
-    public static boolean hasReadArcConnection(AbstractVisualModel visualModel, Node first, Node second) {
+    public static boolean hasReadArcConnection(VisualModel visualModel, Node first, Node second) {
         boolean found = false;
         VisualPlace place = null;
         VisualTransition transition = null;
@@ -341,7 +340,7 @@ public class PetriNetUtils {
         return found;
     }
 
-    public static boolean hasProducingArcConnection(AbstractVisualModel visualModel, Node first, Node second) {
+    public static boolean hasProducingArcConnection(VisualModel visualModel, Node first, Node second) {
         boolean found = false;
         VisualPlace place = null;
         VisualTransition transition = null;
@@ -370,7 +369,7 @@ public class PetriNetUtils {
         return found;
     }
 
-    public static boolean hasConsumingArcConnection(AbstractVisualModel visualModel, Node first, Node second) {
+    public static boolean hasConsumingArcConnection(VisualModel visualModel, Node first, Node second) {
         boolean found = false;
         VisualPlace place = null;
         VisualTransition transition = null;
@@ -399,6 +398,23 @@ public class PetriNetUtils {
         return found;
     }
 
+    public static boolean hasImplicitPlaceArcConnection(VisualModel visualModel, Node first, Node second) {
+        boolean found = false;
+        VisualTransition predTransition = null;
+        VisualTransition succTransition = null;
+        if (first instanceof VisualTransition) {
+            predTransition = (VisualTransition) first;
+        }
+        if (second instanceof VisualTransition) {
+            succTransition = (VisualTransition) second;
+        }
+        if ((predTransition != null) && (succTransition != null)) {
+            Connection connection = visualModel.getConnection(predTransition, succTransition);
+            found = connection instanceof VisualConnection;
+        }
+        return found;
+    }
+
     public static HashSet<VisualConnection> getVisualConsumingArcs(VisualModel visualModel) {
         HashSet<VisualConnection> connections = new HashSet<>();
         for (VisualConnection connection: Hierarchy.getDescendantsOfType(visualModel.getRoot(), VisualConnection.class)) {
diff --git a/STGPlugin/src/org/workcraft/plugins/stg/STG.java b/STGPlugin/src/org/workcraft/plugins/stg/STG.java
index 255226271..022528f6a 100644
--- a/STGPlugin/src/org/workcraft/plugins/stg/STG.java
+++ b/STGPlugin/src/org/workcraft/plugins/stg/STG.java
@@ -360,15 +360,16 @@ public class STG extends AbstractMathModel implements STGModel {
     @Override
     public String getNodeReference(NamespaceProvider provider, Node node) {
         if (node instanceof STGPlace) {
-            if (((STGPlace) node).isImplicit()) {
+            if (node != null && ((STGPlace) node).isImplicit()) {
                 Set<Node> preset = getPreset(node);
                 Set<Node> postset = getPostset(node);
                 if (!(preset.size() == 1 && postset.size() == 1)) {
-                    throw new RuntimeException("An implicit place cannot have more that one transition in its preset or postset.");
+                    throw new RuntimeException("An implicit place must have one transition in its preset and one transition in its postset.");
                 }
-
-                return "<" + NamespaceHelper.hierarchicalToFlatName(referenceManager.getNodeReference(null, preset.iterator().next()))
-                        + "," + NamespaceHelper.hierarchicalToFlatName(referenceManager.getNodeReference(null, postset.iterator().next())) + ">";
+                String predNodeRef = referenceManager.getNodeReference(null, preset.iterator().next());
+                String succNodeRef = referenceManager.getNodeReference(null, postset.iterator().next());
+                return "<" + NamespaceHelper.hierarchicalToFlatName(predNodeRef) + ","
+                        + NamespaceHelper.hierarchicalToFlatName(succNodeRef) + ">";
             }
         }
         return super.getNodeReference(provider, node);
diff --git a/STGPlugin/src/org/workcraft/plugins/stg/VisualSTG.java b/STGPlugin/src/org/workcraft/plugins/stg/VisualSTG.java
index 40a3a960d..7598d00b2 100644
--- a/STGPlugin/src/org/workcraft/plugins/stg/VisualSTG.java
+++ b/STGPlugin/src/org/workcraft/plugins/stg/VisualSTG.java
@@ -121,6 +121,9 @@ public class VisualSTG extends AbstractVisualModel {
         if (PetriNetUtils.hasConsumingArcConnection(this, first, second)) {
             throw new InvalidConnectionException("This consuming arc already exists.");
         }
+        if (PetriNetUtils.hasImplicitPlaceArcConnection(this, first, second)) {
+            throw new InvalidConnectionException("This implicit place arc already exists.");
+        }
     }
 
     @Override
@@ -282,32 +285,34 @@ public class VisualSTG extends AbstractVisualModel {
         Collection<Node> postset = getPostset(place);
         Collection<Replica> replicas = place.getReplicas();
         if ((preset.size() == 1) && (postset.size() == 1) && replicas.isEmpty()) {
-            final STGPlace stgPlace = (STGPlace) place.getReferencedPlace();
-            stgPlace.setImplicit(true);
             VisualComponent first = (VisualComponent) preset.iterator().next();
             VisualComponent second = (VisualComponent) postset.iterator().next();
-
-            VisualConnection con1 = null;
-            VisualConnection con2 = null;
-            Collection<Connection> connections = new ArrayList<>(getConnections(place));
-            for (Connection con: connections) {
-                if (con.getFirst() == place) {
-                    con2 = (VisualConnection) con;
-                } else if (con.getSecond() == place) {
-                    con1 = (VisualConnection) con;
+            if (!PetriNetUtils.hasImplicitPlaceArcConnection(this, first, second)) {
+                final STGPlace stgPlace = (STGPlace) place.getReferencedPlace();
+                stgPlace.setImplicit(true);
+
+                VisualConnection con1 = null;
+                VisualConnection con2 = null;
+                Collection<Connection> connections = new ArrayList<>(getConnections(place));
+                for (Connection con: connections) {
+                    if (con.getFirst() == place) {
+                        con2 = (VisualConnection) con;
+                    } else if (con.getSecond() == place) {
+                        con1 = (VisualConnection) con;
+                    }
                 }
+                MathConnection refCon1 = con1.getReferencedConnection();
+                MathConnection refCon2 = con2.getReferencedConnection();
+                VisualImplicitPlaceArc connection = new VisualImplicitPlaceArc(first, second, refCon1, refCon2, (STGPlace) place.getReferencedPlace());
+                Container parent = Hierarchy.getNearestAncestor(Hierarchy.getCommonParent(first, second), Container.class);
+                parent.add(connection);
+                if (preserveConnectionShape) {
+                    LinkedList<Point2D> locations = ConnectionHelper.getMergedControlPoints(place, con1, con2);
+                    ConnectionHelper.addControlPoints(connection, locations);
+                }
+                // Remove explicit place, all its connections will get removed automatically by the hanging connection remover
+                remove(place);
             }
-            MathConnection refCon1 = con1.getReferencedConnection();
-            MathConnection refCon2 = con2.getReferencedConnection();
-            VisualImplicitPlaceArc connection = new VisualImplicitPlaceArc(first, second, refCon1, refCon2, (STGPlace) place.getReferencedPlace());
-            Container parent = Hierarchy.getNearestAncestor(Hierarchy.getCommonParent(first, second), Container.class);
-            parent.add(connection);
-            if (preserveConnectionShape) {
-                LinkedList<Point2D> locations = ConnectionHelper.getMergedControlPoints(place, con1, con2);
-                ConnectionHelper.addControlPoints(connection, locations);
-            }
-            // Remove explicit place, all its connections will get removed automatically by the hanging connection remover
-            remove(place);
         }
     }
 
diff --git a/STGPlugin/src/org/workcraft/plugins/stg/tools/MakePlacesImplicitTool.java b/STGPlugin/src/org/workcraft/plugins/stg/tools/MakePlacesImplicitTool.java
index 110a45165..ef9b6c974 100644
--- a/STGPlugin/src/org/workcraft/plugins/stg/tools/MakePlacesImplicitTool.java
+++ b/STGPlugin/src/org/workcraft/plugins/stg/tools/MakePlacesImplicitTool.java
@@ -1,11 +1,16 @@
 package org.workcraft.plugins.stg.tools;
 
+import java.util.Collection;
 import java.util.HashSet;
 
 import org.workcraft.NodeTransformer;
 import org.workcraft.TransformationTool;
 import org.workcraft.dom.Model;
 import org.workcraft.dom.Node;
+import org.workcraft.dom.visual.Replica;
+import org.workcraft.dom.visual.VisualComponent;
+import org.workcraft.dom.visual.VisualModel;
+import org.workcraft.plugins.petri.PetriNetUtils;
 import org.workcraft.plugins.petri.VisualPlace;
 import org.workcraft.plugins.stg.STG;
 import org.workcraft.plugins.stg.VisualSTG;
@@ -35,7 +40,21 @@ public class MakePlacesImplicitTool extends TransformationTool implements NodeTr
 
     @Override
     public boolean isEnabled(WorkspaceEntry we, Node node) {
-        return true;
+        if (node instanceof VisualPlace) {
+            VisualModel model = we.getModelEntry().getVisualModel();
+            VisualPlace place = (VisualPlace) node;
+            Collection<Node> preset = model.getPreset(place);
+            Collection<Node> postset = model.getPostset(place);
+            Collection<Replica> replicas = place.getReplicas();
+            if ((preset.size() == 1) && (postset.size() == 1) && replicas.isEmpty()) {
+                VisualComponent first = (VisualComponent) preset.iterator().next();
+                VisualComponent second = (VisualComponent) postset.iterator().next();
+                if (!PetriNetUtils.hasImplicitPlaceArcConnection(model, first, second)) {
+                    return true;
+                }
+            }
+        }
+        return false;
     }
 
     @Override