Package org.dmd.util.parsing

Source Code of org.dmd.util.parsing.JSONParser

//  ---------------------------------------------------------------------------
//  dark-matter-data
//  Copyright (c) 2010 dark-matter-data committers
//  ---------------------------------------------------------------------------
//  This program is free software; you can redistribute it and/or modify it
//  under the terms of the GNU Lesser General Public License as published by the
//  Free Software Foundation; either version 3 of the License, or (at your
//  option) any later version.
//  This program is distributed in the hope that it will be useful, but WITHOUT
//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
//  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
//  more details.
//  You should have received a copy of the GNU Lesser General Public License along
//  with this program; if not, see <http://www.gnu.org/licenses/lgpl.html>.
//  ---------------------------------------------------------------------------
package org.dmd.util.parsing;

import org.dmd.dmc.types.IntegerVar;
import org.dmd.dmc.util.DmcUncheckedObject;
import org.dmd.util.exceptions.ResultException;
import org.dmd.util.exceptions.ResultSet;

/**
* The JSONParser will parse a JSON formatted string and produce a set of DmcUncheckedObjects.
* These unchecked objects may be further processed to create actual DmcObject instances.
*/
public class JSONParser {
 
  // A globally used result set
  ResultSet    rsG;

  /**
   * Constructs a new JSON parser.
   */
  public JSONParser(){
    rsG = new ResultSet();
  }
 
  /**
   * @param input
   * @return an array of unchecked objects parse from the input.
   * @throws ResultException
   */
  public DmcUncheckedObject parse(String input) throws ResultException {
    return(parseObject(input));
  }
 
  /**
   * This method parses a single JSON formatted object of the form { "attr1": "value1", "attr2": [ "valuea", "valueb" ], "attr3": "value3"... }
   * @param in
   * @param objects
   * @throws ResultException
   */
  DmcUncheckedObject parseObject(String in) throws ResultException {
    IntegerVar    index       = new IntegerVar(0);
    boolean     wantStartCurly   = true;
    boolean     wantEndCurly     = false;
    String       input       = in.trim();
    DmcUncheckedObject  basic       = new DmcUncheckedObject();
   
    // Reset the result set
    rsG.reset();
   
    for(; index.intValue()<input.length(); index.set(index.intValue()+1)){
     
      if (wantStartCurly){
        if (input.charAt(index.intValue()) == '{'){
          wantStartCurly = false;
          index.set(index.intValue()+1);
          parseObject(input, index, basic);
          wantEndCurly = true;
        }
        else{
          // ERROR Expecting {
          ResultException ex = new ResultException();
          ex.addError("Expecting opening bracket for object.");
          throw(ex);
        }
      }
      else if (wantEndCurly){
        eatWhiteSpace(input, index, "end bracket for object");
       
        if (input.charAt(index.intValue()) == '}'){
          // This should be the last character, if not, we have extraneous stuff - and, because
          // we trimmed the string at the beginning of the process, we know that there's not
          // just whitespace there.
          if ( (input.length()-1) != index.intValue()){
            // ERROR extraneous stuff
            ResultException ex = new ResultException();
            ex.addError("Extraneous characters after object.");
            throw(ex);
          }
         
          // Set this so that we know we were terminated properly
          wantEndCurly = false;
        }
        else{
          // ERROR Expecting }
          ResultException ex = new ResultException();
          ex.addError("Expecting closing bracket for object.");
          throw(ex);
        }
      }
    }
   
   
   
//    // If there were no objects, that's an error too
//    if (objects.size() == 0){
//      // ERROR no objects
//    }
   
    if (wantEndCurly){
      // ERROR: We're missing the end curly brace
      ResultException ex = new ResultException();
      ex.addError("Closing bracket missing.");
      throw(ex);
    }
   
    return(basic);
   
  }
 
  void parseObject(String input, IntegerVar index, DmcUncheckedObject basic) throws ResultException {
    parseAttributes(input,index,basic);
  }
 
  /**
   * Once we've recognized an object class, we will attempt to read its attributes, which will be
   * contained in a set of curly braces. Attributes come in two basic forms, single valued and multi
   * valued. Single valued attributes appear as:
   * "name": "value"
   * Multi-valued attributes are arrays and appear as:
   * "name": [ "value", "value" ]
   * If there is another attribute coming, the attribute value will be followed by a comma ",".
   * @param input   The input string
   * @param index   The index at which we will start parsing.
   * @param go      The current object that we are trying to populate with attributes.
   * @param objects The place where will append new objects if we need to create them.
   */
  void parseAttributes(String input, IntegerVar index, DmcUncheckedObject basic) throws ResultException {
   
    for(; index.intValue()<input.length(); index.set(index.intValue()+1)){
     
      // We're between the curlies, so we're looking for attributes
      if (parseAttribute(input, index, basic)){
        // After parsing an attribute value, we eat the white space, so, if the
        // value didn't have a comma after it, the current character should be the
        // end bracket.
        if (input.charAt(index.intValue()) == '}'){         
          // We got the terminating bracket for the attributes, we're done, so break
         
          // We back up by one character so that the enclosing parseObject can
          // find the terminating bracket for the object
          index.set(index.intValue()-1);
          break;
        }
        else{
          // ERROR Expecting }
          ResultException ex = new ResultException();
          ex.addError("Expecting closing bracket for object.");
          throw(ex);
        }
      }
     
    }
  }
 
