Skip to content

Commit

Permalink
Merge pull request #125 from sebastian-toepfer/improve_keyword_relati…
Browse files Browse the repository at this point in the history
…onship

improve definition of keyword relationships (affects and affectedBy)
  • Loading branch information
sebastian-toepfer authored Jun 5, 2024
2 parents 1802403 + fa76f02 commit b52da05
Show file tree
Hide file tree
Showing 20 changed files with 952 additions and 146 deletions.
20 changes: 12 additions & 8 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,7 @@
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<scope>provided</scope>
</dependency>

</dependency>
<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
Expand All @@ -99,6 +92,17 @@
<artifactId>media-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>jakarta.json</groupId>
<artifactId>jakarta.json-api</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>com.github.spotbugs</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* The MIT License
*
* Copyright 2024 sebastian.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.github.sebastiantoepfer.jsonschema.core.keyword.type;

import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;

public enum AffectByType {
EXTENDS {
@Override
Keyword affect(final Keyword affectedKeyword) {
return affectedKeyword;
}
},
REPLACE {
@Override
Keyword affect(final Keyword affectedKeyword) {
return new ReplacingKeyword(affectedKeyword);
}
};

abstract Keyword affect(final Keyword affectedKeyword);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* The MIT License
*
* Copyright 2024 sebastian.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.github.sebastiantoepfer.jsonschema.core.keyword.type;

import io.github.sebastiantoepfer.jsonschema.JsonSchema;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.UnaryOperator;

public final class AffectedBy implements Comparable<AffectedBy> {

private final AffectByType type;
private final String name;

public AffectedBy(final AffectByType type, final String name) {
this.type = Objects.requireNonNull(type);
this.name = Objects.requireNonNull(name);
}

@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + Objects.hashCode(this.type);
hash = 83 * hash + Objects.hashCode(this.name);
return hash;
}

@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
return compareTo((AffectedBy) obj) == 0;
}

@Override
public int compareTo(final AffectedBy other) {
final int result;
if (type.compareTo(other.type) == 0) {
result = name.compareTo(other.name);
} else {
result = type.compareTo(other.type);
}
return result;
}

Function<Keyword, Keyword> findAffectedByKeywordIn(final JsonSchema schema) {
final UnaryOperator<Keyword> result;
if (schema.keywordByName(name).isPresent()) {
result = type::affect;
} else {
result = k -> k;
}
return result;
}

