SimplePath.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.sail.shacl.ast.paths;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.rdf4j.common.iteration.CloseableIteration;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.query.QueryResults;
import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment;
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher;
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeWrapper;
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.UnorderedSelect;
import org.eclipse.rdf4j.sail.shacl.ast.targets.EffectiveTarget;
import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup;
import org.eclipse.rdf4j.sail.shacl.wrapper.data.RdfsSubClassOfReasoner;

public class SimplePath extends Path {

	private final IRI predicate;

	public SimplePath(IRI predicate) {
		super(predicate);
		this.predicate = predicate;
	}

	@Override
	public Resource getId() {
		return predicate;
	}

	@Override
	public PlanNode getAllAdded(ConnectionsGroup connectionsGroup, Resource[] dataGraph,
			PlanNodeWrapper planNodeWrapper) {
		PlanNode unorderedSelect = new UnorderedSelect(connectionsGroup.getAddedStatements(), null, predicate, null,
				dataGraph, UnorderedSelect.Mapper.SubjectObjectPropertyShapeMapper.getFunction(), null);

		if (planNodeWrapper != null) {
			unorderedSelect = planNodeWrapper.apply(unorderedSelect);
		}

		return connectionsGroup.getCachedNodeFor(unorderedSelect);
	}

	@Override
	public PlanNode getAnyAdded(ConnectionsGroup connectionsGroup, Resource[] dataGraph,
			PlanNodeWrapper planNodeWrapper) {
		return getAllAdded(connectionsGroup, dataGraph, planNodeWrapper);
	}

	@Override
	public String toString() {
		return "SimplePath{ <" + predicate + "> }";
	}

	@Override
	public void toModel(Resource subject, IRI predicate, Model model, Set<Resource> cycleDetection) {
	}

	@Override
	public SparqlFragment getTargetQueryFragment(StatementMatcher.Variable subject, StatementMatcher.Variable object,
			RdfsSubClassOfReasoner rdfsSubClassOfReasoner,
			StatementMatcher.StableRandomVariableProvider stableRandomVariableProvider, Set<String> inheritedVarNames) {

		StatementMatcher statementMatcher = new StatementMatcher(subject, new StatementMatcher.Variable(predicate),
				object, this, inheritedVarNames);

		return SparqlFragment.bgp(List.of(),
				subject.asSparqlVariable() + " <" + predicate + "> " + object.asSparqlVariable() + " .",
				statementMatcher, (connectionsGroup, dataGraph, path, currentStatementMatcher, currentStatements) -> {
					if (currentStatementMatcher.getOrigin() == this) {
						if (currentStatementMatcher != statementMatcher) {
							return null;
						}
						return Stream.of(
								new EffectiveTarget.SubjectObjectAndMatcher(currentStatements,
										currentStatementMatcher));
					} else {
						if (currentStatementMatcher.hasSubject(object)) {
							var newStatements = currentStatements.stream()
									.map(currentStatement -> {
										try (CloseableIteration<? extends Statement> statements = connectionsGroup
												.getBaseConnection()
												.getStatements(null, predicate, currentStatement.getSubject(), true,
														dataGraph)) {
											return QueryResults.asList(statements);
										}
									})
									.flatMap(List::stream)
									.map(EffectiveTarget.SubjectObjectAndMatcher.SubjectObject::new)
									.collect(Collectors.toList());
							return Stream
									.of(new EffectiveTarget.SubjectObjectAndMatcher(newStatements, statementMatcher));
						} else if (currentStatementMatcher.hasObject(object)) {
							var newStatements = currentStatements.stream()
									.map(currentStatement -> {
										try (CloseableIteration<? extends Statement> statements = connectionsGroup
												.getBaseConnection()
												.getStatements(null, predicate, currentStatement.getObject(), true,
														dataGraph)) {
											return QueryResults.asList(statements);
										}
									})
									.flatMap(List::stream)
									.map(EffectiveTarget.SubjectObjectAndMatcher.SubjectObject::new)
									.collect(Collectors.toList());
							return Stream
									.of(new EffectiveTarget.SubjectObjectAndMatcher(newStatements, statementMatcher));
						}

						return null;
					}

				});
	}

	@Override
	public boolean isSupported() {
		return true;
	}

	@Override
	public String toSparqlPathString() {
		return "<" + predicate + ">";
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}

		SimplePath that = (SimplePath) o;

		return predicate.equals(that.predicate);
	}

	@Override
	public int hashCode() {
		return predicate.hashCode();
	}
}