  /**
   * This function parses a single attribute. When we're finished, the index will be advanced to
   * the position of closing character of the last attribute value - this allows the calling
   * function to pick up processing
   * @param input The original input string.
   * @param index
   * @param go
   * @param objects
   * @return true if the attribute list is finished i.e. if the attribute value DOES NOT have a trailing comma.
   * @throws ResultException
   */
  boolean parseAttribute(String input, IntegerVar index, DmcUncheckedObject basic) throws ResultException {
    boolean rc = true;
    String attrName = getColonTerminatedChunk(input, index, "attribute name");
//    ImdAttributeDef ad = null;
//   
//    ad = schema.adef(attrName);
//   
//    if (ad == null){
//      ResultException ex = new ResultException();
//      ex.addError("Unknown attribute: " + attrName);
//      throw(ex);
//    }
   
//    DebugInfo.debug(attrName);
   
//    if(!go.allowsAttribute(ad)){
//      // The object doesn't allow this attribute, complain
//      ResultException ex = new ResultException();
//      ex.addError("Attribute: " + attrName + " is not allowed in objects of class: " + go.getConstructionClass().getName());
//      throw(ex);
//    }
   
   
    if (isMultiValued(input,index))
      parseMVAttribute(input, index, basic, attrName);
    else
      parseSVAttribute(input, index, basic, attrName);
   
//    // The attribute is okay for the object, now, parse it according to whether it's single or multi-valued.
//    if (BooleanVar.equals(true, ad.getIsSingleValued())){
//      parseSVAttribute(input, index, basic, attrName);
//    }
//    else{
//      parseMVAttribute(input, index, basic, attrName);
//    }
   
    // Eat any white space after the value
    eatWhiteSpace(input, index, "value separating comma");
   
    // If we wouldn't go beyond the end of the string, check if the next character is a comma.
    // If so, advance our index to the comma and return false (to indicate that we're not done
    // parsing attributes.
    if (input.charAt(index.intValue()) == ','){
//      index.set(index.intValue()+1);
      rc = false;
    }
   
    return(rc);
  }
 
  /**
   * This method moves forward to the next available character (eating whitespace as required) and
   * returns true if the next character is a [ and false otherwise.
   * @param input
   * @param index
   * @return
   */
  boolean isMultiValued(String input, IntegerVar index){
    boolean rc = false;
   
    for(int i=index.intValue(); i<input.length(); i++){
      if(Character.isWhitespace(input.charAt(i)))
        continue;
     
      if (input.charAt(i) == '[')
        rc = true;
      break;
    }
    return(rc);
  }
 
  /**
   * This method parses the value for a single valued attribute, which is just a quote delimited string e.g. "value".
   * @param input
   * @param index
   * @param go
   * @param ad
   * @throws ResultException
   */
  void parseSVAttribute(String input, IntegerVar index, DmcUncheckedObject basic, String attrName) throws ResultException {
    String value = getQuotedValue(input, index, "value for attribute: " + attrName);
   
//    if (attrName.equals("objClass")){
//      basic.addClassName(value);
//    }
//    else
      basic.addValue(attrName, value);
  }
 
  /**
   * This method parses the values for a multi-valued attribute. These values are represented as JSON arrays e.g.
   * [ "value", "value"... ]
   * @param input
   * @param index
   * @param go
   * @param ad
   * @throws ResultException
   */
  void parseMVAttribute(String input, IntegerVar index, DmcUncheckedObject basic, String attrName) throws ResultException {
    String value = null;
    boolean wantOpenBracket = true;
   
   
    for( ; index.intValue()<input.length(); index.set(index.intValue()+1)){
      if (wantOpenBracket){
        eatWhiteSpace(input, index, "open square bracket for array");
        if (input.charAt(index.intValue()) == '['){
          wantOpenBracket = false;
        }
      }
      else{
        value = getQuotedValue(input, index, "value for attribute: " + attrName);
       
        if (attrName.equals("objectClass")){
          basic.addToClasses(value);
        }
        else
          basic.addValue(attrName, value);
       
        eatWhiteSpace(input, index, "comma or end of array");
       
        if (input.charAt(index.intValue()) == ']'){
          // We've reached the end of the array
          index.set(index.intValue()+1);
          break;
        }
        else if (input.charAt(index.intValue()) == ','){
          // Go around for another attribute
        }
        else{
          // Don't know what the heck this is
          ResultException ex = new ResultException();
          ex.addError("Malformed array - value followed by unrecognized character: " + input.charAt(index.intValue()));
          throw(ex);
        }
      }
    }
  }
 
