3434import java .util .Collection ;
3535import java .util .Collections ;
3636import java .util .Comparator ;
37+ import java .util .HashMap ;
3738import java .util .LinkedHashSet ;
3839import java .util .List ;
40+ import java .util .Map ;
3941import java .util .Set ;
42+ import java .util .concurrent .ConcurrentHashMap ;
4043import java .util .function .Function ;
44+ import java .util .stream .Collectors ;
4145
4246import io .sundr .adapter .apt .AptContext ;
4347import io .sundr .dsl .internal .graph .Node ;
@@ -74,8 +78,31 @@ public Set<Node<TypeDef>> apply(Set<TypeDef> clazzes) {
7478 }
7579 };
7680
81+ private static final int MAX_TREE_DEPTH = 1000 ;
82+ private static final Map <String , Node <TypeDef >> treeCache = new ConcurrentHashMap <>();
83+
84+ private static String cacheKey (NodeContext context ) {
85+ String visitedNames = context .getVisited ().stream ()
86+ .map (TypeDef ::getName )
87+ .sorted ()
88+ .collect (Collectors .joining ("," ));
89+ return context .getItem ().getName () + "|" + visitedNames ;
90+ }
91+
7792 public static final Function <NodeContext , Node <TypeDef >> TO_TREE = new Function <NodeContext , Node <TypeDef >>() {
7893 public Node <TypeDef > apply (NodeContext context ) {
94+ String key = cacheKey (context );
95+ Node <TypeDef > cached = treeCache .get (key );
96+ if (cached != null ) {
97+ return cached ;
98+ }
99+
100+ int depth = context .getPath ().size ();
101+ if (depth > MAX_TREE_DEPTH ) {
102+ throw new IllegalStateException ("DSL tree depth exceeded " + MAX_TREE_DEPTH + ". Path: " +
103+ context .getPath ().stream ().map (t -> t .getName ()).collect (Collectors .joining (" -> " )));
104+ }
105+
79106 Set <Node <TypeDef >> nextVertices = new LinkedHashSet <Node <TypeDef >>();
80107
81108 //visited and path are the same only in the first iteration. see bellow:
@@ -94,7 +121,10 @@ public Node<TypeDef> apply(NodeContext context) {
94121 nextVertices .add (subGraph );
95122 }
96123 }
97- return DslContextManager .getContext ().getNodeRepository ().getOrCreateNode (context .getItem (), nextVertices );
124+ Node <TypeDef > result = DslContextManager .getContext ().getNodeRepository ().getOrCreateNode (context .getItem (),
125+ nextVertices );
126+ treeCache .put (key , result );
127+ return result ;
98128 }
99129 };
100130
@@ -320,14 +350,22 @@ public Set<String> scopeKeywords(Collection<TypeDef> clazzes) {
320350 private static class CandidateComparator implements Comparator <TypeDef > {
321351
322352 private final NodeContext nodeContext ;
353+ private final Map <TypeDef , Set <TypeDef >> nextCache = new HashMap <TypeDef , Set <TypeDef >>();
323354
324355 private CandidateComparator (NodeContext nodeContext ) {
325356 this .nodeContext = nodeContext ;
326357 }
327358
359+ private Set <TypeDef > getNextFor (TypeDef candidate ) {
360+ if (!nextCache .containsKey (candidate )) {
361+ nextCache .put (candidate , TO_NEXT .apply (nodeContext .contextOfChild (candidate ).addToVisited (candidate ).build ()));
362+ }
363+ return nextCache .get (candidate );
364+ }
365+
328366 public int compare (TypeDef left , TypeDef right ) {
329- Set <TypeDef > leftSet = TO_NEXT . apply ( nodeContext . contextOfChild ( left ). addToVisited ( left ). build () );
330- Set <TypeDef > rightSet = TO_NEXT . apply ( nodeContext . contextOfChild ( right ). addToVisited ( right ). build () );
367+ Set <TypeDef > leftSet = getNextFor ( left );
368+ Set <TypeDef > rightSet = getNextFor ( right );
331369 if (leftSet .contains (right ) && rightSet .contains (left )) {
332370 return 0 ;
333371 } else if (leftSet .contains (right )) {
0 commit comments