Skip to content

Commit

Permalink
add format assertion for dates,times and duration
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastian-toepfer committed Dec 3, 2024
1 parent eea78a4 commit 6dc7a09
Show file tree
Hide file tree
Showing 18 changed files with 914 additions and 1 deletion.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@
<modules>
<module>api</module>
<module>vocabulary-spi</module>
<module>core</module>
<module>core</module>
<module>vocabulary</module>
</modules>

<dependencyManagement>
Expand Down
78 changes: 78 additions & 0 deletions vocabulary/format-assertion/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.sebastian-toepfer.json-schema.vocabulary</groupId>
<artifactId>json-schema-vocabulary</artifactId>
<version>0.4.0-SNAPSHOT</version>
</parent>

<artifactId>json-schema-vocabulary-format-assertion</artifactId>
<name>Json Schema :: vocabulary :: format-assertion</name>

<dependencies>
<dependency>
<groupId>io.github.sebastian-toepfer.json-schema</groupId>
<artifactId>json-schema-vocabulary-spi</artifactId>
<version>${project.version}</version>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.npathai</groupId>
<artifactId>hamcrest-optional</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.sebastian-toepfer.ddd</groupId>
<artifactId>media-core</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>
<artifactId>spotbugs-annotations</artifactId>
<optional>true</optional>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.eclipse.parsson</groupId>
<artifactId>parsson</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.sebastian-toepfer.json-schema</groupId>
<artifactId>json-schema-core</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* 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.vocabulary.formatassertion;

import static io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.FormatKeyword.NAME;

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

final class FormatAssertionKeyWordType implements KeywordType {

private final Formats formats;

public FormatAssertionKeyWordType(final Formats formats) {
this.formats = formats;
}

@Override
public String name() {
return NAME;
}

@Override
public Keyword createKeyword(final JsonSchema schema) {
return new FormatKeyword(formats.findByName(schema.asJsonObject().getString(NAME)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* 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.vocabulary.formatassertion;

import static io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.FormatKeyword.NAME;

import io.github.sebastiantoepfer.jsonschema.Vocabulary;
import io.github.sebastiantoepfer.jsonschema.keyword.KeywordType;
import io.github.sebastiantoepfer.jsonschema.vocabulary.spi.LazyVocabularies;
import java.net.URI;
import java.util.Objects;
import java.util.Optional;

public final class FormatAssertionVocabulary implements LazyVocabularies, Vocabulary {

private final URI id;

public FormatAssertionVocabulary() {
id = URI.create("https://json-schema.org/draft/2020-12/vocab/format-assertion");
}

@Override
public Optional<Vocabulary> loadVocabularyWithId(final URI id) {
final Optional<Vocabulary> result;
if (id().equals(id)) {
result = Optional.of(this);
} else {
result = Optional.empty();
}
return result;
}

@Override
public URI id() {
return id;
}

@Override
public Optional<KeywordType> findKeywordTypeByName(final String name) {
final Optional<KeywordType> result;
if (Objects.equals(NAME, name)) {
result = Optional.of(new FormatAssertionKeyWordType(new Formats()));
} else {
result = Optional.empty();
}
return result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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.vocabulary.formatassertion;

import io.github.sebastiantoepfer.ddd.common.Media;
import io.github.sebastiantoepfer.jsonschema.InstanceType;
import io.github.sebastiantoepfer.jsonschema.keyword.Annotation;
import io.github.sebastiantoepfer.jsonschema.keyword.Assertion;
import jakarta.json.Json;
import jakarta.json.JsonString;
import jakarta.json.JsonValue;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

final class FormatKeyword implements Assertion, Annotation {

static final String NAME = "format";
private final Formats.Format format;

FormatKeyword(final Formats.Format format) {
this.format = Objects.requireNonNull(format);
}

@Override
public Collection<KeywordCategory> categories() {
return List.of(KeywordCategory.ASSERTION, KeywordCategory.ANNOTATION);
}

@Override
public boolean isValidFor(final JsonValue instance) {
return !InstanceType.STRING.isInstance(instance) || hasValidValue((JsonString) instance);
}

private boolean hasValidValue(final JsonString value) {
return format.applyTo(value.getString());
}

@Override
public boolean hasName(final String name) {
return NAME.equals(name);
}

@Override
public JsonValue valueFor(final JsonValue value) {
return Json.createValue(format.name());
}

@Override
public <T extends Media<T>> T printOn(final T media) {
return media.withValue(NAME, format.name());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.vocabulary.formatassertion;

import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc;
import io.github.sebastiantoepfer.jsonschema.vocabulary.formatassertion.rfc.Rfc3339;
import java.util.List;
import java.util.Map;
import java.util.Objects;

final class Formats {

private static final List<Format> RFCS = Map.ofEntries(
Map.entry("date-time", "date-time"),
Map.entry("date", "full-date"),
Map.entry("time", "full-time"),
Map.entry("duration", "duration")
)
.entrySet()
.stream()
.map(entry -> new RfcBasedFormat(entry.getKey(), new Rfc3339(), entry.getValue()))
.map(Format.class::cast)
.toList();

Format findByName(final String name) {
return RFCS.stream().filter(f -> f.name().equals(name)).findFirst().orElseGet(() -> new UnknownFormat(name));
}

private static class RfcBasedFormat implements Format {

private final String formatName;
private final Rfc rfc;
private final String ruleName;

public RfcBasedFormat(final String formatName, final Rfc rfc, final String ruleName) {
this.formatName = Objects.requireNonNull(formatName);
this.rfc = Objects.requireNonNull(rfc);
this.ruleName = Objects.requireNonNull(ruleName);
}

@Override
public boolean applyTo(final String value) {
return rfc.findRuleByName(ruleName).map(r -> r.applyTo(value)).orElse(Boolean.FALSE);
}

@Override
public String name() {
return formatName;
}
}

interface Format {
boolean applyTo(String value);
String name();
}

private static class UnknownFormat implements Format {

private final String name;

public UnknownFormat(final String name) {
this.name = name;
}

@Override
public String name() {
return name;
}

@Override
public boolean applyTo(final String value) {
return false;
}
}
}
Loading

0 comments on commit 6dc7a09

Please sign in to comment.