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

org.eclipse.rdf4j.model.util.Models Maven / Gradle / Ivy

There is a newer version: 5.1.0-M1
Show newest version
/*******************************************************************************
 * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/
package org.eclipse.rdf4j.model.util;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.eclipse.rdf4j.common.annotation.Experimental;
import org.eclipse.rdf4j.model.BNode;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Literal;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.ModelFactory;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Triple;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.vocabulary.RDF;

/**
 * Utility functions for working with {@link Model}s and other {@link Statement} collections.
 *
 * @author Jeen Broekstra
 * @author Arjohn Kampman
 * @see org.eclipse.rdf4j.model.Model
 * @see org.eclipse.rdf4j.model.util.ModelBuilder
 */
public class Models {

	/*
	 * hidden constructor to avoid instantiation
	 */
	protected Models() {
	}

	/**
	 * Retrieves an object {@link Value} from the supplied statements. If more than one possible object value exists,
	 * any one value is picked and returned.
	 *
	 * @param statements the {@link Statement } {@link Iterable} from which to retrieve an object value.
	 * @return an object value from the given statement collection, or {@link Optional#empty()} if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #object(Model)}.
	 */
	public static Optional object(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false).map(st -> st.getObject()).findAny();
	}

	/**
	 * Retrieves an object {@link Value} from the statements in the given model. If more than one possible object value
	 * exists, any one value is picked and returned.
	 *
	 * @param m the model from which to retrieve an object value.
	 * @return an object value from the given model, or {@link Optional#empty()} if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #object(Iterable)}. This method signature kept for binary
	 *          compatibility.
	 */
	public static Optional object(Model m) {
		return object((Iterable) m);
	}

	/**
	 * Retrieves an object {@link Literal} value from the supplied statements. If more than one possible Literal value
	 * exists, any one Literal value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve an object Literal value.
	 * @return an object Literal value from the given model, or {@link Optional#empty()} if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectLiteral(Model)}.
	 */
	public static Optional objectLiteral(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof Literal)
				.map(l -> (Literal) l)
				.findAny();
	}

	/**
	 * Retrieves an object {@link Literal} value from the statements in the given model. If more than one possible
	 * Literal value exists, any one Literal value is picked and returned.
	 *
	 * @param m the {@link Model} from which to retrieve an object Literal value.
	 * @return an object Literal value from the given model, or {@link Optional#empty()} if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectLiteral(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional objectLiteral(Model m) {
		return objectLiteral((Iterable) m);
	}

	/**
	 * Retrieves all object {@link Literal} values from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve all object {@link Literal}
	 *                   values.
	 * @return a {@link Set} containing object {@link Literal} values from the given model, which will be empty if no
	 *         such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectLiterals(Model)}.
	 * @see Model#objects()
	 */
	public static Set objectLiterals(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof Literal)
				.map(l -> (Literal) l)
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all object {@link Literal} values from the statements in the given model.
	 *
	 * @param m the model from which to retrieve all object {@link Literal} values.
	 * @return a {@link Set} containing object {@link Literal} values from the given model, which will be empty if no
	 *         such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectLiterals(Iterable)}. This method signature kept
	 *          for binary compatibility.
	 * @see Model#objects()
	 */
	public static Set objectLiterals(Model m) {
		return objectLiterals((Iterable) m);
	}

	/**
	 * Retrieves an object {@link Resource} value from the supplied statements. If more than one possible Resource value
	 * exists, any one Resource value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve an object Resource value.
	 * @return an {@link Optional} object Resource value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectResource(Model)}.
	 */
	public static Optional objectResource(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof Resource)
				.map(r -> (Resource) r)
				.findAny();
	}

	/**
	 * Retrieves an object {@link Resource} value from the statements in the given model. If more than one possible
	 * Resource value exists, any one Resource value is picked and returned.
	 *
	 * @param m the model from which to retrieve an object Resource value.
	 * @return an {@link Optional} object Resource value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectResource(Iterable)}. This method signature kept
	 *          for binary compatibility.
	 */
	public static Optional objectResource(Model m) {
		return objectResource((Iterable) m);
	}

	/**
	 * Retrieves all object {@link Resource} values from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve all object {@link Resource}
	 *                   values.
	 * @return a {@link Set} containing object {@link Resource} values from the given model, which will be empty if no
	 *         such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectResources(Model)}.
	 * @see Model#objects()
	 */
	public static Set objectResources(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof Resource)
				.map(r -> (Resource) r)
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all object {@link Resource} values from the supplied model.
	 *
	 * @param m the {@link Model} from which to retrieve all object {@link Resource} values.
	 * @return a {@link Set} containing object {@link Resource} values from the given model, which will be empty if no
	 *         such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectResources(Iterable)}. This method signature kept
	 *          for binary compatibility.
	 * @see Model#objects()
	 */
	public static Set objectResources(Model m) {
		return objectResources((Iterable) m);
	}

	/**
	 * Retrieves an object {@link IRI} value from the supplied statements. If more than one possible IRI value exists,
	 * any one value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve an object IRI value.
	 * @return an {@link Optional} object IRI value from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectIRI(Model)}.
	 */
	public static Optional objectIRI(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof IRI)
				.map(r -> (IRI) r)
				.findAny();
	}

	/**
	 * Retrieves an object {@link IRI} value from the supplied statements in the given model. If more than one possible
	 * IRI value exists, any one value is picked and returned.
	 *
	 * @param m the model from which to retrieve an object IRI value.
	 * @return an {@link Optional} object IRI value from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectIRI(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional objectIRI(Model m) {
		return objectIRI((Iterable) m);
	}

	/**
	 * Retrieves all object {@link IRI} values from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve all object IRI values.
	 * @return a {@link Set} containing object IRI values from the given model, which will be empty if no such value
	 *         exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectIRIs(Model)}.
	 * @see Model#objects()
	 */
	public static Set objectIRIs(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject())
				.filter(o -> o instanceof IRI)
				.map(r -> (IRI) r)
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all object {@link IRI} values from the statements in the given model.
	 *
	 * @param m the {@link Model} from which to retrieve all object IRI values.
	 * @return a {@link Set} containing object IRI values from the given model, which will be empty if no such value
	 *         exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectIRIs(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 * @see Model#objects()
	 */
	public static Set objectIRIs(Model m) {
		return objectIRIs((Iterable) m);
	}

	/**
	 * Retrieves an object value as a String from the supplied statements. If more than one possible object value
	 * exists, any one value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve an object String value.
	 * @return an {@link Optional} object String value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectString(Model)}.
	 */
	public static Optional objectString(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false).map(st -> st.getObject().stringValue()).findAny();
	}

	/**
	 * Retrieves an object value as a String from the statements in the given model. If more than one possible object
	 * value exists, any one value is picked and returned.
	 *
	 * @param m the model from which to retrieve an object String value.
	 * @return an {@link Optional} object String value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectString(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional objectString(Model m) {
		return objectString((Iterable) m);
	}

	/**
	 * Retrieves all object String values from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve all object String values.
	 * @return a {@link Set} containing object String values from the given model, which will be empty if no such value
	 *         exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #objectStrings(Model)}.
	 * @see Model#objects()
	 */
	public static Set objectStrings(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getObject().stringValue())
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all object String values from the statements in the given model.
	 *
	 * @param m the model from which to retrieve all object String values.
	 * @return a {@link Set} containing object String values from the given model, which will be empty if no such value
	 *         exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #objectStrings(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 * @see Model#objects()
	 */
	public static Set objectStrings(Model m) {
		return objectStrings((Iterable) m);
	}

	/**
	 * Retrieves a subject {@link Resource} from the supplied statements. If more than one possible resource value
	 * exists, any one resource value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a subject Resource.
	 * @return an {@link Optional} subject resource from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #subject(Model)}.
	 */
	public static Optional subject(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false).map(st -> st.getSubject()).findAny();
	}

	/**
	 * Retrieves a subject {@link Resource} from the statements in the given model. If more than one possible resource
	 * value exists, any one resource value is picked and returned.
	 *
	 * @param m the model from which to retrieve a subject Resource.
	 * @return an {@link Optional} subject resource from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #subject(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional subject(Model m) {
		return subject((Iterable) m);
	}

	/**
	 * Retrieves a subject {@link IRI} from the supplied statements. If more than one possible IRI value exists, any one
	 * IRI value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a subject IRI value.
	 * @return an {@link Optional} subject IRI value from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #subjectIRI(Model)}.
	 */
	public static Optional subjectIRI(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getSubject())
				.filter(s -> s instanceof IRI)
				.map(s -> (IRI) s)
				.findAny();
	}

	/**
	 * Retrieves a subject {@link IRI} from the statements in the given model. If more than one possible IRI value
	 * exists, any one IRI value is picked and returned.
	 *
	 * @param m the model from which to retrieve a subject IRI value.
	 * @return an {@link Optional} subject IRI value from the given model, which will be {@link Optional#empty() empty}
	 *         if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #subjectIRI(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional subjectIRI(Model m) {
		return subjectIRI((Iterable) m);
	}

	/**
	 * Retrieves all subject {@link IRI}s from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a subject IRI value.
	 * @return a {@link Set} of subject IRI values from the given model. The returned Set may be empty.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #subjectIRIs(Model)}.
	 */
	public static Set subjectIRIs(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getSubject())
				.filter(o -> o instanceof IRI)
				.map(r -> (IRI) r)
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all subject {@link IRI}s from the statements in the given model.
	 *
	 * @param m the model from which to retrieve a subject IRI value.
	 * @return a {@link Set} of subject IRI values from the given model. The returned Set may be empty.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #subjectIRIs(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Set subjectIRIs(Model m) {
		return subjectIRIs((Iterable) m);
	}

	/**
	 * Retrieves a subject {@link BNode} from the supplied statements. If more than one possible blank node value
	 * exists, any one blank node value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a subject BNode value.
	 * @return an {@link Optional} subject BNode value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #subjectBNode(Model)}.
	 */
	public static Optional subjectBNode(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getSubject())
				.filter(s -> s instanceof BNode)
				.map(s -> (BNode) s)
				.findAny();
	}

	/**
	 * Retrieves a subject {@link BNode} from the statements in the given model. If more than one possible blank node
	 * value exists, any one blank node value is picked and returned.
	 *
	 * @param m the model from which to retrieve a subject BNode value.
	 * @return an {@link Optional} subject BNode value from the given model, which will be {@link Optional#empty()
	 *         empty} if no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #subjectBNode(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional subjectBNode(Model m) {
		return subjectBNode((Iterable) m);
	}

	/**
	 * Retrieves all subject {@link BNode}s from the supplied statements.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a subject IRI value.
	 * @return a {@link Set} of subject {@link BNode} values from the given model. The returned Set may be empty.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #subjectBNodes(Model)}.
	 */
	public static Set subjectBNodes(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false)
				.map(st -> st.getSubject())
				.filter(o -> o instanceof BNode)
				.map(r -> (BNode) r)
				.collect(Collectors.toSet());
	}

	/**
	 * Retrieves all subject {@link BNode}s from the statements in the given model.
	 *
	 * @param m the model from which to retrieve a subject IRI value.
	 * @return a {@link Set} of subject {@link BNode} values from the given model. The returned Set may be empty.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #subjectBNodes(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Set subjectBNodes(Model m) {
		return subjectBNodes((Iterable) m);
	}

	/**
	 * Retrieves a predicate from the supplied statements. If more than one possible predicate value exists, any one
	 * value is picked and returned.
	 *
	 * @param statements the {@link Statement} {@link Iterable} from which to retrieve a predicate value.
	 * @return an {@link Optional} predicate value from the given model, which will be {@link Optional#empty() empty} if
	 *         no such value exists.
	 * @apiNote this method signature is new in 3.2.0, and is a generalization of {@link #predicate(Model)}.
	 */
	public static Optional predicate(Iterable statements) {
		return StreamSupport.stream(statements.spliterator(), false).map(st -> st.getPredicate()).findAny();
	}

	/**
	 * Retrieves a predicate from the statements in the given model. If more than one possible predicate value exists,
	 * any one value is picked and returned.
	 *
	 * @param m the model from which to retrieve a predicate value.
	 * @return an {@link Optional} predicate value from the given model, which will be {@link Optional#empty() empty} if
	 *         no such value exists.
	 * @apiNote replaced in 3.2.0 with the more generic {@link #predicate(Iterable)}. This method signature kept for
	 *          binary compatibility.
	 */
	public static Optional predicate(Model m) {
		return predicate((Iterable) m);
	}

	/**
	 * Sets the property value for the given subject to the given object value, replacing any existing value(s) for the
	 * subject's property. This method updates the original input Model and then returns that same Model object.
	 *
	 * @param m        the model in which to set the property value. May not be null.
	 * @param subject  the subject for which to set/replace the property value. May not be null.
	 * @param property the property for which to set/replace the value. May not be null.
	 * @param value    the value to set for the given subject and property. May not be null.
	 * @param contexts the context(s) in which to set/replace the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return the Model object, containing the updated property value.
	 */
	public static Model setProperty(Model m, Resource subject, IRI property, Value value, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		Objects.requireNonNull(value, "value may not be null");

		if (m.contains(subject, property, null, contexts)) {
			m.remove(subject, property, null, contexts);
		}
		m.add(subject, property, value, contexts);
		return m;
	}

	/**
	 * Retrieve a property value for the supplied subject from the given model. If more than one property value exists,
	 * any one value is picked and returned.
	 *
	 * @param m        the model from which to retrieve an object value.
	 * @param subject  the subject resource for which to retrieve a property value.
	 * @param property the property for which to retrieve a value.
	 * @param contexts the contexts from which to retrieve the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a property value from the given model, or {@link Optional#empty()} if no such value exists.
	 */
	public static Optional getProperty(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return object(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve all property values for the supplied subject and property from the given model.
	 *
	 * @param m        the model from which to retrieve the property values.
	 * @param subject  the subject resource for which to retrieve all property values.
	 * @param property the property for which to retrieve all values.
	 * @param contexts the contexts from which to retrieve the property values. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a Set of all property values for the supplied input. The resulting set may be empty.
	 */
	public static Set getProperties(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return m.filter(subject, property, null, contexts).objects();
	}

	/**
	 * Retrieve a property value as an IRI for the supplied subject from the given model. If more than one property
	 * value exists, any one value is picked and returned.
	 *
	 * @param m        the model from which to retrieve an object value.
	 * @param subject  the subject resource for which to retrieve a property value.
	 * @param property the property for which to retrieve a value.
	 * @param contexts the contexts from which to retrieve the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a property value Resource from the given model, or {@link Optional#empty()} if no such value exists.
	 */
	public static Optional getPropertyResource(Model m, Resource subject, IRI property,
			Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectResource(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve all property Resource values for the supplied subject and property from the given model.
	 *
	 * @param m        the model from which to retrieve the property Resource values.
	 * @param subject  the subject resource for which to retrieve all property Resource values.
	 * @param property the property for which to retrieve all Resource values.
	 * @param contexts the contexts from which to retrieve the property values. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a Set of all property Resource values for the supplied input. The resulting set may be empty.
	 */
	public static Set getPropertyResources(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectResources(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve a property value as an IRI for the supplied subject from the given model. If more than one property
	 * value exists, any one value is picked and returned.
	 *
	 * @param m        the model from which to retrieve an object value.
	 * @param subject  the subject resource for which to retrieve a property value.
	 * @param property the property for which to retrieve a value.
	 * @param contexts the contexts from which to retrieve the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a property value IRI from the given model, or {@link Optional#empty()} if no such value exists.
	 */
	public static Optional getPropertyIRI(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectIRI(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve all property IRI values for the supplied subject and property from the given model.
	 *
	 * @param m        the model from which to retrieve the property IRI values.
	 * @param subject  the subject resource for which to retrieve all property IRI values.
	 * @param property the property for which to retrieve all IRI values.
	 * @param contexts the contexts from which to retrieve the property values. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a Set of all property IRI values for the supplied input. The resulting set may be empty.
	 */
	public static Set getPropertyIRIs(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectIRIs(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve a property value as a {@link Literal} for the supplied subject from the given model. If more than one
	 * property value exists, any one value is picked and returned.
	 *
	 * @param m        the model from which to retrieve an object value.
	 * @param subject  the subject resource for which to retrieve a property literal value.
	 * @param property the property for which to retrieve a value.
	 * @param contexts the contexts from which to retrieve the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a property value Literal from the given model, or {@link Optional#empty()} if no such value exists.
	 */
	public static Optional getPropertyLiteral(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectLiteral(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve all property Literal values for the supplied subject and property from the given model.
	 *
	 * @param m        the model from which to retrieve the property Literal values.
	 * @param subject  the subject resource for which to retrieve all property Literal values.
	 * @param property the property for which to retrieve all Literal values.
	 * @param contexts the contexts from which to retrieve the property values. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a Set of all property IRI values for the supplied input. The resulting set may be empty.
	 */
	public static Set getPropertyLiterals(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectLiterals(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve a property value as a String for the supplied subject from the given model. If more than one property
	 * value exists, any one value is picked and returned.
	 *
	 * @param m        the model from which to retrieve an object value.
	 * @param subject  the subject resource for which to retrieve a property literal value.
	 * @param property the property for which to retrieve a value.
	 * @param contexts the contexts from which to retrieve the property value. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a property value String from the given model, or {@link Optional#empty()} if no such value exists.
	 */
	public static Optional getPropertyString(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectString(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Retrieve all property values as Strings for the supplied subject and property from the given model.
	 *
	 * @param m        the model from which to retrieve the property values as Strings.
	 * @param subject  the subject resource for which to retrieve all property values as Strings.
	 * @param property the property for which to retrieve all values as Strings.
	 * @param contexts the contexts from which to retrieve the property values. Optional vararg argument. If not
	 *                 specified the operations works on the entire Model.
	 * @return a Set of all property values as Strings for the supplied input. The resulting set may be empty.
	 */
	public static Set getPropertyStrings(Model m, Resource subject, IRI property, Resource... contexts) {
		Objects.requireNonNull(m, "model may not be null");
		Objects.requireNonNull(subject, "subject may not be null");
		Objects.requireNonNull(property, "property may not be null");
		return objectStrings(m.getStatements(subject, property, null, contexts));
	}

	/**
	 * Compares two RDF models, and returns true if they consist of isomorphic graphs and the isomorphic
	 * graph identifiers map 1:1 to each other. RDF graphs are isomorphic graphs if statements from one graphs can be
	 * mapped 1:1 on to statements in the other graphs. In this mapping, blank nodes are not considered mapped when
	 * having an identical internal id, but are mapped from one graph to the other by looking at the statements in which
	 * the blank nodes occur. A Model can consist of more than one graph (denoted by context identifiers). Two models
	 * are considered isomorphic if for each of the graphs in one model, an isomorphic graph exists in the other model,
	 * and the context identifiers of these graphs are either identical or (in the case of blank nodes) map 1:1 on each
	 * other.
	 *
	 * @see RDF Concepts & Abstract Syntax, section
	 *      3.6 (Graph Comparison)
	 */
	public static boolean isomorphic(Iterable model1, Iterable model2) {
		if (model1 == model2) {
			return true;
		}

		Model set1 = toModel(model1);
		Model set2 = toModel(model2);

		return GraphComparisons.isomorphic(set1, set2);
	}

	/**
	 * Legacy implementation of {@link #isomorphic(Iterable, Iterable) isomorphic comparison}. This method is offered as
	 * a temporary fallback for corner cases where the newly introduced isomorphism algorithm (in release 3.6.0) has
	 * worse performance or an unexpected result.
	 *
	 * @apiNote This method is offered as a temporary fallback only, and will likely be removed again quite soon in a
	 *          future minor or major release.
	 * @implNote This uses an algorithm that has poor performance in many cases and can potentially get stuck in an
	 *           endless loop. We strongly recommend using the new algorithm available in the
	 *           {@link #isomorphic(Iterable, Iterable)} implementation.
	 * @see #isomorphic(Iterable, Iterable)
	 * @since 3.6.0
	 * @deprecated Use {@link #isomorphic(Iterable, Iterable)} instead.
	 */
	@Experimental
	@Deprecated(since = "3.6.0")
	public static boolean legacyIsomorphic(Iterable model1, Iterable model2) {
		if (model1 == model2) {
			return true;
		}

		Set set1 = toSet(model1);
		Model set2 = toModel(model2);
		// Compare the number of statements in both sets
		if (set1.size() != set2.size()) {
			return false;
		}

		return isSubsetInternal(set1, set2);
	}

	/**
	 * Compares two RDF models, and returns true if the first model is a subset of the second model, using
	 * graph isomorphism to map statements between models.
	 */
	public static boolean isSubset(Iterable model1, Iterable model2) {
		// Filter duplicates
		Set set1 = toSet(model1);
		Model set2 = toModel(model2);

		return isSubset(set1, set2);
	}

	/**
	 * Compares two RDF models, and returns true if the first model is a subset of the second model, using
	 * graph isomorphism to map statements between models.
	 */
	public static boolean isSubset(Set model1, Set model2) {
		// Compare the number of statements in both sets
		if (model1.size() > model2.size()) {
			return false;
		}

		return isSubsetInternal(toSet(model1), toModel(model2));
	}

	/**
	 * Strips contexts from the input model. This method provides a new {@link Model} containing all statements from the
	 * input model, with the supplied contexts removed from those statements.
	 *
	 * @param model    the input model
	 * @param contexts the contexts to remove. This is a vararg and as such is optional. If not supplied, the method
	 *                 strips all contexts.
	 * @return a new {@link Model} object containg the same statements as the input model, with the supplied contexts
	 *         stripped.
	 */
	public static Model stripContexts(Model model, Resource... contexts) {
		final List contextList = Arrays.asList(contexts);
		return model.stream().map(st -> {
			if (contextList.isEmpty() || contextList.contains(st.getContext())) {
				return Statements.stripContext(st);
			} else {
				return st;
			}
		}).collect(ModelCollector.toModel());
	}

	/**
	 * Creates a {@link Supplier} of {@link ModelException} objects that be passed to
	 * {@link Optional#orElseThrow(Supplier)} to generate exceptions as necessary.
	 *
	 * @param message The message to be used for the exception
	 * @return A {@link Supplier} that will create {@link ModelException} objects with the given message.
	 */
	public static Supplier modelException(String message) {
		return () -> new ModelException(message);
	}

	/**
	 * Make a model thread-safe by synchronizing all its methods. Iterators will still not be thread-safe!
	 *
	 * @param toSynchronize the model that should be synchronized
	 * @return Synchronized Model
	 */
	public static Model synchronizedModel(Model toSynchronize) {
		return new SynchronizedModel(toSynchronize);
	}

	/**
	 * Converts the supplied RDF-star model to RDF reification statements. The converted statements are sent to the
	 * supplied consumer function.
	 * 

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @param consumer the {@link Consumer} function for the produced statements. */ @Experimental public static void convertRDFStarToReification(ValueFactory vf, Model model, Consumer consumer) { model.forEach(st -> Statements.convertRDFStarToReification(vf, st, consumer)); } /** * Converts the supplied RDF-star model to RDF reification statements. The converted statements are sent to the * supplied consumer function. * * @param model the {@link Model} to convert. * @param consumer the {@link Consumer} function for the produced statements. */ @Experimental public static void convertRDFStarToReification(Model model, Consumer consumer) { convertRDFStarToReification(SimpleValueFactory.getInstance(), model, consumer); } /** * Converts the statements in supplied RDF-star model to a new RDF model using reificiation. *

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @return a new {@link Model} with RDF-star statements converted to reified triples. */ @Experimental public static Model convertRDFStarToReification(ValueFactory vf, Model model) { Model reificationModel = new LinkedHashModel(); convertRDFStarToReification(vf, model, (Consumer) reificationModel::add); return reificationModel; } /** * Converts the statements in supplied RDF-star model to a new RDF model using reificiation. *

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @param modelFactory the {@link ModelFactory} used to create the new output {@link Model}. * @return a new {@link Model} with RDF-star statements converted to reified triples. */ @Experimental public static Model convertRDFStarToReification(ValueFactory vf, Model model, ModelFactory modelFactory) { Model reificationModel = modelFactory.createEmptyModel(); convertRDFStarToReification(vf, model, (Consumer) reificationModel::add); return reificationModel; } /** * Converts the statements in the supplied RDF-star model to a new RDF model using reification. * * @param model the {@link Model} to convert. * @return a new {@link Model} with RDF-star statements converted to reified triples. */ @Experimental public static Model convertRDFStarToReification(Model model) { return convertRDFStarToReification(SimpleValueFactory.getInstance(), model); } /** * Converts the supplied RDF reification model to RDF-star statements. The converted statements are sent to the * supplied consumer function. *

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @param consumer the {@link Consumer} function for the produced statements. */ @Experimental public static void convertReificationToRDFStar(ValueFactory vf, Model model, Consumer consumer) { Map convertedStatements = new HashMap<>(); model.filter(null, RDF.TYPE, RDF.STATEMENT).forEach((s) -> { Value subject = object(model.filter(s.getSubject(), RDF.SUBJECT, null)).orElse(null); if (!(subject instanceof IRI) && !(subject instanceof BNode)) { return; } Value predicate = object(model.filter(s.getSubject(), RDF.PREDICATE, null)).orElse(null); if (!(predicate instanceof IRI)) { return; } Value object = object(model.filter(s.getSubject(), RDF.OBJECT, null)).orElse(null); if (!(object instanceof Value)) { return; } Triple t = vf.createTriple((Resource) subject, (IRI) predicate, object); convertedStatements.put(s.getSubject(), t); }); for (Map.Entry e : convertedStatements.entrySet()) { Triple t = e.getValue(); Resource subject = convertedStatements.get(t.getSubject()); Resource object = convertedStatements.get(t.getObject()); if (subject != null || object != null) { // Triples within triples, replace them in the map Triple nt = vf.createTriple(subject != null ? subject : t.getSubject(), t.getPredicate(), object != null ? object : t.getObject()); e.setValue(nt); } } model.forEach((s) -> { Resource subject = s.getSubject(); IRI predicate = s.getPredicate(); Value object = s.getObject(); Triple subjectTriple = convertedStatements.get(subject); Triple objectTriple = convertedStatements.get(object); if (subjectTriple == null && objectTriple == null) { // Statement not part of detected reification, add it as is consumer.accept(s); } else if (subjectTriple == null || ((!RDF.TYPE.equals(predicate) || !RDF.STATEMENT.equals(object)) && !RDF.SUBJECT.equals(predicate) && !RDF.PREDICATE.equals(predicate) && !RDF.OBJECT.equals(predicate))) { // Statement uses reified data and needs to be converted Statement ns = vf.createStatement(subjectTriple != null ? subjectTriple : s.getSubject(), s.getPredicate(), objectTriple != null ? objectTriple : s.getObject(), s.getContext()); consumer.accept(ns); } // else: Statement part of reification and needs to be removed (skipped) }); } /** * Converts the supplied RDF reification model to RDF-star statements. The converted statements are sent to the * supplied consumer function. * * @param model the {@link Model} to convert. * @param consumer the {@link Consumer} function for the produced statements. */ @Experimental public static void convertReificationToRDFStar(Model model, Consumer consumer) { convertReificationToRDFStar(SimpleValueFactory.getInstance(), model, consumer); } /** * Converts the statements in supplied RDF reification model to a new RDF-star model. *

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @param modelFactory the {@link ModelFactory} to use for creating a new Model object for the output. * @return a new {@link Model} with reification statements converted to RDF-star {@link Triple}s. */ @Experimental public static Model convertReificationToRDFStar(ValueFactory vf, Model model, ModelFactory modelFactory) { Model rdfStarModel = modelFactory.createEmptyModel(); convertReificationToRDFStar(vf, model, (Consumer) rdfStarModel::add); return rdfStarModel; } /** * Converts the statements in supplied RDF reification model to a new RDF-star model. *

* The supplied value factory is used to create all new statements. * * @param vf the {@link ValueFactory} to use for creating statements. * @param model the {@link Model} to convert. * @return a new {@link Model} with reification statements converted to RDF-star {@link Triple}s. */ @Experimental public static Model convertReificationToRDFStar(ValueFactory vf, Model model) { return convertReificationToRDFStar(vf, model, new DynamicModelFactory()); } /** * Converts the supplied RDF reification model to a new RDF-star model. * * @param model the {@link Model} to convert. * @return a new {@link Model} with reification statements converted to RDF-star {@link Triple}s. */ @Experimental public static Model convertReificationToRDFStar(Model model) { return convertReificationToRDFStar(SimpleValueFactory.getInstance(), model); } private static boolean isSubsetInternal(Set model1, Model model2) { // try to create a full blank node mapping return matchModels(model1, model2); } private static boolean matchModels(Set model1, Model model2) { // Compare statements without blank nodes first, save the rest for later List model1BNodes = new ArrayList<>(model1.size()); for (Statement st : model1) { if (isBlank(st.getSubject()) || isBlank(st.getObject()) || isBlank(st.getContext())) { model1BNodes.add(st); } else { if (!model2.contains(st)) { return false; } } } return matchModels(Collections.unmodifiableList(model1BNodes), model2); } /** * A recursive method for finding a complete mapping between blank nodes in model1 and blank nodes in model2. The * algorithm does a depth-first search trying to establish a mapping for each blank node occurring in model1. * * @param model1 * @param model2 * @return true if a complete mapping has been found, false otherwise. */ private static boolean matchModels(final List model1, final Model model2) { ArrayDeque> iterators = new ArrayDeque<>(); ArrayDeque> bNodeMappings = new ArrayDeque<>(); Map bNodeMapping = Collections.emptyMap(); int idx = 0; Iterator iterator = null; while (true) { if (idx >= model1.size()) { return true; } Statement st1 = model1.get(idx); if (iterator == null) { List matchingStats = findMatchingStatements(st1, model2, bNodeMapping); iterator = matchingStats.iterator(); } if (iterator.hasNext()) { Statement st2 = iterator.next(); // Map bNodes in st1 to bNodes in st2 Map newBNodeMapping = createNewBnodeMapping(bNodeMapping, st1, st2); iterators.addLast(iterator); bNodeMappings.addLast(bNodeMapping); iterator = null; bNodeMapping = newBNodeMapping; idx++; } if (iterator != null) { idx--; if (idx < 0) { return false; } iterator = iterators.removeLast(); bNodeMapping = bNodeMappings.removeLast(); } } } private static Map createNewBnodeMapping(Map bNodeMapping, Statement st1, Statement st2) { Map newBNodeMapping = new HashMap<>(bNodeMapping); if (isBlank(st1.getSubject()) && isBlank(st2.getSubject())) { newBNodeMapping.put(st1.getSubject(), st2.getSubject()); } if (isBlank(st1.getObject()) && isBlank(st2.getObject())) { newBNodeMapping.put((Resource) st1.getObject(), (Resource) st2.getObject()); } if (isBlank(st1.getContext()) && isBlank(st2.getContext())) { newBNodeMapping.put(st1.getContext(), st2.getContext()); } return newBNodeMapping; } private static List findMatchingStatements(Statement st, Model model, Map bNodeMapping) { Resource s = isBlank(st.getSubject()) ? null : st.getSubject(); IRI p = st.getPredicate(); Value o = isBlank(st.getObject()) ? null : st.getObject(); Resource[] g = isBlank(st.getContext()) ? new Resource[0] : new Resource[] { st.getContext() }; List result = new ArrayList<>(); for (Statement modelSt : model.filter(s, p, o, g)) { if (statementsMatch(st, modelSt, bNodeMapping)) { // All components possibly match result.add(modelSt); } } return Collections.unmodifiableList(result); } private static boolean statementsMatch(Statement st1, Statement st2, Map bNodeMapping) { IRI pred1 = st1.getPredicate(); IRI pred2 = st2.getPredicate(); if (!pred1.equals(pred2)) { // predicates don't match return false; } Resource subj1 = st1.getSubject(); Resource subj2 = st2.getSubject(); if (bnodeValueMatching(bNodeMapping, subj1, subj2)) { return false; } Value obj1 = st1.getObject(); Value obj2 = st2.getObject(); if (bnodeValueMatching(bNodeMapping, obj1, obj2)) { return false; } Resource context1 = st1.getContext(); Resource context2 = st2.getContext(); // no match if in different contexts if (context1 == null) { return context2 == null; } else if (context2 == null) { return false; } if (bnodeValueMatching(bNodeMapping, context1, context2)) { return false; } return true; } private static boolean bnodeValueMatching(Map bNodeMapping, Value obj1, Value obj2) { if (isBlank(obj1) && isBlank(obj2)) { Resource mappedBNode = bNodeMapping.get(obj1); if (mappedBNode != null) { // bNode 'obj1' was already mapped to some other bNode // 'obj1' and 'obj2' do not match return !obj2.equals(mappedBNode); } else { // 'obj1' was not yet mapped. we need to check if 'obj2' is a // possible mapping candidate // 'obj2' is already mapped to some other value. return bNodeMapping.containsValue(obj2); } } else { // objects are not (both) bNodes return !obj1.equals(obj2); } } private static boolean isBlank(Value value) { if (value instanceof BNode) { return true; } if (value instanceof IRI) { boolean skolemizedBNode = value.stringValue().contains("/.well-known/genid/"); return skolemizedBNode; } return false; } private static Model toModel(Iterable iterable) { if (iterable instanceof Model) { return (Model) iterable; } final Model set; if (iterable instanceof Collection) { int size = ((Collection) iterable).size(); set = new LinkedHashModel(size); } else { set = new LinkedHashModel(); } StreamSupport.stream(iterable.spliterator(), false).filter(Objects::nonNull).forEach(set::add); return set; } private static Set toSet(Iterable iterable) { if (iterable instanceof Set) { return (Set) iterable; } if (iterable instanceof Collection) { return new HashSet<>((Collection) iterable); } else { HashSet statements = new HashSet<>(); StreamSupport.stream(iterable.spliterator(), false).filter(Objects::nonNull).forEach(statements::add); return statements; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy