/* * Licensed 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. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. */ package org.seaborne.patch.text; import java.io.IOException; import java.io.OutputStream; import java.io.Writer; import org.apache.jena.atlas.io.AWriter; import org.apache.jena.atlas.io.BufferingWriter; import org.apache.jena.atlas.io.IO; import org.apache.jena.atlas.lib.Chars; import org.apache.jena.graph.Node; import org.apache.jena.riot.RiotException; import org.apache.jena.riot.out.NodeFormatter; import org.apache.jena.riot.out.NodeFormatterTTL; import org.apache.jena.riot.system.PrefixMapFactory; import org.apache.jena.riot.tokens.Token; import org.apache.jena.riot.tokens.TokenizerText; import org.apache.jena.sparql.util.FmtUtils; public class TokenWriterText implements TokenWriter { // Whether to space out the tuples a bit for readability. private static final boolean GAPS = true; private final AWriter out; private boolean inTuple = false; private boolean inSection = false; private final NodeFormatter fmt; private String label; /** * Create a TokenOutputStreamWriter going to a OutputStream. * * @param out */ public TokenWriterText(OutputStream out) { this(writer(out)); } private static Writer writer(OutputStream out) { // IO.wrap(out) -- need buffering version, Writer w1 = IO.asBufferedUTF8(out); Writer w2 = new BufferingWriter(w1, 1024 * 1024); return w2; } /** * Create a TokenOutputStreamWriter going to a Writer, ideally one that * buffers (e.g. {@linkplain BufferingWriter}). * * @param out */ public TokenWriterText(Writer out) { this(null, null, out); } /** * Create a TokenOutputStreamWriter going to a Writer, ideally one that * buffers (e.g. {@linkplain BufferingWriter}). * * @param out */ public TokenWriterText(AWriter out) { this(null, null, out); } /** * Create a TokenOutputStreamWriter going to a Writer, with a given * NodeFormatter policy ideally one that buffers (e.g. * {@linkplain BufferingWriter}). * * @param out */ public TokenWriterText(NodeFormatter formatter, Writer out) { this(null, formatter, out); } /** * Create a TokenOutputStreamWriter going to a Writer, with a given * NodeFormatter policy ideally one that buffers (e.g. * {@linkplain BufferingWriter}). * * @param out */ public TokenWriterText(NodeFormatter formatter, AWriter out) { this(null, formatter, out); } public TokenWriterText(String label, NodeFormatter formatter, Writer out) { this(label, formatter, IO.wrap(out)); } public TokenWriterText(String label, NodeFormatter formatter, AWriter out) { if ( formatter == null ) // For the number abbreviations. formatter = new NodeFormatterTTL(null, PrefixMapFactory.emptyPrefixMap()); // Must write bNodes as <_:....> formatter = new NodeFormatterBNode(formatter); this.fmt = formatter; this.out = out; this.label = label; } static class NodeFormatterBNode extends NodeFormatterWrapper { public NodeFormatterBNode(NodeFormatter other) { super(other); } @Override public void format(AWriter w, Node n) { if ( n.isBlank() ) formatBNode(w, n); else super.format(w, n); } @Override public void formatBNode(AWriter w, Node n) { formatBNode(w, n.getBlankNodeLabel()); } @Override public void formatBNode(AWriter w, String label) { w.print("<_:"); w.print(label); w.print(">"); } } static class NodeFormatterWrapper implements NodeFormatter { private final NodeFormatter fmt; public NodeFormatterWrapper(NodeFormatter other) { this.fmt = other; } @Override public void format(AWriter w, Node n) { fmt.format(w, n); } @Override public void formatURI(AWriter w, Node n) { fmt.formatURI(w, n); } @Override public void formatURI(AWriter w, String uriStr) { fmt.formatURI(w, uriStr); } @Override public void formatVar(AWriter w, Node n) { fmt.formatVar(w, n); } @Override public void formatVar(AWriter w, String name) { fmt.formatVar(w, name); } @Override public void formatBNode(AWriter w, Node n) { fmt.formatBNode(w, n); } @Override public void formatBNode(AWriter w, String label) { fmt.formatBNode(w, label); } @Override public void formatLiteral(AWriter w, Node n) { fmt.formatLiteral(w, n); } @Override public void formatLitString(AWriter w, String lex) { fmt.formatLitString(w, lex); } @Override public void formatLitLang(AWriter w, String lex, String langTag) { fmt.formatLitLang(w, lex, langTag); } @Override public void formatLitDT(AWriter w, String lex, String datatypeURI) { fmt.formatLitDT(w, lex, datatypeURI); } } @Override public void sendToken(Token token) { String string = tokenToString(token); write(string); gap(true); } @Override public void sendNode(Node node) { fmt.format(out, node); gap(false); } @Override public void sendString(String string) { fmt.formatLitString(out, string); gap(false); } @Override public void sendWord(String string) { write(string) ; // no escapes, no quotes gap(true); } private static String cntrlAsString(char cntrl) { return Character.toString((char)TokenizerText.CTRL_CHAR) + Character.toString(cntrl); } @Override public void sendControl(char controlChar) { String x = cntrlAsString(controlChar); write(x); gap(false); } @Override public void sendNumber(long number) { write(Long.toString(number)); gap(true); } @Override public void startTuple() {} @Override public void endTuple() { if ( !inTuple ) return; out.write(Chars.CH_DOT); out.write("\n"); inTuple = false; } @Override public void close() { if ( inTuple ) {} IO.close(out); } @Override public void flush() { out.flush(); } // -------- private String tokenToString(Token token) { switch (token.getType()) { // superclass case NODE: case IRI : return "<" + token.getImage() + ">"; case PREFIXED_NAME : notImplemented(token); return null; case BNODE : return "_:" + token.getImage(); //case BOOLEAN: case STRING : return "\"" + FmtUtils.stringEsc(token.getImage()) + "\""; case LITERAL_LANG : return "\"" + FmtUtils.stringEsc(token.getImage()) + "\"@" + token.getImage2(); case LITERAL_DT : return "\"" + FmtUtils.stringEsc(token.getImage()) + "\"^^" + tokenToString(token.getSubToken2()); case INTEGER : case DECIMAL : case DOUBLE : return token.getImage(); // Not RDF case KEYWORD : return token.getImage(); case DOT : return Chars.S_DOT; case VAR : return "?" + token.getImage(); case COMMA : return Chars.S_COMMA; case SEMICOLON : return Chars.S_SEMICOLON; case COLON : return Chars.S_COLON; case LT : return Chars.S_LT; case GT : return Chars.S_GT; case LE : return Chars.S_LE; case GE : return Chars.S_GE; case UNDERSCORE : return Chars.S_UNDERSCORE; case LBRACE : return Chars.S_LBRACE; case RBRACE : return Chars.S_RBRACE; case LPAREN : return Chars.S_LPAREN; case RPAREN : return Chars.S_RPAREN; case LBRACKET : return Chars.S_LBRACKET; case RBRACKET : return Chars.S_RBRACKET; case PLUS : return Chars.S_PLUS; case MINUS : return Chars.S_MINUS; case STAR : return Chars.S_STAR; case SLASH : return Chars.S_SLASH; case RSLASH : return Chars.S_RSLASH; case HEX : return "0x" + token.getImage(); // Syntax // COLON is only visible if prefix names are not being processed. case DIRECTIVE : return "@" + token.getImage(); case VBAR : return Chars.S_VBAR; case AMPHERSAND : return Chars.S_AMPHERSAND; case EQUALS : return Chars.S_EQUALS; default : notImplemented(token); return null; // case EOF: // case EQUIVALENT : // return "=="; // case LOGICAL_AND : // return "&&"; // case LOGICAL_OR : // return "||"; // case NL : // break; // case NODE : // break; // case WS : // break; } } // A gap is always necessary for items that are not endLog-limited. // For example, numbers adjacent to numbers must have a gap but // quoted string then quoted string does not require a gap. private void gap(boolean required) { if ( required || GAPS ) write(" "); } // Beware of multiple stringing. private void write(String string) { inTuple = true; out.write(string); } private static void exception(IOException ex) { throw new RiotException(ex); } private void notImplemented(Token token) { throw new RiotException("Unencodable token: " + token); } }