ReificationRdfStarQueryEvaluationStep.java

/*******************************************************************************
 * Copyright (c) 2020 Eclipse RDF4J contributors.
 *
 * 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.query.algebra.evaluation.impl.evaluationsteps;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.common.iteration.ConvertingIteration;
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.MutableBindingSet;
import org.eclipse.rdf4j.query.QueryEvaluationException;
import org.eclipse.rdf4j.query.algebra.Var;
import org.eclipse.rdf4j.query.algebra.evaluation.QueryEvaluationStep;
import org.eclipse.rdf4j.query.algebra.evaluation.TripleSource;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.QueryEvaluationContext;
import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategy;

public class ReificationRdfStarQueryEvaluationStep implements QueryEvaluationStep {
	private final Var objVar;
	private final Var extVar;
	private final Var subjVar;
	private final Var predVar;
	private final TripleSource tripleSource;
	private final QueryEvaluationContext context;

	public ReificationRdfStarQueryEvaluationStep(Var subjVar, Var predVar, Var objVar, Var extVar,
			TripleSource tripleSource, QueryEvaluationContext context) {
		this.objVar = objVar;
		this.extVar = extVar;
		this.subjVar = subjVar;
		this.predVar = predVar;
		this.tripleSource = tripleSource;
		this.context = context;
	}

	@Override
	public CloseableIteration<BindingSet> evaluate(BindingSet bindings) {
		final Value subjValue = StrictEvaluationStrategy.getVarValue(subjVar, bindings);
		final Value predValue = StrictEvaluationStrategy.getVarValue(predVar, bindings);
		final Value objValue = StrictEvaluationStrategy.getVarValue(objVar, bindings);
		final Value extValue = StrictEvaluationStrategy.getVarValue(extVar, bindings);
		// standard reification iteration
		// 1. walk over resources used as subjects of (x rdf:type rdf:Statement)
		final CloseableIteration<? extends Resource> iter = new ConvertingIteration<Statement, Resource>(
				tripleSource.getStatements((Resource) extValue, RDF.TYPE, RDF.STATEMENT)) {

			@Override
			protected Resource convert(Statement sourceObject)
					throws QueryEvaluationException {
				return sourceObject.getSubject();
			}
		};
		// for each reification node, fetch and check the subject, predicate and object values against
		// the expected values from TripleRef pattern and supplied bindings collection
		return new LookAheadIteration<>() {
			@Override
			protected void handleClose() throws QueryEvaluationException {
				iter.close();
			}

			@Override
			protected BindingSet getNextElement()
					throws QueryEvaluationException {
				while (iter.hasNext()) {
					Resource theNode = iter.next();
					MutableBindingSet result = context.createBindingSet(bindings);
					// does it match the subjectValue/subjVar
					if (!matchValue(theNode, subjValue, subjVar, result, RDF.SUBJECT)) {
						continue;
					}
					// the predicate, if not, remove the binding that hass been added
					// when the subjValue has been checked and its value added to the solution
					if (!matchValue(theNode, predValue, predVar, result, RDF.PREDICATE)) {
						continue;
					}
					// check the object, if it do not match
					// remove the bindings added for subj and pred
					if (!matchValue(theNode, objValue, objVar, result, RDF.OBJECT)) {
						continue;
					}
					// add the extVar binding if we do not have a value bound.
					if (extValue == null) {
						result.addBinding(extVar.getName(), theNode);
					} else if (!extValue.equals(theNode)) {
						// the extVar value do not match theNode
						continue;
					}
					return result;
				}
				return null;
			}

			//
			private boolean matchValue(Resource theNode, Value value, Var var, MutableBindingSet result,
					IRI predicate) {
				try (CloseableIteration<? extends Statement> valueIter = tripleSource
						.getStatements(theNode, predicate, null)) {

					while (valueIter.hasNext()) {
						Statement valueStatement = valueIter.next();
						if (theNode.equals(valueStatement.getSubject())) {
							if (value == null || value.equals(valueStatement.getObject())) {
								if (value == null) {
									result.addBinding(var.getName(), valueStatement.getObject());
								}
								return true;
							}
						}
					}
					return false;
				}
			}
//
		};
	} // else standard reification iteration
}