Repository: workcraft/workcraft
Issue: In DFS wagging unselected intermediate arcs are not replicated

To reproduce:
1. Create an DFS with a couple of connected nodes.
2. Select the nodes, but not the connection.
3. Apply wagging tool.

Observed behaviour:
Only the nodes are replicated, but not the connection.

Expected behaviour:
Replicate the nodes and also the arcs whose source and destination nodes are both selected.


Changed files:
DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGeneratorTool.java, DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGenerator.java, WorkcraftCore/src/org/workcraft/util/Hierarchy.java

diff --git a/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGenerator.java b/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGenerator.java
index 81b7f39de..b362f0410 100644
--- a/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGenerator.java
+++ b/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGenerator.java
@@ -12,7 +12,6 @@ import org.workcraft.dom.math.MathConnection;
 import org.workcraft.dom.math.MathNode;
 import org.workcraft.dom.visual.BoundingBoxHelper;
 import org.workcraft.dom.visual.VisualComponent;
-import org.workcraft.dom.visual.VisualNode;
 import org.workcraft.dom.visual.VisualTransformableNode;
 import org.workcraft.dom.visual.connections.ControlPoint;
 import org.workcraft.dom.visual.connections.Polyline;
@@ -36,6 +35,7 @@ import org.workcraft.plugins.dfs.VisualPopRegister;
 import org.workcraft.plugins.dfs.VisualPushRegister;
 import org.workcraft.plugins.dfs.VisualRegister;
 import org.workcraft.util.Hierarchy;
