Skip to content

Commit

Permalink
Change RefKeyword into applicator
Browse files Browse the repository at this point in the history
  • Loading branch information
leadpony committed Aug 1, 2020
1 parent 809f6f6 commit b206baa
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,14 @@ default boolean canEvaluate() {
* @return the location.
*/
ApplicableLocation getApplicableLocation();

/**
* Returns whether this keyword is an in-place applicator or not.
*
* @return {@code true} if this keyword is an in-place applicator, {@code false}
* otherwise.
*/
default boolean isInPlace() {
return getApplicableLocation() == ApplicableLocation.CURRENT;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,31 @@
* @author leadpony
* @since 4.0
*/
public interface RefKeyword extends EvaluationKeyword, SimpleValueKeyword<URI> {
public interface RefKeyword extends ApplicatorKeyword, SimpleValueKeyword<URI> {

/**
* {@inheritDoc}}
*/
@Override
default ApplicableLocation getApplicableLocation() {
return ApplicableLocation.CURRENT;
}

/**
* {@inheritDoc}}
*/
@Override
default boolean isInPlace() {
return true;
}

/**
* Returns whether this keyword is direct reference or not.
*
* @return {@code true} if this keyword is direct reference, {@code false}
* otherwise.
*/
boolean isDirect();

/**
* Returns the JSON schema reference.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public KeywordType getType() {
return TYPE;
}

@Override
public boolean isDirect() {
return false;
}

@Override
protected JsonSchema findtTargetSchema(Evaluator parent, JsonSchemaReference reference) {
final JsonSchema direct = reference.getTargetSchema();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
package org.leadpony.justify.internal.keyword.core;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import jakarta.json.JsonValue;

Expand Down Expand Up @@ -75,6 +79,32 @@ public KeywordType getType() {
return TYPE;
}

@Override
public boolean containsSchemas() {
return true;
}

@Override
public Map<String, JsonSchema> getSchemasAsMap() {
Map<String, JsonSchema> schemaMap = new HashMap<>();
schemaMap.put("", getTargetSchema());
return schemaMap;
}

@Override
public Stream<JsonSchema> getSchemasAsStream() {
return Stream.of(getTargetSchema());
}

@Override
public Optional<JsonSchema> findSchema(String jsonPointer) {
if (jsonPointer.isEmpty()) {
return Optional.of(getTargetSchema());
} else {
return Optional.empty();
}
}

@Override
public boolean canEvaluate() {
return true;
Expand All @@ -99,12 +129,17 @@ public URI value() {
return reference.getTargetId();
}

@Override
public boolean isDirect() {
return true;
}

@Override
public JsonSchemaReference getSchemaReference() {
return reference;
}

protected JsonSchema findtTargetSchema(Evaluator parent, JsonSchemaReference reference) {
return reference.getTargetSchema();
return getTargetSchema();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2018-2019 the Justify authors.
* Copyright 2018, 2020 the Justify authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,15 +15,15 @@
*/
package org.leadpony.justify.internal.schema.io;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Collection;
import java.util.Set;

import org.leadpony.justify.api.JsonSchema;
import org.leadpony.justify.api.keyword.ApplicatorKeyword;
import org.leadpony.justify.api.keyword.JsonSchemaReference;
import org.leadpony.justify.api.keyword.Keyword;
import org.leadpony.justify.api.keyword.RefKeyword;
import org.leadpony.justify.internal.base.Sets;

/**
* A detector of infinite recursive looping starting from a schema reference.
Expand All @@ -32,47 +32,42 @@
*/
class InfiniteLoopDetector {

private static final String REFERENCE_KEYWORD = "$ref";

private final Set<JsonSchemaReference> checkPoints = new HashSet<>();
private final Set<JsonSchema> visited = Sets.newIdentitySet();

boolean detectInfiniteLoop(JsonSchemaReference ref) {
try {
return detectLoopFrom(ref);
} finally {
checkPoints.clear();
}
assert visited.isEmpty();
return detectLoopFrom(ref.getTargetSchema());
}

private boolean detectLoopFrom(JsonSchema schema) {

Map<String, Keyword> keywords = schema.getKeywordsAsMap();
if (keywords.containsKey(REFERENCE_KEYWORD)) {
Keyword keyword = keywords.get(REFERENCE_KEYWORD);
if (keyword instanceof RefKeyword) {
RefKeyword ref = (RefKeyword) keyword;
if (detectLoopFrom(ref.getSchemaReference())) {
return true;
}
}
if (visited.contains(schema)) {
return true;
}

Iterator<JsonSchema> it = schema.getInPlaceSubschemas().iterator();
while (it.hasNext()) {
if (detectLoopFrom(it.next())) {
return true;
}
try {
visited.add(schema);
return walkSubschemas(schema);
} finally {
visited.remove(schema);
}
return false;
}

private boolean detectLoopFrom(JsonSchemaReference ref) {
if (checkPoints.contains(ref)) {
return true;
private boolean walkSubschemas(JsonSchema schema) {
Collection<Keyword> keywords = schema.getKeywordsAsMap().values();
return keywords.stream()
.filter(k -> k instanceof ApplicatorKeyword)
.map(k -> (ApplicatorKeyword) k)
.filter(InfiniteLoopDetector::isInPlaceApplicator)
.flatMap(Keyword::getSchemasAsStream)
.anyMatch(this::detectLoopFrom);
}

private static boolean isInPlaceApplicator(ApplicatorKeyword keyword) {
if (keyword instanceof RefKeyword) {
RefKeyword ref = (RefKeyword) keyword;
return ref.isDirect();
} else {
return keyword.isInPlace();
}
checkPoints.add(ref);
boolean result = detectLoopFrom(ref.getTargetSchema());
checkPoints.remove(ref);
return result;
}
}

0 comments on commit b206baa

Please sign in to comment.