refactor: simplify ElkDiagramRenderer layout code
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 59s
CI / docker (push) Successful in 40s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 36s

- Introduce LayoutContext to bundle 8 accumulator params into 1 object
- Extract computeLayout (261 lines) into 6 focused sub-methods:
  buildNodeIndex, partitionNodes, createElkRoot, createElkEdges,
  postProcessDoTrySections, extractLayout
- Consolidate duplicated DO_TRY handler iteration via orderedHandlerChildren
- De-duplicate ELK root configuration (main + handler roots)
- Add DO_TRY test cases for section ordering and uniform width
- Clean up orphaned Javadoc comments

No behavioral changes. 882 → 841 lines.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-28 12:20:08 +01:00
parent 065517f032
commit 90be1875e0
2 changed files with 265 additions and 213 deletions

View File

@@ -172,6 +172,98 @@ class ElkDiagramRendererTest {
assertEquals(2, choiceNode.children().size(), "Choice node should have 2 children (when, otherwise)");
}
/**
* Build a DO_TRY graph: from -> doTry(try: [process, log], doFinally: [cleanup], doCatch: [errorLog]) -> to
*/
private RouteGraph buildDoTryGraph() {
RouteGraph graph = new RouteGraph("try-catch-route");
graph.setExtractedAt(Instant.now());
graph.setVersion(1);
RouteNode from = new RouteNode("node-1", NodeType.ENDPOINT, "timer:tick");
RouteNode doTry = new RouteNode("node-2", NodeType.DO_TRY, "doTry");
RouteNode process = new RouteNode("node-3", NodeType.PROCESSOR, "process");
RouteNode log1 = new RouteNode("node-4", NodeType.LOG, "log:tryBody");
RouteNode doFinally = new RouteNode("node-5", NodeType.DO_FINALLY, "doFinally");
RouteNode cleanup = new RouteNode("node-6", NodeType.LOG, "log:cleanup");
RouteNode doCatch = new RouteNode("node-7", NodeType.DO_CATCH, "doCatch");
RouteNode errorLog = new RouteNode("node-8", NodeType.LOG, "log:error");
RouteNode to = new RouteNode("node-9", NodeType.TO, "log:done");
doFinally.setChildren(List.of(cleanup));
doCatch.setChildren(List.of(errorLog));
doTry.setChildren(List.of(process, log1, doFinally, doCatch));
graph.setRoot(from);
graph.setNodes(List.of(from, doTry, process, log1, doFinally, cleanup, doCatch, errorLog, to));
graph.setEdges(List.of(
new RouteEdge("node-1", "node-2", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-2", "node-3", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-3", "node-4", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-2", "node-5", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-5", "node-6", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-2", "node-7", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-7", "node-8", RouteEdge.EdgeType.FLOW),
new RouteEdge("node-2", "node-9", RouteEdge.EdgeType.FLOW)
));
return graph;
}
@Test
void layoutJson_doTryGraph_sectionsInCorrectOrder() {
DiagramLayout layout = renderer.layoutJson(buildDoTryGraph());
assertNotNull(layout);
// Find the DO_TRY compound node
PositionedNode doTryNode = layout.nodes().stream()
.filter(n -> "node-2".equals(n.id()))
.findFirst()
.orElseThrow(() -> new AssertionError("DO_TRY node not found"));
assertNotNull(doTryNode.children(), "DO_TRY should have children");
assertFalse(doTryNode.children().isEmpty(), "DO_TRY should have non-empty children");
// Find sections by ID pattern
PositionedNode tryBody = doTryNode.children().stream()
.filter(n -> n.id() != null && n.id().contains("._try_body"))
.findFirst().orElse(null);
PositionedNode finallySection = doTryNode.children().stream()
.filter(n -> "node-5".equals(n.id()))
.findFirst().orElse(null);
PositionedNode catchSection = doTryNode.children().stream()
.filter(n -> "node-7".equals(n.id()))
.findFirst().orElse(null);
assertNotNull(tryBody, "Try body wrapper should exist");
assertNotNull(finallySection, "doFinally section should exist");
assertNotNull(catchSection, "doCatch section should exist");
// Verify vertical order: tryBody.y < doFinally.y < doCatch.y
assertTrue(tryBody.y() < finallySection.y(),
"Try body (y=" + tryBody.y() + ") should be above doFinally (y=" + finallySection.y() + ")");
assertTrue(finallySection.y() < catchSection.y(),
"doFinally (y=" + finallySection.y() + ") should be above doCatch (y=" + catchSection.y() + ")");
}
@Test
void layoutJson_doTryGraph_sectionsHaveSameWidth() {
DiagramLayout layout = renderer.layoutJson(buildDoTryGraph());
PositionedNode doTryNode = layout.nodes().stream()
.filter(n -> "node-2".equals(n.id()))
.findFirst()
.orElseThrow(() -> new AssertionError("DO_TRY node not found"));
List<PositionedNode> sections = doTryNode.children();
double firstWidth = sections.get(0).width();
for (PositionedNode section : sections) {
assertEquals(firstWidth, section.width(), 0.1,
"All sections should have the same width, but " + section.id() + " differs");
}
}
@Test
void renderSvg_compoundGraph_producesValidSvg() {
String svg = renderer.renderSvg(buildCompoundGraph());