+import org.workcraft.util.Pair;
 
 public class WaggingGenerator {
     private final VisualDfs dfs;
@@ -59,21 +59,28 @@ public class WaggingGenerator {
         this.count = count;
 
         for (Node node: dfs.getSelection()) {
-            if (node instanceof VisualNode) {
-                if (node instanceof VisualComponent) {
-                    selectedComponents.add((VisualComponent) node);
-                } else if (node instanceof VisualConnection) {
-                    selectedConnections.add((VisualConnection) node);
-                }
+            if (node instanceof VisualComponent) {
+                selectedComponents.add((VisualComponent) node);
+            } else if (node instanceof VisualConnection) {
+                selectedConnections.add((VisualConnection) node);
+            }
+        }
+        for (VisualConnection connection: Hierarchy.getDescendantsOfType(dfs.getRoot(), VisualConnection.class)) {
+            if (selectedComponents.contains(connection.getFirst()) && selectedComponents.contains(connection.getSecond())) {
+                selectedConnections.add(connection);
             }
         }
     }
 
     public void run() {
         replicateSelection();
-        insertInterface();
-        insertPushControl();
-        insertPopControl();
+        Pair<Boolean, Boolean> hasInterface = insertInterface();
+        if (hasInterface.getFirst()) {
+            insertPushControl();
+        }
+        if (hasInterface.getSecond()) {
+            insertPopControl();
+        }
         createGroups();
     }
 
@@ -84,23 +91,25 @@ public class WaggingGenerator {
         for (VisualComponent component: selectedComponents) {
             bb = BoundingBoxHelper.union(bb, component.getBoundingBox());
         }
-        double step = Math.ceil(bb.getHeight());
-        for (int i = 0; i < count; ++i) {
-            HashMap<VisualComponent, VisualComponent> mapComponentToReplica = new HashMap<>();
-            WaggingData waggingData = new WaggingData();
-            for (VisualComponent component: selectedComponents) {
-                VisualComponent replicaComponenet = replicateComponent(component);
-                if (replicaComponenet != null) {
-                    replicaComponenet.setY(replicaComponenet.getY() + step * (2 * i + 1 - count) / 2);
-                    mapComponentToReplica.put(component, replicaComponenet);
-                    replicaToOriginalMap.put(replicaComponenet, component);
-                    waggingData.dataComponents.add(replicaComponenet);
+        if (bb != null) {
+            double step = Math.ceil(bb.getHeight());
+            for (int i = 0; i < count; ++i) {
+                HashMap<VisualComponent, VisualComponent> mapComponentToReplica = new HashMap<>();
+                WaggingData waggingData = new WaggingData();
+                for (VisualComponent component: selectedComponents) {
+                    VisualComponent replicaComponenet = replicateComponent(component);
+                    if (replicaComponenet != null) {
+                        replicaComponenet.setY(replicaComponenet.getY() + step * (2 * i + 1 - count) / 2);
+                        mapComponentToReplica.put(component, replicaComponenet);
+                        replicaToOriginalMap.put(replicaComponenet, component);
+                        waggingData.dataComponents.add(replicaComponenet);
+                    }
                 }
+                for (VisualConnection connection: selectedConnections) {
+                    replicateConnection(connection, mapComponentToReplica);
+                }
+                wagging.add(waggingData);
             }
-            for (VisualConnection connection: selectedConnections) {
-                replicateConnection(connection, mapComponentToReplica);
-            }
-            wagging.add(waggingData);
         }
     }
 
@@ -146,7 +155,9 @@ public class WaggingGenerator {
         return replica;
     }
 
-    private void insertInterface() {
+    private Pair<Boolean, Boolean> insertInterface() {
+        boolean hasPred = false;
+        boolean hasSucc = false;
         for (WaggingData waggingData: wagging) {
             waggingData.pushRegisters.clear();
             waggingData.popRegisters.clear();
@@ -158,6 +169,7 @@ public class WaggingGenerator {
                     createConnection((VisualComponent) pred, push);
                     createConnection(push, cur);
                     waggingData.pushRegisters.add(push);
+                    hasPred = true;
                 }
                 for (Node succ: dfs.getPostset(replicaToOriginalMap.get(cur))) {
                     if (selectedComponents.contains(succ)) continue;
@@ -166,9 +178,11 @@ public class WaggingGenerator {
                     createConnection(cur, pop);
                     createConnection(pop, (VisualComponent) succ);
                     waggingData.popRegisters.add(pop);
+                    hasSucc = true;
                 }
             }
         }
+        return Pair.of(hasPred,  hasSucc);
     }
 
     private void insertPushControl() {
@@ -268,6 +282,10 @@ public class WaggingGenerator {
     }
 
     private void createGroups() {
+        dfs.selectNone();
+        for (VisualComponent component: selectedComponents) {
+            dfs.addToSelection(component);
+        }
         dfs.deleteSelection();
         // data components
         ArrayList<Node> dataNodes = new ArrayList<>();
diff --git a/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGeneratorTool.java b/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGeneratorTool.java
index b1c668eda..fd0b1d31b 100644
--- a/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGeneratorTool.java
+++ b/DfsPlugin/src/org/workcraft/plugins/dfs/tools/WaggingGeneratorTool.java
@@ -4,8 +4,10 @@ import javax.swing.JOptionPane;
 
 import org.workcraft.Framework;
 import org.workcraft.Tool;
+import org.workcraft.dom.Node;
 import org.workcraft.plugins.dfs.Dfs;
 import org.workcraft.plugins.dfs.VisualDfs;
+import org.workcraft.plugins.dfs.VisualRegister;
 import org.workcraft.workspace.WorkspaceEntry;
 
 public class WaggingGeneratorTool implements Tool {
@@ -28,10 +30,16 @@ public class WaggingGeneratorTool implements Tool {
     @Override
     public void run(WorkspaceEntry we) {
         final VisualDfs dfs = (VisualDfs) we.getModelEntry().getVisualModel();
-        if (dfs.getSelection().size() < 1) {
+        int selectedRegisterCount = 0;
+        for (Node node: dfs.getSelection()) {
+            if (node instanceof VisualRegister) {
+                selectedRegisterCount++;
+            }
+        }
+        if (selectedRegisterCount < 1) {
             final Framework framework = Framework.getInstance();
             JOptionPane.showMessageDialog(framework.getMainWindow(),
-                    "Select at least one component for wagging!", "Wagging", JOptionPane.ERROR_MESSAGE);
+                    "Select at least one register for wagging!", "Wagging", JOptionPane.ERROR_MESSAGE);
         } else {
             int count = getWayCount();
             if (count >= 2) {
diff --git a/WorkcraftCore/src/org/workcraft/util/Hierarchy.java b/WorkcraftCore/src/org/workcraft/util/Hierarchy.java
index d1c945f6f..130238c52 100644
--- a/WorkcraftCore/src/org/workcraft/util/Hierarchy.java
+++ b/WorkcraftCore/src/org/workcraft/util/Hierarchy.java
@@ -88,15 +88,14 @@ public class Hierarchy {
 
     public static Node getCommonParent(Node... nodes) {
         ArrayList<Node[]> paths = new ArrayList<>(nodes.length);
-        int minPathLength = Integer.MAX_VALUE;
+        int minPathLength = -1;
         for (Node node : nodes) {
             final Node[] path = getPath(node);
-            if (minPathLength > path.length) {
+            if ((minPathLength < 0) || (minPathLength > path.length)) {
                 minPathLength = path.length;
             }
             paths.add(path);
         }
-
         Node result = null;
         for (int i = 0; i < minPathLength; i++) {
             Node node = paths.get(0)[i];