All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.metreeca.json.Frame Maven / Gradle / Ivy

/*
 * Copyright © 2013-2021 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 org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.base.AbstractIRI;
import org.eclipse.rdf4j.model.vocabulary.DC;
import org.eclipse.rdf4j.model.vocabulary.RDFS;

import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;

import static com.metreeca.json.Values.*;

import static java.util.Collections.*;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;

/**
 * Graph frame.
 *
 * 

Describes a subgraph centered on a set of focus values.

*/ public final class Frame { private static final IRI SchemaName=iri("http://schema.org/", "name"); private static final IRI SchemaDescription=iri("http://schema.org/", "description"); private static final BiFunction, Stream> Labels=alt( RDFS.LABEL, DC.TITLE, SchemaName ); private static final BiFunction, Stream> Notes=alt( RDFS.COMMENT, DC.DESCRIPTION, SchemaDescription ); public static Frame frame(final Value focus) { if ( focus == null ) { throw new NullPointerException("null focus"); } return new Frame(focus, emptySet()); } public static Frame frame(final Value focus, final Collection model) { if ( focus == null ) { throw new NullPointerException("null focus"); } if ( model == null || model.stream().anyMatch(Objects::isNull) ) { throw new NullPointerException("null model or model statement"); } final Set statements=new LinkedHashSet<>(); final Collection visited=new HashSet<>(); final Queue pending=new ArrayDeque<>(singleton(focus)); while ( !pending.isEmpty() ) { final Value value=pending.poll(); if ( visited.add(value) ) { model.forEach(s -> { if ( value.equals(s.getSubject()) || value.equals(s.getObject()) ) { pending.add(s.getSubject()); pending.add(s.getPredicate()); pending.add(s.getObject()); statements.add(s); } }); } } return new Frame(focus, statements); } //// Paths ///////////////////////////////////////////////////////////////////////////////////////////////////////// public static BiFunction, Stream> seq(final IRI path) { if ( path == null ) { throw new NullPointerException("null path"); } return traverse(path, direct -> (focus, model) -> model.stream() .filter(s -> focus.equals(s.getSubject()) && direct.equals(s.getPredicate())) .map(Statement::getObject), inverse -> (focus, model) -> model.stream() .filter(s -> inverse.equals(s.getPredicate()) && focus.equals(s.getObject())) .map(Statement::getSubject) ); } public static BiFunction, Stream> seq(final IRI... path) { if ( path == null || Arrays.stream(path).anyMatch(Objects::isNull) ) { throw new NullPointerException("null path"); } return seq(Arrays.stream(path).map(Frame::seq).collect(toList())); } public static BiFunction, Stream> seq( final Collection, Stream>> paths) { if ( paths == null || paths.stream().anyMatch(Objects::isNull) ) { throw new NullPointerException("null paths"); } return (focus, model) -> { Stream values=Stream.of(focus); for (final BiFunction, Stream> path : paths) { values=values.flatMap(value -> path.apply(value, model)); } return values; }; } public static BiFunction, Stream> alt(final IRI... paths) { if ( paths == null || Arrays.stream(paths).anyMatch(Objects::isNull) ) { throw new NullPointerException("null paths"); } return alt(Arrays.stream(paths).map(Frame::seq).collect(toList())); } public static BiFunction, Stream> alt( final Collection, Stream>> paths) { if ( paths == null || paths.stream().anyMatch(Objects::isNull) ) { throw new NullPointerException("null paths"); } return (focus, model) -> paths.stream().flatMap(path -> path.apply(focus, model)); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private final Value focus; private final Set model; private Frame(final Value focus, final Set model) { this.focus=focus; this.model=model; } public Optional label() { return get(Labels).value(Values::string); } public Optional notes() { return get(Notes).value(Values::string); } public Value focus() { return focus; } public Set model() { return unmodifiableSet(model); } public Stream stream() { return model.stream(); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public Getter get(final IRI path) { if ( path == null ) { throw new NullPointerException("null path"); } return get(seq(path)); } public Getter get(final BiFunction, Stream> path) { if ( path == null ) { throw new NullPointerException("null path"); } return new Getter(path.apply(focus, model).collect(toCollection(LinkedHashSet::new)), model); } public Setter set(final IRI path) { if ( path == null ) { throw new NullPointerException("null path"); } return new Setter(this, path); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @Override public String toString() { return format(focus) +label().map(l -> " : "+l).orElse("") +notes().map(l -> " / "+l).orElse(""); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static final class Getter { private final Set values; private final Set model; private Getter(final Set values, final Set model) { this.values=values; this.model=model; } public Optional value() { return values().findFirst(); } public Optional value(final Function> mapper) { if ( mapper == null ) { throw new NullPointerException("null mapper"); } return values(mapper).findFirst(); } public Stream values() { return values.stream(); } public Stream values(final Function> mapper) { if ( mapper == null ) { throw new NullPointerException("null mapper"); } return values.stream() .map(mapper) .map(Objects::requireNonNull) .filter(Optional::isPresent) .map(Optional::get); } public Optional frame() { return frames().findFirst(); } public Stream frames() { return values.stream().map(value -> new Frame(value, model)); } } public static final class Setter { private final Frame frame; private final IRI path; private Setter(final Frame frame, final IRI path) { this.frame=frame; this.path=path; } public Frame value(final Value value) { if ( value == null ) { throw new NullPointerException("null value"); } return new Frame(frame.focus, Stream.concat(frame.model.stream(), traverse(path, direct -> { if ( !(frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for direct predicate"); } return Stream.of(statement((Resource)frame.focus, direct, value)); }, inverse -> { if ( !(value instanceof Resource) ) { throw new IllegalArgumentException("literal value for inverse predicate"); } return Stream.of(statement((Resource)value, inverse, frame.focus)); } )).collect(toCollection(LinkedHashSet::new))); } public Frame value(final Optional value) { if ( value == null ) { throw new NullPointerException("null value"); } return value.map(this::value).orElse(frame); } public Frame values(final Value... values) { if ( values == null || Arrays.stream(values).anyMatch(Objects::isNull) ) { throw new NullPointerException("null values"); } return values.length == 0 ? frame : values(Arrays.stream(values)); } public Frame values(final Collection values) { if ( values == null || values.stream().anyMatch(Objects::isNull) ) { throw new NullPointerException("null values"); } return values.isEmpty() ? frame : values(values.stream()); } public Frame values(final Stream values) { if ( values == null ) { throw new NullPointerException("null values"); } return new Frame(frame.focus, Stream.concat(frame.model.stream(), traverse(path, direct -> values.map(value -> { if ( value == null ) { throw new NullPointerException("null values"); } if ( !(frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for direct field"); } return statement((Resource)frame.focus, path, value); }), inverse -> values.map(value -> { if ( value == null ) { throw new NullPointerException("null values"); } if ( !(value instanceof Resource) ) { throw new IllegalArgumentException("literal value for inverse field"); } return statement((Resource)value, inverse, frame.focus); }) )).collect(toCollection(LinkedHashSet::new))); } public Frame frame(final Frame frame) { if ( frame == null ) { throw new NullPointerException("null frame"); } return new Frame(this.frame.focus, Stream.concat(this.frame.model.stream(), traverse(path, direct -> { if ( !(this.frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for direct predicate"); } return Stream.concat( Stream.of(statement((Resource)this.frame.focus, direct, frame.focus)), frame.model.stream() ); }, inverse -> { if ( !(frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for inverse predicate"); } return Stream.concat( Stream.of(statement((Resource)frame.focus, inverse, this.frame.focus)), frame.model.stream() ); } )).collect(toCollection(LinkedHashSet::new))); } public Frame frame(final Optional frame) { if ( frame == null ) { throw new NullPointerException("null frame"); } return frame.map(this::frame).orElse(this.frame); } public Frame frames(final Frame... frames) { if ( frames == null || Arrays.stream(frames).anyMatch(Objects::isNull) ) { throw new NullPointerException("null values"); } return frames.length == 0 ? frame : frames(Arrays.stream(frames)); } public Frame frames(final Collection frames) { if ( frames == null || frames.stream().anyMatch(Objects::isNull) ) { throw new NullPointerException("null values"); } return frames.isEmpty() ? frame : frames(frames.stream()); } public Frame frames(final Stream frames) { if ( frames == null ) { throw new NullPointerException("null frames"); } return new Frame(frame.focus, Stream.concat(frame.model.stream(), traverse(path, direct -> frames.flatMap(frame -> { if ( frame == null ) { throw new NullPointerException("null frames"); } if ( !(this.frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for direct field"); } return Stream.concat( Stream.of(statement((Resource)this.frame.focus, direct, frame.focus)), frame.model.stream() ); }), inverse -> frames.flatMap(frame -> { if ( frame == null ) { throw new NullPointerException("null frames"); } if ( !(frame.focus instanceof Resource) ) { throw new IllegalArgumentException("literal focus value for inverse field"); } return Stream.concat( Stream.of(statement((Resource)frame.focus, inverse, this.frame.focus)), frame.model.stream() ); }) )).collect(toCollection(LinkedHashSet::new))); } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// private static final class Inverse extends AbstractIRI { private static final long serialVersionUID=7576383707001017160L; private final String string; private final String namespace; private final String localname; Inverse(final String namespace, final String localname) { this.string=namespace+localname; this.namespace=namespace; this.localname=localname; } @Override public String stringValue() { return string; } @Override public String getNamespace() { return namespace; } @Override public String getLocalName() { return localname; } @Override public boolean equals(final Object object) { return object == this || object instanceof Inverse && super.equals(object); } @Override public int hashCode() { return -super.hashCode(); } @Override public String toString() { return "^"+super.toString(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy