From fd9da401ce538fe44d63fa5be98d28ad167fc59d Mon Sep 17 00:00:00 2001 From: redmitry Date: Wed, 28 Aug 2024 17:16:01 +0200 Subject: [PATCH] simple elements cache --- .../schema/impl/DefaultJsonSchemaParser.java | 13 ++- .../model/impl/AbstractJsonReferenceImpl.java | 3 +- .../model/impl/AbstractJsonSchemaElement.java | 82 ++++++++++++++++++- .../model/impl/JsonArraySchemaImpl.java | 24 ++++-- .../impl/JsonDependentPropertiesImpl.java | 5 +- .../model/impl/JsonDynamicReferenceImpl.java | 20 +++-- .../impl/JsonMultitypeSchemaWrapper.java | 4 +- .../json/schema/model/impl/JsonNotImpl.java | 5 +- .../model/impl/JsonObjectSchemaImpl.java | 21 ++--- .../schema/model/impl/JsonPropertiesImpl.java | 11 ++- .../impl/JsonRecursiveReferenceImpl.java | 11 ++- .../schema/model/impl/JsonReferenceImpl.java | 13 ++- .../model/impl/PrimitiveSchemaImpl.java | 22 +++-- .../schema/model/impl/SchemaArrayImpl.java | 12 +-- 14 files changed, 171 insertions(+), 75 deletions(-) diff --git a/src/main/java/es/elixir/bsc/json/schema/impl/DefaultJsonSchemaParser.java b/src/main/java/es/elixir/bsc/json/schema/impl/DefaultJsonSchemaParser.java index 370ab30..1cb0a97 100644 --- a/src/main/java/es/elixir/bsc/json/schema/impl/DefaultJsonSchemaParser.java +++ b/src/main/java/es/elixir/bsc/json/schema/impl/DefaultJsonSchemaParser.java @@ -55,6 +55,7 @@ import java.io.IOException; import java.net.URI; import java.util.Map; +import java.util.WeakHashMap; import jakarta.json.JsonArray; import jakarta.json.JsonObject; import jakarta.json.JsonString; @@ -68,6 +69,7 @@ public class DefaultJsonSchemaParser implements JsonSubschemaParser { private final Map properties; + private final Map cache = new WeakHashMap(); public DefaultJsonSchemaParser(Map properties) { this.properties = properties; @@ -163,8 +165,15 @@ public AbstractJsonSchema parse(JsonSchemaLocator locator, AbstractJsonSchemaEle case NULL: schema = new JsonNullSchemaImpl(parent, scope, locator, jsonPointer); break; default: return null; } - - return schema.read(this, object); + + AbstractJsonSchema sch = cache.get(schema); + if (sch == null) { + sch = schema.read(this, object); + if (!sch.isDynamicScope()) { + cache.put(sch, sch); + } + } + return sch; } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonReferenceImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonReferenceImpl.java index 3298510..b296a4b 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonReferenceImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonReferenceImpl.java @@ -34,7 +34,6 @@ import es.elixir.bsc.json.schema.ValidationException; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonReference; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import jakarta.json.JsonException; import java.util.List; import jakarta.json.JsonObject; @@ -49,7 +48,7 @@ public abstract class AbstractJsonReferenceImpl extends AbstractJsonSchema implements JsonReference { - protected JsonSchemaElement schema; + protected AbstractJsonSchemaElement schema; protected URI ref; protected String ref_pointer; diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonSchemaElement.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonSchemaElement.java index bd34299..e1b272d 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonSchemaElement.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/AbstractJsonSchemaElement.java @@ -1,6 +1,6 @@ /** * ***************************************************************************** - * Copyright (C) 2023 ELIXIR ES, Spanish National Bioinformatics Institute (INB) + * Copyright (C) 2024 ELIXIR ES, Spanish National Bioinformatics Institute (INB) * and Barcelona Supercomputing Center (BSC) * * Modifications to the initial code base are copyright of their respective @@ -28,6 +28,7 @@ import es.elixir.bsc.json.schema.JsonSchemaLocator; import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.net.URI; +import java.util.Objects; /** * This is an root class that any JSON Schema element inherits from. @@ -37,14 +38,17 @@ * @author Dmitry Repchevsky */ -public abstract class AbstractJsonSchemaElement implements JsonSchemaElement { +public abstract class AbstractJsonSchemaElement + implements JsonSchemaElement, Cloneable { - public final AbstractJsonSchemaElement parent; + private AbstractJsonSchemaElement parent; public final JsonSchemaLocator scope; public final JsonSchemaLocator locator; public final String jsonPointer; + private boolean isDynamicScope; + /** * Constructor of the object. * It only sets an essential properties to identify and locate the element in @@ -80,4 +84,76 @@ public String getJsonPointer() { public AbstractJsonSchemaElement getParent() { return parent; } + + /** + * This is a marker whether this element is in the dynamic scope and + * must not be cached. + * + * @return 'true' if in dynamic scope, 'false' otherwise. + */ + public boolean isDynamicScope() { + return isDynamicScope; + } + + protected void setDynamicScope(boolean isDynamicScope) { + this.isDynamicScope = isDynamicScope; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof AbstractJsonSchemaElement other && + this.getClass() == obj.getClass()) { + return Objects.equals(jsonPointer, other.jsonPointer) && + Objects.equals(scope.uri, other.scope.uri); + } + return false; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + Objects.hashCode(this.scope.uri); + hash = 31 * hash + Objects.hashCode(this.jsonPointer); + hash = 31 * hash + Objects.hashCode(this.getClass().hashCode()); + return hash; + } + + @Override + public Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + /** + * Predicate to clone provided element. + * Method just omits possible (impossible) CloneNotSupportedException. + * + * @param returned type inferred from caller + * @param element the element to be cloned + * + * @return the clone of provided element + */ + protected T clone(T element) { + if (element != null) { + try { + return (T)element.clone(); + } catch (CloneNotSupportedException ex) {} + } + return null; + } + + /** + * Predicate used in streams to replace parent for this element + * + * @param parent new parent for this element + * + * @return affected element (this) + */ + protected AbstractJsonSchemaElement setParent(AbstractJsonSchemaElement parent) { + this.parent = parent; + return this; + } + } diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java index e17cc3d..97addf3 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonArraySchemaImpl.java @@ -36,8 +36,10 @@ import java.util.ArrayList; import java.util.List; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; +import java.util.Collection; import java.util.HashSet; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -75,15 +77,19 @@ public JsonArraySchemaImpl(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { - final Stream stream = Stream.of( + public Stream getChildren() { + + // clone children and set their parent to 'this' + final Stream children = Stream.concat( + Optional.ofNullable(items).map(Collection::stream).orElseGet(Stream::empty), + Stream.of(additionalItemsSchema, unevaluatedItemsSchema, contains) + .filter(Objects::nonNull)) + .map(this::clone) + .map(c -> c.setParent(this)); + + return Stream.concat( super.getChildren(), - items != null ? items.stream() : null, - items != null ? items.stream().flatMap(JsonSchemaElement::getChildren) : null, - additionalItemsSchema != null ? additionalItemsSchema.getChildren() : null, - unevaluatedItemsSchema != null ? unevaluatedItemsSchema.getChildren() : null, - contains != null ? contains.getChildren() : null); - return stream.flatMap(c -> c); + children.flatMap(e -> Stream.concat(Stream.of(e), e.getChildren()))); } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDependentPropertiesImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDependentPropertiesImpl.java index 03bf9c8..30eab67 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDependentPropertiesImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDependentPropertiesImpl.java @@ -31,7 +31,6 @@ import es.elixir.bsc.json.schema.ParsingError; import es.elixir.bsc.json.schema.ParsingMessage; import es.elixir.bsc.json.schema.model.JsonDependentProperties; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import es.elixir.bsc.json.schema.model.StringArray; import jakarta.json.JsonObject; import jakarta.json.JsonValue; @@ -56,8 +55,8 @@ public JsonDependentPropertiesImpl(AbstractJsonSchema parent, } @Override - public Stream getChildren() { - return Stream.of(); + public Stream getChildren() { + return Stream.empty(); // TODO } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDynamicReferenceImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDynamicReferenceImpl.java index a43667d..2142822 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDynamicReferenceImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonDynamicReferenceImpl.java @@ -31,7 +31,6 @@ import es.elixir.bsc.json.schema.ParsingMessage; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonDynamicReference; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.util.stream.Stream; import jakarta.json.JsonObject; import jakarta.json.JsonValue; @@ -49,15 +48,20 @@ public class JsonDynamicReferenceImpl extends JsonReferenceImpl public JsonDynamicReferenceImpl(AbstractJsonSchemaElement parent, JsonSchemaLocator scope, JsonSchemaLocator locator, String jsonPointer) { super(parent, scope, locator, jsonPointer); + + AbstractJsonSchemaElement e = this; + do { + e.setDynamicScope(true); + } while((e = e.getParent()) != null); } @Override - public Stream getChildren() { - return Stream.of(); // TODO + public Stream getChildren() { + return Stream.empty(); // TODO } @Override - public JsonSchemaElement getSchema() throws JsonSchemaException { + public AbstractJsonSchemaElement getSchema() throws JsonSchemaException { if (schema == null) { final String fragment = ref.getFragment(); if (fragment != null) { @@ -71,7 +75,7 @@ public JsonSchemaElement getSchema() throws JsonSchemaException { if (schema != null) { final URI uri = new URI(null, null, fragment); while ((e = e.getParent()) != null) { - final JsonSchemaElement s = getSchema(e, uri); + final AbstractJsonSchemaElement s = getSchema(e, uri); if (s != null) { schema = s; } @@ -93,12 +97,12 @@ public JsonSchemaElement getSchema() throws JsonSchemaException { private AbstractJsonSchemaElement getSchema(AbstractJsonSchemaElement e, URI uri) throws IOException, JsonSchemaException { final String fragment = uri.getFragment(); - final JsonSchemaLocator scope = e.scope.resolve(uri); - final JsonValue value = scope.getSchema("/"); + final JsonSchemaLocator l = e.scope.resolve(uri); + final JsonValue value = l.getSchema("/"); if (value instanceof JsonObject jsubschema) { final String anchor = jsubschema.getString(DYNAMIC_ANCHOR, null); if (fragment.equals(anchor)) { - return parser.parse(scope, this, e.getJsonPointer(), jsubschema, null); + return parser.parse(l, getParent(), e.getJsonPointer(), jsubschema, null); } } return null; diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonMultitypeSchemaWrapper.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonMultitypeSchemaWrapper.java index 27ccc85..e007ae5 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonMultitypeSchemaWrapper.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonMultitypeSchemaWrapper.java @@ -64,10 +64,12 @@ public JsonMultitypeSchemaWrapper(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { + public Stream getChildren() { return StreamSupport.stream( Spliterators.spliteratorUnknownSize(iterator(), Spliterator.ORDERED),false) .filter(s -> s instanceof JsonObjectSchema || s instanceof JsonArraySchema) + .map(this::clone) + .map(c -> c.setParent(this)) .flatMap(JsonSchemaElement::getChildren); } diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonNotImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonNotImpl.java index b13934d..5aad0e3 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonNotImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonNotImpl.java @@ -32,7 +32,6 @@ import es.elixir.bsc.json.schema.ValidationMessage; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonNot; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.util.ArrayList; import java.util.List; import java.util.stream.Stream; @@ -53,8 +52,8 @@ public JsonNotImpl(AbstractJsonSchema parent, } @Override - public Stream getChildren() { - return schema.getChildren(); + public Stream getChildren() { + return schema.clone(schema).setParent(this).getChildren(); } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonObjectSchemaImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonObjectSchemaImpl.java index 9d729de..3e0a2fa 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonObjectSchemaImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonObjectSchemaImpl.java @@ -40,8 +40,6 @@ import es.elixir.bsc.json.schema.ParsingMessage; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonDependentProperties; -import static es.elixir.bsc.json.schema.model.JsonObjectSchema.PROPERTY_NAMES; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import es.elixir.bsc.json.schema.model.JsonType; import jakarta.json.Json; import jakarta.json.JsonArray; @@ -49,6 +47,7 @@ import jakarta.json.JsonValue; import jakarta.json.JsonValue.ValueType; import java.util.ArrayList; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; @@ -82,15 +81,17 @@ public JsonObjectSchemaImpl(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { - return Stream.of( + public Stream getChildren() { + // clone immediate children and set their parent to 'this' + final Stream children = Stream.of( + properties, propertyNames, patternProperties, + unevaluatedPropertiesSchema, dependentSchemas) + .filter(Objects::nonNull).map(this::clone) + .map(c -> c.setParent(this)); + + return Stream.concat( super.getChildren(), - properties != null ? properties.getChildren() : null, - propertyNames != null ? propertyNames.getChildren() : null, - patternProperties != null ? patternProperties.getChildren() : null, - unevaluatedPropertiesSchema != null ? unevaluatedPropertiesSchema.getChildren() : null, - dependentSchemas != null ? dependentSchemas.getChildren() : null) - .flatMap(c -> c); + children.flatMap(AbstractJsonSchemaElement::getChildren)); } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonPropertiesImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonPropertiesImpl.java index 0f64ae5..1dfdb1e 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonPropertiesImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonPropertiesImpl.java @@ -29,7 +29,6 @@ import es.elixir.bsc.json.schema.JsonSchemaLocator; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonProperties; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -53,12 +52,12 @@ public JsonPropertiesImpl(AbstractJsonSchema parent, } @Override - public Stream getChildren() { - final Stream stream = Stream.of( - properties.values().stream(), - properties.values().stream().flatMap(JsonSchemaElement::getChildren)); + public Stream getChildren() { + // clone properties and set their parent to 'this' + final Stream children = + properties.values().stream().map(this::clone).map(c -> c.setParent(this)); - return stream.flatMap(c -> c); + return children.flatMap(p -> Stream.concat(Stream.of(p), p.getChildren())); } @Override diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonRecursiveReferenceImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonRecursiveReferenceImpl.java index baa5454..a4f7a27 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonRecursiveReferenceImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonRecursiveReferenceImpl.java @@ -48,11 +48,16 @@ public class JsonRecursiveReferenceImpl extends AbstractJsonReferenceImpl public JsonRecursiveReferenceImpl(AbstractJsonSchemaElement parent, JsonSchemaLocator scope, JsonSchemaLocator locator, String jsonPointer) { super(parent, scope, locator, jsonPointer); + + AbstractJsonSchemaElement e = this; + do { + e.setDynamicScope(true); + } while((e = e.getParent()) != null); } @Override public Stream getChildren() { - return Stream.of(); // TODO + return Stream.empty(); // TODO } @Override @@ -67,8 +72,8 @@ public JsonSchemaElement getSchema() throws JsonSchemaException { // no 'type' or 'type': [] leads to JsonMultitypeSchemaWrapper wrapper. // because we are in a 'root' parent either has different location // or be the wrapper. - if (e.parent instanceof JsonMultitypeSchemaWrapper) { - e = e.parent; + if (e.getParent() instanceof JsonMultitypeSchemaWrapper) { + e = e.getParent(); } if (Boolean.TRUE == anchor) { diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonReferenceImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonReferenceImpl.java index 04b3d6b..f6605d7 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonReferenceImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/JsonReferenceImpl.java @@ -31,7 +31,6 @@ import es.elixir.bsc.json.schema.ParsingMessage; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; import es.elixir.bsc.json.schema.model.JsonReference; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.io.IOException; import java.util.stream.Stream; import jakarta.json.JsonException; @@ -50,28 +49,28 @@ public JsonReferenceImpl(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { + public Stream getChildren() { if (schema == null) { - JsonSchemaElement s = getParent(); + AbstractJsonSchemaElement s = getParent(); while (s != null) { if (ref_locator.uri.equals(s.getId()) && ref_pointer.equals(s.getJsonPointer())) { - return Stream.of(); // cyclic ref + return Stream.empty(); // cyclic ref } s = s.getParent(); } try { schema = getSchema(); } catch(JsonSchemaException ex) { - return Stream.of(); // unresolvable ref + return Stream.empty(); // unresolvable ref } } - return schema.getChildren(); + return schema.clone(schema).setParent(this).getChildren(); } @Override - public JsonSchemaElement getSchema() throws JsonSchemaException { + public AbstractJsonSchemaElement getSchema() throws JsonSchemaException { if (schema == null) { try { JsonValue jsubschema = ref_locator.getSchema(ref_pointer); diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/PrimitiveSchemaImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/PrimitiveSchemaImpl.java index bb3889f..cfb1ef6 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/PrimitiveSchemaImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/PrimitiveSchemaImpl.java @@ -39,7 +39,6 @@ import es.elixir.bsc.json.schema.model.JsonRecursiveReference; import es.elixir.bsc.json.schema.model.JsonReference; import es.elixir.bsc.json.schema.model.JsonSchema; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import es.elixir.bsc.json.schema.model.PrimitiveSchema; import java.net.URI; import java.util.ArrayList; @@ -49,6 +48,7 @@ import jakarta.json.JsonString; import jakarta.json.JsonValue; import java.util.Map; +import java.util.Objects; /** * Primitive empty Json Schema of any type ("object", "array", "string", etc.) @@ -88,17 +88,15 @@ public PrimitiveSchemaImpl(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { - return Stream.of( - allOf != null ? allOf.getChildren() : null, - anyOf != null ? anyOf.getChildren() : null, - oneOf != null ? oneOf.getChildren() : null, - not != null ? not.getChildren() : null, - _if != null ? _if.getChildren() : null, - _then != null ? _then.getChildren() : null, - _else != null ? _else.getChildren() : null, - ref != null ? ref.getChildren() : null) - .flatMap(c -> c); + public Stream getChildren() { + // clone immediate children and set their parent to 'this' + final Stream children = + Stream.of(allOf, anyOf, oneOf, not, _if, _then, _else, ref) + .filter(Objects::nonNull) + .map(this::clone) + .map(c -> c.setParent(this)); + + return children.flatMap(AbstractJsonSchemaElement::getChildren); } public String getTitle() { diff --git a/src/main/java/es/elixir/bsc/json/schema/model/impl/SchemaArrayImpl.java b/src/main/java/es/elixir/bsc/json/schema/model/impl/SchemaArrayImpl.java index 3f16f43..e51975a 100644 --- a/src/main/java/es/elixir/bsc/json/schema/model/impl/SchemaArrayImpl.java +++ b/src/main/java/es/elixir/bsc/json/schema/model/impl/SchemaArrayImpl.java @@ -30,7 +30,6 @@ import es.elixir.bsc.json.schema.model.SchemaArray; import java.util.HashSet; import es.elixir.bsc.json.schema.impl.JsonSubschemaParser; -import es.elixir.bsc.json.schema.model.JsonSchemaElement; import java.util.Iterator; import java.util.Set; import java.util.stream.Stream; @@ -57,11 +56,12 @@ public SchemaArrayImpl(AbstractJsonSchemaElement parent, } @Override - public Stream getChildren() { - final Stream stream = Stream.of( - schemas.stream(), - schemas.stream().flatMap(JsonSchemaElement::getChildren)); - return stream.flatMap(c -> c); + public Stream getChildren() { + // clone array schemas and set their parent to 'this' + final Stream children = + schemas.stream().map(this::clone).map(c -> c.setParent(this)); + + return children.flatMap(e -> Stream.concat(Stream.of(e), e.getChildren())); } @Override