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 <[email protected]> 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 <[email protected]> --- .../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"); }
