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