blob: d4dc1e0a1709bdf826b71db4dff6104f88f68283 [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.Iterator ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.OpVisitor ;
import org.apache.jena.sparql.algebra.OpVisitorBase ;
import org.apache.jena.sparql.algebra.op.* ;
import org.apache.jena.sparql.core.VarExprList ;
import org.apache.jena.sparql.expr.* ;
/** Walk algebra and expressions */
public class WalkerVisitor implements OpVisitorByTypeAndExpr, ExprVisitorFunction {
protected final ExprVisitor exprVisitor ;
protected final OpVisitor opVisitor ;
protected int opDepthLimit = Integer.MAX_VALUE ;
protected int exprDepthLimit = Integer.MAX_VALUE ;
protected int opDepth = 0 ;
protected int exprDepth = 0 ;
private final OpVisitor beforeVisitor ;
private final OpVisitor afterVisitor ;
/**
* A walker. If a visitor is null, then don't walk in. For
* "no action but keep walking inwards", use {@link OpVisitorBase} and
* {@link ExprVisitorBase}.
*
* @see OpVisitorBase
* @see ExprVisitorBase
*/
public WalkerVisitor(OpVisitor opVisitor, ExprVisitor exprVisitor, OpVisitor before, OpVisitor after) {
this.opVisitor = opVisitor ;
this.exprVisitor = exprVisitor ;
if ( opDepthLimit < 0 )
opDepthLimit = Integer.MAX_VALUE ;
if ( exprDepth < 0 )
exprDepthLimit = Integer.MAX_VALUE ;
opDepth = 0 ;
exprDepth = 0 ;
beforeVisitor = before ;
afterVisitor = after ;
}
protected final void before(Op op) {
if ( beforeVisitor != null )
op.visit(beforeVisitor) ;
}
protected final void after(Op op) {
if ( afterVisitor != null )
op.visit(afterVisitor) ;
}
public void walk(Op op) {
if ( op == null )
return ;
if ( opDepth == opDepthLimit )
// No deeper.
return ;
opDepth++ ;
try { op.visit(this); }
finally { opDepth-- ; }
}
public void walk(Expr expr) {
if ( expr == null )
return ;
if ( exprDepth == exprDepthLimit )
return ;
exprDepth++ ;
try { expr.visit(this) ; }
finally { exprDepth-- ; }
}
public void walk(ExprList exprList) {
if ( exprList == null )
return ;
exprList.forEach(e->walk(e));
}
public void walk(VarExprList varExprList) {
if ( varExprList == null )
return ;
// retains order.
varExprList.forEachVarExpr((v,e) -> {
Expr expr = (e!=null) ? e : Expr.NONE ;
walk(expr) ;
});
}
// ---- Mode swapping between op and expr. visit=>walk
@Override
public void visitExpr(ExprList exprList) {
if ( exprVisitor != null )
walk(exprList) ;
}
@Override
public void visitVarExpr(VarExprList varExprList) {
if ( exprVisitor != null )
walk(varExprList);
}
// ----
public void visitOp(Op op) {
before(op) ;
if ( opVisitor != null )
op.visit(this);
after(op) ;
}
@Override
public void visit0(Op0 op) {
before(op) ;
if ( opVisitor != null )
op.visit(opVisitor) ;
after(op) ;
}
@Override
public void visit1(Op1 op) {
before(op) ;
visit1$(op) ;
after(op) ;
}
// Can be called via different routes.
private void visit1$(Op1 op) {
if ( op.getSubOp() != null )
op.getSubOp().visit(this) ;
if ( opVisitor != null )
op.visit(opVisitor) ;
}
@Override
public void visit2(Op2 op) {
before(op) ;
if ( op.getLeft() != null )
op.getLeft().visit(this) ;
if ( op.getRight() != null )
op.getRight().visit(this) ;
if ( opVisitor != null )
op.visit(opVisitor) ;
after(op) ;
}
@Override
public void visitN(OpN op) {
before(op) ;
for (Iterator<Op> iter = op.iterator(); iter.hasNext();) {
Op sub = iter.next() ;
sub.visit(this) ;
}
if ( opVisitor != null )
op.visit(opVisitor) ;
after(op) ;
}
@Override
public void visitExt(OpExt op) {
before(op) ;
if ( opVisitor != null )
op.visit(opVisitor) ;
after(op) ;
}
@Override
public void visit(OpOrder opOrder) {
// XXX Why not this?
// ApplyTransformVisitor handles the parts of OpOrder.
// before(opOrder) ;
// visitSortConditions(opOrder.getConditions()) ;
// visitModifer(opOrder);
// visit1$(opOrder);
// after(opOrder) ;
visit1(opOrder) ;
}
@Override
public void visit(OpAssign opAssign) {
before(opAssign) ;
VarExprList varExpr = opAssign.getVarExprList() ;
visitVarExpr(varExpr);
visit1$(opAssign) ;
after(opAssign) ;
}
@Override
public void visit(OpExtend opExtend) {
before(opExtend) ;
VarExprList varExpr = opExtend.getVarExprList() ;
visitVarExpr(varExpr);
visit1$(opExtend) ;
after(opExtend) ;
}
// Transforming to quads needs the graph node handled before doing the sub-algebra ops
// so it has to be done as before/after by the Walker. By the time visit(OpGraph) is called,
// the sub-tree has already been visited.
// @Override
// public void visit(OpGraph op) {
// pushGraph(op.getNode()) ;
// OpVisitorByTypeAndExpr.super.visit(op) ;
// popGraph() ;
// }
//
// private Deque<Node> stack = new ArrayDeque<>() ;
//
// public Node getCurrentGraph() { return stack.peek() ; }
//
// private void pushGraph(Node node) {
// stack.push(node) ;
// }
//
// private void popGraph() {
// stack.pop() ;
// }
@Override
public void visit(ExprFunction0 func) { visitExprFunction(func) ; }
@Override
public void visit(ExprFunction1 func) { visitExprFunction(func) ; }
@Override
public void visit(ExprFunction2 func) { visitExprFunction(func) ; }
@Override
public void visit(ExprFunction3 func) { visitExprFunction(func) ; }
@Override
public void visit(ExprFunctionN func) { visitExprFunction(func) ; }
@Override
public void visitExprFunction(ExprFunction func) {
for ( int i = 1 ; i <= func.numArgs() ; i++ ) {
Expr expr = func.getArg(i) ;
if ( expr == null )
// Put a dummy in, e.g. to keep the transform stack aligned.
Expr.NONE.visit(this) ;
else
expr.visit(this) ;
}
if ( exprVisitor != null )
func.visit(exprVisitor) ;
}
@Override
public void visit(ExprFunctionOp funcOp) {
walk(funcOp.getGraphPattern());
if ( exprVisitor != null )
funcOp.visit(exprVisitor) ;
}
@Override
public void visit(NodeValue nv) {
if ( exprVisitor != null )
nv.visit(exprVisitor) ;
}
@Override
public void visit(ExprVar v) {
if ( exprVisitor != null )
v.visit(exprVisitor) ;
}
@Override
public void visit(ExprNone none) {
if ( exprVisitor != null )
none.visit(exprVisitor) ;
}
@Override
public void visit(ExprAggregator eAgg) {
// This is the assignment variable of the aggregation
// not a normal variable of an expression.
visitAssignVar(eAgg.getAggVar().asVar()) ;
if ( exprVisitor != null )
eAgg.visit(exprVisitor) ;
}
}