/* * 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.core; import java.util.ArrayList; import java.util.List; import org.apache.jena.graph.Node; import org.apache.jena.graph.Triple; import org.apache.jena.sparql.algebra.Op; import org.apache.jena.sparql.algebra.TransformCopy; import org.apache.jena.sparql.algebra.Transformer; import org.apache.jena.sparql.algebra.op.*; import org.apache.jena.sparql.engine.binding.Binding; import org.apache.jena.sparql.engine.binding.BindingFactory; import org.apache.jena.sparql.expr.Expr; import org.apache.jena.sparql.expr.ExprList; import org.apache.jena.sparql.pfunction.PropFuncArg; public class Substitute { public static Op substitute(Op op, Binding binding) { // Want to avoid cost if the binding is empty // but the empty test is not zero-cost on non-empty things. if ( isNotNeeded(binding) ) return op; return Transformer.transform(new OpSubstituteWorker(binding), op); } public static Op substitute(Op op, Var var, Node node) { Binding b = BindingFactory.binding(var, node); return substitute(op, b); } public static BasicPattern substitute(BasicPattern bgp, Binding binding) { if ( isNotNeeded(binding) ) return bgp; BasicPattern bgp2 = new BasicPattern(); for ( Triple triple : bgp ) { Triple t = substitute(triple, binding); bgp2.add(t); } return bgp2; } public static Triple substitute(Triple triple, Binding binding) { if ( isNotNeeded(binding) ) return triple; Node s = triple.getSubject(); Node p = triple.getPredicate(); Node o = triple.getObject(); Node s1 = substitute(s, binding); Node p1 = substitute(p, binding); Node o1 = substitute(o, binding); Triple t = triple; if ( s1 != s || p1 != p || o1 != o ) t = new Triple(s1, p1, o1); return t; } public static TriplePath substitute(TriplePath triplePath, Binding binding) { if ( triplePath.isTriple() ) return new TriplePath(Substitute.substitute(triplePath.asTriple(), binding)); Node s = triplePath.getSubject(); Node o = triplePath.getObject(); Node s1 = substitute(s, binding); Node o1 = substitute(o, binding); TriplePath tp = triplePath; if ( s1 != s || o1 != o ) tp = new TriplePath(s1, triplePath.getPath(), o1); return tp; } public static Quad substitute(Quad quad, Binding binding) { if ( isNotNeeded(binding) ) return quad; Node g = quad.getGraph(); Node s = quad.getSubject(); Node p = quad.getPredicate(); Node o = quad.getObject(); Node g1 = substitute(g, binding); Node s1 = substitute(s, binding); Node p1 = substitute(p, binding); Node o1 = substitute(o, binding); Quad q = quad; if ( s1 != s || p1 != p || o1 != o || g1 != g ) q = new Quad(g1, s1, p1, o1); return q; } public static Node substitute(Node n, Binding b) { return Var.lookup(b, n); } public static PropFuncArg substitute(PropFuncArg propFuncArg, Binding binding) { if ( isNotNeeded(binding) ) return propFuncArg; if ( propFuncArg.isNode() ) { Node n = propFuncArg.getArg(); if ( !Var.isVar(n) ) // Not a Var, no substitute needed. return propFuncArg; return new PropFuncArg(substitute(propFuncArg.getArg(), binding)); } List<Node> newArgList = new ArrayList<>(); for ( Node n : propFuncArg.getArgList() ) newArgList.add(substitute(n, binding)); return new PropFuncArg(newArgList); } public static Expr substitute(Expr expr, Binding binding) { if ( isNotNeeded(binding) ) return expr; return expr.copySubstitute(binding); } public static ExprList substitute(ExprList exprList, Binding binding) { if ( isNotNeeded(binding) ) return exprList; return exprList.copySubstitute(binding); } private static boolean isNotNeeded(Binding b) { return b == null || b.isEmpty(); } // ---- private static class OpSubstituteWorker extends TransformCopy { private Binding binding; public OpSubstituteWorker(Binding binding) { super(TransformCopy.COPY_ALWAYS); this.binding = binding; } @Override public Op transform(OpBGP opBGP) { BasicPattern bgp = opBGP.getPattern(); bgp = substitute(bgp, binding); return new OpBGP(bgp); } @Override public Op transform(OpQuadPattern quadPattern) { Node gNode = quadPattern.getGraphNode(); Node g = substitute(gNode, binding); BasicPattern triples = new BasicPattern(); for ( Triple triple : quadPattern.getBasicPattern() ) { Node s = substitute(triple.getSubject(), binding); Node p = substitute(triple.getPredicate(), binding); Node o = substitute(triple.getObject(), binding); Triple t = new Triple(s, p, o); triples.add(t); } // Pure quading. // for ( Iterator iter = quadPattern.getQuads().iterator() ; // iter.hasNext() ; ) // { // Quad quad = (Quad)iter.next() ; // if ( ! quad.getGraph().equals(gNode) ) // throw new ARQInternalErrorException("Internal error: quads block // is not uniform over the graph node") ; // Node s = substitute(quad.getSubject(), binding) ; // Node p = substitute(quad.getPredicate(), binding) ; // Node o = substitute(quad.getObject(), binding) ; // Triple t = new Triple(s, p, o) ; // triples.add(t) ; // } return new OpQuadPattern(g, triples); } @Override public Op transform(OpPath opPath) { return new OpPath(substitute(opPath.getTriplePath(), binding)); } @Override public Op transform(OpPropFunc opPropFunc, Op subOp) { PropFuncArg sArgs = opPropFunc.getSubjectArgs(); PropFuncArg oArgs = opPropFunc.getObjectArgs(); PropFuncArg sArgs2 = substitute(sArgs, binding); PropFuncArg oArgs2 = substitute(oArgs, binding); if ( sArgs2 == sArgs && oArgs2 == oArgs && opPropFunc.getSubOp() == subOp ) return super.transform(opPropFunc, subOp); return new OpPropFunc(opPropFunc.getProperty(), sArgs2, oArgs2, subOp); } @Override public Op transform(OpFilter filter, Op op) { ExprList exprs = filter.getExprs().copySubstitute(binding); if ( exprs == filter.getExprs() ) return filter; return OpFilter.filterBy(exprs, op); } @Override public Op transform(OpAssign opAssign, Op subOp) { VarExprList varExprList2 = transformVarExprList(opAssign.getVarExprList()); if ( varExprList2.isEmpty() ) return subOp; return OpAssign.assign(subOp, varExprList2); } @Override public Op transform(OpExtend opExtend, Op subOp) { VarExprList varExprList2 = transformVarExprList(opExtend.getVarExprList()); if ( varExprList2.isEmpty() ) return subOp; return OpExtend.create(subOp, varExprList2); } private VarExprList transformVarExprList(VarExprList varExprList) { VarExprList varExprList2 = new VarExprList(); for ( Var v : varExprList.getVars() ) { // if ( binding.contains(v)) // // Already bound. No need to do anything because the // // logical assignment will test value. // continue ; Expr expr = varExprList.getExpr(v); expr = expr.copySubstitute(binding); varExprList2.add(v, expr); } return varExprList2; } // The expression? // public Op transform(OpLeftJoin opLeftJoin, Op left, Op right) { // return xform(opLeftJoin, left, right) ; } @Override public Op transform(OpGraph op, Op sub) { Node n = substitute(op.getNode(), binding); return new OpGraph(n, sub); } @Override public Op transform(OpService op, Op sub) { Node n = substitute(op.getService(), binding); return new OpService(n, sub, op.getSilent()); } } }