/* * 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.resultset; import java.io.OutputStream ; import java.util.HashMap ; import java.util.Map ; import org.apache.jena.atlas.io.IndentedWriter ; import org.apache.jena.atlas.logging.Log ; import org.apache.jena.query.ARQ ; import org.apache.jena.query.QuerySolution ; import org.apache.jena.query.ResultSet ; import org.apache.jena.rdf.model.Literal ; import org.apache.jena.rdf.model.RDFNode ; import org.apache.jena.rdf.model.Resource ; import org.apache.jena.rdf.model.impl.Util ; /** XML Output (ResultSet format) */ public class XMLOutputResultSet implements ResultSetProcessor, XMLResults { static boolean outputExplicitUnbound = false ; boolean outputGraphBNodeLabels = ARQ.isTrue(ARQ.outputGraphBNodeLabels) ; int index = 0 ; // First index is 1 String stylesheetURL = null ; boolean xmlInst = true ; IndentedWriter out ; int bNodeCounter = 0 ; Map<Resource, String> bNodeMap = new HashMap<>() ; XMLOutputResultSet(OutputStream outStream) { this(new IndentedWriter(outStream)); } XMLOutputResultSet(IndentedWriter indentedOut) { out = indentedOut; } @Override public void start(ResultSet rs) { if ( xmlInst ) out.println("<?xml version=\"1.0\"?>"); if ( stylesheetURL != null ) { out.print("<?xml-stylesheet type=\"text/xsl\" href=\""); out.print(stylesheetURL); out.println("\"?>"); } // ---- Root out.print("<"); out.print(dfRootTag); out.print(" xmlns=\""); out.print(dfNamespace); out.println("\">"); // ---- Header out.incIndent(INDENT); out.print("<"); out.print(dfHead); out.println(">"); if ( false ) { String link = "UNSET"; out.print("<link href=\""); out.print(link); out.println("\"/>"); } for ( String n : rs.getResultVars() ) { out.incIndent(INDENT); out.print("<"); out.print(dfVariable); out.print(" "); out.print(dfAttrVarName); out.print("=\""); out.print(n); out.print("\""); out.println("/>"); out.decIndent(INDENT); } out.print("</"); out.print(dfHead); out.println(">"); out.decIndent(INDENT); // Start results proper out.incIndent(INDENT); out.print("<"); out.print(dfResults); out.println(">"); out.incIndent(INDENT); } @Override public void finish(ResultSet rs) { out.decIndent(INDENT); out.print("</"); out.print(dfResults); out.println(">"); out.decIndent(INDENT); out.print("</"); out.print(dfRootTag); out.println(">"); out.flush(); } @Override public void start(QuerySolution qs) { out.print("<"); out.print(dfSolution); out.println(">"); index++; out.incIndent(INDENT); } @Override public void finish(QuerySolution qs) { out.decIndent(INDENT); out.print("</"); out.print(dfSolution); out.println(">"); } @Override public void binding(String varName, RDFNode node) { if ( node == null && !outputExplicitUnbound ) return; out.print("<"); out.print(dfBinding); out.print(" name=\""); out.print(varName); out.println("\">"); out.incIndent(INDENT); printBindingValue(node); out.decIndent(INDENT); out.print("</"); out.print(dfBinding); out.println(">"); } void printBindingValue(RDFNode node) { if ( node == null ) { // Unbound out.print("<"); out.print(dfUnbound); out.println("/>"); return; } if ( node instanceof Literal ) { printLiteral((Literal)node); return; } if ( node instanceof Resource ) { printResource((Resource)node); return; } Log.warn(this, "Unknown RDFNode type in result set: " + node.getClass()); } void printLiteral(Literal literal) { out.print("<"); out.print(dfLiteral); if ( Util.isLangString(literal) ) { String lang = literal.getLanguage(); out.print(" xml:lang=\""); out.print(literal.getLanguage()); out.print("\""); } else if ( !Util.isSimpleString(literal) ) { // Datatype // (RDF 1.1) not xsd:string nor rdf:langString. // (RDF 1.0) any datatype. String datatype = literal.getDatatypeURI(); out.print(" "); out.print(dfAttrDatatype); out.print("=\""); out.print(datatype); out.print("\""); } out.print(">"); out.print(xml_escape(literal.getLexicalForm())); out.print("</"); out.print(dfLiteral); out.println(">"); } void printResource(Resource r) { if ( r.isAnon() ) { String label; if ( outputGraphBNodeLabels ) label = r.asNode().getBlankNodeId().getLabelString(); else { if ( !bNodeMap.containsKey(r) ) bNodeMap.put(r, "b" + (bNodeCounter++)); label = bNodeMap.get(r); } out.print("<"); out.print(dfBNode); out.print(">"); out.print(xml_escape(label)); out.print("</"); out.print(dfBNode); out.println(">"); } else { out.print("<"); out.print(dfURI); out.print(">"); out.print(xml_escape(r.getURI())); out.print("</"); out.print(dfURI); out.println(">"); } } private static String xml_escape(String string) { final StringBuilder sb = new StringBuilder(string); int offset = 0; String replacement; char found; for (int i = 0; i < string.length(); i++) { found = string.charAt(i); switch (found) { case '&' : replacement = "&"; break; case '<' : replacement = "<"; break; case '>' : replacement = ">"; break; case '\r': replacement = " "; break; case '\n': replacement = " "; break; default : replacement = null; } if (replacement != null) { sb.replace(offset + i, offset + i + 1, replacement); offset += replacement.length() - 1; // account for added chars } } return sb.toString(); } /** @return Returns the stylesheetURL. */ public String getStylesheetURL() { return stylesheetURL ; } /** @param stylesheetURL The stylesheetURL to set. */ public void setStylesheetURL(String stylesheetURL) { this.stylesheetURL = stylesheetURL ; } /** @return Returns the xmlInst. */ public boolean getXmlInst() { return xmlInst ; } /** @param xmlInst The xmlInst to set. */ public void setXmlInst(boolean xmlInst) { this.xmlInst = xmlInst ; } }