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

it.uniroma2.art.semanticturkey.changetracking.model.HistoryRepositories Maven / Gradle / Ivy

There is a newer version: 13.1
Show newest version
package it.uniroma2.art.semanticturkey.changetracking.model;

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

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.eclipse.rdf4j.RDF4JException;
import org.eclipse.rdf4j.common.iteration.ConvertingIteration;
import org.eclipse.rdf4j.common.iteration.Iteration;
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.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.ValueFactory;
import org.eclipse.rdf4j.model.impl.LinkedHashModel;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.model.vocabulary.SESAME;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.GraphQueryResult;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.QueryResult;
import org.eclipse.rdf4j.query.QueryResults;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.impl.SimpleDataset;
import org.eclipse.rdf4j.queryrender.RenderUtils;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.rio.ntriples.NTriplesUtil;

import it.uniroma2.art.semanticturkey.changetracking.vocabulary.CHANGELOG;
import it.uniroma2.art.semanticturkey.changetracking.vocabulary.CHANGETRACKER;
import it.uniroma2.art.semanticturkey.changetracking.vocabulary.PROV;

/**
 * Utility class encapsulating common operations with connections to history repositories.
 * 
 * @author Manuel Fiorelli
 */
public abstract class HistoryRepositories {

	/**
	 * Return the tips of the MASTER branch in the connected history repository. This method throws an
	 * IllegalStateException, if more than one tip is found for MASTER.
	 * 
	 * @param conn
	 * @param historyGraph
	 * @return
	 * @throws IllegalStateException
	 * @throws RDF4JException
	 */
	public static Optional getTip(RepositoryConnection conn, IRI historyGraph)
			throws IllegalStateException, RDF4JException {
		TupleQuery tipQuery = conn.prepareTupleQuery(
			// @formatter:off
			" PREFIX cl:                    \n" +
			" SELECT ?tip                                                                    \n" +
			" WHERE {                                                                        \n" +
			"   cl:MASTER cl:tip ?tip .                                                      \n" +
			"   FILTER(isIRI(?tip) || isBLANK(?tip))                                         \n" +
			" }                                                                              \n" +
			" LIMIT 2                                                                          "
			// @formatter:on
		);
		SimpleDataset dataset = new SimpleDataset();
		dataset.addDefaultGraph(historyGraph);
		tipQuery.setDataset(dataset);

		tipQuery.setIncludeInferred(false);
		List tipList = QueryResults.stream(tipQuery.evaluate()).map(bs -> bs.getValue("tip"))
				.map(Resource.class::cast).collect(toList());

		int tipCount = tipList.size();
		if (tipCount > 1) {
			throw new IllegalStateException("MASTER has an ambiguous tip");
		}

		if (tipCount == 0) {
			return Optional.empty();
		} else {
			return Optional.of(tipList.get(0));
		}
	}

	/**
	 * Returns the statements additions associated with a commit
	 * 
	 * @param conn
	 * @param commit
	 * @param historyGraph
	 * @return
	 */
	public static QueryResult getAddedStaments(RepositoryConnection conn, Resource commit,
			IRI historyGraph) {
		return getCommitStaments(conn, commit, CHANGELOG.ADDED_STATEMENT, historyGraph);
	}

	/**
	 * Returns the statements removals associated with a commit
	 * 
	 * @param conn
	 * @param commit
	 * @param historyGraph
	 * @return
	 */
	public static QueryResult getRemovedStaments(RepositoryConnection conn, Resource commit,
			IRI historyGraph) {
		return getCommitStaments(conn, commit, CHANGELOG.REMOVED_STATEMENT, historyGraph);
	}

	/**
	 * Helper function returning the statements associated with a commit by a certain property. This function
	 * encapsulate the dereification of statements.
	 * 
	 * @param conn
	 * @param commit
	 * @param statementPredicate
	 * @param historyGraph
	 * @return
	 */
	private static QueryResult getCommitStaments(RepositoryConnection conn, Resource commit,
			IRI statementPredicate, IRI historyGraph) {
		TupleQuery tipQuery = conn.prepareTupleQuery(
			// @formatter:off
			" PREFIX cl:                    \n" +
			" PREFIX prov:                                       \n" +
			" SELECT ?s ?p ?o ?c                                                             \n" +
			" WHERE {                                                                        \n" +
			"   ?commit prov:generated ?modifiedTriples .                                    \n" +
			"   ?modifiedTriples ?statementPredicate ?q .                                    \n" +
			"   ?q cl:subject ?s .                                                           \n" +
			"   ?q cl:predicate ?p .                                                         \n" +
			"   ?q cl:object ?o .                                                            \n" +
			"   ?q cl:context ?c .                                                           \n" +
			" }                                                                                "
			// @formatter:on
		);

		tipQuery.setBinding("statementPredicate", statementPredicate);
		tipQuery.setBinding("commit", commit);

		SimpleDataset dataset = new SimpleDataset();
		dataset.addDefaultGraph(historyGraph);
		tipQuery.setDataset(dataset);

		tipQuery.setIncludeInferred(false);
		return new TupleBinding2StatementIteration(tipQuery.evaluate());
	}

