blob: b8e692fd0af0e0dff9a50cbeb2835ee43295ac69 [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.jena.sparql.algebra.walker;
import java.util.* ;
import org.apache.jena.atlas.lib.InternalErrorException ;
import org.apache.jena.atlas.logging.Log ;
import org.apache.jena.query.SortCondition ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.OpVisitor ;
import org.apache.jena.sparql.algebra.Transform ;
import org.apache.jena.sparql.algebra.op.* ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.core.VarExprList ;
import org.apache.jena.sparql.expr.* ;
import org.apache.jena.sparql.expr.aggregate.Aggregator ;
/** Apply the {@link Transform}, {@link ExprTransform}
* Works in conjunction with {@link WalkerVisitor}.
*/
public class ApplyTransformVisitor implements OpVisitorByTypeAndExpr, ExprVisitor {
private final Transform opTransform ;
private final ExprTransform exprTransform ;
protected final boolean visitService ;
private final Deque<Op> opStack = new ArrayDeque<>() ;
private final Deque<Expr> exprStack = new ArrayDeque<>() ;
private final OpVisitor beforeVisitor ;
private final OpVisitor afterVisitor ;
public ApplyTransformVisitor(Transform opTransform, ExprTransform exprTransform,
boolean visitService,
OpVisitor before, OpVisitor after) {
this.opTransform = opTransform ;
this.exprTransform = exprTransform ;
this.beforeVisitor = before ;
this.afterVisitor = after ;
this.visitService = visitService ;
}
public /*package*/ final Op opResult() {
return pop(opStack) ;
}
/*package*/ final Expr exprResult() {
return pop(exprStack) ;
}
// These three could be calls within WalkerVisitor followed by "collect".
protected Expr transform(Expr expr) {
int x1 = opStack.size() ;
int x2 = exprStack.size() ;
try {
return Walker.transform(expr, this, beforeVisitor, afterVisitor) ;
} finally {
int y1 = opStack.size() ;
int y2 = exprStack.size() ;
if ( x1 != y1 )
Log.error(ApplyTransformVisitor.class, "Misaligned opStack") ;
if ( x2 != y2 )
Log.error(ApplyTransformVisitor.class, "Misaligned exprStack") ;
}
}
protected ExprList transform(ExprList exprList) {
// if ( exprList == null || exprTransform == null )
// return exprList ;
ExprList exprList2 = new ExprList() ;
exprList.forEach( e->exprList2.add(transform(e)) );
return exprList2 ;
}
protected List<SortCondition> transform(List<SortCondition> conditions) {
List<SortCondition> conditions2 = new ArrayList<>() ;
boolean changed = false ;
for ( SortCondition sc : conditions ) {
Expr e = sc.getExpression() ;
Expr e2 = transform(e) ;
conditions2.add(new SortCondition(e2, sc.getDirection())) ;
if ( e != e2 )
changed = true ;
}
if ( changed )
return conditions2 ;
else
return conditions ;
}
// Interact with WalkerVisitor.
@Override
public void visit(OpOrder opOrder) {
List<SortCondition> conditions = opOrder.getConditions() ;
List<SortCondition> conditions2 = new ArrayList<>() ;
boolean changed = false ;
for ( SortCondition sc : conditions ) {
Expr e = sc.getExpression() ;
Expr e2 = transform(e) ;
conditions2.add(new SortCondition(e2, sc.getDirection())) ;
if ( e != e2 )
changed = true ;
}
OpOrder x = opOrder ;
if ( changed )
x = new OpOrder(opOrder.getSubOp(), conditions2) ;
visit1(x) ;
}
@Override
public void visit(OpAssign opAssign) {
VarExprList varExpr = opAssign.getVarExprList() ;
VarExprList varExpr2 = collect(varExpr) ;
OpAssign opAssign2 = opAssign ;
if ( varExpr != varExpr2 )
opAssign2 = OpAssign.create(opAssign.getSubOp(), varExpr2) ;
visit1(opAssign2) ;
}
@Override
public void visit(OpExtend opExtend) {
VarExprList varExpr = opExtend.getVarExprList() ;
VarExprList varExpr2 = collect(varExpr) ;
OpExtend opExtend2 = opExtend ;
if ( varExpr != varExpr2 )
opExtend2 = OpExtend.create(opExtend.getSubOp(), varExpr2) ;
visit1(opExtend2) ;
}
// Special test cases for collectors.
// Careful about order.
private VarExprList collect(VarExprList varExprList) {
if ( varExprList == null )
return varExprList ;
List<Var> vars = varExprList.getVars() ;
VarExprList varExpr2 = new VarExprList() ;
List<Expr> x = collect(vars.size()) ;
boolean changed = false ;
for ( int i = 0 ; i < vars.size() ; i++ ) {
Var v = vars.get(i) ;
Expr e2 = x.get(i) ;
Expr e = varExpr2.getExpr(v) ;
if ( e != e2 )
changed = true ;
if ( e2 == null )
varExpr2.add(v) ;
else {
varExpr2.add(v, e2) ;
}
}
return changed ? varExpr2 : varExprList ;
}
private ExprList collect(ExprList exprList) {
if ( exprList == null )
return null ;
List<Expr> x = collect(exprList.size()) ;
boolean changed = false ;
for ( int i = 0 ; i < x.size() ; i++ ) {
if ( x.get(i) != exprList.get(i) ) {
changed = true ;
break ;
}
}
if ( ! changed )
return exprList ;
return new ExprList(x) ;
}
private ExprList collect(List<Expr> exprList) {
if ( exprList == null )
return null ;
return new ExprList(collect(exprList.size())) ;
}
// collect and return in the original order (take account of stack reversal).
private List<Expr> collect(int N) {
// Check for "same"/unchanged
List<Expr> x = new ArrayList<>(N) ;
for ( int i = N-1 ; i >= 0 ; i-- ) {
Expr e2 = pop(exprStack) ;
if ( e2 == Expr.NONE )
e2 = null ;
x.add(0, e2) ;
}
return x ;
}
@Override
public void visit(OpGroup opGroup) {
boolean changed = false ;
VarExprList varExpr = opGroup.getGroupVars() ;
VarExprList varExpr2 = collect(varExpr) ;
if ( varExpr != varExpr2 )
changed = true ;
List<ExprAggregator> aggs = opGroup.getAggregators() ;
List<ExprAggregator> aggs2 = aggs ;
// And the aggregators...
aggs2 = new ArrayList<>() ;
for ( ExprAggregator agg : aggs ) {
Aggregator aggregator = agg.getAggregator() ;
Var v = agg.getVar() ;
// Variable associated with the aggregate
Expr eVar = agg.getAggVar() ; // Not .getExprVar()
Expr eVar2 = transform(eVar) ;
if ( eVar != eVar2 )
changed = true ;
// The Aggregator expression
ExprList e = aggregator.getExprList() ;
ExprList e2 = e ;
if ( e != null )
// Null means "no relevant expression" e.g. COUNT(*)
e2 = transform(e) ;
if ( e != e2 )
changed = true ;
Aggregator a2 = aggregator.copy(e2) ;
aggs2.add(new ExprAggregator(eVar2.asVar(), a2)) ;
}
OpGroup opGroup2 = opGroup ;
if ( changed )
opGroup2 = OpGroup.create(opGroup.getSubOp(), varExpr2, aggs2) ;
visit1(opGroup2) ;
}
@Override
public void visit0(Op0 op) {
push(opStack, op.apply(opTransform)) ;
}
@Override
public void visit1(Op1 op) {
Op subOp = null ;
if ( op.getSubOp() != null )
subOp = pop(opStack) ;
push(opStack, op.apply(opTransform, subOp)) ;
}
@Override
public void visit2(Op2 op) {
Op left = null ;
Op right = null ;
// Must do right-left because the pushes onto the stack were left-right.
if ( op.getRight() != null )
right = pop(opStack) ;
if ( op.getLeft() != null )
left = pop(opStack) ;
Op opX = op.apply(opTransform, left, right) ;
push(opStack, opX) ;
}
@Override
public void visitN(OpN op) {
List<Op> x = new ArrayList<>(op.size()) ;
for ( Iterator<Op> iter = op.iterator() ; iter.hasNext() ; ) {
Op sub = iter.next() ;
Op r = pop(opStack) ;
// Skip nulls.
if ( r != null )
// Add in reverse.
x.add(0, r) ;
}
Op opX = op.apply(opTransform, x) ;
push(opStack, opX) ;
}
private void dump(String label) {
System.out.println(label) ;
String x = opStack.toString().replace('\n', ' ').replaceAll(" +", " ") ;
String y = exprStack.toString().replace('\n', ' ').replaceAll(" +", " ") ;
System.out.println(" O:"+x);
System.out.println(" E:"+y);
}
@Override
public void visit(OpFilter opFilter) {
Op subOp = null ;
if ( opFilter.getSubOp() != null )
subOp = pop(opStack) ;
ExprList ex = opFilter.getExprs() ;
if ( ex == null || ex.isEmpty() ) {
// No expressions.
// Doesn't normally happen but this code is safe in these cases.
push(opStack, opFilter.apply(opTransform, subOp)) ;
return ;
}
// ex2 is the same length as ex.
ExprList ex2 = collect(ex) ;
OpFilter f = opFilter ;
if ( ex != ex2 || opFilter.getSubOp() != subOp ) {
f = OpFilter.filterAlways(ex2, subOp) ;
// If we removed a layer of filter, then subOp needs changing
// else f-subOp is subOp anyway.
subOp = f.getSubOp() ;
}
push(opStack, f.apply(opTransform, subOp)) ;
}
@Override
public void visit(OpLeftJoin op) {
Op left = null ;
Op right = null ;
// Must do right-left because the pushes onto the stack were left-right.
if ( op.getRight() != null )
right = pop(opStack) ;
if ( op.getLeft() != null )
left = pop(opStack) ;
ExprList exprs = op.getExprs() ;
ExprList exprs2 = collect(exprs) ;
OpLeftJoin x = op ;
if ( exprs != exprs2 )
x = OpLeftJoin.createLeftJoin(left, right, exprs2) ;
Op opX = x.apply(opTransform, left, right) ;
push(opStack, opX) ;
}
@Override
public void visit(OpService op) {
if ( ! visitService ) {
// No visit - no transform - push input.
push(opStack, op) ;
return ;
}
OpVisitorByTypeAndExpr.super.visit(op);
}
@Override
public void visitExt(OpExt op) {
push(opStack, opTransform.transform(op)) ;
}
@Override
public void visitExpr(ExprList exprs) {
throw new InternalErrorException("Didn't expect as call to ApplyTransformVisit.visitExpr") ;
}
@Override
public void visitVarExpr(VarExprList exprVarExprList) {
throw new InternalErrorException("Didn't expect as call to ApplyTransformVisit.visitVarExpr") ;
}
@Override
public void visit(ExprFunction0 func) {
Expr e = func.apply(exprTransform) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprFunction1 func) {
Expr e1 = pop(exprStack) ;
Expr e = func.apply(exprTransform, e1) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprFunction2 func) {
Expr e2 = pop(exprStack) ;
Expr e1 = pop(exprStack) ;
Expr e = func.apply(exprTransform, e1, e2) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprFunction3 func) {
Expr e3 = pop(exprStack) ;
Expr e2 = pop(exprStack) ;
Expr e1 = pop(exprStack) ;
Expr e = func.apply(exprTransform, e1, e2, e3) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprFunctionN func) {
ExprList x = collect(func.getArgs()) ;
Expr e = func.apply(exprTransform, x) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprFunctionOp funcOp) {
ExprList x = null ;
if ( funcOp.getArgs() != null )
x = collect(funcOp.getArgs()) ;
Op op = pop(opStack) ;
Expr e = funcOp.apply(exprTransform, x, op) ;
push(exprStack, e) ;
}
@Override
public void visit(NodeValue nv) {
Expr e = nv.apply(exprTransform) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprVar var) {
Expr e = var.apply(exprTransform) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprAggregator eAgg) {
Expr e = eAgg.apply(exprTransform) ;
push(exprStack, e) ;
}
@Override
public void visit(ExprNone e) {
push(exprStack, e) ;
}
private <T> void push(Deque<T> stack, T value) {
if ( value == null )
Log.warn(ApplyTransformVisitor.class, "Pushing null onto the "+stackLabel(stack)+" stack") ;
stack.push(value) ;
}
private <T> T pop(Deque<T> stack) {
try {
T v = stack.pop() ;
if ( v == null )
Log.warn(ApplyTransformVisitor.class, "Pop null from the "+stackLabel(stack)+" stack") ;
return v ;
}
catch (NoSuchElementException ex) {
if ( true ) throw new RuntimeException() ;
Log.warn(ApplyTransformVisitor.class, "Empty "+stackLabel(stack)+" stack") ;
return null ;
}
}
private String stackLabel(Deque<?> stack) {
if ( stack == opStack ) return "Op" ;
if ( stack == exprStack ) return "Expr" ;
return "<other>" ;
}
}