![JAR search and dependency download from the Maven repository](/logo.png)
org.eclipse.rdf4j.query.algebra.Service Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others.
*
* 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;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
/**
* The SERVICE keyword as defined in SERVICE
* definition. The service expression is evaluated at the specified service URI. If the service reference is a
* variable, a value for this variable must be available at evaluation time (e.g. from earlier computations).
*
* @author Andreas Schwarte
*/
public class Service extends UnaryTupleOperator {
/*-----------*
* Variables *
*-----------*/
private Var serviceRef;
/* a string representation of the inner expression (e.g. extracted during parsing) */
private String serviceExpressionString;
private final Set serviceVars;
/* the prefix declarations, potentially null */
private Map prefixDeclarations;
private String baseURI;
/*
* prepared queries, including prefix. Contains %PROJECTION_VARS% to be replaced at evaluation time. see
*/
private String preparedSelectQueryString;
private String preparedAskQueryString;
private final boolean silent;
/*--------------*
* Constructors *
*--------------*/
public Service(Var serviceRef, TupleExpr serviceExpr, String serviceExpressionString,
Map prefixDeclarations, String baseURI, boolean silent) {
super(serviceExpr);
setServiceRef(serviceRef);
setExpressionString(serviceExpressionString);
this.serviceVars = computeServiceVars(serviceExpr);
setPrefixDeclarations(prefixDeclarations);
setBaseURI(baseURI);
initPreparedQueryString();
this.silent = silent;
}
/*---------*
* Methods *
*---------*/
public Var getServiceRef() {
return this.serviceRef;
}
public TupleExpr getServiceExpr() {
return this.arg;
}
public void setServiceRef(Var serviceRef) {
this.serviceRef = serviceRef;
this.serviceRef.setParentNode(this);
}
/**
* @return Returns the silent.
*/
public boolean isSilent() {
return silent;
}
/**
* @return Returns the prefixDeclarations.
*/
public Map getPrefixDeclarations() {
return prefixDeclarations;
}
/**
* @param prefixDeclarations The prefixDeclarations to set.
*/
public void setPrefixDeclarations(Map prefixDeclarations) {
this.prefixDeclarations = prefixDeclarations;
}
/**
* The SERVICE expression, either complete or just the expression e.g. "SERVICE { ... }" becomes " ... "
*
* @param serviceExpressionString the inner expression as SPARQL String representation
*/
public void setExpressionString(String serviceExpressionString) {
this.serviceExpressionString = parseServiceExpression(serviceExpressionString);
}
/**
* @return Returns the serviceExpressionString.
*/
public String getServiceExpressionString() {
return serviceExpressionString;
}
/**
* Returns an ASK query string using no projection vars.
*
* @return an ASK query string
*/
public String getAskQueryString() {
return preparedAskQueryString;
}
/**
* Returns a SELECT query string using the provided projection vars. The variables are inserted into the
* preparedSelectQueryString in the SELECT clause.
*
* @param projectionVars
* @return SELECT query string, utilizing the given projection variables
*/
public String getSelectQueryString(Set projectionVars) {
if (projectionVars.isEmpty()) {
return preparedSelectQueryString.replace("%PROJECTION_VARS%", "*");
}
StringBuilder sb = new StringBuilder();
for (String var : projectionVars) {
sb.append(" ?").append(var);
}
return preparedSelectQueryString.replace("%PROJECTION_VARS%", sb.toString());
}
/**
* @return Returns the serviceVars.
*/
public Set getServiceVars() {
return serviceVars;
}
@Override
public void visit(QueryModelVisitor visitor) throws X {
visitor.meet(this);
}
@Override
public void visitChildren(QueryModelVisitor visitor) throws X {
serviceRef.visit(visitor);
super.visitChildren(visitor);
}
@Override
public void replaceChildNode(QueryModelNode current, QueryModelNode replacement) {
if (serviceRef == current) {
setServiceRef((Var) replacement);
} else {
super.replaceChildNode(current, replacement);
}
}
@Override
public boolean equals(Object other) {
if (other instanceof Service && super.equals(other)) {
Service o = (Service) other;
return serviceRef.equals(o.getServiceRef());
}
return false;
}
@Override
public int hashCode() {
return super.hashCode() ^ serviceRef.hashCode();
}
@Override
public Service clone() {
Service clone = (Service) super.clone();
clone.setServiceRef(serviceRef.clone());
return clone;
}
/**
* Compute the variable names occurring in the service expression using tree traversal, since these are necessary
* for building the SPARQL query.
*
* @return the set of variable names in the given service expression
*/
private Set computeServiceVars(TupleExpr serviceExpression) {
final Set res = new HashSet<>();
serviceExpression.visit(new AbstractQueryModelVisitor() {
@Override
public void meet(Var node) throws RuntimeException {
// take only real vars, i.e. ignore blank nodes
if (!node.hasValue() && !node.isAnonymous()) {
res.add(node.getName());
}
}
@Override
public void meet(BindingSetAssignment bsa) {
res.addAll(bsa.getAssuredBindingNames());
}
@Override
public void meet(Extension e) {
super.meet(e);
for (ExtensionElem elem : e.getElements()) {
res.add(elem.getName());
}
}
// TODO maybe stop tree traversal in nested SERVICE?
});
return res;
}
private static final Pattern subselectPattern = Pattern.compile("SELECT.*",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
private void initPreparedQueryString() {
serviceExpressionString = serviceExpressionString.trim();
String prefixString = computePrefixString(prefixDeclarations);
// build the raw SELECT query string
StringBuilder sb = new StringBuilder();
sb.append(prefixString);
if (subselectPattern.matcher(serviceExpressionString).matches()) {
sb.append(serviceExpressionString);
} else {
sb.append("SELECT %PROJECTION_VARS% WHERE { ");
sb.append(serviceExpressionString);
sb.append("\n}");
}
preparedSelectQueryString = sb.toString();
// build the raw ASK query string
sb = new StringBuilder();
sb.append(prefixString);
sb.append("ASK {");
sb.append(serviceExpressionString);
sb.append(" }");
preparedAskQueryString = sb.toString();
}
/**
* Compute the prefix string only once to avoid computation overhead during evaluation.
*
* @param prefixDeclarations
* @return a Prefix String or an empty string if there are no prefixes
*/
private String computePrefixString(Map prefixDeclarations) {
if (prefixDeclarations == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (String prefix : prefixDeclarations.keySet()) {
String uri = prefixDeclarations.get(prefix);
sb.append("PREFIX ").append(prefix).append(":").append(" <").append(uri).append("> ");
}
return sb.toString();
}
/**
* Parses a service expression to just have the inner expression, e.g. from something like "SERVICE <url> {
* ... }" becomes " ... ", also applies {@link String#trim()} to remove leading/tailing space
*
* @param serviceExpression
* @return the inner expression of the given service expression
*/
private String parseServiceExpression(String serviceExpression) {
if (serviceExpression.toLowerCase().startsWith("service")) {
return serviceExpression.substring(serviceExpression.indexOf('{') + 1, serviceExpression.lastIndexOf('}'))
.trim();
}
return serviceExpression;
}
/**
* @param baseURI The baseURI to set.
*/
public void setBaseURI(String baseURI) {
this.baseURI = baseURI;
}
/**
* @return Returns the baseURI.
*/
public String getBaseURI() {
return baseURI;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy