Skip to content

Commit ddc9828

Browse files
committed
moving the remaining parts of loadSchemaObject() into extractors
1 parent 155b799 commit ddc9828

File tree

2 files changed

+198
-164
lines changed

2 files changed

+198
-164
lines changed

core/src/main/java/org/everit/json/schema/loader/SchemaExtractor.java

Lines changed: 191 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
11
package org.everit.json.schema.loader;
22

3+
import static java.lang.String.format;
4+
import static java.util.Arrays.asList;
35
import static java.util.Collections.emptyList;
46
import static java.util.Collections.singleton;
57
import static java.util.Collections.singletonList;
68
import static java.util.Objects.requireNonNull;
9+
import static org.everit.json.schema.loader.SpecificationVersion.DRAFT_4;
10+
import static org.everit.json.schema.loader.SpecificationVersion.DRAFT_7;
711

12+
import java.util.ArrayList;
813
import java.util.Collection;
914
import java.util.HashSet;
1015
import java.util.List;
1116
import java.util.Optional;
1217
import java.util.Set;
1318

19+
import org.everit.json.schema.ArraySchema;
20+
import org.everit.json.schema.BooleanSchema;
21+
import org.everit.json.schema.CombinedSchema;
22+
import org.everit.json.schema.ConditionalSchema;
23+
import org.everit.json.schema.ConstSchema;
1424
import org.everit.json.schema.EnumSchema;
25+
import org.everit.json.schema.NotSchema;
26+
import org.everit.json.schema.NullSchema;
27+
import org.everit.json.schema.NumberSchema;
28+
import org.everit.json.schema.ObjectSchema;
1529
import org.everit.json.schema.Schema;
30+
import org.everit.json.schema.SchemaException;
1631

