| /* |
| * 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 java.util.ArrayList ; |
| import java.util.HashMap ; |
| import java.util.List ; |
| import java.util.Map ; |
| |
| import org.apache.jena.graph.Node ; |
| import org.apache.jena.graph.Triple ; |
| import org.apache.jena.sparql.algebra.op.OpBGP ; |
| import org.apache.jena.sparql.algebra.op.OpPropFunc ; |
| import org.apache.jena.sparql.algebra.op.OpSequence ; |
| import org.apache.jena.sparql.algebra.op.OpTable ; |
| import org.apache.jena.sparql.core.BasicPattern ; |
| import org.apache.jena.sparql.expr.Expr ; |
| import org.apache.jena.sparql.expr.ExprList ; |
| import org.apache.jena.sparql.pfunction.PropFuncArg ; |
| import org.apache.jena.sparql.pfunction.PropertyFunctionRegistry ; |
| import org.apache.jena.sparql.util.Context ; |
| import org.apache.jena.sparql.util.ExprUtils ; |
| import org.apache.jena.sparql.util.graph.GNode ; |
| import org.apache.jena.sparql.util.graph.GraphList ; |
| |
| public class PropertyFunctionGenerator |
| { |
| public static Op buildPropertyFunctions(PropertyFunctionRegistry registry, OpBGP opBGP, Context context) |
| { |
| if ( opBGP.getPattern().isEmpty() ) |
| return opBGP ; |
| return compilePattern(registry, opBGP.getPattern(), context) ; |
| } |
| |
| private static Op compilePattern(PropertyFunctionRegistry registry, BasicPattern pattern, Context context) |
| { |
| // Split into triples and property functions. |
| |
| // 1/ Find property functions. |
| // Property functions may involve other triples (for list arguments) |
| // (but leave the property function triple in-place as a marker) |
| // 2/ Find arguments for property functions |
| // (but leave the property function triple in-place as a marker) |
| // 3/ For remaining triples, put into basic graph patterns, |
| // and string together the procedure calls and BGPs. |
| |
| List<Triple> propertyFunctionTriples = new ArrayList<>() ; // Property functions seen |
| BasicPattern triples = new BasicPattern(pattern) ; // A copy of all triples (later, it is mutated) |
| |
| // Find the triples invoking property functions, and those not. |
| findPropertyFunctions(context, pattern, registry, propertyFunctionTriples) ; |
| |
| if ( propertyFunctionTriples.size() == 0 ) |
| //No property functions. |
| return new OpBGP(pattern) ; |
| |
| Map<Triple, PropertyFunctionInstance> pfInvocations = new HashMap<>() ; // Map triple => property function instance |
| // Removes triples of list arguments. This mutates 'triples' |
| findPropertyFunctionArgs(context, triples, propertyFunctionTriples, pfInvocations) ; |
| |
| // Now make the OpSequence structure. |
| Op op = makeStages(triples, pfInvocations) ; |
| return op ; |
| } |
| |
| private static void findPropertyFunctions(Context context, |
| BasicPattern pattern, |
| PropertyFunctionRegistry registry, |
| List<Triple> propertyFunctionTriples) |
| { |
| // Step 1 : find property functions (if any); collect triples. |
| // Not list arg triples at this point. |
| for ( Triple t : pattern ) |
| { |
| if ( isMagicProperty(registry, t) ) |
| propertyFunctionTriples.add(t) ; |
| } |
| } |
| |
| |
| private static void findPropertyFunctionArgs(Context context, |
| BasicPattern triples, |
| List<Triple> propertyFunctionTriples, |
| Map<Triple, PropertyFunctionInstance> pfInvocations) |
| { |
| // Step 2 : for each property function, remove associated triples in list arguments; |
| // Leave the propertyFunction triple itself. |
| |
| for ( Triple pf : propertyFunctionTriples ) |
| { |
| PropertyFunctionInstance pfi = magicProperty( context, pf, triples ); |
| pfInvocations.put( pf, pfi ); |
| } |
| } |
| |
| private static class PropertyFunctionInstance |
| { |
| Node predicate ; |
| PropFuncArg subjArgs ; |
| PropFuncArg objArgs ; |
| |
| PropertyFunctionInstance(PropFuncArg sArgs, Node predicate, PropFuncArg oArgs) |
| { |
| this.subjArgs = sArgs ; |
| this.predicate = predicate ; |
| this.objArgs = oArgs ; |
| } |
| |
| ExprList argList() |
| { |
| ExprList exprList = new ExprList() ; |
| argList(exprList, subjArgs) ; |
| argList(exprList, objArgs) ; |
| return exprList ; |
| } |
| |
| PropFuncArg getSubjectArgList() { return subjArgs ; } |
| PropFuncArg getObjectArgList() { return objArgs ; } |
| |
| private static void argList(ExprList exprList, PropFuncArg pfArg) |
| { |
| if ( pfArg.isNode() ) |
| { |
| Node n = pfArg.getArg() ; |
| Expr expr = ExprUtils.nodeToExpr(n) ; |
| exprList.add(expr) ; |
| return ; |
| } |
| |
| for ( Node n : pfArg.getArgList() ) |
| { |
| Expr expr = ExprUtils.nodeToExpr(n) ; |
| exprList.add(expr) ; |
| } |
| } |
| } |
| |
| private static Op makeStages(BasicPattern triples, Map<Triple, PropertyFunctionInstance> pfInvocations) |
| { |
| // Step 3 : Make the operation expression. |
| // For each property function, insert the implementation |
| // For each block of non-property function triples, make a BGP. |
| |
| Op op = null; |
| BasicPattern pattern = null ; |
| for ( Triple t : triples ) |
| { |
| if ( pfInvocations.containsKey(t) ) |
| { |
| op = flush(pattern, op) ; |
| pattern = null ; |
| PropertyFunctionInstance pfi = pfInvocations.get(t) ; |
| OpPropFunc opPF = new OpPropFunc(t.getPredicate(), pfi.getSubjectArgList(), pfi.getObjectArgList(), op) ; |
| op = opPF ; |
| continue ; |
| } |
| |
| // Regular triples - make sure there is a basic pattern in progress. |
| if ( pattern == null ) |
| pattern = new BasicPattern() ; |
| pattern.add(t) ; |
| } |
| op = flush(pattern, op) ; |
| return op ; |
| } |
| |
| private static Op flush(BasicPattern pattern, Op op) |
| { |
| if ( pattern == null || pattern.isEmpty() ) |
| { |
| if ( op == null ) |
| return OpTable.unit() ; |
| return op ; |
| } |
| OpBGP opBGP = new OpBGP(pattern) ; |
| return OpSequence.create(op, opBGP) ; |
| } |
| |
| private static boolean isMagicProperty(PropertyFunctionRegistry registry, Triple pfTriple) |
| { |
| if ( ! pfTriple.getPredicate().isURI() ) |
| return false ; |
| |
| if ( registry.manages(pfTriple.getPredicate().getURI()) ) |
| return true ; |
| |
| return false ; |
| } |
| |
| // Remove all triples associated with this magic property. |
| // Make an instance record. |
| private static PropertyFunctionInstance magicProperty(Context context, |
| Triple pfTriple, |
| BasicPattern triples) |
| { |
| List<Triple> listTriples = new ArrayList<>() ; |
| |
| GNode sGNode = new GNode(triples, pfTriple.getSubject()) ; |
| GNode oGNode = new GNode(triples, pfTriple.getObject()) ; |
| List<Node> sList = null ; |
| List<Node> oList = null ; |
| |
| if ( GraphList.isListNode(sGNode) ) |
| { |
| sList = GraphList.members(sGNode) ; |
| GraphList.allTriples(sGNode, listTriples) ; |
| } |
| if ( GraphList.isListNode(oGNode) ) |
| { |
| oList = GraphList.members(oGNode) ; |
| GraphList.allTriples(oGNode, listTriples) ; |
| } |
| |
| PropFuncArg subjArgs = new PropFuncArg(sList, pfTriple.getSubject()) ; |
| PropFuncArg objArgs = new PropFuncArg(oList, pfTriple.getObject()) ; |
| |
| // Confuses single arg with a list of one. |
| PropertyFunctionInstance pfi = new PropertyFunctionInstance(subjArgs, pfTriple.getPredicate(), objArgs) ; |
| |
| triples.getList().removeAll(listTriples) ; |
| return pfi ; |
| } |
| |
| |
| } |