1313
1414public final class Ref {
1515
16+ private static final int RESOLVE_LIMIT = Integer .getInteger ("io.vertx.json.schema.resolve.limit" , 50 );
17+
1618 public static final List <String > POINTER_KEYWORD = Arrays .asList (
1719 "$ref" ,
1820 "$id" ,
@@ -37,6 +39,13 @@ public final class Ref {
3739 }
3840
3941 public static JsonObject resolve (Map <String , JsonSchema > refs , URL baseUri , JsonSchema schema ) {
42+ return resolve (refs , baseUri , schema , RESOLVE_LIMIT );
43+ }
44+ private static JsonObject resolve (Map <String , JsonSchema > refs , URL baseUri , JsonSchema schema , int limit ) {
45+ if (limit == 0 ) {
46+ throw new RuntimeException ("Too much recursion resolving schema" );
47+ }
48+
4049 final JsonObject tree = ((JsonObject ) schema ).copy ();
4150 final Map <String , List <Ref >> pointers = new HashMap <>();
4251
@@ -49,6 +58,8 @@ public static JsonObject resolve(Map<String, JsonSchema> refs, URL baseUri, Json
4958
5059 final JsonObject dynamicAnchors = new JsonObject ();
5160
61+ boolean updated = false ;
62+
5263 pointers
5364 .computeIfAbsent ("$id" , key -> Collections .emptyList ())
5465 .forEach (item -> {
@@ -90,31 +101,30 @@ public static JsonObject resolve(Map<String, JsonSchema> refs, URL baseUri, Json
90101 dynamicAnchors .put ("#" + ref , obj );
91102 });
92103
93- pointers
94- .computeIfAbsent ("$ref" , key -> Collections .emptyList ())
95- .forEach (item -> {
96- final String ref = item .ref ;
97- final String prop = item .prop ;
98- final JsonObject obj = item .obj ;
99- final String id = item .id ;
100-
101- obj .remove (prop );
102-
103- final String decodedRef = decodeURIComponent (ref );
104- final String fullRef = decodedRef .charAt (0 ) != '#' ? decodedRef : id + decodedRef ;
105- // re-assign the obj
106- obj .mergeIn (
107- new JsonObject (
108- resolveUri (refs , baseUri , schema , fullRef , anchors )
109- // filter out pointer keywords
110- .stream ()
111- .filter (kv -> !POINTER_KEYWORD .contains (kv .getKey ()))
112- .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ))));
113- });
104+ for (Ref item : pointers .computeIfAbsent ("$ref" , key -> Collections .emptyList ())) {
105+ final String ref = item .ref ;
106+ final String prop = item .prop ;
107+ final JsonObject obj = item .obj ;
108+ final String id = item .id ;
109+
110+ obj .remove (prop );
111+
112+ final String decodedRef = decodeURIComponent (ref );
113+ final String fullRef = decodedRef .charAt (0 ) != '#' ? decodedRef : id + decodedRef ;
114+ // re-assign the obj
115+ obj .mergeIn (
116+ new JsonObject (
117+ resolveUri (refs , baseUri , schema , fullRef , anchors )
118+ // filter out pointer keywords
119+ .stream ()
120+ .filter (kv -> !POINTER_KEYWORD .contains (kv .getKey ()))
121+ .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ))));
122+
123+ // the underlying schema was updated
124+ updated = true ;
125+ }
114126
115- pointers
116- .computeIfAbsent ("$dynamicRef" , key -> Collections .emptyList ())
117- .forEach (item -> {
127+ for (Ref item : pointers .computeIfAbsent ("$dynamicRef" , key -> Collections .emptyList ())) {
118128 final String ref = item .ref ;
119129 final String prop = item .prop ;
120130 final JsonObject obj = item .obj ;
@@ -130,9 +140,17 @@ public static JsonObject resolve(Map<String, JsonSchema> refs, URL baseUri, Json
130140 .stream ()
131141 .filter (kv -> !POINTER_KEYWORD .contains (kv .getKey ()))
132142 .collect (Collectors .toMap (Map .Entry ::getKey , Map .Entry ::getValue ))));
133- });
134143
135- return tree ;
144+ // the underlying schema was updated
145+ updated = true ;
146+ }
147+
148+ if (updated ) {
149+ // the schema changed we need to re-run
150+ return resolve (refs , baseUri , JsonSchema .of (tree ), limit - 1 );
151+ } else {
152+ return tree ;
153+ }
136154 }
137155
138156 private static void findRefsAndClean (Object obj , String path , String id , Map <String , List <Ref >> pointers ) {
0 commit comments