/*
* 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.jena.sparql.syntax.syntaxtransform;
import java.util.List;
import java.util.Map;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryVisitor;
import org.apache.jena.query.SortCondition;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.shared.impl.PrefixMappingImpl;
import org.apache.jena.sparql.ARQException;
import org.apache.jena.sparql.core.DatasetDescription;
import org.apache.jena.sparql.core.Prologue;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.core.VarExprList;
import org.apache.jena.sparql.expr.Expr;
import org.apache.jena.sparql.expr.ExprTransform;
import org.apache.jena.sparql.expr.ExprTransformer;
import org.apache.jena.sparql.expr.ExprVar;
import org.apache.jena.sparql.graph.NodeTransform;
import org.apache.jena.sparql.syntax.Element;
import org.apache.jena.sparql.syntax.ElementGroup;
/** Support for transformation of query abstract syntax. */
public class QueryTransformOps {
public static Query transform(Query query, Map<Var, ? extends Node> substitutions) {
ElementTransform eltrans = new ElementTransformSubst(substitutions);
NodeTransform nodeTransform = new NodeTransformSubst(substitutions);
ExprTransform exprTrans = new ExprTransformNodeElement(nodeTransform, eltrans);
return transform(query, eltrans, exprTrans);
}
public static Query transform(Query query, ElementTransform transform, ExprTransform exprTransform) {
Query q2 = QueryTransformOps.shallowCopy(query);
// "Shallow copy with transform."
transformVarExprList(q2.getProject(), exprTransform);
transformVarExprList(q2.getGroupBy(), exprTransform);
transformExprList(q2.getHavingExprs(), exprTransform);
// ?? DOES NOT WORK: transformExprListAgg(q2.getAggregators(),
// exprTransform) ;
// ??
if (q2.getOrderBy() != null) {
transformSortConditions(q2.getOrderBy(), exprTransform);
}
// if ( q2.hasHaving() ) {}
// if ( q2.hasAggregators() ) {}
Element el = q2.getQueryPattern();
Element el2 = ElementTransformer.transform(el, transform, exprTransform);
// Top level is always a group.
if (!(el2 instanceof ElementGroup)) {
ElementGroup eg = new ElementGroup();
eg.addElement(el2);
el2 = eg;
}
q2.setQueryPattern(el2);
return q2;
}
public static Query transform(Query query, ElementTransform transform) {
ExprTransform noop = new ExprTransformApplyElementTransform(transform);
return transform(query, transform, noop);
}
// ** Mutates the List
private static void transformExprList(List<Expr> exprList, ExprTransform exprTransform) {
for (int i = 0; i < exprList.size(); i++) {
Expr e1 = exprList.get(0);
Expr e2 = ExprTransformer.transform(exprTransform, e1);
if (e2 == null || e2 == e1)
continue;
exprList.set(i, e2);
}
}
private static void transformSortConditions(List<SortCondition> conditions, ExprTransform exprTransform) {
for (int i = 0; i < conditions.size(); i++) {
SortCondition s1 = conditions.get(i);
Expr e = ExprTransformer.transform(exprTransform, s1.expression);
if (e == null || s1.expression.equals(e))
continue;
conditions.set(i, new SortCondition(e, s1.direction));
}
}
// ** Mutates the VarExprList
private static void transformVarExprList(VarExprList varExprList, ExprTransform exprTransform) {
Map<Var, Expr> map = varExprList.getExprs();
for (Var v : varExprList.getVars()) {
Expr e = varExprList.getExpr(v);
ExprVar ev = new ExprVar(v);
Expr ev2 = exprTransform.transform(ev);
if (ev != ev2) {
if (e != null)
throw new ARQException("Can't substitute " + v + " because it's used as an AS variable");
if (ev2.isConstant() || ev2.isVariable()) {
// Convert to (substitute value AS ?var)
map.put(v, ev2);
continue;
} else
throw new ARQException("Can't substitute " + v + " because it's not a simple value: " + ev2);
}
if (e == null)
continue;
// Didn't change the variable.
Expr e2 = ExprTransformer.transform(exprTransform, e);
if (e != e2)
// replace
map.put(v, e2);
}
}
static class QueryShallowCopy implements QueryVisitor {
final Query newQuery = new Query();
QueryShallowCopy() {
}
@Override
public void startVisit(Query query) {
newQuery.setSyntax(query.getSyntax());
if (query.explicitlySetBaseURI())
newQuery.setBaseURI(query.getPrologue().getResolver());
newQuery.setQueryResultStar(query.isQueryResultStar());
if (query.hasDatasetDescription()) {
DatasetDescription desc = query.getDatasetDescription();
for (String x : desc.getDefaultGraphURIs())
newQuery.addGraphURI(x);
for (String x : desc.getDefaultGraphURIs())
newQuery.addNamedGraphURI(x);
}
// Aggregators.
newQuery.getAggregators().addAll(query.getAggregators());
}
@Override
public void visitPrologue(Prologue prologue) {
// newQuery.setBaseURI(prologue.getResolver()) ;
PrefixMapping pmap = new PrefixMappingImpl().setNsPrefixes(prologue.getPrefixMapping());
newQuery.setPrefixMapping(pmap);
}
@Override
public void visitResultForm(Query q) {
}
@Override
public void visitSelectResultForm(Query query) {
newQuery.setQuerySelectType();
newQuery.setDistinct(query.isDistinct());
VarExprList x = query.getProject();
for (Var v : x.getVars()) {
Expr expr = x.getExpr(v);
if (expr == null)
newQuery.addResultVar(v);
else
newQuery.addResultVar(v, expr);
}
}
@Override
public void visitConstructResultForm(Query query) {
newQuery.setQueryConstructType();
newQuery.setConstructTemplate(query.getConstructTemplate());
}
@Override
public void visitDescribeResultForm(Query query) {
newQuery.setQueryDescribeType();
for (Node x : query.getResultURIs())
newQuery.addDescribeNode(x);
}
@Override
public void visitAskResultForm(Query query) {
newQuery.setQueryAskType();
}
@Override
public void visitDatasetDecl(Query query) {
}
@Override
public void visitQueryPattern(Query query) {
newQuery.setQueryPattern(query.getQueryPattern());
}
@Override
public void visitGroupBy(Query query) {
if (query.hasGroupBy()) {
VarExprList x = query.getGroupBy();
for (Var v : x.getVars()) {
Expr expr = x.getExpr(v);
if (expr == null)
newQuery.addGroupBy(v);
else
newQuery.addGroupBy(v, expr);
}
}
}
@Override
public void visitHaving(Query query) {
if (query.hasHaving()) {
for (Expr expr : query.getHavingExprs())
newQuery.addHavingCondition(expr);
}
}
@Override
public void visitOrderBy(Query query) {
if (query.hasOrderBy()) {
for (SortCondition sc : query.getOrderBy())
newQuery.addOrderBy(sc);
}
}
@Override
public void visitLimit(Query query) {
newQuery.setLimit(query.getLimit());
}
@Override
public void visitOffset(Query query) {
newQuery.setOffset(query.getOffset());
}
@Override
public void visitValues(Query query) {
if (query.hasValues())
newQuery.setValuesDataBlock(query.getValuesVariables(), query.getValuesData());
}
@Override
public void finishVisit(Query query) {
}
}
public static Query shallowCopy(Query query) {
QueryShallowCopy copy = new QueryShallowCopy();
query.visit(copy);
Query q2 = copy.newQuery;
return q2;
}
}