55
55
*/
56
56
public class SchemaLoader {
57
57
58
- private static final List <String > ARRAY_SCHEMA_PROPS = Arrays .asList ("items" , "additionalItems" ,
59
- "minItems" ,
60
- "maxItems" ,
61
- "uniqueItems" );
62
-
63
- private static final List <String > OBJECT_SCHEMA_PROPS = Arrays .asList ("properties" , "required" ,
64
- "minProperties" ,
65
- "maxProperties" ,
66
- "dependencies" ,
67
- "patternProperties" ,
68
- "additionalProperties" );
69
-
70
- private static final List <String > NUMBER_SCHEMA_PROPS = Arrays .asList ("minimum" , "maximum" ,
71
- "minimumExclusive" , "maximumExclusive" , "multipleOf" );
72
-
73
- private static final List <String > STRING_SCHEMA_PROPS = Arrays .asList ("minLength" , "maxLength" ,
74
- "pattern" );
75
-
76
- private static final Map <String , Function <Collection <Schema >, CombinedSchema .Builder >> //
77
- COMBINED_SUBSCHEMA_PROVIDERS = new HashMap <>(3 );
78
-
79
- static {
80
- COMBINED_SUBSCHEMA_PROVIDERS .put ("allOf" , CombinedSchema ::allOf );
81
- COMBINED_SUBSCHEMA_PROVIDERS .put ("anyOf" , CombinedSchema ::anyOf );
82
- COMBINED_SUBSCHEMA_PROVIDERS .put ("oneOf" , CombinedSchema ::oneOf );
83
- }
84
-
85
- /**
86
- * Loads a JSON schema to a schema validator using a {@link DefaultSchemaClient default HTTP
87
- * client}.
88
- *
89
- * @param schemaJson
90
- * the JSON representation of the schema.
91
- * @return the schema validator object
92
- */
93
- public static Schema load (final JSONObject schemaJson ) {
94
- return load (schemaJson , new DefaultSchemaClient ());
95
- }
96
-
97
- /**
98
- * Creates Schema instance from its JSON representation.
99
- *
100
- * @param schemaJson
101
- * the JSON representation of the schema.
102
- * @param httpClient
103
- * the HTTP client to be used for resolving remote JSON references.
104
- * @return the created schema
105
- */
106
- public static Schema load (final JSONObject schemaJson , final SchemaClient httpClient ) {
107
- String schemaId = schemaJson .optString ("id" );
108
- return new SchemaLoader (schemaId , schemaJson , schemaJson , new HashMap <>(), httpClient )
109
- .load ().build ();
110
- }
111
-
112
58
/**
113
59
* Created and used by {@link TypeBasedMultiplexer} to set actions (consumers) for matching
114
60
* classes.
@@ -136,26 +82,6 @@ interface OnTypeConsumer<E> {
136
82
*/
137
83
class TypeBasedMultiplexer {
138
84
139
- /**
140
- * Default implementation of {@link OnTypeConsumer}, instantiated by
141
- * {@link TypeBasedMultiplexer#ifIs(Class)}.
142
- */
143
- private class OnTypeConsumerImpl <E > implements OnTypeConsumer <E > {
144
-
145
- protected final Class <?> key ;
146
-
147
- public OnTypeConsumerImpl (final Class <?> key ) {
148
- this .key = key ;
149
- }
150
-
151
- @ Override
152
- public TypeBasedMultiplexer then (final Consumer <E > consumer ) {
153
- actions .put (key , consumer );
154
- return TypeBasedMultiplexer .this ;
155
- }
156
-
157
- }
158
-
159
85
/**
160
86
* An {@link OnTypeConsumer} implementation which wraps the action ({@code obj} consumer} set by
161
87
* {@link #then(Consumer)} into an other consumer which maintains {@link SchemaLoader#id}.
@@ -189,12 +115,39 @@ public TypeBasedMultiplexer then(final Consumer<JSONObject> consumer) {
189
115
190
116
}
191
117
118
+ /**
119
+ * Default implementation of {@link OnTypeConsumer}, instantiated by
120
+ * {@link TypeBasedMultiplexer#ifIs(Class)}.
121
+ */
122
+ private class OnTypeConsumerImpl <E > implements OnTypeConsumer <E > {
123
+
124
+ protected final Class <?> key ;
125
+
126
+ public OnTypeConsumerImpl (final Class <?> key ) {
127
+ this .key = key ;
128
+ }
129
+
130
+ @ Override
131
+ public TypeBasedMultiplexer then (final Consumer <E > consumer ) {
132
+ actions .put (key , consumer );
133
+ return TypeBasedMultiplexer .this ;
134
+ }
135
+
136
+ }
137
+
192
138
private final String keyOfObj ;
193
139
194
140
private final Object obj ;
195
141
196
142
private final Map <Class <?>, Consumer <?>> actions = new HashMap <>();
197
143
144
+ /**
145
+ * Constructor with {@code null} {@code keyOfObj}.
146
+ */
147
+ public TypeBasedMultiplexer (final Object obj ) {
148
+ this (null , obj );
149
+ }
150
+
198
151
/**
199
152
* Constructor.
200
153
*
@@ -210,13 +163,6 @@ public TypeBasedMultiplexer(final String keyOfObj, final Object obj) {
210
163
this .obj = obj ;
211
164
}
212
165
213
- /**
214
- * Constructor with {@code null} {@code keyOfObj}.
215
- */
216
- public TypeBasedMultiplexer (final Object obj ) {
217
- this (null , obj );
218
- }
219
-
220
166
/**
221
167
* Creates a setter which will be invoked by {@link #orElse(Consumer)} or {@link #requireAny()}
222
168
* if {@code obj} is an instance of {@code predicateClass}.
@@ -256,10 +202,10 @@ public OnTypeConsumer<JSONObject> ifObject() {
256
202
public void orElse (final Consumer <Object > orElseConsumer ) {
257
203
@ SuppressWarnings ("unchecked" )
258
204
Consumer <Object > consumer = (Consumer <Object >) actions .keySet ().stream ()
259
- .filter (clazz -> clazz .isAssignableFrom (obj .getClass ()))
260
- .findFirst ()
261
- .map (actions ::get )
262
- .orElse (orElseConsumer ::accept );
205
+ .filter (clazz -> clazz .isAssignableFrom (obj .getClass ()))
206
+ .findFirst ()
207
+ .map (actions ::get )
208
+ .orElse (orElseConsumer ::accept );
263
209
consumer .accept (obj );
264
210
265
211
}
@@ -276,12 +222,58 @@ public void requireAny() {
276
222
}
277
223
}
278
224
279
- TypeBasedMultiplexer typeMultiplexer (final Object obj ) {
280
- return new TypeBasedMultiplexer (obj );
225
+ private static final List <String > ARRAY_SCHEMA_PROPS = Arrays .asList ("items" , "additionalItems" ,
226
+ "minItems" ,
227
+ "maxItems" ,
228
+ "uniqueItems" );
229
+
230
+ private static final List <String > OBJECT_SCHEMA_PROPS = Arrays .asList ("properties" , "required" ,
231
+ "minProperties" ,
232
+ "maxProperties" ,
233
+ "dependencies" ,
234
+ "patternProperties" ,
235
+ "additionalProperties" );
236
+
237
+ private static final List <String > NUMBER_SCHEMA_PROPS = Arrays .asList ("minimum" , "maximum" ,
238
+ "minimumExclusive" , "maximumExclusive" , "multipleOf" );
239
+
240
+ private static final List <String > STRING_SCHEMA_PROPS = Arrays .asList ("minLength" , "maxLength" ,
241
+ "pattern" );
242
+
243
+ private static final Map <String , Function <Collection <Schema >, CombinedSchema .Builder >> //
244
+ COMBINED_SUBSCHEMA_PROVIDERS = new HashMap <>(3 );
245
+
246
+ static {
247
+ COMBINED_SUBSCHEMA_PROVIDERS .put ("allOf" , CombinedSchema ::allOf );
248
+ COMBINED_SUBSCHEMA_PROVIDERS .put ("anyOf" , CombinedSchema ::anyOf );
249
+ COMBINED_SUBSCHEMA_PROVIDERS .put ("oneOf" , CombinedSchema ::oneOf );
281
250
}
282
251
283
- TypeBasedMultiplexer typeMultiplexer (final String keyOfObj , final Object obj ) {
284
- return new TypeBasedMultiplexer (keyOfObj , obj );
252
+ /**
253
+ * Loads a JSON schema to a schema validator using a {@link DefaultSchemaClient default HTTP
254
+ * client}.
255
+ *
256
+ * @param schemaJson
257
+ * the JSON representation of the schema.
258
+ * @return the schema validator object
259
+ */
260
+ public static Schema load (final JSONObject schemaJson ) {
261
+ return load (schemaJson , new DefaultSchemaClient ());
262
+ }
263
+
264
+ /**
265
+ * Creates Schema instance from its JSON representation.
266
+ *
267
+ * @param schemaJson
268
+ * the JSON representation of the schema.
269
+ * @param httpClient
270
+ * the HTTP client to be used for resolving remote JSON references.
271
+ * @return the created schema
272
+ */
273
+ public static Schema load (final JSONObject schemaJson , final SchemaClient httpClient ) {
274
+ String schemaId = schemaJson .optString ("id" );
275
+ return new SchemaLoader (schemaId , schemaJson , schemaJson , new HashMap <>(), httpClient )
276
+ .load ().build ();
285
277
}
286
278
287
279
private String id = null ;
@@ -309,19 +301,19 @@ TypeBasedMultiplexer typeMultiplexer(final String keyOfObj, final Object obj) {
309
301
310
302
private void addDependencies (final Builder builder , final JSONObject deps ) {
311
303
Arrays .stream (JSONObject .getNames (deps ))
312
- .forEach (ifPresent -> addDependency (builder , ifPresent , deps .get (ifPresent )));
304
+ .forEach (ifPresent -> addDependency (builder , ifPresent , deps .get (ifPresent )));
313
305
}
314
306
315
307
private void addDependency (final Builder builder , final String ifPresent , final Object deps ) {
316
308
typeMultiplexer (deps )
317
- .ifObject ().then (obj -> {
318
- builder .schemaDependency (ifPresent , loadChild (obj ).build ());
319
- })
320
- .ifIs (JSONArray .class ).then (propNames -> {
321
- IntStream .range (0 , propNames .length ())
309
+ .ifObject ().then (obj -> {
310
+ builder .schemaDependency (ifPresent , loadChild (obj ).build ());
311
+ })
312
+ .ifIs (JSONArray .class ).then (propNames -> {
313
+ IntStream .range (0 , propNames .length ())
322
314
.mapToObj (i -> propNames .getString (i ))
323
315
.forEach (dependency -> builder .propertyDependency (ifPresent , dependency ));
324
- }).requireAny ();
316
+ }).requireAny ();
325
317
}
326
318
327
319
private CombinedSchema .Builder buildAnyOfSchemaForMultipleTypes () {
@@ -350,13 +342,22 @@ private ArraySchema.Builder buildArraySchema() {
350
342
}
351
343
if (schemaJson .has ("items" )) {
352
344
typeMultiplexer ("items" , schemaJson .get ("items" ))
353
- .ifObject ().then (itemSchema -> builder .allItemSchema (loadChild (itemSchema ).build ()))
354
- .ifIs (JSONArray .class ).then (arr -> buildTupleSchema (builder , arr ))
355
- .requireAny ();
345
+ .ifObject ().then (itemSchema -> builder .allItemSchema (loadChild (itemSchema ).build ()))
346
+ .ifIs (JSONArray .class ).then (arr -> buildTupleSchema (builder , arr ))
347
+ .requireAny ();
356
348
}
357
349
return builder ;
358
350
}
359
351
352
+ private EnumSchema .Builder buildEnumSchema () {
353
+ Set <Object > possibleValues = new HashSet <>();
354
+ JSONArray arr = schemaJson .getJSONArray ("enum" );
355
+ IntStream .range (0 , arr .length ())
356
+ .mapToObj (arr ::get )
357
+ .forEach (possibleValues ::add );
358
+ return EnumSchema .builder ().possibleValues (possibleValues );
359
+ }
360
+
360
361
private NotSchema .Builder buildNotSchema () {
361
362
Schema mustNotMatch = loadChild (schemaJson .getJSONObject ("not" )).build ();
362
363
return NotSchema .builder ().mustNotMatch (mustNotMatch );
@@ -379,8 +380,8 @@ private ObjectSchema.Builder buildObjectSchema() {
379
380
if (schemaJson .has ("properties" )) {
380
381
JSONObject propertyDefs = schemaJson .getJSONObject ("properties" );
381
382
Arrays .stream (Optional .ofNullable (JSONObject .getNames (propertyDefs )).orElse (new String [0 ]))
382
- .forEach (key -> builder .addPropertySchema (key ,
383
- loadChild (propertyDefs .getJSONObject (key )).build ()));
383
+ .forEach (key -> builder .addPropertySchema (key ,
384
+ loadChild (propertyDefs .getJSONObject (key )).build ()));
384
385
}
385
386
if (schemaJson .has ("additionalProperties" )) {
386
387
typeMultiplexer ("additionalProperties" , schemaJson .get ("additionalProperties" ))
@@ -391,8 +392,8 @@ private ObjectSchema.Builder buildObjectSchema() {
391
392
if (schemaJson .has ("required" )) {
392
393
JSONArray requiredJson = schemaJson .getJSONArray ("required" );
393
394
IntStream .range (0 , requiredJson .length ())
394
- .mapToObj (requiredJson ::getString )
395
- .forEach (builder ::addRequiredProperty );
395
+ .mapToObj (requiredJson ::getString )
396
+ .forEach (builder ::addRequiredProperty );
396
397
}
397
398
if (schemaJson .has ("patternProperties" )) {
398
399
JSONObject patternPropsJson = schemaJson .getJSONObject ("patternProperties" );
@@ -481,25 +482,6 @@ private Schema.Builder<?> load() {
481
482
return builder ;
482
483
}
483
484
484
- private Schema .Builder <?> loadForType (final Object type ) {
485
- if (type instanceof JSONArray ) {
486
- return buildAnyOfSchemaForMultipleTypes ();
487
- } else if (type instanceof String ) {
488
- return loadForExplicitType ((String ) type );
489
- } else {
490
- throw new SchemaException ("type" , Arrays .asList (JSONArray .class , String .class ), type );
491
- }
492
- }
493
-
494
- private EnumSchema .Builder buildEnumSchema () {
495
- Set <Object > possibleValues = new HashSet <>();
496
- JSONArray arr = schemaJson .getJSONArray ("enum" );
497
- IntStream .range (0 , arr .length ())
498
- .mapToObj (arr ::get )
499
- .forEach (possibleValues ::add );
500
- return EnumSchema .builder ().possibleValues (possibleValues );
501
- }
502
-
503
485
private Schema .Builder <?> loadChild (final JSONObject childJson ) {
504
486
return new SchemaLoader (id , childJson , rootSchemaJson , pointerSchemas ,
505
487
httpClient ).load ();
@@ -526,6 +508,16 @@ private Schema.Builder<?> loadForExplicitType(final String typeString) {
526
508
}
527
509
}
528
510
511
+ private Schema .Builder <?> loadForType (final Object type ) {
512
+ if (type instanceof JSONArray ) {
513
+ return buildAnyOfSchemaForMultipleTypes ();
514
+ } else if (type instanceof String ) {
515
+ return loadForExplicitType ((String ) type );
516
+ } else {
517
+ throw new SchemaException ("type" , Arrays .asList (JSONArray .class , String .class ), type );
518
+ }
519
+ }
520
+
529
521
/**
530
522
* Returns a schema builder instance after looking up the JSON pointer.
531
523
*/
@@ -547,11 +539,19 @@ private Schema.Builder<?> lookupReference(final String relPointerString) {
547
539
return refBuilder ;
548
540
}
549
541
} else {
542
+ if (pointerSchemas .containsKey (absPointerString )) {
543
+ return pointerSchemas .get (absPointerString );
544
+ }
545
+ ReferenceSchema .Builder refBuilder = ReferenceSchema .builder ();
546
+ pointerSchemas .put (absPointerString , refBuilder );
550
547
pointer = JSONPointer .forURL (httpClient , absPointerString );
548
+ QueryResult result = pointer .query ();
549
+ SchemaLoader childLoader = new SchemaLoader (id , result .getQueryResult (),
550
+ result .getContainingDocument (), pointerSchemas , httpClient );
551
+ Schema referredSchema = childLoader .load ().build ();
552
+ refBuilder .build ().setReferredSchema (referredSchema );
553
+ return refBuilder ;
551
554
}
552
- QueryResult result = pointer .query ();
553
- return new SchemaLoader (id , result .getQueryResult (), result .getContainingDocument (),
554
- pointerSchemas , httpClient ).load ();
555
555
}
556
556
557
557
private boolean schemaHasAnyOf (final Collection <String > propNames ) {
@@ -603,4 +603,12 @@ private CombinedSchema.Builder tryCombinedSchema() {
603
603
return null ;
604
604
}
605
605
}
606
+
607
+ TypeBasedMultiplexer typeMultiplexer (final Object obj ) {
608
+ return new TypeBasedMultiplexer (obj );
609
+ }
610
+
611
+ TypeBasedMultiplexer typeMultiplexer (final String keyOfObj , final Object obj ) {
612
+ return new TypeBasedMultiplexer (keyOfObj , obj );
613
+ }
606
614
}
0 commit comments