/*PLEASE DO NOT EDIT THIS CODE*/
/*This code was generated using the UMPLE 1.35.0.7523.c616a4dce modeling language!*/

package example;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.sql.*;
import java.lang.reflect.Constructor;

// line 4 "../ToJsonTest_1.ump"
public class Person
{

  //------------------------
  // MEMBER VARIABLES
  //------------------------

  //Person Attributes
  private String name;

  //Person Associations
  private List<Address> addresses;

  //------------------------
  // CONSTRUCTOR
  //------------------------

  public Person(String aName)
  {
    name = aName;
    addresses = new ArrayList<Address>();
  }

  //------------------------
  // INTERFACE
  //------------------------

  public boolean setName(String aName)
  {
    boolean wasSet = false;
    name = aName;
    wasSet = true;
    return wasSet;
  }

  public String getName()
  {
    return name;
  }
  /* Code from template association_GetMany */
  public Address getAddress(int index)
  {
    Address aAddress = addresses.get(index);
    return aAddress;
  }

  public List<Address> getAddresses()
  {
    List<Address> newAddresses = Collections.unmodifiableList(addresses);
    return newAddresses;
  }

  public int numberOfAddresses()
  {
    int number = addresses.size();
    return number;
  }

  public boolean hasAddresses()
  {
    boolean has = addresses.size() > 0;
    return has;
  }

  public int indexOfAddress(Address aAddress)
  {
    int index = addresses.indexOf(aAddress);
    return index;
  }
  /* Code from template association_MinimumNumberOfMethod */
  public static int minimumNumberOfAddresses()
  {
    return 0;
  }
  /* Code from template association_AddManyToOne */
  public Address addAddress(String aStreet)
  {
    return new Address(aStreet, this);
  }

  public boolean addAddress(Address aAddress)
  {
    boolean wasAdded = false;
    if (addresses.contains(aAddress)) { return false; }
    Person existingPerson = aAddress.getPerson();
    boolean isNewPerson = existingPerson != null && !this.equals(existingPerson);
    if (isNewPerson)
    {
      aAddress.setPerson(this);
    }
    else
    {
      addresses.add(aAddress);
    }
    wasAdded = true;
    return wasAdded;
  }

  public boolean removeAddress(Address aAddress)
  {
    boolean wasRemoved = false;
    //Unable to remove aAddress, as it must always have a person
    if (!this.equals(aAddress.getPerson()))
    {
      addresses.remove(aAddress);
      wasRemoved = true;
    }
    return wasRemoved;
  }
  /* Code from template association_AddIndexControlFunctions */
  public boolean addAddressAt(Address aAddress, int index)
  {  
    boolean wasAdded = false;
    if(addAddress(aAddress))
    {
      if(index < 0 ) { index = 0; }
      if(index > numberOfAddresses()) { index = numberOfAddresses() - 1; }
      addresses.remove(aAddress);
      addresses.add(index, aAddress);
      wasAdded = true;
    }
    return wasAdded;
  }

  public boolean addOrMoveAddressAt(Address aAddress, int index)
  {
    boolean wasAdded = false;
    if(addresses.contains(aAddress))
    {
      if(index < 0 ) { index = 0; }
      if(index > numberOfAddresses()) { index = numberOfAddresses() - 1; }
      addresses.remove(aAddress);
      addresses.add(index, aAddress);
      wasAdded = true;
    } 
    else 
    {
      wasAdded = addAddressAt(aAddress, index);
    }
    return wasAdded;
  }

  public void delete()
  {
    for(int i=addresses.size(); i > 0; i--)
    {
      Address aAddress = addresses.get(i - 1);
      aAddress.delete();
    }
  }


  public String toString()
  {
    return super.toString() + "["+
            "name" + ":" + getName()+ "]";
  }
       
  /*
  * Generate Json for this object and connected objects visited objects to enable avoidance of infinite loops
  *
  * @return a string in Json  format of this object
  */ 
  public String toJson()
  {
    HashSet<Object> visitedList = new HashSet<Object>();
    StringBuilder toJsonOutput = new StringBuilder();
    toJsonOutput.append("{\n");
    this.toJsonHelper(toJsonOutput, visitedList,1,true);
    toJsonOutput.append("\n}");
    return(toJsonOutput.toString());
  }
   
