/******************************************************************************* * Copyright (c) 2008 Cambridge Semantics Incorporated. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * File: $Source$ * Created by: Matthew Roy ( <a href="mailto:mroy@cambridgesemantics.com">mroy@cambridgesemantics.com </a>) * Created on: Oct 14, 2007 * Revision: $Id$ * * Contributors: * Cambridge Semantics Incorporated - initial API and implementation *******************************************************************************/ package org.openanzo.services.serialization; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.codehaus.jackson.JsonFactory; import org.codehaus.jackson.JsonParseException; import org.codehaus.jackson.JsonParser; import org.codehaus.jackson.JsonToken; import org.openanzo.exceptions.AnzoException; import org.openanzo.exceptions.ExceptionConstants; import org.openanzo.rdf.Constants; import org.openanzo.rdf.MemURI; import org.openanzo.rdf.Statement; import org.openanzo.rdf.URI; import org.openanzo.rdf.utils.JSONRdfParser; import org.openanzo.rdf.utils.SerializationConstants; import org.openanzo.services.INamedGraphUpdate; import org.openanzo.services.IPrecondition; import org.openanzo.services.impl.Precondition; import org.openanzo.services.impl.UpdateTransaction; /** * Parse JSON formatted repository update messages * * @author Matthew Roy ( <a href="mailto:mroy@us.ibm.com">mroy@us.ibm.com </a>) * */ public class JSONUpdatesReader implements IUpdatesReader { private final static JsonFactory factory = new JsonFactory(); private final String input; /** * Create a new updates reader from given input * * @param input * input containg update data */ public JSONUpdatesReader(String input) { this.input = input; } public void read(final IUpdatesHandler handler) throws AnzoException { JSONUpdatesReader.parseUpdateTransactions(input, handler); } /** * Parse the data within the reader, passing the results to the IRepositoryHandler * * @param reader * reader containing the data * @return collection of namedgraph updates * @throws AnzoException */ public static Collection<INamedGraphUpdate> parseUpdates(Reader reader) throws AnzoException { try { JsonParser parser = factory.createJsonParser(reader); boolean doneInternal = false; Collection<INamedGraphUpdate> updates = new ArrayList<INamedGraphUpdate>(); while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case START_OBJECT: updates.add(readNamedGraphUpdate(parser)); break; case END_OBJECT: doneInternal = true; break; case END_ARRAY: doneInternal = true; break; } } return updates; } catch (JsonParseException ioe) { throw new AnzoException(ExceptionConstants.IO.READ_ERROR, ioe, ioe.getMessage()); } catch (IOException ioe) { throw new AnzoException(ExceptionConstants.IO.READ_ERROR, ioe, ioe.getMessage()); } catch (AnzoException ae) { throw ae; } } /** * Parse the data within the reader, passing the results to the IRepositoryHandler * * @param input * input string containing the data * @param handler * Handler which will handle the elements within the data * @throws AnzoException */ public static void parseUpdateTransactions(String input, final IUpdatesHandler handler) throws AnzoException { try { JsonParser parser = factory.createJsonParser(input); while (parser.nextToken() != null) { JsonToken token = parser.getCurrentToken(); switch (token) { case START_ARRAY: handleTransactionArray(parser, handler); break; } } } catch (JsonParseException ioe) { throw new AnzoException(ExceptionConstants.IO.READ_ERROR, ioe, ioe.getMessage()); } catch (IOException ioe) { throw new AnzoException(ExceptionConstants.IO.READ_ERROR, ioe, ioe.getMessage()); } catch (AnzoException ae) { throw ae; } } static Collection<Statement> parseStatements(JsonParser parser) throws AnzoException, IOException, JsonParseException { boolean doneInternal = false; ArrayList<Statement> stmts = new ArrayList<Statement>(); while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case START_OBJECT: stmts.add(JSONRdfParser.parseStatement(parser)); break; case END_OBJECT: doneInternal = true; break; case END_ARRAY: doneInternal = true; break; } } return stmts; } @SuppressWarnings("unchecked") static void handleTransactionObject(JsonParser parser, final IUpdatesHandler handler) throws AnzoException, IOException, JsonParseException { Map<String, Object> transaction = new HashMap<String, Object>(); String objectName = null; boolean done = false; String currentName = null; while (!done && parser.nextToken() != null) { JsonToken token = parser.getCurrentToken(); switch (token) { case START_ARRAY: objectName = currentName; if (objectName.equals(SerializationConstants.transactionContext)) { transaction.put(objectName, parseStatements(parser)); } else if (objectName.equals(SerializationConstants.errorResult)) { boolean doneInternal = false; ArrayList<AnzoException> anzoExceptions = new ArrayList<AnzoException>(); while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case START_OBJECT: anzoExceptions.add(readError(parser)); break; case END_OBJECT: doneInternal = true; break; case END_ARRAY: transaction.put(objectName, anzoExceptions); doneInternal = true; break; } } } else if (objectName.equals(SerializationConstants.preconditions)) { boolean doneInternal = false; ArrayList<IPrecondition> preconditions = new ArrayList<IPrecondition>(); while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case START_OBJECT: preconditions.add(readPrecondition(parser)); break; case END_OBJECT: doneInternal = true; break; case END_ARRAY: transaction.put(objectName, preconditions); doneInternal = true; break; } } } else if (objectName.equals(SerializationConstants.namedGraphs)) { boolean doneInternal = false; Collection<INamedGraphUpdate> updates = new ArrayList<INamedGraphUpdate>(); while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case START_OBJECT: updates.add(readNamedGraphUpdate(parser)); break; case END_OBJECT: doneInternal = true; break; case END_ARRAY: transaction.put(objectName, updates); doneInternal = true; break; } } } break; case END_OBJECT: done = true; break; case END_ARRAY: break; case FIELD_NAME: currentName = parser.getText(); break; case VALUE_FALSE: transaction.put(parser.getCurrentName(), false); break; case VALUE_TRUE: transaction.put(parser.getCurrentName(), true); break; case VALUE_NULL: break; case VALUE_NUMBER_FLOAT: transaction.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_NUMBER_INT: transaction.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_STRING: transaction.put(parser.getCurrentName(), parser.getText()); break; } } String transactionUri = transaction.containsKey(SerializationConstants.transactionURI) ? (String) transaction.get(SerializationConstants.transactionURI) : null; String namedGraphRevisions = transaction.containsKey(SerializationConstants.namedGraphUpdates) ? (String) transaction.get(SerializationConstants.namedGraphUpdates) : null; long transactionTimestamp = transaction.containsKey(SerializationConstants.transactionTimestamp) ? ((Number) transaction.get(SerializationConstants.transactionTimestamp)).longValue() : -1; URI uri = (transactionUri != null) ? MemURI.create(transactionUri) : null; Collection<Statement> contextStatements = null; if (transaction.containsKey(SerializationConstants.transactionContext)) { contextStatements = (ArrayList<Statement>) transaction.get(SerializationConstants.transactionContext); } UpdateTransaction updateTransaction = new UpdateTransaction(uri, transactionTimestamp, contextStatements, new HashSet<IPrecondition>(), namedGraphRevisions); if (transaction.containsKey(SerializationConstants.errorResult)) { List<AnzoException> exceptions = (List<AnzoException>) transaction.get(SerializationConstants.errorResult); updateTransaction.getErrors().addAll(exceptions); } if (transaction.containsKey(SerializationConstants.preconditions)) { List<IPrecondition> preconditions = (List<IPrecondition>) transaction.get(SerializationConstants.preconditions); updateTransaction.getPreconditions().addAll(preconditions); } if (transaction.containsKey(SerializationConstants.namedGraphs)) { List<INamedGraphUpdate> updates = (List<INamedGraphUpdate>) transaction.get(SerializationConstants.namedGraphs); for (INamedGraphUpdate update : updates) { updateTransaction.addNamedGraphUpdate(update); } } handler.handleTransaction(updateTransaction); } static void handleTransactionArray(JsonParser parser, final IUpdatesHandler handler) throws AnzoException, JsonParseException, IOException { while (parser.nextToken() != null) { JsonToken token = parser.getCurrentToken(); switch (token) { case START_OBJECT: handleTransactionObject(parser, handler); break; case END_ARRAY: return; } } } @SuppressWarnings("null") static AnzoException readError(JsonParser parser) throws AnzoException, IOException, JsonParseException { boolean doneInternal = false; Map<String, Object> errorObject = new HashMap<String, Object>(); ArrayList<String> args = null; List<String> errorMessageArgs = null; String currentName = null; while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case FIELD_NAME: currentName = parser.getText(); break; case START_OBJECT: break; case END_OBJECT: doneInternal = true; break; case START_ARRAY: if (currentName.equals(SerializationConstants.errorMessageArg)) { args = new ArrayList<String>(); } break; case END_ARRAY: if (args != null) { errorMessageArgs = args; } args = null; break; case VALUE_NUMBER_FLOAT: errorObject.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_NUMBER_INT: errorObject.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_STRING: if (args != null) { args.add(parser.getText()); } else { errorObject.put(parser.getCurrentName(), parser.getText()); } break; } } String errorSubNumber = (String) errorObject.get(SerializationConstants.errorCode); long errorSubNum = (errorSubNumber != null) ? Long.parseLong(errorSubNumber) : 0; if (errorMessageArgs == null) errorMessageArgs = Collections.<String> emptyList(); return new AnzoException(errorSubNum, errorMessageArgs.toArray(new String[0])); } @SuppressWarnings( { "unchecked", "null" }) static INamedGraphUpdate readNamedGraphUpdate(JsonParser parser) throws AnzoException, IOException, JsonParseException { boolean doneInternal = false; Map<String, Object> updateMap = new HashMap<String, Object>(); String currentName = null; while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case FIELD_NAME: currentName = parser.getText(); break; case START_OBJECT: break; case END_OBJECT: doneInternal = true; break; case START_ARRAY: if (currentName.equals(SerializationConstants.metaRemovals) || currentName.equals(SerializationConstants.removals) || currentName.equals(SerializationConstants.metaAdditions) || currentName.equals(SerializationConstants.additions)) { updateMap.put(currentName, parseStatements(parser)); } break; case END_ARRAY: break; case VALUE_NUMBER_FLOAT: updateMap.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_NUMBER_INT: updateMap.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_STRING: updateMap.put(parser.getCurrentName(), parser.getText()); break; } } String uri = (String) updateMap.get(SerializationConstants.namedGraphUri); URI namedGraphURI = Constants.valueFactory.createURI(uri); String uuid = (updateMap.containsKey(SerializationConstants.namedGraphUUID)) ? (String) updateMap.get(SerializationConstants.namedGraphUUID) : null; URI uuidURI = (uuid != null) ? Constants.valueFactory.createURI(uuid) : null; Collection<Statement> removeStatements = (updateMap.containsKey(SerializationConstants.removals)) ? (Collection<Statement>) updateMap.get(SerializationConstants.removals) : null; Collection<Statement> metaRemoveStatements = (updateMap.containsKey(SerializationConstants.metaRemovals)) ? (Collection<Statement>) updateMap.get(SerializationConstants.metaRemovals) : null; Collection<Statement> additionStatements = (updateMap.containsKey(SerializationConstants.additions)) ? (Collection<Statement>) updateMap.get(SerializationConstants.additions) : null; Collection<Statement> metaAdditionStatements = (updateMap.containsKey(SerializationConstants.metaAdditions)) ? (Collection<Statement>) updateMap.get(SerializationConstants.metaAdditions) : null; return new NamedGraphUpdate(namedGraphURI, uuidURI, additionStatements, removeStatements, metaAdditionStatements, metaRemoveStatements); } /** * Parse a JSON array into the Preconditions that are encoded within * * @param command * JSONObject containing data for preconditions * @return set of preconditions contained within statements * @throws AnzoException */ @SuppressWarnings( { "unchecked", "null" }) static IPrecondition readPrecondition(JsonParser parser) throws AnzoException, IOException, JsonParseException { boolean doneInternal = false; boolean inQuery = false; Map<String, Object> preconditionMap = new HashMap<String, Object>(); Set<URI> currentList = null; String currentName = null; String objectName = null; while (!doneInternal && parser.nextToken() != null) { JsonToken objectToken = parser.getCurrentToken(); switch (objectToken) { case FIELD_NAME: currentName = parser.getText(); break; case START_OBJECT: if (currentName.equals(SerializationConstants.query)) { inQuery = true; } break; case END_OBJECT: if (!inQuery) { doneInternal = true; } else { inQuery = false; } break; case START_ARRAY: if (currentName.equals(SerializationConstants.defaultGraphs) || currentName.equals(SerializationConstants.namedGraphs)) { currentList = new HashSet<URI>(); objectName = currentName; } break; case END_ARRAY: if (currentList != null) { preconditionMap.put(objectName, currentList); } objectName = null; currentList = null; break; case VALUE_FALSE: preconditionMap.put(parser.getCurrentName(), Boolean.FALSE); break; case VALUE_TRUE: preconditionMap.put(parser.getCurrentName(), Boolean.TRUE); break; case VALUE_NUMBER_FLOAT: preconditionMap.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_NUMBER_INT: preconditionMap.put(parser.getCurrentName(), parser.getNumberValue()); break; case VALUE_STRING: if (currentList != null) { currentList.add(Constants.valueFactory.createURI(parser.getText())); } else { preconditionMap.put(parser.getCurrentName(), parser.getText()); } break; } } IPrecondition precondition = new Precondition(); if (preconditionMap.containsKey(SerializationConstants.defaultGraphs)) { precondition.setDefaultGraphUris((Set<URI>) preconditionMap.get(SerializationConstants.defaultGraphs)); } if (preconditionMap.containsKey(SerializationConstants.namedGraphs)) { precondition.setNamedGraphUris((Set<URI>) preconditionMap.get(SerializationConstants.namedGraphs)); } if (preconditionMap.containsKey(SerializationConstants.queryString)) precondition.setQuery((String) preconditionMap.get(SerializationConstants.queryString)); if (preconditionMap.containsKey(SerializationConstants.askQueryResult)) precondition.setResult((Boolean) preconditionMap.get(SerializationConstants.askQueryResult)); return precondition; } }