PropertyPathBuilder.java

/*******************************************************************************
 * Copyright (c) 2021 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.sparqlbuilder.constraint.propertypath.builder;

import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.p;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pAlt;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pGroup;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pOneOrMore;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pSeq;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pZeroOrMore;
import static org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.builder.PropertyPaths.pZeroOrOne;
import static org.eclipse.rdf4j.sparqlbuilder.rdf.Rdf.iri;

import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.GroupedPath;
import org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.InversePath;
import org.eclipse.rdf4j.sparqlbuilder.constraint.propertypath.PropertyPath;
import org.eclipse.rdf4j.sparqlbuilder.rdf.Iri;

/**
 * @author Florian Kleedorfer
 * @since 4.0.0
 */
public class PropertyPathBuilder {
	private PropertyPath head;

	private PropertyPathBuilder() {
		this.head = null;
	}

	PropertyPathBuilder(Iri predicate) {
		this.head = p(predicate);
	}

	PropertyPathBuilder(IRI predicate) {
		this(iri(predicate));
	}

	public static PropertyPathBuilder of(Iri predicate) {
		return new PropertyPathBuilder(predicate);
	}

	public static PropertyPathBuilder of(IRI predicate) {
		return new PropertyPathBuilder(predicate);
	}

	/**
	 * Build the path.
	 *
	 * @return
	 */
	public PropertyPath build() {
		return head;
	}

	/**
	 * Invert whatever comes next (i.e. append <code>^</code>.
	 */
	public PropertyPathBuilder inv() {
		Objects.requireNonNull(head);
		head = new InversePath(groupIfNotGrouped(head));
		return this;
	}

	private PropertyPath groupIfNotGrouped(PropertyPath path) {
		if (head instanceof GroupedPath) {
			return path;
		}
		return new GroupedPath(path);
	}

	/**
	 * Append <code>`/` predicate</code> to the path.
	 */
	public PropertyPathBuilder then(Iri predicate) {
		return then(p(predicate));
	}

	/**
	 * Append <code>`/` path</code> to the path.
	 */
	public PropertyPathBuilder then(IRI predicate) {
		return then(iri(predicate));
	}

	/**
	 * Append <code>`/` path</code> to the path.
	 */
	public PropertyPathBuilder then(PropertyPath path) {
		Objects.requireNonNull(head);
		head = pSeq(head, path);
		return this;
	}

	/**
	 * Append <code>`/`</code> and the product of the <code>subtreeBuilder</code> to the path.
	 */
	public PropertyPathBuilder then(Consumer<EmptyPropertyPathBuilder> subtreeBuilder) {
		return withSubtree(subtreeBuilder, PropertyPaths::pSeq);
	}

	private PropertyPathBuilder withSubtree(
			Consumer<EmptyPropertyPathBuilder> subtreeBuilder,
			BiFunction<PropertyPath, PropertyPath, PropertyPath> assembler) {
		Objects.requireNonNull(head);
		EmptyPropertyPathBuilder b = new EmptyPropertyPathBuilder();
		subtreeBuilder.accept(b);
		head = assembler.apply(head, b.build());
		return this;
	}

	/**
	 * Append <code>`|` predicate</code> to the path.
	 */
	public PropertyPathBuilder or(Iri predicate) {
		return or(p(predicate));
	}

	/**
	 * Append <code>`|` path</code> to the path.
	 */
	public PropertyPathBuilder or(IRI predicate) {
		return or(iri(predicate));
	}

	/**
	 * Append <code>`|` path</code> to the path.
	 */
	public PropertyPathBuilder or(PropertyPath path) {
		Objects.requireNonNull(head);
		head = pAlt(head, path);
		return this;
	}

	/**
	 * Append <code>`|`</code> and the product of the <code>subtreeBuilder</code> to the path.
	 */
	public PropertyPathBuilder or(Consumer<EmptyPropertyPathBuilder> subtreeBuilder) {
		return withSubtree(subtreeBuilder, PropertyPaths::pAlt);
	}

	/**
	 * Append <code>`*`</code> to the path.
	 */
	public PropertyPathBuilder zeroOrMore() {
		Objects.requireNonNull(head);
		head = pZeroOrMore(head);
		return this;
	}

	/**
	 * Append <code>`+`</code> to the path.
	 */

	public PropertyPathBuilder oneOrMore() {
		Objects.requireNonNull(head);
		head = pOneOrMore(head);
		return this;
	}

	/**
	 * Append <code>`?`</code> to the path.
	 */
	public PropertyPathBuilder zeroOrOne() {
		Objects.requireNonNull(head);
		head = pZeroOrOne(head);
		return this;
	}

	/**
	 * Enclose the path with <code>`(` and `)`</code>.
	 */
	public PropertyPathBuilder group() {
		Objects.requireNonNull(head);
		head = pGroup(head);
		return this;
	}
}