Skip to content

Commit c07bf38

Browse files
committed
implementing AdjacentSchemaExtractionState and integrating into SchemaLoader, also some testing improvements
1 parent 6802f57 commit c07bf38

File tree

9 files changed

+171
-27
lines changed

9 files changed

+171
-27
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.everit.json.schema.loader;
2+
3+
import java.util.Collection;
4+
import java.util.HashSet;
5+
import java.util.Set;
6+
7+
import org.everit.json.schema.Schema;
8+
9+
class AdjacentSchemaExtractionState {
10+
11+
private final JsonObject context;
12+
13+
private final Set<Schema.Builder<?>> extractedSchemas;
14+
15+
AdjacentSchemaExtractionState(JsonObject context) {
16+
this(context, new HashSet<>());
17+
}
18+
19+
private AdjacentSchemaExtractionState(JsonObject context, Set<Schema.Builder<?>> extractedSchemas) {
20+
this.context = context;
21+
this.extractedSchemas = extractedSchemas;
22+
}
23+
24+
AdjacentSchemaExtractionState reduce(ExtractionResult result) {
25+
Set<Schema.Builder<?>> newExtractedSchemas = new HashSet<>(extractedSchemas.size() + result.extractedSchemas.size());
26+
newExtractedSchemas.addAll(extractedSchemas);
27+
newExtractedSchemas.addAll(result.extractedSchemas);
28+
JsonObject projectedContext = new ProjectedJsonObject(context, result.consumedKeys);
29+
return new AdjacentSchemaExtractionState(projectedContext, newExtractedSchemas);
30+
}
31+
32+
public JsonObject projectedSchemaJson() {
33+
return context;
34+
}
35+
36+
public Collection<Schema.Builder<?>> extractedSchemaBuilders() {
37+
return extractedSchemas;
38+
}
39+
}

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33
import static java.util.Objects.requireNonNull;
44
import static java.util.stream.Collectors.toList;
5+
import static java.util.stream.Collectors.toSet;
56

67
import java.util.ArrayList;
78
import java.util.Collection;
89
import java.util.HashMap;
9-
import java.util.List;
1010
import java.util.Map;
11+
import java.util.Set;
1112
import java.util.function.Function;
1213

1314
import org.everit.json.schema.CombinedSchema;
@@ -42,16 +43,18 @@ public CombinedSchemaLoader(SchemaLoader defaultLoader) {
4243
}
4344

