Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
// Copyright (c) 2019, 2020 RTX BBN Technologies Corp.
package com.bbn.parliament.sparql_query_builder;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.function.BinaryOperator;
import java.util.function.UnaryOperator;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.sparql.path.Path;
import org.apache.jena.sparql.path.PathFactory;
/**
* Builds SPARQL property paths in the form of Jena's {@code Path} class using a
* fluent API. The correct mental model for using this builder is a reverse
* Polish notation (RPN) calculator, in other words it uses post-fix ordering of
* operands and operators. For instance, to produce the property path
* {@code :foo/:bar/:baz} use this code:
*
*
*
* Most of the methods in this class throw {@code IllegalStateException} in case
* of error. For instance, the {@code sequence} method will throw if there are
* not two arguments preceding it in the stack.
*
* @author iemmons
*/
public class PathBuilder {
private static final int MIN_PATH_OPERATOR_ARGS = 2;
/** Indicates that no minimum or maximum path length is being specified */
public static final long UNSPECIFIED = PathFactory.UNSET;
private transient final Deque tokens;
/**
* Create an empty {@code PathBuilder}
*/
public PathBuilder() {
tokens = new ArrayDeque<>();
}
/**
* Push a predicate onto the stack
*
* @param resource The predicate, represented as a {@code Resource}
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder pushIri(Resource resource) {
return pushIri(resource.asNode());
}
/**
* Push a predicate onto the stack
*
* @param node The predicate, represented as a {@code Node}
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder pushIri(Node node) {
tokens.addFirst(PathFactory.pathLink(node));
return this;
}
/**
* Push a predicate onto the stack
*
* @param iri The predicate, represented as a {@code String}
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder pushIri(String iri) {
return pushIri(NodeFactory.createURI(iri));
}
/**
* Consume the previous two items from the stack, combine them as alternatives,
* and push the result back onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder alternative() {
return build("alternative", PathFactory::pathAlt);
}
/**
* Consume the previous item from the stack, add the distinct modifier, and push
* the result back onto the stack. This is an extension to the SPARQL standard.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder distinct() {
return build("distinct", PathFactory::pathDistinct);
}
/**
* Consume the previous item from the stack, invert it, and push the result back
* onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder invert() {
return build("inverse", PathFactory::pathInverse);
}
/**
* Consume the previous item from the stack, add the length-between modifier,
* and push the result back onto the stack. This is an extension to the SPARQL
* standard.
*
* @param minimum The minimum length of the path. Set to {@code UNSPECIFIED} (or
* zero) to leave the minimum unspecified.
* @param maximum The maximum length of the path. Set to {@code UNSPECIFIED} to
* leave the maximum unspecified.
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder lengthBetween(long minimum, long maximum) {
long min = (minimum == 0) ? UNSPECIFIED : minimum;
if (min == UNSPECIFIED && maximum == UNSPECIFIED) {
return zeroOrMore();
} else if (min == maximum) {
return build("mod", path -> PathFactory.pathFixedLength(path, maximum));
} else {
return build("lengthBetween", path -> PathFactory.pathMod(path, min, maximum));
}
}
/**
* Consume the previous item from the stack, add the multi modifier, and push
* the result back onto the stack. This is an extension to the SPARQL standard.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder multi() {
return build("multi", PathFactory::pathMulti);
}
/**
* Consume the previous item from the stack, negate it, and push the result back
* onto the stack. (Currently not implemented.)
*
* @return This {@code PathBuilder} to enable method chaining
*/
@SuppressWarnings("static-method")
public PathBuilder negate() {
throw new UnsupportedOperationException(
"Jena's PathFactory class does not support property path negation");
}
/**
* Consume the previous item from the stack, add the one-or-more operator, and
* push the result back onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder oneOrMore() {
return build("oneOrMore", PathFactory::pathOneOrMore1);
}
/**
* Consume the previous item from the stack, add the one-or-more-N modifier, and
* push the result back onto the stack. This is an extension to the SPARQL
* standard.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder oneOrMoreN() {
return build("oneOrMoreN", PathFactory::pathOneOrMoreN);
}
/**
* Consume the previous two items from the stack, combine them as a sequence,
* and push the result back onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder sequence() {
return build("sequence", PathFactory::pathSeq);
}
/**
* Consume the previous item from the stack, add the shortest modifier, and push
* the result back onto the stack. This is an extension to the SPARQL standard.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder shortest() {
return build("shortest", PathFactory::pathShortest);
}
/**
* Consume the previous item from the stack, add the zero-or-more operator, and
* push the result back onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder zeroOrMore() {
return build("zeroOrMore", PathFactory::pathZeroOrMore1);
}
/**
* Consume the previous item from the stack, add the zero-or-more-N modifier,
* and push the result back onto the stack. This is an extension to the SPARQL
* standard.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder zeroOrMoreN() {
return build("zeroOrMoreN", PathFactory::pathZeroOrMoreN);
}
/**
* Consume the previous item from the stack, add the zero-or-one operator, and
* push the result back onto the stack.
*
* @return This {@code PathBuilder} to enable method chaining
*/
public PathBuilder zeroOrOne() {
return build("zeroOrOne", PathFactory::pathZeroOrOne);
}
private PathBuilder build(String label, UnaryOperator operator) {
if (tokens.isEmpty()) {
throw new IllegalStateException("No argument available for " + label + " path operator");
}
Path arg = tokens.removeFirst();
tokens.addFirst(operator.apply(arg));
return this;
}
private PathBuilder build(String label, BinaryOperator operator) {
if (tokens.size() < MIN_PATH_OPERATOR_ARGS) {
throw new IllegalStateException("Insufficient arguments available for " + label + " path operator");
}
Path rhs = tokens.removeFirst();
Path lhs = tokens.removeFirst();
tokens.addFirst(operator.apply(lhs, rhs));
return this;
}
/**
* Build the completed path, leaving the {@code PathBuilder} ready to build
* another property path. Throws {@code IllegalStateException} if there is not
* exactly one entry on the stack.
*
* @return The completed path
*/
public Path build() {
if (tokens.isEmpty()) {
throw new IllegalStateException("Path is empty");
} else if (tokens.size() > 1) { // NOPMD - AvoidLiteralsInIfCondition
throw new IllegalStateException("Path has leftover arguments");
}
return tokens.removeFirst();
}
}