blob: 2a59315e94685643fc05bcbcf214772b7b58ecb1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.rya.indexing.pcj.fluo.app.util;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.rdf4j.query.algebra.Filter;
import org.eclipse.rdf4j.query.algebra.SingletonSet;
import org.eclipse.rdf4j.query.algebra.evaluation.function.Function;
import org.eclipse.rdf4j.query.algebra.evaluation.function.FunctionRegistry;
import org.eclipse.rdf4j.query.algebra.helpers.AbstractQueryModelVisitor;
import org.eclipse.rdf4j.query.parser.ParsedQuery;
import org.eclipse.rdf4j.query.parser.ParsedTupleQuery;
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
import org.eclipse.rdf4j.queryrender.sparql.SPARQLQueryRenderer;
/**
* Class for creating a String representation a given Filter, and for
* converting the String representation of the Filter back to the Filter.
*
*/
public class FilterSerializer {
private static final SPARQLQueryRenderer renderer = new SPARQLQueryRenderer();
private static final SPARQLParser parser = new SPARQLParser();
/**
* Converts a {@link Filter} to a SPARQL query containing only the SPARQL representation
* of the Filter along with a Select clause that return all variables. The argument of the
* Filter is replaced by a {@link SingletonSet} so that the body of the SPARQL query consists of only a
* single Filter clause.
* @param filter - Filter to be serialized
* @return - SPARQL String containing a single Filter clause that represents the serialized Filter
* @throws FilterParseException
*/
public static String serialize(Filter filter) throws FilterParseException {
Filter clone = filter.clone();
clone.setArg(new SingletonSet());
try {
return removeAngularBracketsFromNonUriFunctions(renderer.render(new ParsedTupleQuery(clone)));
} catch (Exception e) {
throw new FilterParseException("Unable to parse Filter.", e);
}
}
/**
* Converts a SPARQL query consisting of a single Filter clause back to a Filter.
* @param sparql - SPARQL query representing a Filter
* @return - parsed Filter included in the SPARQL query
* @throws FilterParseException
*/
public static Filter deserialize(String sparql) throws FilterParseException {
try {
ParsedQuery pq = parser.parseQuery(sparql, null);
FilterVisitor visitor = new FilterVisitor();
pq.getTupleExpr().visit(visitor);
Set<Filter> filters = visitor.getFilters();
if(filters.size() != 1) {
throw new FilterParseException("Filter String must contain only one Filter.");
}
return filters.iterator().next();
} catch (Exception e) {
throw new FilterParseException("Unable to parse Filter.", e);
}
}
public static class FilterVisitor extends AbstractQueryModelVisitor<RuntimeException> {
private Set<Filter> filters;
public FilterVisitor() {
filters = new HashSet<>();
}
public Set<Filter> getFilters() {
return filters;
}
public void meet(Filter node) {
filters.add(node);
}
}
/**
* There are a number of Functions in the FunctionRegistry whose getURI() method does not return a valid URI (NOW()
* is one such method). The SPARQLQueryRender adds angular brackets to the result returned by
* {@link Function#getURI()} by default, which leads to a MalformedQueryException when the SPARQLParser attempts to
* parse the SPARQL created by the renderer. Therefore, a call to serialize and then deserialize for a Filter
* containing a Function that returns an invalid URI will generate an exception. This method removes the angular
* brackets from the result returned by {@link Function#getURI()} if it is not a valid URI so that the parser will
* parse it.
*
* @param query - query generated by query renderer
* @return - String with angular brackets removed from around all invalid Function URI
*/
private static String removeAngularBracketsFromNonUriFunctions(String query) {
FunctionRegistry registry = FunctionRegistry.getInstance();
for(String key: registry.getKeys()) {
if (key.indexOf(':') < 0) {
query = query.replace("<"+key.trim()+">", key);
}
}
return query;
}
public static class FilterParseException extends Exception {
private static final long serialVersionUID = 1L;
/**
* Constructs an instance of {@link FilterParseException}.
*
* @param message - Explains why this exception is being thrown.
*/
public FilterParseException(final String message) {
super(message);
}
/**
* Constructs an instance of {@link FilterParseException}.
*
* @param message - Explains why this exception is being thrown.
* @param cause - The exception that caused this one to be thrown.
*/
public FilterParseException(final String message, final Throwable t) {
super(message, t);
}
}
}