  /**
   * This method advances the index past any whitespace characters it finds, starting at the the
   * current index. If we advance beyond the end of the string, we throw an exception. Obviously,
   * you only want to call this when you know you're looking for something that should be there.
   * @param input
   * @param index
   * @param message Indicates what you're looking for
   * @throws ResultException
   */
  void eatWhiteSpace(String input, IntegerVar index, String message) throws ResultException {
    for( ;index.intValue()<input.length(); index.set(index.intValue() + 1)){
      if(Character.isWhitespace(input.charAt(index.intValue()))){
        continue;
      }
      else{
        break;
      }
    }
   
    if (index.intValue() == input.length()){
      ResultException ex = new ResultException();
      ex.addError("Ran out of characters while expecting to find: " + message);
      throw(ex);
    }
  }
 
  /**
   * This method starts at the specified index and grabs the string that should exist between a pair of
   * double quotes and is terminated by a colon e.g. "somestring":
   * @param input   The entire input string.
   * @param index   The index at which we're going to start looking.
   * @param message The message fragment indicating what we were trying to find e.g. class name, attribute name etc.
   * @return
   * @throws ResultException
   */
  String getColonTerminatedChunk(String input, IntegerVar index, String message) throws ResultException {
    String rc = null;
    int quote1  = -1;
    int quote2  = -1;
    int colon  = -1;
   
    try{
//      quote1 = indexOf(input,"\"",index.intValue()+1,"Expecting open quote: ");
      quote1 = indexOf(input,"\"",index.intValue(),"Expecting open quote: ");
      quote2 = indexOf(input,"\"",quote1+1,"Expecting close quote: ");
      colon  = indexOf(input,":",quote2+1,"Expecting colon after: ");
    }
    catch(ResultException ex){
      ex.result.lastResult().moreMessages("While trying to parse " + message);
      throw(ex);
    }
   
    // Grab the string between the quotes
    rc = input.substring(quote1+1,quote2);
   
    // If it's empty, pitch a fit
    if (rc.length() == 0){
      ResultException ex = new ResultException();
      ex.addError("Empty string while trying to parse " + message + " at index: " + index.intValue() + " in input string: " + input);
      throw(ex);
    }
   
    index.set(colon + 1);
   
    return(rc);
  }
 
  /**
   *
   * @param input
   * @param index
   * @param message
   * @return
   * @throws ResultException
   */
  String getQuotedValue(String input, IntegerVar index, String message) throws ResultException {
    String rc = null;
    int quote1  = -1;
    int quote2  = -1;
   
    try{
//      quote1 = indexOf(input,"\"",index.intValue()+1,"Expecting open quote: ");
      quote1 = indexOf(input,"\"",index.intValue(),"Expecting open quote: ");
      quote2 = indexOf(input,"\"",quote1+1,"Expecting close quote: ");
    }
    catch(ResultException ex){
      ex.result.lastResult().moreMessages("While trying to parse " + message);
      throw(ex);
    }
   
    // Grab the string between the quotes
    rc = input.substring(quote1+1,quote2);
   
    // If it's empty, pitch a fit
    if (rc.length() == 0){
      ResultException ex = new ResultException();
      ex.addError("Empty string while trying to parse " + message + " at index: " + index.intValue() + " in input string: " + input);
      throw(ex);
    }
   
    index.set(quote2 + 1);
   
    return(rc);
  }
 
  /**
   * This method tries to retrieve the index of the specified string after the specified index.
   * If we can't find the substr, we throw an exception with the specified message.
   * @param instr
   * @param substr
   * @param after
   * @param message
   * @return
   */
  private int indexOf(String instr, String substr, int after, String message) throws ResultException {
    if (after >= instr.length()){
      ResultException ex = new ResultException();
      ex.addError("Ran out of input while trying to find: " + substr + "  at offset: " + after + "  in input string: " + instr );
      throw(ex);
    }
   
    int rc = instr.indexOf(substr,after);
   
    if (rc == -1){
      ResultException ex = new ResultException();
      ex.addError(message + instr);
      throw(ex);
    }
   
    return(rc);
  }
 

}
TOP

Related Classes of org.dmd.util.parsing.JSONParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.