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 2013/02/03 19:11:27 UTC

svn commit: r1441944 - in /jena/Scratch/AFS/Jena-Dev/trunk/src/dev: Jena340_TransformFilterNotExists.java Jena384_SubstitueFilterOptimize.java jena340/ jena340/AlgebraQuad2.java jena340/OpWalker2.java jena340/Transformer2.java

Author: andy
Date: Sun Feb  3 18:11:27 2013
New Revision: 1441944

URL: http://svn.apache.org/viewvc?rev=1441944&view=rev
Log:
Investigation for Jena 340 and related issues.

Added:
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena340_TransformFilterNotExists.java
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/AlgebraQuad2.java
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/OpWalker2.java
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/Transformer2.java
Modified:
    jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena384_SubstitueFilterOptimize.java

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena340_TransformFilterNotExists.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena340_TransformFilterNotExists.java?rev=1441944&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena340_TransformFilterNotExists.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena340_TransformFilterNotExists.java Sun Feb  3 18:11:27 2013
@@ -0,0 +1,149 @@
+/**
+ * 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 dev;
+
+import org.apache.jena.atlas.lib.StrUtils ;
+
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.query.QueryFactory ;
+import com.hp.hpl.jena.sparql.algebra.Algebra ;
+import com.hp.hpl.jena.sparql.algebra.Op ;
+import com.hp.hpl.jena.sparql.algebra.Transform ;
+import com.hp.hpl.jena.sparql.algebra.optimize.TransformPropertyFunction ;
+import com.hp.hpl.jena.sparql.sse.SSE ;
+import com.hp.hpl.jena.vocabulary.RDFS ;
+
+import dev.jena340.AlgebraQuad2 ;
+import dev.jena340.Transformer2 ;
+
+public class Jena340_TransformFilterNotExists
+{
+    public static void main(String[] args)
+    {
+        //caseSubQueryExisits() ;
+        caseExistsNG() ;
+        //casePropertyFunctions();
+        System.exit(0) ;
+        
+        // 1 -- PF does not apply onto quads
+        // 2 -- 
+        
+        // Transforms inside NOT EXISTS not applied.
+        // But an external NG is.
+        // Quad form does specially?
+        // Different transformers manage expr {} differently?
+    }
+    
+    public static void caseSubQueryExisits()
+    {
+        // JENA-389
+        // Wrong rewrite of FILTER -- too many //  
+        String qs = StrUtils.strjoinNL(
+                                       "SELECT ?Z # bad",
+                                       //"SELECT * # good",
+                                       "{",
+                                       "  SELECT (COUNT(?x1) as ?Z)",
+                                       "  WHERE {",
+                                       "    ?x1 ?p ?z",
+                                       "    FILTER NOT EXISTS {?x1 ?q ?z}",
+                                       "   }",
+                                       "}") ;
+        System.out.println(qs) ;
+        Query query = QueryFactory.create(qs) ;
+        Op op0 = Algebra.compile(query) ;
+        System.out.println(op0) ;
+        Op op1 = Algebra.optimize(op0) ;
+        System.out.println(op1) ;
+        System.out.println("------------") ;
+    }
+    
+    public static void caseExistsNG()
+    {
+        // JENA-340
+        // GRAPH in EXISTS not seen.
+//        String qs = StrUtils.strjoinNL(
+//                                       "PREFIX : <http://example/>" ,
+//                                       "SELECT *" ,
+//                                       "{" ,
+//                                       "   FILTER NOT EXISTS{" ,
+//                                       "     GRAPH :foo { ?s ?p ?o }",
+//            "}}"    ) ;
+//        System.out.println(qs) ;
+//        System.out.println("------------") ;
+//        Query query = QueryFactory.create(qs) ;
+        //Op op0 = Algebra.compile(query) ;
+        
+        String $  = StrUtils.strjoinNL("(graph <http://example/GGG>",
+                                       "  (filter (notexists",
+                                       "            (graph <http://example/AAA>",
+                                       "             (bgp (triple ?s ?p ?o))))",
+                                       "     (bgp (triple ?s ?p ?o)))",
+                                       ")") ;
+        Op op0 = SSE.parseOp($) ;
+        System.out.println("** algebra") ;
+        System.out.print(op0) ;
+        System.out.println("------------") ;
+        //Op op1 = Algebra.toQuadForm(op0) ;
+        Op op1 = AlgebraQuad2.quadize(op0) ; 
+        System.out.println("** quads") ;
+        System.out.print(op1) ;
+        System.out.println("------------") ;
+        
+    }
+
+    public static void casePropertyFunctions()
+    {
+        // related?
+        // Property functions after quads does not work.
+        // Wjhen in FILTER NOT EXISTS, not transformed.
+        String qs = StrUtils.strjoinNL("PREFIX : <http://example/>",
+                                       "PREFIX rdfs: <"+RDFS.getURI()+">",
+                                       "SELECT * {",
+                                       "  GRAPH :g {" ,
+                                       "     ?s rdfs:member ?o",
+                                       "     FILTER NOT EXISTS { ?s rdfs:member ?o }",
+                                       "  }",
+                                       "}") ;
+
+        System.out.println(qs) ;
+        Query query = QueryFactory.create(qs) ;
+        Op op0 = Algebra.compile(query) ;
+        System.out.println(op0) ;
+        Transform t = new TransformPropertyFunction(ARQ.getContext()) ;
+        Op op3 = Transformer2.transform(t, op0) ;
+        System.out.println(op3) ;
+        
+        
+        if ( true ) return ;
+        
+//        // Wrong order
+//        Op op1 = Algebra.toQuadForm(op0) ;
+//        
+//        // Wrong order
+//        Op op2a = Transformer.transform(t, op0) ;
+//        Op op2b = Transformer.transform(t, op1) ;
+//        System.out.println("**triples") ;
+//        System.out.println(op2a) ;
+//        System.out.println("**quads") ;
+//        System.out.println(op2b) ;
+//        System.out.println("------------") ;
+    }
+
+}

Modified: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena384_SubstitueFilterOptimize.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena384_SubstitueFilterOptimize.java?rev=1441944&r1=1441943&r2=1441944&view=diff
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena384_SubstitueFilterOptimize.java (original)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/Jena384_SubstitueFilterOptimize.java Sun Feb  3 18:11:27 2013
@@ -44,7 +44,6 @@ public class Jena384_SubstitueFilterOpti
         String term = "<http://example/>" ;
         //String term = "'A55'@en" ;
         
-        
         Query query = QueryFactory.create("ASK WHERE { FILTER (?arg = "+term+")}");
         
         System.out.println(query) ;
@@ -67,7 +66,7 @@ public class Jena384_SubstitueFilterOpti
         
         {
             // Order - substitute then optimize.
-            // Right answer wrong reasons.
+            // Wrong answer, right reasons.
             Op op1 = Transformer.transform(new TransformFilterEquality(), op);
             op1 = Substitute.substitute(op1, initialBinding) ;
             System.out.println("optimized-substitute") ;
@@ -75,7 +74,7 @@ public class Jena384_SubstitueFilterOpti
         }
         {
             // Order - substitue then optimize.
-            // Wrong answer, right reasons.
+            // Right answer -- wrong reasons.
             Op op2 = Substitute.substitute(op,initialBinding) ;
             op2 = Transformer.transform(new TransformFilterEquality(), op2);
             System.out.println("substitute-optimize") ;

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/AlgebraQuad2.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/AlgebraQuad2.java?rev=1441944&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/AlgebraQuad2.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/AlgebraQuad2.java Sun Feb  3 18:11:27 2013
@@ -0,0 +1,221 @@
+/*
+ * 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 dev.jena340;
+
+import java.util.ArrayDeque ;
+import java.util.Collection ;
+import java.util.Deque ;
+
+import com.hp.hpl.jena.graph.Node ;
+import com.hp.hpl.jena.sparql.ARQConstants ;
+import com.hp.hpl.jena.sparql.algebra.Op ;
+import com.hp.hpl.jena.sparql.algebra.OpVars ;
+import com.hp.hpl.jena.sparql.algebra.OpVisitor ;
+import com.hp.hpl.jena.sparql.algebra.OpVisitorBase ;
+import com.hp.hpl.jena.sparql.algebra.TransformCopy ;
+import com.hp.hpl.jena.sparql.algebra.op.OpAssign ;
+import com.hp.hpl.jena.sparql.algebra.op.OpBGP ;
+import com.hp.hpl.jena.sparql.algebra.op.OpDatasetNames ;
+import com.hp.hpl.jena.sparql.algebra.op.OpGraph ;
+import com.hp.hpl.jena.sparql.algebra.op.OpPath ;
+import com.hp.hpl.jena.sparql.algebra.op.OpPropFunc ;
+import com.hp.hpl.jena.sparql.algebra.op.OpQuadPattern ;
+import com.hp.hpl.jena.sparql.algebra.op.OpTable ;
+import com.hp.hpl.jena.sparql.core.Quad ;
+import com.hp.hpl.jena.sparql.core.Var ;
+import com.hp.hpl.jena.sparql.core.VarAlloc ;
+import com.hp.hpl.jena.sparql.expr.ExprVar ;
+
+/** Convert an algebra expression into a quad form */
+public class AlgebraQuad2 extends TransformCopy
+{
+    // Transform to a quad form:
+    //   + BGPs go to quad patterns
+    //   + Drop (previous) OpGraph 
+    //   + Paths (complex - simple ones are flatten elsewhere) go to (graph (path ...)) [later: quad paths] 
+
+    // Done as a before/after pair to run the stack of graph nodes for rewrite.
+    // Need to be careful of use of a variable in GRAPH ?g { .. } and then use ?g inside the pattern.
+    
+    private AlgebraQuad2() { }
+
+    public static Op quadize(Op op)
+    {
+        final Deque<QuadSlot> stack = new ArrayDeque<QuadSlot>() ;
+        QuadSlot qSlot = new QuadSlot(Quad.defaultGraphNodeGenerated, Quad.defaultGraphNodeGenerated) ;  
+        stack.push(qSlot) ;             // Starting condition
+        
+        OpVisitor before = new Pusher(stack) ;
+        OpVisitor after = new Popper(stack) ;
+        
+        TransformQuadGraph qg = new TransformQuadGraph(stack) ;
+        return Transformer2.transformSkipService(qg, op, before, after) ;
+    }
+    
+    /** This is the record of the transformation.
+     *  The rewriteGraphName is the node to put in the graph slot of the quad.
+     *  The actualGraphName is the node used in SPARQL.
+     *  If they are the same (by ==), the quadrewrite is OK as is.
+     *  If they are different (and that means they are variables)
+     *  an assign is done after the execution of the graph pattern block. 
+     */
+    private static class QuadSlot
+    {   // Oh scala, where art thou!
+        final Node actualGraphName ;
+        final Node rewriteGraphName ;
+        QuadSlot(Node actualGraphName, Node rewriteGraphName)
+        {
+            this.actualGraphName = actualGraphName ;
+            this.rewriteGraphName = rewriteGraphName ;
+        }
+        @Override public String toString() { return "actualGraphName="+actualGraphName+" rewriteGraphName="+rewriteGraphName ; } 
+    }
+    
+    private static class Pusher extends OpVisitorBase
+    {
+        Deque<QuadSlot> stack ;
+        VarAlloc varAlloc = new VarAlloc(ARQConstants.allocVarQuad) ;
+        Pusher(Deque<QuadSlot> stack) { this.stack = stack ; }
+        @Override
+        public void visit(OpGraph opGraph)
+        {
+            // Name in SPARQL
+            Node gn = opGraph.getNode() ;
+            // Name in rewrite
+            Node gnQuad = gn ;
+            
+            if ( Var.isVar(gn) )
+            {
+                Collection<Var> vars = OpVars.allVars(opGraph.getSubOp()) ;
+                if ( vars.contains(gn) )
+                    gnQuad = varAlloc.allocVar() ;
+            }
+            stack.push(new QuadSlot(gn, gnQuad)) ;
+        }
+    }
+    
+    private static class Popper extends OpVisitorBase
+    {
+        Deque<QuadSlot> stack ;
+        Popper(Deque<QuadSlot> stack) { this.stack = stack ; }
+        @Override
+        public void visit(OpGraph opGraph)
+        {
+            // The final work is done in the main vistor, 
+            // which is called after the subnode has been 
+            // rewritten.
+            stack.pop() ;
+        }
+    }
+
+    private static class TransformQuadGraph extends TransformCopy
+    {
+        private Deque<QuadSlot> tracker ;
+
+        public TransformQuadGraph(Deque<QuadSlot> tracker) { this.tracker = tracker ; }
+        
+        private Node getNode() { return tracker.peek().rewriteGraphName ; }
+
+        @Override
+        public Op transform(OpGraph opGraph, Op op)
+        {
+            // ?? Could just leave the (graph) in place always - just rewrite BGPs. 
+            boolean noPattern = false ;
+            
+            /* One case to consider is when the pattern for the GRAPH
+             * statement includes uses the variable inside the GRAPH clause. 
+             * In this case, we must rename away the inner variable
+             * to allow stream execution via index joins, 
+             * and then put back the value via an assign.
+             * (This is what QueryIterGraph does using a streaming join
+             * for triples)
+             */
+
+            // Note: op is already quads by this point.
+            // Must test scoping by the subOp of GRAPH
+            
+            QuadSlot qSlot = tracker.peek() ;
+            Node actualName= qSlot.actualGraphName ;
+            Node rewriteName= qSlot.rewriteGraphName ; 
+            
+            if ( OpBGP.isBGP(op) )
+            {
+                // Empty BGP
+                if ( ((OpBGP)op).getPattern().isEmpty() )
+                    noPattern = true ;
+            }
+            else if ( op instanceof OpTable )
+            {
+                // Empty BGP compiled to a unit table
+                if ( ((OpTable)op).isJoinIdentity() )
+                    noPattern = true ;
+            }
+            
+            if ( noPattern )
+            {
+                // The case of something like:
+                // GRAPH ?g {} or GRAPH <v> {}
+                // which are ways of accessing the names in the dataset.
+                return new OpDatasetNames(opGraph.getNode()) ;
+            }
+            
+            if ( actualName != rewriteName )
+                op = OpAssign.assign(op, Var.alloc(actualName), new ExprVar(rewriteName)) ;
+
+            // Drop (graph...) because inside nodes
+            // have been converted to quads.
+            return op ;
+        }
+        
+        @Override
+        public Op transform(OpPropFunc opPropFunc, Op subOp)
+        {
+            if ( opPropFunc.getSubOp() != subOp )
+                opPropFunc = new OpPropFunc(opPropFunc.getProperty(), opPropFunc.getSubjectArgs(), opPropFunc.getObjectArgs(), subOp) ;
+            // Put the (graph) back round it so the property function works on the named graph.
+            return new OpGraph(getNode() , opPropFunc) ;
+        }
+        
+        @Override
+        public Op transform(OpPath opPath)
+        {
+            // Put the (graph) back round it
+            // ?? inc default graph node.
+            return new OpGraph(getNode() , opPath) ;
+            // Does not get removed by transform above because this is
+            // not the OpGraph that gets walked by the transform.  
+        }
+        
+        @Override
+        public Op transform(OpBGP opBGP)
+        {
+            return new OpQuadPattern(getNode(), opBGP.getPattern()) ;
+        }
+        
+//        static class X extends ExprTransformBase
+//        {
+//            @Override public Expr transform(ExprFunctionOp funcOp, ExprList args, Op opArg)
+//            { 
+//                System.out.println("****") ;
+//                
+//                return funcOp ; }
+//        }
+
+    }    
+}

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/OpWalker2.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/OpWalker2.java?rev=1441944&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/OpWalker2.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/OpWalker2.java Sun Feb  3 18:11:27 2013
@@ -0,0 +1,150 @@
+/*
+ * 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 dev.jena340;
+
+import java.util.Iterator ;
+
+import com.hp.hpl.jena.sparql.algebra.Op ;
+import com.hp.hpl.jena.sparql.algebra.OpVisitor ;
+import com.hp.hpl.jena.sparql.algebra.OpVisitorByType ;
+import com.hp.hpl.jena.sparql.algebra.op.* ;
+
+/** Apply a visitor to the whole structure of Ops, recursively.
+ *  Visit sub Op before the current level
+ */
+
+public class OpWalker2
+{
+    public static void walk(Op op, OpVisitor visitor)
+    {
+        walk(new WalkerVisitor(visitor, null, null), op) ;
+    }
+    
+    public static void walk(Op op, OpVisitor visitor, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+    {
+        walk(new WalkerVisitor(visitor, beforeVisitor, afterVisitor), op) ;
+    }
+    
+    public static void walk(WalkerVisitor walkerVisitor, Op op)
+    {
+        op.visit(walkerVisitor) ;
+    }
+    
+    static class WalkerVisitor extends OpVisitorByType
+    {
+        private final OpVisitor beforeVisitor ;
+        private final OpVisitor afterVisitor ;
+        protected final OpVisitor visitor ;
+
+        public WalkerVisitor(OpVisitor visitor, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+        { 
+            this.visitor = visitor ;
+            this.beforeVisitor = beforeVisitor ;
+            this.afterVisitor = afterVisitor ;
+        }
+
+        public WalkerVisitor(OpVisitor visitor) { this(visitor, null, null) ; }
+        
+        protected final void before(Op op)
+        { 
+            if ( beforeVisitor != null )
+                op.visit(beforeVisitor) ;
+        }
+
+        protected final void after(Op op)
+        {
+            if ( afterVisitor != null )
+                op.visit(afterVisitor) ;
+        }
+        
+        @Override
+        protected void visit0(Op0 op)         
+        {  
+            before(op) ;
+            if ( visitor != null ) op.visit(visitor) ;
+            after(op) ;
+        }
+
+        @Override
+        protected void visit1(Op1 op)
+        {
+            before(op) ;
+            if ( op.getSubOp() != null ) op.getSubOp().visit(this) ;
+            if ( visitor != null ) op.visit(visitor) ; 
+            after(op) ;
+        }
+
+        @Override
+        protected void visitFilter(OpFilter op)
+        {
+            // Treat OpFilter as an Op1
+            visit1(op) ;
+
+          // WRONG  
+//            before(op) ;
+//            
+//            ExprVisitor vb = ( beforeVisitor == null ) ? null : new ExprVisitorApplyVisitor(beforeVisitor) ; 
+//            ExprVisitor v  = new ExprVisitorApplyVisitor(visitor) ;
+//            ExprVisitor va = ( afterVisitor == null ) ? null : new ExprVisitorApplyVisitor(afterVisitor) ;
+//            
+//            for ( Expr e : op.getExprs().getList() )
+//            {
+//                if ( vb != null ) e.visit(vb) ;
+//                e.visit(v) ;
+//                if ( va != null ) e.visit(va) ;
+//            }
+//            
+//            if ( op.getSubOp() != null ) op.getSubOp().visit(this) ;
+//            if ( visitor != null ) op.visit(visitor) ; 
+//            after(op) ;
+
+        }
+        
+        @Override
+        protected void visit2(Op2 op)
+        {
+            before(op) ;
+            if ( op.getLeft() != null ) op.getLeft().visit(this) ;
+            if ( op.getRight() != null ) op.getRight().visit(this) ;
+            if ( visitor != null ) op.visit(visitor) ;      
+            after(op) ; 
+        }
+        
+        @Override
+        protected void visitN(OpN op)
+        {
+            before(op) ;
+            for ( Iterator<Op> iter = op.iterator() ; iter.hasNext() ; )
+            {
+                Op sub = iter.next() ;
+                sub.visit(this) ;
+            }
+            if ( visitor != null ) op.visit(visitor) ;
+            after(op) ;
+        }
+
+        @Override
+        protected void visitExt(OpExt op)
+        {
+            before(op) ;
+            if ( visitor != null ) op.visit(visitor) ;
+            after(op) ;
+        }
+    }
+}

Added: jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/Transformer2.java
URL: http://svn.apache.org/viewvc/jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/Transformer2.java?rev=1441944&view=auto
==============================================================================
--- jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/Transformer2.java (added)
+++ jena/Scratch/AFS/Jena-Dev/trunk/src/dev/jena340/Transformer2.java Sun Feb  3 18:11:27 2013
@@ -0,0 +1,447 @@
+/*
+ * 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 dev.jena340;
+
+import java.util.* ;
+
+import org.apache.jena.atlas.logging.Log ;
+
+import com.hp.hpl.jena.query.SortCondition ;
+import com.hp.hpl.jena.sparql.algebra.* ;
+import com.hp.hpl.jena.sparql.algebra.op.* ;
+import com.hp.hpl.jena.sparql.algebra.optimize.ExprTransformApplyTransform ;
+import com.hp.hpl.jena.sparql.core.Var ;
+import com.hp.hpl.jena.sparql.core.VarExprList ;
+import com.hp.hpl.jena.sparql.expr.Expr ;
+import com.hp.hpl.jena.sparql.expr.ExprAggregator ;
+import com.hp.hpl.jena.sparql.expr.ExprList ;
+import com.hp.hpl.jena.sparql.expr.ExprTransformer ;
+import com.hp.hpl.jena.sparql.expr.aggregate.Aggregator ;
+
+import dev.jena340.OpWalker2.WalkerVisitor ;
+
+/** A botton-top application of a transformation of SPARQL algebra */  
+public class Transformer2
+{
+    private static Transformer2 singleton = new Transformer2();
+    
+    // TopQuadrant extend Transformer for use in their SPARQL debugger.
+    /** Get the current transformer */
+    public static Transformer2 get() { return singleton; }
+    
+    /** Set the current transformer - use with care */
+    public static void set(Transformer2 value) { Transformer2.singleton = value; }
+    
+    /** Transform an algebra expression */
+    public static Op transform(Transform transform, Op op)
+    { return get().transformation(transform, op, null, null) ; }
+    
+    public static Op transform(Transform transform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+    {
+        return get().transformation(transform, op, beforeVisitor, afterVisitor) ;
+    }
+
+    /** Transform an algebra expression except skip (leave alone) any OpService nodes */
+    public static Op transformSkipService(Transform transform, Op op)
+    {
+        return transformSkipService(transform, op, null, null) ; 
+    }
+
+    /** Transform an algebra expression except skip (leave alone) any OpService nodes */
+    public static Op transformSkipService(Transform transform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+    {
+        // Skip SERVICE
+        if ( true )
+        {
+            // Simplest way but still walks the OpService subtree (and throws away the transformation).
+            Transform walker = new TransformSkipService(transform) ;
+            return Transformer2.transform(walker, op, beforeVisitor, afterVisitor) ;
+        }
+        else
+        {
+            // Don't transform OpService and don't walk the sub-op 
+            ApplyTransformVisitorServiceAsLeaf v = new ApplyTransformVisitorServiceAsLeaf(transform) ;
+            WalkerVisitorSkipService walker = new WalkerVisitorSkipService(v, beforeVisitor, afterVisitor) ;
+            OpWalker2.walk(walker, op) ;
+            return v.result() ;
+        }
+    }
+    
+    /** Transform an Op - not recursively */ 
+    public static Op transformOne(Transform transform, Op op)
+    {
+        OpTransformApplyOne visitor = new OpTransformApplyOne(transform) ;
+        op.visit(visitor) ;
+        return visitor.result ;
+    }
+    
+    // To allow subclassing this class, we use a singleton pattern 
+    // and theses protected methods.
+    protected Op transformation(Transform transform, Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+    {
+        ApplyTransformVisitor v = new ApplyTransformVisitor(transform) ;
+        return transformation(v, op, beforeVisitor, afterVisitor) ;
+    }
+    
+    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 ;
+        }
+        return applyTransformation(transformApply, op, beforeVisitor, afterVisitor) ;
+    }
+
+    /** The primitive operation to apply a transformation to an Op */
+    protected Op applyTransformation(ApplyTransformVisitor transformApply,
+                                     Op op, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+    {
+        OpWalker2.walk(op, transformApply, beforeVisitor, afterVisitor) ;
+        Op r = transformApply.result() ;
+        return r ;
+    }
+
+    
+    protected Transformer2() { }
+    
+    public static
+    class ApplyTransformVisitor extends OpVisitorByType
+    {
+        protected final Transform transform ;
+        private final ExprTransformApplyTransform exprTransform ;
+
+        private final Deque<Op> stack = new ArrayDeque<Op>() ;
+        protected final Op pop() 
+        { return stack.pop(); }
+        
+        protected final void push(Op op)
+        { 
+            // Including nulls
+            stack.push(op) ;
+        }
+        
+        public ApplyTransformVisitor(Transform transform)
+        { 
+            this.transform = transform ;
+            this.exprTransform = new ExprTransformApplyTransform(transform) ;
+
+        }
+        
+        final Op result()
+        {
+            if ( stack.size() != 1 )
+                Log.warn(this, "Stack is not aligned") ;
+            return pop() ; 
+        }
+    
+        // ----
+        // Algebra operations that involve an Expr, and so might include NOT EXISTS 
+
+        @Override
+        public void visit(OpOrder opOrder)
+        {
+            List<SortCondition> conditions = opOrder.getConditions() ;
+            List<SortCondition> conditions2 = new ArrayList<SortCondition>() ;
+            boolean changed = false ;
+
+            for ( SortCondition sc : conditions )
+            {
+                Expr e = sc.getExpression() ;
+                Expr e2 = ExprTransformer.transform(exprTransform, 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.assignDirect(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.extendDirect(opExtend.getSubOp(), varExpr2) ;
+            visit1(opExtend2) ;
+        }
+        
+        private VarExprList process(VarExprList varExpr)
+        {
+            List<Var> vars = varExpr.getVars() ;
+            VarExprList varExpr2 = new VarExprList() ;
+            boolean changed = false ;
+            for ( Var v : vars )
+            {
+                Expr e = varExpr.getExpr(v) ;
+                Expr e2 =  e ;
+                if ( e != null )
+                    e2 = ExprTransformer.transform(exprTransform, e) ;
+                if ( e2 == null )
+                    varExpr2.add(v) ;
+                else
+                    varExpr2.add(v, e2) ; 
+                if ( e != e2 )
+                    changed = true ;
+            }
+            if ( ! changed ) return varExpr ;
+            return varExpr2 ;
+        }
+
+        @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<ExprAggregator>() ;
+            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 = ExprTransformer.transform(exprTransform, eVar) ;
+                if ( eVar != eVar2 )
+                    changed = true ;
+
+                // The Aggregator expression
+                Expr e = aggregator.getExpr() ;
+                Expr e2 = e ;
+                if ( e != null )    // Null means "no relevant expression" e.g. COUNT(*)
+                    ExprTransformer.transform(exprTransform, 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
+        protected void visit0(Op0 op)
+        {
+            push(op.apply(transform)) ;
+        }
+        
+        @Override
+        protected void visit1(Op1 op)
+        {
+            Op subOp = null ;
+            if ( op.getSubOp() != null )
+                subOp = pop() ;
+            push(op.apply(transform, subOp)) ;
+        }
+
+        @Override
+        protected 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() ;
+            if ( op.getLeft() != null )
+                left = pop() ;
+            Op opX = op.apply(transform, left, right) ; 
+            push(opX) ;
+        }
+        
+        @Override
+        protected void visitN(OpN op)
+        {
+            List<Op> x = new ArrayList<Op>(op.size()) ;
+            
+            for ( Iterator<Op> iter = op.iterator() ; iter.hasNext() ; )
+            {
+                Op sub = iter.next() ;
+                Op r = pop() ;
+                // Skip nulls.
+                if ( r != null )
+                    // Add in reverse.
+                    x.add(0, r) ;
+            }
+            Op opX = op.apply(transform, x) ;  
+            push(opX) ;
+        }
+        
+        @Override
+        protected void visitFilter(OpFilter opFilter) {
+            ExprList ex = new ExprList() ;
+            boolean changed = false ;
+            for ( Expr e : opFilter.getExprs() )
+            {
+                Expr e2 = ExprTransformer.transform(exprTransform, e) ;
+                ex.add(e2) ;
+                if ( e != e2 )
+                    changed = true ;
+            }
+            OpFilter f = opFilter ;
+            if ( changed )
+                f = (OpFilter)OpFilter.filter(ex, opFilter.getSubOp()) ;
+            // As Op1.
+            Op subOp = null ;
+            if ( f.getSubOp() != null )
+                subOp = pop() ;
+            push(f.apply(transform, subOp)) ;
+        }
+        
+        @Override
+        protected void visitExt(OpExt op)
+        {
+            push(transform.transform(op)) ;
+        }
+    }
+    
+    // --------------------------------
+    // Transformations that avoid touching SERVICE.
+    // Modified classes to avoid transforming SERVICE/OpService.
+    // Plan A: In the application of the transform, skip OpService. 
+    
+    /** Treat OpService as a leaf of the tree */
+    static class ApplyTransformVisitorServiceAsLeaf extends ApplyTransformVisitor
+    {
+        public ApplyTransformVisitorServiceAsLeaf(Transform transform)
+        {
+            super(transform) ;
+        }
+        
+        @Override
+        public void visit(OpService op)
+        {
+            // Treat as a leaf that does not change.
+            push(op) ;
+        }
+    }
+    
+    // Plan B: The walker skips walking into OpService nodes.
+    
+    /** Don't walk down an OpService sub-operation */
+    static class WalkerVisitorSkipService extends WalkerVisitor
+    {
+        public WalkerVisitorSkipService(OpVisitor visitor, OpVisitor beforeVisitor, OpVisitor afterVisitor)
+        {
+            super(visitor, beforeVisitor, afterVisitor) ;
+        }
+        
+        public WalkerVisitorSkipService(OpVisitor visitor)
+        {
+            super(visitor) ;
+        }
+        
+        @Override
+        public void visit(OpService op)
+        { 
+            before(op) ;
+            // visit1 code from WalkerVisitor
+//            if ( op.getSubOp() != null ) op.getSubOp().visit(this) ;
+            
+            // Just visit the OpService node itself.
+            // The transformer needs to push the code as a result (see ApplyTransformVisitorSkipService)
+            if ( visitor != null ) op.visit(visitor) ;
+            
+            after(op) ;
+        }
+        
+    }
+    
+    // --------------------------------
+    // Safe: ignore transformation of OpService and return the original.
+    // Still walks the sub-op of OpService 
+    static class TransformSkipService extends TransformWrapper
+    {
+        public TransformSkipService(Transform transform)
+        {
+            super(transform) ;
+        }
+        
+        @Override
+        public Op transform(OpService opService, Op subOp)
+        { return opService ; } 
+    }
+    
+    static class OpTransformApplyOne extends OpVisitorByType
+    {
+        private final Transform transform ;
+        Op result ;
+
+        OpTransformApplyOne(Transform transform)
+        {
+            this.transform = transform ;
+        }
+
+        @Override
+        protected void visitN(OpN op)
+        { result = op.apply(transform, op.getElements()) ; }
+
+        @Override
+        protected void visit2(Op2 op)
+        { result = op.apply(transform, op.getLeft(), op.getRight()) ; }
+
+        @Override
+        protected void visit1(Op1 op)
+        { result = op.apply(transform, op.getSubOp()) ; }
+
+        @Override
+        protected void visit0(Op0 op)
+        { result = op.apply(transform) ; }
+
+        @Override
+        protected void visitFilter(OpFilter op)
+        { result = op.apply(transform, op.getSubOp()) ; }
+        
+        @Override
+        protected void visitExt(OpExt op)
+        { op.apply(transform) ; }
+    }
+}