blob: 1a0b45d7a07ee1be23a0ed462c6e4131b094931a [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.sse;
import java.io.OutputStream;
import java.util.Map;
import java.util.Set;
import org.apache.jena.atlas.io.IndentedWriter;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.shared.PrefixMapping;
import org.apache.jena.sparql.graph.NodeConst;
import org.apache.jena.sparql.serializer.SerializationContext;
import org.apache.jena.sparql.sse.writers.WriterNode;
import org.apache.jena.sparql.util.FmtUtils;
public class ItemWriter
{
public static boolean includeBase = false;
private static boolean CloseSameLine = true;
public static void write(OutputStream out, Item item) {
IndentedWriter iw = new IndentedWriter(out);
write(iw, item, null);
iw.ensureStartOfLine();
iw.flush();
}
public static void write(IndentedWriter out, Item item, SerializationContext sCxt) {
writeInline(out, item, sCxt);
}
// Core function. Does not apply reverse lift. Does not write prefixes or base.
private static void writeInline(IndentedWriter out, Item item, SerializationContext sCxt) {
Print pv = new Print(out, sCxt);
pv.startPrint();
item.visit(pv);
pv.finishPrint();
}
// Core function. Writes self contained output with prfixes and base.
private static void writeFn(IndentedWriter out, Item item, SerializationContext sCxt) {
Print pv = new Print(out, sCxt);
pv.startPrint();
item.visit(pv);
pv.finishPrint();
}
// No compound node conversion.
private static final boolean writeQuotedTriplesAsNodes = true;
/**
* Write a node in SSE syntax. This code writes the node as given, with symbol
* translation but no compound lift conversions
* <p>
* {@linkplain ItemLift#lowerCompound(Item)} turns RDF term (nodes) into compound
* form. In writers - call
* {@link WriterNode#output(IndentedWriter, Node, SerializationContext)} which
* converse compounds and then calls this function.
*/
public static void write(IndentedWriter out, Node node, SerializationContext sCxt) {
writeNode(out, node, sCxt);
}
private static void writeNode(IndentedWriter out, Node node, SerializationContext sCxt) {
if ( node.isNodeTriple() ) {
Triple t = node.getTriple();
if ( writeQuotedTriplesAsNodes ) {
// As <<....>>
out.print("<< ");
writeNode(out, t.getSubject(), sCxt);
out.print(" ");
writeNode(out, t.getPredicate(), sCxt);
out.print(" ");
writeNode(out, t.getObject(), sCxt);
out.print(" >>");
} else {
// As (qtriple ...)
out.print("(");
out.print(Tags.tagQTriple);
out.print(" ");
writeNode(out, t.getSubject(), sCxt);
out.print(" ");
writeNode(out, t.getPredicate(), sCxt);
out.print(" ");
writeNode(out, t.getObject(), sCxt);
out.print(")");
}
return;
}
if ( Node.ANY.equals(node) ) {
out.print("ANY");
return;
}
if ( NodeConst.TRUE.equals(node) ) {
out.print("true");
return;
}
if ( NodeConst.FALSE.equals(node) ) {
out.print("false");
return;
}
// Atomic SSE Node - URI, BNode, Literal, Variable.
out.print(FmtUtils.stringForNode(node, sCxt));
}
private static class Print implements ItemVisitor {
IndentedWriter out;
SerializationContext sCxt;
boolean doneBase = false;
boolean donePrefix = false;
Print(IndentedWriter out, SerializationContext sCxt) {
if ( sCxt == null )
sCxt = new SerializationContext();
this.out = out;
this.sCxt = sCxt;
}
void startPrint() {}
void writeContext() {
if ( sCxt != null ) {
if ( includeBase && sCxt.getBaseIRI() != null ) {
out.print("(base ");
out.println(FmtUtils.stringForURI(sCxt.getBaseIRI()));
doneBase = true;
out.incIndent();
}
PrefixMapping pmap = sCxt.getPrefixMapping();
if ( pmap != null ) {
Map<String, String> pm = pmap.getNsPrefixMap();
donePrefix = (pm.size() != 0);
if ( pm.size() != 0 ) {
out.println("(prefix");
out.incIndent();
printPrefixes(pm, out);
out.println();
}
}
}
}
void finishPrint() {
if ( doneBase ) {
out.print(")");
out.decIndent();
}
if ( donePrefix ) {
out.print(")");
out.decIndent();
}
}
@Override
public void visit(Item item, Node node) {
writeNode(out, node, sCxt);
}
@Override
public void visit(Item item, String symbol) {
out.print(symbol);
}
// Tags to leave on one line.
private static Set<String> oneLineTags = Set.of(Tags.tagQTriple, Tags.tagTriple);
@Override
public void visit(Item item, ItemList list) {
out.print("(");
boolean listMode = false;
for ( Item subItem : list ) {
if ( ! oneLine(subItem) ) {
listMode = true;
break;
}
}
// Lists are printed with structure.
// If no lists, print on one line.
if ( listMode )
printAsList(list);
else
printOneLine(list);
}
private static boolean oneLine(Item item) {
if ( ! item.isList() )
return true;
ItemList list = item.getList();
if ( list.isEmpty() )
return true;
Item item0 = list.getFirst();
if ( ! item0.isSymbol() )
return false;
String symbol = item0.getSymbol();
return oneLineTags.contains(symbol);
}
@Override
public void visitNil(Item item) {
out.print("nil");
}
private void printAsList(ItemList list) {
boolean first = true;
int indentlevel = out.getUnitIndent();
if ( list.size() >= 1 && list.get(0).isList() )
indentlevel = 1;
for ( Item subItem : list ) {
if ( !first )
out.println();
subItem.visit(this);
if ( first )
out.incIndent(indentlevel);
first = false;
}
if ( !first )
out.decIndent(indentlevel);
if ( !CloseSameLine )
out.println();
out.print(")");
}
private void printOneLine(ItemList list) {
boolean first = true;
for ( Item subItem : list ) {
if ( !first )
out.print(" ");
first = false;
subItem.visit(this);
}
out.print(")");
}
private void printPrefixes(Map<String, String> map, IndentedWriter out) {
if ( map.size() == 0 )
return;
out.print("( ");
out.incIndent(2);
boolean first = true;
for ( String s : map.keySet() ) {
if ( !first ) {
out.println();
}
first = false;
String k = s;
String v = map.get(k);
out.print("(");
out.print(k);
out.print(':');
// Include at least one space
out.print(' ', 6 - k.length());
out.print(FmtUtils.stringForURI(v));
out.print(")");
}
out.decIndent(2);
out.print(")");
}
}
}