001/*
002 * (c) Copyright 2009 University of Bristol
003 * All rights reserved.
004 * [See end of file]
005 */
006
007package net.rootdev.javardfa.output;
008
009import java.io.OutputStream;
010import javax.xml.stream.XMLOutputFactory;
011import javax.xml.stream.XMLStreamException;
012import javax.xml.stream.XMLStreamWriter;
013import net.rootdev.javardfa.StatementSink;
014import org.slf4j.Logger;
015import org.slf4j.LoggerFactory;
016
017/**
018 * A pretty ropey RDF/XML serialiser.
019 * Advantages: streams, no dependencies.
020 *
021 * @author pldms
022 */
023public class RDFXMLSink implements StatementSink {
024    final static Logger log = LoggerFactory.getLogger(RDFXMLSink.class);
025    final static String RDFNS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
026
027    private final XMLStreamWriter out;
028    private final String[] comments;
029
030    public RDFXMLSink(OutputStream os, String... comments) {
031        this.comments = comments;
032        XMLOutputFactory factory = XMLOutputFactory.newInstance();
033        factory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
034        try {
035            out = factory.createXMLStreamWriter(os, "utf-8");
036        } catch (XMLStreamException ex) {
037            throw new RuntimeException("Couldn't create writer", ex);
038        }
039    }
040
041    public void start() {
042        try {
043            out.writeStartDocument("utf-8", "1.0");
044            if (comments.length != 0) {
045                out.writeCharacters("\n");
046                StringBuilder sb = new StringBuilder("\n");
047                for (String line: comments) { sb.append(line); sb.append("\n"); }
048                out.writeComment(sb.toString());
049            }
050            out.writeCharacters("\n");
051            out.writeStartElement(RDFNS, "RDF");
052            out.writeNamespace("rdf", RDFNS);
053            out.writeCharacters("\n");
054        } catch (XMLStreamException ex) {
055            throw new RuntimeException("Problem starting document", ex);
056        }
057    }
058
059    public void end() {
060        try {
061            out.writeEndDocument();
062            out.flush();
063            out.close();
064        } catch (XMLStreamException ex) {
065            throw new RuntimeException("Problem ending document", ex);
066        }
067    }
068
069    public void addObject(String subject, String predicate, String object) {
070        try {
071            out.writeStartElement(RDFNS, "Description");
072            writeSubject(subject);
073            out.writeCharacters("\n\t");
074            writePredicate(predicate, true); // closed
075            writeObject(object);
076            out.writeCharacters("\n");
077            out.writeEndElement();
078            out.writeCharacters("\n");
079        } catch (XMLStreamException ex) {
080            throw new RuntimeException("Problem writing statement", ex);
081        }
082    }
083
084    public void addLiteral(String subject, String predicate, String lex, String lang, String datatype) {
085        try {
086            out.writeStartElement(RDFNS, "Description");
087            writeSubject(subject);
088            out.writeCharacters("\n\t");
089            writePredicate(predicate, false); // not closed
090            writeLiteral(lex, lang, datatype);
091            out.writeEndElement();
092            out.writeCharacters("\n");
093            out.writeEndElement();
094            out.writeCharacters("\n");
095        } catch (XMLStreamException ex) {
096            throw new RuntimeException("Problem writing statement", ex);
097        }
098    }
099
100    public void addPrefix(String prefix, String uri) {
101    }
102
103    private void writeSubject(String subject) throws XMLStreamException {
104        if (blank(subject))
105            out.writeAttribute(RDFNS, "nodeID", id(subject));
106        else
107            out.writeAttribute(RDFNS, "about", subject);
108    }
109
110    private void writePredicate(String predicate, boolean closed) throws XMLStreamException {
111        String[] nsln = split(predicate);
112        if (closed)
113            out.writeEmptyElement("ns", nsln[1], nsln[0]);
114        else
115            out.writeStartElement("ns", nsln[1], nsln[0]);
116        out.writeNamespace("ns", nsln[0]);
117    }
118
119    private void writeObject(String object) throws XMLStreamException {
120        if (blank(object))
121            out.writeAttribute(RDFNS, "nodeID", id(object));
122        else
123            out.writeAttribute(RDFNS, "resource", object);
124    }
125
126    private void writeLiteral(String lex, String lang, String datatype) throws XMLStreamException {
127        if (lang != null)
128            out.writeAttribute("xml:lang", lang);
129        else if (datatype != null)
130            out.writeAttribute(RDFNS, "datatype", datatype);
131        out.writeCharacters(lex);
132    }
133
134    private boolean blank(String subject) {
135        return subject.startsWith("_:");
136    }
137
138    private String id(String subject) {
139        return subject.substring(2);
140    }
141
142    protected String[] split(String predicate) {
143        String[] toReturn = new String[2];
144        int i = predicate.length() - 1;
145        int lastStartChar = -1;
146        while (i > 0 && isNameChar(predicate.codePointAt(i))) {
147            if (isNameStartChar(predicate.codePointAt(i)))
148                lastStartChar = i;
149            i--;
150        }
151        if (lastStartChar == -1)
152            throw new RuntimeException("Unsplitable predicate " + predicate);
153        toReturn[0] = predicate.substring(0, lastStartChar);
154        toReturn[1] = predicate.substring(lastStartChar);
155        return toReturn;
156    }
157
158    private boolean isNameChar(int cp) {
159        return isNameStartChar(cp) ||
160                (cp == '.') ||
161                (cp == '-') ||
162                (cp >= '0' && cp <= '9');
163    }
164
165    private boolean isNameStartChar(int cp) {
166        return (cp >= 'a' && cp <= 'z') ||
167                (cp >= 'A' && cp <= 'Z') ||
168                cp == ':' ||
169                cp == '_';
170    }
171
172    public void setBase(String base) {}
173}
174
175/*
176 * (c) Copyright 2009 University of Bristol
177 * All rights reserved.
178 *
179 * Redistribution and use in source and binary forms, with or without
180 * modification, are permitted provided that the following conditions
181 * are met:
182 * 1. Redistributions of source code must retain the above copyright
183 *    notice, this list of conditions and the following disclaimer.
184 * 2. Redistributions in binary form must reproduce the above copyright
185 *    notice, this list of conditions and the following disclaimer in the
186 *    documentation and/or other materials provided with the distribution.
187 * 3. The name of the author may not be used to endorse or promote products
188 *    derived from this software without specific prior written permission.
189 *
190 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
191 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
192 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
193 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
194 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
195 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
196 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
197 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
198 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
199 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
200 */