1732
class ExtractionResult {
1833

@@ -39,43 +54,91 @@ interface SchemaExtractor {
3954

4055
abstract class AbstractSchemaExtractor implements SchemaExtractor {
4156

57+
static final List<String> NUMBER_SCHEMA_PROPS = asList("minimum", "maximum",
58+
"exclusiveMinimum", "exclusiveMaximum", "multipleOf");
59+
60+
static final List<String> STRING_SCHEMA_PROPS = asList("minLength", "maxLength",
61+
"pattern", "format");
62+
4263
protected JsonObject schemaJson;
4364

4465
private Set<String> consumedKeys;
4566

67+
final SchemaLoader defaultLoader;
68+
69+
private ExclusiveLimitHandler exclusiveLimitHandler;
70+
71+
AbstractSchemaExtractor(SchemaLoader defaultLoader) {
72+
this.defaultLoader = requireNonNull(defaultLoader, "defaultLoader cannot be null");
73+
}
74+
4675
@Override
4776
public final ExtractionResult extract(JsonObject schemaJson) {
4877
this.schemaJson = requireNonNull(schemaJson, "schemaJson cannot be null");
78+
this.exclusiveLimitHandler = ExclusiveLimitHandler.ofSpecVersion(config().specVersion);
4979
consumedKeys = new HashSet<>(schemaJson.keySet().size());
5080
return new ExtractionResult(consumedKeys, extract());
5181
}
5282

53-
private void keyConsumed(String key) {
83+
void keyConsumed(String key) {
5484
if (schemaJson.keySet().contains(key)) {
5585
consumedKeys.add(key);
5686
}
5787
}
5888

59-
protected JsonValue require(String key) {
89+
JsonValue require(String key) {
6090
keyConsumed(key);
6191
return schemaJson.require(key);
6292
}
6393

64-
protected Optional<JsonValue> maybe(String key) {
94+
Optional<JsonValue> maybe(String key) {
6595
keyConsumed(key);
6696
return schemaJson.maybe(key);
6797
}
6898

69-
protected boolean containsKey(String key) {
99+
boolean containsKey(String key) {
70100
return schemaJson.containsKey(key);
71101
}
72102

73-
protected abstract List<Schema.Builder<?>> extract();
103+
boolean schemaHasAnyOf(Collection<String> propNames) {
104+
return propNames.stream().anyMatch(schemaJson::containsKey);
105+
}
106+
107+
LoaderConfig config() {
108+
return schemaJson.ls.config;
109+
}
110+
111+
ObjectSchema.Builder buildObjectSchema() {
112+
config().specVersion.objectKeywords().forEach(this::keyConsumed);
113+
return new ObjectSchemaLoader(schemaJson.ls, config(), defaultLoader).load();
114+
}
115+
116+
ArraySchema.Builder buildArraySchema() {
117+
config().specVersion.arrayKeywords().forEach(this::keyConsumed);
118+
return new ArraySchemaLoader(schemaJson.ls, config(), defaultLoader).load();
119+
}
120+
121+
NumberSchema.Builder buildNumberSchema() {
122+
PropertySnifferSchemaExtractor.NUMBER_SCHEMA_PROPS.forEach(this::keyConsumed);
123+
NumberSchema.Builder builder = NumberSchema.builder();
124+
maybe("minimum").map(JsonValue::requireNumber).ifPresent(builder::minimum);
125+
maybe("maximum").map(JsonValue::requireNumber).ifPresent(builder::maximum);
126+
maybe("multipleOf").map(JsonValue::requireNumber).ifPresent(builder::multipleOf);
127+
maybe("exclusiveMinimum").ifPresent(exclMin -> exclusiveLimitHandler.handleExclusiveMinimum(exclMin, builder));
128+
maybe("exclusiveMaximum").ifPresent(exclMax -> exclusiveLimitHandler.handleExclusiveMaximum(exclMax, builder));
129+
return builder;
130+
}
131+
132+
abstract List<Schema.Builder<?>> extract();
74133
}
75134

76135
class EnumSchemaExtractor extends AbstractSchemaExtractor {
77136

78-
@Override protected List<Schema.Builder<?>> extract() {
137+
EnumSchemaExtractor(SchemaLoader defaultLoader) {
138+
super(defaultLoader);
139+
}
140+
141+
@Override List<Schema.Builder<?>> extract() {
79142
if (!containsKey("enum")) {
80143
return emptyList();
81144
}
@@ -90,11 +153,132 @@ class EnumSchemaExtractor extends AbstractSchemaExtractor {
90153

91154
class ReferenceSchemaExtractor extends AbstractSchemaExtractor {
92155

93-
@Override protected List<Schema.Builder<?>> extract() {
156+
ReferenceSchemaExtractor(SchemaLoader defaultLoader) {
157+
super(defaultLoader);
158+
}
159+
160+
@Override List<Schema.Builder<?>> extract() {
94161
if (containsKey("$ref")) {
95162
String ref = require("$ref").requireString();
96163
return singletonList(new ReferenceLookup(schemaJson.ls).lookup(ref, schemaJson));
97164
}
98165
return emptyList();
99166
}
100167
}
168+
169+
class PropertySnifferSchemaExtractor extends AbstractSchemaExtractor {
170+
171+
static final List<String> CONDITIONAL_SCHEMA_KEYWORDS = asList("if", "then", "else");
172+
173+
PropertySnifferSchemaExtractor(SchemaLoader defaultLoader) {
174+
super(defaultLoader);
175+
}
176+
177+
@Override List<Schema.Builder<?>> extract() {
178+
List<Schema.Builder<?>> builders = new ArrayList<>(1);
179+
if (schemaHasAnyOf(config().specVersion.arrayKeywords())) {
180+
builders.add(new ArraySchemaLoader(schemaJson.ls, config(), defaultLoader).load().requiresArray(false));
181+
}
182+
if (schemaHasAnyOf(config().specVersion.objectKeywords())) {
183+
builders.add(new ObjectSchemaLoader(schemaJson.ls, config(), defaultLoader).load().requiresObject(false));
184+
}
185+
if (schemaHasAnyOf(NUMBER_SCHEMA_PROPS)) {
186+
builders.add(buildNumberSchema().requiresNumber(false));
187+
}
188+
if (schemaHasAnyOf(STRING_SCHEMA_PROPS)) {
189+
builders.add(new StringSchemaLoader(schemaJson.ls, config().formatValidators).load().requiresString(false));
190+
}
191+
if (config().specVersion.isAtLeast(DRAFT_7) && schemaHasAnyOf(CONDITIONAL_SCHEMA_KEYWORDS)) {
192+
builders.add(buildConditionalSchema());
193+
}
194+
return builders;
195+
}
196+
197+
private ConditionalSchema.Builder buildConditionalSchema() {
198+
ConditionalSchema.Builder builder = ConditionalSchema.builder();
199+
maybe("if").map(defaultLoader::loadChild).map(Schema.Builder::build).ifPresent(builder::ifSchema);
200+
maybe("then").map(defaultLoader::loadChild).map(Schema.Builder::build).ifPresent(builder::thenSchema);
201+
maybe("else").map(defaultLoader::loadChild).map(Schema.Builder::build).ifPresent(builder::elseSchema);
202+
return builder;
203+
}
204+
205+
}
206+
207+
class TypeBasedSchemaExtractor extends AbstractSchemaExtractor {
208+
209+
TypeBasedSchemaExtractor(SchemaLoader defaultLoader) {
210+
super(defaultLoader);
211+
}
212+
213+
@Override List<Schema.Builder<?>> extract() {
214+
if (containsKey("type")) {
215+
return singletonList(require("type").canBeMappedTo(JsonArray.class, arr -> (Schema.Builder) buildAnyOfSchemaForMultipleTypes())
216+
.orMappedTo(String.class, this::loadForExplicitType)
217+
.requireAny());
218+
} else {
219+
return emptyList();
220+
}
221+
}
222+
223+
private CombinedSchema.Builder buildAnyOfSchemaForMultipleTypes() {
224+
JsonArray subtypeJsons = require("type").requireArray();
225+
Collection<Schema> subschemas = new ArrayList<>(subtypeJsons.length());
226+
subtypeJsons.forEach((j, raw) -> {
227+
subschemas.add(loadForExplicitType(raw.requireString()).build());
228+
});
229+
return CombinedSchema.anyOf(subschemas);
230+
}
231+
232+
private Schema.Builder<?> loadForExplicitType(String typeString) {
233+
switch (typeString) {
234+
case "string":
235+
PropertySnifferSchemaExtractor.STRING_SCHEMA_PROPS.forEach(this::keyConsumed);
236+
return new StringSchemaLoader(schemaJson.ls, config().formatValidators).load();
237+
case "integer":
238+
return buildNumberSchema().requiresInteger(true);
239+
case "number":
240+
return buildNumberSchema();
241+
case "boolean":
242+
return BooleanSchema.builder();
243+
case "null":
244+
return NullSchema.builder();
245+
case "array":
246+
return buildArraySchema();
247+
case "object":
248+
return buildObjectSchema();
249+
default:
250+
throw new SchemaException(schemaJson.ls.locationOfCurrentObj(), format("unknown type: [%s]", typeString));
251+
}
252+
}
253+
254+
}
255+
256+
class NotSchemaExtractor extends AbstractSchemaExtractor {
257+
258+
NotSchemaExtractor(SchemaLoader defaultLoader) {
259+
super(defaultLoader);
260+
}
261+
262+
@Override List<Schema.Builder<?>> extract() {
263+
if (containsKey("not")) {
264+
Schema mustNotMatch = defaultLoader.loadChild(require("not")).build();
265+
return singletonList(NotSchema.builder().mustNotMatch(mustNotMatch));
266+
}
267+
return emptyList();
268+
}
269+
}
270+
271+
class ConstSchemaExtractor extends AbstractSchemaExtractor {
272+
273+
ConstSchemaExtractor(SchemaLoader defaultLoader) {
274+
super(defaultLoader);
275+
}
276+
277+
@Override List<Schema.Builder<?>> extract() {
278+
if (config().specVersion != DRAFT_4 && containsKey("const")) {
279+
return singletonList(ConstSchema.builder().permittedValue(require("const").unwrap()));
280+
} else {
281+
return emptyList();
282+
}
283+
}
284+
}

0 commit comments

Comments
 (0)