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) ; }
+ }
+}