/*
* Copyright 2008, 2009, 2010 Talis Systems Ltd.
*
* Licensed 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 com.talis.rdfwriters.json;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.jena.JenaRuntime;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFErrorHandler;
import org.apache.jena.rdf.model.RDFWriter;
import org.apache.jena.rdf.model.ResIterator;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
public class JSONJenaWriter implements RDFWriter{
private static final Logger LOG = LoggerFactory.getLogger(JSONJenaWriter.class);
static protected final String propBase = "http://jena.hpl.hp.com/json/properties/" ;
private String lineSeparator = JenaRuntime.getLineSeparator() ;
private RDFErrorHandler errorHandler = null;
private Map<String, Object> writerPropertyMap = null ;
private String baseURIref = null ;
private String baseURIrefHash = null ;
private Writer out = null;
public RDFErrorHandler setErrorHandler(RDFErrorHandler errHandler) {
RDFErrorHandler old = errorHandler;
errorHandler = errHandler;
return old;
}
public Object setProperty(String propName, Object propValue) {
if ( ! ( propValue instanceof String ) )
{
LOG.warn("JSON.setProperty: Property for '"+propName+"' is not a string") ;
propValue = propValue.toString() ;
}
// Store absolute name of property
propName = absolutePropName(propName) ;
if ( writerPropertyMap == null )
writerPropertyMap = new HashMap<String, Object>() ;
Object oldValue = writerPropertyMap.get(propName);
writerPropertyMap.put(propName, propValue);
return oldValue;
}
protected String absolutePropName(String propName)
{
if ( propName.indexOf(':') == -1 )
return JSONJenaWriter.propBase + propName ;
return propName ;
}
public void write(Model model, OutputStream output, String base) {
try {
Writer w = new BufferedWriter(new OutputStreamWriter(output, "UTF-8")) ;
write(model, w, base) ;
try { w.flush() ; } catch (IOException ioEx) {}
} catch (java.io.UnsupportedEncodingException ex)
{
System.err.println("Failed to create UTF-8 writer") ;
}
}
public void write(Model baseModel, Writer _out, String base) {
if (!(_out instanceof BufferedWriter))
_out = new BufferedWriter(_out);
out = _out;
if ( base != null )
{
baseURIref = base ;
if ( !base.endsWith("#") &&! isOpaque(base) )
baseURIrefHash = baseURIref+"#" ;
}
try {
processModel(baseModel) ;
} catch (IOException e) {
LOG.error("IOError writing model.", e);
}
}
private boolean isOpaque(String uri)
{
try {
return new URI(uri).isOpaque() ;
} catch (URISyntaxException ex) { return true ; }
}
private void processModel(Model baseModel) throws IOException
{
writeLine("{");
boolean first = true;
ResIterator subjectIterator = baseModel.listSubjects();
while (subjectIterator.hasNext()) {
if (!first) {
writeLine(",");
}
first = false;
Resource subjectResource = subjectIterator.nextResource();
processSubject(subjectResource);
}
writeLine("");
writeLine("}");
}
private void processSubject(Resource subjectResource) throws IOException {
write(" \"");
if (subjectResource.isAnon()) {
write("_:");
write(escape(subjectResource.asNode().getBlankNodeId().getLabelString()));
} else {
write(escape(subjectResource.getURI()));
}
writeLine("\" : {");
processProperties(subjectResource);
write(" }");
}
private void processProperties(Resource subjectResource) throws IOException {
HashSet<Property> propertiesToProcess = new HashSet<Property>();
StmtIterator allPropertiesIterator = subjectResource.listProperties();
while (allPropertiesIterator.hasNext()) {
Statement statement = allPropertiesIterator.nextStatement();
if (!propertiesToProcess.contains(statement.getPredicate().getURI())) {
propertiesToProcess.add(statement.getPredicate());
}
}
int processed = 0;
for (Property property : propertiesToProcess) {
write(" \"");
write(escape(property.getURI()));
write("\" : [ ");
StmtIterator propertyIterator = subjectResource.listProperties(property);
boolean moreThanOneValue = false;
while (propertyIterator.hasNext()) {
Statement propertyStatement = propertyIterator.nextStatement();
boolean moreValues = propertyIterator.hasNext();
if (moreValues) {
moreThanOneValue = true;
}
if (moreThanOneValue) {
writeLine("");
write(" ");
}
processProperty(propertyStatement);
if (moreValues) {
write(",");
}
}
if (moreThanOneValue) {
writeLine("");
write(" ");
} else {
write(" ");
}
write("]");
processed++;
if (processed < propertiesToProcess.size()) {
write(",");
}
writeLine("");
}
}
private void processProperty(Statement property) throws IOException {
write("{ \"value\" : \"");
if (property.getObject().isURIResource()) {
Resource r = (Resource)property.getObject();
write(escape(r.getURI()));
write("\", \"type\" : \"uri\"");
} else if (property.getObject().isLiteral()) {
Literal l = (Literal)property.getObject();
write(escape(l.getLexicalForm()));
write("\", \"type\" : \"literal\"");
String languageValue = l.getLanguage();
if (languageValue != null && !languageValue.trim().equals("")) {
write(", \"lang\" : \"");
write(languageValue);
write("\"");
}
String dataTypeValue = l.getDatatypeURI();
if (dataTypeValue != null && !dataTypeValue.trim().equals("")) {
write(", \"datatype\" : \"");
write(escape(dataTypeValue));
write("\"");
}
} else if (property.getObject().isAnon()) {
write("_:");
write(escape(property.getObject().asNode().getBlankNodeId().getLabelString()));
write("\", \"type\" : \"bnode\"");
}
write(" }");
}
protected String escape(String s) {
StringBuilder builder = new StringBuilder();
for (int i= 0; i<s.length(); i++) {
char c = s.charAt(i);
if (c == '"') {
builder.append("\\\"");
} else if (c == '\\') {
builder.append("\\\\");
} else if (c == '/') {
builder.append("\\/");
} else if (c == '\b') {
builder.append("\\b");
} else if (c == '\f') {
builder.append("\\f");
} else if (c == '\n') {
builder.append("\\n");
} else if (c == '\r') {
builder.append("\\r");
} else if (c == '\t') {
builder.append("\\t");
} else {
builder.append(c);
}
}
return builder.toString();
}
private void writeLine(String line) throws IOException {
write(line);
write(lineSeparator);
}
private void write(String line) throws IOException {
out.write(line);
}
}