Java tutorial
/******************************************************************************* * Copyright (c) 2015 Eclipse RDF4J contributors, Aduna, and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/org/documents/edl-v10.php. *******************************************************************************/ package org.eclipse.rdf4j.rio.rdfjson; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.Collection; import java.util.HashSet; import java.util.Set; import org.apache.commons.io.input.BOMInputStream; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Literal; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.Value; import org.eclipse.rdf4j.model.ValueFactory; import org.eclipse.rdf4j.rio.RDFFormat; import org.eclipse.rdf4j.rio.RDFHandler; import org.eclipse.rdf4j.rio.RDFHandlerException; import org.eclipse.rdf4j.rio.RDFParseException; import org.eclipse.rdf4j.rio.RDFParser; import org.eclipse.rdf4j.rio.RioSetting; import org.eclipse.rdf4j.rio.helpers.AbstractRDFParser; import org.eclipse.rdf4j.rio.helpers.RDFJSONParserSettings; import com.fasterxml.jackson.core.JsonLocation; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; /** * {@link RDFParser} implementation for the RDF/JSON format * * @author Peter Ansell */ public class RDFJSONParser extends AbstractRDFParser implements RDFParser { private final RDFFormat actualFormat; /** * Creates a parser using {@link RDFFormat#RDFJSON} to identify the parser. */ public RDFJSONParser() { this(RDFFormat.RDFJSON); } /** * Creates a parser using the given RDFFormat to self identify. * * @param actualFormat */ public RDFJSONParser(final RDFFormat actualFormat) { this.actualFormat = actualFormat; } @Override public RDFFormat getRDFFormat() { return this.actualFormat; } @Override public void parse(final InputStream inputStream, final String baseUri) throws IOException, RDFParseException, RDFHandlerException { JsonParser jp = null; clear(); try { if (this.rdfHandler != null) { this.rdfHandler.startRDF(); } jp = RDFJSONUtility.JSON_FACTORY.createParser(new BOMInputStream(inputStream, false)); rdfJsonToHandlerInternal(this.rdfHandler, this.valueFactory, jp); } catch (final IOException e) { if (jp != null) { reportFatalError("Found IOException during parsing", e, jp.getCurrentLocation()); } else { reportFatalError(e); } } finally { clear(); if (jp != null) { try { jp.close(); } catch (final IOException e) { reportFatalError("Found exception while closing JSON parser", e, jp.getCurrentLocation()); } } } if (this.rdfHandler != null) { this.rdfHandler.endRDF(); } } /** * Creates a literal, using the current value, language, and datatype, and additionally using the given * {@link JsonLocation} to provide information about the line and column numbers in the event of a * warning, error or exception being generated by the creation of the literal. * * @param label * the literal's lexical label * @param language * the literal's language tag. Can be null. * @param datatype * the literal's datatype. Can be null. * @param currentLocation * the current JsonLocation. May not be null. * @return the created {@link Literal} object. * @throws RDFParseException */ protected Literal createLiteral(String label, String language, IRI datatype, JsonLocation currentLocation) throws RDFParseException { return createLiteral(label, language, datatype, currentLocation.getLineNr(), currentLocation.getColumnNr()); } protected void reportError(String msg, Throwable e, JsonLocation location, RioSetting<Boolean> setting) throws RDFParseException { reportError(msg, location.getLineNr(), location.getColumnNr(), setting); } protected void reportError(String msg, JsonLocation location, RioSetting<Boolean> setting) throws RDFParseException { reportError(msg, location.getLineNr(), location.getColumnNr(), setting); } protected void reportFatalError(String msg, Throwable e, JsonLocation location) throws RDFParseException { reportFatalError(msg, location.getLineNr(), location.getColumnNr()); } protected void reportFatalError(String msg, JsonLocation location) throws RDFParseException { reportFatalError(msg, location.getLineNr(), location.getColumnNr()); } @Override public void parse(final Reader reader, final String baseUri) throws IOException, RDFParseException, RDFHandlerException { JsonParser jp = null; clear(); try { if (this.rdfHandler != null) { this.rdfHandler.startRDF(); } jp = RDFJSONUtility.JSON_FACTORY.createParser(reader); rdfJsonToHandlerInternal(rdfHandler, valueFactory, jp); } catch (final IOException e) { if (jp != null) { reportFatalError("Found IOException during parsing", e, jp.getCurrentLocation()); } else { reportFatalError(e); } } finally { clear(); if (jp != null) { try { jp.close(); } catch (final IOException e) { reportFatalError("Found exception while closing JSON parser", e, jp.getCurrentLocation()); } } } if (rdfHandler != null) { rdfHandler.endRDF(); } } private void rdfJsonToHandlerInternal(final RDFHandler handler, final ValueFactory vf, final JsonParser jp) throws IOException, JsonParseException, RDFParseException, RDFHandlerException { if (jp.nextToken() != JsonToken.START_OBJECT) { reportFatalError("Expected RDF/JSON document to start with an Object", jp.getCurrentLocation()); } while (jp.nextToken() != JsonToken.END_OBJECT) { final String subjStr = jp.getCurrentName(); Resource subject = null; subject = subjStr.startsWith("_:") ? createNode(subjStr.substring(2)) : vf.createIRI(subjStr); if (jp.nextToken() != JsonToken.START_OBJECT) { reportFatalError("Expected subject value to start with an Object", jp.getCurrentLocation()); } boolean foundPredicate = false; while (jp.nextToken() != JsonToken.END_OBJECT) { final String predStr = jp.getCurrentName(); final IRI predicate = vf.createIRI(predStr); foundPredicate = true; if (jp.nextToken() != JsonToken.START_ARRAY) { reportFatalError("Expected predicate value to start with an array", jp.getCurrentLocation()); } boolean foundObject = false; while (jp.nextToken() != JsonToken.END_ARRAY) { if (jp.getCurrentToken() != JsonToken.START_OBJECT) { reportFatalError("Expected object value to start with an Object: subject=<" + subjStr + "> predicate=<" + predStr + ">", jp.getCurrentLocation()); } String nextValue = null; String nextType = null; String nextDatatype = null; String nextLanguage = null; final Set<String> nextContexts = new HashSet<String>(2); while (jp.nextToken() != JsonToken.END_OBJECT) { final String fieldName = jp.getCurrentName(); if (RDFJSONUtility.VALUE.equals(fieldName)) { if (nextValue != null) { reportError( "Multiple values found for a single object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation(), RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_VALUES); } jp.nextToken(); nextValue = jp.getText(); } else if (RDFJSONUtility.TYPE.equals(fieldName)) { if (nextType != null) { reportError( "Multiple types found for a single object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation(), RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_TYPES); } jp.nextToken(); nextType = jp.getText(); } else if (RDFJSONUtility.LANG.equals(fieldName)) { if (nextLanguage != null) { reportError( "Multiple languages found for a single object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation(), RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_LANGUAGES); } jp.nextToken(); nextLanguage = jp.getText(); } else if (RDFJSONUtility.DATATYPE.equals(fieldName)) { if (nextDatatype != null) { reportError( "Multiple datatypes found for a single object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation(), RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_DATATYPES); } jp.nextToken(); nextDatatype = jp.getText(); } else if (RDFJSONUtility.GRAPHS.equals(fieldName)) { if (jp.nextToken() != JsonToken.START_ARRAY) { reportError("Expected graphs to start with an array", jp.getCurrentLocation(), RDFJSONParserSettings.SUPPORT_GRAPHS_EXTENSION); } while (jp.nextToken() != JsonToken.END_ARRAY) { final String nextGraph = jp.getText(); nextContexts.add(nextGraph); } } else { reportError( "Unrecognised JSON field name for object: subject=" + subjStr + " predicate=" + predStr + " fieldname=" + fieldName, jp.getCurrentLocation(), RDFJSONParserSettings.FAIL_ON_UNKNOWN_PROPERTY); } } Value object = null; if (nextType == null) { reportFatalError("No type for object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } if (nextValue == null) { reportFatalError("No value for object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } if (RDFJSONUtility.LITERAL.equals(nextType)) { if (nextLanguage != null) { object = this.createLiteral(nextValue, nextLanguage, null, jp.getCurrentLocation()); } else if (nextDatatype != null) { object = this.createLiteral(nextValue, null, this.createURI(nextDatatype), jp.getCurrentLocation()); } else { object = this.createLiteral(nextValue, null, null, jp.getCurrentLocation()); } } else if (RDFJSONUtility.BNODE.equals(nextType)) { if (nextLanguage != null) { reportFatalError("Language was attached to a blank node object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } if (nextDatatype != null) { reportFatalError("Datatype was attached to a blank node object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } object = createNode(nextValue.substring(2)); } else if (RDFJSONUtility.URI.equals(nextType)) { if (nextLanguage != null) { reportFatalError("Language was attached to a uri object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } if (nextDatatype != null) { reportFatalError("Datatype was attached to a uri object: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } object = vf.createIRI(nextValue); } foundObject = true; if (!nextContexts.isEmpty()) { for (final String nextContext : nextContexts) { final Resource context; if (nextContext.equals(RDFJSONUtility.NULL)) { context = null; } else if (nextContext.startsWith("_:")) { context = createNode(nextContext.substring(2)); } else { context = vf.createIRI(nextContext); } Statement st = vf.createStatement(subject, predicate, object, context); if (handler != null) { handler.handleStatement(st); } } } else { Statement st = vf.createStatement(subject, predicate, object); if (handler != null) { handler.handleStatement(st); } } } if (!foundObject) { reportFatalError("No object for predicate: subject=" + subjStr + " predicate=" + predStr, jp.getCurrentLocation()); } } if (!foundPredicate) { reportFatalError("No predicate for object: subject=" + subjStr, jp.getCurrentLocation()); } } } @Override public Collection<RioSetting<?>> getSupportedSettings() { Collection<RioSetting<?>> result = new HashSet<RioSetting<?>>(super.getSupportedSettings()); result.add(RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_DATATYPES); result.add(RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_LANGUAGES); result.add(RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_TYPES); result.add(RDFJSONParserSettings.FAIL_ON_MULTIPLE_OBJECT_VALUES); result.add(RDFJSONParserSettings.FAIL_ON_UNKNOWN_PROPERTY); result.add(RDFJSONParserSettings.SUPPORT_GRAPHS_EXTENSION); return result; } }