Open Source Repository

Home /json/json-lib-2.4-jdk13 | Repository Home



net/sf/json/JSONObject.java
/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.sf.json;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.ezmorph.Morpher;
import net.sf.ezmorph.array.ObjectArrayMorpher;
import net.sf.ezmorph.bean.BeanMorpher;
import net.sf.ezmorph.object.IdentityObjectMorpher;
import net.sf.json.processors.JsonBeanProcessor;
import net.sf.json.processors.JsonValueProcessor;
import net.sf.json.processors.JsonVerifier;
import net.sf.json.processors.PropertyNameProcessor;
import net.sf.json.regexp.RegexpUtils;
import net.sf.json.util.CycleDetectionStrategy;
import net.sf.json.util.JSONTokener;
import net.sf.json.util.JSONUtils;
import net.sf.json.util.PropertyFilter;
import net.sf.json.util.PropertySetStrategy;

import org.apache.commons.beanutils.DynaBean;
import org.apache.commons.beanutils.DynaProperty;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * A JSONObject is an unordered collection of name/value pairs. Its external
 * form is a string wrapped in curly braces with colons between the names and
 * values, and commas between the values and names. The internal form is an
 * object having <code>get</code> and <code>opt</code> methods for accessing
 * the values by name, and <code>put</code> methods for adding or replacing
 * values by name. The values can be any of these types: <code>Boolean</code>,
 <code>JSONArray</code><code>JSONObject</code><code>Number</code>,
 <code>String</code>, or the <code>JSONNull</code> object. A JSONObject
 * constructor can be used to convert an external form JSON text into an
 * internal form whose values can be retrieved with the <code>get</code> and
 <code>opt</code> methods, or to convert values into a JSON text using the
 <code>element</code> and <code>toString</code> methods. A
 <code>get</code> method returns a value if one can be found, and throws an
 * exception if one cannot be found. An <code>opt</code> method returns a
 * default value instead of throwing an exception, and so is useful for
 * obtaining optional values.
 <p>
 * The generic <code>get()</code> and <code>opt()</code> methods return an
 * object, which you can cast or query for type. There are also typed
 <code>get</code> and <code>opt</code> methods that do type checking and
 * type coercion for you.
 <p>
 * The <code>put</code> methods adds values to an object. For example,
 *
 <pre>
 *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
 *
 * produces the string <code>{"JSON": "Hello, World"}</code>.
 <p>
 * The texts produced by the <code>toString</code> methods strictly conform to
 * the JSON syntax rules. The constructors are more forgiving in the texts they
 * will accept:
 <ul>
 <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
 * before the closing brace.</li>
 <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single quote)</small>.</li>
 <li>Strings do not need to be quoted at all if they do not begin with a
 * quote or single quote, and if they do not contain leading or trailing spaces,
 * and if they do not contain any of these characters:
 <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
 * if they are not the reserved words <code>true</code><code>false</code>,
 * or <code>null</code>.</li>
 <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
 * by <code>:</code>.</li>
 <li>Values can be followed by <code>;</code> <small>(semicolon)</small>
 * as well as by <code>,</code> <small>(comma)</small>.</li>
 <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
 <code>0x-</code> <small>(hex)</small> prefix.</li>
 <li>Comments written in the slashshlash, slashstar, and hash conventions
 * will be ignored.</li>
 </ul>
 *
 @author JSON.org
 */
public final class JSONObject extends AbstractJSON implements JSON, Map, Comparable {

