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/27 21:00:15 UTC
(camel) 03/08: CAMEL-23631: Highlight arrows/lines instead of boxes in route diagram
This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch CAMEL-23631-route-diagram-highlight-error-path
in repository https://gitbox.apache.org/repos/asf/camel.git
commit d9cc166ec214db590febcc54250773c2b3156c79
Author: Claus Ibsen <cl...@gmail.com>
AuthorDate: Wed May 27 21:55:46 2026 +0200
CAMEL-23631: Highlight arrows/lines instead of boxes in route diagram
Co-Authored-By: Claude Opus 4.6 <no...@anthropic.com>
---
.../camel/diagram/RouteDiagramAsciiRenderer.java | 72 +++++++++++++++-------
.../apache/camel/diagram/RouteDiagramRenderer.java | 20 ++----
.../org/apache/camel/diagram/RouteDiagramTest.java | 34 ++++++++--
3 files changed, 84 insertions(+), 42 deletions(-)
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 150fb77a736a..a09e23bb7e4c 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
@@ -188,22 +188,26 @@ public class RouteDiagramAsciiRenderer {
for (LayoutNode ln : lr.nodes) {
if (ln.parentNode != null) {
+ boolean highlightArrow = highlightedNodeIds != null
+ && isHighlighted(ln, highlightedNodeIds)
+ && isHighlighted(ln.parentNode, highlightedNodeIds);
if (ln.connectFromMerge) {
- drawMergeArrow(grid, ln);
+ drawMergeArrow(grid, ln, highlightArrow, highlightStyle);
} else {
- drawArrow(grid, ln.parentNode, ln);
+ drawArrow(grid, ln.parentNode, ln, highlightArrow, highlightStyle);
}
}
}
for (LayoutNode ln : lr.nodes) {
drawNode(grid, ln);
- if (highlightedNodeIds != null && ln.id != null && highlightedNodeIds.contains(ln.id)) {
- recordHighlightPositions(grid, ln, highlightStyle);
- }
}
}
+ private static boolean isHighlighted(LayoutNode node, Set<String> highlightedNodeIds) {
+ return node.id != null && highlightedNodeIds.contains(node.id);
+ }
+
private void drawRoute(char[][] grid, LayoutRoute lr) {
drawRoute(grid, lr, null, null);
}
@@ -251,24 +255,13 @@ public class RouteDiagramAsciiRenderer {
}
}
- private void recordHighlightPositions(
- char[][] grid, LayoutNode node, RouteDiagramHelper.HighlightStyle style) {
- int col = toCol(node.x);
- int row = toRow(node.y);
- int innerWidth = boxWidth - 4;
- List<String> lines = rewrapText(node, innerWidth);
- int height = 2 + lines.size();
-
- CounterType ct = style == RouteDiagramHelper.HighlightStyle.FAIL
- ? CounterType.HIGHLIGHT_FAIL
- : CounterType.HIGHLIGHT_SUCCESS;
-
- for (int r = row; r < row + height && r < grid.length; r++) {
- counterPositions.add(new CounterPos(r, col, boxWidth, ct));
- }
+ private void drawArrow(char[][] grid, LayoutNode from, LayoutNode to) {
+ drawArrow(grid, from, to, false, null);
}
- private void drawArrow(char[][] grid, LayoutNode from, LayoutNode to) {
+ private void drawArrow(
+ char[][] grid, LayoutNode from, LayoutNode to,
+ boolean highlighted, RouteDiagramHelper.HighlightStyle highlightStyle) {
int fromCx = centerCol(from);
int fromBottom = toRow(from.y) + boxHeight(from);
int toCx = centerCol(to);
@@ -279,10 +272,19 @@ public class RouteDiagramAsciiRenderer {
boolean dashed = metrics && total == 0;
drawArrowPath(grid, fromCx, fromBottom, toCx, toTop, dashed);
+ if (highlighted) {
+ recordArrowHighlight(fromCx, fromBottom, toCx, toTop, highlightStyle);
+ }
drawCounters(grid, toCx, toTop, stat);
}
private void drawMergeArrow(char[][] grid, LayoutNode to) {
+ drawMergeArrow(grid, to, false, null);
+ }
+
+ private void drawMergeArrow(
+ char[][] grid, LayoutNode to,
+ boolean highlighted, RouteDiagramHelper.HighlightStyle highlightStyle) {
int fromCx = toCol(to.mergeCx);
int fromRow = toRow(to.mergeY);
int toCx = centerCol(to);
@@ -293,9 +295,37 @@ public class RouteDiagramAsciiRenderer {
boolean dashed = metrics && total == 0;
drawArrowPath(grid, fromCx, fromRow, toCx, toTop, dashed);
+ if (highlighted) {
+ recordArrowHighlight(fromCx, fromRow, toCx, toTop, highlightStyle);
+ }
drawCounters(grid, toCx, toTop, stat);
}
+ private void recordArrowHighlight(
+ int fromCx, int fromRow, int toCx, int toRow,
+ RouteDiagramHelper.HighlightStyle style) {
+ CounterType ct = style == RouteDiagramHelper.HighlightStyle.FAIL
+ ? CounterType.HIGHLIGHT_FAIL
+ : CounterType.HIGHLIGHT_SUCCESS;
+
+ if (fromCx == toCx) {
+ for (int r = fromRow; r < toRow; r++) {
+ counterPositions.add(new CounterPos(r, fromCx, 1, ct));
+ }
+ } else {
+ int midRow = fromRow + (toRow - fromRow) / 2;
+ for (int r = fromRow; r <= midRow; r++) {
+ counterPositions.add(new CounterPos(r, fromCx, 1, ct));
+ }
+ int minC = Math.min(fromCx, toCx);
+ int maxC = Math.max(fromCx, toCx);
+ counterPositions.add(new CounterPos(midRow, minC, maxC - minC + 1, ct));
+ for (int r = midRow; r < toRow; r++) {
+ counterPositions.add(new CounterPos(r, toCx, 1, ct));
+ }
+ }
+ }
+
private StatInfo resolveStatInfo(LayoutNode to) {
if (!metrics) {
return null;
diff --git a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
index a87b85001636..789d04a5b314 100644
--- a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
+++ b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
@@ -324,7 +324,7 @@ public class RouteDiagramRenderer {
}
for (LayoutNode ln : lr.nodes) {
- drawNode(g, ln, colors, highlightedNodeIds, highlightStyle);
+ drawNode(g, ln, colors);
}
}
@@ -417,22 +417,14 @@ public class RouteDiagramRenderer {
drawArrowFromMerge(g, to, colors, false, null);
}
- private void drawNode(
- Graphics2D g, LayoutNode node, DiagramColors colors,
- Set<String> highlightedNodeIds, RouteDiagramHelper.HighlightStyle highlightStyle) {
+ private void drawNode(Graphics2D g, LayoutNode node, DiagramColors colors) {
Color color = getNodeColor(node.type, colors);
g.setColor(color);
g.fillRoundRect(node.x, node.y, nodeWidth, node.height, ARC, ARC);
- boolean highlighted = highlightedNodeIds != null && isHighlighted(node, highlightedNodeIds);
- if (highlighted) {
- g.setColor(highlightColor(highlightStyle));
- g.setStroke(new BasicStroke(HIGHLIGHT_STROKE_WIDTH));
- } else {
- g.setColor(color.brighter());
- g.setStroke(new BasicStroke(BORDER_STROKE_WIDTH));
- }
+ g.setColor(color.brighter());
+ g.setStroke(new BasicStroke(BORDER_STROKE_WIDTH));
g.drawRoundRect(node.x, node.y, nodeWidth, node.height, ARC, ARC);
g.setColor(colors.getText());
@@ -458,10 +450,6 @@ public class RouteDiagramRenderer {
}
}
- private void drawNode(Graphics2D g, LayoutNode node, DiagramColors colors) {
- drawNode(g, node, colors, null, null);
- }
-
private void drawArrow(
Graphics2D g, LayoutNode from, LayoutNode to, DiagramColors colors,
boolean highlighted, RouteDiagramHelper.HighlightStyle highlightStyle) {
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 829d711b4cf4..4024de903f33 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
@@ -1308,7 +1308,7 @@ class RouteDiagramTest {
}
@Test
- void testAsciiDiagramHighlightOnlyTargetedNodes() {
+ void testAsciiDiagramHighlightArrowBetweenNodes() {
RouteInfo route = new RouteInfo();
route.routeId = "route1";
route.nodes.add(nodeWithId("from", "timer:tick", 0, "from1"));
@@ -1318,7 +1318,31 @@ class RouteDiagramTest {
RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING);
- // only highlight from1, not to1 or to2
+ // highlight from1 and to1 — the arrow between them should be highlighted
+ Set<String> highlighted = Set.of("from1", "to1");
+ RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth());
+ renderer.renderDiagram(
+ List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP,
+ highlighted, HighlightStyle.SUCCESS);
+
+ long highlightCount = renderer.getCounterPositions().stream()
+ .filter(cp -> cp.type() == RouteDiagramAsciiRenderer.CounterType.HIGHLIGHT_SUCCESS)
+ .count();
+ assertTrue(highlightCount > 0, "Should have highlight positions for arrow between from1 and to1");
+ }
+
+ @Test
+ void testAsciiDiagramNoHighlightForSingleNode() {
+ RouteInfo route = new RouteInfo();
+ route.routeId = "route1";
+ route.nodes.add(nodeWithId("from", "timer:tick", 0, "from1"));
+ route.nodes.add(nodeWithId("to", "log:a", 1, "to1"));
+ route.nodes.add(nodeWithId("to", "log:b", 1, "to2"));
+
+ RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
+ LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING);
+
+ // only from1 highlighted — no arrow between two highlighted nodes
Set<String> highlighted = Set.of("from1");
RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth());
renderer.renderDiagram(
@@ -1328,7 +1352,7 @@ class RouteDiagramTest {
long highlightCount = renderer.getCounterPositions().stream()
.filter(cp -> cp.type() == RouteDiagramAsciiRenderer.CounterType.HIGHLIGHT_SUCCESS)
.count();
- assertTrue(highlightCount > 0, "Should have highlight positions for from1");
+ assertEquals(0, highlightCount, "No arrow highlight when only one endpoint is highlighted");
}
@Test
@@ -1431,13 +1455,13 @@ class RouteDiagramTest {
RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine();
LayoutRoute lr = engine.layoutRoute(route, RouteDiagramLayoutEngine.PADDING);
- Set<String> highlighted = Set.of("from1");
+ Set<String> highlighted = Set.of("from1", "to1");
RouteDiagramAsciiRenderer renderer = new RouteDiagramAsciiRenderer(engine.getNodeWidth(), true);
String ansi = renderer.renderDiagramAnsi(
List.of(lr), lr.maxY + RouteDiagramLayoutEngine.V_GAP,
highlighted, HighlightStyle.SUCCESS);
- assertTrue(ansi.contains("\033[32m"), "Unicode mode should also apply ANSI highlight colors");
+ assertTrue(ansi.contains("\033[32m"), "Unicode mode should also apply ANSI highlight colors on arrows");
assertTrue(ansi.contains("┌"), "Should still use Unicode box-drawing characters");
}