Skip to content

Commit

Permalink
simple elements cache
Browse files Browse the repository at this point in the history
  • Loading branch information
redmitry committed Aug 28, 2024
1 parent 1b735fb commit 7c88d57
Show file tree
Hide file tree
Showing 14 changed files with 171 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.WeakHashMap;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonString;
Expand All @@ -68,6 +69,7 @@
public class DefaultJsonSchemaParser implements JsonSubschemaParser {

private final Map<String, Object> properties;
private final Map<AbstractJsonSchema, AbstractJsonSchema> cache = new WeakHashMap();

public DefaultJsonSchemaParser(Map<String, Object> properties) {
this.properties = properties;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 javax.json.JsonException;
import java.util.List;
import javax.json.JsonObject;
Expand All @@ -49,7 +48,7 @@
public abstract class AbstractJsonReferenceImpl extends AbstractJsonSchema<JsonObject>
implements JsonReference {

protected JsonSchemaElement schema;
protected AbstractJsonSchemaElement schema;

protected URI ref;
protected String ref_pointer;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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 <T> returned type inferred from caller
* @param element the element to be cloned
*
* @return the clone of provided element
*/
protected <T extends AbstractJsonSchemaElement> 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -75,15 +77,19 @@ public JsonArraySchemaImpl(AbstractJsonSchemaElement parent,
}

@Override
public Stream<JsonSchemaElement> getChildren() {
final Stream stream = Stream.of(
public Stream<AbstractJsonSchemaElement> getChildren() {

// clone children and set their parent to 'this'
final Stream<AbstractJsonSchemaElement> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 javax.json.JsonObject;
import javax.json.JsonValue;
Expand All @@ -56,8 +55,8 @@ public JsonDependentPropertiesImpl(AbstractJsonSchema parent,
}

@Override
public <T extends JsonSchemaElement> Stream<T> getChildren() {
return Stream.of();
public Stream<AbstractJsonSchemaElement> getChildren() {
return Stream.empty(); // TODO
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 javax.json.JsonObject;
import javax.json.JsonValue;
Expand All @@ -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 <T extends JsonSchemaElement> Stream<T> getChildren() {
return Stream.of(); // TODO
public Stream<AbstractJsonSchemaElement> 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) {
Expand All @@ -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;
}
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ public JsonMultitypeSchemaWrapper(AbstractJsonSchemaElement parent,
}

@Override
public Stream<JsonSchemaElement> getChildren() {
public Stream<AbstractJsonSchemaElement> 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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -53,8 +52,8 @@ public JsonNotImpl(AbstractJsonSchema parent,
}

@Override
public <T extends JsonSchemaElement> Stream<T> getChildren() {
return schema.getChildren();
public Stream<AbstractJsonSchemaElement> getChildren() {
return schema.clone(schema).setParent(this).getChildren();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,14 @@
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 javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonValue;
import javax.json.JsonValue.ValueType;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
Expand Down Expand Up @@ -82,15 +81,17 @@ public JsonObjectSchemaImpl(AbstractJsonSchemaElement parent,
}

@Override
public Stream<JsonSchemaElement> getChildren() {
return Stream.of(
public Stream<AbstractJsonSchemaElement> getChildren() {
// clone immediate children and set their parent to 'this'
final Stream<AbstractJsonSchemaElement> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -53,12 +52,12 @@ public JsonPropertiesImpl(AbstractJsonSchema parent,
}

@Override
public <T extends JsonSchemaElement> Stream<T> getChildren() {
final Stream stream = Stream.of(
properties.values().stream(),
properties.values().stream().flatMap(JsonSchemaElement::getChildren));
public Stream<AbstractJsonSchemaElement> getChildren() {
// clone properties and set their parent to 'this'
final Stream<AbstractJsonSchemaElement> 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
Expand Down
Loading

0 comments on commit 7c88d57

Please sign in to comment.