4445
@Override
45-
public Collection<Schema.Builder<?>> extract(LoadingState ls) {
46-
List<String> presentKeys = COMB_SCHEMA_PROVIDERS.keySet().stream()
47-
.filter(ls.schemaJson()::containsKey)
46+
public ExtractionResult extract(JsonObject schemaJson) {
47+
Set<String> presentKeys = COMB_SCHEMA_PROVIDERS.keySet().stream()
48+
.filter(schemaJson::containsKey)
49+
.collect(toSet());
50+
Collection<Schema.Builder<?>> extractedSchemas = presentKeys.stream().map(key -> loadCombinedSchemaForKeyword(schemaJson, key))
4851
.collect(toList());
49-
return presentKeys.stream().map(key -> loadCombinedSchemaForKeyword(ls, key)).collect(toList());
52+
return new ExtractionResult(presentKeys, extractedSchemas);
5053
}
5154

52-
private CombinedSchema.Builder loadCombinedSchemaForKeyword(LoadingState ls, String key) {
55+
private CombinedSchema.Builder loadCombinedSchemaForKeyword(JsonObject schemaJson, String key) {
5356
Collection<Schema> subschemas = new ArrayList<>();
54-
ls.schemaJson().require(key).requireArray()
57+
schemaJson.require(key).requireArray()
5558
.forEach((i, subschema) -> subschemas.add(defaultLoader.loadChild(subschema).build()));
5659
return COMB_SCHEMA_PROVIDERS.get(key).apply(subschemas);
5760
}

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@
1212
import java.util.function.Function;
1313

1414
import org.everit.json.schema.SchemaException;
15-
import org.json.JSONObject;
1615

1716
/**
1817
* @author erosb
1918
*/
20-
final class JsonObject extends JsonValue {
19+
class JsonObject extends JsonValue {
2120

22-
private final Map<String, Object> storage;
21+
final Map<String, Object> storage;
2322

2423
JsonObject(Map<String, Object> storage) {
2524
super(storage);
Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.everit.json.schema.loader;
22

33
import static java.util.Arrays.asList;
4-
import static java.util.Collections.emptyList;
4+
import static java.util.Collections.emptySet;
5+
import static java.util.Collections.singleton;
6+
import static java.util.Objects.requireNonNull;
7+
import static org.everit.json.schema.loader.ExtractionResult.EMPTY;
58

69
import java.util.Collection;
710
import java.util.HashSet;
@@ -10,23 +13,42 @@
1013
import org.everit.json.schema.EnumSchema;
1114
import org.everit.json.schema.Schema;
1215

16+
class ExtractionResult {
17+
18+
static final ExtractionResult EMPTY = new ExtractionResult(emptySet(), emptySet());
19+
20+
final Set<String> consumedKeys;
21+
22+
final Collection<Schema.Builder<?>> extractedSchemas;
23+
24+
ExtractionResult(Set<String> consumedKeys, Collection<Schema.Builder<?>> extractedSchemas) {
25+
this.consumedKeys = requireNonNull(consumedKeys, "consumedKeys cannot be null");
26+
this.extractedSchemas = requireNonNull(extractedSchemas, "extractedSchemas cannot be null");
27+
}
28+
29+
ExtractionResult(String consumedKeys, Collection<Schema.Builder<?>> extactedSchemas) {
30+
this(singleton(consumedKeys), extactedSchemas);
31+
}
32+
33+
}
34+
1335
interface SchemaExtractor {
1436

15-
Collection<Schema.Builder<?>> extract(LoadingState ls);
37+
ExtractionResult extract(JsonObject schemaJson);
1638

1739
}
1840

1941
class EnumSchemaExtractor implements SchemaExtractor {
2042

21-
@Override public Collection<Schema.Builder<?>> extract(LoadingState ls) {
22-
if (!ls.schemaJson().containsKey("enum")) {
23-
return emptyList();
43+
@Override public ExtractionResult extract(JsonObject schemaJson) {
44+
if (!schemaJson.containsKey("enum")) {
45+
return EMPTY;
2446
}
2547
EnumSchema.Builder builder = EnumSchema.builder();
2648
Set<Object> possibleValues = new HashSet<>();
27-
ls.schemaJson().require("enum").requireArray().forEach((i, item) -> possibleValues.add(item.unwrap()));
49+
schemaJson.require("enum").requireArray().forEach((i, item) -> possibleValues.add(item.unwrap()));
2850
builder.possibleValues(possibleValues);
29-
return asList(builder);
51+
return new ExtractionResult("enum", asList(builder));
3052
}
3153

3254
}

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,17 @@ private Schema.Builder loadSchemaBoolean(Boolean rawBoolean) {
412412
}
413413

414414
private Schema.Builder loadSchemaObject(JsonObject o) {
415-
List<Schema.Builder> extractedSchemas = new ArrayList<>(1);
415+
Collection<Schema.Builder<?>> extractedSchemas = new ArrayList<>(1);
416416
List<SchemaExtractor> extractors = asList(new EnumSchemaExtractor(), new CombinedSchemaLoader(this));
417-
extractors.stream().map(extractor -> extractor.extract(ls)).forEach(extractedSchemas::addAll);
417+
// extractors.stream().map(extractor -> extractor.extract(o)).forEach(extractedSchemas::addAll);
418+
419+
AdjacentSchemaExtractionState state = new AdjacentSchemaExtractionState(o);
420+
for (SchemaExtractor extractor : extractors) {
421+
ExtractionResult result = extractor.extract(state.projectedSchemaJson());
422+
state = state.reduce(result);
423+
}
424+
extractedSchemas = state.extractedSchemaBuilders();
425+
418426
//////////////////////
419427
Schema.Builder builder;
420428
if (ls.schemaJson().containsKey("const") && (config.specVersion != DRAFT_4)) {
@@ -438,7 +446,7 @@ private Schema.Builder loadSchemaObject(JsonObject o) {
438446
if (extractedSchemas.isEmpty()) {
439447
effectiveReturnedSchema = EmptySchema.builder();
440448
} else if (extractedSchemas.size() == 1) {
441-
effectiveReturnedSchema = extractedSchemas.get(0);
449+
effectiveReturnedSchema = extractedSchemas.iterator().next();
442450
} else {
443451
Collection<Schema> built = extractedSchemas.stream()
444452
.map(Schema.Builder::build)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package org.everit.json.schema.loader;
2+
3+
import static java.util.Arrays.asList;
4+
import static java.util.Collections.singleton;
5+
import static org.junit.Assert.assertEquals;
6+
7+
import org.everit.json.schema.ConstSchema;
8+
import org.junit.Test;
9+
10+
import com.google.common.collect.ImmutableMap;
11+
12+
public class AdjacentSchemaExtractionStateTest {
13+
14+
@Test
15+
public void testReduce() {
16+
AdjacentSchemaExtractionState original = new AdjacentSchemaExtractionState(JsonValue.of(ImmutableMap.builder()
17+
.put("const", "2")
18+
.put("minimum", 1)
19+
.build()
20+
).requireObject());
21+
ConstSchema.ConstSchemaBuilder schemaBuilder = ConstSchema.builder().permittedValue("2");
22+
23+
AdjacentSchemaExtractionState actual = original.reduce(new ExtractionResult("const", asList(schemaBuilder)));
24+
25+
assertEquals(singleton(schemaBuilder), actual.extractedSchemaBuilders());
26+
assertEquals(singleton("minimum"), actual.projectedSchemaJson().keySet());
27+
}
28+
}

core/src/test/java/org/everit/json/schema/loader/CombinedSchemaLoaderTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,11 @@ public void combinedSchemaWithMultipleBaseSchemas() {
6565
@Test
6666
public void multipleCombinedSchemasAtTheSameNestingLevel() {
6767
SchemaLoader defaultLoader = SchemaLoader.builder().schemaJson(get("multipleKeywords")).build();
68-
JsonValue json = JsonValue.of(get("multipleKeywords"));
68+
JsonObject json = JsonValue.of(get("multipleKeywords")).requireObject();
6969
new LoadingState(LoaderConfig.defaultV4Config(), emptyMap(), json, json, null, emptyList());
7070
CombinedSchemaLoader subject = new CombinedSchemaLoader(defaultLoader);
71-
Set<Schema> actual = new HashSet<>(subject.extract(json.ls).stream().map(builder -> builder.build()).collect(toList()));
71+
Set<Schema> actual = new HashSet<>(
72+
subject.extract(json).extractedSchemas.stream().map(builder -> builder.build()).collect(toList()));
7273
HashSet<CombinedSchema> expected = new HashSet<>(asList(
7374
CombinedSchema.allOf(singletonList(BooleanSchema.INSTANCE)).build(),
7475
CombinedSchema.anyOf(singletonList(StringSchema.builder().build())).build()

tests/src/test/java/org/everit/json/schema/IssueTest.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.everit.json.schema;
22

33
import static java.util.Objects.requireNonNull;
4+
import static java.util.stream.Collectors.joining;
45

56
import java.io.File;
67
import java.io.FileInputStream;
@@ -12,6 +13,7 @@
1213
import java.util.ArrayList;
1314
import java.util.Arrays;
1415
import java.util.HashMap;
16+
import java.util.HashSet;
1517
import java.util.List;
1618
import java.util.Map;
1719
import java.util.Objects;
@@ -184,9 +186,16 @@ private void validate(final File file, final Schema schema, final boolean should
184186
Optional<File> expectedFile = fileByName("expectedException.json");
185187
if (expectedFile.isPresent()) {
186188
if (!checkExpectedValues(expectedFile.get(), thrown)) {
189+
expectedFailureList.stream()
190+
.filter(exp -> !validationFailureList.contains(exp))
191+
.forEach(System.out::println);
192+
System.out.println("--");
193+
validationFailureList.stream()
194+
.filter(exp -> !expectedFailureList.contains(exp))
195+
.forEach(System.out::println);
187196
Assert.fail("Validation failures do not match expected values: \n" +
188-
"Expected: " + expectedFailureList + ",\nActual: " +
189-
validationFailureList);
197+
"Expected: " + expectedFailureList.stream().collect(joining("\n\t")) + ",\nActual: " +
198+
validationFailureList.stream().collect(joining("\n\t")));
190199
}
191200
}
192201
}
@@ -234,20 +243,19 @@ private Object loadJsonFile(final File file) {
234243
*/
235244
private boolean checkExpectedValues(final File expectedExceptionsFile,
236245
final ValidationException ve) {
237-
238246
// Read the expected values from user supplied file
239247
Object expected = loadJsonFile(expectedExceptionsFile);
240248
expectedFailureList = new ArrayList<String>();
241249
// NOTE: readExpectedValues() will update expectedFailureList
242250
readExpectedValues((JSONObject) expected);
243251

244252
// Read the actual validation failures into a list
245-
validationFailureList = new ArrayList<String>();
253+
validationFailureList = new ArrayList<>();
246254
// NOTE: processValidationFailures() will update validationFailureList
247255
processValidationFailures(ve);
248256

249257
// Compare expected to actual
250-
return expectedFailureList.equals(validationFailureList);
258+
return new HashSet<>(expectedFailureList).equals(new HashSet<>(validationFailureList));
251259
}
252260

253261
// Recursively process the ValidationExceptions, which can contain lists
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"causingExceptions": [
3+
{
4+
"causingExceptions": [
5+
{
6+
"causingExceptions": [
7+
{
8+
"causingExceptions": [],
9+
"message": "#/prop: 4 is not a valid enum value"
10+
}
11+
],
12+
"message": "#/prop: #: only 1 subschema matches out of 2"
13+
},
14+
{
15+
"causingExceptions": [],
16+
"message": "#/prop: 4 is not less or equal to 0"
17+
},
18+
{
19+
"causingExceptions": [],
20+
"message": "#/prop: subject must not be valid against schema {}"
21+
}
22+
],
23+
"message": "#/prop: #: 0 subschemas matched instead of one"
24+
},
25+
{
26+
"causingExceptions": [
27+
{
28+
"causingExceptions": [],
29+
"message": "#/prop: 4 is not a multiple of 3"
30+
}
31+
],
32+
"message": "#/prop: #: only 1 subschema matches out of 2"
33+
}
34+
],
35+
"message": "#/prop: #: only 0 subschema matches out of 2"
36+
}

0 commit comments

Comments
 (0)