You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2016/05/21 18:54:27 UTC

[07/23] jena git commit: Towards new walker/transformation code

Towards new walker/transformation code


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/c1fde992
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/c1fde992
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/c1fde992

Branch: refs/heads/master
Commit: c1fde992d024d1402639c9c2e98bfa486c324966
Parents: 86877d1
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Apr 22 16:34:29 2016 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sat May 21 18:29:13 2016 +0100

----------------------------------------------------------------------
 .../jena/sparql/algebra/TransformQuadGraph.java |   4 +
 .../apache/jena/sparql/algebra/Transformer.java |  31 +-
 .../algebra/walker/ApplyTransformVisitor.java   | 419 +++++++++++++++++++
 .../algebra/walker/ElementWalker_New.java       | 196 +++++++++
 .../algebra/walker/ExprVisitorFunction.java     |  38 ++
 .../algebra/walker/OpVisitorByTypeAndExpr.java  | 229 ++++++++++
 .../jena/sparql/algebra/walker/Walker.java      | 190 +++++++++
 .../sparql/algebra/walker/WalkerVisitor.java    | 272 ++++++++++++
 8 files changed, 1368 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformQuadGraph.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformQuadGraph.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformQuadGraph.java
index fffb57e..27d44e5 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformQuadGraph.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/TransformQuadGraph.java
@@ -46,6 +46,9 @@ public class TransformQuadGraph extends TransformCopy
 
     @Override
     public Op transform(OpGraph opGraph, Op op) {
+        
+        //System.err.println("transform(OpGraph)\n"+opGraph+op) ;
+        
         // ?? Could just leave the (graph) in place always - just rewrite BGPs. 
         boolean noPattern = false ;
         
@@ -113,6 +116,7 @@ public class TransformQuadGraph extends TransformCopy
     
     @Override
     public Op transform(OpBGP opBGP) { 
+        //System.err.println("transform(OpBGP) : "+getNode()+"\n"+opBGP) ;
         return new OpQuadPattern(getNode(), opBGP.getPattern()) ;
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transformer.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transformer.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transformer.java
index ba3ee21..9f155d4 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transformer.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Transformer.java
@@ -25,6 +25,7 @@ import org.apache.jena.query.SortCondition ;
 import org.apache.jena.sparql.algebra.OpWalker.WalkerVisitor ;
 import org.apache.jena.sparql.algebra.op.* ;
 import org.apache.jena.sparql.algebra.optimize.ExprTransformApplyTransform ;
+import org.apache.jena.sparql.algebra.walker.Walker ;
 import org.apache.jena.sparql.core.Var ;
 import org.apache.jena.sparql.core.VarExprList ;
 import org.apache.jena.sparql.expr.* ;
@@ -119,21 +120,30 @@ public class Transformer
     // and theses protected methods.
     protected Op transformation(Transform transform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
     {
+        // XXX
         ExprTransform exprTransform = new ExprTransformApplyTransform(transform, beforeVisitor, afterVisitor) ;
         return transformation(transform, exprTransform, op, beforeVisitor, afterVisitor) ;
-    }
+    }   
+    
+    protected Op transformation(Transform transform, ExprTransform exprTransform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        // XXX Switch on before/after via the Walker.
+        if ( true )
+            return Walker.transform(op, transform, exprTransform, beforeVisitor, afterVisitor) ;
         
-    protected Op transformation(Transform transform, ExprTransform exprTransform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
-    {
+        if ( beforeVisitor == null && afterVisitor == null )
+            return Walker.transform(op, transform, exprTransform) ;
+//        static Set<Class<?>> seen = new HashSet<>() ;
+//        if ( ! seen.contains(beforeVisitor.getClass()) ) {
+//            System.out.println("T:"+beforeVisitor.getClass().getName()) ;
+//            seen.add(beforeVisitor.getClass()) ;
+//        }
         ApplyTransformVisitor v = new ApplyTransformVisitor(transform, exprTransform) ;
         return transformation(v, op, beforeVisitor, afterVisitor) ;
     }
     
-    protected Op transformation(ApplyTransformVisitor transformApply,
-                                Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
-    {
-        if ( op == null )
-        {
+    protected Op transformation(ApplyTransformVisitor transformApply, Op op, 
+                                OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        if ( op == null ) {
             Log.warn(this, "Attempt to transform a null Op - ignored") ;
             return op ;
         }
@@ -141,9 +151,8 @@ public class Transformer
     }
 
     /** The primitive operation to apply a transformation to an Op */
-    protected Op applyTransformation(ApplyTransformVisitor transformApply,
-                                     Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
-    {
+    private /*protected*/ Op applyTransformation(ApplyTransformVisitor transformApply, Op op,
+                                     OpVisitor beforeVisitor, OpVisitor afterVisitor) {
         OpWalker.walk(op, transformApply, beforeVisitor, afterVisitor) ;
         Op r = transformApply.result() ;
         return r ;

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
new file mode 100644
index 0000000..750f893
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ApplyTransformVisitor.java
@@ -0,0 +1,419 @@
+/*
+ * 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.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 ;
+
+public class ApplyTransformVisitor implements OpVisitorByTypeAndExpr, ExprVisitor {
+    private final Transform     opTransform ;
+    private final ExprTransform exprTransform ;
+
+    protected boolean           visitService = true ;
+    
+    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, OpVisitor before, OpVisitor after) {
+        this.opTransform = opTransform ;
+        this.exprTransform = exprTransform ;
+        this.beforeVisitor = before ;
+        this.afterVisitor = after ;
+    }
+
+    /*package*/ final Op opResult() {
+        return pop(opStack) ;
+    }
+
+    /*package*/ final Expr exprResult() {
+        return pop(exprStack) ;
+    }
+
+    protected Op transform(Op op) {
+        // reuse this ApplyTransformVisitor? with stack checking?
+        return Walker.transform(op, this, beforeVisitor, afterVisitor) ;
+    }
+    
+    protected Expr transform(Expr expr) {
+        // reuse this ApplyTransformVisitor? with stack checking?
+        return Walker.transform(expr, this, beforeVisitor, afterVisitor) ;
+    }
+    
+    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 ;
+    }
+
+    @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 = process(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 = process(varExpr) ;
+        OpExtend opExtend2 = opExtend ;
+        if ( varExpr != varExpr2 )
+            opExtend2 = OpExtend.create(opExtend.getSubOp(), varExpr2) ;
+        visit1(opExtend2) ;
+    }
+
+    private VarExprList process(VarExprList varExprList) {
+        if ( varExprList == null )
+            return varExprList ;
+        List<Var> vars = varExprList.getVars() ;
+        VarExprList varExpr2 = new VarExprList() ;
+        boolean changed = false ;
+        for ( Var v : vars ) {
+            Expr e = varExprList.getExpr(v) ;
+            Expr e2 = e ;
+            if ( e != null )
+                e2 = transform(e) ;
+            if ( e2 == null )
+                varExpr2.add(v) ;
+            else
+                varExpr2.add(v, e2) ;
+            if ( e != e2 )
+                changed = true ;
+        }
+        if ( !changed )
+            return varExprList ;
+        return varExpr2 ;
+    }
+
+    private ExprList process(ExprList exprList) {
+        if ( exprList == null )
+            return null ;
+        ExprList exprList2 = new ExprList() ;
+        boolean changed = false ;
+        for ( Expr e : exprList ) {
+            Expr e2 = process(e) ;
+            exprList2.add(e2) ;
+            if ( e != e2 )
+                changed = true ;
+        }
+        if ( !changed )
+            return exprList ;
+        return exprList2 ;
+    }
+
+    private Expr process(Expr expr) {
+        Expr e = expr ;
+        Expr e2 = e ;
+        if ( e != null )
+            e2 = transform(e) ;
+        if ( e == e2 )
+            return expr ;
+        return e2 ;
+    }
+
+    @Override
+    public void visit(OpGroup opGroup) {
+        boolean changed = false ;
+
+        VarExprList varExpr = opGroup.getGroupVars() ;
+        VarExprList varExpr2 = process(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 = new OpGroup(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) ;
+    }
+
+    @Override
+    public void visit(OpFilter opFilter) {
+        Op subOp = null ;
+        if ( opFilter.getSubOp() != null )
+            subOp = pop(opStack) ;
+        boolean changed = (opFilter.getSubOp() != subOp) ;
+
+        ExprList ex = opFilter.getExprs() ;
+        ExprList ex2 = process(ex) ;
+        OpFilter f = opFilter ;
+        if ( ex != ex2 )
+            f = (OpFilter)OpFilter.filter(ex2, subOp) ;
+        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 = process(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 - push input.
+            push(opStack, op) ;
+            return ;
+        }
+        // op.getService()
+        OpVisitorByTypeAndExpr.super.visit(op);
+    }
+
+    @Override
+    public void visitExt(OpExt op) {
+        push(opStack, opTransform.transform(op)) ;
+    }
+    
+    @Override
+    public void visitExpr(ExprList exprs) { 
+        System.err.println("visitExpr(ExprList)") ;
+        if ( exprs != null && exprTransform != null ) {
+            
+        }
+    }
+    
+    @Override
+    public void visitExpr(VarExprList exprVarExprList)  {
+        System.err.println("visitExpr(ExprList)") ;
+        if ( exprVarExprList != null && exprTransform != null ) {
+            
+        }
+    }
+    
+    @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 = process(func.getArgs()) ;
+        Expr e = func.apply(exprTransform, x) ;
+        push(exprStack, e) ;
+    }
+
+    private ExprList process(List<Expr> exprList) {
+        if ( exprList == null )
+            return null ;
+        int N = exprList.size() ;
+        List<Expr> x = new ArrayList<>(N) ;
+        for ( Expr anExprList : exprList ) {
+            Expr e2 = pop(exprStack) ;
+            // Add in reverse.
+            x.add(0, e2) ;
+        }
+        return new ExprList(x) ;
+    }
+
+    @Override
+    public void visit(ExprFunctionOp funcOp) {
+        ExprList x = null ;
+//        Op op = transform(funcOp.getGraphPattern()) ;
+        if ( funcOp.getArgs() != null )
+            x = process(funcOp.getArgs()) ;
+        Expr e = funcOp.apply(exprTransform, x, funcOp.getGraphPattern()) ;
+        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) ;
+    }
+
+    private <T> void push(Deque<T> stack, T value) {
+        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 "+stackLabel(stack)+" stack") ;
+            return v ;
+        }
+        catch (EmptyStackException ex) {
+            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>" ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ElementWalker_New.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ElementWalker_New.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ElementWalker_New.java
new file mode 100644
index 0000000..7ca53b9
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ElementWalker_New.java
@@ -0,0 +1,196 @@
+/*
+ * 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 org.apache.jena.sparql.expr.* ;
+import org.apache.jena.sparql.syntax.* ;
+
+/** An element visitor that walks the graph pattern tree for one query level. 
+ *  applying a visitor at each Element traversed.<br/>
+ *  Does not process subqueries.<br/>      
+ *  Does not process (NOT)EXISTS in filters.<br/>
+ *  These will need to call down themselves if it is meaningful for the visitor.
+ *  Bottom-up walk - apply to subelements before applying to current element.
+ */
+
+public class ElementWalker_New {
+    public static void walk(Element el, ElementVisitor visitor) {
+        walk(el, visitor, null) ;
+    }
+
+    public static void walk(Element el, ElementVisitor elVisitor, ExprVisitor exprVisitor) {
+        EltWalker w = new EltWalker(elVisitor, exprVisitor) ;
+        el.visit(w) ;
+    }
+
+//    protected static void walk$(Element el, Walker walker) {
+//        el.visit(walker) ;
+//    }
+
+    static public class EltWalker implements ElementVisitor, ExprVisitorFunction {
+        protected final ElementVisitor elementVisitor ;
+        protected final ExprVisitor exprVisitor ;
+
+        protected EltWalker(ElementVisitor visitor, ExprVisitor exprVisitor) {
+            this.elementVisitor = visitor ;
+            this.exprVisitor = exprVisitor ;
+        }
+
+        @Override
+        public void visit(ElementTriplesBlock el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementFilter el) {
+            el.getExpr().visit(this);
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementAssign el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementBind el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementData el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementUnion el) {
+            for ( Element e : el.getElements() )
+                e.visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementGroup el) {
+            for ( Element e : el.getElements() )
+                e.visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementOptional el) {
+            if ( el.getOptionalElement() != null )
+                el.getOptionalElement().visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementDataset el) {
+            if ( el.getElement() != null )
+                el.getElement().visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementNamedGraph el) {
+            if ( el.getElement() != null )
+                el.getElement().visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementService el) {
+            if ( el.getElement() != null )
+                el.getElement().visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        // EXISTs, NOT EXISTs also occur in FILTERs via expressions.
+
+        @Override
+        public void visit(ElementExists el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementNotExists el) {
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementMinus el) {
+            if ( el.getMinusElement() != null )
+                el.getMinusElement().visit(this) ;
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementSubQuery el) {
+            // This does not automatically walk into the subquery.
+            elementVisitor.visit(el) ;
+        }
+
+        @Override
+        public void visit(ElementPathBlock el) {
+            elementVisitor.visit(el) ;
+        }
+        
+        @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.
+                    NodeValue.nvNothing.visit(this) ;
+                else
+                    expr.visit(this) ;
+            }
+            func.visit(exprVisitor) ;
+        }
+        
+        @Override
+        public void visit(ExprFunctionOp funcOp) {
+            // Walk the op
+            funcOp.getElement().visit(this);
+            funcOp.visit(exprVisitor) ;
+        }
+        
+        @Override
+        public void visit(NodeValue nv)         { nv.visit(exprVisitor) ; }
+        @Override
+        public void visit(ExprVar v)            { v.visit(exprVisitor) ; }
+        @Override
+        public void visit(ExprAggregator eAgg)  {
+            //eAgg.getAggVar().visit(visitorExpr);
+            // XXX Hack for varsMentioned
+            eAgg.visit(exprVisitor) ; 
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ExprVisitorFunction.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ExprVisitorFunction.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ExprVisitorFunction.java
new file mode 100644
index 0000000..fa72d89
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/ExprVisitorFunction.java
@@ -0,0 +1,38 @@
+/*
+ * 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 org.apache.jena.sparql.expr.* ;
+
+/** Convert all visit calls on the expressions in a call to a generic visit operation for expression functions */
+public interface ExprVisitorFunction extends ExprVisitor 
+{
+    @Override
+    public default void visit(ExprFunction0 func) { visitExprFunction(func) ; }
+    @Override
+    public default void visit(ExprFunction1 func) { visitExprFunction(func) ; }
+    @Override
+    public default void visit(ExprFunction2 func) { visitExprFunction(func) ; }
+    @Override
+    public default void visit(ExprFunction3 func) { visitExprFunction(func) ; }
+    @Override
+    public default void visit(ExprFunctionN func) { visitExprFunction(func) ; }
+
+    public void visitExprFunction(ExprFunction func) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
new file mode 100644
index 0000000..7dfb65f
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/OpVisitorByTypeAndExpr.java
@@ -0,0 +1,229 @@
+/*
+ * 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 org.apache.jena.sparql.algebra.OpVisitor ;
+import org.apache.jena.sparql.algebra.op.* ;
+import org.apache.jena.sparql.core.VarExprList ;
+import org.apache.jena.sparql.expr.ExprList ;
+
+/** A visitor helper that maps all visits to a few general ones */ 
+public interface OpVisitorByTypeAndExpr extends OpVisitor
+{
+    public void visitN(OpN op) ;
+
+    public void visit2(Op2 op) ;
+    
+    public void visit1(Op1 op) ;
+    
+    public void visit0(Op0 op) ;    
+    
+    public default void visitExt(OpExt op) {
+        op.effectiveOp().visit(this);
+    }
+
+    public void visitExpr(ExprList exprs) ;
+    public void visitExpr(VarExprList exprs) ;
+    //public void visitAssignVar(Var var) ;
+    
+    public default void visitModifer(OpModifier opMod) {
+        visit1(opMod);
+    }
+
+    @Override
+    public default void visit(OpBGP opBGP) {
+        visit0(opBGP);
+    }
+
+    @Override
+    public default void visit(OpQuadPattern quadPattern) {
+        visit0(quadPattern);
+    }
+
+    @Override
+    public default void visit(OpQuadBlock quadBlock) {
+        visit0(quadBlock);
+    }
+
+    @Override
+    public default void visit(OpTriple opTriple) {
+        visit0(opTriple);
+    }
+
+    @Override
+    public default void visit(OpQuad opQuad) {
+        visit0(opQuad);
+    }
+
+    @Override
+    public default void visit(OpPath opPath) {
+        visit0(opPath);
+    }
+
+    @Override
+    public default void visit(OpProcedure opProcedure) {
+        visit1(opProcedure);
+    }
+
+    @Override
+    public default void visit(OpPropFunc opPropFunc) {
+        visit1(opPropFunc);
+    }
+
+    @Override
+    public default void visit(OpJoin opJoin) {
+        visit2(opJoin);
+    }
+
+    @Override
+    public default void visit(OpSequence opSequence) {
+        visitN(opSequence);
+    }
+
+    @Override
+    public default void visit(OpDisjunction opDisjunction) {
+        visitN(opDisjunction);
+    }
+
+    @Override
+    public default void visit(OpLeftJoin opLeftJoin) {
+        visitExpr(opLeftJoin.getExprs());
+        visit2(opLeftJoin);
+    }
+
+    @Override
+    public default void visit(OpDiff opDiff) {
+        visit2(opDiff);
+    }
+
+    @Override
+    public default void visit(OpMinus opMinus) {
+        visit2(opMinus);
+    }
+
+    @Override
+    public default void visit(OpUnion opUnion) {
+        visit2(opUnion);
+    }
+
+    @Override
+    public default void visit(OpConditional opCond) {
+        visit2(opCond);
+    }
+
+    @Override
+    public default void visit(OpFilter opFilter) {
+        visitExpr(opFilter.getExprs());
+        visit1(opFilter);
+    }
+
+    @Override
+    public default void visit(OpGraph opGraph) {
+        visit1(opGraph);
+    }
+
+    @Override
+    public default void visit(OpService opService) {
+        visit1(opService);
+    }
+
+    @Override
+    public default void visit(OpDatasetNames dsNames) {
+        visit0(dsNames);
+    }
+
+    @Override
+    public default void visit(OpTable opUnit) {
+        visit0(opUnit);
+    }
+
+    @Override
+    public default void visit(OpExt opExt) {
+        visitExt(opExt);
+    }
+
+    @Override
+    public default void visit(OpNull opNull) {
+        visit0(opNull);
+    }
+
+    @Override
+    public default void visit(OpLabel opLabel) {
+        visit1(opLabel);
+    }
+
+    @Override
+    public default void visit(OpOrder opOrder) {
+        // XXX
+        //opOrder.getConditions() ;
+        visitModifer(opOrder);
+    }
+
+    @Override
+    public default void visit(OpGroup opGroup) {
+        visitExpr(opGroup.getGroupVars()) ;
+        // XXX
+        //opGroup.getAggregators() ;
+        visit1(opGroup);
+    }
+
+    @Override
+    public default void visit(OpTopN opTop) {
+        // XXX
+        // opTop.getConditions() ;
+        visit1(opTop);
+    }
+    
+    @Override
+    public default void visit(OpAssign opAssign) {
+        visitExpr(opAssign.getVarExprList()) ;
+        visit1(opAssign);
+    }
+
+    @Override
+    public default void visit(OpExtend opExtend) {
+        visitExpr(opExtend.getVarExprList()) ;
+        visit1(opExtend);
+    }
+
+    @Override
+    public default void visit(OpList opList) {
+        visitModifer(opList);
+    }
+
+    @Override
+    public default void visit(OpProject opProject) {
+        visitModifer(opProject);
+    }
+
+    @Override
+    public default void visit(OpReduced opReduced) {
+        visitModifer(opReduced);
+    }
+
+    @Override
+    public default void visit(OpDistinct opDistinct) {
+        visitModifer(opDistinct);
+    }
+
+    @Override
+    public default void visit(OpSlice opSlice) {
+        visitModifer(opSlice);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/Walker.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/Walker.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/Walker.java
new file mode 100644
index 0000000..6135416
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/Walker.java
@@ -0,0 +1,190 @@
+/*
+ * 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.Objects ;
+
+import org.apache.jena.sparql.algebra.* ;
+import org.apache.jena.sparql.core.VarExprList ;
+import org.apache.jena.sparql.expr.* ;
+
+/** Walk algebra, {@link Op}s and {@link Expr}s. */
+public class Walker {
+
+    /** Walk visiting every {@link Op} with an {@link OpVisitor},
+     * including inside expressions.
+     */
+    public static void walk(Op op, OpVisitor opVisitor) {
+        Objects.requireNonNull(opVisitor) ;
+        walk(op, opVisitor, null);
+    }
+    
+    /** Walk visiting every {@link Op} and every {@link Expr},
+     *  starting from an {@link Op}.
+     */
+    public static void walk(Op op, OpVisitor opVisitor, ExprVisitor exprVisitor) {
+        walk(op, opVisitor, exprVisitor, null, null) ; 
+    }
+    
+    /** Walk visiting every {@link Op} and every {@link Expr},
+     *  starting from an {@link Op}.
+     */
+    public static void walk(Op op, OpVisitor opVisitor, ExprVisitor exprVisitor, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        if ( op == null )
+            return ;
+        createWalker(opVisitor, exprVisitor, beforeVisitor, afterVisitor).walk(op);
+    }
+
+    /** Walk visiting every {@link Expr} with an {@link ExprVisitor},
+     * including inside any {@link Op} in expressions.
+     */
+    public static void walk(Expr expr, ExprVisitor exprVisitor) {
+        Objects.requireNonNull(exprVisitor) ;
+        walk(expr, null, exprVisitor);
+    }
+    
+    /** Walk visiting every {@link Op} and every {@link Expr},
+     *  starting from an {@link Expr}.
+     */
+    public static void walk(Expr expr, OpVisitor opVisitor, ExprVisitor exprVisitor) {
+        walk(expr, opVisitor, exprVisitor, null, null) ;
+    }
+    
+    /** Walk visiting every {@link Op} and every {@link Expr},
+     *  starting from an {@link Expr}.
+     */
+    public static void walk(Expr expr, OpVisitor opVisitor, ExprVisitor exprVisitor, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        if ( expr == null )
+            return ;
+        Objects.requireNonNull(expr) ;
+        createWalker(opVisitor, exprVisitor, beforeVisitor,afterVisitor).walk(expr);
+    }
+    
+
+    /** Walk visiting every {@link Expr} with an {@link ExprVisitor},
+     * including inside any {@link Op} in expressions.
+     */
+    public static void walk(ExprList exprList, ExprVisitor exprVisitor) {
+       walk(exprList, null, exprVisitor);
+    }
+    
+    /** Walk visiting every {@link Op} and every {@link Expr},
+     *  starting from an {@link ExprList}.
+     */
+    public static void walk(ExprList exprList, OpVisitor opVisitor, ExprVisitor exprVisitor) {
+        if ( exprList == null )
+            return ;
+        Objects.requireNonNull(exprVisitor) ;
+        exprList.forEach(e->walk(e,opVisitor, exprVisitor)) ;
+    }
+
+    public static void walk(VarExprList varExprList, ExprVisitor exprVisitor) {
+        Objects.requireNonNull(exprVisitor) ;
+        walk(varExprList, null, exprVisitor);
+     }
+     
+     public static void walk(VarExprList varExprList, OpVisitor opVisitor, ExprVisitor exprVisitor) {
+         if ( varExprList == null )
+             return ;
+         varExprList.forEach((v,e)->walk(e,opVisitor, exprVisitor)) ;
+     }
+ 
+    private static OpVisitor   nullOpVisitor   = new OpVisitorBase() ;
+    private static ExprVisitor nullExprVisitor = new ExprVisitorBase() ;
+     
+    public static WalkerVisitor createWalker(OpVisitor opVisitor, ExprVisitor exprVisitor) {
+        return createWalker(opVisitor, exprVisitor, null, null) ;
+    }
+    
+    public static WalkerVisitor createWalker(OpVisitor opVisitor, ExprVisitor exprVisitor, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        
+        if ( opVisitor == null )
+            opVisitor = nullOpVisitor ;
+        if ( exprVisitor == null )
+            exprVisitor = new ExprVisitorBase() ;
+        return new WalkerVisitor(opVisitor, exprVisitor, beforeVisitor, afterVisitor)  ;
+    }
+    
+    /** Transform an {@link Op}. */
+    public static Op transform(Op op, Transform opTransform, ExprTransform exprTransform) {
+        ApplyTransformVisitor v = createTransformer(opTransform, exprTransform) ;
+        return transform(op, v) ;
+    }
+    
+    /** Transform an {@link Op}. */
+    public static Op transform(Op op, Transform opTransform, ExprTransform exprTransform, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        ApplyTransformVisitor v = createTransformer(opTransform, exprTransform) ;
+        return transform(op, v, beforeVisitor, afterVisitor) ;
+    }
+
+    /** Transform an {@link Expr}. */
+    public static Expr transform(Expr expr, Transform opTransform, ExprTransform exprTransform) {
+        ApplyTransformVisitor v = createTransformer(opTransform, exprTransform) ;
+        return transform(expr, v) ;
+    }
+
+    /** Transform an {@link Op}. */
+    public static Op transform(Op op, ApplyTransformVisitor v) {
+        return transform(op, v, null, null) ;
+    }
+
+    /** Transform an {@link Op}. */
+    public static Op transform(Op op, ApplyTransformVisitor v, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        walk(op, v, v, beforeVisitor, afterVisitor) ;
+        return v.opResult() ;
+    }
+
+    /** Transform an {@link Expr}. */
+    public static Expr transform(Expr expr, ApplyTransformVisitor v) {
+        walk(expr, v) ;
+        return v.exprResult() ;
+    }
+
+    /** Transform an {@link Expr}. */
+    public static Expr transform(Expr expr, ApplyTransformVisitor v, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        walk(expr, v, v, beforeVisitor, afterVisitor) ;
+        return v.exprResult() ;
+    }
+
+    /** Transform an algebra expression */
+    public static Op transform(Op op, Transform transform) {
+       return transform(op, transform, null) ;
+    }
+    
+    /** Transform an expression */
+    public static Expr transform(Expr expr, ExprTransform exprTransform) {
+        return transform(expr, null, exprTransform) ;
+    }
+        
+    private static Transform     nullOpTransform   = new TransformBase() ;
+    private static ExprTransform nullExprTransform = new ExprTransformBase() ;
+    
+    public static ApplyTransformVisitor createTransformer(Transform opTransform, ExprTransform exprTransform) {
+        return createTransformer(opTransform, exprTransform, null, null) ;
+    }
+ 
+    public static ApplyTransformVisitor createTransformer(Transform opTransform, ExprTransform exprTransform, OpVisitor beforeVisitor, OpVisitor afterVisitor) {
+        if ( opTransform == null )
+            opTransform = nullOpTransform ;
+        if ( exprTransform == null )
+            exprTransform = nullExprTransform ;
+        return new ApplyTransformVisitor(opTransform, exprTransform, null, null) ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/c1fde992/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
new file mode 100644
index 0000000..59fa8a0
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/walker/WalkerVisitor.java
@@ -0,0 +1,272 @@
+/*
+ * 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 boolean           visitService      = true ;
+    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 ;
+    }
+
+    private void before(Op op) {
+        if ( beforeVisitor != null )
+            op.visit(beforeVisitor) ;
+    }
+
+    private 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 ;
+        varExprList.forEach((v,e) -> walk(e));
+    }
+
+    // ---- Mode swapping between op and expr. visit=>?walk
+    // XXX
+    @Override
+    public void visitExpr(ExprList exprList) {
+        if ( exprVisitor != null )
+            walk(exprList) ;
+    }
+
+    @Override
+    public void visitExpr(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) {
+        if ( opVisitor != null )
+            op.visit(opVisitor) ;
+    }
+
+    @Override
+    public void visit1(Op1 op) {
+        before(op) ;
+        if ( op.getSubOp() != null )
+            op.getSubOp().visit(this) ;
+        if ( opVisitor != null )
+            op.visit(opVisitor) ;
+        after(op) ;
+    }
+    
+    @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) ;
+    }
+
+    // Special case Ops.
+    // These should call super.visit to do full processing.
+    
+    @Override
+    public void visit(OpService op) {
+        if ( ! visitService )
+            return ;
+        OpVisitorByTypeAndExpr.super.visit(op) ;
+    }
+    
+    // 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() ;
+//    }
+
+    // Shared with ElementWalker - mixin
+    @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.
+                NodeValue.nvNothing.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(ExprAggregator eAgg) {
+        // This is the assignment variable of the aggregation
+        // not a normal variable of an expression.
+
+        // visitAssignVar(eAgg.getAggVar().asVar()) ;
+
+        // XXX Hack for varsMentioned
+
+        if ( exprVisitor != null )
+            eAgg.visit(exprVisitor) ;
+    }
+}
+
+