  /*
  * Helper function to generate Json for this object and connected objects visited objects to enable avoidance of infinite loops
  *
  * @param toJsonOutput  Output is aded to this as the network of objects is traversed
  * @param visitedList  Every concrete object visited is added so we don't re-outpu
  * @param nestLevel    As we output deeper objects, indent them more
  * @param atConcreteClass false when we are recursing to a superclass
  *     so we get the superclass data
  * @return whether or not anything was output (so we can tell whether we need to output a comma)
  */ 
  public boolean toJsonHelper(StringBuilder toJsonOutput, HashSet<Object> visitedList, int nestLevel, boolean atConcreteClass){
      
      String indent = "  ".repeat(nestLevel);
      boolean alreadyVisited = false;
      boolean haveOutputItem = false;
      
    if(atConcreteClass) {
      // This will not be true in a super call; output header
      toJsonOutput.append(indent+"\""+this.toString().split("@")[0]+ "\" : {\n"+indent+ "  \"umpleObjectID\" : \""+System.identityHashCode(this)+"\"");
      // Check if we have already visited this object. If so we will not output details
      alreadyVisited = visitedList.contains(this);
      if(!alreadyVisited) {
        toJsonOutput.append(",\n");
        visitedList.add(this);
      }
    }
    
    // There is no superclass of this class
    if(alreadyVisited) {
      toJsonOutput.append("\n");
    }
    else {
      // Check if this class has a superclass. If it does, we make a call to output superclass content 
      // This will keep calling super so the topmost attributes and associations appear first
      // When an object has not already been visited, output its details
      if(haveOutputItem){
      //toJsonOutput.append(",\n");  
      }
        
          if(haveOutputItem){
            toJsonOutput.append(",\n");
          }
          toJsonOutput.append(indent);
          toJsonOutput.append("  \"");
          toJsonOutput.append("name");
          toJsonOutput.append("\" : \"");
          String primValue_0=""+getName()+"";
          toJsonOutput.append(primValue_0.replace("\\","\\\\").replace("\"","\\\""));
          toJsonOutput.append("\"");
          haveOutputItem=true;
      //haveOutputItem = false;
      //haveOutputItem = false;
         if(haveOutputItem) {
           toJsonOutput.append(",\n");
         }
          toJsonOutput.append(indent);
            toJsonOutput.append("  \"");
            toJsonOutput.append("addresses");
            toJsonOutput.append("\"");
            toJsonOutput.append(" : [");
            toJsonOutput.append("\n");
            haveOutputItem = false;
            try{
              for (Address anItem_0 :getAddresses()){
              if(haveOutputItem) {
                toJsonOutput.append(",\n");
              }
              toJsonOutput.append(indent+"  {");
              toJsonOutput.append("\n");
              anItem_0.toJsonHelper(toJsonOutput, visitedList,nestLevel+2,true);
              toJsonOutput.append("\n");
              toJsonOutput.append(indent+"  }");
              haveOutputItem=true;
            }
            }catch (NullPointerException e){
            }
            toJsonOutput.append("\n");    
            toJsonOutput.append(indent+"  ]");
            haveOutputItem=true;
              
          toJsonOutput.append(indent+"  \n");
    }
    // Finalize the output of the concrete class
    if(atConcreteClass) {
      if(!alreadyVisited) {
        toJsonOutput.append("\n");
      }
      toJsonOutput.append(indent+"}");
    }
    haveOutputItem = true;
    return haveOutputItem;
  }

  
      
  /*
  * Deserialize Json string to instantiate Objects from top-level class
  *
  * @param umpleObjectIDMap<String, Object> mapping parsed objectID (from Json string) with newly instantiated object's objectID
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @return newly instantiated Object 
  */  

  public static Person fromJson(String aJsonString){
    // process the input jsonString so that it can further processed using regex
    aJsonString=aJsonString.replace("\n","").replace(" ","");   
    // a map to store the umpleObjectID present in jsonString
    Map<String, Object> umpleObjectIDMap=new HashMap<>();
    
    // instantiate a new object
Person anObject = new Person(aJsonString, umpleObjectIDMap);
    return anObject;
  }
   
