package org.aksw.jena_sparql_api.utils;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.aksw.jena_sparql_api.backports.syntaxtransform.QueryTransformOps;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
import org.apache.jena.sparql.algebra.Op;
import org.apache.jena.sparql.algebra.op.OpSlice;
import org.apache.jena.sparql.core.DatasetDescription;
import org.apache.jena.sparql.core.Quad;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.engine.binding.Binding;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.syntax.Element;
import org.apache.jena.sparql.syntax.ElementFilter;
import org.apache.jena.sparql.syntax.PatternVars;
import org.apache.jena.sparql.util.ExprUtils;
import com.google.common.collect.DiscreteDomain;
import com.google.common.collect.Range;
public class QueryUtils {
public static Query randomizeVars(Query query) {
Map<Var, Var> varMap = createRandomVarMap(query, "rv");
Query result = QueryTransformOps.transform(query, varMap);
System.out.println(query + "now:\n" + result);
return result;
}
public static Map<Var, Var> createRandomVarMap(Query query, String base) {
Collection<Var> vars = PatternVars.vars(query.getQueryPattern());
Generator<Var> gen = VarGeneratorBlacklist.create(base, vars);
Map<Var, Var> varMap = vars.stream()
.collect(Collectors.toMap(
v -> v,
v -> gen.next()));
return varMap;
}
// public static Query applyVarMap(Query query, Map<Var, ? extends Node> varMap) {
//// Map<Var, Node> tmp = varMap.entrySet().stream()
//// .collect(Collectors.toMap(
//// e -> e.getKey(),
//// e -> (Node)e.getValue()));
//
// Query result = QueryTransformOps.transform(query, varMap);
// return result;
// }
public static void injectFilter(Query query, String exprStr) {
Expr expr = ExprUtils.parse(exprStr);
injectFilter(query, expr);
}
public static void injectFilter(Query query, Expr expr) {
injectElement(query, new ElementFilter(expr));
}
// public static void injectElement(Query query, String elementStr) {
// ElementUtils.pa
// }
public static void injectElement(Query query, Element element) {
Element queryPattern = query.getQueryPattern();
Element replacement = ElementUtils.mergeElements(queryPattern, element);
query.setQueryPattern(replacement);
}
public static Range<Long> toRange(OpSlice op) {
Range<Long> result = toRange(op.getStart(), op.getLength());
return result;
}
public static Op applyRange(Op op, Range<Long> range) {
long start = rangeToOffset(range);
long length = rangeToLimit(range);
Op result = start == Query.NOLIMIT && length == Query.NOLIMIT
? op
: new OpSlice(op, start, length);
return result;
}
/**
* Limit the query to the given range, relative to its own given range
*
* @param query
* @param offset
* @param limit
* @param cloneOnChange
* @return
*/
public static Query applySlice(Query query, Long offset, Long limit, boolean cloneOnChange) {
Range<Long> parent = toRange(query);
Range<Long> child = toRange(offset, limit);
Range<Long> subRange = subRange(parent, child);
boolean isUnchanged =
parent.lowerEndpoint().equals(subRange.lowerEndpoint()) &&
parent.hasUpperBound() == subRange.hasUpperBound() &&
(parent.hasUpperBound() ? parent.upperEndpoint().equals(subRange.upperEndpoint()) : true);
boolean hasChanged = !isUnchanged;
Query result = cloneOnChange && hasChanged ? query.cloneQuery() : query;
if(hasChanged) {
applyRange(result, subRange);
}
return result;
}
public static void applyRange(Query query, Range<Long> range) {
long offset = rangeToOffset(range);
long limit = rangeToLimit(range);
query.setOffset(offset);
query.setLimit(limit);
}
public static Range<Long> createRange(Long limit, Long offset) {
long beginIndex = offset == null ? 0 : offset;
Long endIndex = limit == null ? null : beginIndex + limit;
Range<Long> result = endIndex == null
? Range.atLeast(beginIndex)
: Range.closedOpen(beginIndex, endIndex)
;
return result;
}
//public static LimitAndOffset rangeToLimitAndOffset(Range<Long> range)
public static long rangeToOffset(Range<Long> range) {
long result = range.lowerEndpoint();
result = result == 0 ? Query.NOLIMIT : result;
return result;
}
/**
*
* @param range
* @return
*/
public static long rangeToLimit(Range<Long> range) {
range = range.canonical(DiscreteDomain.longs());
long result = range.hasUpperBound()
? DiscreteDomain.longs().distance(range.lowerEndpoint(), range.upperEndpoint())
: Query.NOLIMIT;
return result;
}
public static Range<Long> toRange(Query query) {
Range<Long> result = toRange(query.getOffset(), query.getLimit());
return result;
}
public static Range<Long> toRange(Long offset, Long limit) {
Long min = offset == null || offset.equals(Query.NOLIMIT) ? 0 : offset;
Long delta = limit == null || limit.equals(Query.NOLIMIT) ? null : limit;
Long max = delta == null ? null : min + delta;
Range<Long> result = max == null
? Range.atLeast(min)
: Range.closedOpen(min, max);
return result;
}
public static Range<Long> subRange(Range<Long> parent, Range<Long> child) {
long newMin = parent.lowerEndpoint() + child.lowerEndpoint();
Long newMax = (parent.hasUpperBound()
? child.hasUpperBound()
? Math.min(parent.upperEndpoint(), child.upperEndpoint())
: parent.upperEndpoint()
: child.hasUpperBound()
? child.upperEndpoint()
: null);
Range<Long> result = newMax == null
? Range.atLeast(newMin)
: Range.closed(newMin, newMax);
return result;
}
public static void applyDatasetDescription(Query query,
DatasetDescription dd) {
DatasetDescription present = query.getDatasetDescription();
if (present == null && dd != null) {
{
List<String> items = dd.getDefaultGraphURIs();
if (items != null) {
for (String item : items) {
query.addGraphURI(item);
}
}
}
{
List<String> items = dd.getNamedGraphURIs();
if (items != null) {
for (String item : items) {
query.addNamedGraphURI(item);
}
}
}
}
}
public static Query fixVarNames(Query query) {
Query result = query.cloneQuery();
Element element = query.getQueryPattern();
Element repl = ElementUtils.fixVarNames(element);
result.setQueryPattern(repl);
return result;
}
/**
*
*
* @param pattern
* a pattern of a where-clause
* @param resultVar
* an optional result variable (used for describe queries)
* @return
*/
public static Query elementToQuery(Element pattern, String resultVar) {
if (pattern == null)
return null;
Query query = new Query();
query.setQueryPattern(pattern);
query.setQuerySelectType();
if (resultVar == null) {
query.setQueryResultStar(true);
}
query.setResultVars();
if (resultVar != null) {
query.getResultVars().add(resultVar);
}
return query;
}
public static Query elementToQuery(Element pattern) {
return elementToQuery(pattern, null);
}
/**
* This method does basically the same as
* org.apache.jena.sparql.engine.QueryExecutionBase.execConstruct and
* SparqlerBaseSelect note sure if it is redundant
*
* @param quads
* @param binding
* @return
*/
public static Set<Quad> instanciate(Iterable<Quad> quads, Binding binding) {
Set<Quad> result = new HashSet<Quad>();
Node nodes[] = new Node[4];
for (Quad quad : quads) {
for (int i = 0; i < 4; ++i) {
Node node = QuadUtils.getNode(quad, i);
// If the node is a variable, then substitute it's value
if (node.isVariable()) {
node = binding.get((Var) node);
}
// If the node is null, or any non-object position
// gets assigned a literal then we cannot instanciate
if (node == null || (i < 3 && node.isLiteral())) {
result.clear();
return result;
}
nodes[i] = node;
}
Quad inst = QuadUtils.create(nodes);
result.add(inst);
}
return result;
}
}