All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
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.
org.topbraid.shacl.validation.sparql.SPARQLSubstitutions Maven / Gradle / Ivy
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*/
package org.topbraid.shacl.validation.sparql;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.ResourceFactory;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.shared.impl.PrefixMappingImpl;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDFS;
import org.topbraid.jenax.util.ARQFactory;
import org.topbraid.jenax.util.JenaUtil;
import org.topbraid.jenax.util.RDFLabels;
import org.topbraid.shacl.validation.SHACLException;
import org.topbraid.shacl.vocabulary.SH;
import org.topbraid.shacl.vocabulary.TOSH;
/**
* Collects various helper algorithms currently used by the SPARQL execution language.
*
* @author Holger Knublauch
*/
public class SPARQLSubstitutions {
// Flag to bypass sh:prefixes and instead use all prefixes in the Jena object of the shapes graph.
public static boolean useGraphPrefixes = false;
// Currently switched to old setInitialBinding solution
private static boolean USE_TRANSFORM = false;
public static void addMessageVarNames(String labelTemplate, Set results) {
for(int i = 0; i < labelTemplate.length(); i++) {
if(i < labelTemplate.length() - 3 && labelTemplate.charAt(i) == '{' && labelTemplate.charAt(i + 1) == '?') {
int varEnd = i + 2;
while(varEnd < labelTemplate.length()) {
if(labelTemplate.charAt(varEnd) == '}') {
String varName = labelTemplate.substring(i + 2, varEnd);
results.add(varName);
break;
}
else {
varEnd++;
}
}
i = varEnd;
}
}
}
public static QueryExecution createQueryExecution(Query query, Dataset dataset, QuerySolution bindings) {
if(USE_TRANSFORM && bindings != null) {
Map substitutions = new HashMap();
Iterator varNames = bindings.varNames();
while(varNames.hasNext()) {
String varName = varNames.next();
substitutions.put(Var.alloc(varName), bindings.get(varName).asNode());
}
Query newQuery = JenaUtil.queryWithSubstitutions(query, substitutions);
return ARQFactory.get().createQueryExecution(newQuery, dataset);
}
else {
return ARQFactory.get().createQueryExecution(query, dataset, bindings);
}
}
public static Query substitutePaths(Query query, String pathString, Model model) {
// TODO: This is a bad algorithm - should be operating on syntax tree, not string
String str = query.toString().replaceAll(" \\?" + SH.PATHVar.getVarName() + " ", pathString);
return ARQFactory.get().createQuery(model, str);
}
public static Literal withSubstitutions(Literal template, QuerySolution bindings, Function labelFunction) {
StringBuffer buffer = new StringBuffer();
String labelTemplate = template.getLexicalForm();
for(int i = 0; i < labelTemplate.length(); i++) {
if(i < labelTemplate.length() - 3 && labelTemplate.charAt(i) == '{' && (labelTemplate.charAt(i + 1) == '?' || labelTemplate.charAt(i + 1) == '$')) {
int varEnd = i + 2;
while(varEnd < labelTemplate.length()) {
if(labelTemplate.charAt(varEnd) == '}') {
String varName = labelTemplate.substring(i + 2, varEnd);
RDFNode varValue = bindings.get(varName);
if(varValue != null) {
if(labelFunction != null) {
buffer.append(labelFunction.apply(varValue));
}
else if(varValue instanceof Resource) {
buffer.append(RDFLabels.get().getLabel((Resource)varValue));
}
else if(varValue instanceof Literal) {
buffer.append(varValue.asNode().getLiteralLexicalForm());
}
}
break;
}
else {
varEnd++;
}
}
i = varEnd;
}
else {
buffer.append(labelTemplate.charAt(i));
}
}
if(template.getLanguage().isEmpty()) {
return ResourceFactory.createTypedLiteral(buffer.toString());
}
else {
return ResourceFactory.createLangLiteral(buffer.toString(), template.getLanguage());
}
}
static void appendTargets(StringBuffer sb, Resource shape, Dataset dataset) {
List targets = new LinkedList();
if(shape.getModel().contains(shape, SH.targetNode, (RDFNode)null)) {
targets.add(" GRAPH " + SH.JS_SHAPES_VAR + " { $" + SH.currentShapeVar.getName() + " <" + SH.targetNode + "> ?this } .\n");
}
if(JenaUtil.hasIndirectType(shape, RDFS.Class)) {
String varName = "?CLASS_VAR";
targets.add(" " + varName + " <" + RDFS.subClassOf + ">* $" + SH.currentShapeVar.getName() + " .\n ?this a " + varName + " .\n");
}
for(Resource cls : JenaUtil.getResourceProperties(shape, SH.targetClass)) {
String varName = "?SHAPE_CLASS_VAR";
targets.add(" " + varName + " <" + RDFS.subClassOf + ">* <" + cls + "> .\n ?this a " + varName + " .\n");
}
int index = 0;
for(Resource property : JenaUtil.getResourceProperties(shape, SH.targetSubjectsOf)) {
targets.add(" ?this <" + property + "> ?ANY_VALUE_" + index++ + " .\n");
}
for(Resource property : JenaUtil.getResourceProperties(shape, SH.targetObjectsOf)) {
targets.add(" ?ANY_VALUE_" + index++ + " <" + property + "> ?this .\n");
}
if(shape.hasProperty(SH.target)) {
targets.add(createTargets(shape));
}
if(targets.isEmpty()) {
throw new SHACLException("Shape without target " + shape);
}
else if(targets.size() == 1) {
sb.append(targets.get(0));
}
else {
for(int i = 0; i < targets.size(); i++) {
sb.append(" {");
sb.append(targets.get(i));
sb.append(" }");
if(i < targets.size() - 1) {
sb.append(" UNION\n");
}
}
}
}
private static String createTargets(Resource shape) {
String targetVar = "?trgt_" + (int)(Math.random() * 10000);
return " GRAPH $" + SH.shapesGraphVar.getName() + " { $" + SH.currentShapeVar.getName() + " <" + SH.target + "> " + targetVar + "} .\n" +
" (" + targetVar + " $" + SH.shapesGraphVar.getName() + ") <" + TOSH.targetContains + "> ?this .\n";
}
/**
* Gets a parsable SPARQL string based on a fragment and prefix declarations.
* Depending on the setting of the flag useGraphPrefixes, this either uses the
* prefixes from the Jena graph of the given executable, or strictly uses sh:prefixes.
* @param str the query fragment (e.g. starting with SELECT)
* @param executable the sh:SPARQLExecutable potentially holding the sh:prefixes
* @return the parsable SPARQL string
*/
public static String withPrefixes(String str, Resource executable) {
if(useGraphPrefixes) {
return ARQFactory.get().createPrefixDeclarations(executable.getModel()) + str;
}
else {
StringBuffer sb = new StringBuffer();
PrefixMapping pm = new PrefixMappingImpl();
Set reached = new HashSet();
for(Resource ontology : JenaUtil.getResourceProperties(executable, SH.prefixes)) {
String duplicate = collectPrefixes(ontology, pm, reached);
if(duplicate != null) {
throw new SHACLException("Duplicate prefix declaration for prefix " + duplicate);
}
}
for(String prefix : pm.getNsPrefixMap().keySet()) {
sb.append("PREFIX ");
sb.append(prefix);
sb.append(": <");
sb.append(pm.getNsPrefixURI(prefix));
sb.append(">\n");
}
sb.append(str);
return sb.toString();
}
}
// Returns the duplicate prefix, if any
private static String collectPrefixes(Resource ontology, PrefixMapping pm, Set reached) {
reached.add(ontology);
for(Resource decl : JenaUtil.getResourceProperties(ontology, SH.declare)) {
String prefix = JenaUtil.getStringProperty(decl, SH.prefix);
String ns = JenaUtil.getStringProperty(decl, SH.namespace);
if(prefix != null && ns != null) {
String oldNS = pm.getNsPrefixURI(prefix);
if(oldNS != null && !oldNS.equals(ns)) {
return prefix;
}
pm.setNsPrefix(prefix, ns);
}
}
for(Resource imp : JenaUtil.getResourceProperties(ontology, OWL.imports)) {
if(!reached.contains(imp)) {
String duplicate = collectPrefixes(imp, pm, reached);
if(duplicate != null) {
return duplicate;
}
}
}
return null;
}
}