  /*
  * A new constructor specifically implemented if -s genJson is specified
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @param umpleObjectIDMap<String, Integer> mapping parsed objectID (from Json string) with newly instantiated object's objectID
  */ 
  @SuppressWarnings("unchecked")
  public Person(String aJsonString, Map<String, Object> umpleObjectIDMap){
    // Initialize a HashMap to store the parsed result
    // key is the attribute name present in the jsonString
    // value is the attribute value present in the jsonString
      boolean visitedSuperClass=false;
    
    HashMap<String,String> parsedResult = new HashMap<String,String>();
    parsedResult = fromJsonParser(aJsonString);
    if(!parsedResult.isEmpty()){
    boolean classExist;
    boolean classChildExist;
    String parsedClassName=parsedResult.get("className");
    String childName=this.getClass().getSimpleName();
    classExist="Person".equals(parsedClassName);
    classChildExist=childName.equals(parsedClassName);
    // if top-level class does not exist, throw exception
    if(!classExist&&!classChildExist){
      throw new IllegalArgumentException("Top-level class \""+parsedClassName+"\" does not exist, please check the input json string");
    }
    String umpleObjectId=parsedResult.get("umpleObjectID");
    // Check if the object has already been visited and created
    //if((umpleObjectIDMap.get(umpleObjectId)==null)||visitedSuperClass){
      try{
      
    visitedSuperClass=false;
    // map the old objectID (in jsonString) with the newly created object's hashCode in umpleObjectIDMap 
    umpleObjectIDMap.put(umpleObjectId,this);
    
    String jsonKey="";
       jsonKey="name";
       //more types should be considered here
        this.name=parsedResult.get(jsonKey);
    
    // below for-loop check the association class of the top level class
       
       jsonKey="addresses";
       String newJsonString_0=parsedResult.get(jsonKey);
       String newIDRegex_0="\\\"umpleObjectID\\\"\\:\\\"[0-9]*\\\"";
       String newUmpleID_0=fromJsonParserHelper(newJsonString_0,newIDRegex_0);
       
       //Multiple associations
           List<String> multiAssoObjList_0 = new ArrayList<String>();
           multiAssoObjList_0=fromJsonParserList(newJsonString_0);
addresses=new ArrayList<Address>();
           for(String obj: multiAssoObjList_0){
             newUmpleID_0=fromJsonParserHelper(obj,newIDRegex_0);
             
             String newClassName_0=fromJsonParserClassName(obj);
           if(!umpleObjectIDMap.containsKey(newUmpleID_0)){
           
            boolean subClassFound=false;
            Class clazz_0=Class.forName(newClassName_0);
            Constructor constructor_0=clazz_0.getConstructor(String.class, Map.class);
            Object objectNew_0=constructor_0.newInstance(obj,umpleObjectIDMap);
addresses.add((Address)objectNew_0);
            }
           else{
addresses.add((Address)umpleObjectIDMap.get(newUmpleID_0));
           }
         }


    
    }catch(Exception e){
    }

    //}
  }
  } 
  /*
  * A json parser to parse the input jsonString, if -s genJson is specified
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @ return HashMap<String,String> that stores paresed result, key is the attribute(or associations) of an object, value is the attribute value or association string
  */ 
  public static HashMap<String,String> fromJsonParser(String jsonString){
    
    HashMap<String,String> parsedResultMap = new HashMap<String,String>();
    try{
      //Below (String, Pattern, Matcher) are the regex strings and their patterns and matcher used to process a jsonString
    
        // topLevelString is the regex representing the topLevel class name
        String topLevelString = "\\{\\\"[A-Z]\\w*\\\":";
        Pattern topLevelPattern = Pattern.compile(topLevelString);
        Matcher topLevelMatcher = topLevelPattern.matcher(jsonString);
        String quotes="\\\"";
        String colon="\\:";
        String colonSquareBracket="\\:\\[";
        
        if(topLevelMatcher.find()){
            //actual string that represent the topLevel className
            String topLevelStringFound=topLevelMatcher.group(0);
            String className=topLevelStringFound.split(quotes)[1];
            parsedResultMap.put("className", className);
            jsonString=jsonString.split(topLevelString,2)[1];
            
            //objIDString is the regex representing umpleObjectID that could be found in jsonString
            String objIDString = "\"umpleObjectID\"\\:\"[0-9]*\",";
            Pattern objIDPattern = Pattern.compile(objIDString);
            Matcher objIDMatcher = objIDPattern.matcher(jsonString);
            if(objIDMatcher.find()){
              String objIDFound=objIDMatcher.group(0);
              parsedResultMap.put("umpleObjectID", objIDFound.split(colon)[1].split(quotes)[1]);
              jsonString=jsonString.replaceFirst(objIDFound,"");
            }
            String timeString="\\\"\\w*\\\"\\:\\\"\\d{2}:\\d{2}\\:\\d{2}\\\"";
            Pattern timeStringPattern=Pattern.compile(timeString);
            Matcher timeStringPatternMatcher=timeStringPattern.matcher(jsonString);
            if(timeStringPatternMatcher.find()){
              String timeStringFound=timeStringPatternMatcher.group(0);
              parsedResultMap.put(timeStringFound.split(colon,2)[0].split(quotes)[1], timeStringFound.split(colon,2)[1].split(quotes)[1]);
          
            }
            // pairString is the regex for an object's attribute and attribute value, the string parsed will be in key-value formate
        String pairString = "(\\\"(?!umpleObjectID)[^\\\"]+)\\\":\\\"((?:\\\\\\\"|[^\\\"])*)";
        Pattern pairPattern=Pattern.compile(pairString);
        Matcher pairMatcher=pairPattern.matcher(jsonString);
        String associationString="(?=\\\"\\w*\\\"\\:\\[\\{)(?:(?=.*?\\[\\{(?!.*?\\1)(.*\\}\\](?!.*\\2).*))(?=.*?\\}\\](?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\[]*(?=\\2$)";
        Pattern associationPattern=Pattern.compile(associationString);
        String newObjJsonString="(?=\\,\\\"\\w*\\\"\\:\\{)(?:(?=.*?\\{\\\"(?!.*?\\1)(.*\\}(?!.*\\2).*))(?=.*?\\}(?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\{]*(?=\\2$)";
        Pattern newObjJsonPattern=Pattern.compile(newObjJsonString);
        String quoteColonQuote="\\\"\\:\\\"";
        //Keep on parsing the attribute's key-value pair, 
        //until a List (multi-associations) pattern or a newObject parttern (single association) is found
        while(pairMatcher.find()){
               String pairStringFound=pairMatcher.group(0);
              if(parsedResultMap.get(pairStringFound.split(colon)[0].split(quotes)[1])==null){
                String keyPair=pairStringFound.split(colon)[0].split(quotes)[1];
                String valuePair=pairStringFound.split(quoteColonQuote,2)[1];
                keyPair=keyPair.replace("\\\\","\\").replace("\\\"","\"");
                valuePair=valuePair.replace("\\\\","\\").replace("\\\"","\"");
                parsedResultMap.put(keyPair,valuePair);
                jsonString=jsonString.replaceFirst(pairString, "");
              }
                int lastIndex = 0;
                while (lastIndex<jsonString.length()){
                  String remainingJson=jsonString.substring(lastIndex);
                  Matcher associationMatcher=associationPattern.matcher(jsonString);
                  Matcher newObjJsonMatcher=newObjJsonPattern.matcher(jsonString);
                  
                  boolean assoFound=associationMatcher.find();
                  boolean newObjFound=newObjJsonMatcher.find();
                  
                  if (assoFound&&(!newObjFound||associationMatcher.start()<newObjJsonMatcher.start())){
                    String assoStringFound=associationMatcher.group(0);
                    String associationName=assoStringFound.split(colon,2)[0].split(quotes)[1];;
                    String associationItems=assoStringFound.split(colonSquareBracket,2)[1];
                    parsedResultMap.put(associationName, associationItems);
                    jsonString=jsonString.replaceFirst(associationString, "");
                  }
                  else if (newObjFound){
                    String newObjJsonFound=newObjJsonMatcher.group(0);
                    String newObjName=newObjJsonFound.split(colon,2)[0].split(quotes)[1];
                    String newObjItems=newObjJsonFound.split(colon,2)[1];
                    parsedResultMap.put(newObjName, newObjItems);
                    jsonString=jsonString.replaceFirst(newObjJsonString, "");
                  }else{
                    break;
                  }
                }
        }
        }
        return parsedResultMap;
    }catch(NullPointerException e){
      return parsedResultMap;
    }
  }
   
  public static String fromJsonParserHelper(String jsonString, String regexString){
    String umpleID = "";
    String quo="\\\"";
    try{
      Pattern umpleIDPattern=Pattern.compile(regexString);
      Matcher umpleIDMatcher=umpleIDPattern.matcher(jsonString);
      if(umpleIDMatcher.find()){
        String umpleIDStringFound=umpleIDMatcher.group(0);
        String idString="\\\"\\d*\\\"";
        Pattern idPattern=Pattern.compile(idString);
        Matcher idMatcher=idPattern.matcher(umpleIDStringFound);
        if(idMatcher.find()){
          umpleID=idMatcher.group(0).split(quo)[1];
        }
      }
      return umpleID;
    } catch(NullPointerException e){
      return umpleID;
    }
  }
   
  public static List<String> fromJsonParserList(String objListString){
    List<String> objList=new ArrayList<String>();
    try{
      String objString="(?:(?=.*?\\{(?!.*?\\1)(.*\\}(?!.*\\2).*))(?=.*?\\}(?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\{]*(?=\\2$)";
      Pattern objStringPattern=Pattern.compile(objString);
      Matcher objStringMatcher=objStringPattern.matcher(objListString);
      while(objStringMatcher.find()){
        objList.add(objStringMatcher.group(0));
      }
      return objList;
   }catch(NullPointerException e){
     return objList;
   }
  }
   
  public static String fromJsonParserClassName(String objNameString){
    String nextName="";
    String quotes="\\\"";
    try{
      String nextNameString="\\{\\\"[A-Z]\\w*\\\":";
      Pattern nextNamePattern=Pattern.compile(nextNameString);
      Matcher nextNameMatcher=nextNamePattern.matcher(objNameString);
      if(nextNameMatcher.find()){
        nextName=nextNameMatcher.group(0).split(quotes)[1];
       
      }
      return nextName;
   }catch(NullPointerException e){
     return nextName;
   }
  }
  
}
/*PLEASE DO NOT EDIT THIS CODE*/
/*This code was generated using the UMPLE 1.35.0.7523.c616a4dce modeling language!*/

package example;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.ArrayList;
import java.util.List;
import java.sql.*;
import java.lang.reflect.Constructor;

// line 8 "../ToJsonTest_1.ump"
public class Address
{

  //------------------------
  // MEMBER VARIABLES
  //------------------------

  //Address Attributes
  private String street;

  //Address Associations
  private Person person;

  //------------------------
  // CONSTRUCTOR
  //------------------------

  public Address(String aStreet, Person aPerson)
  {
    street = aStreet;
    boolean didAddPerson = setPerson(aPerson);
    if (!didAddPerson)
    {
      throw new RuntimeException("Unable to create address due to person. See https://manual.umple.org?RE002ViolationofAssociationMultiplicity.html");
    }
  }

  //------------------------
  // INTERFACE
  //------------------------

  public boolean setStreet(String aStreet)
  {
    boolean wasSet = false;
    street = aStreet;
    wasSet = true;
    return wasSet;
  }

  public String getStreet()
  {
    return street;
  }
  /* Code from template association_GetOne */
  public Person getPerson()
  {
    return person;
  }
  /* Code from template association_SetOneToMany */
  public boolean setPerson(Person aPerson)
  {
    boolean wasSet = false;
    if (aPerson == null)
    {
      return wasSet;
    }

    Person existingPerson = person;
    person = aPerson;
    if (existingPerson != null && !existingPerson.equals(aPerson))
    {
      existingPerson.removeAddress(this);
    }
    person.addAddress(this);
    wasSet = true;
    return wasSet;
  }

  public void delete()
  {
    Person placeholderPerson = person;
    this.person = null;
    if(placeholderPerson != null)
    {
      placeholderPerson.removeAddress(this);
    }
  }


  public String toString()
  {
    return super.toString() + "["+
            "street" + ":" + getStreet()+ "]" + System.getProperties().getProperty("line.separator") +
            "  " + "person = "+(getPerson()!=null?Integer.toHexString(System.identityHashCode(getPerson())):"null");
  }
       
  /*
  * Generate Json for this object and connected objects visited objects to enable avoidance of infinite loops
  *
  * @return a string in Json  format of this object
  */ 
  public String toJson()
  {
    HashSet<Object> visitedList = new HashSet<Object>();
    StringBuilder toJsonOutput = new StringBuilder();
    toJsonOutput.append("{\n");
    this.toJsonHelper(toJsonOutput, visitedList,1,true);
    toJsonOutput.append("\n}");
    return(toJsonOutput.toString());
  }
   
  /*
  * Helper function to generate Json for this object and connected objects visited objects to enable avoidance of infinite loops
  *
  * @param toJsonOutput  Output is aded to this as the network of objects is traversed
  * @param visitedList  Every concrete object visited is added so we don't re-outpu
  * @param nestLevel    As we output deeper objects, indent them more
  * @param atConcreteClass false when we are recursing to a superclass
  *     so we get the superclass data
  * @return whether or not anything was output (so we can tell whether we need to output a comma)
  */ 
  public boolean toJsonHelper(StringBuilder toJsonOutput, HashSet<Object> visitedList, int nestLevel, boolean atConcreteClass){
      
      String indent = "  ".repeat(nestLevel);
      boolean alreadyVisited = false;
      boolean haveOutputItem = false;
      
    if(atConcreteClass) {
      // This will not be true in a super call; output header
      toJsonOutput.append(indent+"\""+this.toString().split("@")[0]+ "\" : {\n"+indent+ "  \"umpleObjectID\" : \""+System.identityHashCode(this)+"\"");
      // Check if we have already visited this object. If so we will not output details
      alreadyVisited = visitedList.contains(this);
      if(!alreadyVisited) {
        toJsonOutput.append(",\n");
        visitedList.add(this);
      }
    }
    
    // There is no superclass of this class
    if(alreadyVisited) {
      toJsonOutput.append("\n");
    }
    else {
      // Check if this class has a superclass. If it does, we make a call to output superclass content 
      // This will keep calling super so the topmost attributes and associations appear first
      // When an object has not already been visited, output its details
      if(haveOutputItem){
      //toJsonOutput.append(",\n");  
      }
        
          if(haveOutputItem){
            toJsonOutput.append(",\n");
          }
          toJsonOutput.append(indent);
          toJsonOutput.append("  \"");
          toJsonOutput.append("street");
          toJsonOutput.append("\" : \"");
          String primValue_0=""+getStreet()+"";
          toJsonOutput.append(primValue_0.replace("\\","\\\\").replace("\"","\\\""));
          toJsonOutput.append("\"");
          haveOutputItem=true;
      //haveOutputItem = false;
      //haveOutputItem = false;
         if(haveOutputItem) {
           toJsonOutput.append(",\n");
         }
          toJsonOutput.append(indent);
            toJsonOutput.append("\n"+indent);
            toJsonOutput.append("  \"");
            toJsonOutput.append("person");
            toJsonOutput.append("\"");
            toJsonOutput.append(" : ");
            toJsonOutput.append("\n");
            toJsonOutput.append(indent+"  {");
            toJsonOutput.append("\n");Person anotherItem_0 = getPerson();
            anotherItem_0.toJsonHelper(toJsonOutput, visitedList, nestLevel+2, true);
            toJsonOutput.append("\n");
            toJsonOutput.append(indent+"  }");
            haveOutputItem=true;
              
          toJsonOutput.append(indent+"  \n");
    }
    // Finalize the output of the concrete class
    if(atConcreteClass) {
      if(!alreadyVisited) {
        toJsonOutput.append("\n");
      }
      toJsonOutput.append(indent+"}");
    }
    haveOutputItem = true;
    return haveOutputItem;
  }

  
      
  /*
  * Deserialize Json string to instantiate Objects from top-level class
  *
  * @param umpleObjectIDMap<String, Object> mapping parsed objectID (from Json string) with newly instantiated object's objectID
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @return newly instantiated Object 
  */  

  public static Address fromJson(String aJsonString){
    // process the input jsonString so that it can further processed using regex
    aJsonString=aJsonString.replace("\n","").replace(" ","");   
    // a map to store the umpleObjectID present in jsonString
    Map<String, Object> umpleObjectIDMap=new HashMap<>();
    
    // instantiate a new object
Address anObject = new Address(aJsonString, umpleObjectIDMap);
    return anObject;
  }
   
