diff --git a/src/main/java/org/javarosa/core/model/TriggerableDag.java b/src/main/java/org/javarosa/core/model/TriggerableDag.java index 4ea6f4e7a..47e1f65a6 100644 --- a/src/main/java/org/javarosa/core/model/TriggerableDag.java +++ b/src/main/java/org/javarosa/core/model/TriggerableDag.java @@ -332,7 +332,28 @@ public class TriggerableDag { message += "\nThe following nodes are likely involved in the loop:" + hints; } - throw new IllegalStateException(message); + + // Generate Mermaid diagram + StringBuilder mermaid = new StringBuilder(); + mermaid.append("graph LR\n"); + Set nodes = new HashSet<>(); + for (QuickTriggerable qt : triggerables) { + for (TreeReference target : qt.getTargets()) { + String to = target.toString(true).replace("/", "_").replace("[", "_").replace("]", "_").replace("@", "_"); + nodes.add(to); + for (TreeReference trigger : qt.getTriggerable().getTriggers()) { + String from = trigger.toString(true).replace("/", "_").replace("[", "_").replace("]", "_").replace("@", "_"); + nodes.add(from); + String label = target.getNameLast(); + mermaid.append(from).append(" -->|").append(label).append("| ").append(to).append("\n"); + } + } + } + logger.error("Mermaid diagram of cycle (limited to nodes in error message):\n{}", mermaid.toString()); + + message += "\n\nMermaid diagram of cycle (limited to nodes in error message):\n" + mermaid.toString(); + + throw new IllegalStateException(message); } void reportDependencyCycles() { @@ -396,7 +417,18 @@ public class TriggerableDag { } logger.error("XForm Parse Error: {}", b.toString()); - throw new RuntimeException("Dependency cycles amongst the xpath expressions in relevant/calculate"); + // Generate Mermaid diagram + StringBuilder mermaid = new StringBuilder(); + mermaid.append("graph LR\n"); + for (TreeReference[] edge : edges) { + String from = edge[0].toString(true).replace("/", "_").replace("[", "_").replace("]", "_").replace("@", "_"); + String to = edge[1].toString(true).replace("/", "_").replace("[", "_").replace("]", "_").replace("@", "_"); + String label = edge[1].getNameLast(); // Name of the target reference as "column" + mermaid.append(from).append(" -->|").append(label).append("| ").append(to).append("\n"); + } + logger.error("Mermaid diagram of cycle (limited to nodes in error message):\n{}", mermaid.toString()); + + throw new RuntimeException("Dependency cycles amongst the xpath expressions in relevant/calculate\n" + b.toString() + "\n\nMermaid diagram of cycle (limited to nodes in error message):\n" + mermaid.toString()); } } @@ -749,4 +781,4 @@ public class TriggerableDag { public void disablePredicateCaching() { predicateCaching = false; } -} +} \ No newline at end of file