blob: e00ea78291d9a21342ab83798b939cbd8903b925 [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.engine.iterator;
import static org.apache.jena.graph.Node_Triple.triple;
import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.sparql.core.Var;
import org.apache.jena.sparql.core.VarAlloc;
import org.apache.jena.sparql.engine.ExecutionContext;
import org.apache.jena.sparql.engine.QueryIterator;
/**
* Solver library for RDF*.
* <p>
* There are two entry points.
* <p>
* Function {@link #rdfStarTriple} for matching a single triple pattern in a basic
* graph pattern that may involve RDF* terms.
* <p>
* Function {@link #matchTripleStar} for matches a triple term and assigning the
* triple matched to a variable. It is used within {@link #rdfStarTriple} for nested
* triple term and a temporary allocated variable as well can for
* {@code FIND(<<...>> AS ?t)}.
*/
public class RX {
// From context?
static String allocTripleTerms = "*";
static VarAlloc varAlloc = new VarAlloc(allocTripleTerms) ;
// QueryIterator - acceptable as API as universal.
// Internal TDB is a delayed binding iterator.
// <Triple, Node> vs <Tuple, NodeId>
// TDB: Iterator<BindingNodeId>
// TDB: StageMatchTuple ~~ QueryIterTriplePattern
// TDB: SolverLib.solve
// The two solver libs are identical except for imports.
/*
public static Iterator<BindingNodeId> solve(NodeTupleTable nodeTupleTable,
Tuple<Node> tuple,
boolean anyGraph,
Iterator<BindingNodeId> chain, Predicate<Tuple<NodeId>> filter,
ExecutionContext execCxt)
{
return new StageMatchTuple(nodeTupleTable, chain, tuple, anyGraph, filter, execCxt) ;
}
*/
/**
* Match a single triple pattern that may involve RDF* terms.
* This is the top level function for matching triples.
*
* The function {@link #matchTripleStar} matches a triple term and assigns the triple matched to a variable.
* It is used within {@link #rdfStarTriple} for nested triple term and a temporary allocated variable
* as well can for {@code FIND(<<...>> AS ?t)}.
*
* @implNote
* Without RDF*, this would be a plain call of {@link #matchData} which is simply:
* <pre>
* new QueryIterTriplePattern(chain, triple, execContext)}
* </pre>
*
*/
public static QueryIterator rdfStarTriple(QueryIterator chain, Triple triple, ExecutionContext execCxt) {
// Should all work without this trap for plain RDF but for now,
// fast track the non-RDF* case.
if ( ! tripleHasNodeTriple(triple) )
// No RDF* : direct to data.
return matchData(chain, triple, execCxt);
return rdfStarTripleSub(chain, triple, execCxt);
}
/**
* Insert the stages necessary for a triple with triple pattern term inside it.
* If the triple pattern has a triple term, possibly with variables, introduce
* an iterator to solve for that, assign the matching triple term to a hidden
* variable, and put allocated variable in to main triple pattern. Do for subject
* and object positions, and also any nested triple pattern terms.
*/
private static QueryIterator rdfStarTripleSub(QueryIterator chain, Triple triple, ExecutionContext execContext) {
Pair<QueryIterator, Triple> pair = preprocess(chain, triple, execContext);
QueryIterator chain2 = matchData(pair.getLeft(), pair.getRight(), execContext);
return chain2;
}
/**
* Match a triple pattern (which may have nested triple terms in it).
* Any matched triples are added as triple terms bound to the supplied variable.
*/
public static QueryIterator matchTripleStar(QueryIterator chain, Var var, Triple triple, ExecutionContext execContext) {
if ( tripleHasNodeTriple(triple) ) {
Pair<QueryIterator, Triple> pair = preprocess(chain, triple, execContext);
chain = pair.getLeft();
triple = pair.getRight();
}
// Assign to var in each binding, based on the triple pattern grounded by the match.
QueryIterator qIter = bindTripleTerm(chain, var, triple, execContext);
return qIter;
}
// If we assume the data is correct (in PG mode), no need to test for the triple
// of a concrete Node_Triple because we are able to test for it in the triple
// pattern itself. This should be "false".
// XXX To be removed.
private static final boolean TEST_FOR_CONCRETE_TRIPLE_TERM = false;
/**
* Process a triple for triple terms.
* <p>
* This creates additional matchers for triple terms in the pattern triple recursively.
*/
private static Pair<QueryIterator, Triple> preprocess(QueryIterator chain, Triple patternTriple, ExecutionContext execContext) {
Triple triple2 = patternTriple;
Node s = patternTriple.getSubject();
Node p = patternTriple.getPredicate();
Node o = patternTriple.getObject();
Node s1 = null;
Node o1 = null;
// Recurse.
if ( s.isNodeTriple() ) {
if ( TEST_FOR_CONCRETE_TRIPLE_TERM || ! s.isConcrete() ) {
Triple t2 = triple(s);
Var var = varAlloc.allocVar();
Triple tripleTerm = Triple.create(t2.getSubject(), t2.getPredicate(), t2.getObject());
chain = matchTripleStar(chain, var, tripleTerm, execContext);
s1 = var;
}
}
if ( o.isNodeTriple() ) {
if ( TEST_FOR_CONCRETE_TRIPLE_TERM || ! o.isConcrete() ) {
Triple t2 = triple(o);
Var var = varAlloc.allocVar();
Triple tripleTerm = Triple.create(t2.getSubject(), t2.getPredicate(), t2.getObject());
chain = matchTripleStar(chain, var, tripleTerm, execContext);
o1 = var;
}
}
// No triple term in this triple.
if ( s1 == null && o1 == null )
return Pair.create(chain, patternTriple);
// Change. Replace original.
if ( s1 == null )
s1 = s ;
if ( o1 == null )
o1 = o ;
Triple triple1 = Triple.create(s1, p, o1);
return Pair.create(chain, triple1);
}
/**
* Match the graph with a triple pattern.
* This is the accessor the graph.
* It assumes any triple terms have been dealt with.
*/
private static QueryIterator matchData(QueryIterator chain, Triple triple, ExecutionContext execContext) {
QueryIterator qIter = new QueryIterTriplePattern(chain, triple, execContext);
return qIter;
}
/**
* Add a binding to each row with triple grounded by the current row.
* If the triple isn't concrete, then just return the row as-is.
*/
private static QueryIterator bindTripleTerm(QueryIterator chain, Var var, Triple triple, ExecutionContext execContext) {
return new QueryIterAddTripleTerm(chain, var, triple, execContext);
}
/**
* Test whether a triple has an triple term as one of its components.
*/
private static boolean tripleHasNodeTriple(Triple triple) {
return triple.getSubject().isNodeTriple()
/*|| triple.getPredicate().isNodeTriple()*/
|| triple.getObject().isNodeTriple();
}
}