From 1842edbffb645af57bb8054973cb7722b6729377 Mon Sep 17 00:00:00 2001 From: Josh McCullough Date: Wed, 17 Aug 2022 19:45:29 -0400 Subject: [PATCH] switch HashMap for LinkedHashMap to perserve order of things #395 --- .../org/everit/json/schema/ObjectSchema.java | 25 ++++++------- .../everit/json/schema/ReferenceSchema.java | 8 ++--- .../java/org/everit/json/schema/Schema.java | 11 +++--- .../everit/json/schema/loader/JsonObject.java | 12 +++---- .../everit/json/schema/loader/JsonValue.java | 16 ++++----- .../json/schema/loader/OrgJsonUtil.java | 3 +- .../schema/loader/ProjectedJsonObject.java | 14 ++++---- .../json/schema/loader/ReferenceLookup.java | 9 ++--- .../schema/DynamicReferenceSchemaExample.java | 8 ++--- .../everit/json/schema/ObjectSchemaTest.java | 36 +++++++++---------- .../json/schema/internal/JSONPrinterTest.java | 17 ++++----- .../json/schema/loader/JsonObjectTest.java | 32 +++++++++-------- .../loader/JsonPointerEvaluatorTest.java | 28 ++++++++------- .../schema/ObjectSchemaPropertyOrderTest.java | 35 ++++++++++++++++++ 14 files changed, 150 insertions(+), 104 deletions(-) create mode 100644 tests/vanilla/src/main/java/org/everit/json/schema/ObjectSchemaPropertyOrderTest.java 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 b1be0268b..fe1d07ad4 100644 --- a/core/src/main/java/org/everit/json/schema/ObjectSchema.java +++ b/core/src/main/java/org/everit/json/schema/ObjectSchema.java @@ -1,23 +1,24 @@ package org.everit.json.schema; -import static java.util.Objects.requireNonNull; -import static java.util.stream.Collectors.toMap; -import static org.everit.json.schema.JSONPointer.unescape; +import org.everit.json.schema.regexp.JavaUtilRegexpFactory; +import org.everit.json.schema.regexp.Regexp; +import org.everit.json.schema.regexp.RegexpFactory; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; -import org.everit.json.schema.regexp.JavaUtilRegexpFactory; -import org.everit.json.schema.regexp.Regexp; -import org.everit.json.schema.regexp.RegexpFactory; +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.toMap; +import static org.everit.json.schema.JSONPointer.unescape; /** * Object schema validator. @@ -35,13 +36,13 @@ private static Regexp toRegexp(String pattern) { return DEFAULT_REGEXP_FACTORY.createHandler(pattern); } - private final Map patternProperties = new HashMap<>(); + private final Map patternProperties = new LinkedHashMap<>(); private boolean requiresObject = true; - private final Map definitionSchemas = new HashMap<>(); + private final Map definitionSchemas = new LinkedHashMap<>(); - private final Map propertySchemas = new HashMap<>(); + private final Map propertySchemas = new LinkedHashMap<>(); private boolean additionalProperties = true; @@ -53,9 +54,9 @@ private static Regexp toRegexp(String pattern) { private Integer maxProperties; - private final Map> propertyDependencies = new HashMap<>(); + private final Map> propertyDependencies = new LinkedHashMap<>(); - private final Map schemaDependencies = new HashMap<>(); + private final Map schemaDependencies = new LinkedHashMap<>(); private Schema propertyNameSchema; @@ -190,7 +191,7 @@ public static Builder builder() { } private static Map copyMap(Map original) { - return Collections.unmodifiableMap(new HashMap<>(original)); + return Collections.unmodifiableMap(new LinkedHashMap<>(original)); } private final Map definitionSchemas; diff --git a/core/src/main/java/org/everit/json/schema/ReferenceSchema.java b/core/src/main/java/org/everit/json/schema/ReferenceSchema.java index 76b05053a..0d82ff20a 100644 --- a/core/src/main/java/org/everit/json/schema/ReferenceSchema.java +++ b/core/src/main/java/org/everit/json/schema/ReferenceSchema.java @@ -1,11 +1,11 @@ package org.everit.json.schema; -import static java.util.Objects.requireNonNull; - -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; +import static java.util.Objects.requireNonNull; + /** * This class is used by {@link org.everit.json.schema.loader.SchemaLoader} to resolve JSON pointers * during the construction of the schema. This class has been made mutable to permit the loading of @@ -50,7 +50,7 @@ public Builder refValue(String refValue) { @Override public ReferenceSchema.Builder unprocessedProperties(Map unprocessedProperties) { if (retval != null) { - retval.unprocessedProperties = new HashMap<>(unprocessedProperties); + retval.unprocessedProperties = new LinkedHashMap<>(unprocessedProperties); } super.unprocessedProperties(unprocessedProperties); return this; 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 134a67578..0135eff2e 100644 --- a/core/src/main/java/org/everit/json/schema/Schema.java +++ b/core/src/main/java/org/everit/json/schema/Schema.java @@ -1,14 +1,15 @@ package org.everit.json.schema; -import static java.util.Collections.unmodifiableMap; +import org.everit.json.schema.internal.JSONPrinter; +import org.json.JSONWriter; import java.io.StringWriter; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; -import org.everit.json.schema.internal.JSONPrinter; -import org.json.JSONWriter; +import static java.util.Collections.unmodifiableMap; /** * Superclass of all other schema validator classes of this package. @@ -41,7 +42,7 @@ public abstract static class Builder> private Boolean writeOnly = null; - public Map unprocessedProperties = new HashMap<>(0); + public Map unprocessedProperties = new LinkedHashMap<>(0); public B title(String title) { this.title = title; @@ -139,7 +140,7 @@ protected Schema(Builder builder) { this.nullable = builder.nullable; this.readOnly = builder.readOnly; this.writeOnly = builder.writeOnly; - this.unprocessedProperties = new HashMap<>(builder.unprocessedProperties); + this.unprocessedProperties = new LinkedHashMap<>(builder.unprocessedProperties); } /** diff --git a/core/src/main/java/org/everit/json/schema/loader/JsonObject.java b/core/src/main/java/org/everit/json/schema/loader/JsonObject.java index 3008db7e7..d054880af 100644 --- a/core/src/main/java/org/everit/json/schema/loader/JsonObject.java +++ b/core/src/main/java/org/everit/json/schema/loader/JsonObject.java @@ -1,17 +1,17 @@ package org.everit.json.schema.loader; -import static java.lang.String.format; -import static java.util.Collections.unmodifiableMap; -import static java.util.Collections.unmodifiableSet; +import org.everit.json.schema.SchemaException; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; -import org.everit.json.schema.SchemaException; +import static java.lang.String.format; +import static java.util.Collections.unmodifiableMap; +import static java.util.Collections.unmodifiableSet; /** * @author erosb @@ -97,7 +97,7 @@ private void iterateOnEntry(Map.Entry entry, JsonObjectIterator } @Override protected Object unwrap() { - return new HashMap<>(storage); + return new LinkedHashMap<>(storage); } Map toMap() { diff --git a/core/src/main/java/org/everit/json/schema/loader/JsonValue.java b/core/src/main/java/org/everit/json/schema/loader/JsonValue.java index 9fe537ed1..b89d9b94e 100644 --- a/core/src/main/java/org/everit/json/schema/loader/JsonValue.java +++ b/core/src/main/java/org/everit/json/schema/loader/JsonValue.java @@ -1,11 +1,11 @@ package org.everit.json.schema.loader; -import static org.everit.json.schema.loader.OrgJsonUtil.toList; -import static org.everit.json.schema.loader.OrgJsonUtil.toMap; -import static org.everit.json.schema.loader.SpecificationVersion.DRAFT_4; +import org.everit.json.schema.SchemaException; +import org.json.JSONArray; +import org.json.JSONObject; -import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -13,9 +13,9 @@ import java.util.function.Consumer; import java.util.function.Function; -import org.everit.json.schema.SchemaException; -import org.json.JSONArray; -import org.json.JSONObject; +import static org.everit.json.schema.loader.OrgJsonUtil.toList; +import static org.everit.json.schema.loader.OrgJsonUtil.toMap; +import static org.everit.json.schema.loader.SpecificationVersion.DRAFT_4; /** * @author erosb @@ -24,7 +24,7 @@ class JsonValue { class Multiplexer { - protected Map, Function> actions = new HashMap<>(); + protected Map, Function> actions = new LinkedHashMap<>(); Multiplexer(Class expectedType, Function mapper) { actions.put(expectedType, mapper); diff --git a/core/src/main/java/org/everit/json/schema/loader/OrgJsonUtil.java b/core/src/main/java/org/everit/json/schema/loader/OrgJsonUtil.java index 88703e9a2..c21f2db20 100644 --- a/core/src/main/java/org/everit/json/schema/loader/OrgJsonUtil.java +++ b/core/src/main/java/org/everit/json/schema/loader/OrgJsonUtil.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -19,7 +20,7 @@ public class OrgJsonUtil { * Used as a replacement of {@code JSONObject#toMap()} (which doesn't exist in the android version of org.json). */ public static Map toMap(JSONObject obj) { - Map rval = new HashMap<>(obj.length()); + Map rval = new LinkedHashMap<>(obj.length()); Iterator keyIt = obj.keys(); while (keyIt.hasNext()) { String key = keyIt.next(); diff --git a/core/src/main/java/org/everit/json/schema/loader/ProjectedJsonObject.java b/core/src/main/java/org/everit/json/schema/loader/ProjectedJsonObject.java index 38926f449..21b92249d 100644 --- a/core/src/main/java/org/everit/json/schema/loader/ProjectedJsonObject.java +++ b/core/src/main/java/org/everit/json/schema/loader/ProjectedJsonObject.java @@ -1,17 +1,17 @@ package org.everit.json.schema.loader; -import static java.lang.String.format; -import static java.util.Collections.emptyMap; -import static java.util.Objects.requireNonNull; - -import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; +import static java.lang.String.format; +import static java.util.Collections.emptyMap; +import static java.util.Objects.requireNonNull; + class ProjectedJsonObject extends JsonObject { private final JsonObject original; @@ -89,13 +89,13 @@ private void throwExceptionIfNotVisible(String key) { } @Override protected Object unwrap() { - Map storage = new HashMap<>(original.storage); + Map storage = new LinkedHashMap<>(original.storage); removeHiddenKeysFrom(storage); return storage; } @Override Map toMap() { - Map map = new HashMap<>(original.toMap()); + Map map = new LinkedHashMap<>(original.toMap()); removeHiddenKeysFrom(map); return map; } diff --git a/core/src/main/java/org/everit/json/schema/loader/ReferenceLookup.java b/core/src/main/java/org/everit/json/schema/loader/ReferenceLookup.java index 25348b871..c2003d617 100644 --- a/core/src/main/java/org/everit/json/schema/loader/ReferenceLookup.java +++ b/core/src/main/java/org/everit/json/schema/loader/ReferenceLookup.java @@ -7,6 +7,7 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -61,9 +62,9 @@ static Map extend(Map additional, Map rawObj = new HashMap<>(); - original.forEach(rawObj::put); - additional.forEach(rawObj::put); + Map rawObj = new LinkedHashMap<>(); + rawObj.putAll(original); + rawObj.putAll(additional); return rawObj; } @@ -111,7 +112,7 @@ private Map doExtend(Map additional, Map withoutRef(JsonObject original) { - Map rawObj = new HashMap<>(); + Map rawObj = new LinkedHashMap<>(); original.keySet().stream() .filter(name -> !"$ref".equals(name)) .forEach(name -> rawObj.put(name, original.get(name))); diff --git a/core/src/test/java/org/everit/json/schema/DynamicReferenceSchemaExample.java b/core/src/test/java/org/everit/json/schema/DynamicReferenceSchemaExample.java index a62938c5b..c547c3663 100644 --- a/core/src/test/java/org/everit/json/schema/DynamicReferenceSchemaExample.java +++ b/core/src/test/java/org/everit/json/schema/DynamicReferenceSchemaExample.java @@ -1,10 +1,10 @@ package org.everit.json.schema; -import java.util.HashMap; -import java.util.Map; - import org.json.JSONObject; +import java.util.LinkedHashMap; +import java.util.Map; + public class DynamicReferenceSchemaExample { public static void main(String[] args) { @@ -14,7 +14,7 @@ public static void main(String[] args) { .build(); rootSchema.addPropertySchema("myProperty", referenceSchema); - Map unprocessed = new HashMap<>(); + Map unprocessed = new LinkedHashMap<>(); JSONObject defs = new JSONObject(); StringSchema referredSchema = StringSchema.builder() .minLength(2).maxLength(5) 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 535705a17..fa789596b 100644 --- a/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java +++ b/core/src/test/java/org/everit/json/schema/ObjectSchemaTest.java @@ -15,29 +15,29 @@ */ package org.everit.json.schema; -import static java.util.Arrays.asList; -import static java.util.stream.Collectors.toMap; -import static org.everit.json.schema.JSONMatcher.sameJsonAs; -import static org.everit.json.schema.TestSupport.buildWithLocation; -import static org.everit.json.schema.TestSupport.loadAsV6; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.*; - -import java.util.AbstractMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Callable; - +import com.google.re2j.Pattern; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; import org.everit.json.schema.loader.SchemaLoader; import org.json.JSONObject; import org.json.JSONPointer; import org.junit.jupiter.api.Test; -import com.google.re2j.Pattern; +import java.util.AbstractMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; -import nl.jqno.equalsverifier.EqualsVerifier; -import nl.jqno.equalsverifier.Warning; +import static java.util.Arrays.asList; +import static java.util.stream.Collectors.toMap; +import static org.everit.json.schema.JSONMatcher.sameJsonAs; +import static org.everit.json.schema.TestSupport.buildWithLocation; +import static org.everit.json.schema.TestSupport.loadAsV6; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.fail; public class ObjectSchemaTest { @@ -259,7 +259,7 @@ public void patternPropertyTranslation() { ObjectSchema subject = ObjectSchema.builder() .patternProperty(java.util.regex.Pattern.compile("b_.*"), BooleanSchema.INSTANCE) .build(); - Map expected = new HashMap<>(); + Map expected = new LinkedHashMap<>(); expected.put(java.util.regex.Pattern.compile("b_.*"), BooleanSchema.INSTANCE); assertEquals(toStringToSchemaMap(expected), toStringToSchemaMap(subject.getPatternProperties())); } diff --git a/core/src/test/java/org/everit/json/schema/internal/JSONPrinterTest.java b/core/src/test/java/org/everit/json/schema/internal/JSONPrinterTest.java index 5eca2f192..53ec95338 100644 --- a/core/src/test/java/org/everit/json/schema/internal/JSONPrinterTest.java +++ b/core/src/test/java/org/everit/json/schema/internal/JSONPrinterTest.java @@ -1,17 +1,18 @@ package org.everit.json.schema.internal; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.StringWriter; -import java.util.HashMap; - import org.everit.json.schema.NullSchema; import org.everit.json.schema.Schema; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + public class JSONPrinterTest { private StringWriter buffer; @@ -127,10 +128,10 @@ public void arraySupport() { @Test public void printSchemaMap() { - HashMap input = new HashMap(); + Map input = new LinkedHashMap<>(); input.put(2, NullSchema.INSTANCE); subject().printSchemaMap(input); - assertEquals("{\"2\":" + NullSchema.INSTANCE.toString() + "}", buffer.toString()); + assertEquals("{\"2\":" + NullSchema.INSTANCE + "}", buffer.toString()); } } diff --git a/core/src/test/java/org/everit/json/schema/loader/JsonObjectTest.java b/core/src/test/java/org/everit/json/schema/loader/JsonObjectTest.java index 106c4a1fe..7fd984790 100644 --- a/core/src/test/java/org/everit/json/schema/loader/JsonObjectTest.java +++ b/core/src/test/java/org/everit/json/schema/loader/JsonObjectTest.java @@ -1,25 +1,29 @@ package org.everit.json.schema.loader; -import static org.everit.json.schema.loader.JsonValueTest.asV6Value; -import static org.everit.json.schema.loader.JsonValueTest.withLs; -import static org.everit.json.schema.loader.OrgJsonUtil.toMap; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; +import org.everit.json.schema.ResourceLoader; +import org.everit.json.schema.SchemaException; +import org.json.JSONObject; +import org.junit.jupiter.api.Test; import java.net.URI; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Optional; import java.util.function.Consumer; import java.util.function.Function; -import org.everit.json.schema.ResourceLoader; -import org.everit.json.schema.SchemaException; -import org.json.JSONObject; -import org.junit.jupiter.api.Test; +import static org.everit.json.schema.loader.JsonValueTest.asV6Value; +import static org.everit.json.schema.loader.JsonValueTest.withLs; +import static org.everit.json.schema.loader.OrgJsonUtil.toMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; /** * @author erosb @@ -36,7 +40,7 @@ static Consumer mockConsumer() { public static final JSONObject TESTSCHEMAS = ResourceLoader.DEFAULT.readObj("testschemas.json"); private Map storage() { - Map rval = new HashMap<>(); + Map rval = new LinkedHashMap<>(); rval.put("a", true); rval.put("b", new JSONObject()); return rval; diff --git a/core/src/test/java/org/everit/json/schema/loader/JsonPointerEvaluatorTest.java b/core/src/test/java/org/everit/json/schema/loader/JsonPointerEvaluatorTest.java index f7b8999e2..e344e4cb5 100644 --- a/core/src/test/java/org/everit/json/schema/loader/JsonPointerEvaluatorTest.java +++ b/core/src/test/java/org/everit/json/schema/loader/JsonPointerEvaluatorTest.java @@ -1,5 +1,19 @@ package org.everit.json.schema.loader; +import org.everit.json.schema.ResourceLoader; +import org.everit.json.schema.SchemaException; +import org.everit.json.schema.SchemaLocation; +import org.json.JSONObject; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; import static org.everit.json.schema.JSONMatcher.sameJsonAs; @@ -13,18 +27,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; - -import org.everit.json.schema.ResourceLoader; -import org.everit.json.schema.SchemaException; -import org.everit.json.schema.SchemaLocation; -import org.json.JSONObject; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - public class JsonPointerEvaluatorTest { private static final JsonObject rootSchemaJson = withLs(JsonValue.of(ResourceLoader.DEFAULT.readObj("testschemas.json") @@ -77,7 +79,7 @@ private LoadingState createLoadingState(SchemaClient schemaClient, String ref) { LoaderConfig config = new LoaderConfig(schemaClient, emptyMap(), SpecificationVersion.DRAFT_4, false); URI parentScopeId = null; Object rootSchemaJson = this.rootSchemaJson; - HashMap schemaJson = new HashMap<>(); + Map schemaJson = new LinkedHashMap<>(); schemaJson.put("$ref", ref); return new LoadingState(config, new HashMap<>(), rootSchemaJson, schemaJson, parentScopeId, SchemaLocation.empty()); } diff --git a/tests/vanilla/src/main/java/org/everit/json/schema/ObjectSchemaPropertyOrderTest.java b/tests/vanilla/src/main/java/org/everit/json/schema/ObjectSchemaPropertyOrderTest.java new file mode 100644 index 000000000..c13447255 --- /dev/null +++ b/tests/vanilla/src/main/java/org/everit/json/schema/ObjectSchemaPropertyOrderTest.java @@ -0,0 +1,35 @@ +package org.everit.json.schema; + +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.UUID; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ObjectSchemaPropertyOrderTest +{ + @Test + public void validatePropertyOrder() + { + String[] keys = IntStream + .range(0, 100) + .mapToObj(o -> UUID.randomUUID().toString()) + .toArray(String[]::new); + ObjectSchema.Builder builder = ObjectSchema.builder(); + + // add properties + Arrays.stream(keys).forEach(k -> builder.addPropertySchema(k, new NumberSchema())); + + // make schema, get property keys + ObjectSchema schema = builder.build(); + String[] schemaKeys = schema.getPropertySchemas().keySet().toArray(new String[]{}); + + // validate key order + for (int i = 0; i < keys.length; i++) + { + assertEquals(keys[i], schemaKeys[i]); + } + } +}