You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by da...@apache.org on 2026/05/12 13:02:41 UTC
(camel) 05/07: CAMEL-23485: camel-diagram - Add scope boxes to ASCII art renderer
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-23485-ascii-art-renderer
in repository https://gitbox.apache.org/repos/asf/camel.git
commit 3363f7c54f91154a7d7584aade64dc24f0d87cba
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Tue May 12 14:29:20 2026 +0200
CAMEL-23485: camel-diagram - Add scope boxes to ASCII art renderer
Co-Authored-By: Claude Opus 4.6 (1M context) <no...@anthropic.com>
---
.../camel-diagram/src/main/docs/diagram.adoc | 3 +-
.../camel/diagram/RouteDiagramAsciiRenderer.java | 52 +++++++++++++++++++++-
.../org/apache/camel/diagram/RouteDiagramTest.java | 22 +++++++++
3 files changed, 74 insertions(+), 3 deletions(-)
diff --git a/components/camel-diagram/src/main/docs/diagram.adoc b/components/camel-diagram/src/main/docs/diagram.adoc
index e8e6ca9e417a..c9525206ede9 100644
--- a/components/camel-diagram/src/main/docs/diagram.adoc
+++ b/components/camel-diagram/src/main/docs/diagram.adoc
@@ -181,4 +181,5 @@ route1
+----------------------+ +----------------------+
----
-NOTE: Scope boxes (dotted boundaries for filter, split, etc.) are not included in ASCII art rendering.
+Scope boxes (for filter, split, doTry, etc.) are rendered with dashed borders using `:` for vertical
+and `- - -` for horizontal lines.
diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java
index d79cf001bf54..86c4c6ba1226 100644
--- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java
+++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramAsciiRenderer.java
@@ -20,10 +20,13 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import org.apache.camel.diagram.RouteDiagramLayoutEngine.Bounds;
import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutNode;
import org.apache.camel.diagram.RouteDiagramLayoutEngine.LayoutRoute;
+import org.apache.camel.diagram.RouteDiagramLayoutEngine.TreeNode;
import static org.apache.camel.diagram.RouteDiagramLayoutEngine.PADDING;
+import static org.apache.camel.diagram.RouteDiagramLayoutEngine.SCOPE_BOX_PAD;
/**
* Renders route diagrams as plain ASCII art text.
@@ -73,6 +76,12 @@ public class RouteDiagramAsciiRenderer {
}
drawText(grid, labelRow, toCol(PADDING), label);
+ for (LayoutNode ln : lr.nodes) {
+ if (ln.treeNode != null && RouteDiagramLayoutEngine.hasScope(ln.treeNode)) {
+ drawScopeBox(grid, ln);
+ }
+ }
+
for (LayoutNode ln : lr.nodes) {
if (ln.parentNode != null) {
if (ln.connectFromMerge) {
@@ -132,7 +141,7 @@ public class RouteDiagramAsciiRenderer {
int fromCx = centerCol(from);
int fromBottom = toRow(from.y) + boxHeight(from);
int toCx = centerCol(to);
- int toTop = toRow(to.y);
+ int toTop = getTopRow(to);
drawArrowPath(grid, fromCx, fromBottom, toCx, toTop);
}
@@ -141,7 +150,7 @@ public class RouteDiagramAsciiRenderer {
int fromCx = toCol(to.mergeCx);
int fromRow = toRow(to.mergeY);
int toCx = centerCol(to);
- int toTop = toRow(to.y);
+ int toTop = getTopRow(to);
drawArrowPath(grid, fromCx, fromRow, toCx, toTop);
}
@@ -178,6 +187,45 @@ public class RouteDiagramAsciiRenderer {
}
}
+ private void drawScopeBox(char[][] grid, LayoutNode scopeNode) {
+ TreeNode tn = scopeNode.treeNode;
+ Bounds bounds = new Bounds(
+ scopeNode.x, scopeNode.y,
+ scopeNode.x + nodeWidth, scopeNode.y + scopeNode.height);
+ for (TreeNode child : tn.children) {
+ RouteDiagramLayoutEngine.expandBoundsForBox(child, bounds, nodeWidth);
+ }
+
+ int col1 = toCol(bounds.minX - SCOPE_BOX_PAD);
+ int row1 = toRow(bounds.minY - SCOPE_BOX_PAD);
+ int col2 = toCol(bounds.maxX + SCOPE_BOX_PAD);
+ int row2 = toRow(bounds.maxY + SCOPE_BOX_PAD);
+
+ drawDashedHLine(grid, row1, col1, col2);
+ drawDashedHLine(grid, row2, col1, col2);
+ for (int r = row1 + 1; r < row2; r++) {
+ setChar(grid, r, col1, ':');
+ setChar(grid, r, col2, ':');
+ }
+ }
+
+ private void drawDashedHLine(char[][] grid, int row, int col1, int col2) {
+ for (int c = col1; c <= col2; c++) {
+ if (c == col1 || c == col2) {
+ setChar(grid, row, c, '+');
+ } else if ((c - col1) % 2 == 0) {
+ setChar(grid, row, c, '-');
+ }
+ }
+ }
+
+ private int getTopRow(LayoutNode node) {
+ if (node.treeNode != null && RouteDiagramLayoutEngine.hasScope(node.treeNode)) {
+ return toRow(node.y - SCOPE_BOX_PAD);
+ }
+ return toRow(node.y);
+ }
+
private int centerCol(LayoutNode node) {
return toCol(node.x + nodeWidth / 2);
}
diff --git a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
index 40d662a0b330..7024198eef7d 100644
--- a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
+++ b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
@@ -949,6 +949,28 @@ class RouteDiagramTest {
assertTrue(result.contains("log:a"));
}
+ @Test
+ void testAsciiDiagramWithScopeBox() {
+ RouteInfo route = new RouteInfo();
+ route.routeId = "route1";
+ route.nodes.add(node("from", "direct:start", 0));
+ route.nodes.add(node("filter", "filter[header(x)]", 1));
+ route.nodes.add(node("log", "log[filtered]", 2));
+ route.nodes.add(node("to", "to[mock:end]", 1));
+
+ RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
+ LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING);
+
+ RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth());
+ String result = renderer.renderDiagram(List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP);
+
+ assertTrue(result.contains("filter[header(x)]"));
+ assertTrue(result.contains("log[filtered]"));
+ assertTrue(result.contains("to[mock:end]"));
+ assertTrue(result.contains(":"), "Scope box should have dashed vertical borders");
+ assertTrue(result.contains("+ -"), "Scope box should have dashed horizontal borders");
+ }
+
@Test
void testAsciiWrapTextShort() {
List<String> lines = RouteDiagramAsciiRenderer.wrapText("timer:tick", 20);