Repository: jena Updated Branches: refs/heads/master cd1c21ff6 -> 6f12cdbd3
JENA-1167: Cope with filter-before-defined variables Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/6f12cdbd Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/6f12cdbd Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/6f12cdbd Branch: refs/heads/master Commit: 6f12cdbd3964adfddfdaddd3092970a9ea5460aa Parents: cd1c21f Author: Andy Seaborne <[email protected]> Authored: Mon Jun 6 12:14:42 2016 +0100 Committer: Andy Seaborne <[email protected]> Committed: Mon Jun 6 12:14:42 2016 +0100 ---------------------------------------------------------------------- .../jena/sparql/engine/main/JoinClassifier.java | 59 +++++++------ .../sparql/engine/main/LeftJoinClassifier.java | 47 ++++++----- .../jena/sparql/engine/main/VarFinder.java | 88 +++++++++++++++++--- .../jena/sparql/algebra/TestClassify.java | 23 +++-- .../jena/sparql/algebra/TestTransformQuads.java | 3 +- .../jena/sparql/algebra/TestVarFinder.java | 49 ++++++----- 6 files changed, 182 insertions(+), 87 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/JoinClassifier.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/JoinClassifier.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/JoinClassifier.java index 22c3f39..00b06ea 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/JoinClassifier.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/JoinClassifier.java @@ -87,40 +87,45 @@ public class JoinClassifier System.err.println(rightOp) ; } - // Need only check left/rght. + // Need only check left/right. VarFinder vfLeft = VarFinder.process(leftOp) ; Set<Var> vLeftFixed = vfLeft.getFixed() ; Set<Var> vLeftOpt = vfLeft.getOpt() ; // Set<Var> vLeftFilter = vfLeft.getFilter() ; - if ( print ) - System.err.println("Left/fixed: " + vLeftFixed) ; - if ( print ) - System.err.println("Left/opt: " + vLeftOpt) ; - // if (print) System.err.println("Left/filter: " + vLeftFilter) ; - - VarFinder vfRight = VarFinder.process(rightOp) ; - Set<Var> vRightFixed = vfRight.getFixed() ; - Set<Var> vRightOpt = vfRight.getOpt() ; - Set<Var> vRightFilter = vfRight.getFilter() ; - Set<Var> vRightAssign = vfRight.getAssign() ; - - if ( print ) - System.err.println("Right/fixed: " + vRightFixed) ; - if ( print ) - System.err.println("Right/opt: " + vRightOpt) ; - if ( print ) - System.err.println("Right/filter: " + vRightFilter) ; - if ( print ) - System.err.println("Right/assign: " + vRightAssign) ; - - // Step 1 : remove any variable definitely fixed from the floating sets + if ( print ) { + System.err.println("Left") ; + vfLeft.print(System.err) ; + } + VarFinder vfRight = VarFinder.process(rightOp) ; + if ( print ) { + System.err.println("Right") ; + vfRight.print(System.err) ; + } + + Set<Var> vRightFixed = vfRight.getFixed() ; + Set<Var> vRightOpt = vfRight.getOpt() ; + Set<Var> vRightFilter = vfRight.getFilter() ; + Set<Var> vRightFilterOnly = vfRight.getFilterOnly() ; + Set<Var> vRightAssign = vfRight.getAssign() ; + + // Step 1 : If there are any variables in the LHS that are filter-only or filter-before define, + // we can't do anything. + if ( ! vRightFilterOnly.isEmpty() ) { + // A tigher condition is to see of any of the getFilterOnly are possible from the + // left. If not, then we can still use a sequence. + // But an outer sequence may push arbitrary here so play safe on the argument + // this is a relative uncommon case. + return false ; + } + + // Step 2 : remove any variable definitely fixed from the floating sets // because the nature of the "join" will deal with that. vLeftOpt = SetUtils.difference(vLeftOpt, vLeftFixed) ; vRightOpt = SetUtils.difference(vRightOpt, vRightFixed) ; // And also assign/filter variables in the RHS which are always defined - // in the - // RHS. Leaves any potentially free variables in RHS filter. + // in the RHS. + // Leaves any potentially free variables in RHS filter. vRightFilter = SetUtils.difference(vRightFilter, vRightFixed) ; vRightAssign = SetUtils.difference(vRightAssign, vRightFixed) ; @@ -156,7 +161,7 @@ public class JoinClassifier if ( print ) System.err.println("Case 1 = " + bad1) ; - // Case 2 : a filter in the RHS is uses a variable from the LHS (whether + // Case 3 : a filter in the RHS is uses a variable from the LHS (whether // fixed or optional) // Scoping means we must hide the LHS value form the RHS // Could mask (??). For now, we stop linearization of this join. @@ -167,7 +172,7 @@ public class JoinClassifier if ( print ) System.err.println("Case 2 = " + bad2) ; - // Case 3 : an assign in the RHS uses a variable not introduced + // Case 4 : an assign in the RHS uses a variable not introduced // Scoping means we must hide the LHS value from the RHS // Think this may be slightly relaxed, using variables in an http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/LeftJoinClassifier.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/LeftJoinClassifier.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/LeftJoinClassifier.java index 9608334..ad68c09 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/LeftJoinClassifier.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/LeftJoinClassifier.java @@ -47,12 +47,11 @@ public class LeftJoinClassifier // Need also worry about filters in the right (not in the LJ condition) // which use vars from the left. - static public boolean isLinear(OpLeftJoin op) - { + static public boolean isLinear(OpLeftJoin op) { return isLinear(op.getLeft(), op.getRight()) ; } - static public boolean isLinear(Op left, Op right) - { + + static public boolean isLinear(Op left, Op right) { left = effectiveOp(left) ; right = effectiveOp(right) ; @@ -62,34 +61,43 @@ public class LeftJoinClassifier return false ; Set<Var> leftVars = OpVars.visibleVars(left) ; + if ( print ) { + System.err.println("Left") ; + System.err.println(leftVars) ; + } VarFinder vf = VarFinder.process(right) ; + if ( print ) { + System.err.println("Right") ; + vf.print(System.err) ; + } + + // Case 1 : If there are any variables in the LHS that are + // filter-only or filter-before define, we can't do anything. + if ( ! vf.getFilterOnly().isEmpty() ) { + // A tigher condition is to see of any of the getFilterOnly are possible from the + // left. If not, then we can still use a sequence. + // But an outer sequence may push arbitrary here so play safe on the argument + // this is a relative uncommon case. + return false ; + } Set<Var> optRight = vf.getOpt() ; Set<Var> fixedRight = vf.getFixed() ; Set<Var> filterVarsRight = vf.getFilter() ; Set<Var> assignVarsRight = vf.getAssign() ; - - if (print) { - System.err.println("Left/visible: " + leftVars) ; - System.err.println("Right/fixed: " + fixedRight) ; - System.err.println("Right/opt: " + optRight) ; - System.err.println("Right/filter: " + filterVarsRight) ; - System.err.println("Right/assign: " + assignVarsRight) ; - } - - // Case 1 + // Case 2 // A variable is nested in an optional on the RHS and on the LHS // Cannot linearize as we must preserve scope boolean b1 = SetUtils.intersectionP(leftVars, optRight) ; if (print) System.err.println("Case 1 - " + b1); - // Case 2 + // Case 3 // A variable mentioned in a filter within the RHS already exists on the LHS // Cannot linearize as would change filter evaluation boolean b2 = SetUtils.intersectionP(leftVars, filterVarsRight) ; if (print) System.err.println("Case 2 - " + b2); - // Case 3 + // Case 4 // A variable mentioned in the assign is not introduced on the RHS // Cannot linearize as would change bind evaluation Set<Var> unsafeAssign = new HashSet<>(assignVarsRight); @@ -101,8 +109,7 @@ public class LeftJoinClassifier return ! b1 && ! b2 && ! b3 ; } - static public Set<Var> nonLinearVars(OpLeftJoin op) - { + static public Set<Var> nonLinearVars(OpLeftJoin op) { Op left = effectiveOp(op.getLeft()) ; Op right = effectiveOp(op.getRight()) ; Set<Var> leftVars = OpVars.visibleVars(left) ; @@ -111,11 +118,9 @@ public class LeftJoinClassifier return SetUtils.intersection(leftVars, optRight) ; } - private static Op effectiveOp(Op op) - { + private static Op effectiveOp(Op op) { if (op instanceof OpExt) op = ((OpExt) op).effectiveOp() ; return op ; } - } http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java index 258f7ea..be6c8e0 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/main/VarFinder.java @@ -24,6 +24,7 @@ import static org.apache.jena.sparql.util.VarUtils.addVarsFromQuad ; import static org.apache.jena.sparql.util.VarUtils.addVarsFromTriple ; import static org.apache.jena.sparql.util.VarUtils.addVarsFromTriplePath ; +import java.io.PrintStream ; import java.util.HashSet ; import java.util.List ; import java.util.Set ; @@ -69,11 +70,31 @@ public class VarFinder private VarFinder(Op op) { varUsageVisitor = VarUsageVisitor.apply(op) ; } - public Set<Var> getOpt() { return varUsageVisitor.optDefines ; } - public Set<Var> getFilter() { return varUsageVisitor.filterMentions ; } - public Set<Var> getAssign() { return varUsageVisitor.assignMentions ; } - public Set<Var> getFixed() { return varUsageVisitor.defines ; } + public Set<Var> getOpt() { return varUsageVisitor.optDefines ; } + public Set<Var> getFilter() { return varUsageVisitor.filterMentions ; } + public Set<Var> getFilterOnly() { return varUsageVisitor.filterMentionsOnly ; } + public Set<Var> getAssign() { return varUsageVisitor.assignMentions ; } + public Set<Var> getFixed() { return varUsageVisitor.defines ; } + @Override + public String toString() { + StringBuilder sb = new StringBuilder() ; + sb.append("Fixed:").append(getFixed()) ; + sb.append(", Filter:").append(getFilter()) ; + sb.append(", Filter2:").append(getFilterOnly()) ; + sb.append(", Opt:").append(getOpt()) ; + sb.append(", Assign:").append(getAssign()) ; + return sb.toString() ; + } + + public void print(PrintStream out) { + out.printf(" Filter: %s\n", getFilter()) ; + out.printf(" Filter2: %s\n", getFilterOnly()) ; + out.printf(" Fixed : %s\n", getFixed()) ; + out.printf(" Opt: %s\n", getOpt()) ; + out.printf(" Assign: %s\n", getAssign()) ; + } + private static class VarUsageVisitor //extends OpVisitorBase implements OpVisitor @@ -84,22 +105,25 @@ public class VarFinder return v; } - Set<Var> defines = null; - Set<Var> optDefines = null; - Set<Var> filterMentions = null; - Set<Var> assignMentions = null; + Set<Var> defines = null ; + Set<Var> optDefines = null ; + Set<Var> filterMentions = null ; + Set<Var> filterMentionsOnly = null ; // Mentioned in filter before defined. + Set<Var> assignMentions = null ; VarUsageVisitor() { defines = new HashSet<>(); optDefines = new HashSet<>(); filterMentions = new HashSet<>(); + filterMentionsOnly = new HashSet<>(); assignMentions = new HashSet<>(); } - VarUsageVisitor(Set<Var> _defines, Set<Var> _optDefines, Set<Var> _filterMentions, Set<Var> _assignMentions) { + VarUsageVisitor(Set<Var> _defines, Set<Var> _optDefines, Set<Var> _filterMentions, Set<Var> _filterMentions2, Set<Var> _assignMentions) { defines = _defines; optDefines = _optDefines; filterMentions = _filterMentions; + filterMentionsOnly = _filterMentions2 ; assignMentions = _assignMentions; } @@ -156,6 +180,7 @@ public class VarFinder defines.addAll(usage.defines); optDefines.addAll(usage.optDefines); filterMentions.addAll(usage.filterMentions); + filterMentionsOnly.addAll(usage.filterMentionsOnly); assignMentions.addAll(usage.assignMentions); } @@ -177,13 +202,22 @@ public class VarFinder private void mergeMinusDiff(Op left, Op right) { mergeVars(left) ; VarUsageVisitor usage = VarUsageVisitor.apply(right); - // Everything in the right side is really a filter. + // Everything in the right side is really a filter. + combinefilterMentions(this, usage.filterMentionsOnly) ; + filterMentions.addAll(usage.defines) ; filterMentions.addAll(usage.optDefines) ; filterMentions.addAll(usage.filterMentions) ; filterMentions.addAll(usage.assignMentions) ; } + private static void combinefilterMentions(VarUsageVisitor usage, Set<Var> mentions) { + for ( Var v : mentions ) { + if ( ! usage.defines.contains(v) ) + usage.filterMentionsOnly.add(v) ; + } + } + @Override public void visit(OpConditional opLeftJoin) { leftJoin(opLeftJoin.getLeft(), opLeftJoin.getRight(), null); @@ -196,11 +230,13 @@ public class VarFinder defines.addAll(leftUsage.defines); optDefines.addAll(leftUsage.optDefines); filterMentions.addAll(leftUsage.filterMentions); + filterMentionsOnly.addAll(leftUsage.filterMentionsOnly); assignMentions.addAll(leftUsage.assignMentions); optDefines.addAll(rightUsage.defines); // Asymmetric. optDefines.addAll(rightUsage.optDefines); filterMentions.addAll(rightUsage.filterMentions); + filterMentionsOnly.addAll(rightUsage.filterMentionsOnly); assignMentions.addAll(rightUsage.assignMentions); // Remove any definites that are in the optionals @@ -208,8 +244,20 @@ public class VarFinder optDefines.removeAll(leftUsage.defines); // And the associated filter. - if ( exprs != null ) + if ( exprs != null ) { + processExpr(exprs, rightUsage.defines) ; exprs.varsMentioned(filterMentions); + } + } + + // additionalDefines - set of variables which are defined is the filter is executed. + private void processExpr(ExprList exprs, Set<Var> additionalDefines) { + Set<Var> vars = exprs.getVarsMentioned() ; + filterMentions.addAll(vars) ; + for ( Var v : vars ) { + if ( ! defines.contains(v) && (additionalDefines == null || ! additionalDefines.contains(v) ) ) + filterMentionsOnly.add(v) ; + } } @Override @@ -231,6 +279,9 @@ public class VarFinder filterMentions.addAll(usage1.filterMentions); filterMentions.addAll(usage2.filterMentions); + filterMentionsOnly.addAll(usage1.filterMentionsOnly); + filterMentionsOnly.addAll(usage2.filterMentionsOnly); + assignMentions.addAll(usage1.assignMentions); assignMentions.addAll(usage2.assignMentions); } @@ -248,8 +299,8 @@ public class VarFinder @Override public void visit(OpFilter opFilter) { - opFilter.getExprs().varsMentioned(filterMentions); opFilter.getSubOp().visit(this); + processExpr(opFilter.getExprs(), null) ; } @Override @@ -279,10 +330,12 @@ public class VarFinder subUsage.defines.retainAll(vars); subUsage.optDefines.retainAll(vars); subUsage.filterMentions.retainAll(vars) ; + subUsage.filterMentionsOnly.retainAll(vars) ; subUsage.assignMentions.retainAll(vars) ; defines.addAll(subUsage.defines); optDefines.addAll(subUsage.optDefines); filterMentions.addAll(subUsage.filterMentions); + filterMentionsOnly.addAll(subUsage.filterMentionsOnly); assignMentions.addAll(subUsage.assignMentions); } @@ -353,5 +406,16 @@ public class VarFinder defines.addAll(vars) ; } } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder() ; + sb.append("Fixed:").append(defines) ; + sb.append(", Filter:").append(filterMentions) ; + sb.append(", Filter2:").append(filterMentionsOnly) ; + sb.append(", Opt:").append(optDefines) ; + sb.append(", Assign:").append(assignMentions) ; + return sb.toString() ; + } } } http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestClassify.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestClassify.java b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestClassify.java index 17e7692..7cea0a2 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestClassify.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestClassify.java @@ -39,8 +39,12 @@ public class TestClassify extends BaseTest @Test public void testClassify_Join_03() { classifyJ("{?s :p :o . { ?s :p ?o FILTER(?o) } }", true) ; } + // JENA-1167 + // This actually safe in thsi case but as general component. + // the JoinClassifier is not clever enough to know that ?o is completely + // unbound. It may be boudn bu whatever feeds into the potential sequence. @Test public void testClassify_Join_04() - { classifyJ("{?s :p :o . { ?s :p :o FILTER(?o) } }", true) ; } + { classifyJ("{?s :p :o . { ?s :p :o FILTER(?o) } }", false) ; } @Test public void testClassify_Join_05() { classifyJ("{?s :p :o . { ?x :p :o FILTER(?s) } }", false) ; } @@ -61,6 +65,9 @@ public class TestClassify extends BaseTest @Test public void testClassify_Join_10() { classifyJ("{ { ?x :p :o FILTER(?s) } ?s :p :o }", true) ; } + + // OPTIONAL nested inside {} so it is a join of the LHS and the {}-RHS. + // Not safe: ?s // Other parts of RHS may restrict ?s to things that can't match the LHS. @Test public void testClassify_Join_11() @@ -74,9 +81,15 @@ public class TestClassify extends BaseTest { classifyJ("{?s :p :o . { ?x :p :o OPTIONAL { :s :p :o FILTER(?x) } } }", true) ; } @Test public void testClassify_Join_14() - { classifyJ("{?s :p :o . { OPTIONAL { :s :p :o FILTER(?o) } } }", true) ; } + { classifyJ("{?s :p :o . { OPTIONAL { :s :p :o FILTER(?o) } } }", false) ; } + + @Test public void testClassify_Join_14a() + { classifyJ("{?s :p :o . { OPTIONAL { :s :p ?o FILTER(?o) } } }", true) ; } - @Test public void testClassify_Join_15() + @Test public void testClassify_Join_14b() + { classifyJ("{?s :p ?o . { OPTIONAL { :s :p :o FILTER(?o) } } }", false) ; } + + @Test public void testClassify_Join_15() { classifyJ("{?s :p :o . { OPTIONAL { ?x :p :o FILTER(?s) } } }", false) ; } @Test public void testClassify_Join_20() @@ -89,9 +102,9 @@ public class TestClassify extends BaseTest @Test public void testClassify_Join_31() { classifyJ("{ ?x ?y ?z {SELECT ?s { ?s ?p ?o} } }", true) ; } - // Use of a filter variable not in from the LHS + // JENA-1167 : Use of a filter variable not in from the LHS @Test public void testClassify_Join_32() - { classifyJ("{ GRAPH ?g { ?x ?y ?z } { FILTER (?a) } }", true) ; } + { classifyJ("{ GRAPH ?g { ?x ?y ?z } { FILTER (?a) } }", false) ; } // Use of a filter variable from the LHS @Test public void testClassify_Join_33() http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestTransformQuads.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestTransformQuads.java b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestTransformQuads.java index 20d7f3a..24d99e3 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestTransformQuads.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestTransformQuads.java @@ -72,9 +72,10 @@ public class TestTransformQuads extends BaseTest ) ; } // Nested and filter + // ?g is unbound in the filter. @Test public void quads20() { test ("{ GRAPH ?g { ?s ?p ?o GRAPH ?g1 { ?s1 ?p1 ?o1 FILTER (str(?g) = 'graphURI') } } }", "(assign ((?g ?*g0))" + - " (sequence" + + " (join" + " (quadpattern (quad ?*g0 ?s ?p ?o))" + " (filter (= (str ?g) 'graphURI')" + " (quadpattern (quad ?g1 ?s1 ?p1 ?o1)))))" http://git-wip-us.apache.org/repos/asf/jena/blob/6f12cdbd/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestVarFinder.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestVarFinder.java b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestVarFinder.java index aeb8332..b3eb539 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestVarFinder.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/algebra/TestVarFinder.java @@ -35,57 +35,64 @@ public class TestVarFinder extends BaseTest @Test public void varfind_01_1() { varfindFixed("(bgp (?s <p> <o>))", "s") ; } @Test public void varfind_01_2() { varfindOpt("(bgp (?s <p> <o>))") ; } @Test public void varfind_01_3() { varfindFilter("(bgp (?s <p> <o>))") ; } + @Test public void varfind_01_4() { varfindFilter("(bgp (?s <p> <o>))") ; } @Test public void varfind_02_1() { varfindFixed("(graph ?g (bgp (?s <p> <o>)))", "s", "g") ; } @Test public void varfind_02_2() { varfindOpt("(graph ?g (bgp (?s <p> <o>)))") ; } @Test public void varfind_02_3() { varfindFilter("(graph ?g (bgp (?s <p> <o>)))") ; } + @Test public void varfind_02_4() { varfindFilterOnly("(graph ?g (bgp (?s <p> <o>)))") ; } @Test public void varfind_03_1() { varfindFixed("(filter (?s) (bgp (?s <p> <o>)))", "s") ; } @Test public void varfind_03_2() { varfindOpt("(filter (?s) (bgp (?s <p> <o>)))") ; } @Test public void varfind_03_3() { varfindFilter("(filter (?s) (bgp (?s <p> <o>)))", "s") ; } + @Test public void varfind_03_4() { varfindFilterOnly("(filter (?s) (bgp (?s <p> <o>)))") ; } + @Test public void varfind_03_5() { varfindFilterOnly("(filter (?z) (bgp (?s <p> <o>)))", "z") ; } @Test public void varfind_04_1() { varfindFixed("(leftjoin (bgp (?x <q> <v>)) (filter (?s) (bgp (?s <p> <o>))))", "x") ; } @Test public void varfind_04_2() { varfindOpt("(leftjoin (bgp (?x <q> <v>)) (filter (?s) (bgp (?s <p> <o>))))", "s") ; } @Test public void varfind_04_3() { varfindFilter("(leftjoin (bgp (?x <q> <v>)) (filter (?s) (bgp (?s <p> <o>))))", "s") ; } + @Test public void varfind_04_4() { varfindFilterOnly("(leftjoin (bgp (?x <q> <v>)) (filter (?Z) (bgp (?s <p> <o>))))", "Z") ; } + @Test public void varfind_04_5() { varfindFilterOnly("(leftjoin (bgp (?x <q> <v>)) (bgp (?s <p> <o>)) ?Z)", "Z") ; } - - private static void varfindFixed(String string, String...vars) - { - varfind(string, vars, null, null) ; + private static void varfindFixed(String string, String... vars) { + varfind(string, vars, null, null, null) ; + } + + private static void varfindOpt(String string, String... vars) { + varfind(string, null, vars, null, null) ; } - private static void varfindOpt(String string, String...vars) - { - varfind(string, null, vars, null) ; + private static void varfindFilter(String string, String... vars) { + varfind(string, null, null, vars, null) ; } - private static void varfindFilter(String string, String...vars) - { - varfind(string, null, null, vars) ; + private static void varfindFilterOnly(String string, String... vars) { + varfind(string, null, null, null, vars) ; } - private static void varfind(String string, String[] varsFixed, String [] varsOpt, String [] varsFilter) - { + private static void varfind(String string, String[] varsFixed, String[] varsOpt, String[] varsFilter, String[] varsFilterOnly) { Op op = SSE.parseOp(string) ; VarFinder vf = VarFinder.process(op) ; - if ( varsFixed != null ) check(varsFixed, vf.getFixed()) ; - if ( varsOpt != null ) check(varsOpt, vf.getOpt()) ; - if ( varsFilter != null ) check(varsFilter, vf.getFilter()) ; + if ( varsFixed != null ) + check(varsFixed, vf.getFixed()) ; + if ( varsOpt != null ) + check(varsOpt, vf.getOpt()) ; + if ( varsFilter != null ) + check(varsFilter, vf.getFilter()) ; + if ( varsFilterOnly != null ) + check(varsFilterOnly, vf.getFilterOnly()) ; } - private static void check(String[] varsExpected, Set<Var> varsFound) - { + private static void check(String[] varsExpected, Set<Var> varsFound) { Var[] vars = new Var[varsExpected.length] ; - for ( int i = 0 ; i < varsExpected.length ; i++ ) - { + for ( int i = 0 ; i < varsExpected.length ; i++ ) { Var v = Var.alloc(varsExpected[i]) ; vars[i] = v ; } - + List<Var> varList = Arrays.asList(vars) ; HashSet<Var> varSet = new HashSet<>() ; varSet.addAll(varList) ; assertEquals(varSet, varsFound) ; } - }
