fix: use graph root + edge walk to separate main flow from handlers
Root cause: graph.getNodes() is a flat list with duplicates — handler compound children appear both nested inside their parent AND as top-level entries. The previous separation tried to filter the flat list but missed the duplicates, leaving handler children in rootNode. New approach: walk from graph.getRoot() following non-ERROR edges to discover main flow nodes. Edges targeting handler compounds (ON_EXCEPTION, ON_COMPLETION) are not followed. This cleanly separates main flow from handler sections using the graph's own structure. Falls back to flat list filtering (old behavior) when graph.getRoot() is null (legacy/test graphs). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -206,24 +206,59 @@ public class ElkDiagramRenderer implements DiagramRenderer {
|
||||
Map<String, Color> nodeColors = new HashMap<>();
|
||||
Set<String> compoundNodeIds = new HashSet<>();
|
||||
|
||||
// Separate handler sections from main flow nodes.
|
||||
// graph.getNodes() is a FLAT list that includes handler compound children
|
||||
// as top-level entries. We must identify and exclude them.
|
||||
// Build a lookup from the flat nodes list (graph.getNodes() contains
|
||||
// duplicates — children appear both nested AND top-level). Use this for
|
||||
// resolving edge endpoints to RouteNode objects.
|
||||
Map<String, RouteNode> nodeById = new HashMap<>();
|
||||
if (graph.getNodes() != null) {
|
||||
for (RouteNode rn : graph.getNodes()) {
|
||||
nodeById.put(rn.getId(), rn);
|
||||
}
|
||||
}
|
||||
|
||||
// Separate main flow from handler sections using graph structure:
|
||||
// - Start from graph.getRoot() and walk FLOW edges for main flow
|
||||
// - Handler sections are top-level nodes of HANDLER_SECTION_TYPES
|
||||
// This avoids the problem of the flat nodes list containing duplicates.
|
||||
Set<String> mainNodeIds = new HashSet<>();
|
||||
List<RouteNode> mainNodes = new ArrayList<>();
|
||||
List<RouteNode> handlerNodes = new ArrayList<>();
|
||||
Set<String> handlerDescendantIds = new HashSet<>();
|
||||
|
||||
// Collect main flow node IDs by walking from root along non-ERROR edges.
|
||||
// graph.getRoot() provides the entry point; ERROR edges lead to handler sections.
|
||||
if (graph.getRoot() != null && graph.getEdges() != null) {
|
||||
mainNodeIds.add(graph.getRoot().getId());
|
||||
boolean changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (RouteEdge re : graph.getEdges()) {
|
||||
if (re.getEdgeType() == RouteEdge.EdgeType.ERROR) continue;
|
||||
if (mainNodeIds.contains(re.getSource()) && !mainNodeIds.contains(re.getTarget())) {
|
||||
// Don't follow edges INTO handler compounds
|
||||
RouteNode target = nodeById.get(re.getTarget());
|
||||
if (target != null && target.getType() != null
|
||||
&& HANDLER_SECTION_TYPES.contains(target.getType())) {
|
||||
continue;
|
||||
}
|
||||
mainNodeIds.add(re.getTarget());
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build node lists — deduplicate (flat list has duplicates)
|
||||
Set<String> seen = new HashSet<>();
|
||||
if (graph.getNodes() != null) {
|
||||
// First pass: identify handler compounds and collect their descendant IDs
|
||||
for (RouteNode rn : graph.getNodes()) {
|
||||
if (seen.contains(rn.getId())) continue;
|
||||
seen.add(rn.getId());
|
||||
if (rn.getType() != null && HANDLER_SECTION_TYPES.contains(rn.getType())
|
||||
&& rn.getChildren() != null && !rn.getChildren().isEmpty()) {
|
||||
handlerNodes.add(rn);
|
||||
collectDescendantIds(rn.getChildren(), handlerDescendantIds);
|
||||
}
|
||||
}
|
||||
// Second pass: main flow = everything that isn't a handler or handler descendant
|
||||
for (RouteNode rn : graph.getNodes()) {
|
||||
if (!handlerNodes.contains(rn) && !handlerDescendantIds.contains(rn.getId())) {
|
||||
} else if (mainNodeIds.isEmpty() || mainNodeIds.contains(rn.getId())) {
|
||||
// When no root is available (legacy/test graphs), include all
|
||||
// non-handler nodes as main flow (fallback to old behavior)
|
||||
mainNodes.add(rn);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user