com.metreeca.json.Shape Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of metreeca-json Show documentation
Show all versions of metreeca-json Show documentation
A shape-based JSON modelling framework.
The newest version!
/*
* Copyright © 2013-2022 Metreeca srl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.metreeca.json;
import com.metreeca.json.shapes.*;
import org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.vocabulary.LDP;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import static com.metreeca.json.Focus.focus;
import static com.metreeca.json.Values.inverse;
import static com.metreeca.json.shapes.All.all;
import static com.metreeca.json.shapes.And.and;
import static com.metreeca.json.shapes.Field.field;
import static com.metreeca.json.shapes.Guard.Mode;
import static com.metreeca.json.shapes.MaxCount.maxCount;
import static com.metreeca.json.shapes.MinCount.minCount;
import static com.metreeca.json.shapes.Range.range;
import static com.metreeca.json.shapes.When.when;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toSet;
/**
* Linked data shape constraint.
*/
public abstract class Shape {
/**
* The default predicate linking resource collections to their items.
*/
public static final IRI Contains=LDP.CONTAINS;
//// Shape Shorthands //////////////////////////////////////////////////////////////////////////////////////////////
public static Shape required() { return and(minCount(1), maxCount(1)); }
public static Shape optional() { return maxCount(1); }
public static Shape repeatable() { return minCount(1); }
public static Shape multiple() { return and(); }
public static Shape exactly(final Value... values) { return and(all(values), range(values)); }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Checks if this shape is empty.
*
* @return {@code true} if this shape is equivalent to either {@link And#and()} or {@link Or#or()}; {@code false}
* otherwise
*/
public boolean empty() {
return map(new ShapeEvaluator()) != null;
}
/**
* Traverses this shape.
*
* @param path the property path to be traversed
*
* @return the optional shape reached following {@code path} from this shape or an empty optional, if {@code path}
* includes unknown steps
*
* @throws NullPointerException if {@code path} is null or contains null elements
*/
public Optional walk(final Collection path) {
if ( path == null || path.stream().anyMatch(Objects::isNull) ) {
throw new NullPointerException("null path");
}
Optional shape=Optional.of(this);
for (final IRI step : path) {
shape=shape.flatMap(s -> field(s, step)).map(Field::shape);
}
return shape;
}
/**
* Identifies statements implied by this shape.
*
* @param focus the initial focus values for shape traversal
*
* @return a set of statements implied by this shape when recursively traversed starting from {@code focus} values
*
* @throws NullPointerException if {@code focus} is null or contains null elements
*/
public Set outline(final Value... focus) {
if ( focus == null || stream(focus).anyMatch(Objects::isNull) ) {
throw new NullPointerException("null focus");
}
return redact(Mode).map(new ShapeOutliner(focus)).collect(toSet());
}
/**
* Extends this shape with inferred constraints.
*
* @return a copy of this shape extended with inferred constraints
*/
public Shape expand() {
return map(new ShapeInferencer());
}
/**
* Localizes this shape.
*
* @param tags the target language tag set
*
* @return a copy of this shape where tag sets of {@link Lang lang} shapes are replaced with their intersection with
* {@code tags} or {@code tags}, if the intersection is empty; if {@code tags} is empty or contains a wildcard
* ("{@code *}") the shape is not modified
*
* @throws NullPointerException if {@code tags} is null or contains null elements
*/
public Shape localize(final String... tags) {
if ( tags == null || stream(tags).anyMatch(Objects::isNull) ) {
throw new NullPointerException("null tags");
}
return localize(asList(tags));
}
/**
* Localizes this shape.
*
* @param tags the target language tag set
*
* @return a copy of this shape where tag sets of {@link Lang lang} shapes are replaced with their intersection with
* {@code tags} or {@code tags}, if the intersection is empty; if {@code tags} is empty or contains a wildcard
* ("{@code *}") the shape is not modified
*
* @throws NullPointerException if {@code tags} is null or contains null elements
*/
public Shape localize(final Collection tags) {
if ( tags == null || tags.stream().anyMatch(Objects::isNull) ) {
throw new NullPointerException("null tags");
}
return tags.isEmpty() || tags.contains("*") ? this : map(new ShapeLocalizer(tags));
}
/**
* Redacts guards in this shape.
*
* @param axis the axis to be retained
*
* @return a copy of this shape where {@link Guard} shapes along {@code axis} are selectively replaced with an empty
* {@link And#and() and} shape
*
* @throws NullPointerException if {@code axis} is null
*/
public Shape redact(final String axis) {
if ( axis == null ) {
throw new NullPointerException("null axis");
}
return map(new ShapeRedactor(axis, null));
}
/**
* Redacts guards in this shape.
*
* @param axis the axis to be retained
* @param values the axis values to be retained
*
* @return a copy of this shape where {@link Guard} shapes along {@code axis} are selectively replaced with an empty
* {@link And#and() and} shape, if their {@link Guard#values() values set} intersect {@code values}, or an empty
* {@link Or#or() and} shape, otherwise
*
* @throws NullPointerException if either {@code axis} or {@code values} is null or contains null elements
*/
public Shape redact(final String axis, final Object... values) {
if ( axis == null ) {
throw new NullPointerException("null axis");
}
if ( values == null || stream(values).anyMatch(Objects::isNull) ) {
throw new NullPointerException("null values");
}
return map(new ShapeRedactor(axis, asList(values)));
}
/**
* Redacts guards in this shape.
*
* @param axis the axis to be retained
* @param values the axis values to be retained
*
* @return a copy of this shape where {@link Guard} shapes along {@code axis} are selectively replaced with an empty
* {@link And#and() and} shape, if their {@link Guard#values() values set} intersect {@code values}, or an empty
* {@link Or#or() and} shape, otherwise
*
* @throws NullPointerException if either {@code axis} or {@code values} is null or contains null elements
*/
public Shape redact(final String axis, final Collection
© 2015 - 2024 Weber Informatics LLC | Privacy Policy