/*
* 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.path.eval ;
import java.util.Collection ;
import java.util.Iterator ;
import java.util.function.Predicate;
import org.apache.jena.atlas.iterator.Iter ;
import org.apache.jena.graph.Graph ;
import org.apache.jena.graph.Node ;
import org.apache.jena.graph.Triple ;
import org.apache.jena.sparql.ARQNotImplemented ;
import org.apache.jena.sparql.path.* ;
import org.apache.jena.sparql.sse.writers.WriterPath ;
final class PathEvaluator implements PathVisitor
{
protected final Graph graph ;
protected final Node node ;
protected final Collection<Node> output ;
private PathEngine engine ;
protected PathEvaluator(Graph g, Node n, Collection<Node> output, PathEngine engine) {
this.graph = g ;
this.node = n ;
this.output = output ;
this.engine = engine ;
}
protected final void fill(Iterator<Node> iter) {
for (; iter.hasNext();)
output.add(iter.next()) ;
}
// These operations yield the same results regardless of counting
// (their subpaths may not).
@Override
public void visit(P_Link pathNode) {
Iterator<Node> nodes = engine.doOne(node, pathNode.getNode()) ;
fill(nodes) ;
}
@Override
public void visit(P_ReverseLink pathNode) {
engine.flipDirection() ;
Iterator<Node> nodes = engine.doOne(node, pathNode.getNode()) ;
fill(nodes) ;
engine.flipDirection() ;
}
@Override
public void visit(P_Inverse inversePath) {
// boolean b = forwardMode ;
// Flip direction and evaluate
engine.flipDirection() ;
engine.eval(inversePath.getSubPath(), node, output) ;
engine.flipDirection() ;
}
@Override
public void visit(P_NegPropSet pathNotOneOf) {
engine.doNegatedPropertySet(pathNotOneOf, node, output) ;
}
@Override
public void visit(P_Mod pathMod) {
// do..Or.. need to take a visited set.
if ( pathMod.isZeroOrMore() ) {
// :p{0,}
engine.doOneOrMoreN(pathMod.getSubPath(), node, output) ;
return ;
}
if ( pathMod.isOneOrMore() ) {
engine.doOneOrMoreN(pathMod.getSubPath(), node, output) ;
return ;
}
if ( pathMod.isFixedLength() )
engine.doFixedLengthPath(pathMod.getSubPath(), node, pathMod.getFixedLength(), output) ;
else
engine.doMultiLengthPath(pathMod.getSubPath(), node, pathMod.getMin(), pathMod.getMax(), output) ;
}
@Override
public void visit(P_FixedLength pFixedLength) {
engine.doFixedLengthPath(pFixedLength.getSubPath(), node, pFixedLength.getCount(), output) ;
}
@Override
public void visit(P_ZeroOrOne path) {
engine.doZeroOrOne(path.getSubPath(), node, output) ;
}
@Override
public void visit(P_ZeroOrMore1 path) {
engine.doZeroOrMore(path.getSubPath(), node, output) ;
}
@Override
public void visit(P_ZeroOrMoreN path) {
engine.doZeroOrMoreN(path.getSubPath(), node, output) ;
}
@Override
public void visit(P_OneOrMore1 path) {
engine.doOneOrMore(path.getSubPath(), node, output) ;
}
@Override
public void visit(P_OneOrMoreN path) {
engine.doOneOrMoreN(path.getSubPath(), node, output) ;
}
@Override
public void visit(P_Alt pathAlt) {
engine.doAlt(pathAlt.getLeft(), pathAlt.getRight(), node, output) ;
}
@Override
public void visit(P_Distinct pathDistinct) {
PathEngine engine2 = engine ;
engine = new PathEngine1(graph, engine.direction()) ;
engine.eval(pathDistinct.getSubPath(), node, output) ;
engine = engine2 ;
}
@Override
public void visit(P_Multi pathMulti) {
PathEngine engine2 = engine ;
engine = new PathEngineN(graph, engine.direction()) ;
engine.eval(pathMulti.getSubPath(), node, output) ;
engine = engine2 ;
}
@Override
public void visit(P_Shortest path) {
throw new ARQNotImplemented(WriterPath.asString(path)) ;
}
@Override
public void visit(P_Seq pathSeq) {
engine.doSeq(pathSeq.getLeft(), pathSeq.getRight(), node, output) ;
}
// Other operations can produce duplicates and so may be executed in
// different ways depending on cardibnality requirements.
protected static class FilterExclude implements Predicate<Triple>
{
private Collection<Node> excludes ;
public FilterExclude(Collection<Node> excludes) {
this.excludes = excludes ;
}
@Override
public boolean test(Triple triple) {
return !excludes.contains(triple.getPredicate()) ;
}
}
final protected Iter<Triple> between(Node x, Node z) {
return Iter.iter(engine.graphFind(x, Node.ANY, z)) ;
}
final protected void doZero(Path path, Node node, Collection<Node> output) {
// Ignores path.
output.add(node) ;
}
}