blob: 07109ecaadae5968de597094f65c79c1b8811637 [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.optimize;
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.ARQConstants ;
import org.apache.jena.sparql.ARQException ;
import org.apache.jena.sparql.algebra.Op ;
import org.apache.jena.sparql.algebra.TransformCopy ;
import org.apache.jena.sparql.algebra.op.OpJoin ;
import org.apache.jena.sparql.algebra.op.OpPath ;
import org.apache.jena.sparql.algebra.op.OpTriple ;
import org.apache.jena.sparql.algebra.op.OpUnion ;
import org.apache.jena.sparql.core.TriplePath ;
import org.apache.jena.sparql.core.Var ;
import org.apache.jena.sparql.core.VarAlloc ;
import org.apache.jena.sparql.path.* ;
/** The path transformation step exactly as per the SPARQL 1.1 spec.
* i.e. joins triples rather creating BGPs.
* It does not produce very nice execution structures so ARQ uses
* a functional equivalent, but different, transformation.
*/
public class TransformPathFlatternStd extends TransformCopy
{
public TransformPathFlatternStd() { }
@Override
public Op transform(OpPath opPath)
{
TriplePath tp = opPath.getTriplePath() ;
Op op = transformPath(opPath, tp.getSubject(), tp.getPath(), tp.getObject() ) ;
// And combine adjacent triple patterns.
return op ;
}
static Op transformPath(OpPath op, Node subject, Path path, Node object)
{
PathTransform transform = new PathTransform(subject, object) ;
path.visit(transform) ;
Op r = transform.getResult() ;
if ( r == null )
{
if ( op == null )
op = make(subject, path, object) ;
return op ;
}
return r ;
}
static OpPath make(Node subject, Path path, Node object)
{
TriplePath tp = new TriplePath(subject, path, object) ;
return new OpPath(tp) ;
}
static VarAlloc varAlloc = new VarAlloc(ARQConstants.allocVarAnonMarker+"Q") ;
private static Op join(Op op1, Op op2)
{
return OpJoin.create(op1, op2) ;
}
private static Op union(Op left, Op right)
{
return new OpUnion(left, right) ;
}
static class PathTransform extends PathVisitorBase
{
private final Node subject ;
private final Node object ;
private Op result = null ;
Op getResult() { return result ; }
public PathTransform(Node subject, Node object)
{
this.subject = subject ;
this.object = object ;
this.result = null ;
}
@Override
public void visit(P_Link pathNode)
{
Op op = new OpTriple(new Triple(subject, pathNode.getNode(), object)) ;
result = op ;
}
@Override
public void visit(P_ReverseLink pathNode)
{
Op op = new OpTriple(new Triple(object, pathNode.getNode(), subject)) ;
result = op ;
}
/*
* Reverse transformations.
* X !(^:uri1|...|^:urin)Y ==> ^(X !(:uri1|...|:urin) Y)
* Split into forward and reverse.
* X !(:uri1|...|:urii|^:urii+1|...|^:urim) Y ==> { X !(:uri1|...|:urii|)Y } UNION { X !(^:urii+1|...|^:urim) Y }
*/
@Override
public void visit(P_NegPropSet pathNotOneOf)
{
Op opFwd = null ;
Op opBwd = null ;
List<P_Path0> forwards = new ArrayList<>() ;
List<P_Path0> backwards = new ArrayList<>() ;
for ( P_Path0 p : pathNotOneOf.getNodes() )
{
if ( p.isForward() )
forwards.add(p) ;
else
backwards.add(p) ;
}
if ( ! forwards.isEmpty() )
{
P_NegPropSet pFwd = new P_NegPropSet() ;
for ( P_Path0 p : forwards )
pFwd.add(p) ;
opFwd = make(subject, pFwd, object) ;
}
if ( ! backwards.isEmpty() )
{
// Could reverse here.
P_NegPropSet pBwd = new P_NegPropSet() ;
for ( P_Path0 p : backwards )
pBwd.add(p) ;
opBwd = make(subject, pBwd, object) ;
}
if ( opFwd == null && opBwd == null )
{
result = make(subject, pathNotOneOf, object) ;
return ;
}
result = union(opFwd, opBwd) ;
}
@Override
public void visit(P_Inverse inversePath)
{
PathTransform pt = new PathTransform(object, subject) ;
inversePath.getSubPath().visit(pt) ;
result = pt.getResult() ;
}
@Override
public void visit(P_Mod pathMod)
{
if ( pathMod.getMin() > pathMod.getMax() )
throw new ARQException("Bad path: "+pathMod) ;
Op op = null ;
for ( long i = pathMod.getMin() ; i <= pathMod.getMax() ; i++ )
{
Path p = PathFactory.pathFixedLength(pathMod.getSubPath(), i) ;
Op sub = transformPath(null, subject, p, object) ;
op = union(op, sub) ;
}
result = op ;
}
@Override
public void visit(P_FixedLength pFixedLength)
{
Op op = null ;
Var v1 = null ;
for ( int i = 0 ; i < pFixedLength.getCount() ; i++ )
{
Var v2 = varAlloc.allocVar() ;
Node s = (v1 == null) ? subject : v1 ;
Node o = (i == pFixedLength.getCount()-1) ? object : v2 ;
Op op1 = transformPath(null, s, pFixedLength.getSubPath() , o) ;
op = join(op, op1) ;
v1 = v2 ;
}
result = op ;
}
@Override
public void visit(P_Alt pathAlt)
{
Op op1 = transformPath(null, subject, pathAlt.getLeft() , object) ;
Op op2 = transformPath(null, subject, pathAlt.getRight() , object) ;
result = union(op1, op2) ;
}
@Override
public void visit(P_Seq pathSeq)
{
Var v = varAlloc.allocVar() ;
Op op1 = transformPath(null, subject, pathSeq.getLeft() , v) ;
Op op2 = transformPath(null, v, pathSeq.getRight() , object) ;
result = join(op1, op2) ;
}
}
}