   private static final Log log = LogFactory.getLogJSONObject.class );

   /**
    * Creates a JSONObject.<br>
    * Inspects the object type to call the correct JSONObject factory method.
    * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans.
    *
    @param object
    @throws JSONException if the object can not be converted to a proper
    *         JSONObject.
    */

   public static JSONObject fromObjectObject object ) {
      return fromObjectobject, new JsonConfig() );
   }

   /**
    * Creates a JSONObject.<br>
    * Inspects the object type to call the correct JSONObject factory method.
    * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans.
    *
    @param object
    @throws JSONException if the object can not be converted to a proper
    *         JSONObject.
    */
   public static JSONObject fromObjectObject object, JsonConfig jsonConfig ) {
      ifobject == null || JSONUtils.isNullobject ) ){
         return new JSONObjecttrue );
      }else ifobject instanceof JSONObject ){
         return _fromJSONObject( (JSONObjectobject, jsonConfig );
      }else ifobject instanceof DynaBean ){
         return _fromDynaBean( (DynaBeanobject, jsonConfig );
      }else ifobject instanceof JSONTokener ){
         return _fromJSONTokener( (JSONTokenerobject, jsonConfig );
      }else ifobject instanceof JSONString ){
         return _fromJSONString( (JSONStringobject, jsonConfig );
      }else ifobject instanceof Map ){
         return _fromMap( (Mapobject, jsonConfig );
      }else ifobject instanceof String ){
         return _fromString( (Stringobject, jsonConfig );
      }else ifJSONUtils.isNumberobject || JSONUtils.isBooleanobject )
            || JSONUtils.isStringobject ) ){
         return new JSONObject();
      }else ifJSONUtils.isArrayobject ) ){
         throw new JSONException"'object' is an array. Use JSONArray instead" );
      }else{
         return _fromBeanobject, jsonConfig );
      }
   }

   /**
    * Creates a JSONDynaBean from a JSONObject.
    */
   public static Object toBeanJSONObject jsonObject ) {
      ifjsonObject == null || jsonObject.isNullObject() ){
         return null;
      }

      DynaBean dynaBean = null;

      JsonConfig jsonConfig = new JsonConfig();
      Map props = JSONUtils.getPropertiesjsonObject );
      dynaBean = JSONUtils.newDynaBeanjsonObject, jsonConfig );
      forIterator entries = jsonObject.namesjsonConfig )
            .iterator(); entries.hasNext()){
         String name = (Stringentries.next();
         String key = JSONUtils.convertToJavaIdentifiername, jsonConfig );
         Class type = (Classprops.getname );
         Object value = jsonObject.getname );
         try{
            if!JSONUtils.isNullvalue ) ){
               ifvalue instanceof JSONArray ){
                  dynaBean.setkey, JSONArray.toCollection( (JSONArrayvalue ) );
               }else ifString.class.isAssignableFromtype )
                     || Boolean.class.isAssignableFromtype || JSONUtils.isNumbertype )
                     || Character.class.isAssignableFromtype )
                     || JSONFunction.class.isAssignableFromtype ) ){
                  dynaBean.setkey, value );
               }else{
                  dynaBean.setkey, toBean( (JSONObjectvalue ) );
               }
            }else{
               iftype.isPrimitive() ){
                  // assume assigned default value
                  log.warn"Tried to assign null value to " + key + ":" + type.getName() );
                  dynaBean.setkey, JSONUtils.getMorpherRegistry()
                        .morphtype, null ) );
               }else{
                  dynaBean.setkey, null );
               }
            }
         }catchJSONException jsone ){
            throw jsone;
         }catchException e ){
            throw new JSONException"Error while setting property=" + name + " type" + type, e );
         }
      }

      return dynaBean;
   }

   /**
    * Creates a bean from a JSONObject, with a specific target class.<br>
    */
   public static Object toBeanJSONObject jsonObject, Class beanClass ) {
      JsonConfig jsonConfig = new JsonConfig();
      jsonConfig.setRootClassbeanClass );
      return toBeanjsonObject, jsonConfig );
   }

   /**
    * Creates a bean from a JSONObject, with a specific target class.<br>
    * If beanClass is null, this method will return a graph of DynaBeans. Any
    * attribute that is a JSONObject and matches a key in the classMap will be
    * converted to that target class.<br>
    * The classMap has the following conventions:
    <ul>
    <li>Every key must be an String.</li>
    <li>Every value must be a Class.</li>
    <li>A key may be a regular expression.</li>
    </ul>
    */
   public static Object toBeanJSONObject jsonObject, Class beanClass, Map classMap ) {
      JsonConfig jsonConfig = new JsonConfig();
      jsonConfig.setRootClassbeanClass );
      jsonConfig.setClassMapclassMap );
      return toBeanjsonObject, jsonConfig );
   }

   /**
    * Creates a bean from a JSONObject, with the specific configuration.
    */
   public static Object toBeanJSONObject jsonObject, JsonConfig jsonConfig ) {
      ifjsonObject == null || jsonObject.isNullObject() ){
         return null;
      }

      Class beanClass = jsonConfig.getRootClass();
      Map classMap = jsonConfig.getClassMap();

      ifbeanClass == null ){
         return toBeanjsonObject );
      }
      ifclassMap == null ){
         classMap = Collections.EMPTY_MAP;
      }

      Object bean = null;
      try{
         ifbeanClass.isInterface() ){
            if!Map.class.isAssignableFrombeanClass ) ){
               throw new JSONException"beanClass is an interface. " + beanClass );
            }else{
               bean = new HashMap();
            }
         }else{
            bean = jsonConfig.getNewBeanInstanceStrategy()
                  .newInstancebeanClass, jsonObject );
         }
      }catchJSONException jsone ){
         throw jsone;
      }catchException e ){
         throw new JSONException);
      }

      Map props = JSONUtils.getPropertiesjsonObject );
      PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter();
      forIterator entries = jsonObject.namesjsonConfig )
            .iterator(); entries.hasNext()){
         String name = (Stringentries.next();
         Class type = (Classprops.getname );
         Object value = jsonObject.getname );
         ifjavaPropertyFilter != null && javaPropertyFilter.applybean, name, value ) ){
            continue;
         }
         String key = Map.class.isAssignableFrombeanClass )
               && jsonConfig.isSkipJavaIdentifierTransformationInMapKeys() ? name
               : JSONUtils.convertToJavaIdentifiername, jsonConfig );
         PropertyNameProcessor propertyNameProcessor = jsonConfig.findJavaPropertyNameProcessorbeanClass );
         ifpropertyNameProcessor != null ){
            key = propertyNameProcessor.processPropertyNamebeanClass, key );
         }
         try{
            ifMap.class.isAssignableFrombeanClass ) ){
               // no type info available for conversion
               ifJSONUtils.isNullvalue ) ){
                  setPropertybean, key, value, jsonConfig );
               }else ifvalue instanceof JSONArray ){
                  setPropertybean, key, convertPropertyValueToCollectionkey, value, jsonConfig, name,
                        classMap, List.class ), jsonConfig );
               }else ifString.class.isAssignableFromtype || JSONUtils.isBooleantype )
                     || JSONUtils.isNumbertype || JSONUtils.isStringtype )
                     || JSONFunction.class.isAssignableFromtype ) ){
                  ifjsonConfig.isHandleJettisonEmptyElement() && "".equalsvalue ) ){
                     setPropertybean, key, null, jsonConfig );
                  }else{
                     setPropertybean, key, value, jsonConfig );
                  }
               }else{
                  Class targetClass = resolveClass(classMap, key, name, type);
                  JsonConfig jsc = jsonConfig.copy();
                  jsc.setRootClasstargetClass );
                  jsc.setClassMapclassMap );
                  iftargetClass != null ){
                     setPropertybean, key, toBean( (JSONObjectvalue, jsc ), jsonConfig );
                  }else{
                     setPropertybean, key, toBean( (JSONObjectvalue ), jsonConfig );
                  }
               }
            }else{
               PropertyDescriptor pd = PropertyUtils.getPropertyDescriptorbean, key );
               ifpd != null && pd.getWriteMethod() == null ){
                  log.info"Property '" + key + "' of "+ bean.getClass()+" has no write method. SKIPPED." );
                  continue;
               }

               ifpd != null ){
                  Class targetType = pd.getPropertyType();
                  if!JSONUtils.isNullvalue ) ){
                     ifvalue instanceof JSONArray ){
                        ifList.class.isAssignableFrompd.getPropertyType() ) ){
                           setPropertybean, key, convertPropertyValueToCollectionkey, value,
                                 jsonConfig, name, classMap, pd.getPropertyType() ), jsonConfig );
                        }else ifSet.class.isAssignableFrompd.getPropertyType() ) ){
                           setPropertybean, key, convertPropertyValueToCollectionkey, value,
                                 jsonConfig, name, classMap, pd.getPropertyType() ), jsonConfig );
                        }else{
                           setPropertybean, key, convertPropertyValueToArraykey, value,
                                 targetType, jsonConfig, classMap ), jsonConfig );
                        }
                     }else ifString.class.isAssignableFromtype || JSONUtils.isBooleantype )
                           || JSONUtils.isNumbertype || JSONUtils.isStringtype )
                           || JSONFunction.class.isAssignableFromtype ) ){
                        ifpd != null ){
                           ifjsonConfig.isHandleJettisonEmptyElement() && "".equalsvalue ) ){
                              setPropertybean, key, null, jsonConfig );
                           }else if!targetType.isInstancevalue ) ){
                              setPropertybean, key, morphPropertyValuekey, value, type,
                                    targetType ), jsonConfig );
                           }else{
                              setPropertybean, key, value, jsonConfig );
                           }
                        }else ifbeanClass == null || bean instanceof Map ){
                           setPropertybean, key, value, jsonConfig );
                        }else{
                           log.warn"Tried to assign property " + key + ":" + type.getName()
                                 " to bean of class " + bean.getClass()
                                       .getName() );
                        }
                     }else{
                        ifjsonConfig.isHandleJettisonSingleElementArray() ){
                           JSONArray array = new JSONArray().elementvalue, jsonConfig );
                           Class newTargetClass = resolveClass(classMap, key, name, type);
                           JsonConfig jsc = jsonConfig.copy();
                           jsc.setRootClassnewTargetClass );
                           jsc.setClassMapclassMap );
                           iftargetType.isArray() ){
                              setPropertybean, key, JSONArray.toArrayarray, jsc ), jsonConfig );
                           }else ifJSONArray.class.isAssignableFromtargetType ) ){
                              setPropertybean, key, array, jsonConfig );
                           }else ifList.class.isAssignableFromtargetType )
                                 || Set.class.isAssignableFromtargetType ) ){
                              jsc.setCollectionTypetargetType );
                              setPropertybean, key, JSONArray.toCollectionarray, jsc ),
                                    jsonConfig );
                           }else{
                              setPropertybean, key, toBean( (JSONObjectvalue, jsc ), jsonConfig );
                           }
                        }else{
                           iftargetType == Object.class || targetType.isInterface() ) {
                              Class targetTypeCopy = targetType;
                              targetType = findTargetClasskey, classMap );
                              targetType = targetType == null ? findTargetClassname, classMap )
                                    : targetType;
                              targetType = targetType == null && targetTypeCopy.isInterface() ? targetTypeCopy
                                    : targetType;
                           }
                           JsonConfig jsc = jsonConfig.copy();
                           jsc.setRootClasstargetType );
                           jsc.setClassMapclassMap );
                           setPropertybean, key, toBean( (JSONObjectvalue, jsc ), jsonConfig );
                        }
                     }
                  }else{
                     iftype.isPrimitive() ){
                        // assume assigned default value
                        log.warn"Tried to assign null value to " + key + ":" + type.getName() );
                        setPropertybean, key, JSONUtils.getMorpherRegistry()
                              .morphtype, null ), jsonConfig );
                     }else{
                        setPropertybean, key, null, jsonConfig );
                     }
                  }
               }else{
                  // pd is null
                  if!JSONUtils.isNullvalue ) ){
                     ifvalue instanceof JSONArray ){
                        setPropertybean, key, convertPropertyValueToCollectionkey, value,
                              jsonConfig, name, classMap, List.class ), jsonConfig );
                     }else ifString.class.isAssignableFromtype || JSONUtils.isBooleantype )
                           || JSONUtils.isNumbertype || JSONUtils.isStringtype )
                           || JSONFunction.class.isAssignableFromtype ) ){
                        ifbeanClass == null || bean instanceof Map || jsonConfig.getPropertySetStrategy() != null || 
                            !jsonConfig.isIgnorePublicFields() ){
                           setPropertybean, key, value, jsonConfig );
                        }else{
                           log.warn"Tried to assign property " + key + ":" + type.getName()
                                 " to bean of class " + bean.getClass()
                                       .getName() );
                        }
                     }else{
                        ifjsonConfig.isHandleJettisonSingleElementArray() ){
                           Class newTargetClass = resolveClass(classMap, key, name, type);
                           JsonConfig jsc = jsonConfig.copy();
                           jsc.setRootClassnewTargetClass );
                           jsc.setClassMapclassMap );
                           setPropertybean, key, toBean( (JSONObjectvalue, jsc ), jsonConfig );
                        }else{
                           setPropertybean, key, value, jsonConfig );
                        }
                     }
                  }else{
                     iftype.isPrimitive() ){
                        // assume assigned default value
                        log.warn"Tried to assign null value to " + key + ":" + type.getName() );
                        setPropertybean, key, JSONUtils.getMorpherRegistry()
                              .morphtype, null ), jsonConfig );
                     }else{
                        setPropertybean, key, null, jsonConfig );
                     }
                  }
               }
            }
         }catchJSONException jsone ){
            throw jsone;
         }catchException e ){
            throw new JSONException"Error while setting property=" + name + " type " + type, e );
         }
      }

      return bean;
   }

   /**
    * Creates a bean from a JSONObject, with the specific configuration.
    */
   public static Object toBeanJSONObject jsonObject, Object root, JsonConfig jsonConfig ) {
      ifjsonObject == null || jsonObject.isNullObject() || root == null ){
         return root;
      }

      Class rootClass = root.getClass();
      ifrootClass.isInterface() ){
         throw new JSONException"Root bean is an interface. " + rootClass );
      }

      Map classMap = jsonConfig.getClassMap();
      ifclassMap == null ){
         classMap = Collections.EMPTY_MAP;
      }

      Map props = JSONUtils.getPropertiesjsonObject );
      PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter();
      forIterator entries = jsonObject.namesjsonConfig )
            .iterator(); entries.hasNext()){
         String name = (Stringentries.next();
         Class type = (Classprops.getname );
         Object value = jsonObject.getname );
         ifjavaPropertyFilter != null && javaPropertyFilter.applyroot, name, value ) ){
            continue;
         }
         String key = JSONUtils.convertToJavaIdentifiername, jsonConfig );
         try{
            PropertyDescriptor pd = PropertyUtils.getPropertyDescriptorroot, key );
            ifpd != null && pd.getWriteMethod() == null ){
               log.info"Property '" + key + "' of "+ root.getClass()+" has no write method. SKIPPED." );
               continue;
            }

            if!JSONUtils.isNullvalue ) ){
               ifvalue instanceof JSONArray ){
                  ifpd == null || List.class.isAssignableFrompd.getPropertyType() ) ){
                     Class targetClass = resolveClass(classMap, key, name, type);
                     Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
                           .newInstancetargetClass, null );
                     List list = JSONArray.toList( (JSONArrayvalue, newRoot, jsonConfig );
                     setPropertyroot, key, list, jsonConfig );
                  }else{
                     Class innerType = JSONUtils.getInnerComponentTypepd.getPropertyType() );
                     Class targetInnerType = findTargetClasskey, classMap );
                     ifinnerType.equalsObject.class && targetInnerType != null
                           && !targetInnerType.equalsObject.class ) ){
                        innerType = targetInnerType;
                     }
                     Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
                           .newInstanceinnerType, null );
                     Object array = JSONArray.toArray( (JSONArrayvalue, newRoot, jsonConfig );
                     ifinnerType.isPrimitive() || JSONUtils.isNumberinnerType )
                           || Boolean.class.isAssignableFrominnerType )
                           || JSONUtils.isStringinnerType ) ){
                        array = JSONUtils.getMorpherRegistry()
                              .morphArray.newInstanceinnerType, )
                                    .getClass(), array );
                     }else if!array.getClass()
                           .equalspd.getPropertyType() ) ){
                        if!pd.getPropertyType()
                              .equalsObject.class ) ){
                           Morpher morpher = JSONUtils.getMorpherRegistry()
                                 .getMorpherForArray.newInstanceinnerType, )
                                       .getClass() );
                           ifIdentityObjectMorpher.getInstance()
                                 .equalsmorpher ) ){
                              ObjectArrayMorpher beanMorpher = new ObjectArrayMorpher(
                                    new BeanMorpherinnerType, JSONUtils.getMorpherRegistry() ) );
                              JSONUtils.getMorpherRegistry()
                                    .registerMorpherbeanMorpher );
                           }
                           array = JSONUtils.getMorpherRegistry()
                                 .morphArray.newInstanceinnerType, )
                                       .getClass(), array );
                        }
                     }
                     setPropertyroot, key, array, jsonConfig );
                  }
               }else ifString.class.isAssignableFromtype || JSONUtils.isBooleantype )
                     || JSONUtils.isNumbertype || JSONUtils.isStringtype )
                     || JSONFunction.class.isAssignableFromtype ) ){
                  ifpd != null ){
                     ifjsonConfig.isHandleJettisonEmptyElement() && "".equalsvalue ) ){
                        setPropertyroot, key, null, jsonConfig );
                     }else if!pd.getPropertyType()
                           .isInstancevalue ) ){
                        Morpher morpher = JSONUtils.getMorpherRegistry()
                              .getMorpherForpd.getPropertyType() );
                        ifIdentityObjectMorpher.getInstance()
                              .equalsmorpher ) ){
                           log.warn"Can't transform property '" + key + "' from "
                                 + type.getName() " into " + pd.getPropertyType()
                                       .getName() ". Will register a default BeanMorpher" );
                           JSONUtils.getMorpherRegistry()
                                 .registerMorpher(
                                       new BeanMorpherpd.getPropertyType(),
                                             JSONUtils.getMorpherRegistry() ) );
                        }
                        setPropertyroot, key, JSONUtils.getMorpherRegistry()
                              .morphpd.getPropertyType(), value ), jsonConfig );
                     }else{
                        setPropertyroot, key, value, jsonConfig );
                     }
                  }else ifroot instanceof Map ){
                     setPropertyroot, key, value, jsonConfig );
                  }else{
                     log.warn"Tried to assign property " + key + ":" + type.getName()
                           " to bean of class " + root.getClass()
                                 .getName() );
                  }
               }else{
                  ifpd != null ){
                     Class targetClass = pd.getPropertyType();
                     ifjsonConfig.isHandleJettisonSingleElementArray() ){
                        JSONArray array = new JSONArray().elementvalue, jsonConfig );
                        Class newTargetClass = resolveClass(classMap, key, name, type);
                        Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
                              .newInstancenewTargetClass, (JSONObjectvalue );
                        iftargetClass.isArray() ){
                           setPropertyroot, key, JSONArray.toArrayarray, newRoot, jsonConfig ),
                                 jsonConfig );
                        }else ifCollection.class.isAssignableFromtargetClass ) ){
                           setPropertyroot, key, JSONArray.toListarray, newRoot, jsonConfig ),
                                 jsonConfig );
                        }else ifJSONArray.class.isAssignableFromtargetClass ) ){
                           setPropertyroot, key, array, jsonConfig );
                        }else{
                           setPropertyroot, key,
                                 toBean( (JSONObjectvalue, newRoot, jsonConfig ), jsonConfig );
                        }
                     }else{
                        iftargetClass == Object.class ){
                           targetClass = resolveClass(classMap, key, name, type);
                           if(targetClass == null) {
                               targetClass = Object.class;
                           }
                        }
                        Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
                              .newInstancetargetClass, (JSONObjectvalue );
                        setPropertyroot, key, toBean( (JSONObjectvalue, newRoot, jsonConfig ),
                              jsonConfig );
                     }
                  }else ifroot instanceof Map ){
                     Class targetClass = findTargetClasskey, classMap );
                     targetClass = targetClass == null ? findTargetClassname, classMap )
                           : targetClass;
                     Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
                           .newInstancetargetClass, (JSONObjectvalue );
                     setPropertyroot, key, toBean( (JSONObjectvalue, newRoot, jsonConfig ),
                           jsonConfig );
                  }else{
                     log.warn"Tried to assign property " + key + ":" + type.getName()
                           " to bean of class " + rootClass.getName() );
                  }
               }
            }else{
               iftype.isPrimitive() ){
                  // assume assigned default value
                  log.warn"Tried to assign null value to " + key + ":" + type.getName() );
                  setPropertyroot, key, JSONUtils.getMorpherRegistry()
                        .morphtype, null ), jsonConfig );
               }else{
                  setPropertyroot, key, null, jsonConfig );
               }
            }
         }catchJSONException jsone ){
            throw jsone;
         }catchException e ){
            throw new JSONException"Error while setting property=" + name + " type " + type, e );
         }
      }

      return root;
   }

   /**
    * Creates a JSONObject from a POJO.<br>
    * Supports nested maps, POJOs, and arrays/collections.
    *
    @param bean An object with POJO conventions
    @throws JSONException if the bean can not be converted to a proper
    *         JSONObject.
    */
   private static JSONObject _fromBeanObject bean, JsonConfig jsonConfig ) {
      if!addInstancebean ) ){
         try{
            return jsonConfig.getCycleDetectionStrategy()
                  .handleRepeatedReferenceAsObjectbean );
         }catchJSONException jsone ){
            removeInstancebean );
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }catchRuntimeException e ){
            removeInstancebean );
            JSONException jsone = new JSONException);
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }
      }
      fireObjectStartEventjsonConfig );

      JsonBeanProcessor processor = jsonConfig.findJsonBeanProcessorbean.getClass() );
      ifprocessor != null ){
         JSONObject json = null;
         try{
            json = processor.processBeanbean, jsonConfig );
            ifjson == null ){
               json = (JSONObjectjsonConfig.findDefaultValueProcessorbean.getClass() )
                     .getDefaultValuebean.getClass() );
               ifjson == null ){
                  json = new JSONObjecttrue );
               }
            }
            removeInstancebean );
            fireObjectEndEventjsonConfig );
         }catchJSONException jsone ){
            removeInstancebean );
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }catchRuntimeException e ){
            removeInstancebean );
            JSONException jsone = new JSONException);
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }
         return json;
      }

      JSONObject jsonObject = defaultBeanProcessing(bean, jsonConfig);
      removeInstancebean );
      fireObjectEndEventjsonConfig );
      return jsonObject;
   }

   private static JSONObject defaultBeanProcessing(Object bean, JsonConfig jsonConfig) {
      Class beanClass = bean.getClass();
      PropertyNameProcessor propertyNameProcessor = jsonConfig.findJsonPropertyNameProcessorbeanClass );      
      Collection exclusions = jsonConfig.getMergedExcludesbeanClass );
      JSONObject jsonObject = new JSONObject();
      try{
         PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptorsbean );
         PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
         forint i = 0; i < pds.length; i++ ){
            boolean bypass = false;
            String key = pds[i].getName();
            ifexclusions.containskey ) ){
               continue;
            }

            ifjsonConfig.isIgnoreTransientFields() && isTransientFieldkey, beanClass ) ){
               continue;
            }

            Class type = pds[i].getPropertyType();
            try pds[i].getReadMethod()}
            catchException e ) {
               // bug 2565295
               String warning = "Property '" + key + "' of "+ beanClass+" has no read method. SKIPPED";
               fireWarnEventwarning, jsonConfig );
               log.infowarning );
               continue;
            }
            ifpds[i].getReadMethod() != null ){
               Object value = PropertyUtils.getPropertybean, key );
               ifjsonPropertyFilter != null && jsonPropertyFilter.applybean, key, value ) ){
                  continue;
               }
               JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(
                     beanClass, type, key );
               ifjsonValueProcessor != null ){
                  value = jsonValueProcessor.processObjectValuekey, value, jsonConfig );
                  bypass = true;
                  if!JsonVerifier.isValidJsonValuevalue ) ){
                     throw new JSONException"Value is not a valid JSON value. " + value );
                  }
               }
               ifpropertyNameProcessor != null ){
                  key = propertyNameProcessor.processPropertyNamebeanClass, key );
               }
               setValuejsonObject, key, value, type, jsonConfig, bypass );
            }else{
               String warning = "Property '" + key + "' of "+ beanClass+" has no read method. SKIPPED";
               fireWarnEventwarning, jsonConfig );
               log.infowarning );
            }
         }
         // inspect public fields, this operation may fail under
         // a SecurityManager so we will eat all exceptions
         try {
            if!jsonConfig.isIgnorePublicFields() ) {
               Field[] fields = beanClass.getFields();
               forint i = 0; i < fields.length; i++ ) {
                  boolean bypass = false;
                  Field field = fields[i];
                  String key = field.getName();
                  ifexclusions.containskey ) ) {
                     continue;
                  }

                  ifjsonConfig.isIgnoreTransientFields() && isTransientFieldfield ) ) {
                     continue;
                  }

                  Class type = field.getType();
                  Object value = field.getbean );
                  ifjsonPropertyFilter != null && jsonPropertyFilter.applybean, key, value ) ) {
                     continue;
                  }
                  JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessorbeanClass, type, key );
                  ifjsonValueProcessor != null ) {
                     value = jsonValueProcessor.processObjectValuekey, value, jsonConfig );
                     bypass = true;
                     if!JsonVerifier.isValidJsonValuevalue ) ) {
                        throw new JSONException"Value is not a valid JSON value. " + value );
                     }
                  }
                  ifpropertyNameProcessor != null ) {
                     key = propertyNameProcessor.processPropertyNamebeanClass, key );
                  }
                  setValuejsonObject, key, value, type, jsonConfig, bypass );
               }
            }
         }
         catchException e ){
            log.trace"Couldn't read public fields.", e );
         }
      }catchJSONException jsone ){
         removeInstancebean );
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }catchException e ){
         removeInstancebean );
         JSONException jsone = new JSONException);
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }
      return jsonObject;
   }

   private static JSONObject _fromDynaBeanDynaBean bean, JsonConfig jsonConfig ) {
      ifbean == null ){
         fireObjectStartEventjsonConfig );
         fireObjectEndEventjsonConfig );
         return new JSONObjecttrue );
      }

      if!addInstancebean ) ){
         try{
            return jsonConfig.getCycleDetectionStrategy()
                  .handleRepeatedReferenceAsObjectbean );
         }catchJSONException jsone ){
            removeInstancebean );
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }catchRuntimeException e ){
            removeInstancebean );
            JSONException jsone = new JSONException);
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }
      }
      fireObjectStartEventjsonConfig );

      JSONObject jsonObject = new JSONObject();
      try{
         DynaProperty[] props = bean.getDynaClass()
               .getDynaProperties();
         Collection exclusions = jsonConfig.getMergedExcludes();
         PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
         forint i = 0; i < props.length; i++ ){
            boolean bypass = false;
            DynaProperty dynaProperty = props[i];
            String key = dynaProperty.getName();
            ifexclusions.containskey ) ){
               continue;
            }
            Class type = dynaProperty.getType();
            Object value = bean.getdynaProperty.getName() );
            ifjsonPropertyFilter != null && jsonPropertyFilter.applybean, key, value ) ){
               continue;
            }
            JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessortype, key );
            ifjsonValueProcessor != null ){
               value = jsonValueProcessor.processObjectValuekey, value, jsonConfig );
               bypass = true;
               if!JsonVerifier.isValidJsonValuevalue ) ){
                  throw new JSONException"Value is not a valid JSON value. " + value );
               }
            }
            setValuejsonObject, key, value, type, jsonConfig, bypass );
         }
      }catchJSONException jsone ){
         removeInstancebean );
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }catchRuntimeException e ){
         removeInstancebean );
         JSONException jsone = new JSONException);
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }

      removeInstancebean );
      fireObjectEndEventjsonConfig );
      return jsonObject;
   }

   private static JSONObject _fromJSONObjectJSONObject object, JsonConfig jsonConfig ) {
      ifobject == null || object.isNullObject() ){
         fireObjectStartEventjsonConfig );
         fireObjectEndEventjsonConfig );
         return new JSONObjecttrue );
      }

      if!addInstanceobject ) ){
         try{
            return jsonConfig.getCycleDetectionStrategy()
                  .handleRepeatedReferenceAsObjectobject );
         }catchJSONException jsone ){
            removeInstanceobject );
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }catchRuntimeException e ){
            removeInstanceobject );
            JSONException jsone = new JSONException);
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }
      }
      fireObjectStartEventjsonConfig );

      JSONArray sa = object.names(jsonConfig);
      Collection exclusions = jsonConfig.getMergedExcludes();
      JSONObject jsonObject = new JSONObject();
      PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
      forIterator i = sa.iterator(); i.hasNext()){
         Object k =  i.next();
         ifk == null ){
            throw new JSONException("JSON keys cannot be null.");
         }
         if!(instanceof String&& !jsonConfig.isAllowNonStringKeys()) {
            throw new ClassCastException("JSON keys must be strings.");
         }
         String key = String.valueOf);
         if"null".equalskey )){
            throw new NullPointerException("JSON keys must not be null nor the 'null' string.");
         }
         ifexclusions.containskey ) ){
            continue;
         }
         Object value = object.optkey );
         ifjsonPropertyFilter != null && jsonPropertyFilter.applyobject, key, value ) ){
            continue;
         }
         ifjsonObject.properties.containsKeykey ) ){
            jsonObject.accumulatekey, value, jsonConfig );
            firePropertySetEventkey, value, true, jsonConfig );
         }else{
            jsonObject.setInternalkey, value, jsonConfig );
            firePropertySetEventkey, value, false, jsonConfig );
         }
      }

      removeInstanceobject );
      fireObjectEndEventjsonConfig );
      return jsonObject;
   }

   private static JSONObject _fromJSONStringJSONString string, JsonConfig jsonConfig ) {
      return _fromJSONTokenernew JSONTokenerstring.toJSONString() ), jsonConfig );
   }

   private static JSONObject _fromJSONTokenerJSONTokener tokener, JsonConfig jsonConfig ) {

      try{
         char c;
         String key;
         Object value;

         iftokener.matches"null.*" ) ){
            fireObjectStartEventjsonConfig );
            fireObjectEndEventjsonConfig );
            return new JSONObjecttrue );
         }

         iftokener.nextClean() != '{' ){
            throw tokener.syntaxError"A JSONObject text must begin with '{'" );
         }
         fireObjectStartEventjsonConfig );

         Collection exclusions = jsonConfig.getMergedExcludes();
         PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
         JSONObject jsonObject = new JSONObject();
         for;; ){
            c = tokener.nextClean();
            switch){
               case 0:
                  throw tokener.syntaxError"A JSONObject text must end with '}'" );
               case '}':
                  fireObjectEndEventjsonConfig );
                  return jsonObject;
               default:
                  tokener.back();
                  key = tokener.nextValuejsonConfig )
                        .toString();
            }

            /*
             * The key is followed by ':'. We will also tolerate '=' or '=>'.
             */

            c = tokener.nextClean();
            ifc == '=' ){
               iftokener.next() != '>' ){
                  tokener.back();
               }
            }else ifc != ':' ){
               throw tokener.syntaxError"Expected a ':' after a key" );
            }

            char peek = tokener.peek();
            boolean quoted = peek == '"' || peek == '\'';
            Object v = tokener.nextValuejsonConfig );
            ifquoted || !JSONUtils.isFunctionHeader) ){
               ifexclusions.containskey ) ){
                  switchtokener.nextClean() ){
                     case ';':
                     case ',':
                        iftokener.nextClean() == '}' ){
                           fireObjectEndEventjsonConfig );
                           return jsonObject;
                        }
                        tokener.back();
                        break;
                     case '}':
                        fireObjectEndEventjsonConfig );
                        return jsonObject;
                     default:
                        throw tokener.syntaxError"Expected a ',' or '}'" );
                  }
                  continue;
               }
               ifjsonPropertyFilter == null || !jsonPropertyFilter.applytokener, key, v ) ){
                  ifquoted && v instanceof String && (JSONUtils.mayBeJSON( (String|| JSONUtils.isFunction))){
                     v = JSONUtils.DOUBLE_QUOTE + v + JSONUtils.DOUBLE_QUOTE;
                  }
                  ifjsonObject.properties.containsKeykey ) ){
                     jsonObject.accumulatekey, v, jsonConfig );
                     firePropertySetEventkey, v, true, jsonConfig );
                  }else{
                     jsonObject.elementkey, v, jsonConfig );
                     firePropertySetEventkey, v, false, jsonConfig );
                  }
               }
            }else{
               // read params if any
               String params = JSONUtils.getFunctionParams( (String);
               // read function text
               int i = 0;
               StringBuffer sb = new StringBuffer();
               for;; ){
                  char ch = tokener.next();
                  ifch == ){
                     break;
                  }
                  ifch == '{' ){
                     i++;
                  }
                  ifch == '}' ){
                     i--;
                  }
                  sb.appendch );
                  ifi == ){
                     break;
                  }
               }
               ifi != ){
                  throw tokener.syntaxError"Unbalanced '{' or '}' on prop: " + v );
               }
               // trim '{' at start and '}' at end
               String text = sb.toString();
               text = text.substring1, text.length() )
                     .trim();
               value = new JSONFunction(
                     (params != null? StringUtils.splitparams, "," : null, text );
               ifjsonPropertyFilter == null || !jsonPropertyFilter.applytokener, key, value ) ){
                  ifjsonObject.properties.containsKeykey ) ){
                     jsonObject.accumulatekey, value, jsonConfig );
                     firePropertySetEventkey, value, true, jsonConfig );
                  }else{
                     jsonObject.elementkey, value, jsonConfig );
                     firePropertySetEventkey, value, false, jsonConfig );
                  }
               }
            }

            /*
             * Pairs are separated by ','. We will also tolerate ';'.
             */

            switchtokener.nextClean() ){
               case ';':
               case ',':
                  iftokener.nextClean() == '}' ){
                     fireObjectEndEventjsonConfig );
                     return jsonObject;
                  }
                  tokener.back();
                  break;
               case '}':
                  fireObjectEndEventjsonConfig );
                  return jsonObject;
               default:
                  throw tokener.syntaxError"Expected a ',' or '}'" );
            }
         }
      }catchJSONException jsone ){
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }
   }

   private static JSONObject _fromMapMap map, JsonConfig jsonConfig ) {
      ifmap == null ){
         fireObjectStartEventjsonConfig );
         fireObjectEndEventjsonConfig );
         return new JSONObjecttrue );
      }

      if!addInstancemap ) ){
         try{
            return jsonConfig.getCycleDetectionStrategy()
                  .handleRepeatedReferenceAsObjectmap );
         }catchJSONException jsone ){
            removeInstancemap );
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }catchRuntimeException e ){
            removeInstancemap );
            JSONException jsone = new JSONException);
            fireErrorEventjsone, jsonConfig );
            throw jsone;
         }
      }
      fireObjectStartEventjsonConfig );

      Collection exclusions = jsonConfig.getMergedExcludes();
      JSONObject jsonObject = new JSONObject();
      PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
      try{
         forIterator entries = map.entrySet()
               .iterator(); entries.hasNext()){
            boolean bypass = false;
            Map.Entry entry = (Map.Entryentries.next();
            Object k = entry.getKey();
            ifk == null ){
               throw new JSONException("JSON keys cannot be null.");
            }
            if!(instanceof String&& !jsonConfig.isAllowNonStringKeys() ) {
               throw new ClassCastException("JSON keys must be strings.");
            }
            String key = String.valueOf);
            if"null".equalskey )){
               throw new NullPointerException("JSON keys must not be null nor the 'null' string.");
            }
            ifexclusions.containskey ) ){
               continue;
            }
            Object value = entry.getValue();
            ifjsonPropertyFilter != null && jsonPropertyFilter.applymap, key, value ) ){
               continue;
            }
            ifvalue != null ){
               JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(
                     value.getClass(), key );
               ifjsonValueProcessor != null ){
                  value = jsonValueProcessor.processObjectValuekey, value, jsonConfig );
                  bypass = true;
                  if!JsonVerifier.isValidJsonValuevalue ) ){
                     throw new JSONException"Value is not a valid JSON value. " + value );
                  }
               }
               setValuejsonObject, key, value, value.getClass(), jsonConfig, bypass );
            }else{
               ifjsonObject.properties.containsKeykey ) ){
                  jsonObject.accumulatekey, JSONNull.getInstance() );
                  firePropertySetEventkey, JSONNull.getInstance(), true, jsonConfig );
               }else{
                  jsonObject.elementkey, JSONNull.getInstance() );
                  firePropertySetEventkey, JSONNull.getInstance(), false, jsonConfig );
               }
            }
         }
      }catchJSONException jsone ){
         removeInstancemap );
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }catchRuntimeException e ){
         removeInstancemap );
         JSONException jsone = new JSONException);
         fireErrorEventjsone, jsonConfig );
         throw jsone;
      }

      removeInstancemap );
      fireObjectEndEventjsonConfig );
      return jsonObject;
   }

   private static JSONObject _fromStringString str, JsonConfig jsonConfig ) {
      ifstr == null || "null".equalsstr ) ){
         fireObjectStartEventjsonConfig );
         fireObjectEndEventjsonConfig );
         return new JSONObjecttrue );
      }
      return _fromJSONTokenernew JSONTokenerstr ), jsonConfig );
   }

   private static Object convertPropertyValueToArrayString key, Object value, Class targetType,
         JsonConfig jsonConfig, Map classMap ) {
      Class innerType = JSONUtils.getInnerComponentTypetargetType );
      Class targetInnerType = findTargetClasskey, classMap );
      ifinnerType.equalsObject.class && targetInnerType != null
            && !targetInnerType.equalsObject.class ) ){
         innerType = targetInnerType;
      }
      JsonConfig jsc = jsonConfig.copy();
      jsc.setRootClassinnerType );
      jsc.setClassMapclassMap );
      Object array = JSONArray.toArray( (JSONArrayvalue, jsc );
      ifinnerType.isPrimitive() || JSONUtils.isNumberinnerType )
            || Boolean.class.isAssignableFrominnerType || JSONUtils.isStringinnerType ) ){
         array = JSONUtils.getMorpherRegistry()
               .morphArray.newInstanceinnerType, )
                     .getClass(), array );
      }else if!array.getClass()
            .equalstargetType ) ){
         if!targetType.equalsObject.class ) ){
            Morpher morpher = JSONUtils.getMorpherRegistry()
                  .getMorpherForArray.newInstanceinnerType, )
                        .getClass() );
            ifIdentityObjectMorpher.getInstance()
                  .equalsmorpher ) ){
               ObjectArrayMorpher beanMorpher = new ObjectArrayMorphernew BeanMorpherinnerType,
                     JSONUtils.getMorpherRegistry() ) );
               JSONUtils.getMorpherRegistry()
                     .registerMorpherbeanMorpher );
            }
            array = JSONUtils.getMorpherRegistry()
                  .morphArray.newInstanceinnerType, )
                        .getClass(), array );
         }
      }
      return array;
   }

   private static List convertPropertyValueToListString key, Object value, JsonConfig jsonConfig,
         String name, Map classMap ) {
      Class targetClass = findTargetClasskey, classMap );
      targetClass = targetClass == null ? findTargetClassname, classMap : targetClass;
      JsonConfig jsc = jsonConfig.copy();
      jsc.setRootClasstargetClass );
      jsc.setClassMapclassMap );
      List list = (ListJSONArray.toCollection( (JSONArrayvalue, jsc );
      return list;
   }

   private static Collection convertPropertyValueToCollectionString key, Object value, JsonConfig jsonConfig,
         String name, Map classMap, Class collectionType ) {
      Class targetClass = findTargetClasskey, classMap );
      targetClass = targetClass == null ? findTargetClassname, classMap : targetClass;
      JsonConfig jsc = jsonConfig.copy();
      jsc.setRootClasstargetClass );
      jsc.setClassMapclassMap );
      jsc.setCollectionTypecollectionType );
      return JSONArray.toCollection( (JSONArrayvalue, jsc );
   }

   private static Class resolveClass(Map classMap, String key, String name, Class type) {
       Class targetClass = findTargetClass(key, classMap);
       if (targetClass == null) {
           targetClass = findTargetClass(name, classMap);
       }
       if(targetClass == null && type != null) {
           if(List.class.equals(type)) {
               targetClass = ArrayList.class;
           else if(Map.class.equals(type)) {
               targetClass = LinkedHashMap.class;
           else if(Set.class.equals(type)) {
               targetClass = LinkedHashSet.class;
           else if(!type.isInterface() && !Object.class.equals(type)) {
               targetClass = type;
           }
       }
       return targetClass;
   }
   
   /**
    * Locates a Class associated to a specifi key.<br>
    * The key may be a regexp.
    */
   private static Class findTargetClassString key, Map classMap ) {
      // try get first
      Class targetClass = (ClassclassMap.getkey );
      iftargetClass == null ){
         // try with regexp
         // this will hit performance as it must iterate over all the keys
         // and create a RegexpMatcher for each key
         forIterator i = classMap.entrySet()
               .iterator(); i.hasNext()){
            Map.Entry entry = (Map.Entryi.next();
            ifRegexpUtils.getMatcher( (Stringentry.getKey() )
                  .matcheskey ) ){
               targetClass = (Classentry.getValue();
               break;
            }
         }
      }

      return targetClass;
   }

   private static boolean isTransientFieldString name, Class beanClass ) {
      try{
         return isTransientField(beanClass.getDeclaredFieldname ));
      }catchException e ){
         // swallow exception
      }
      return false;
   }
   
   private static boolean isTransientFieldField field ) {
      return (field.getModifiers() & Modifier.TRANSIENT== Modifier.TRANSIENT;
   }

   private static Object morphPropertyValueString key, Object value, Class type, Class targetType ) {
      Morpher morpher = JSONUtils.getMorpherRegistry()
            .getMorpherFortargetType );
      ifIdentityObjectMorpher.getInstance()
            .equalsmorpher ) ){
         log.warn"Can't transform property '" + key + "' from " + type.getName() " into "
               + targetType.getName() ". Will register a default BeanMorpher" );
         JSONUtils.getMorpherRegistry()
               .registerMorphernew BeanMorphertargetType, JSONUtils.getMorpherRegistry() ) );
      }
      value = JSONUtils.getMorpherRegistry()
            .morphtargetType, value );
      return value;
   }

   /**
    * Sets a property on the target bean.<br>
    * Bean may be a Map or a POJO.
    */
   private static void setPropertyObject bean, String key, Object value, JsonConfig jsonConfig )
         throws Exception {
      PropertySetStrategy propertySetStrategy = jsonConfig.getPropertySetStrategy() != null ? jsonConfig.getPropertySetStrategy()
            : PropertySetStrategy.DEFAULT;
      propertySetStrategy.setPropertybean, key, value, jsonConfig );
   }
   
   private static void setValueJSONObject jsonObject, String key, Object value, Class type,
         JsonConfig jsonConfig, boolean bypass ) {
      boolean accumulated = false;
      ifvalue == null ){
         value = jsonConfig.findDefaultValueProcessortype )
               .getDefaultValuetype );
         if!JsonVerifier.isValidJsonValuevalue ) ){
            throw new JSONException"Value is not a valid JSON value. " + value );
         }
      }
      ifjsonObject.properties.containsKeykey ) ){
         ifString.class.isAssignableFromtype ) ){
            Object o = jsonObject.optkey );
            ifinstanceof JSONArray ){
               ((JSONArrayo).addString( (Stringvalue );
            }else{
               jsonObject.properties.putkey, new JSONArray().element)
                     .addString( (Stringvalue ) );
            }
         }else{
            jsonObject.accumulatekey, value, jsonConfig );
         }
         accumulated = true;
      }else{
         ifbypass || String.class.isAssignableFromtype ) ){
            jsonObject.properties.putkey, value );
         }else{
            jsonObject.setInternalkey, value, jsonConfig );
         }
      }

      value = jsonObject.optkey );
      ifaccumulated ){
         JSONArray array = (JSONArrayvalue;
         value = array.getarray.size() );
      }
      firePropertySetEventkey, value, accumulated, jsonConfig );
   }

   // ------------------------------------------------------

   /** identifies this object as null */
   private boolean nullObject;

   /**
    * The Map where the JSONObject's properties are kept.
    */
   private Map properties;

   /**
    * Construct an empty JSONObject.
    */
   public JSONObject() {
      this.properties = new ListOrderedMap();
   }

   /**
    * Creates a JSONObject that is null.
    */
   public JSONObjectboolean isNull ) {
      this();
      this.nullObject = isNull;
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, boolean value ) {
      return _accumulatekey, value ? Boolean.TRUE : Boolean.FALSE, new JsonConfig() );
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, double value ) {
      return _accumulatekey, new Doublevalue )new JsonConfig() );
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, int value ) {
      return _accumulatekey, new Integervalue )new JsonConfig() );
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, long value ) {
      return _accumulatekey, new Longvalue )new JsonConfig() );
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, Object value ) {
      return _accumulatekey, value, new JsonConfig() );
   }

   /**
    * Accumulate values under a key. It is similar to the element method except
    * that if there is already an object stored under the key then a JSONArray
    * is stored under the key to hold all of the accumulated values. If there is
    * already a JSONArray, then the new value is appended to it. In contrast,
    * the replace method replaces the previous value.
    *
    @param key A key string.
    @param value An object to be accumulated under the key.
    @return this.
    @throws JSONException If the value is an invalid number or if the key is
    *         null.
    */
   public JSONObject accumulateString key, Object value, JsonConfig jsonConfig ) {
      return _accumulatekey, value, jsonConfig );
   }

   public void accumulateAllMap map ) {
      accumulateAllmap, new JsonConfig() );
   }

   public void accumulateAllMap map, JsonConfig jsonConfig ) {
      ifmap instanceof JSONObject ){
         forIterator entries = map.entrySet()
               .iterator(); entries.hasNext()){
            Map.Entry entry = (Map.Entryentries.next();
            String key = (Stringentry.getKey();
            Object value = entry.getValue();
            accumulatekey, value, jsonConfig );
         }
      }else{
         forIterator entries = map.entrySet()
               .iterator(); entries.hasNext()){
            Map.Entry entry = (Map.Entryentries.next();
            String key = String.valueOfentry.getKey() );
            Object value = entry.getValue();
            accumulatekey, value, jsonConfig );
         }
      }
   }

   public void clear() {
      properties.clear();
   }

   public int compareToObject obj ) {
      ifobj != null && (obj instanceof JSONObject) ){
         JSONObject other = (JSONObjectobj;
         int size1 = size();
         int size2 = other.size();
         ifsize1 < size2 ){
            return -1;
         }else ifsize1 > size2 ){
            return 1;
         }else ifthis.equalsother ) ){
            return 0;
         }
      }
      return -1;
   }

   public boolean containsKeyObject key ) {
      return properties.containsKeykey );
   }

   public boolean containsValueObject value ) {
      return containsValuevalue, new JsonConfig() );
   }

   public boolean containsValueObject value, JsonConfig jsonConfig ) {
      try{
         value = processValuevalue, jsonConfig );
      }catchJSONException e ){
         return false;
      }
      return properties.containsValuevalue );
   }

   /**
    * Remove a name and its value, if present.
    *
    @param key A key string.
    @return this.
    */
   public JSONObject discardString key ) {
      verifyIsNull();
      this.properties.removekey );
      return this;
   }

   /**
    * Put a key/boolean pair in the JSONObject.
    *
    @param key A key string.
    @param value A boolean which is the value.
    @return this.
    @throws JSONException If the key is null.
    */
   public JSONObject elementString key, boolean value ) {
      verifyIsNull();
      return elementkey, value ? Boolean.TRUE : Boolean.FALSE );
   }

   /**
    * Put a key/value pair in the JSONObject, where the value will be a
    * JSONArray which is produced from a Collection.
    *
    @param key A key string.
    @param value A Collection value.
    @return this.
    @throws JSONException
    */
   public JSONObject elementString key, Collection value ) {
      return elementkey, value, new JsonConfig() );
   }

   /**
    * Put a key/value pair in the JSONObject, where the value will be a
    * JSONArray which is produced from a Collection.
    *
    @param key A key string.
    @param value A Collection value.
    @return this.
    @throws JSONException
    */
   public JSONObject elementString key, Collection value, JsonConfig jsonConfig ) {
      verifyIsNull();
      if!(value instanceof JSONArray) ){
         value = JSONArray.fromObjectvalue, jsonConfig );
      }
      return setInternalkey, value, jsonConfig );
   }

   /**
    * Put a key/double pair in the JSONObject.
    *
    @param key A key string.
    @param value A double which is the value.
    @return this.
    @throws JSONException If the key is null or if the number is invalid.
    */
   public JSONObject elementString key, double value ) {
      verifyIsNull();
      Double d = new Doublevalue );
      JSONUtils.testValidity);
      return elementkey, d );
   }

   /**
    * Put a key/int pair in the JSONObject.
    *
    @param key A key string.
    @param value An int which is the value.
    @return this.
    @throws JSONException If the key is null.
    */
   public JSONObject elementString key, int value ) {
      verifyIsNull();
      return elementkey, new Integervalue ) );
   }

   /**
    * Put a key/long pair in the JSONObject.
    *
    @param key A key string.
    @param value A long which is the value.
    @return this.
    @throws JSONException If the key is null.
    */
   public JSONObject elementString key, long value ) {
      verifyIsNull();
      return elementkey, new Longvalue ) );
   }

   /**
    * Put a key/value pair in the JSONObject, where the value will be a
    * JSONObject which is produced from a Map.
    *
    @param key A key string.
    @param value A Map value.
    @return this.
    @throws JSONException
    */
   public JSONObject elementString key, Map value ) {
      return elementkey, value, new JsonConfig() );
   }

   /**
    * Put a key/value pair in the JSONObject, where the value will be a
    * JSONObject which is produced from a Map.
    *
    @param key A key string.
    @param value A Map value.
    @return this.
    @throws JSONException
    */
   public JSONObject elementString key, Map value, JsonConfig jsonConfig ) {
      verifyIsNull();
      ifvalue instanceof JSONObject ){
         return setInternalkey, value, jsonConfig );
      }else{
         return elementkey, JSONObject.fromObjectvalue, jsonConfig ), jsonConfig );
      }
   }

   /**
    * Put a key/value pair in the JSONObject. If the value is null, then the key
    * will be removed from the JSONObject if it is present.<br>
    * If there is a previous value assigned to the key, it will call accumulate.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is non-finite number or if the key is
    *         null.
    */
   public JSONObject elementString key, Object value ) {
      return elementkey, value, new JsonConfig() );
   }

   /**
    * Put a key/value pair in the JSONObject. If the value is null, then the key
    * will be removed from the JSONObject if it is present.<br>
    * If there is a previous value assigned to the key, it will call accumulate.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is non-finite number or if the key is
    *         null.
    */
   public JSONObject elementString key, Object value, JsonConfig jsonConfig ) {
      verifyIsNull();
      ifkey == null ){
         throw new JSONException"Null key." );
      }
      ifvalue != null ){
         value = processValuekey, value, jsonConfig );
         _setInternalkey, value, jsonConfig );
      }else{
         removekey );
      }
      return this;
   }

   /**
    * Put a key/value pair in the JSONObject, but only if the key and the value
    * are both non-null.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is a non-finite number.
    */
   public JSONObject elementOptString key, Object value ) {
      return elementOptkey, value, new JsonConfig() );
   }

   /**
    * Put a key/value pair in the JSONObject, but only if the key and the value
    * are both non-null.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is a non-finite number.
    */
   public JSONObject elementOptString key, Object value, JsonConfig jsonConfig ) {
      verifyIsNull();
      ifkey != null && value != null ){
         elementkey, value, jsonConfig );
      }
      return this;
   }

   public Set entrySet() {
      return Collections.unmodifiableSetproperties.entrySet() );
   }

   public boolean equalsObject obj ) {
      ifobj == this ){
         return true;
      }
      ifobj == null ){
         return false;
      }

      if!(obj instanceof JSONObject) ){
         return false;
      }

      JSONObject other = (JSONObjectobj;

      ifisNullObject() ){
         ifother.isNullObject() ){
            return true;
         }else{
            return false;
         }
      }else{
         ifother.isNullObject() ){
            return false;
         }
      }

      ifother.size() != size() ){
         return false;
      }

      forIterator keys = properties.keySet()
            .iterator(); keys.hasNext()){
         String key = (Stringkeys.next();
         if!other.properties.containsKeykey ) ){
            return false;
         }
         Object o1 = properties.getkey );
         Object o2 = other.properties.getkey );

         ifJSONNull.getInstance()
               .equalso1 ) ){
            ifJSONNull.getInstance()
                  .equalso2 ) ){
               continue;
            }else{
               return false;
            }
         }else{
            ifJSONNull.getInstance()
                  .equalso2 ) ){
               return false;
            }
         }

         ifo1 instanceof String && o2 instanceof JSONFunction ){
            if!o1.equalsString.valueOfo2 ) ) ){
               return false;
            }
         }else ifo1 instanceof JSONFunction && o2 instanceof String ){
            if!o2.equalsString.valueOfo1 ) ) ){
               return false;
            }
         }else ifo1 instanceof JSONObject && o2 instanceof JSONObject ){
            if!o1.equalso2 ) ){
               return false;
            }
         }else ifo1 instanceof JSONArray && o2 instanceof JSONArray ){
            if!o1.equalso2 ) ){
               return false;
            }
         }else ifo1 instanceof JSONFunction && o2 instanceof JSONFunction ){
            if!o1.equalso2 ) ){
               return false;
            }
         }else{
            ifo1 instanceof String ){
               if!o1.equalsString.valueOfo2 ) ) ){
                  return false;
               }
            }else ifo2 instanceof String ){
               if!o2.equalsString.valueOfo1 ) ) ){
                  return false;
               }
            }else{
               Morpher m1 = JSONUtils.getMorpherRegistry()
                     .getMorpherForo1.getClass() );
               Morpher m2 = JSONUtils.getMorpherRegistry()
                     .getMorpherForo2.getClass() );
               ifm1 != null && m1 != IdentityObjectMorpher.getInstance() ){
                  if!o1.equalsJSONUtils.getMorpherRegistry()
                        .morpho1.getClass(), o2 ) ) ){
                     return false;
                  }
               }else ifm2 != null && m2 != IdentityObjectMorpher.getInstance() ){
                  if!JSONUtils.getMorpherRegistry()
                        .morpho1.getClass(), o1 )
                        .equalso2 ) ){
                     return false;
                  }
               }else{
                  if!o1.equalso2 ) ){
                     return false;
                  }
               }
            }
         }
      }
      return true;
   }

   public Object getObject key ) {
      ifkey instanceof String ){
         return get( (Stringkey );
      }
      return null;
   }

   /**
    * Get the value object associated with a key.
    *
    @param key A key string.
    @return The object associated with the key.
    @throws JSONException if this.isNull() returns true.
    */
   public Object getString key ) {
      verifyIsNull();
      return this.properties.getkey );
   }

   /**
    * Get the boolean value associated with a key.
    *
    @param key A key string.
    @return The truth.
    @throws JSONException if the value is not a Boolean or the String "true"
    *         or "false".
    */
   public boolean getBooleanString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null ){
         ifo.equalsBoolean.FALSE )
               || (instanceof String && ((Stringo).equalsIgnoreCase"false" )) ){
            return false;
         }else ifo.equalsBoolean.TRUE )
               || (instanceof String && ((Stringo).equalsIgnoreCase"true" )) ){
            return true;
         }
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a Boolean." );
   }

   /**
    * Get the double value associated with a key.
    *
    @param key A key string.
    @return The numeric value.
    @throws JSONException if the key is not found or if the value is not a
    *         Number object and cannot be converted to a number.
    */
   public double getDoubleString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null ){
         try{
            return instanceof Number ? ((Numbero).doubleValue()
                  : Double.parseDouble( (String);
         }catchException e ){
            throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a number." );
         }
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a number." );
   }

   /**
    * Get the int value associated with a key. If the number value is too large
    * for an int, it will be clipped.
    *
    @param key A key string.
    @return The integer value.
    @throws JSONException if the key is not found or if the value cannot be
    *         converted to an integer.
    */
   public int getIntString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null ){
         return instanceof Number ? ((Numbero).intValue() (intgetDoublekey );
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a number." );
   }

   /**
    * Get the JSONArray value associated with a key.
    *
    @param key A key string.
    @return A JSONArray which is the value.
    @throws JSONException if the key is not found or if the value is not a
    *         JSONArray.
    */
   public JSONArray getJSONArrayString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null && o instanceof JSONArray ){
         return (JSONArrayo;
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a JSONArray." );
   }

   /**
    * Get the JSONObject value associated with a key.
    *
    @param key A key string.
    @return A JSONObject which is the value.
    @throws JSONException if the key is not found or if the value is not a
    *         JSONObject.
    */
   public JSONObject getJSONObjectString key ) {
      verifyIsNull();
      Object o = getkey );
      ifJSONNull.getInstance()
            .equals) ){
         return new JSONObjecttrue );
      }else ifinstanceof JSONObject ){
         return (JSONObjecto;
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a JSONObject." );
   }

   /**
    * Get the long value associated with a key. If the number value is too long
    * for a long, it will be clipped.
    *
    @param key A key string.
    @return The long value.
    @throws JSONException if the key is not found or if the value cannot be
    *         converted to a long.
    */
   public long getLongString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null ){
         return instanceof Number ? ((Numbero).longValue() (longgetDoublekey );
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] is not a number." );
   }

   /**
    * Get the string associated with a key.
    *
    @param key A key string.
    @return A string which is the value.
    @throws JSONException if the key is not found.
    */
   public String getStringString key ) {
      verifyIsNull();
      Object o = getkey );
      ifo != null ){
         return o.toString();
      }
      throw new JSONException"JSONObject[" + JSONUtils.quotekey "] not found." );
   }

   /**
    * Determine if the JSONObject contains a specific key.
    *
    @param key A key string.
    @return true if the key exists in the JSONObject.
    */
   public boolean hasString key ) {
      verifyIsNull();
      return this.properties.containsKeykey );
   }

   public int hashCode() {
      int hashcode = 19;
      ifisNullObject() ){
         return hashcode + JSONNull.getInstance()
               .hashCode();
      }
      forIterator entries = properties.entrySet()
            .iterator(); entries.hasNext()){
         Map.Entry entry = (Map.Entryentries.next();
         Object key = entry.getKey();
         Object value = entry.getValue();
         hashcode += key.hashCode() + JSONUtils.hashCodevalue );
      }
      return hashcode;
   }

   public boolean isArray() {
      return false;
   }

   public boolean isEmpty() {
      // verifyIsNull();
      return this.properties.isEmpty();
   }

   /**
    * Returs if this object is a null JSONObject.
    */
   public boolean isNullObject() {
      return nullObject;
   }

   /**
    * Get an enumeration of the keys of the JSONObject.
    *
    @return An iterator of the keys.
    */
   public Iterator keys() {
      verifyIsNull();
      return keySet().iterator();
   }

   public Set keySet() {
      return Collections.unmodifiableSetproperties.keySet() );
   }

   /**
    * Produce a JSONArray containing the names of the elements of this
    * JSONObject.
    *
    @return A JSONArray containing the key strings, or null if the JSONObject
    *         is empty.
    */
   public JSONArray names() {
      return namesnew JsonConfig() );
   }
   
   /**
    * Produce a JSONArray containing the names of the elements of this
    * JSONObject.
    *
    @return A JSONArray containing the key strings, or null if the JSONObject
    *         is empty.
    */
   public JSONArray namesJsonConfig jsonConfig ) {
      verifyIsNull();
      JSONArray ja = new JSONArray();
      Iterator keys = keys();
      whilekeys.hasNext() ){
         ja.elementkeys.next(), jsonConfig );
      }
      return ja;
   }

   /**
    * Get an optional value associated with a key.
    *
    @param key A key string.
    @return An object which is the value, or null if there is no value.
    */
   public Object optString key ) {
      verifyIsNull();
      return key == null null this.properties.getkey );
   }

   /**
    * Get an optional boolean associated with a key. It returns false if there
    * is no such key, or if the value is not Boolean.TRUE or the String "true".
    *
    @param key A key string.
    @return The truth.
    */
   public boolean optBooleanString key ) {
      verifyIsNull();
      return optBooleankey, false );
   }

   /**
    * Get an optional boolean associated with a key. It returns the defaultValue
    * if there is no such key, or if it is not a Boolean or the String "true" or
    * "false" (case insensitive).
    *
    @param key A key string.
    @param defaultValue The default.
    @return The truth.
    */
   public boolean optBooleanString key, boolean defaultValue ) {
      verifyIsNull();
      try{
         return getBooleankey );
      }catchException e ){
         return defaultValue;
      }
   }

   /**
    * Get an optional double associated with a key, or NaN if there is no such
    * key or if its value is not a number. If the value is a string, an attempt
    * will be made to evaluate it as a number.
    *
    @param key A string which is the key.
    @return An object which is the value.
    */
   public double optDoubleString key ) {
      verifyIsNull();
      return optDoublekey, Double.NaN );
   }

   /**
    * Get an optional double associated with a key, or the defaultValue if there
    * is no such key or if its value is not a number. If the value is a string,
    * an attempt will be made to evaluate it as a number.
    *
    @param key A key string.
    @param defaultValue The default.
    @return An object which is the value.
    */
   public double optDoubleString key, double defaultValue ) {
      verifyIsNull();
      try{
         Object o = optkey );
         return instanceof Number ? ((Numbero).doubleValue()
               new Double( (String).doubleValue();
      }catchException e ){
         return defaultValue;
      }
   }

   /**
    * Get an optional int value associated with a key, or zero if there is no
    * such key or if the value is not a number. If the value is a string, an
    * attempt will be made to evaluate it as a number.
    *
    @param key A key string.
    @return An object which is the value.
    */
   public int optIntString key ) {
      verifyIsNull();
      return optIntkey, );
   }

   /**
    * Get an optional int value associated with a key, or the default if there
    * is no such key or if the value is not a number. If the value is a string,
    * an attempt will be made to evaluate it as a number.
    *
    @param key A key string.
    @param defaultValue The default.
    @return An object which is the value.
    */
   public int optIntString key, int defaultValue ) {
      verifyIsNull();
      try{
         return getIntkey );
      }catchException e ){
         return defaultValue;
      }
   }

   /**
    * Get an optional JSONArray associated with a key. It returns null if there
    * is no such key, or if its value is not a JSONArray.
    *
    @param key A key string.
    @return A JSONArray which is the value.
    */
   public JSONArray optJSONArrayString key ) {
      verifyIsNull();
      Object o = optkey );
      return instanceof JSONArray ? (JSONArrayo : null;
   }

   /**
    * Get an optional JSONObject associated with a key. It returns null if there
    * is no such key, or if its value is not a JSONObject.
    *
    @param key A key string.
    @return A JSONObject which is the value.
    */
   public JSONObject optJSONObjectString key ) {
      verifyIsNull();
      Object o = optkey );
      return instanceof JSONObject ? (JSONObjecto : null;
   }

   /**
    * Get an optional long value associated with a key, or zero if there is no
    * such key or if the value is not a number. If the value is a string, an
    * attempt will be made to evaluate it as a number.
    *
    @param key A key string.
    @return An object which is the value.
    */
   public long optLongString key ) {
      verifyIsNull();
      return optLongkey, );
   }

   /**
    * Get an optional long value associated with a key, or the default if there
    * is no such key or if the value is not a number. If the value is a string,
    * an attempt will be made to evaluate it as a number.
    *
    @param key A key string.
    @param defaultValue The default.
    @return An object which is the value.
    */
   public long optLongString key, long defaultValue ) {
      verifyIsNull();
      try{
         return getLongkey );
      }catchException e ){
         return defaultValue;
      }
   }

   /**
    * Get an optional string associated with a key. It returns an empty string
    * if there is no such key. If the value is not a string and is not null,
    * then it is coverted to a string.
    *
    @param key A key string.
    @return A string which is the value.
    */
   public String optStringString key ) {
      verifyIsNull();
      return optStringkey, "" );
   }

   /**
    * Get an optional string associated with a key. It returns the defaultValue
    * if there is no such key.
    *
    @param key A key string.
    @param defaultValue The default.
    @return A string which is the value.
    */
   public String optStringString key, String defaultValue ) {
      verifyIsNull();
      Object o = optkey );
      return o != null ? o.toString() : defaultValue;
   }

   public Object putObject key, Object value ) {
      ifkey == null ){
         throw new IllegalArgumentException"key is null." );
      }
      Object previous = properties.getkey );
      elementString.valueOfkey ), value );
      return previous;
   }

   public void putAllMap map ) {
      putAllmap, new JsonConfig() );
   }

   public void putAllMap map, JsonConfig jsonConfig ) {
      ifmap instanceof JSONObject ){
         forIterator entries = map.entrySet()
               .iterator(); entries.hasNext()){
            Map.Entry entry = (Map.Entryentries.next();
            String key = (Stringentry.getKey();
            Object value = entry.getValue();
            this.properties.putkey, value );
         }
      }else{
         forIterator entries = map.entrySet()
               .iterator(); entries.hasNext()){
            Map.Entry entry = (Map.Entryentries.next();
            String key = String.valueOfentry.getKey() );
            Object value = entry.getValue();
            elementkey, value, jsonConfig );
         }
      }
   }

   public Object removeObject key ) {
      return properties.removekey );
   }

   /**
    * Remove a name and its value, if present.
    *
    @param key The name to be removed.
    @return The value that was associated with the name, or null if there was
    *         no value.
    */
   public Object removeString key ) {
      verifyIsNull();
      return this.properties.removekey );
   }

   /**
    * Get the number of keys stored in the JSONObject.
    *
    @return The number of keys in the JSONObject.
    */
   public int size() {
      // verifyIsNull();
      return this.properties.size();
   }

   /**
    * Produce a JSONArray containing the values of the members of this
    * JSONObject.
    *
    @param names A JSONArray containing a list of key strings. This determines
    *        the sequence of the values in the result.
    @return A JSONArray of values.
    @throws JSONException If any of the values are non-finite numbers.
    */
   public JSONArray toJSONArrayJSONArray names ) {
      verifyIsNull();
      ifnames == null || names.size() == ){
         return null;
      }
      JSONArray ja = new JSONArray();
      forint i = 0; i < names.size(); i += ){
         ja.elementthis.optnames.getString) ) );
      }
      return ja;
   }

   /**
    * Make a JSON text of this JSONObject. For compactness, no whitespace is
    * added. If this would not result in a syntactically correct JSON text, then
    * null will be returned instead.
    <p>
    * Warning: This method assumes that the data structure is acyclical.
    *
    @return a printable, displayable, portable, transmittable representation
    *         of the object, beginning with <code>{</code>&nbsp;<small>(left
    *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
    *         brace)</small>.
    */
   public String toString() {
      ifisNullObject() ){
         return JSONNull.getInstance()
               .toString();
      }
      try{
         Iterator keys = keys();
         StringBuffer sb = new StringBuffer"{" );

         whilekeys.hasNext() ){
            ifsb.length() ){
               sb.append',' );
            }
            Object o = keys.next();
            sb.appendJSONUtils.quoteo.toString() ) );
            sb.append':' );
            sb.appendJSONUtils.valueToStringthis.properties.get) ) );
         }
         sb.append'}' );
         return sb.toString();
      }catchException e ){
         return null;
      }
   }

   /**
    * Make a prettyprinted JSON text of this JSONObject.
    <p>
    * Warning: This method assumes that the data structure is acyclical.
    *
    @param indentFactor The number of spaces to add to each level of
    *        indentation.
    @return a printable, displayable, portable, transmittable representation
    *         of the object, beginning with <code>{</code>&nbsp;<small>(left
    *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
    *         brace)</small>.
    @throws JSONException If the object contains an invalid number.
    */
   public String toStringint indentFactor ) {
      ifisNullObject() ){
         return JSONNull.getInstance()
               .toString();
      }
      ifindentFactor == ){
         return this.toString();
      }
      return toStringindentFactor, );
   }

   /**
    * Make a prettyprinted JSON text of this JSONObject.
    <p>
    * Warning: This method assumes that the data structure is acyclical.
    *
    @param indentFactor The number of spaces to add to each level of
    *        indentation.
    @param indent The indentation of the top level.
    @return a printable, displayable, transmittable representation of the
    *         object, beginning with <code>{</code>&nbsp;<small>(left brace)</small>
    *         and ending with <code>}</code>&nbsp;<small>(right brace)</small>.
    @throws JSONException If the object contains an invalid number.
    */
   public String toStringint indentFactor, int indent ) {
      ifisNullObject() ){
         return JSONNull.getInstance()
               .toString();
      }
      int i;
      int n = size();
      ifn == ){
         return "{}";
      }
      ifindentFactor == ){
         return this.toString();
      }
      Iterator keys = keys();
      StringBuffer sb = new StringBuffer"{" );
      int newindent = indent + indentFactor;
      Object o;
      ifn == ){
         o = keys.next();
         sb.appendJSONUtils.quoteo.toString() ) );
         sb.append": " );
         sb.appendJSONUtils.valueToStringthis.properties.get), indentFactor, indent ) );
      }else{
         whilekeys.hasNext() ){
            o = keys.next();
            ifsb.length() ){
               sb.append",\n" );
            }else{
               sb.append'\n' );
            }
            fori = 0; i < newindent; i += ){
               sb.append' ' );
            }
            sb.appendJSONUtils.quoteo.toString() ) );
            sb.append": " );
            sb.appendJSONUtils.valueToStringthis.properties.get), indentFactor, newindent ) );
         }
         ifsb.length() ){
            sb.append'\n' );
            fori = 0; i < indent; i += ){
               sb.append' ' );
            }
         }
         fori = 0; i < indent; i += ){
            sb.insert0' ' );
         }
      }
      sb.append'}' );
      return sb.toString();
   }

   public Collection values() {
      return Collections.unmodifiableCollectionproperties.values() );
   }

   /**
    * Write the contents of the JSONObject as JSON text to a writer. For
    * compactness, no whitespace is added.
    <p>
    * Warning: This method assumes that the data structure is acyclical.
    *
    @return The writer.
    @throws JSONException
    */
   public Writer writeWriter writer ) {
      try{
         ifisNullObject() ){
            writer.writeJSONNull.getInstance()
                  .toString() );
            return writer;
         }

         boolean b = false;
         Iterator keys = keys();
         writer.write'{' );

         whilekeys.hasNext() ){
            if){
               writer.write',' );
            }
            Object k = keys.next();
            writer.writeJSONUtils.quotek.toString() ) );
            writer.write':' );
            Object v = this.properties.get);
            ifinstanceof JSONObject ){
               ((JSONObjectv).writewriter );
            }else ifinstanceof JSONArray ){
               ((JSONArrayv).writewriter );
            }else{
               writer.writeJSONUtils.valueToString) );
            }
            b = true;
         }
         writer.write'}' );
         return writer;
      }catchIOException e ){
         throw new JSONException);
      }
   }

   private JSONObject _accumulateString key, Object value, JsonConfig jsonConfig ) {
      ifisNullObject() ){
         throw new JSONException"Can't accumulate on null object" );
      }

      if!haskey ) ){
         setInternalkey, value, jsonConfig );
      }else{
         Object o = optkey );
         ifinstanceof JSONArray ){
            ((JSONArrayo).elementvalue, jsonConfig );
         }else{
            setInternalkey, new JSONArray().element)
                  .elementvalue, jsonConfig ), jsonConfig );
         }
      }

      return this;
   }

   protected Object _processValueObject value, JsonConfig jsonConfig ) {
      ifvalue instanceof JSONTokener ) {
         return _fromJSONTokener( (JSONTokenervalue, jsonConfig );
      }
      return super._processValuevalue, jsonConfig );
   }

   /**
    * Put a key/value pair in the JSONObject.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is non-finite number or if the key is
    *         null.
    */
   private JSONObject _setInternalString key, Object value, JsonConfig jsonConfig ) {
      verifyIsNull();
      ifkey == null ){
         throw new JSONException"Null key." );
      }

      ifJSONUtils.isStringvalue && JSONUtils.mayBeJSONString.valueOfvalue ) ) ){
         this.properties.putkey, value );
      }else{
         /*
         Object jo = _processValue( value, jsonConfig );
         if( CycleDetectionStrategy.IGNORE_PROPERTY_OBJ == jo
               || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == jo ){
            // do nothing
         }else{
            this.properties.put( key, jo );
         }
         */
         ifCycleDetectionStrategy.IGNORE_PROPERTY_OBJ == value
               || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == value ){
            // do nothing
         }else{
            this.properties.putkey, value );
         }
      }

      return this;
   }

   private Object processValueObject value, JsonConfig jsonConfig ) {
      ifvalue != null ){
         JsonValueProcessor processor = jsonConfig.findJsonValueProcessorvalue.getClass() );
         ifprocessor != null ){
            value = processor.processObjectValuenull, value, jsonConfig );
            if!JsonVerifier.isValidJsonValuevalue ) ){
               throw new JSONException"Value is not a valid JSON value. " + value );
            }
         }
      }
      return _processValuevalue, jsonConfig );
   }

   private Object processValueString key, Object value, JsonConfig jsonConfig ) {
      ifvalue != null ){
         JsonValueProcessor processor = jsonConfig.findJsonValueProcessorvalue.getClass(), key );
         ifprocessor != null ){
            value = processor.processObjectValuenull, value, jsonConfig );
            if!JsonVerifier.isValidJsonValuevalue ) ){
               throw new JSONException"Value is not a valid JSON value. " + value );
            }
         }
      }
      return _processValuevalue, jsonConfig );
   }

   /**
    * Put a key/value pair in the JSONObject.
    *
    @param key A key string.
    @param value An object which is the value. It should be of one of these
    *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
    *        String, or the JSONNull object.
    @return this.
    @throws JSONException If the value is non-finite number or if the key is
    *         null.
    */
   private JSONObject setInternalString key, Object value, JsonConfig jsonConfig ) {
      return _setInternalkey, processValuekey, value, jsonConfig ), jsonConfig );
   }

   /**
    * Checks if this object is a "null" object.
    */
   private void verifyIsNull() {
      ifisNullObject() ){
         throw new JSONException"null object" );
      }
   }
}