
org.topbraid.jenax.util.ARQFactory Maven / Gradle / Ivy
The newest version!
/*
* 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.jenax.util;
import java.net.http.HttpClient;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.jena.graph.Node;
import org.apache.jena.http.HttpEnv;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.Syntax;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.sparql.core.DatasetImpl;
import org.apache.jena.sparql.exec.http.QueryExecutionHTTP;
import org.apache.jena.sparql.syntax.ElementNamedGraph;
import org.apache.jena.sparql.syntax.ElementVisitorBase;
import org.apache.jena.sparql.syntax.ElementWalker;
import org.apache.jena.update.UpdateRequest;
/**
* A singleton that can create ARQ SPARQL Queries and QueryExecution objects.
*
* SHACL and SPIN API users should use the provided methods here.
*
* @author Holger Knublauch
*/
public class ARQFactory {
private static ARQFactory singleton = new ARQFactory();
/**
* Caches Jena query objects for SPARQL command or expression Strings.
*/
private Map string2Query = new ConcurrentHashMap<>();
/**
* Caches Jena query objects for SPARQL command or expression Strings.
*/
private Map string2Update = new ConcurrentHashMap();
private boolean useCaches = true;
/**
* Gets the singleton instance of this class.
* @return the singleton
*/
public static ARQFactory get() {
return singleton;
}
/**
* Convenience method to get a named graph from the current ARQFactory's Dataset.
* @param graphURI the URI of the graph to get
* @return the named graph or null
*/
public static Model getNamedModel(String graphURI) {
return ARQFactory.get().getDataset(null).getNamedModel(graphURI);
}
/**
* Changes the singleton to some subclass.
* @param value the new ARQFactory (not null)
*/
public static void set(ARQFactory value) {
ARQFactory.singleton = value;
}
/**
* Can be overloaded to install extra things such as Lucene indices to all
* local QueryExecutions generated by this factory.
* Does nothing by default.
* @param qexec the QueryExecution to modify
*/
protected void adjustQueryExecution(QueryExecution qexec) {
}
/**
* Programmatically resets any cached queries.
*/
public void clearCaches() {
string2Query.clear();
string2Update.clear();
}
public Query createExpressionQuery(String expression) {
Query result = string2Query.get(expression);
if(result == null) {
String queryString = "SELECT (" + expression + ") WHERE {}";
result = doCreateQuery(queryString);
if(useCaches) {
string2Query.put(expression, result);
}
}
return result;
}
/**
* Same as createPrefixDeclarations(model, true)
.
* @param model the Model to create prefix declarations for
* @return the prefix declarations
*/
public String createPrefixDeclarations(Model model) {
return createPrefixDeclarations(model, true);
}
/**
* Creates SPARQL prefix declarations for a given Model.
* @param model the Model to get the prefixes from
* @param includeExtraPrefixes true to also include implicit prefixes like afn
* @return the prefix declarations
*/
public String createPrefixDeclarations(Model model, boolean includeExtraPrefixes) {
StringBuffer queryString = new StringBuffer();
String defaultNamespace = JenaUtil.getNsPrefixURI(model, "");
if(defaultNamespace != null) {
queryString.append("PREFIX : <" + defaultNamespace + ">\n");
}
Map map = model.getNsPrefixMap();
if(includeExtraPrefixes) {
Map extraPrefixes = ExtraPrefixes.getExtraPrefixes();
for(String prefix : extraPrefixes.keySet()) {
String ns = extraPrefixes.get(prefix);
perhapsAppend(queryString, prefix, ns, map);
}
}
map.forEach((prefix,namespace) -> {
if(!prefix.isEmpty() && namespace != null) {
queryString.append("PREFIX " + prefix + ": <" + namespace + ">\n");
}
});
return queryString.toString();
}
public Query createQuery(String queryString) {
Query result = string2Query.get(queryString);
if(result == null) {
result = doCreateQuery(queryString);
if(useCaches) {
string2Query.put(queryString, result);
}
}
return result;
}
public Query doCreateQuery(String queryString) {
return doCreateQuery(queryString, null);
}
/**
* Creates the "physical" Jena Query instance.
* Can be overloaded to create engine-specific Query objects such as those
* for AllegroGraph.
* @param queryString the parsable query string
* @param prefixMapping an optional PrefixMapping to initialize the Query with
* (this object may be modified)
* @return the ARQ Query object
*/
protected Query doCreateQuery(String queryString, PrefixMapping prefixMapping) {
Query query = new Query();
if(prefixMapping != null) {
query.setPrefixMapping(prefixMapping);
}
return QueryFactory.parse(query, queryString, null, getSyntax());
}
/**
* Creates a new Query from a partial query (possibly lacking PREFIX declarations),
* using the ARQ syntax specified by getSyntax
.
* This will also use the ExtraPrefixes, e.g. for function definitions.
* @param model the Model to use the prefixes from
* @param partialQuery the (partial) query string
* @return the Query
*/
public Query createQuery(Model model, String partialQuery) {
PrefixMapping pm = ExtraPrefixes.createPrefixMappingWithExtraPrefixes(model);
return doCreateQuery(partialQuery, pm);
}
/**
* Creates a new Query from a partial query (possibly lacking PREFIX declarations),
* using the ARQ syntax specified by getSyntax
.
* @param partialQuery the (partial) query string
* @param prefixMapping the PrefixMapping to use
* @return the Query
*/
public Query createQueryWithPrefixMapping(String partialQuery, PrefixMapping prefixMapping) {
return doCreateQuery(partialQuery, prefixMapping);
}
/**
* Creates a QueryExecution for a given Query in a given Model,
* with no initial bindings.
* The implementation basically uses Jena's QueryExecutionFactory
* but with the option to use different Dataset as specified by
* getDataset(model)
.
* @param query the Query
* @param model the Model to query
* @return a QueryExecution
*/
public QueryExecution createQueryExecution(Query query, Model model) {
return createQueryExecution(query, model, null);
}
/**
* Creates a QueryExecution for a given Query in a given Model, with
* some given initial bindings.
* The implementation basically uses Jena's QueryExecutionFactory
* but with the option to use different Dataset as specified by
* getDataset(model)
.
* @param query the Query
* @param model the Model to query
* @param initialBinding the initial variable bindings or null
* @return a QueryExecution
*/
public QueryExecution createQueryExecution(Query query, Model model, QuerySolution initialBinding) {
Dataset dataset = getDataset(model);
if(dataset == null) {
dataset = DatasetFactory.create(model);
}
return createQueryExecution(query, dataset, initialBinding);
}
public QueryExecution createQueryExecution(Query query, Dataset dataset) {
return createQueryExecution(query, dataset, null);
}
/** Fine-grained control for development : switch on and off query printing */
public static boolean LOG_QUERIES = false;
public QueryExecution createQueryExecution(Query query, Dataset dataset, QuerySolution initialBinding) {
if(!query.getGraphURIs().isEmpty() || !query.getNamedGraphURIs().isEmpty()) {
dataset = new FromDataset(dataset, query);
}
if ( LOG_QUERIES ) {
// And the data - can be long.
// System.err.println("~~ ~~");
// RDFDataMgr.write(System.err, dataset.getDefaultModel(), Lang.TTL);
System.err.println("~~ ~~");
System.err.println(initialBinding);
System.err.println(query);
}
QueryExecution qexec = QueryExecutionFactoryFilter.get().create(query, dataset, initialBinding);
adjustQueryExecution(qexec);
return qexec;
}
/**
* Creates a remote QueryExecution on a given Query.
* @param query the Query to execute
* @return a remote QueryExecution
*/
public QueryExecutionHTTP createRemoteQueryExecution(Query query) {
List graphURIs = query.getGraphURIs();
return createRemoteQueryExecution(query, graphURIs);
}
public QueryExecutionHTTP createRemoteQueryExecution(Query query, List graphURIs) {
String service = graphURIs.get(0);
String serviceAsURI = service;
if(service.endsWith("/sparql")) {
serviceAsURI = service.substring(0, service.lastIndexOf('/'));
}
return createRemoteQueryExecution(service, query, Collections.singletonList(serviceAsURI), graphURIs, null, null);
}
public QueryExecutionHTTP createRemoteQueryExecution(
String service,
Query query,
List defaultGraphURIs,
List namedGraphURIs,
String user,
String password) {
HttpClient httpClient = buildHttpClient(user, password);
return QueryExecutionFactoryFilter.get().sparqlService(service, query, httpClient, defaultGraphURIs, namedGraphURIs);
}
public static HttpClient buildHttpClient(String user, String password) {
return (user == null) ?
HttpEnv.getDftHttpClient() :
HttpEnv.httpClientBuilder()
.authenticator(BasicAuthenticator.with(user, password))
.build();
}
public UpdateRequest createUpdateRequest(String parsableString) {
UpdateRequest result = string2Update.get(parsableString);
if(result == null) {
result = UpdateFactoryFilter.get().create(parsableString);
if(useCaches) {
string2Update.put(parsableString, result);
}
}
return result;
}
/**
* Specifies a Dataset that shall be used for query execution.
* Returns a new DatasetImpl by default but may be overloaded in subclasses.
* For example, TopBraid delegates this to the currently open Graphs.
* @param defaultModel the default Model of the Dataset
* @return the Dataset
*/
public Dataset getDataset(Model defaultModel) {
if(defaultModel != null) {
return new DatasetImpl(defaultModel);
}
else {
return new DatasetImpl(JenaUtil.createMemoryModel());
}
}
/**
* Gets a list of named graphs (GRAPH elements) mentioned in a given
* Query.
* @param query the Query to traverse
* @return a List of those GRAPHs
*/
public static List getNamedGraphURIs(Query query) {
final List results = new LinkedList();
ElementWalker.walk(query.getQueryPattern(), new ElementVisitorBase() {
@Override
public void visit(ElementNamedGraph el) {
Node node = el.getGraphNameNode();
if(node != null && node.isURI()) {
String uri = node.getURI();
if(!results.contains(uri)) {
results.add(uri);
}
}
}
});
return results;
}
/**
* The ARQ Syntax used by default: Syntax.syntaxARQ.
* @return the default syntax
*/
public Syntax getSyntax() {
return Syntax.syntaxARQ;
}
public boolean isUsingCaches() {
return useCaches;
}
private static void perhapsAppend(StringBuffer queryString, String prefix, String namespace, Map map) {
if(!map.containsKey(prefix) && namespace != null) {
queryString.append("PREFIX ");
queryString.append(prefix);
queryString.append(": <");
queryString.append(namespace);
queryString.append(">\n");
}
}
/**
* Tells the ARQFactory whether to use caches for the various createXY functions.
* These are on by default.
* Warning: there may be memory leaks if the first executed query of its kind keeps a reference to a
* E_Function which keeps a reference to a Function, and FunctionBase to a FunctionEnv with an active graph.
* @param value false to switch caches off
*/
public void setUseCaches(boolean value) {
this.useCaches = value;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy