diff --git a/pom.xml b/pom.xml
index 34c71edb..d846758b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -53,7 +53,7 @@
5
true
- 3.5
+ 3.6.1
1.4.4.0
diff --git a/src/main/java/io/hyperfoil/tools/h5m/cli/ListNode.java b/src/main/java/io/hyperfoil/tools/h5m/cli/ListNode.java
index 29b9f254..d6243c83 100644
--- a/src/main/java/io/hyperfoil/tools/h5m/cli/ListNode.java
+++ b/src/main/java/io/hyperfoil/tools/h5m/cli/ListNode.java
@@ -3,16 +3,14 @@
import io.hyperfoil.tools.h5m.api.Node;
import io.hyperfoil.tools.h5m.api.NodeGroup;
import io.hyperfoil.tools.h5m.api.svc.NodeGroupServiceInterface;
-import io.hyperfoil.tools.h5m.entity.NodeEntity;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.aesh.util.graph.GraphStyle;
import picocli.CommandLine;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.concurrent.Callable;
+import java.util.stream.Collectors;
import org.aesh.util.graph.Graph;
import org.aesh.util.graph.GraphNode;
@@ -32,6 +30,16 @@ public static enum Render {Table, Graph};
@CommandLine.Option(names = {"as"}, description = "Valid values: ${COMPLETION-CANDIDATES}\")", defaultValue = "Table") Render render;
+ @CommandLine.Option(names = {"--hide"}, description = "hide nodes matching suffix pattern (comma-separated, e.g. _primary,_alt)", arity = "0..1") String hidePattern;
+
+ @CommandLine.Option(names = {"--type"}, description = "filter by node type (comma-separated, e.g. sql,ecma,fp,rd)", arity = "0..1") String typeFilter;
+
+ @CommandLine.Option(names = {"--depth"}, description = "max depth from root", arity = "0..1", defaultValue = "0") int maxDepth;
+
+ @CommandLine.Option(names = {"--show-type"}, description = "prefix node labels with [type]", defaultValue = "true") boolean showType;
+
+ @CommandLine.Option(names = {"--max-label-width"}, description = "wrap labels at word boundaries when wider than N chars (0 = no wrap)", defaultValue = "30") int maxLabelWidth;
+
@Override
@Transactional
public Integer call() throws Exception {
@@ -47,13 +55,31 @@ public Integer call() throws Exception {
return 1;
}
if(render.equals(Render.Graph)){
- GraphNode rootNode = GraphNode.of("root");
+ Set skipNames = new HashSet<>();
+ if (hidePattern != null) {
+ List suffixes = Arrays.stream(hidePattern.split(",")).map(String::trim).toList();
+ for (Node source : nodeGroup.sources()) {
+ for (String suffix : suffixes) {
+ if (source.name().endsWith(suffix)) {
+ skipNames.add(source.name());
+ break;
+ }
+ }
+ }
+ }
+
+ Set types = typeFilter != null
+ ? Arrays.stream(typeFilter.split(",")).map(String::trim).collect(Collectors.toSet())
+ : Collections.emptySet();
+
+ String rootLabel = showType ? "[root] root" : "root";
+ GraphNode rootNode = GraphNode.of(rootLabel);
Map nodes = new HashMap<>();
nodes.put(nodeGroup.root(), rootNode);
- for(Node source: nodeGroup.sources()){
- walk(source,nodes);
- }
- System.out.println(Graph.render(rootNode, GraphStyle.ROUNDED));
+ for(Node source: nodeGroup.sources()){
+ walk(source, nodes, skipNames, types, 1, maxDepth, showType);
+ }
+ System.out.println(Graph.render(rootNode, GraphStyle.ROUNDED, 0, maxLabelWidth));
}else {
System.out.println(
ListCmd.table(80,nodeGroup.sources(),List.of("name","type","fqdn","operation"),
@@ -69,18 +95,34 @@ public Integer call() throws Exception {
return 0;
}
- public GraphNode walk(Node node, Map nodes){
+ public GraphNode walk(Node node, Map nodes, Set skipNames,
+ Set typeFilter, int depth, int maxDepth, boolean showType){
if(nodes.containsKey(node)){
return nodes.get(node);
- }else{
- GraphNode rtrn = GraphNode.of(node.name());
+ }
+ if(maxDepth > 0 && depth > maxDepth){
+ return null;
+ }
+
+ boolean skip = skipNames.contains(node.name())
+ || (!typeFilter.isEmpty() && !typeFilter.contains(node.type().display()));
+
+ GraphNode rtrn = null;
+ if (!skip) {
+ String label = showType ? "[" + node.type().display() + "] " + node.name() : node.name();
+ rtrn = GraphNode.of(label);
nodes.put(node, rtrn);
- for(Node source: node.sources()){
- GraphNode fromSource = walk(source,nodes);
- fromSource.child(rtrn);
- }
- return rtrn;
}
+
+ for(Node source: node.sources()){
+ GraphNode fromSource = walk(source, nodes, skipNames, typeFilter, depth + 1, maxDepth, showType);
+ if(rtrn != null && fromSource != null){
+ fromSource.child(rtrn);
+ } else if(rtrn != null && nodes.containsKey(source)){
+ nodes.get(source).child(rtrn);
+ }
+ }
+ return rtrn;
}
}