	public static Optional getParent(RepositoryConnection conn, Resource commit, IRI historyGraph) {
		List parentList = QueryResults
				.asList(conn.getStatements(commit, CHANGELOG.PARENT_COMMIT, null, historyGraph));

		int parentSize = parentList.size();

		if (parentSize > 1) {
			throw new IllegalStateException(
					"Commit " + NTriplesUtil.toNTriplesString(commit) + " has more than one parent");
		}

		return parentSize != 0 ? Optional.of((Resource) parentList.get(0).getObject()) : Optional.empty();
	}

	public static Model getCommitUserMetadata(RepositoryConnection supportRepoConn, IRI commit, IRI graph,
			boolean rewriteCommit) {
		GraphQuery describeQuery = supportRepoConn.prepareGraphQuery(
				"describe " + RenderUtils.toSPARQL(commit) + " from " + RenderUtils.toSPARQL(graph));
		describeQuery.setIncludeInferred(false);

		LinkedHashModel metadataModel = new LinkedHashModel();
		Map bnodeRewriting = new LinkedHashMap<>();

		try (GraphQueryResult results = describeQuery.evaluate()) {
			while (results.hasNext()) {
				Statement statement = results.next();
				if (statement.getPredicate().getNamespace().equals(CHANGELOG.NAMESPACE))
					continue;

				Value obj = statement.getObject();

				if (obj instanceof IRI) {
					if (((IRI) obj).getNamespace().equals(CHANGELOG.NAMESPACE))
						continue;
				}

				metadataModel
						.add(cloneStatement(statement, SimpleValueFactory.getInstance(), bnodeRewriting));
			}
		}

		metadataModel.remove(commit, PROV.STARTED_AT_TIME, null);
		metadataModel.remove(commit, PROV.ENDED_AT_TIME, null);
		metadataModel.remove(commit, PROV.GENERATED, null);

		if (rewriteCommit) {
			Iterator it = metadataModel.filter(commit, null, null).iterator();
			Model rewrittenStatements = new LinkedHashModel();
			while (it.hasNext()) {
				Statement stmt = it.next();
				it.remove();
				rewrittenStatements.add(CHANGETRACKER.COMMIT_METADATA, stmt.getPredicate(), stmt.getObject());
			}

			metadataModel.addAll(rewrittenStatements);
		}
		return metadataModel;
	}

	public static IRI cloneValue(IRI input, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		return vf.createIRI(input.stringValue());
	}

	public static BNode cloneValue(BNode input, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		if (bnodeRewriting == null) {
			return vf.createBNode(input.stringValue());
		} else {
			return bnodeRewriting.computeIfAbsent(input, i -> vf.createBNode());
		}
	}

	public static Resource cloneValue(Resource input, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		if (input instanceof IRI) {
			return cloneValue((IRI) input, vf, bnodeRewriting);
		} else {
			return cloneValue((BNode) input, vf, bnodeRewriting);
		}
	}

	public static Literal cloneValue(Literal input, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		if (input.getLanguage().isPresent()) {
			return vf.createLiteral(input.getLabel(), input.getLanguage().get());
		} else {
			return vf.createLiteral(input.getLabel(), vf.createIRI(input.getDatatype().stringValue()));
		}
	}

	public static Value cloneValue(Value input, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		if (input instanceof Resource) {
			return cloneValue((Resource) input, vf, bnodeRewriting);
		} else {
			return cloneValue((Literal) input, vf, bnodeRewriting);
		}
	}

	public static Statement cloneStatement(Statement stmt, ValueFactory vf,
			/* @Nullable */ Map bnodeRewriting) {
		return vf.createStatement(cloneValue(stmt.getSubject(), vf, bnodeRewriting),
				cloneValue(stmt.getPredicate(), vf, bnodeRewriting),
				cloneValue(stmt.getObject(), vf, bnodeRewriting),
				stmt.getContext() != null ? cloneValue(stmt.getContext(), vf, bnodeRewriting) : null);

	}

}

class TupleBinding2StatementIteration
		extends ConvertingIteration
		implements QueryResult {

	private final ValueFactory vf;

	public TupleBinding2StatementIteration(
			Iteration iter) {
		super(iter);
		vf = SimpleValueFactory.getInstance();
	}

	@Override
	protected Statement convert(BindingSet sourceObject) throws QueryEvaluationException {
		Resource subject = (Resource) Objects.requireNonNull(sourceObject.getValue("s"),
				"variable ?s may not be null");
		IRI predicate = (IRI) Objects.requireNonNull(sourceObject.getValue("p"),
				"variable ?p may not be null");
		Value object = Objects.requireNonNull(sourceObject.getValue("o"), "variable ?o may not be null");
		Resource context = (Resource) Objects.requireNonNull(sourceObject.getValue("c"),
				"variable ?c may not be null");
		if (SESAME.NIL.equals(context)) {
			context = null;
		}

		return vf.createStatement(HistoryRepositories.cloneValue(subject, vf, null),
				HistoryRepositories.cloneValue(predicate, vf, null),
				HistoryRepositories.cloneValue(object, vf, null),
				context != null ? HistoryRepositories.cloneValue(context, vf, null) : null);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy