diff --git a/core/src/main/java/org/everit/json/schema/ArraySchema.java b/core/src/main/java/org/everit/json/schema/ArraySchema.java index 524841e05..5a3393e52 100644 --- a/core/src/main/java/org/everit/json/schema/ArraySchema.java +++ b/core/src/main/java/org/everit/json/schema/ArraySchema.java @@ -200,44 +200,45 @@ public boolean requiresArray() { return requiresArray; } - private Optional testItemCount(final JSONArray subject) { + private void testItemCount(final JSONArray subject, List validationExceptions) { int actualLength = subject.length(); if (minItems != null && actualLength < minItems) { - return Optional.of(failure("expected minimum item count: " + minItems + validationExceptions.add( + failure("expected minimum item count: " + minItems + ", found: " + actualLength, "minItems")); + return; } if (maxItems != null && maxItems < actualLength) { - return Optional.of(failure("expected maximum item count: " + maxItems + validationExceptions.add( + failure("expected maximum item count: " + maxItems + ", found: " + actualLength, "maxItems")); } - return Optional.empty(); } - private List testItems(final JSONArray subject) { - List rval = new ArrayList<>(); + private void testItems(final JSONArray subject, List validationExceptions) { if (allItemSchema != null) { validateItemsAgainstSchema(IntStream.range(0, subject.length()), subject, allItemSchema, - rval::add); + validationExceptions::add); } else if (itemSchemas != null) { if (!additionalItems && subject.length() > itemSchemas.size()) { - rval.add(failure(format("expected: [%d] array items, found: [%d]", + validationExceptions.add( + failure(format("expected: [%d] array items, found: [%d]", itemSchemas.size(), subject.length()), "items")); } int itemValidationUntil = Math.min(subject.length(), itemSchemas.size()); validateItemsAgainstSchema(IntStream.range(0, itemValidationUntil), subject, itemSchemas::get, - rval::add); + validationExceptions::add); if (schemaOfAdditionalItems != null) { validateItemsAgainstSchema(IntStream.range(itemValidationUntil, subject.length()), subject, schemaOfAdditionalItems, - rval::add); + validationExceptions::add); } } - return rval; } private void validateItemsAgainstSchema(final IntStream indices, final JSONArray items, @@ -257,58 +258,57 @@ private void validateItemsAgainstSchema(final IntStream indices, final JSONArray } } - private Optional testUniqueness(final JSONArray subject) { + private void testUniqueness(final JSONArray subject, List validationExceptions) { if (subject.length() == 0) { - return Optional.empty(); + return; } Collection uniqueItems = new ArrayList(subject.length()); for (int i = 0; i < subject.length(); ++i) { Object item = subject.get(i); for (Object contained : uniqueItems) { if (ObjectComparator.deepEquals(contained, item)) { - return Optional.of( + validationExceptions.add( failure("array items are not unique", "uniqueItems")); + return; } } uniqueItems.add(item); } - return Optional.empty(); } @Override public void validate(final Object subject) { - List failures = new ArrayList<>(); if (!(subject instanceof JSONArray)) { if (requiresArray) { throw failure(JSONArray.class, subject); } } else { + List validationExceptions = new ArrayList<>(); JSONArray arrSubject = (JSONArray) subject; - testItemCount(arrSubject).ifPresent(failures::add); + testItemCount(arrSubject, validationExceptions); if (uniqueItems) { - testUniqueness(arrSubject).ifPresent(failures::add); + testUniqueness(arrSubject, validationExceptions); + } + testItems(arrSubject, validationExceptions); + testContains(arrSubject, validationExceptions); + if (null != validationExceptions) { + ValidationException.throwFor(this, validationExceptions); } - failures.addAll(testItems(arrSubject)); - testContains(arrSubject).ifPresent(failures::add); } - ValidationException.throwFor(this, failures); } - private Optional testContains(JSONArray arrSubject) { + private void testContains(JSONArray arrSubject, List validationExceptions) { if (containedItemSchema == null) { - return Optional.empty(); + return; } - boolean anyMatch = IntStream.range(0, arrSubject.length()) - .mapToObj(arrSubject::get) - .map(item -> ifFails(containedItemSchema, item)) - .filter(maybeFailure -> !maybeFailure.isPresent()) - .findFirst() - .isPresent(); - if (anyMatch) { - return Optional.empty(); - } else { - return Optional.of(failure("expected at least one array item to match 'contains' schema", "contains")); + for (int i = 0; i < arrSubject.length(); i++) { + Optional exception = ifFails(containedItemSchema, arrSubject.get(i)); + if (!exception.isPresent()) { + return; + } } + validationExceptions.add( + failure("expected at least one array item to match 'contains' schema", "contains")); } @Override diff --git a/core/src/main/java/org/everit/json/schema/CombinedSchema.java b/core/src/main/java/org/everit/json/schema/CombinedSchema.java index 1ccaaf673..95465b0e1 100644 --- a/core/src/main/java/org/everit/json/schema/CombinedSchema.java +++ b/core/src/main/java/org/everit/json/schema/CombinedSchema.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; import org.everit.json.schema.internal.JSONPrinter; @@ -183,10 +182,13 @@ private ValidationException getFailure(final Schema schema, final Object subject @Override public void validate(final Object subject) { - List failures = subschemas.stream() - .map(schema -> getFailure(schema, subject)) - .filter(Objects::nonNull) - .collect(Collectors.toList()); + List failures = new ArrayList<>(); + for (Schema subschema: subschemas) { + ValidationException exception = getFailure(subschema, subject); + if (null != exception) { + failures.add(exception); + } + } int matchingCount = subschemas.size() - failures.size(); try { criterion.validate(subschemas.size(), matchingCount); @@ -202,9 +204,12 @@ public void validate(final Object subject) { @Override public boolean definesProperty(final String field) { - List matching = subschemas.stream() - .filter(schema -> schema.definesProperty(field)) - .collect(Collectors.toList()); + List matching = new ArrayList<>(); + for (Schema subschema: subschemas) { + if (subschema.definesProperty(field)) { + matching.add(subschema); + } + } try { criterion.validate(subschemas.size(), matching.size()); } catch (ValidationException e) { diff --git a/core/src/main/java/org/everit/json/schema/EnumSchema.java b/core/src/main/java/org/everit/json/schema/EnumSchema.java index 545f75e8c..78af590a7 100644 --- a/core/src/main/java/org/everit/json/schema/EnumSchema.java +++ b/core/src/main/java/org/everit/json/schema/EnumSchema.java @@ -72,12 +72,12 @@ public Set getPossibleValues() { @Override public void validate(final Object subject) { Object effectiveSubject = toJavaValue(subject); - possibleValues - .stream() - .filter(val -> ObjectComparator.deepEquals(val, effectiveSubject)) - .findAny() - .orElseThrow( - () -> failure(format("%s is not a valid enum value", subject), "enum")); + for (Object possibleValue: possibleValues) { + if (ObjectComparator.deepEquals(possibleValue, effectiveSubject)) { + return; + } + } + throw failure(format("%s is not a valid enum value", subject), "enum"); } @Override diff --git a/core/src/main/java/org/everit/json/schema/ObjectSchema.java b/core/src/main/java/org/everit/json/schema/ObjectSchema.java index 79cfc3803..188f85eec 100644 --- a/core/src/main/java/org/everit/json/schema/ObjectSchema.java +++ b/core/src/main/java/org/everit/json/schema/ObjectSchema.java @@ -6,14 +6,10 @@ import java.util.*; import java.util.Map.Entry; import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.lang.String.format; import static java.util.Arrays.asList; -import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toList; /** * Object schema validator. @@ -192,15 +188,18 @@ public ObjectSchema(final Builder builder) { this.propertyNameSchema = builder.propertyNameSchema; } - private Stream getAdditionalProperties(final JSONObject subject) { + private List getAdditionalProperties(final JSONObject subject) { String[] names = JSONObject.getNames(subject); if (names == null) { - return Stream.empty(); + return new ArrayList<>(); } else { - return Arrays - .stream(names) - .filter(key -> !propertySchemas.containsKey(key)) - .filter(key -> !matchesAnyPattern(key)); + List namesList = new ArrayList<>(); + for (String name:names) { + if (!propertySchemas.containsKey(name) && !matchesAnyPattern(name)) { + namesList.add(name); + } + } + return namesList; } } @@ -250,10 +249,12 @@ private Optional ifFails(final Schema schema, final Object } private boolean matchesAnyPattern(final String key) { - return patternProperties.keySet().stream() - .filter(pattern -> pattern.matcher(key).find()) - .findAny() - .isPresent(); + for (Pattern pattern: patternProperties.keySet()) { + if (pattern.matcher(key).find()) { + return true; + } + } + return false; } public boolean permitsAdditionalProperties() { @@ -264,101 +265,103 @@ public boolean requiresObject() { return requiresObject; } - private List testAdditionalProperties(final JSONObject subject) { + private void testAdditionalProperties(final JSONObject subject, List validationExceptions) { if (!additionalProperties) { - return getAdditionalProperties(subject) - .map(unneeded -> format("extraneous key [%s] is not permitted", unneeded)) - .map(msg -> new ValidationException(this, msg, "additionalProperties")) - .collect(toList()); + List additionalProperties = getAdditionalProperties(subject); + if (null == additionalProperties || additionalProperties.isEmpty()) { + return; + } + for (String additionalProperty: additionalProperties) { + validationExceptions.add(new ValidationException(this, + format("extraneous key [%s] is not permitted", additionalProperty), "additionalProperties")); + } } else if (schemaOfAdditionalProperties != null) { - List additionalPropNames = getAdditionalProperties(subject) - .collect(toList()); - List rval = new ArrayList(); + List additionalPropNames = getAdditionalProperties(subject); for (String propName : additionalPropNames) { Object propVal = subject.get(propName); - ifFails(schemaOfAdditionalProperties, propVal) - .map(failure -> failure.prepend(propName, this)) - .ifPresent(rval::add); + Optional exception = ifFails(schemaOfAdditionalProperties, propVal); + if (exception.isPresent()) { + validationExceptions.add(exception.get().prepend(propName, this)); + } } - return rval; } - return emptyList(); } - private List testPatternProperties(final JSONObject subject) { + private void testPatternProperties(final JSONObject subject, List validationExceptions) { String[] propNames = JSONObject.getNames(subject); if (propNames == null || propNames.length == 0) { - return emptyList(); + return; } - List rval = new ArrayList<>(); for (Entry entry : patternProperties.entrySet()) { for (String propName : propNames) { if (entry.getKey().matcher(propName).find()) { - ifFails(entry.getValue(), subject.get(propName)) - .map(exc -> exc.prepend(propName)) - .ifPresent(rval::add); + Optional exception = ifFails(entry.getValue(), subject.get(propName)); + if (exception.isPresent()) { + validationExceptions.add(exception.get().prepend(propName)); + } } } } - return rval; } - private List testProperties(final JSONObject subject) { + private void testProperties(final JSONObject subject, List validationExceptions) { if (propertySchemas != null) { - List rval = new ArrayList<>(); for (Entry entry : propertySchemas.entrySet()) { String key = entry.getKey(); if (subject.has(key)) { - ifFails(entry.getValue(), subject.get(key)) - .map(exc -> exc.prepend(key)) - .ifPresent(rval::add); + Optional exception = ifFails(entry.getValue(), subject.get(key)); + if (exception.isPresent()) { + validationExceptions.add(exception.get().prepend(key)); + } } } - return rval; } - return emptyList(); } - private List testPropertyDependencies(final JSONObject subject) { - return propertyDependencies.keySet().stream() - .filter(subject::has) - .flatMap(ifPresent -> propertyDependencies.get(ifPresent).stream()) - .filter(mustBePresent -> !subject.has(mustBePresent)) - .map(missingKey -> format("property [%s] is required", missingKey)) - .map(excMessage -> failure(excMessage, "dependencies")) - .collect(toList()); + private void testPropertyDependencies(final JSONObject subject, List validationExceptions) { + for (String property: propertyDependencies.keySet()) { + if (subject.has(property)) { + for (String mustBePresent : propertyDependencies.get(property)) { + if (!subject.has(mustBePresent)) { + validationExceptions.add( + failure(format("property [%s] is required", mustBePresent), "dependencies")); + } + } + } + } } - private List testRequiredProperties(final JSONObject subject) { - return requiredProperties.stream() - .filter(key -> !subject.has(key)) - .map(missingKey -> format("required key [%s] not found", missingKey)) - .map(excMessage -> failure(excMessage, "required")) - .collect(toList()); + private void testRequiredProperties(final JSONObject subject, List validationExceptions) { + for (String required:requiredProperties) { + if (!subject.has(required)) { + validationExceptions.add( + failure(format("required key [%s] not found", required), "required")); + } + } } - private List testSchemaDependencies(final JSONObject subject) { - List rval = new ArrayList<>(); + private void testSchemaDependencies(final JSONObject subject, List validationExceptions) { for (Map.Entry schemaDep : schemaDependencies.entrySet()) { String propName = schemaDep.getKey(); if (subject.has(propName)) { - ifFails(schemaDep.getValue(), subject).ifPresent(rval::add); + ifFails(schemaDep.getValue(), subject).ifPresent(validationExceptions::add); } } - return rval; } - private List testSize(final JSONObject subject) { + private void testSize(final JSONObject subject, List validationExceptions) { int actualSize = subject.length(); if (minProperties != null && actualSize < minProperties.intValue()) { - return asList(failure(format("minimum size: [%d], found: [%d]", minProperties, actualSize), - "minProperties")); + validationExceptions.addAll( + asList(failure(format("minimum size: [%d], found: [%d]", minProperties, actualSize), + "minProperties"))); + return; } if (maxProperties != null && actualSize > maxProperties.intValue()) { - return asList(failure(format("maximum size: [%d], found: [%d]", maxProperties, actualSize), - "maxProperties")); + validationExceptions.addAll( + asList(failure(format("maximum size: [%d], found: [%d]", maxProperties, actualSize), + "maxProperties"))); } - return emptyList(); } @Override @@ -368,40 +371,34 @@ public void validate(final Object subject) { throw failure(JSONObject.class, subject); } } else { - List failures = new ArrayList<>(); + List validationExceptions = new ArrayList<>(); JSONObject objSubject = (JSONObject) subject; - failures.addAll(testProperties(objSubject)); - failures.addAll(testRequiredProperties(objSubject)); - failures.addAll(testAdditionalProperties(objSubject)); - failures.addAll(testSize(objSubject)); - failures.addAll(testPropertyDependencies(objSubject)); - failures.addAll(testSchemaDependencies(objSubject)); - failures.addAll(testPatternProperties(objSubject)); - failures.addAll(testPropertyNames(objSubject)); - ValidationException.throwFor(this, failures); + testProperties(objSubject, validationExceptions); + testRequiredProperties(objSubject, validationExceptions); + testAdditionalProperties(objSubject, validationExceptions); + testSize(objSubject, validationExceptions); + testPropertyDependencies(objSubject, validationExceptions); + testSchemaDependencies(objSubject, validationExceptions); + testPatternProperties(objSubject, validationExceptions); + testPropertyNames(objSubject, validationExceptions); + ValidationException.throwFor(this, validationExceptions); } } - private Collection testPropertyNames(JSONObject subject) { + private void testPropertyNames(JSONObject subject, List validationExceptions) { if (propertyNameSchema != null) { String[] names = JSONObject.getNames(subject); if (names == null || names.length == 0) { - return emptyList(); + return; } - Collection failures = Arrays.stream(names) - .map(name -> { - try { - propertyNameSchema.validate(name); - return null; - } catch (ValidationException e) { - return e.prepend(name); - } - }) - .filter(Objects::nonNull) - .collect(toList()); - return failures; - } - return emptyList(); + for (String name: names) { + try { + propertyNameSchema.validate(name); + } catch (ValidationException e) { + validationExceptions.add(e.prepend(name)); + } + } + } } @Override @@ -435,21 +432,26 @@ private boolean definesSchemaProperty(String current, final String remaining) { } private boolean definesPatternProperty(final String current, final String remaining) { - return patternProperties.keySet() - .stream() - .filter(pattern -> pattern.matcher(current).matches()) - .map(pattern -> patternProperties.get(pattern)) - .filter(schema -> remaining == null || schema.definesProperty(remaining)) - .findAny() - .isPresent(); + for (Pattern pattern: patternProperties.keySet()) { + if (pattern.matcher(current).matches()) { + if (remaining == null || patternProperties.get(pattern).definesProperty(remaining)) { + return true; + } + } + } + return false; } private boolean definesSchemaDependencyProperty(final String field) { - return schemaDependencies.containsKey(field) - || schemaDependencies.values().stream() - .filter(schema -> schema.definesProperty(field)) - .findAny() - .isPresent(); + if (schemaDependencies.containsKey(field)) { + return true; + } + for (Schema schema: schemaDependencies.values()) { + if (schema.definesProperty(field)) { + return true; + } + } + return false; } private String unescape(final String value) { diff --git a/core/src/main/java/org/everit/json/schema/Schema.java b/core/src/main/java/org/everit/json/schema/Schema.java index 0c527f714..66ae05b04 100644 --- a/core/src/main/java/org/everit/json/schema/Schema.java +++ b/core/src/main/java/org/everit/json/schema/Schema.java @@ -1,7 +1,6 @@ package org.everit.json.schema; import org.everit.json.schema.internal.JSONPrinter; -import org.json.JSONPointer; import org.json.JSONWriter; import java.io.StringWriter; diff --git a/core/src/main/java/org/everit/json/schema/StringSchema.java b/core/src/main/java/org/everit/json/schema/StringSchema.java index 0f451e7c1..f13b6a782 100644 --- a/core/src/main/java/org/everit/json/schema/StringSchema.java +++ b/core/src/main/java/org/everit/json/schema/StringSchema.java @@ -117,27 +117,26 @@ public Pattern getPattern() { return pattern; } - private List testLength(final String subject) { + private void testLength(final String subject, List validationExceptions) { int actualLength = subject.codePointCount(0, subject.length()); - List rval = new ArrayList<>(); if (minLength != null && actualLength < minLength.intValue()) { - rval.add(failure("expected minLength: " + minLength + ", actual: " + validationExceptions.add( + failure("expected minLength: " + minLength + ", actual: " + actualLength, "minLength")); } if (maxLength != null && actualLength > maxLength.intValue()) { - rval.add(failure("expected maxLength: " + maxLength + ", actual: " + validationExceptions.add( + failure("expected maxLength: " + maxLength + ", actual: " + actualLength, "maxLength")); } - return rval; } - private List testPattern(final String subject) { + private void testPattern(final String subject, List validationExceptions) { if (pattern != null && !pattern.matcher(subject).find()) { String message = format("string [%s] does not match pattern %s", subject, pattern.pattern()); - return Arrays.asList(failure(message, "pattern")); + validationExceptions.addAll(Arrays.asList(failure(message, "pattern"))); } - return Collections.emptyList(); } @Override @@ -147,14 +146,17 @@ public void validate(final Object subject) { throw failure(String.class, subject); } } else { + List validationExceptions = new ArrayList<>(); String stringSubject = (String) subject; - List rval = new ArrayList<>(); - rval.addAll(testLength(stringSubject)); - rval.addAll(testPattern(stringSubject)); - formatValidator.validate(stringSubject) - .map(failure -> failure(failure, "format")) - .ifPresent(rval::add); - ValidationException.throwFor(this, rval); + testLength(stringSubject, validationExceptions); + testPattern(stringSubject, validationExceptions); + Optional failure = formatValidator.validate(stringSubject); + if (failure.isPresent()) { + validationExceptions.add(failure(failure.get(), "format")); + } + if (null != validationExceptions) { + ValidationException.throwFor(this, validationExceptions); + } } } diff --git a/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java b/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java index a21a64a18..c5bcc142c 100644 --- a/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java +++ b/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java @@ -20,7 +20,6 @@ import org.everit.json.schema.loader.SchemaLoader; import org.json.JSONObject; import org.json.JSONPointer; -import org.junit.Assert; import org.junit.Test; import java.util.List;