blob: 69d97d9306d9b674af1b79f71539f6b8de882e83 [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 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 ;
}
}