/* * (C) Copyright IBM Corp. 2013 * * LICENSE: Eclipse Public License v1.0 * http://www.eclipse.org/legal/epl-v10.html */ package com.ibm.gaiandb.webservices.scanner.json; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.HashSet; import java.util.Set; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import com.ibm.db2j.GenericWS; import com.ibm.gaiandb.diags.GDBMessages; import com.ibm.gaiandb.webservices.scanner.GaianHandler; import com.ibm.juno.core.ObjectList; import com.ibm.juno.core.ObjectMap; import com.ibm.juno.core.json.JsonParser; import com.ibm.juno.core.parser.ParseException; /** * * The purpose of this class is to provide a JSON scanner, which can parse JSON * files thanks to a GaianHandler handler. * * @author remi - IBM Hursley * */ public class JsonScanner implements Runnable { // ---------------------------------------------------------------------------------- // ----------------------------------------------------------------------- ATTRIBUTES // =========================================================================== Public // --------------------------------------------------------------------------- Static // Use PROPRIETARY notice if class contains a main() method, otherwise use // COPYRIGHT notice. public static final String COPYRIGHT_NOTICE = "(c) Copyright IBM Corp. 2013"; // -------------------------------------------------------------------------- Dynamic // ======================================================================== Protected // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic // ========================================================================== Private // --------------------------------------------------------------------------- Static /** * The name of the json objects which depth is on the root of the * input stream to scan. */ private static final String ROOT_NAME = "root"; // -------------------------------------------------------------------------- Dynamic private GenericWS caller; private InputStream inputStream; private DefaultHandler handler; // ---------------------------------------------------------------------------------- // ---------------------------------------------------------------------------- TOOLS // ---------------------------------------------------------------------------------- // -------------------------------------------------------------------------- METHODS // ===================================================================== Constructors // --------------------------------------------------------------------------- Public /** Creates a JsonScanner object. */ public JsonScanner(GenericWS caller, InputStream inputStream) {//GenericWS caller, this.caller = caller; this.inputStream = inputStream; this.handler = new GaianHandler(this.caller); } /** Creates a JsonScanner object. */ public JsonScanner(GenericWS caller, InputStream inputStream, DefaultHandler handler) { this.caller = caller; this.inputStream = inputStream; this.handler = handler; } // -------------------------------------------------------------------------- Private // =========================================================================== Public // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic @Override public void run() { try { this.start(this.handler); } catch (IOException ioe) { if (this.caller != null) this.caller.logException( GDBMessages.DSWRAPPER_GENERICWS_LOST_CONNECTION, "An IO exception occurred while reading the data sent by the web service.\n" + ioe.getMessage(), ioe); } catch (Exception e) { if (this.caller != null) this.caller.logException( GDBMessages.DSWRAPPER_GENERICWS_WRONG_FORMAT_FOR_RECEIVED_DATA, e.getMessage(), e); } catch (NoClassDefFoundError e) { if (this.caller != null) this.caller.logException( // Probably means that the JUNO jar can't be found GDBMessages.DSWRAPPER_GENERICWS_PARSER_ERROR, e.getMessage(), e); } finally { if (this.caller != null) { this.caller.confirmSendingOfLastRecord(); } } } // ======================================================================== Protected // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic // ========================================================================== Private // --------------------------------------------------------------------------- Static // -------------------------------------------------------------------------- Dynamic /** * Starts the scan of the given stream. * * @param inputStream * The stream to read, containing a json file's content. * @param handler * The handler managing the scan of the Json file. * * @throws IOException if problems occur while reading the stream. * @throws NullPointerException if the input stream is null. * @throws ParseException if the content of the stream is not is the json format. */ private void start(DefaultHandler handler) throws IOException, ParseException, NullPointerException { if (this.inputStream == null) { throw new NullPointerException("Trying to parse a null data."); } // Generates JsonObject Reader jsonReader = new InputStreamReader(this.inputStream); Object o = JsonParser.DEFAULT.parse(jsonReader, Object.class); // Goes through the object this.handleObject(o, null, handler); } /** * Depending on the type of the object given, lets the handler know * about the behaviour to have. * * @param jsonObject * the object parsed by the scanner. It can be a ObjectMap, * a ObectList or a String, Long, Integer, Double, Float or Boolean. * For any other type, the method doesn't call the handler. * @param objectName * The name of the current object. If this name is null, it * set up with the value "root". * @param handler * The GaianHandler to be called. */ private void handleObject(Object jsonObject, String objectName, DefaultHandler handler) { // Algorithm: // The Object can basically be 3 kind of objects: // - ObjectMap // - String, Long, Integer, Double, Float or Boolean // - ObjectList // // If the object is an Object Map, // the method will scan all the children. // If the child scanned is a String, Long, Integer, Double, Float or Boolean // Their name, value will define an attribute of the current object // the method will scan all the other children... // ...and apply the method recursively // If the object is an Object List, // the method will scan all the other children... // ...and apply the method recursively // ------------------------------------------------------------ // Beginning of document if (objectName == null || objectName.isEmpty()) { try { handler.startDocument(); } catch (SAXException e) { try { handler.error(new SAXParseException(e.getMessage(), null)); } catch (SAXException e1) { } } objectName = ROOT_NAME; } // ------------------------------------------------------------ // If the object is an Object Map, if (jsonObject instanceof ObjectMap) { JsonAttributes attributes = new JsonAttributes(); // --- Gets the details of the current object --- // scans all the children and generates an attribute if the child is // neither an ObjectList, nor an ObjectMap (but still a known type) Set<String> kids = ((ObjectMap)jsonObject).keySet(); Set<String> filteredKids = new HashSet<String>(kids); for (String kid : kids) { Object kidsValue = ((ObjectMap)jsonObject).get(kid); if ((kidsValue instanceof String) || (kidsValue instanceof Long) || (kidsValue instanceof Integer) || (kidsValue instanceof Double) || (kidsValue instanceof Float) || (kidsValue instanceof Boolean)) { attributes.addAttribute(kid, kidsValue); filteredKids.remove(kid); } } try { handler.startElement(null, null, objectName, attributes); } catch (SAXException e) { try { handler.error(new SAXParseException(e.getMessage(), null)); } catch (SAXException e1) { } } // --- Go through the kids of the current object --- // all the kids which are still in the Set are ObjectList or ObjectMap for (String kid : filteredKids) { // recursive call Object kidsValue = ((ObjectMap)jsonObject).get(kid); this.handleObject(kidsValue, kid, handler); } // --- Closes the current object --- try { handler.endElement(null, null, objectName); } catch (SAXException e) { try { handler.error(new SAXParseException(e.getMessage(), null)); } catch (SAXException e1) { } } } // ------------------------------------------------------------ // If the object is an Object Map, else if (jsonObject instanceof ObjectList) { ObjectList ol = (ObjectList)jsonObject; for (Object kid : ol) { this.handleObject(kid, objectName, handler); } } // ------------------------------------------------------------ // If the object is a String, Long, Integer, Double, Float or Boolean, // just print the content else if ((jsonObject instanceof String) || (jsonObject instanceof Long) || (jsonObject instanceof Integer) || (jsonObject instanceof Double) || (jsonObject instanceof Float) || (jsonObject instanceof Boolean)) { char [] objectValue = jsonObject.toString().toCharArray(); try { handler.startElement(null, null, objectName, null); handler.characters(objectValue, 0, objectValue.length); handler.endElement(null, null, objectName); } catch (SAXException e) { try { handler.error(new SAXParseException(e.getMessage(), null)); } catch (SAXException e1) { } } } // ------------------------------------------------------------ // End of document if (objectName.equals(ROOT_NAME)) { try { handler.endDocument(); } catch (SAXException e) { try { handler.error(new SAXParseException(e.getMessage(), null)); } catch (SAXException e1) { } } } } }