org.openrdf.rio.rdfxml.RDFXMLWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sesame-rio-rdfxml Show documentation
Show all versions of sesame-rio-rdfxml Show documentation
Rio parser and writer implementation for the RDF/XML file format.
/*
* Licensed to Aduna under one or more contributor license agreements.
* See the NOTICE.txt file distributed with this work for additional
* information regarding copyright ownership.
*
* Aduna licenses this file to you under the terms of the Aduna BSD
* License (the "License"); you may not use this file except in compliance
* with the License. See the LICENSE.txt file distributed with this work
* for the full License.
*
* 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.openrdf.rio.rdfxml;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.Charset;
import java.util.LinkedHashMap;
import java.util.Map;
import info.aduna.xml.XMLUtil;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.util.Literals;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFWriter;
import org.openrdf.rio.helpers.BasicParserSettings;
import org.openrdf.rio.helpers.RDFWriterBase;
import org.openrdf.rio.helpers.XMLWriterSettings;
/**
* An implementation of the RDFWriter interface that writes RDF documents in
* XML-serialized RDF format.
*/
public class RDFXMLWriter extends RDFWriterBase implements RDFWriter {
/*-----------*
* Variables *
*-----------*/
protected Writer writer;
protected String defaultNamespace;
protected boolean writingStarted;
protected boolean headerWritten;
protected Resource lastWrittenSubject;
/*--------------*
* Constructors *
*--------------*/
/**
* Creates a new RDFXMLWriter that will write to the supplied OutputStream.
*
* @param out
* The OutputStream to write the RDF/XML document to.
*/
public RDFXMLWriter(OutputStream out) {
this(new OutputStreamWriter(out, Charset.forName("UTF-8")));
}
/**
* Creates a new RDFXMLWriter that will write to the supplied Writer.
*
* @param writer
* The Writer to write the RDF/XML document to.
*/
public RDFXMLWriter(Writer writer) {
this.writer = writer;
namespaceTable = new LinkedHashMap();
writingStarted = false;
headerWritten = false;
lastWrittenSubject = null;
}
/*---------*
* Methods *
*---------*/
public RDFFormat getRDFFormat() {
return RDFFormat.RDFXML;
}
@Override
public void startRDF()
throws RDFHandlerException
{
if (writingStarted) {
throw new RDFHandlerException("Document writing has already started");
}
writingStarted = true;
}
protected void writeHeader()
throws IOException
{
try {
// This export format needs the RDF namespace to be defined, add a
// prefix for it if there isn't one yet.
setNamespace(RDF.PREFIX, RDF.NAMESPACE);
if (getWriterConfig().get(XMLWriterSettings.INCLUDE_XML_PI)) {
writer.write("\n");
}
if (getWriterConfig().get(XMLWriterSettings.INCLUDE_ROOT_RDF_TAG)) {
writeStartOfStartTag(RDF.NAMESPACE, "RDF");
if (defaultNamespace != null) {
writeNewLine();
writeIndent();
writer.write("xmlns=\"");
writer.write(XMLUtil.escapeDoubleQuotedAttValue(defaultNamespace));
writer.write("\"");
}
for (Map.Entry entry : namespaceTable.entrySet()) {
String name = entry.getKey();
String prefix = entry.getValue();
writeNewLine();
writeIndent();
writer.write("xmlns:");
writer.write(prefix);
writer.write("=\"");
writer.write(XMLUtil.escapeDoubleQuotedAttValue(name));
writer.write("\"");
}
writeEndOfStartTag();
}
writeNewLine();
}
finally {
headerWritten = true;
}
}
public void endRDF()
throws RDFHandlerException
{
if (!writingStarted) {
throw new RDFHandlerException("Document writing has not yet started");
}
try {
if (!headerWritten) {
writeHeader();
}
flushPendingStatements();
writeNewLine();
if (getWriterConfig().get(XMLWriterSettings.INCLUDE_ROOT_RDF_TAG)) {
writeEndTag(RDF.NAMESPACE, "RDF");
}
writer.flush();
}
catch (IOException e) {
throw new RDFHandlerException(e);
}
finally {
writingStarted = false;
headerWritten = false;
}
}
public void handleNamespace(String prefix, String name) {
setNamespace(prefix, name);
}
protected void setNamespace(String prefix, String name) {
if (headerWritten) {
// Header containing namespace declarations has already been written
return;
}
if (prefix.length() == 0) {
defaultNamespace = name;
return;
}
if (namespaceTable.containsKey(name)) {
// Namespace is already mapped to a prefix
return;
}
// Try to give the namespace the specified prefix
boolean isLegalPrefix = XMLUtil.isNCName(prefix);
if (!isLegalPrefix || namespaceTable.containsValue(prefix)) {
// Specified prefix is not legal or the prefix is already in use,
// generate a legal unique prefix
if (!isLegalPrefix) {
prefix = "ns";
}
int number = 1;
while (namespaceTable.containsValue(prefix + number)) {
number++;
}
prefix += number;
}
namespaceTable.put(name, prefix);
}
public void handleStatement(Statement st)
throws RDFHandlerException
{
if (!writingStarted) {
throw new RDFHandlerException("Document writing has not yet been started");
}
Resource subj = st.getSubject();
URI pred = st.getPredicate();
Value obj = st.getObject();
// Verify that an XML namespace-qualified name can be created for the
// predicate
String predString = pred.toString();
int predSplitIdx = XMLUtil.findURISplitIndex(predString);
if (predSplitIdx == -1) {
throw new RDFHandlerException("Unable to create XML namespace-qualified name for predicate: "
+ predString);
}
String predNamespace = predString.substring(0, predSplitIdx);
String predLocalName = predString.substring(predSplitIdx);
try {
if (!headerWritten) {
writeHeader();
}
// SUBJECT
if (!subj.equals(lastWrittenSubject)) {
flushPendingStatements();
// Write new subject:
writeNewLine();
writeStartOfStartTag(RDF.NAMESPACE, "Description");
if (subj instanceof BNode) {
BNode bNode = (BNode)subj;
writeAttribute(RDF.NAMESPACE, "nodeID", getValidNodeId(bNode));
}
else {
URI uri = (URI)subj;
writeAttribute(RDF.NAMESPACE, "about", uri.toString());
}
writeEndOfStartTag();
writeNewLine();
lastWrittenSubject = subj;
}
// PREDICATE
writeIndent();
writeStartOfStartTag(predNamespace, predLocalName);
// OBJECT
if (obj instanceof Resource) {
Resource objRes = (Resource)obj;
if (objRes instanceof BNode) {
BNode bNode = (BNode)objRes;
writeAttribute(RDF.NAMESPACE, "nodeID", getValidNodeId(bNode));
}
else {
URI uri = (URI)objRes;
writeAttribute(RDF.NAMESPACE, "resource", uri.toString());
}
writeEndOfEmptyTag();
}
else if (obj instanceof Literal) {
Literal objLit = (Literal)obj;
// datatype attribute
boolean isXMLLiteral = false;
// language attribute
if (Literals.isLanguageLiteral(objLit)) {
writeAttribute("xml:lang", objLit.getLanguage());
}
else {
URI datatype = objLit.getDatatype();
// Check if datatype is rdf:XMLLiteral
isXMLLiteral = datatype.equals(RDF.XMLLITERAL);
if (isXMLLiteral) {
writeAttribute(RDF.NAMESPACE, "parseType", "Literal");
}
else {
writeAttribute(RDF.NAMESPACE, "datatype", datatype.toString());
}
}
writeEndOfStartTag();
// label
if (isXMLLiteral) {
// Write XML literal as plain XML
writer.write(objLit.getLabel());
}
else {
writeCharacterData(objLit.getLabel());
}
writeEndTag(predNamespace, predLocalName);
}
writeNewLine();
// Don't write yet, maybe the next statement
// has the same subject.
}
catch (IOException e) {
throw new RDFHandlerException(e);
}
}
public void handleComment(String comment)
throws RDFHandlerException
{
try {
if (!headerWritten) {
writeHeader();
}
flushPendingStatements();
writer.write("");
writeNewLine();
}
catch (IOException e) {
throw new RDFHandlerException(e);
}
}
protected void flushPendingStatements()
throws IOException, RDFHandlerException
{
if (lastWrittenSubject != null) {
// The last statement still has to be closed:
writeEndTag(RDF.NAMESPACE, "Description");
writeNewLine();
lastWrittenSubject = null;
}
}
protected void writeStartOfStartTag(String namespace, String localName)
throws IOException
{
if (namespace.equals(defaultNamespace)) {
writer.write("<");
writer.write(localName);
}
else {
String prefix = namespaceTable.get(namespace);
// If the prefix was not defined, or the root rdf:RDF tag was not
// written, then use xmlns=...
if (prefix == null || !getWriterConfig().get(XMLWriterSettings.INCLUDE_ROOT_RDF_TAG)) {
writer.write("<");
writer.write(localName);
writer.write(" xmlns=\"");
writer.write(XMLUtil.escapeDoubleQuotedAttValue(namespace));
writer.write("\"");
}
else {
writer.write("<");
writer.write(prefix);
writer.write(":");
writer.write(localName);
}
}
}
protected void writeAttribute(String attName, String value)
throws IOException
{
writer.write(" ");
writer.write(attName);
writer.write("=\"");
writer.write(XMLUtil.escapeDoubleQuotedAttValue(value));
writer.write("\"");
}
protected void writeAttribute(String namespace, String attName, String value)
throws IOException, RDFHandlerException
{
// Note: attribute cannot use the default namespace
String prefix = namespaceTable.get(namespace);
if (prefix == null) {
throw new RDFHandlerException(
"No prefix has been declared for the namespace used in this attribute: " + namespace);
}
writer.write(" ");
writer.write(prefix);
writer.write(":");
writer.write(attName);
writer.write("=\"");
writer.write(XMLUtil.escapeDoubleQuotedAttValue(value));
writer.write("\"");
}
protected void writeEndOfStartTag()
throws IOException
{
writer.write(">");
}
protected void writeEndOfEmptyTag()
throws IOException
{
writer.write("/>");
}
protected void writeEndTag(String namespace, String localName)
throws IOException
{
if (namespace.equals(defaultNamespace)) {
writer.write("");
writer.write(localName);
writer.write(">");
}
else {
writer.write("");
String prefix = namespaceTable.get(namespace);
if (prefix != null) {
writer.write(prefix);
writer.write(":");
}
writer.write(localName);
writer.write(">");
}
}
protected void writeCharacterData(String chars)
throws IOException
{
writer.write(XMLUtil.escapeCharacterData(chars));
}
protected void writeIndent()
throws IOException
{
writer.write("\t");
}
protected void writeNewLine()
throws IOException
{
writer.write("\n");
}
/**
* Create a syntactically valid node id from the supplied blank node id. This
* is necessary because RDF/XML syntax enforces the blank node id is a valid
* NCName.
*
* @param bNode
* a blank node identifier
* @return the blank node identifier converted to a form that is a valid
* NCName.
* @see section 7.2.34
* of the RDF/XML Syntax specification
*/
protected String getValidNodeId(BNode bNode)
throws IOException
{
String validNodeId = bNode.getID();
if (!XMLUtil.isNCName(validNodeId)) {
StringBuilder builder = new StringBuilder();
if (validNodeId.isEmpty()) {
if (this.getWriterConfig().get(BasicParserSettings.PRESERVE_BNODE_IDS)) {
throw new IOException("Cannot consistently write blank nodes with empty internal identifiers");
}
builder.append("genid-hash-");
builder.append(Integer.toHexString(System.identityHashCode(bNode)));
}
else {
if (!XMLUtil.isNCNameStartChar(validNodeId.charAt(0))) {
// prepend legal start char
builder.append("genid-start-");
builder.append(Integer.toHexString(validNodeId.charAt(0)));
}
else {
builder.append(validNodeId.charAt(0));
}
for (int i = 1; i < validNodeId.length(); i++) {
// do char-by-char scan and replace illegal chars where
// necessary.
if (XMLUtil.isNCNameChar(validNodeId.charAt(i))) {
builder.append(validNodeId.charAt(i));
}
else {
// replace incompatible char with encoded hex value that will
// always be alphanumeric.
builder.append(Integer.toHexString(validNodeId.charAt(i)));
}
}
}
validNodeId = builder.toString();
}
return validNodeId;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy