/*PLEASE DO NOT EDIT THIS CODE*/
/*This code was generated using the UMPLE 1.38.0.8942.247e17422 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){
    // the parser below reads json with nothing between its tokens, so remove every whitespace character json allows there
    aJsonString=aJsonString.replace("\n","").replace("\r","").replace("\t","").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(java.lang.reflect.InvocationTargetException e){
      // an associated object with an unknown state must fail the load, as the top-level object does
      if(e.getCause() instanceof IllegalArgumentException){
        throw (IllegalArgumentException)e.getCause();
      }
    }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="\\\"";
        
        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);
            // The object's own "key":value pairs follow, a value being a "string", a [list] or an {object}.
            // Each value is skipped whole, so the pairs of an associated object are left for that object to read
            int pairStart=topLevelMatcher.end()+1;
            while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)=='"'){
              int keyEnd=fromJsonParserValueEnd(jsonString,pairStart);
              // without the colon there is no telling which value is the key's, so read no further
              if(keyEnd<0||!jsonString.startsWith(":",keyEnd)){
                break;
              }
              int valueEnd=fromJsonParserValueEnd(jsonString,keyEnd+1);
              if(valueEnd<0){
                break;
              }
              String key=jsonString.substring(pairStart+1,keyEnd-1);
              String value=jsonString.substring(keyEnd+1,valueEnd);
              // The class name and object id come first and win over an attribute named like them.
              // toJson quotes every attribute and state, so a bare null, boolean or number is left unread, as it always was
              if(value.startsWith("\"")){
                parsedResultMap.putIfAbsent(key,value.substring(1,value.length()-1).replace("\\\\","\\").replace("\\\"","\""));
              }
              else if(value.startsWith("[")||value.startsWith("{")){
                parsedResultMap.putIfAbsent(key,value);
              }
              // skip the comma, and the extra one left by a superclass with nothing to output
              pairStart=valueEnd;
              while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)==','){
                pairStart++;
              }
            }
        }
        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{
      // objListString is a [list]; each {object} in it is taken whole with the objects nested in it,
      // and any other element, which toJson does not write, is skipped
      int itemStart=1;
      while(itemStart<objListString.length()&&objListString.charAt(itemStart)!=']'){
        int itemEnd=fromJsonParserValueEnd(objListString,itemStart);
        if(itemEnd<0){
          break;
        }
        if(objListString.charAt(itemStart)=='{'){
          objList.add(objListString.substring(itemStart,itemEnd));
        }
        itemStart=itemEnd+1;
      }
      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;
   }
  }
   
  /*
  * Returns the index just after the json value that starts at index start,
  * or -1 if there is none there or it is cut short.
  * A "string", [list] or {object} ends where it closes; brackets and escaped quotes inside a string are text.
  * A bare null, boolean or number ends at the next comma or closing bracket
  */
  private static int fromJsonParserValueEnd(String jsonString, int start){
    if(start>=jsonString.length()){
      return -1;
    }
    if("\"[{".indexOf(jsonString.charAt(start))<0){
      int end=start;
      while(end<jsonString.length()&&",}]".indexOf(jsonString.charAt(end))<0){
        end++;
      }
      return end>start&&end<jsonString.length()?end:-1;
    }
    int depth=0;
    boolean inString=false;
    for(int i=start;i<jsonString.length();i++){
      char c=jsonString.charAt(i);
      if(inString){
        if(c=='\\'){
          i++;
        }
        else if(c=='"'){
          inString=false;
        }
      }
      else if(c=='"'){
        inString=true;
      }
      else if(c=='{'||c=='['){
        depth++;
      }
      else if(c=='}'||c==']'){
        depth--;
      }
      if(!inString&&depth==0){
        return i+1;
      }
    }
    return -1;
  }
  
}
/*PLEASE DO NOT EDIT THIS CODE*/
/*This code was generated using the UMPLE 1.38.0.8942.247e17422 modeling language!*/


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 3 "ToJsonTest_3.ump"
public class Player
{

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

  //Player Attributes
  private String name;

  //Player State Machines
  public enum Status { Off, On }
  public enum StatusOn { Null, Idle, Playing, H }
  private Status status;
  private StatusOn statusOn;
  private StatusOn statusOnH;
  public enum Mode { Active, Stopped }
  public enum ModeActive { Null, Waiting, Busy, HStar }
  private Mode mode;
  private ModeActive modeActive;
  private ModeActive modeActiveHStar;
  public enum Unused {  }
  private Unused unused;

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

