blob: d7ea842ccc6b6b1f82f192126b8f8ae04e5961b3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jena.sparql.algebra ;
import static org.apache.jena.sparql.core.Vars.addVar ;
import java.util.* ;
import org.apache.jena.atlas.lib.SetUtils ;
import org.apache.jena.atlas.lib.tuple.Tuple ;
import org.apache.jena.atlas.lib.tuple.TupleFactory ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.sparql.algebra.OpWalker.OpWalkerVisitor;
//import org.apache.jena.sparql.algebra.walker.WalkerVisitor ;
import org.apache.jena.sparql.algebra.op.* ;
//import org.apache.jena.sparql.algebra.walker.WalkerVisitorVisible;
import org.apache.jena.sparql.core.BasicPattern ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.expr.ExprVars ;
import org.apache.jena.sparql.pfunction.PropFuncArg ;
import org.apache.jena.sparql.util.VarUtils ;
/** Get vars for a pattern */
public class OpVars
{
// Choose the default collector - LinkedHashSet is predictable and
// keeps the "found" order
private static Set<Var> collector() {
return new LinkedHashSet<>() ;
}
public static Set<Var> visibleVars(Op op) {
Set<Var> acc = collector() ;
visibleVars(op, acc) ;
return acc ;
}
public static void visibleVars(Op op, Set<Var> acc) {
OpVarsPattern visitor = new OpVarsPattern(acc, true) ;
// Does not work yet for new walker.
OpWalker.walk(new OpWalkerVisitorVisible(visitor, acc), op) ;
}
/** The set of variables that will be in every solution of this Op */
public static Set<Var> fixedVars(Op op) {
Set<Var> acc = collector() ;
fixedVars(op, acc) ;
return acc ;
}
public static void fixedVars(Op op, Set<Var> acc) {
OpVarsPattern visitor = new OpVarsPattern(acc, true) ;
OpWalker.walk(new OpWalkerVisitorFixed(visitor, acc), op) ;
}
public static Tuple<Set<Var>> mentionedVarsByPosition(Op op) {
Set<Var> graphAcc = collector() ;
Set<Var> subjAcc = collector() ;
Set<Var> predAcc = collector() ;
Set<Var> objAcc = collector() ;
Set<Var> unknownAcc = collector() ;
OpVarsPatternWithPositions visitor = new OpVarsPatternWithPositions(graphAcc, subjAcc, predAcc, objAcc, unknownAcc, false);
OpWalker.walk(op, visitor);
return TupleFactory.tuple(graphAcc, subjAcc, predAcc, objAcc, unknownAcc);
}
public static Tuple<Set<Var>> mentionedVarsByPosition(Op... ops) {
Set<Var> graphAcc = collector() ;
Set<Var> subjAcc = collector() ;
Set<Var> predAcc = collector() ;
Set<Var> objAcc = collector() ;
Set<Var> unknownAcc = collector() ;
OpVarsPatternWithPositions visitor = new OpVarsPatternWithPositions(graphAcc, subjAcc, predAcc, objAcc, unknownAcc, false);
for (Op op : ops)
OpWalker.walk(op, visitor);
return TupleFactory.tuple(graphAcc, subjAcc, predAcc, objAcc, unknownAcc);
}
// All mentioned variables regardless of scope/visibility.
public static Collection<Var> mentionedVars(Op op) {
Set<Var> acc = collector() ;
mentionedVars(op, acc) ;
return acc ;
}
// All mentioned variables regardless of scope/visibility.
public static void mentionedVars(Op op, Set<Var> acc) {
OpVarsMentioned visitor = new OpVarsMentioned(acc) ;
OpWalker.walk(op, visitor) ;
}
/** Do project and don't walk into it. MINUS vars aren't visible either */
private static class OpWalkerVisitorVisible extends OpWalkerVisitor
{
private final Collection<Var> acc ;
public OpWalkerVisitorVisible(OpVarsPattern visitor, Collection<Var> acc) {
super(visitor) ;
this.acc = acc ;
}
@Override
public void visit(OpProject op) {
before(op) ;
// Skip Project subop.
acc.addAll(op.getVars()) ;
after(op) ;
}
@Override
public void visit(OpMinus op) {
before(op) ;
if (op.getLeft() != null)
op.getLeft().visit(this) ;
// Skip right.
// if ( op.getRight() != null ) op.getRight().visit(this) ;
if (visitor != null)
op.visit(visitor) ;
after(op) ;
}
}
// Only consider variables that are visible and definitely defined.
// OPTIONAL (2 forms) and UNION are the interesting cases.
private static class OpWalkerVisitorFixed extends OpWalkerVisitor
{
private final Collection<Var> acc ;
public OpWalkerVisitorFixed(OpVarsPattern visitor, Collection<Var> acc) {
//super(visitor, null, null, null) ;
super(visitor);
this.acc = acc ;
}
@Override
public void visit(OpLeftJoin x) {
x.getLeft().visit(this);
}
@Override
public void visit(OpConditional x) {
x.getLeft().visit(this);
}
@Override
public void visit(OpUnion x) {
Set<Var> left = fixedVars(x.getLeft()) ;
Set<Var> right = fixedVars(x.getRight()) ;
Set<Var> r = SetUtils.intersection(left, right) ;
acc.addAll(r) ;
}
@Override
public void visit(OpProject op) {
before(op) ;
// Skip Project subop.
acc.addAll(op.getVars()) ;
after(op) ;
}
@Override
public void visit(OpMinus op) {
before(op) ;
if (op.getLeft() != null)
op.getLeft().visit(this) ;
// Skip right.
// if ( op.getRight() != null ) op.getRight().visit(this) ;
// if (opVisitor != null)
// op.visit(opVisitor) ;
if (visitor != null)
op.visit(visitor) ;
after(op) ;
}
}
/** Collect variables.
* What to collect from is controlled by the walker e.g.
* OpWalkerVisitorFixed, OpWalkerVisitorVisible
* and for OpVarsMentioned, the full geenral walker.
*/
private static class OpVarsPattern extends OpVisitorBase
{
// The possibly-set-vars
protected Set<Var> acc ;
final boolean visibleOnly ;
OpVarsPattern(Set<Var> acc, boolean visibleOnly) {
this.acc = acc ;
this.visibleOnly = visibleOnly ;
}
@Override
public void visit(OpBGP opBGP) {
VarUtils.addVars(acc, opBGP.getPattern()) ;
}
@Override
public void visit(OpPath opPath) {
addVar(acc, opPath.getTriplePath().getSubject()) ;
addVar(acc, opPath.getTriplePath().getObject()) ;
}
@Override
public void visit(OpQuadPattern quadPattern) {
addVar(acc, quadPattern.getGraphNode()) ;
VarUtils.addVars(acc, quadPattern.getBasicPattern()) ;
}
@Override
public void visit(OpQuadBlock quadBlock) {
VarUtils.addVars(acc, quadBlock.getPattern()) ;
}
@Override
public void visit(OpTriple opTriple) {
VarUtils.addVarsFromTriple(acc, opTriple.getTriple());
}
@Override
public void visit(OpQuad opQuad) {
VarUtils.addVarsFromQuad(acc, opQuad.getQuad());
}
@Override
public void visit(OpGraph opGraph) {
addVar(acc, opGraph.getNode()) ;
}
@Override
public void visit(OpFind opFind) {
VarUtils.addVarsFromTriple(acc, opFind.getTriple());
addVar(acc, opFind.getVar());
}
@Override
public void visit(OpDatasetNames dsNames) {
addVar(acc, dsNames.getGraphNode()) ;
}
@Override
public void visit(OpTable opTable) {
// Only the variables with values in the tables (When building,
// undefs didn't get into bindings so no variable mentioned)
Table t = opTable.getTable() ;
acc.addAll(t.getVars()) ;
}
@Override
public void visit(OpProject opProject) {
// The walker (WalkerVisitorVisible) handles this
// for visible variables, not mentioned variable collecting.
// The visibleOnly/clear is simply to be as general as possible.
// visible: OpWalkerVisitorFixed, OpWalkerVisitorVisible,
// all (visibleOnly==false) for OpVarsMentioned
if (visibleOnly)
acc.clear() ;
acc.addAll(opProject.getVars()) ;
}
@Override
public void visit(OpAssign opAssign) {
acc.addAll(opAssign.getVarExprList().getVars()) ;
}
@Override
public void visit(OpExtend opExtend) {
acc.addAll(opExtend.getVarExprList().getVars()) ;
}
@Override
public void visit(OpPropFunc opPropFunc) {
PropFuncArg.addVars(acc, opPropFunc.getSubjectArgs()) ;
PropFuncArg.addVars(acc, opPropFunc.getObjectArgs()) ;
}
@Override
public void visit(OpProcedure opProc) {
ExprVars.varsMentioned(acc, opProc.getArgs()) ;
}
@Override
public void visit(OpExt opExt) {
// OpWalkerVisitor is taking care of calling opExt.effectiveOp().visit(this)
}
}
private static class OpVarsPatternWithPositions extends OpVisitorBase
{
// The possibly-set-vars
protected Set<Var> graphAcc, subjAcc, predAcc, objAcc, unknownAcc ;
final boolean visibleOnly ;
OpVarsPatternWithPositions(Set<Var> graphAcc, Set<Var> subjAcc, Set<Var> predAcc, Set<Var> objAcc, Set<Var> unknownAcc, boolean visibleOnly) {
this.graphAcc = graphAcc;
this.subjAcc = subjAcc;
this.predAcc = predAcc;
this.objAcc = objAcc;
this.unknownAcc = unknownAcc;
this.visibleOnly = visibleOnly ;
}
@Override
public void visit(OpBGP opBGP) {
vars(opBGP.getPattern()) ;
}
@Override
public void visit(OpPath opPath) {
addVar(subjAcc, opPath.getTriplePath().getSubject()) ;
addVar(objAcc, opPath.getTriplePath().getObject()) ;
}
@Override
public void visit(OpQuadPattern quadPattern) {
addVar(graphAcc, quadPattern.getGraphNode()) ;
vars(quadPattern.getBasicPattern()) ;
}
@Override
public void visit(OpGraph opGraph) {
addVar(graphAcc, opGraph.getNode()) ;
}
@Override
public void visit(OpDatasetNames dsNames) {
addVar(graphAcc, dsNames.getGraphNode()) ;
}
@Override
public void visit(OpTable opTable) {
// Only the variables with values in the tables
// (When building, undefs didn't get into bindings so no variable
// mentioned)
Table t = opTable.getTable() ;
// Treat as unknown position
unknownAcc.addAll(t.getVars()) ;
}
@Override
public void visit(OpProject opProject) {
// The walker (WalkerVisitorVisible) handles this
// for visible variables, not mentioned variable collecting.
// The visibleOnly/clear is simply to be as general as possible.
List<Var> vs = opProject.getVars();
if (visibleOnly) {
clear(graphAcc, vs);
clear(subjAcc, vs);
clear(predAcc, vs);
clear(objAcc, vs);
}
for (Var v : vs) {
if (!graphAcc.contains(v) && !subjAcc.contains(v) && !predAcc.contains(v) && !objAcc.contains(v)) {
addVar(unknownAcc, v);
}
}
}
@Override
public void visit(OpAssign opAssign) {
// Unknown position
unknownAcc.addAll(opAssign.getVarExprList().getVars()) ;
}
@Override
public void visit(OpExtend opExtend) {
// Unknown position
unknownAcc.addAll(opExtend.getVarExprList().getVars()) ;
}
@Override
public void visit(OpPropFunc opPropFunc) {
addvars(subjAcc, opPropFunc.getSubjectArgs()) ;
addvars(objAcc, opPropFunc.getObjectArgs()) ;
}
private void addvars(Set<Var> acc, PropFuncArg pfArg) {
if (pfArg.isNode()) {
addVar(acc, pfArg.getArg()) ;
return ;
}
for (Node n : pfArg.getArgList())
addVar(acc, n) ;
}
@Override
public void visit(OpProcedure opProc) {
unknownAcc.addAll(OpVars.mentionedVars(opProc));
}
private void vars(BasicPattern bp) {
for (Triple t : bp.getList())
{
addVar(subjAcc, t.getSubject());
addVar(predAcc, t.getPredicate());
addVar(objAcc, t.getObject());
}
}
private void clear(Set<Var> acc, List<Var> visible) {
List<Var> toRemove = new ArrayList<>();
for (Var found : acc)
{
if (!visible.contains(found)) {
toRemove.add(found);
}
}
for (Var v : toRemove) {
acc.remove(v);
}
}
}
private static class OpVarsMentioned extends OpVarsPattern
{
OpVarsMentioned(Set<Var> acc) {
super(acc, false) ;
}
@Override
public void visit(OpFilter opFilter) {
ExprVars.varsMentioned(acc, opFilter.getExprs()) ;
}
@Override
public void visit(OpOrder opOrder) {
ExprVars.varsMentioned(acc, opOrder.getConditions()) ;
}
}
}