/*
* 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;
import static org.apache.jena.sparql.path.P_Mod.UNSET ;
import java.util.List ;
import org.apache.jena.atlas.io.IndentedLineBuffer ;
import org.apache.jena.atlas.io.IndentedWriter ;
import org.apache.jena.graph.Node ;
import org.apache.jena.sparql.ARQException ;
import org.apache.jena.sparql.core.Prologue ;
import org.apache.jena.sparql.util.FmtUtils ;
public class PathWriter
{
public static void write(Path path, Prologue prologue)
{
write(IndentedWriter.stdout, path, prologue) ;
}
public static void write(IndentedWriter out, Path path, Prologue prologue)
{
PathWriterWorker w = new PathWriterWorker(out, prologue) ;
path.visit(w) ;
out.flush();
}
public static String asString(Path path) { return asString(path, null) ; }
public static String asString(Path path, Prologue prologue)
{
IndentedLineBuffer buff = new IndentedLineBuffer() ;
PathWriterWorker w = new PathWriterWorker(buff, prologue) ;
path.visit(w) ;
w.out.flush();
return buff.asString() ;
}
static class PathWriterWorker implements PathVisitor
{
private IndentedWriter out ;
private Prologue prologue ;
private static boolean alwaysInnerParens = true ;
private boolean needParens = false ;
PathWriterWorker(IndentedWriter indentedWriter, Prologue prologue)
{
this.out = indentedWriter ;
this.prologue = prologue ;
}
private void visitPath(Path path)
{ visitPath(path, true) ; }
private void visitPath(Path path, boolean needParensThisTime)
{
if ( alwaysInnerParens )
needParensThisTime = true ;
boolean b = needParens ;
needParens = needParensThisTime ;
path.visit(this) ;
needParens = b ;
}
private void output(Node node)
{
out.print(FmtUtils.stringForNode(node, prologue)) ;
}
private void output(P_Path0 path0)
{
if ( ! path0.isForward() )
out.print("^") ;
out.print(FmtUtils.stringForNode(path0.getNode(), prologue)) ;
}
@Override
public void visit(P_Link pathNode)
{
output(pathNode.getNode()) ;
}
@Override
public void visit(P_ReverseLink pathNode)
{
out.println("^") ;
output(pathNode.getNode()) ;
}
@Override
public void visit(P_NegPropSet pathNotOneOf)
{
List<P_Path0> props = pathNotOneOf.getNodes() ;
if ( props.size() == 0 )
throw new ARQException("Bad path element: NotOneOf found with no elements") ;
out.print("!") ;
if ( props.size() == 1 )
output(props.get(0)) ;
else
{
out.print("(") ;
boolean first = true ;
for (P_Path0 p : props)
{
if (!first) out.print("|") ;
first = false ;
output(p) ;
}
out.print(")") ;
}
// List<P_Path0> props = pathNotOneOf.getNodes() ;
// if ( props.size() == 0 )
// throw new ARQException("Bad path element: NotOneOf found with no elements") ;
// out.print("!") ;
// if ( props.size() == 1 )
// output(props.get(0)) ;
// else
// {
// out.print("(") ;
// boolean first = true ;
// for (P_Path0 p : props)
// {
// if (!first) out.print("|") ;
// first = false ;
// output(p) ;
// }
// out.print(")") ;
// }
}
@Override
public void visit(P_Alt pathAlt)
{
visit2(pathAlt, "|", true) ;
}
@Override
public void visit(P_Seq pathSeq)
{
visit2(pathSeq, "/", false) ;
}
// Should pass around precedence numbers.
private void visit2(P_Path2 path2, String sep, boolean isSeq)
{
if ( needParens ) out.print("(") ;
visitPath(path2.getLeft()) ;
out.print(sep) ;
// Don't need parens if same as before.
if ( isSeq )
{
// Make / and ^ chains look nice
if ( path2.getRight() instanceof P_Seq )
visitPath(path2.getRight(), needParens) ;
else
visitPath(path2.getRight(), true) ;
}
else
visitPath(path2.getRight(), true) ;
if ( needParens ) out.print(")") ;
}
@Override
public void visit(P_Mod pathMod)
{
if ( needParens )
out.print("(") ;
if ( alwaysInnerParens )
out.print("(") ;
pathMod.getSubPath().visit(this) ;
if ( alwaysInnerParens )
out.print(")") ;
out.print("{") ;
if ( pathMod.getMin() != UNSET )
out.print(Long.toString(pathMod.getMin())) ;
out.print(",") ;
if ( pathMod.getMax() != UNSET )
out.print(Long.toString(pathMod.getMax())) ;
out.print("}") ;
// if ( pathMod.isZeroOrMore() )
// out.print("*") ;
// else if ( pathMod.isOneOrMore() )
// out.print("+") ;
// else if ( pathMod.isZeroOrOne() )
// out.print("?") ;
// else
// {
// out.print("{") ;
// if ( pathMod.getMin() != UNSET )
// out.print(Long.toString(pathMod.getMin())) ;
// out.print(",") ;
// if ( pathMod.getMax() != UNSET )
// out.print(Long.toString(pathMod.getMax())) ;
// out.print("}") ;
// }
if ( needParens )
out.print(")") ;
}
@Override
public void visit(P_FixedLength pFixedLength)
{
if ( needParens )
out.print("(") ;
if ( alwaysInnerParens )
out.print("(") ;
pFixedLength.getSubPath().visit(this) ;
if ( alwaysInnerParens )
out.print(")") ;
out.print("{") ;
out.print(Long.toString(pFixedLength.getCount())) ;
out.print("}") ;
if ( needParens )
out.print(")") ;
}
@Override
public void visit(P_Distinct pathDistinct)
{
out.print("DISTINCT(") ;
pathDistinct.getSubPath().visit(this) ;
out.print(")") ;
}
@Override
public void visit(P_Multi pathMulti)
{
out.print("MULTI(") ;
pathMulti.getSubPath().visit(this) ;
out.print(")") ;
}
@Override
public void visit(P_Shortest path)
{
out.print("SHORTEST(") ;
path.getSubPath().visit(this) ;
out.print(")") ;
}
@Override
public void visit(P_ZeroOrOne path)
{ printPathMod("?", path.getSubPath()) ; }
@Override
public void visit(P_ZeroOrMore1 path)
{ printPathMod("*", path.getSubPath()) ; }
@Override
public void visit(P_ZeroOrMoreN path)
{ printPathMod("{*}", path.getSubPath()) ; }
@Override
public void visit(P_OneOrMore1 path)
{ printPathMod("+", path.getSubPath()) ; }
@Override
public void visit(P_OneOrMoreN path)
{ printPathMod("{+}", path.getSubPath()) ; }
private void printPathMod(String mod, Path path)
{
boolean doParens = ( needParens || alwaysInnerParens ) ;
if ( doParens )
out.print("(") ;
path.visit(this) ;
if ( doParens )
out.print(")") ;
out.print(mod) ;
}
// Need to consider binary ^
@Override
public void visit(P_Inverse inversePath)
{
out.print("^") ;
Path p = inversePath.getSubPath() ;
boolean parens = true ;
if ( p instanceof P_Link )
parens = false ;
visitPath(p, parens) ;
}
}
}