  public Player(String aName)
  {
    name = aName;
    setStatusOn(StatusOn.Null);
    statusOnH = StatusOn.Idle;
    setStatus(Status.Off);
    setModeActive(ModeActive.Null);
    modeActiveHStar = ModeActive.Waiting;
    setMode(Mode.Active);
  }

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

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

  public String getName()
  {
    return name;
  }

  public String getStatusFullName()
  {
    String answer = status.toString();
    if (statusOn != StatusOn.Null) { answer += "." + statusOn.toString(); }
    return answer;
  }

  public String getModeFullName()
  {
    String answer = mode.toString();
    if (modeActive != ModeActive.Null) { answer += "." + modeActive.toString(); }
    return answer;
  }

  public String getUnusedFullName()
  {
    String answer = unused.toString();
    return answer;
  }

  public Status getStatus()
  {
    return status;
  }

  public Mode getMode()
  {
    return mode;
  }

  public Unused getUnused()
  {
    return unused;
  }

  public StatusOn getStatusOn()
  {
    return statusOn;
  }

  public ModeActive getModeActive()
  {
    return modeActive;
  }

  public boolean turnOn()
  {
    boolean wasEventProcessed = false;
    
    Status aStatus = status;
    switch (aStatus)
    {
      case Off:
        setStatus(Status.On);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean resume()
  {
    boolean wasEventProcessed = false;
    
    Status aStatus = status;
    switch (aStatus)
    {
      case Off:
        setStatusOn(statusOnH);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean turnOff()
  {
    boolean wasEventProcessed = false;
    
    Status aStatus = status;
    switch (aStatus)
    {
      case On:
        exitStatus();
        setStatus(Status.Off);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean stop()
  {
    boolean wasEventProcessed = false;
    
    Mode aMode = mode;
    switch (aMode)
    {
      case Active:
        exitMode();
        setMode(Mode.Stopped);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean restart()
  {
    boolean wasEventProcessed = false;
    
    Mode aMode = mode;
    switch (aMode)
    {
      case Stopped:
        setModeActive(ModeActive.HStar);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean play()
  {
    boolean wasEventProcessed = false;
    
    StatusOn aStatusOn = statusOn;
    if (statusOn != StatusOn.Null){statusOnH = statusOn;}
    switch (aStatusOn)
    {
      case Idle:
        exitStatusOn();
        setStatusOn(StatusOn.Playing);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  public boolean work()
  {
    boolean wasEventProcessed = false;
    
    ModeActive aModeActive = modeActive;
    if (modeActive != ModeActive.Null){modeActiveHStar = modeActive;}
    switch (aModeActive)
    {
      case Waiting:
        exitModeActive();
        setModeActive(ModeActive.Busy);
        wasEventProcessed = true;
        break;
      default:
        // Other states do respond to this event
    }

    return wasEventProcessed;
  }

  private void exitStatus()
  {
    switch(status)
    {
      case On:
        exitStatusOn();
        break;
    }
  }

  private void setStatus(Status aStatus)
  {
    status = aStatus;

    // entry actions and do activities
    switch(status)
    {
      case On:
        if (statusOn == StatusOn.Null) { setStatusOn(StatusOn.Idle); }
        break;
    }
  }

  private void exitMode()
  {
    switch(mode)
    {
      case Active:
        exitModeActive();
        break;
    }
  }

  private void setMode(Mode aMode)
  {
    mode = aMode;

    // entry actions and do activities
    switch(mode)
    {
      case Active:
        if (modeActive == ModeActive.Null) { setModeActive(ModeActive.Waiting); }
        break;
    }
  }

  private void exitStatusOn()
  {
    switch(statusOn)
    {
      case Idle:
        setStatusOn(StatusOn.Null);
        break;
      case Playing:
        setStatusOn(StatusOn.Null);
        break;
      case H:
        setStatusOn(StatusOn.Null);
        break;
    }
  }

  private void setStatusOn(StatusOn aStatusOn)
  {
    statusOn = aStatusOn;
    if (status != Status.On && aStatusOn != StatusOn.Null) { setStatus(Status.On); }
  }

  private void exitModeActive()
  {
    switch(modeActive)
    {
      case Waiting:
        setModeActive(ModeActive.Null);
        break;
      case Busy:
        setModeActive(ModeActive.Null);
        break;
      case HStar:
        setModeActive(ModeActive.Null);
        break;
    }
  }

  private void setModeActive(ModeActive aModeActive)
  {
    modeActive = aModeActive;
    if (mode != Mode.Active && aModeActive != ModeActive.Null) { setMode(Mode.Active); }
    if (aModeActive == ModeActive.HStar) { modeActive = modeActiveHStar;}
  }

  public void 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+"  \"status\" : \""+this.status+"\"");
          haveOutputItem=true;
          if(haveOutputItem){
            toJsonOutput.append(",\n");
          }
          toJsonOutput.append(indent+"  \"statusOn\" : \""+this.statusOn+"\"");
          haveOutputItem=true;
          if(haveOutputItem){
            toJsonOutput.append(",\n");
          }
          toJsonOutput.append(indent+"  \"mode\" : \""+this.mode+"\"");
          haveOutputItem=true;
          if(haveOutputItem){
            toJsonOutput.append(",\n");
          }
          toJsonOutput.append(indent+"  \"modeActive\" : \""+this.modeActive+"\"");
          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 Player fromJson(String aJsonString){
    // the parser below reads json with nothing between its tokens, so remove every whitespace character json allows there
    aJsonString=aJsonString.replace("\n","").replace("\r","").replace("\t","").replace(" ","");
    // a map to store the umpleObjectID present in jsonString
    Map<String, Object> umpleObjectIDMap=new HashMap<>();
    
    // instantiate a new object
Player anObject = new Player(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 Player(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="Player".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");
    // Restore the current state of each state machine; a missing one gets its start state
    this.status=parsedResult.containsKey("status")?Status.valueOf(parsedResult.get("status")):Status.Off;
    this.statusOn=parsedResult.containsKey("statusOn")?StatusOn.valueOf(parsedResult.get("statusOn")):StatusOn.Null;
    this.statusOnH=StatusOn.Idle;
    this.mode=parsedResult.containsKey("mode")?Mode.valueOf(parsedResult.get("mode")):Mode.Active;
    this.modeActive=parsedResult.containsKey("modeActive")?ModeActive.valueOf(parsedResult.get("modeActive")):ModeActive.Waiting;
    this.modeActiveHStar=ModeActive.Waiting;
    // 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

    
    }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="\\\"";
        
        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);
            // The object's own "key":value pairs follow, a value being a "string", a [list] or an {object}.
            // Each value is skipped whole, so the pairs of an associated object are left for that object to read
            int pairStart=topLevelMatcher.end()+1;
            while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)=='"'){
              int keyEnd=fromJsonParserValueEnd(jsonString,pairStart);
              // without the colon there is no telling which value is the key's, so read no further
              if(keyEnd<0||!jsonString.startsWith(":",keyEnd)){
                break;
              }
              int valueEnd=fromJsonParserValueEnd(jsonString,keyEnd+1);
              if(valueEnd<0){
                break;
              }
              String key=jsonString.substring(pairStart+1,keyEnd-1);
              String value=jsonString.substring(keyEnd+1,valueEnd);
              // The class name and object id come first and win over an attribute named like them.
              // toJson quotes every attribute and state, so a bare null, boolean or number is left unread, as it always was
              if(value.startsWith("\"")){
                parsedResultMap.putIfAbsent(key,value.substring(1,value.length()-1).replace("\\\\","\\").replace("\\\"","\""));
              }
              else if(value.startsWith("[")||value.startsWith("{")){
                parsedResultMap.putIfAbsent(key,value);
              }
              // skip the comma, and the extra one left by a superclass with nothing to output
              pairStart=valueEnd;
              while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)==','){
                pairStart++;
              }
            }
        }
        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{
      // objListString is a [list]; each {object} in it is taken whole with the objects nested in it,
      // and any other element, which toJson does not write, is skipped
      int itemStart=1;
      while(itemStart<objListString.length()&&objListString.charAt(itemStart)!=']'){
        int itemEnd=fromJsonParserValueEnd(objListString,itemStart);
        if(itemEnd<0){
          break;
        }
        if(objListString.charAt(itemStart)=='{'){
          objList.add(objListString.substring(itemStart,itemEnd));
        }
        itemStart=itemEnd+1;
      }
      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;
   }
  }
   
  /*
  * Returns the index just after the json value that starts at index start,
  * or -1 if there is none there or it is cut short.
  * A "string", [list] or {object} ends where it closes; brackets and escaped quotes inside a string are text.
  * A bare null, boolean or number ends at the next comma or closing bracket
  */
  private static int fromJsonParserValueEnd(String jsonString, int start){
    if(start>=jsonString.length()){
      return -1;
    }
    if("\"[{".indexOf(jsonString.charAt(start))<0){
      int end=start;
      while(end<jsonString.length()&&",}]".indexOf(jsonString.charAt(end))<0){
        end++;
      }
      return end>start&&end<jsonString.length()?end:-1;
    }
    int depth=0;
    boolean inString=false;
    for(int i=start;i<jsonString.length();i++){
      char c=jsonString.charAt(i);
      if(inString){
        if(c=='\\'){
          i++;
        }
        else if(c=='"'){
          inString=false;
        }
      }
      else if(c=='"'){
        inString=true;
      }
      else if(c=='{'||c=='['){
        depth++;
      }
      else if(c=='}'||c==']'){
        depth--;
      }
      if(!inString&&depth==0){
        return i+1;
      }
    }
    return -1;
  }
  
}
/*PLEASE DO NOT EDIT THIS CODE*/
/*This code was generated using the UMPLE 1.38.0.8942.247e17422 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){
    // the parser below reads json with nothing between its tokens, so remove every whitespace character json allows there
    aJsonString=aJsonString.replace("\n","").replace("\r","").replace("\t","").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(java.lang.reflect.InvocationTargetException e){
      // an associated object with an unknown state must fail the load, as the top-level object does
      if(e.getCause() instanceof IllegalArgumentException){
        throw (IllegalArgumentException)e.getCause();
      }
    }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="\\\"";
        
        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);
            // The object's own "key":value pairs follow, a value being a "string", a [list] or an {object}.
            // Each value is skipped whole, so the pairs of an associated object are left for that object to read
            int pairStart=topLevelMatcher.end()+1;
            while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)=='"'){
              int keyEnd=fromJsonParserValueEnd(jsonString,pairStart);
              // without the colon there is no telling which value is the key's, so read no further
              if(keyEnd<0||!jsonString.startsWith(":",keyEnd)){
                break;
              }
              int valueEnd=fromJsonParserValueEnd(jsonString,keyEnd+1);
              if(valueEnd<0){
                break;
              }
              String key=jsonString.substring(pairStart+1,keyEnd-1);
              String value=jsonString.substring(keyEnd+1,valueEnd);
              // The class name and object id come first and win over an attribute named like them.
              // toJson quotes every attribute and state, so a bare null, boolean or number is left unread, as it always was
              if(value.startsWith("\"")){
                parsedResultMap.putIfAbsent(key,value.substring(1,value.length()-1).replace("\\\\","\\").replace("\\\"","\""));
              }
              else if(value.startsWith("[")||value.startsWith("{")){
                parsedResultMap.putIfAbsent(key,value);
              }
              // skip the comma, and the extra one left by a superclass with nothing to output
              pairStart=valueEnd;
              while(pairStart<jsonString.length()&&jsonString.charAt(pairStart)==','){
                pairStart++;
              }
            }
        }
        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{
      // objListString is a [list]; each {object} in it is taken whole with the objects nested in it,
      // and any other element, which toJson does not write, is skipped
      int itemStart=1;
      while(itemStart<objListString.length()&&objListString.charAt(itemStart)!=']'){
        int itemEnd=fromJsonParserValueEnd(objListString,itemStart);
        if(itemEnd<0){
          break;
        }
        if(objListString.charAt(itemStart)=='{'){
          objList.add(objListString.substring(itemStart,itemEnd));
        }
        itemStart=itemEnd+1;
      }
      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;
   }
  }
   
  /*
  * Returns the index just after the json value that starts at index start,
  * or -1 if there is none there or it is cut short.
  * A "string", [list] or {object} ends where it closes; brackets and escaped quotes inside a string are text.
  * A bare null, boolean or number ends at the next comma or closing bracket
  */
  private static int fromJsonParserValueEnd(String jsonString, int start){
    if(start>=jsonString.length()){
      return -1;
    }
    if("\"[{".indexOf(jsonString.charAt(start))<0){
      int end=start;
      while(end<jsonString.length()&&",}]".indexOf(jsonString.charAt(end))<0){
        end++;
      }
      return end>start&&end<jsonString.length()?end:-1;
    }
    int depth=0;
    boolean inString=false;
    for(int i=start;i<jsonString.length();i++){
      char c=jsonString.charAt(i);
      if(inString){
        if(c=='\\'){
          i++;
        }
        else if(c=='"'){
          inString=false;
        }
      }
      else if(c=='"'){
        inString=true;
      }
      else if(c=='{'||c=='['){
        depth++;
      }
      else if(c=='}'||c==']'){
        depth--;
      }
      if(!inString&&depth==0){
        return i+1;
      }
    }
    return -1;
  }
  
}