@Override
public String toString() {
return "AffectedBy{" + "type=" + type + ", name=" + name + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,30 @@
*/
package io.github.sebastiantoepfer.jsonschema.core.keyword.type;

import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.toList;

import io.github.sebastiantoepfer.jsonschema.JsonSchema;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;

public class AffectedByKeywordType implements KeywordType {
public final class AffectedByKeywordType implements KeywordType {

private final String name;
private final List<String> affectedBy;
private final BiFunction<List<Keyword>, JsonSchema, Keyword> keywordCreator;
private final Collection<AffectedBy> affectedBy;
private final Function<JsonSchema, Keyword> keywordCreator;

public AffectedByKeywordType(
final String name,
final List<String> affectedBy,
final BiFunction<List<Keyword>, JsonSchema, Keyword> keywordCreator
final Collection<AffectedBy> affectedBy,
final Function<JsonSchema, Keyword> keywordCreator
) {
if (affectedBy.isEmpty()) {
throw new IllegalArgumentException("affectedBy can not be empty!");
}
this.name = Objects.requireNonNull(name);
this.affectedBy = List.copyOf(affectedBy);
this.keywordCreator = Objects.requireNonNull(keywordCreator);
Expand All @@ -57,34 +59,35 @@ public String name() {

@Override
public Keyword createKeyword(final JsonSchema schema) {
return new AffectedByKeyword(schema, name, affectedBy, keywordCreator);
return new AffectedKeyword(schema, name, affectedBy, keywordCreator);
}

static final class AffectedByKeyword extends KeywordRelationship {
static final class AffectedKeyword extends KeywordRelationship {

private final JsonSchema schema;
private final List<String> affectedBy;
private final BiFunction<List<Keyword>, JsonSchema, Keyword> keywordCreator;
private final SortedSet<AffectedBy> affectedBy;
private final Function<JsonSchema, Keyword> keywordCreator;

public AffectedByKeyword(
public AffectedKeyword(
final JsonSchema schema,
final String name,
final List<String> affectedBy,
final BiFunction<List<Keyword>, JsonSchema, Keyword> keywordCreator
final Collection<AffectedBy> affectedBy,
final Function<JsonSchema, Keyword> keywordCreator
) {
super(name);
this.schema = Objects.requireNonNull(schema);
this.affectedBy = List.copyOf(affectedBy);
this.affectedBy = new TreeSet<>(affectedBy);
this.keywordCreator = Objects.requireNonNull(keywordCreator);
}

@Override
protected Keyword delegate() {
return affectedBy
.stream()
.map(schema::keywordByName)
.flatMap(Optional::stream)
.collect(collectingAndThen(toList(), k -> keywordCreator.apply(k, schema)));
.map(a -> a.findAffectedByKeywordIn(schema))
.reduce(Function::andThen)
.orElseThrow()
.apply(keywordCreator.apply(schema));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* The MIT License
*
* Copyright 2024 sebastian.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.github.sebastiantoepfer.jsonschema.core.keyword.type;

import io.github.sebastiantoepfer.jsonschema.JsonSchema;
import io.github.sebastiantoepfer.jsonschema.keyword.Annotation;
import io.github.sebastiantoepfer.jsonschema.keyword.Keyword;
import io.github.sebastiantoepfer.jsonschema.keyword.StaticAnnotation;
import jakarta.json.JsonValue;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

public final class Affects {

private final String name;
private final AbsenceStrategy strategy;

public Affects(final String name, final JsonValue answerInAbsence) {
this(name, new ProvideDefaultValue(answerInAbsence));
}

public Affects(final String name, final AbsenceStrategy strategy) {
this.name = Objects.requireNonNull(name);
this.strategy = Objects.requireNonNull(strategy);
}

@Override
public String toString() {
return "Affects{" + "name=" + name + ", strategy=" + strategy.getClass() + '}';
}

Map.Entry<Annotation, Function<Keyword, Keyword>> findAffectsKeywordIn(final JsonSchema schema) {
final Map.Entry<Annotation, Function<Keyword, Keyword>> result;
final Optional<Annotation> annotation = schema
.keywordByName(name)
.filter(k -> k.hasCategory(Keyword.KeywordCategory.ANNOTATION))
.map(Keyword::asAnnotation);
if (annotation.isPresent()) {
result = Map.entry(annotation.get(), k -> k);
} else {
result = strategy.create(name);
}
return result;
}

public interface AbsenceStrategy {
Map.Entry<Annotation, Function<Keyword, Keyword>> create(String name);
}

public static final class ReplaceKeyword implements AbsenceStrategy {

@Override
public Map.Entry<Annotation, Function<Keyword, Keyword>> create(final String name) {
return Map.entry(new StaticAnnotation(name, JsonValue.NULL), ReplacingKeyword::new);
}
}

public static final class ProvideDefaultValue implements AbsenceStrategy {

private final JsonValue answerInAbsence;

public ProvideDefaultValue(final JsonValue answerInAbsence) {
this.answerInAbsence = Objects.requireNonNullElse(answerInAbsence, JsonValue.NULL);
}

@Override
public Map.Entry<Annotation, Function<Keyword, Keyword>> create(final String name) {
return Map.entry(new StaticAnnotation(name, answerInAbsence), k -> k);
}
}
}
Loading

0 comments on commit b52da05

Please sign in to comment.