  /*
  * A new constructor specifically implemented if -s genJson is specified
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @param umpleObjectIDMap<String, Integer> mapping parsed objectID (from Json string) with newly instantiated object's objectID
  */ 
  @SuppressWarnings("unchecked")
  public Address(String aJsonString, Map<String, Object> umpleObjectIDMap){
    // Initialize a HashMap to store the parsed result
    // key is the attribute name present in the jsonString
    // value is the attribute value present in the jsonString
      boolean visitedSuperClass=false;
    
    HashMap<String,String> parsedResult = new HashMap<String,String>();
    parsedResult = fromJsonParser(aJsonString);
    if(!parsedResult.isEmpty()){
    boolean classExist;
    boolean classChildExist;
    String parsedClassName=parsedResult.get("className");
    String childName=this.getClass().getSimpleName();
    classExist="Address".equals(parsedClassName);
    classChildExist=childName.equals(parsedClassName);
    // if top-level class does not exist, throw exception
    if(!classExist&&!classChildExist){
      throw new IllegalArgumentException("Top-level class \""+parsedClassName+"\" does not exist, please check the input json string");
    }
    String umpleObjectId=parsedResult.get("umpleObjectID");
    // Check if the object has already been visited and created
    //if((umpleObjectIDMap.get(umpleObjectId)==null)||visitedSuperClass){
      try{
      
    visitedSuperClass=false;
    // map the old objectID (in jsonString) with the newly created object's hashCode in umpleObjectIDMap 
    umpleObjectIDMap.put(umpleObjectId,this);
    
    String jsonKey="";
       jsonKey="street";
       //more types should be considered here
        this.street=parsedResult.get(jsonKey);
    
    // below for-loop check the association class of the top level class
       
       jsonKey="person";
       String newJsonString_0=parsedResult.get(jsonKey);
       String newIDRegex_0="\\\"umpleObjectID\\\"\\:\\\"[0-9]*\\\"";
       String newUmpleID_0=fromJsonParserHelper(newJsonString_0,newIDRegex_0);
       
       //Multiple associations
          
          if(!umpleObjectIDMap.containsKey(newUmpleID_0)){
            String newClassNameOne_0=fromJsonParserClassName(newJsonString_0);
            Class clazzOne_0=Class.forName(newClassNameOne_0);
            Constructor constructorOne_0=clazzOne_0.getConstructor(String.class, Map.class);
            Object objectNewOne_0=constructorOne_0.newInstance(newJsonString_0,umpleObjectIDMap);
Person oneAssoObj_0=(Person)objectNewOne_0;
person=oneAssoObj_0;
          }
          else{
person=(Person)umpleObjectIDMap.get(newUmpleID_0);
          }


    
    }catch(Exception e){
    }

    //}
  }
  } 
  /*
  * A json parser to parse the input jsonString, if -s genJson is specified
  * 
  * @param String aJsonString is the string in json format that is to be processed and turned into an object
  *
  * @ return HashMap<String,String> that stores paresed result, key is the attribute(or associations) of an object, value is the attribute value or association string
  */ 
  public static HashMap<String,String> fromJsonParser(String jsonString){
    
    HashMap<String,String> parsedResultMap = new HashMap<String,String>();
    try{
      //Below (String, Pattern, Matcher) are the regex strings and their patterns and matcher used to process a jsonString
    
        // topLevelString is the regex representing the topLevel class name
        String topLevelString = "\\{\\\"[A-Z]\\w*\\\":";
        Pattern topLevelPattern = Pattern.compile(topLevelString);
        Matcher topLevelMatcher = topLevelPattern.matcher(jsonString);
        String quotes="\\\"";
        String colon="\\:";
        String colonSquareBracket="\\:\\[";
        
        if(topLevelMatcher.find()){
            //actual string that represent the topLevel className
            String topLevelStringFound=topLevelMatcher.group(0);
            String className=topLevelStringFound.split(quotes)[1];
            parsedResultMap.put("className", className);
            jsonString=jsonString.split(topLevelString,2)[1];
            
            //objIDString is the regex representing umpleObjectID that could be found in jsonString
            String objIDString = "\"umpleObjectID\"\\:\"[0-9]*\",";
            Pattern objIDPattern = Pattern.compile(objIDString);
            Matcher objIDMatcher = objIDPattern.matcher(jsonString);
            if(objIDMatcher.find()){
              String objIDFound=objIDMatcher.group(0);
              parsedResultMap.put("umpleObjectID", objIDFound.split(colon)[1].split(quotes)[1]);
              jsonString=jsonString.replaceFirst(objIDFound,"");
            }
            String timeString="\\\"\\w*\\\"\\:\\\"\\d{2}:\\d{2}\\:\\d{2}\\\"";
            Pattern timeStringPattern=Pattern.compile(timeString);
            Matcher timeStringPatternMatcher=timeStringPattern.matcher(jsonString);
            if(timeStringPatternMatcher.find()){
              String timeStringFound=timeStringPatternMatcher.group(0);
              parsedResultMap.put(timeStringFound.split(colon,2)[0].split(quotes)[1], timeStringFound.split(colon,2)[1].split(quotes)[1]);
          
            }
            // pairString is the regex for an object's attribute and attribute value, the string parsed will be in key-value formate
        String pairString = "(\\\"(?!umpleObjectID)[^\\\"]+)\\\":\\\"((?:\\\\\\\"|[^\\\"])*)";
        Pattern pairPattern=Pattern.compile(pairString);
        Matcher pairMatcher=pairPattern.matcher(jsonString);
        String associationString="(?=\\\"\\w*\\\"\\:\\[\\{)(?:(?=.*?\\[\\{(?!.*?\\1)(.*\\}\\](?!.*\\2).*))(?=.*?\\}\\](?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\[]*(?=\\2$)";
        Pattern associationPattern=Pattern.compile(associationString);
        String newObjJsonString="(?=\\,\\\"\\w*\\\"\\:\\{)(?:(?=.*?\\{\\\"(?!.*?\\1)(.*\\}(?!.*\\2).*))(?=.*?\\}(?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\{]*(?=\\2$)";
        Pattern newObjJsonPattern=Pattern.compile(newObjJsonString);
        String quoteColonQuote="\\\"\\:\\\"";
        //Keep on parsing the attribute's key-value pair, 
        //until a List (multi-associations) pattern or a newObject parttern (single association) is found
        while(pairMatcher.find()){
               String pairStringFound=pairMatcher.group(0);
              if(parsedResultMap.get(pairStringFound.split(colon)[0].split(quotes)[1])==null){
                String keyPair=pairStringFound.split(colon)[0].split(quotes)[1];
                String valuePair=pairStringFound.split(quoteColonQuote,2)[1];
                keyPair=keyPair.replace("\\\\","\\").replace("\\\"","\"");
                valuePair=valuePair.replace("\\\\","\\").replace("\\\"","\"");
                parsedResultMap.put(keyPair,valuePair);
                jsonString=jsonString.replaceFirst(pairString, "");
              }
                int lastIndex = 0;
                while (lastIndex<jsonString.length()){
                  String remainingJson=jsonString.substring(lastIndex);
                  Matcher associationMatcher=associationPattern.matcher(jsonString);
                  Matcher newObjJsonMatcher=newObjJsonPattern.matcher(jsonString);
                  
                  boolean assoFound=associationMatcher.find();
                  boolean newObjFound=newObjJsonMatcher.find();
                  
                  if (assoFound&&(!newObjFound||associationMatcher.start()<newObjJsonMatcher.start())){
                    String assoStringFound=associationMatcher.group(0);
                    String associationName=assoStringFound.split(colon,2)[0].split(quotes)[1];;
                    String associationItems=assoStringFound.split(colonSquareBracket,2)[1];
                    parsedResultMap.put(associationName, associationItems);
                    jsonString=jsonString.replaceFirst(associationString, "");
                  }
                  else if (newObjFound){
                    String newObjJsonFound=newObjJsonMatcher.group(0);
                    String newObjName=newObjJsonFound.split(colon,2)[0].split(quotes)[1];
                    String newObjItems=newObjJsonFound.split(colon,2)[1];
                    parsedResultMap.put(newObjName, newObjItems);
                    jsonString=jsonString.replaceFirst(newObjJsonString, "");
                  }else{
                    break;
                  }
                }
        }
        }
        return parsedResultMap;
    }catch(NullPointerException e){
      return parsedResultMap;
    }
  }
   
