Path.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.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Namespace;
import org.eclipse.rdf4j.model.Resource;
import org.eclipse.rdf4j.model.Statement;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.sail.shacl.ShaclSailConnection;
import org.eclipse.rdf4j.sail.shacl.ast.Exportable;
import org.eclipse.rdf4j.sail.shacl.ast.Identifiable;
import org.eclipse.rdf4j.sail.shacl.ast.ShaclUnsupportedException;
import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher;
import org.eclipse.rdf4j.sail.shacl.ast.Targetable;
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode;
import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeWrapper;
import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup;
import org.eclipse.rdf4j.sail.shacl.wrapper.shape.ShapeSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class Path implements Identifiable, Exportable, Targetable {

	private static final Logger logger = LoggerFactory.getLogger(ShaclSailConnection.class);

	Resource id;

	public Path(Resource id) {
		this.id = id;
	}

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

	static public Path buildPath(ShapeSource shapeSource, Resource id) {
		if (id == null) {
			return null;
		}

		if (id.isBNode()) {
			List<Statement> collect = shapeSource.getAllStatements(id)
					.filter(statement -> !statement.getPredicate().equals(RDF.TYPE))
					.collect(Collectors.toList());

			Path ret = null;

			for (Statement statement : collect) {
				IRI pathType = statement.getPredicate();

				switch (pathType.toString()) {
				case "http://www.w3.org/ns/shacl#inversePath":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new InversePath(id, (Resource) statement.getObject(), shapeSource);
					break;
				case "http://www.w3.org/ns/shacl#alternativePath":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new AlternativePath(id, (Resource) statement.getObject(), shapeSource);
					break;
				case "http://www.w3.org/ns/shacl#zeroOrMorePath":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new ZeroOrMorePath(id, (Resource) statement.getObject(), shapeSource);
					break;
				case "http://www.w3.org/ns/shacl#oneOrMorePath":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new OneOrMorePath(id, (Resource) statement.getObject(), shapeSource);
					break;
				case "http://www.w3.org/ns/shacl#zeroOrOnePath":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new ZeroOrOnePath(id, (Resource) statement.getObject(), shapeSource);
					break;
				case "http://www.w3.org/1999/02/22-rdf-syntax-ns#first":
					if (ret != null) {
						logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray()));
					}
					ret = new SequencePath(id, shapeSource);
					break;
				default:
					break;
				}

			}

			if (ret != null) {
				return ret;
			} else {
				if (collect.size() == 1) {
					throw new ShaclUnsupportedException("Unknown path type: " + collect.get(0));
				} else {
					throw new ShaclUnsupportedException("Unknown path type: " + Arrays.toString(collect.toArray()));
				}
			}

		} else {
			return new SimplePath((IRI) id);
		}

	}

	/**
	 * Get all values added in this transaction.
	 */
	public abstract PlanNode getAllAdded(ConnectionsGroup connectionsGroup,
			Resource[] dataGraph, PlanNodeWrapper planNodeWrapper);

	/**
	 * Get values added in this transaction. Validation performance may improve if more values are retrieved. Validation
	 * correctness and completeness will not be affected if fewer values are retrieved, or if no items are retrieved.
	 */
	public abstract PlanNode getAnyAdded(ConnectionsGroup connectionsGroup,
			Resource[] dataGraph, PlanNodeWrapper planNodeWrapper);

	public String getVariablePrefix(StatementMatcher.Variable subject, StatementMatcher.Variable object) {
		String className = this.getClass().getSimpleName();
		return subject.getName() + "_" + (object != null ? object.getName() : "null") + "_" + className + "_";
	}

	abstract public boolean isSupported();

	public abstract String toSparqlPathString();

	@Override
	public final Set<Namespace> getNamespaces() {
		return Set.of();
	}

}