| /* |
| * 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.riot.out; |
| |
| import java.net.MalformedURLException; |
| import java.util.Map; |
| import java.util.StringJoiner; |
| |
| import org.apache.jena.atlas.io.IndentedLineBuffer; |
| import org.apache.jena.atlas.io.IndentedWriter; |
| import org.apache.jena.atlas.lib.Bytes; |
| import org.apache.jena.atlas.lib.Chars; |
| import org.apache.jena.graph.Node; |
| import org.apache.jena.graph.Triple; |
| import org.apache.jena.iri.IRI; |
| import org.apache.jena.iri.IRIRelativize; |
| import org.apache.jena.rdf.model.RDFNode; |
| import org.apache.jena.riot.system.*; |
| import org.apache.jena.shared.PrefixMapping; |
| import org.apache.jena.sparql.ARQConstants; |
| import org.apache.jena.sparql.core.Quad; |
| |
| /** Presentation utilities for Nodes, Triples, Quads and more. |
| * <p> |
| * Methods <tt>str</tt> generate a reparsable string. |
| * <p> |
| * Methods <tt>displayStr</tt> do not guarantee a reparsable string |
| * e.g. may use abbreviations or common prefixes. |
| */ |
| public class NodeFmtLib |
| { |
| // Replaces FmtUtils |
| // See OutputLangUtils. |
| // See and use EscapeStr |
| |
| private static final NodeFormatter plainFormatter = new NodeFormatterNT(); |
| |
| private static PrefixMap dftPrefixMap = PrefixMapFactory.create(); |
| static { |
| PrefixMapping pm = ARQConstants.getGlobalPrefixMap(); |
| Map<String, String> map = pm.getNsPrefixMap(); |
| for ( Map.Entry<String, String> e : map.entrySet() ) |
| dftPrefixMap.add(e.getKey(), e.getValue() ); |
| } |
| |
| public static String str(Triple t) { |
| return strNodes(t.getSubject(), t.getPredicate(), t.getObject()); |
| } |
| |
| public static String str(Quad q) { |
| return strNodes(q.getGraph(), q.getSubject(), q.getPredicate(), q.getObject()); |
| } |
| |
| public static String str(Node n) { |
| IndentedLineBuffer sw = new IndentedLineBuffer(); |
| str(sw, n); |
| return sw.toString(); |
| } |
| |
| // Worker |
| public static String strNodes(Node...nodes) { |
| IndentedLineBuffer sw = new IndentedLineBuffer(); |
| boolean first = true; |
| for ( Node n : nodes ) { |
| if ( !first ) |
| sw.append(" "); |
| first = false; |
| if ( n == null ) { |
| sw.append("null"); |
| continue; |
| } |
| str(sw, n); |
| } |
| return sw.toString(); |
| } |
| |
| /** A displayable string for an RDFNode. Includes common abbreviations */ |
| public static String displayStr(RDFNode obj) { |
| return displayStr(obj.asNode()); |
| } |
| |
| public static String displayStr(Triple t) { |
| return displayStrNodes(t.getSubject(), t.getPredicate(), t.getObject()); |
| } |
| |
| public static String displayStr(Node n) { |
| return str(n, null, dftPrefixMap); |
| } |
| |
| private static String displayStrNodes(Node...nodes) { |
| StringJoiner sj = new StringJoiner(" "); |
| for ( Node n : nodes ) |
| sj.add(displayStr(n)); |
| return sj.toString(); |
| } |
| |
| public static void str(IndentedWriter w, Node n) { |
| serialize(w, n, null, null); |
| } |
| |
| public static String str(Node n, Prologue prologue) { |
| return str(n, prologue.getBaseURI(), prologue.getPrefixMap()); |
| } |
| |
| public static String str(Node n, PrefixMap prefixMap) { |
| return str(n, null, prefixMap); |
| } |
| |
| public static String str(Node n, String base, PrefixMap prefixMap) { |
| IndentedLineBuffer sw = new IndentedLineBuffer(); |
| serialize(sw, n, base, prefixMap); |
| return sw.toString(); |
| } |
| |
| public static void serialize(IndentedWriter w, Node n, Prologue prologue) { |
| serialize(w, n, prologue.getBaseURI(), prologue.getPrefixMap()); |
| } |
| |
| public static void serialize(IndentedWriter w, Node n, String base, PrefixMap prefixMap) { |
| NodeFormatter formatter; |
| if ( base == null && prefixMap == null ) |
| formatter = plainFormatter; |
| else |
| formatter = new NodeFormatterTTL(base, prefixMap); |
| formatter.format(w, n); |
| } |
| |
| // ---- Blank node labels. |
| |
| // Strict N-triples only allows [A-Za-z][A-Za-z0-9] |
| static char encodeMarkerChar = 'X'; |
| |
| // These two form a pair to convert bNode labels to a safe (i.e. legal N-triples form) and back agains. |
| |
| // Encoding is: |
| // 1 - Add a Letter |
| // 2 - Hexify, as Xnn, anything outside ASCII A-Za-z0-9 |
| // 3 - X is encoded as XX |
| |
| private static char LabelLeadingLetter = 'B'; |
| |
| public static String encodeBNodeLabel(String label) { |
| StringBuilder buff = new StringBuilder(); |
| // Must be at least one char and not a digit. |
| buff.append(LabelLeadingLetter); |
| |
| for ( int i = 0 ; i < label.length() ; i++ ) { |
| char ch = label.charAt(i); |
| if ( ch == encodeMarkerChar ) { |
| buff.append(ch); |
| buff.append(ch); |
| } else if ( RiotChars.isA2ZN(ch) ) |
| buff.append(ch); |
| else |
| Chars.encodeAsHex(buff, encodeMarkerChar, ch); |
| } |
| return buff.toString(); |
| } |
| |
| // Assumes that blank nodes only have characters in the range of 0-255 |
| public static String decodeBNodeLabel(String label) { |
| StringBuilder buffer = new StringBuilder(); |
| |
| if ( label.charAt(0) != LabelLeadingLetter ) |
| return label; |
| |
| // Skip first. |
| for ( int i = 1 ; i < label.length() ; i++ ) { |
| char ch = label.charAt(i); |
| |
| if ( ch != encodeMarkerChar ) { |
| buffer.append(ch); |
| } else { |
| // Maybe XX or Xnn. |
| char ch2 = label.charAt(i + 1); |
| if ( ch2 == encodeMarkerChar ) { |
| i++; |
| buffer.append(ch); |
| continue; |
| } |
| |
| // Xnn |
| i++; |
| char hiC = label.charAt(i); |
| int hi = Bytes.hexCharToInt(hiC); |
| i++; |
| char loC = label.charAt(i); |
| int lo = Bytes.hexCharToInt(loC); |
| |
| int combined = ((hi << 4) | lo); |
| buffer.append((char)combined); |
| } |
| } |
| |
| return buffer.toString(); |
| } |
| |
| // ---- Relative URIs. |
| |
| static private int relFlags = IRIRelativize.SAMEDOCUMENT | IRIRelativize.CHILD; |
| static public String abbrevByBase(String uri, String base) { |
| if ( base == null ) |
| return null; |
| IRI baseIRI = IRIResolver.iriFactory().construct(base); |
| IRI rel = baseIRI.relativize(uri, relFlags); |
| String r = null; |
| try { |
| r = rel.toASCIIString(); |
| } catch (MalformedURLException ex) { |
| r = rel.toString(); |
| } |
| return r; |
| } |
| } |