  public static String fromJsonParserHelper(String jsonString, String regexString){
    String umpleID = "";
    String quo="\\\"";
    try{
      Pattern umpleIDPattern=Pattern.compile(regexString);
      Matcher umpleIDMatcher=umpleIDPattern.matcher(jsonString);
      if(umpleIDMatcher.find()){
        String umpleIDStringFound=umpleIDMatcher.group(0);
        String idString="\\\"\\d*\\\"";
        Pattern idPattern=Pattern.compile(idString);
        Matcher idMatcher=idPattern.matcher(umpleIDStringFound);
        if(idMatcher.find()){
          umpleID=idMatcher.group(0).split(quo)[1];
        }
      }
      return umpleID;
    } catch(NullPointerException e){
      return umpleID;
    }
  }
   
  public static List<String> fromJsonParserList(String objListString){
    List<String> objList=new ArrayList<String>();
    try{
      String objString="(?:(?=.*?\\{(?!.*?\\1)(.*\\}(?!.*\\2).*))(?=.*?\\}(?!.*?\\2)(.*)).)+?.*?(?=\\1)[^\\{]*(?=\\2$)";
      Pattern objStringPattern=Pattern.compile(objString);
      Matcher objStringMatcher=objStringPattern.matcher(objListString);
      while(objStringMatcher.find()){
        objList.add(objStringMatcher.group(0));
      }
      return objList;
   }catch(NullPointerException e){
     return objList;
   }
  }
   
  public static String fromJsonParserClassName(String objNameString){
    String nextName="";
    String quotes="\\\"";
    try{
      String nextNameString="\\{\\\"[A-Z]\\w*\\\":";
      Pattern nextNamePattern=Pattern.compile(nextNameString);
      Matcher nextNameMatcher=nextNamePattern.matcher(objNameString);
      if(nextNameMatcher.find()){
        nextName=nextNameMatcher.group(0).split(quotes)[1];
       
      }
      return nextName;
   }catch(NullPointerException e){
     return nextName;
   }
  }
  
}