fix: use invisible ordering edges to enforce DO_TRY section order
Layer constraints (FIRST/LAST) don't work for disconnected components in ELK's layered algorithm. Replace with invisible edges that chain try_body → doFinally → doCatch to guarantee correct top-to-bottom order. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -356,6 +356,12 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
allEdges.addAll(collectAllEdges(hr));
|
||||
}
|
||||
for (ElkEdge elkEdge : allEdges) {
|
||||
// Skip invisible ordering edges used only for DO_TRY section layout
|
||||
String edgeId = elkEdge.getIdentifier();
|
||||
if (edgeId != null && edgeId.startsWith("__ordering_")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String sourceId = elkEdge.getSources().isEmpty() ? "" : elkEdge.getSources().get(0).getIdentifier();
|
||||
String targetId = elkEdge.getTargets().isEmpty() ? "" : elkEdge.getTargets().get(0).getIdentifier();
|
||||
|
||||
@@ -568,6 +574,7 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
}
|
||||
|
||||
// Virtual _TRY_BODY wrapper with LR direction for horizontal try body chain
|
||||
// Layer constraint FIRST ensures it stays at the top of the DO_TRY
|
||||
if (!tryBodyChildren.isEmpty()) {
|
||||
String wrapperId = rn.getId() + "._try_body";
|
||||
ElkNode wrapper = factory.createElkNode();
|
||||
@@ -591,8 +598,7 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
// Handler children stacked below try body: DO_FINALLY first, then DO_CATCH
|
||||
// (ELK TB layout places children in insertion order)
|
||||
// Handler children: DO_FINALLY in the middle, DO_CATCH at the bottom
|
||||
for (RouteNode child : handlerChildren) {
|
||||
if (child.getType() == NodeType.DO_FINALLY) {
|
||||
childNodeIds.add(child.getId());
|
||||
@@ -607,6 +613,30 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
compoundNodeIds, childNodeIds, doTryNodeIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Create invisible ordering edges to enforce top-to-bottom:
|
||||
// try_body → doFinally → doCatch
|
||||
List<ElkNode> orderedSections = new ArrayList<>();
|
||||
if (!tryBodyChildren.isEmpty()) {
|
||||
orderedSections.add(elkNodeMap.get(rn.getId() + "._try_body"));
|
||||
}
|
||||
for (RouteNode child : handlerChildren) {
|
||||
if (child.getType() == NodeType.DO_FINALLY) {
|
||||
orderedSections.add(elkNodeMap.get(child.getId()));
|
||||
}
|
||||
}
|
||||
for (RouteNode child : handlerChildren) {
|
||||
if (child.getType() == NodeType.DO_CATCH) {
|
||||
orderedSections.add(elkNodeMap.get(child.getId()));
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < orderedSections.size() - 1; i++) {
|
||||
ElkEdge orderEdge = factory.createElkEdge();
|
||||
orderEdge.setIdentifier("__ordering_" + rn.getId() + "_" + i);
|
||||
orderEdge.setContainingNode(elkNode);
|
||||
orderEdge.getSources().add(orderedSections.get(i));
|
||||
orderEdge.getTargets().add(orderedSections.get(i + 1));
|
||||
}
|
||||
} else if (isCompound) {
|
||||
compoundNodeIds.add(rn.getId());
|
||||
elkNode.setWidth(200);
|
||||
|
||||
Reference in New Issue
Block a user