Coverage Report - net.sf.json.JSONObject
 
Classes in this File Line Coverage Branch Coverage Complexity
JSONObject
0%
0/1156
0%
0/785
5.34
 
 1  
 /*
 2  
  * Copyright 2002-2009 the original author or authors.
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package net.sf.json;
 17  
 
 18  
 import java.beans.PropertyDescriptor;
 19  
 import java.io.IOException;
 20  
 import java.io.Writer;
 21  
 import java.lang.reflect.Array;
 22  
 import java.lang.reflect.Field;
 23  
 import java.lang.reflect.Modifier;
 24  
 import java.util.Collection;
 25  
 import java.util.Collections;
 26  
 import java.util.HashMap;
 27  
 import java.util.Iterator;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 import java.util.Set;
 31  
 
 32  
 import net.sf.ezmorph.Morpher;
 33  
 import net.sf.ezmorph.array.ObjectArrayMorpher;
 34  
 import net.sf.ezmorph.bean.BeanMorpher;
 35  
 import net.sf.ezmorph.object.IdentityObjectMorpher;
 36  
 import net.sf.json.processors.JsonBeanProcessor;
 37  
 import net.sf.json.processors.JsonValueProcessor;
 38  
 import net.sf.json.processors.JsonVerifier;
 39  
 import net.sf.json.processors.PropertyNameProcessor;
 40  
 import net.sf.json.regexp.RegexpUtils;
 41  
 import net.sf.json.util.CycleDetectionStrategy;
 42  
 import net.sf.json.util.JSONTokener;
 43  
 import net.sf.json.util.JSONUtils;
 44  
 import net.sf.json.util.PropertyFilter;
 45  
 import net.sf.json.util.PropertySetStrategy;
 46  
 
 47  
 import org.apache.commons.beanutils.DynaBean;
 48  
 import org.apache.commons.beanutils.DynaProperty;
 49  
 import org.apache.commons.beanutils.PropertyUtils;
 50  
 import org.apache.commons.collections.map.ListOrderedMap;
 51  
 import org.apache.commons.lang.StringUtils;
 52  
 import org.apache.commons.logging.Log;
 53  
 import org.apache.commons.logging.LogFactory;
 54  
 
 55  
 /**
 56  
  * A JSONObject is an unordered collection of name/value pairs. Its external
 57  
  * form is a string wrapped in curly braces with colons between the names and
 58  
  * values, and commas between the values and names. The internal form is an
 59  
  * object having <code>get</code> and <code>opt</code> methods for accessing
 60  
  * the values by name, and <code>put</code> methods for adding or replacing
 61  
  * values by name. The values can be any of these types: <code>Boolean</code>,
 62  
  * <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>,
 63  
  * <code>String</code>, or the <code>JSONNull</code> object. A JSONObject
 64  
  * constructor can be used to convert an external form JSON text into an
 65  
  * internal form whose values can be retrieved with the <code>get</code> and
 66  
  * <code>opt</code> methods, or to convert values into a JSON text using the
 67  
  * <code>element</code> and <code>toString</code> methods. A
 68  
  * <code>get</code> method returns a value if one can be found, and throws an
 69  
  * exception if one cannot be found. An <code>opt</code> method returns a
 70  
  * default value instead of throwing an exception, and so is useful for
 71  
  * obtaining optional values.
 72  
  * <p>
 73  
  * The generic <code>get()</code> and <code>opt()</code> methods return an
 74  
  * object, which you can cast or query for type. There are also typed
 75  
  * <code>get</code> and <code>opt</code> methods that do type checking and
 76  
  * type coercion for you.
 77  
  * <p>
 78  
  * The <code>put</code> methods adds values to an object. For example,
 79  
  *
 80  
  * <pre>
 81  
  *     myString = new JSONObject().put("JSON", "Hello, World!").toString();</pre>
 82  
  *
 83  
  * produces the string <code>{"JSON": "Hello, World"}</code>.
 84  
  * <p>
 85  
  * The texts produced by the <code>toString</code> methods strictly conform to
 86  
  * the JSON syntax rules. The constructors are more forgiving in the texts they
 87  
  * will accept:
 88  
  * <ul>
 89  
  * <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
 90  
  * before the closing brace.</li>
 91  
  * <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single quote)</small>.</li>
 92  
  * <li>Strings do not need to be quoted at all if they do not begin with a
 93  
  * quote or single quote, and if they do not contain leading or trailing spaces,
 94  
  * and if they do not contain any of these characters:
 95  
  * <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
 96  
  * if they are not the reserved words <code>true</code>, <code>false</code>,
 97  
  * or <code>null</code>.</li>
 98  
  * <li>Keys can be followed by <code>=</code> or <code>=></code> as well as
 99  
  * by <code>:</code>.</li>
 100  
  * <li>Values can be followed by <code>;</code> <small>(semicolon)</small>
 101  
  * as well as by <code>,</code> <small>(comma)</small>.</li>
 102  
  * <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
 103  
  * <code>0x-</code> <small>(hex)</small> prefix.</li>
 104  
  * <li>Comments written in the slashshlash, slashstar, and hash conventions
 105  
  * will be ignored.</li>
 106  
  * </ul>
 107  
  *
 108  
  * @author JSON.org
 109  
  */
 110  
 public final class JSONObject extends AbstractJSON implements JSON, Map, Comparable {
 111  
 
 112  
    private static final Log log = LogFactory.getLog( JSONObject.class );
 113  
 
 114  
    /**
 115  
     * Creates a JSONObject.<br>
 116  
     * Inspects the object type to call the correct JSONObject factory method.
 117  0
     * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans.
 118  
     *
 119  
     * @param object
 120  
     * @throws JSONException if the object can not be converted to a proper
 121  
     *         JSONObject.
 122  
     */
 123  
 
 124  
    public static JSONObject fromObject( Object object ) {
 125  
       return fromObject( object, new JsonConfig() );
 126  
    }
 127  
 
 128  
    /**
 129  
     * Creates a JSONObject.<br>
 130  0
     * Inspects the object type to call the correct JSONObject factory method.
 131  
     * Accepts JSON formatted strings, Maps, DynaBeans and JavaBeans.
 132  
     *
 133  
     * @param object
 134  
     * @throws JSONException if the object can not be converted to a proper
 135  
     *         JSONObject.
 136  
     */
 137  
    public static JSONObject fromObject( Object object, JsonConfig jsonConfig ) {
 138  
       if( object == null || JSONUtils.isNull( object ) ){
 139  
          return new JSONObject( true );
 140  
       }else if( object instanceof JSONObject ){
 141  
          return _fromJSONObject( (JSONObject) object, jsonConfig );
 142  
       }else if( object instanceof DynaBean ){
 143  0
          return _fromDynaBean( (DynaBean) object, jsonConfig );
 144  0
       }else if( object instanceof JSONTokener ){
 145  0
          return _fromJSONTokener( (JSONTokener) object, jsonConfig );
 146  0
       }else if( object instanceof JSONString ){
 147  0
          return _fromJSONString( (JSONString) object, jsonConfig );
 148  
       }else if( object instanceof Map ){
 149  0
          return _fromMap( (Map) object, jsonConfig );
 150  0
       }else if( object instanceof String ){
 151  0
          return _fromString( (String) object, jsonConfig );
 152  0
       }else if( JSONUtils.isNumber( object ) || JSONUtils.isBoolean( object )
 153  0
             || JSONUtils.isString( object ) ){
 154  0
          return new JSONObject();
 155  0
       }else if( JSONUtils.isArray( object ) ){
 156  0
          throw new JSONException( "'object' is an array. Use JSONArray instead" );
 157  0
       }else{
 158  0
          return _fromBean( object, jsonConfig );
 159  0
       }
 160  0
    }
 161  0
 
 162  0
    /**
 163  
     * Creates a JSONDynaBean from a JSONObject.
 164  0
     */
 165  0
    public static Object toBean( JSONObject jsonObject ) {
 166  0
       if( jsonObject == null || jsonObject.isNullObject() ){
 167  
          return null;
 168  0
       }
 169  
 
 170  
       DynaBean dynaBean = null;
 171  
 
 172  
       JsonConfig jsonConfig = new JsonConfig();
 173  
       Map props = JSONUtils.getProperties( jsonObject );
 174  
       dynaBean = JSONUtils.newDynaBean( jsonObject, jsonConfig );
 175  
       for( Iterator entries = jsonObject.names( jsonConfig )
 176  0
             .iterator(); entries.hasNext(); ){
 177  0
          String name = (String) entries.next();
 178  
          String key = JSONUtils.convertToJavaIdentifier( name, jsonConfig );
 179  
          Class type = (Class) props.get( name );
 180  0
          Object value = jsonObject.get( name );
 181  
          try{
 182  0
             if( !JSONUtils.isNull( value ) ){
 183  0
                if( value instanceof JSONArray ){
 184  0
                   dynaBean.set( key, JSONArray.toCollection( (JSONArray) value ) );
 185  0
                }else if( String.class.isAssignableFrom( type )
 186  0
                      || Boolean.class.isAssignableFrom( type ) || JSONUtils.isNumber( type )
 187  0
                      || Character.class.isAssignableFrom( type )
 188  0
                      || JSONFunction.class.isAssignableFrom( type ) ){
 189  0
                   dynaBean.set( key, value );
 190  0
                }else{
 191  
                   dynaBean.set( key, toBean( (JSONObject) value ) );
 192  0
                }
 193  0
             }else{
 194  0
                if( type.isPrimitive() ){
 195  0
                   // assume assigned default value
 196  
                   log.warn( "Tried to assign null value to " + key + ":" + type.getName() );
 197  
                   dynaBean.set( key, JSONUtils.getMorpherRegistry()
 198  
                         .morph( type, null ) );
 199  0
                }else{
 200  
                   dynaBean.set( key, null );
 201  0
                }
 202  
             }
 203  
          }catch( JSONException jsone ){
 204  0
             throw jsone;
 205  
          }catch( Exception e ){
 206  0
             throw new JSONException( "Error while setting property=" + name + " type" + type, e );
 207  0
          }
 208  
       }
 209  
 
 210  0
       return dynaBean;
 211  
    }
 212  
 
 213  0
    /**
 214  0
     * Creates a bean from a JSONObject, with a specific target class.<br>
 215  0
     */
 216  0
    public static Object toBean( JSONObject jsonObject, Class beanClass ) {
 217  0
       JsonConfig jsonConfig = new JsonConfig();
 218  0
       jsonConfig.setRootClass( beanClass );
 219  
       return toBean( jsonObject, jsonConfig );
 220  0
    }
 221  
 
 222  
    /**
 223  
     * Creates a bean from a JSONObject, with a specific target class.<br>
 224  
     * If beanClass is null, this method will return a graph of DynaBeans. Any
 225  
     * attribute that is a JSONObject and matches a key in the classMap will be
 226  
     * converted to that target class.<br>
 227  0
     * The classMap has the following conventions:
 228  0
     * <ul>
 229  0
     * <li>Every key must be an String.</li>
 230  
     * <li>Every value must be a Class.</li>
 231  
     * <li>A key may be a regular expression.</li>
 232  
     * </ul>
 233  
     */
 234  
    public static Object toBean( JSONObject jsonObject, Class beanClass, Map classMap ) {
 235  
       JsonConfig jsonConfig = new JsonConfig();
 236  
       jsonConfig.setRootClass( beanClass );
 237  
       jsonConfig.setClassMap( classMap );
 238  
       return toBean( jsonObject, jsonConfig );
 239  
    }
 240  
 
 241  
    /**
 242  
     * Creates a bean from a JSONObject, with the specific configuration.
 243  
     */
 244  
    public static Object toBean( JSONObject jsonObject, JsonConfig jsonConfig ) {
 245  0
       if( jsonObject == null || jsonObject.isNullObject() ){
 246  0
          return null;
 247  0
       }
 248  0
 
 249  
       Class beanClass = jsonConfig.getRootClass();
 250  
       Map classMap = jsonConfig.getClassMap();
 251  
 
 252  
       if( beanClass == null ){
 253  
          return toBean( jsonObject );
 254  
       }
 255  0
       if( classMap == null ){
 256  0
          classMap = Collections.EMPTY_MAP;
 257  
       }
 258  
 
 259  0
       Object bean = null;
 260  0
       try{
 261  
          if( beanClass.isInterface() ){
 262  0
             if( !Map.class.isAssignableFrom( beanClass ) ){
 263  0
                throw new JSONException( "beanClass is an interface. " + beanClass );
 264  
             }else{
 265  0
                bean = new HashMap();
 266  0
             }
 267  
          }else{
 268  
             bean = jsonConfig.getNewBeanInstanceStrategy()
 269  0
                   .newInstance( beanClass, jsonObject );
 270  
          }
 271  0
       }catch( JSONException jsone ){
 272  0
          throw jsone;
 273  0
       }catch( Exception e ){
 274  
          throw new JSONException( e );
 275  0
       }
 276  
 
 277  
       Map props = JSONUtils.getProperties( jsonObject );
 278  0
       PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter();
 279  
       for( Iterator entries = jsonObject.names( jsonConfig )
 280  
             .iterator(); entries.hasNext(); ){
 281  0
          String name = (String) entries.next();
 282  0
          Class type = (Class) props.get( name );
 283  0
          Object value = jsonObject.get( name );
 284  0
          if( javaPropertyFilter != null && javaPropertyFilter.apply( bean, name, value ) ){
 285  0
             continue;
 286  
          }
 287  0
          String key = Map.class.isAssignableFrom( beanClass )
 288  0
                && jsonConfig.isSkipJavaIdentifierTransformationInMapKeys() ? name
 289  0
                : JSONUtils.convertToJavaIdentifier( name, jsonConfig );
 290  0
          PropertyNameProcessor propertyNameProcessor = jsonConfig.findJavaPropertyNameProcessor( beanClass );
 291  0
          if( propertyNameProcessor != null ){
 292  0
             key = propertyNameProcessor.processPropertyName( beanClass, key );
 293  0
          }
 294  0
          try{
 295  0
             if( Map.class.isAssignableFrom( beanClass ) ){
 296  
                // no type info available for conversion
 297  0
                if( JSONUtils.isNull( value ) ){
 298  
                   setProperty( bean, key, value, jsonConfig );
 299  
                }else if( value instanceof JSONArray ){
 300  0
                   setProperty( bean, key, convertPropertyValueToCollection( key, value, jsonConfig, name,
 301  0
                         classMap, List.class ), jsonConfig );
 302  0
                }else if( String.class.isAssignableFrom( type ) || JSONUtils.isBoolean( type )
 303  
                      || JSONUtils.isNumber( type ) || JSONUtils.isString( type )
 304  
                      || JSONFunction.class.isAssignableFrom( type ) ){
 305  0
                   if( jsonConfig.isHandleJettisonEmptyElement() && "".equals( value ) ){
 306  
                      setProperty( bean, key, null, jsonConfig );
 307  0
                   }else{
 308  0
                      setProperty( bean, key, value, jsonConfig );
 309  0
                   }
 310  0
                }else{
 311  
                   Class targetClass = findTargetClass( key, classMap );
 312  0
                   targetClass = targetClass == null ? findTargetClass( name, classMap )
 313  
                         : targetClass;
 314  
                   JsonConfig jsc = jsonConfig.copy();
 315  0
                   jsc.setRootClass( targetClass );
 316  0
                   jsc.setClassMap( classMap );
 317  
                   if( targetClass != null ){
 318  0
                      setProperty( bean, key, toBean( (JSONObject) value, jsc ), jsonConfig );
 319  
                   }else{
 320  
                      setProperty( bean, key, toBean( (JSONObject) value ), jsonConfig );
 321  0
                   }
 322  0
                }
 323  
             }else{
 324  0
                PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor( bean, key );
 325  0
                if( pd != null && pd.getWriteMethod() == null ){
 326  0
                   log.info( "Property '" + key + "' of "+ bean.getClass()+" has no write method. SKIPPED." );
 327  0
                   continue;
 328  0
                }
 329  
 
 330  0
                if( pd != null ){
 331  
                   Class targetType = pd.getPropertyType();
 332  0
                   if( !JSONUtils.isNull( value ) ){
 333  
                      if( value instanceof JSONArray ){
 334  0
                         if( List.class.isAssignableFrom( pd.getPropertyType() ) ){
 335  0
                            setProperty( bean, key, convertPropertyValueToCollection( key, value,
 336  0
                                  jsonConfig, name, classMap, pd.getPropertyType() ), jsonConfig );
 337  0
                         }else if( Set.class.isAssignableFrom( pd.getPropertyType() ) ){
 338  
                            setProperty( bean, key, convertPropertyValueToCollection( key, value,
 339  
                                  jsonConfig, name, classMap, pd.getPropertyType() ), jsonConfig );
 340  0
                         }else{
 341  0
                            setProperty( bean, key, convertPropertyValueToArray( key, value,
 342  0
                                  targetType, jsonConfig, classMap ), jsonConfig );
 343  0
                         }
 344  0
                      }else if( String.class.isAssignableFrom( type ) || JSONUtils.isBoolean( type )
 345  0
                            || JSONUtils.isNumber( type ) || JSONUtils.isString( type )
 346  
                            || JSONFunction.class.isAssignableFrom( type ) ){
 347  0
                         if( pd != null ){
 348  0
                            if( jsonConfig.isHandleJettisonEmptyElement() && "".equals( value ) ){
 349  
                               setProperty( bean, key, null, jsonConfig );
 350  
                            }else if( !targetType.isInstance( value ) ){
 351  0
                               setProperty( bean, key, morphPropertyValue( key, value, type,
 352  
                                     targetType ), jsonConfig );
 353  
                            }else{
 354  0
                               setProperty( bean, key, value, jsonConfig );
 355  
                            }
 356  
                         }else if( beanClass == null || bean instanceof Map ){
 357  0
                            setProperty( bean, key, value, jsonConfig );
 358  0
                         }else{
 359  0
                            log.warn( "Tried to assign property " + key + ":" + type.getName()
 360  0
                                  + " to bean of class " + bean.getClass()
 361  0
                                        .getName() );
 362  
                         }
 363  
                      }else{
 364  0
                         if( jsonConfig.isHandleJettisonSingleElementArray() ){
 365  
                            JSONArray array = new JSONArray().element( value, jsonConfig );
 366  0
                            Class newTargetClass = findTargetClass( key, classMap );
 367  0
                            newTargetClass = newTargetClass == null ? findTargetClass( name,
 368  
                                  classMap ) : newTargetClass;
 369  0
                            JsonConfig jsc = jsonConfig.copy();
 370  
                            jsc.setRootClass( newTargetClass );
 371  
                            jsc.setClassMap( classMap );
 372  
                            if( targetType.isArray() ){
 373  
                               setProperty( bean, key, JSONArray.toArray( array, jsc ), jsonConfig );
 374  0
                            }else if( JSONArray.class.isAssignableFrom( targetType ) ){
 375  0
                               setProperty( bean, key, array, jsonConfig );
 376  0
                            }else if( List.class.isAssignableFrom( targetType )
 377  0
                                  || Set.class.isAssignableFrom( targetType ) ){
 378  
                               jsc.setCollectionType( targetType );
 379  0
                               setProperty( bean, key, JSONArray.toCollection( array, jsc ),
 380  0
                                     jsonConfig );
 381  0
                            }else{
 382  0
                               setProperty( bean, key, toBean( (JSONObject) value, jsc ), jsonConfig );
 383  0
                            }
 384  0
                         }else{
 385  0
                            if( targetType == Object.class || targetType.isInterface() ) {
 386  0
                               Class targetTypeCopy = targetType;
 387  
                               targetType = findTargetClass( key, classMap );
 388  0
                               targetType = targetType == null ? findTargetClass( name, classMap )
 389  0
                                     : targetType;
 390  
                               targetType = targetType == null && targetTypeCopy.isInterface() ? targetTypeCopy
 391  
                                     : targetType;
 392  0
                            }
 393  
                            JsonConfig jsc = jsonConfig.copy();
 394  0
                            jsc.setRootClass( targetType );
 395  0
                            jsc.setClassMap( classMap );
 396  0
                            setProperty( bean, key, toBean( (JSONObject) value, jsc ), jsonConfig );
 397  0
                         }
 398  0
                      }
 399  
                   }else{
 400  0
                      if( type.isPrimitive() ){
 401  
                         // assume assigned default value
 402  
                         log.warn( "Tried to assign null value to " + key + ":" + type.getName() );
 403  0
                         setProperty( bean, key, JSONUtils.getMorpherRegistry()
 404  0
                               .morph( type, null ), jsonConfig );
 405  0
                      }else{
 406  0
                         setProperty( bean, key, null, jsonConfig );
 407  0
                      }
 408  
                   }
 409  
                }else{
 410  0
                   // pd is null
 411  
                   if( !JSONUtils.isNull( value ) ){
 412  0
                      if( value instanceof JSONArray ){
 413  0
                         setProperty( bean, key, convertPropertyValueToCollection( key, value,
 414  
                               jsonConfig, name, classMap, List.class ), jsonConfig );
 415  
                      }else if( String.class.isAssignableFrom( type ) || JSONUtils.isBoolean( type )
 416  0
                            || JSONUtils.isNumber( type ) || JSONUtils.isString( type )
 417  
                            || JSONFunction.class.isAssignableFrom( type ) ){
 418  
                         if( beanClass == null || bean instanceof Map || jsonConfig.getPropertySetStrategy() != null || 
 419  0
                             !jsonConfig.isIgnorePublicFields() ){
 420  0
                            setProperty( bean, key, value, jsonConfig );
 421  0
                         }else{
 422  0
                            log.warn( "Tried to assign property " + key + ":" + type.getName()
 423  
                                  + " to bean of class " + bean.getClass()
 424  0
                                        .getName() );
 425  
                         }
 426  
                      }else{
 427  0
                         if( jsonConfig.isHandleJettisonSingleElementArray() ){
 428  
                            Class newTargetClass = findTargetClass( key, classMap );
 429  0
                            newTargetClass = newTargetClass == null ? findTargetClass( name,
 430  
                                  classMap ) : newTargetClass;
 431  0
                            JsonConfig jsc = jsonConfig.copy();
 432  
                            jsc.setRootClass( newTargetClass );
 433  
                            jsc.setClassMap( classMap );
 434  
                            setProperty( bean, key, toBean( (JSONObject) value, jsc ), jsonConfig );
 435  
                         }else{
 436  0
                            setProperty( bean, key, value, jsonConfig );
 437  0
                         }
 438  0
                      }
 439  
                   }else{
 440  0
                      if( type.isPrimitive() ){
 441  0
                         // assume assigned default value
 442  0
                         log.warn( "Tried to assign null value to " + key + ":" + type.getName() );
 443  0
                         setProperty( bean, key, JSONUtils.getMorpherRegistry()
 444  0
                               .morph( type, null ), jsonConfig );
 445  0
                      }else{
 446  
                         setProperty( bean, key, null, jsonConfig );
 447  
                      }
 448  
                   }
 449  0
                }
 450  
             }
 451  0
          }catch( JSONException jsone ){
 452  0
             throw jsone;
 453  
          }catch( Exception e ){
 454  
             throw new JSONException( "Error while setting property=" + name + " type " + type, e );
 455  0
          }
 456  
       }
 457  
 
 458  
       return bean;
 459  
    }
 460  0
 
 461  0
    /**
 462  0
     * Creates a bean from a JSONObject, with the specific configuration.
 463  0
     */
 464  0
    public static Object toBean( JSONObject jsonObject, Object root, JsonConfig jsonConfig ) {
 465  0
       if( jsonObject == null || jsonObject.isNullObject() || root == null ){
 466  
          return root;
 467  0
       }
 468  
 
 469  
       Class rootClass = root.getClass();
 470  
       if( rootClass.isInterface() ){
 471  
          throw new JSONException( "Root bean is an interface. " + rootClass );
 472  
       }
 473  
 
 474  
       Map classMap = jsonConfig.getClassMap();
 475  
       if( classMap == null ){
 476  
          classMap = Collections.EMPTY_MAP;
 477  
       }
 478  
 
 479  
       Map props = JSONUtils.getProperties( jsonObject );
 480  
       PropertyFilter javaPropertyFilter = jsonConfig.getJavaPropertyFilter();
 481  
       for( Iterator entries = jsonObject.names( jsonConfig )
 482  
             .iterator(); entries.hasNext(); ){
 483  
          String name = (String) entries.next();
 484  
          Class type = (Class) props.get( name );
 485  
          Object value = jsonObject.get( name );
 486  
          if( javaPropertyFilter != null && javaPropertyFilter.apply( root, name, value ) ){
 487  
             continue;
 488  
          }
 489  
          String key = JSONUtils.convertToJavaIdentifier( name, jsonConfig );
 490  
          try{
 491  
             PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor( root, key );
 492  
             if( pd != null && pd.getWriteMethod() == null ){
 493  
                log.info( "Property '" + key + "' of "+ root.getClass()+" has no write method. SKIPPED." );
 494  
                continue;
 495  
             }
 496  
 
 497  
             if( !JSONUtils.isNull( value ) ){
 498  
                if( value instanceof JSONArray ){
 499  
                   if( pd == null || List.class.isAssignableFrom( pd.getPropertyType() ) ){
 500  
                      Class targetClass = findTargetClass( key, classMap );
 501  
                      targetClass = targetClass == null ? findTargetClass( name, classMap )
 502  
                            : targetClass;
 503  
                      Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
 504  
                            .newInstance( targetClass, null );
 505  
                      List list = JSONArray.toList( (JSONArray) value, newRoot, jsonConfig );
 506  
                      setProperty( root, key, list, jsonConfig );
 507  
                   }else{
 508  
                      Class innerType = JSONUtils.getInnerComponentType( pd.getPropertyType() );
 509  
                      Class targetInnerType = findTargetClass( key, classMap );
 510  
                      if( innerType.equals( Object.class ) && targetInnerType != null
 511  
                            && !targetInnerType.equals( Object.class ) ){
 512  
                         innerType = targetInnerType;
 513  
                      }
 514  
                      Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
 515  
                            .newInstance( innerType, null );
 516  
                      Object array = JSONArray.toArray( (JSONArray) value, newRoot, jsonConfig );
 517  
                      if( innerType.isPrimitive() || JSONUtils.isNumber( innerType )
 518  
                            || Boolean.class.isAssignableFrom( innerType )
 519  
                            || JSONUtils.isString( innerType ) ){
 520  
                         array = JSONUtils.getMorpherRegistry()
 521  
                               .morph( Array.newInstance( innerType, 0 )
 522  
                                     .getClass(), array );
 523  
                      }else if( !array.getClass()
 524  
                            .equals( pd.getPropertyType() ) ){
 525  
                         if( !pd.getPropertyType()
 526  
                               .equals( Object.class ) ){
 527  
                            Morpher morpher = JSONUtils.getMorpherRegistry()
 528  
                                  .getMorpherFor( Array.newInstance( innerType, 0 )
 529  
                                        .getClass() );
 530  
                            if( IdentityObjectMorpher.getInstance()
 531  
                                  .equals( morpher ) ){
 532  
                               ObjectArrayMorpher beanMorpher = new ObjectArrayMorpher(
 533  
                                     new BeanMorpher( innerType, JSONUtils.getMorpherRegistry() ) );
 534  
                               JSONUtils.getMorpherRegistry()
 535  
                                     .registerMorpher( beanMorpher );
 536  
                            }
 537  
                            array = JSONUtils.getMorpherRegistry()
 538  
                                  .morph( Array.newInstance( innerType, 0 )
 539  
                                        .getClass(), array );
 540  
                         }
 541  
                      }
 542  
                      setProperty( root, key, array, jsonConfig );
 543  
                   }
 544  
                }else if( String.class.isAssignableFrom( type ) || JSONUtils.isBoolean( type )
 545  
                      || JSONUtils.isNumber( type ) || JSONUtils.isString( type )
 546  
                      || JSONFunction.class.isAssignableFrom( type ) ){
 547  
                   if( pd != null ){
 548  
                      if( jsonConfig.isHandleJettisonEmptyElement() && "".equals( value ) ){
 549  
                         setProperty( root, key, null, jsonConfig );
 550  
                      }else if( !pd.getPropertyType()
 551  
                            .isInstance( value ) ){
 552  
                         Morpher morpher = JSONUtils.getMorpherRegistry()
 553  
                               .getMorpherFor( pd.getPropertyType() );
 554  
                         if( IdentityObjectMorpher.getInstance()
 555  
                               .equals( morpher ) ){
 556  
                            log.warn( "Can't transform property '" + key + "' from "
 557  
                                  + type.getName() + " into " + pd.getPropertyType()
 558  
                                        .getName() + ". Will register a default BeanMorpher" );
 559  
                            JSONUtils.getMorpherRegistry()
 560  
                                  .registerMorpher(
 561  
                                        new BeanMorpher( pd.getPropertyType(),
 562  
                                              JSONUtils.getMorpherRegistry() ) );
 563  
                         }
 564  
                         setProperty( root, key, JSONUtils.getMorpherRegistry()
 565  
                               .morph( pd.getPropertyType(), value ), jsonConfig );
 566  
                      }else{
 567  
                         setProperty( root, key, value, jsonConfig );
 568  
                      }
 569  
                   }else if( root instanceof Map ){
 570  
                      setProperty( root, key, value, jsonConfig );
 571  
                   }else{
 572  
                      log.warn( "Tried to assign property " + key + ":" + type.getName()
 573  
                            + " to bean of class " + root.getClass()
 574  
                                  .getName() );
 575  
                   }
 576  
                }else{
 577  
                   if( pd != null ){
 578  
                      Class targetClass = pd.getPropertyType();
 579  
                      if( jsonConfig.isHandleJettisonSingleElementArray() ){
 580  
                         JSONArray array = new JSONArray().element( value, jsonConfig );
 581  
                         Class newTargetClass = findTargetClass( key, classMap );
 582  
                         newTargetClass = newTargetClass == null ? findTargetClass( name, classMap )
 583  
                               : newTargetClass;
 584  
                         Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
 585  
                               .newInstance( newTargetClass, null );
 586  
                         if( targetClass.isArray() ){
 587  
                            setProperty( root, key, JSONArray.toArray( array, newRoot, jsonConfig ),
 588  
                                  jsonConfig );
 589  
                         }else if( Collection.class.isAssignableFrom( targetClass ) ){
 590  
                            setProperty( root, key, JSONArray.toList( array, newRoot, jsonConfig ),
 591  
                                  jsonConfig );
 592  
                         }else if( JSONArray.class.isAssignableFrom( targetClass ) ){
 593  
                            setProperty( root, key, array, jsonConfig );
 594  
                         }else{
 595  
                            setProperty( root, key,
 596  
                                  toBean( (JSONObject) value, newRoot, jsonConfig ), jsonConfig );
 597  
                         }
 598  
                      }else{
 599  
                         if( targetClass == Object.class ){
 600  
                            targetClass = findTargetClass( key, classMap );
 601  
                            targetClass = targetClass == null ? findTargetClass( name, classMap )
 602  
                                  : targetClass;
 603  
                         }
 604  
                         Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
 605  
                               .newInstance( targetClass, null );
 606  
                         setProperty( root, key, toBean( (JSONObject) value, newRoot, jsonConfig ),
 607  
                               jsonConfig );
 608  
                      }
 609  
                   }else if( root instanceof Map ){
 610  
                      Class targetClass = findTargetClass( key, classMap );
 611  
                      targetClass = targetClass == null ? findTargetClass( name, classMap )
 612  
                            : targetClass;
 613  
                      Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
 614  
                            .newInstance( targetClass, null );
 615  
                      setProperty( root, key, toBean( (JSONObject) value, newRoot, jsonConfig ),
 616  
                            jsonConfig );
 617  
                   }else{
 618  
                      log.warn( "Tried to assign property " + key + ":" + type.getName()
 619  
                            + " to bean of class " + rootClass.getName() );
 620  
                   }
 621  
                }
 622  
             }else{
 623  
                if( type.isPrimitive() ){
 624  
                   // assume assigned default value
 625  
                   log.warn( "Tried to assign null value to " + key + ":" + type.getName() );
 626  
                   setProperty( root, key, JSONUtils.getMorpherRegistry()
 627  
                         .morph( type, null ), jsonConfig );
 628  
                }else{
 629  
                   setProperty( root, key, null, jsonConfig );
 630  
                }
 631  
             }
 632  
          }catch( JSONException jsone ){
 633  
             throw jsone;
 634  
          }catch( Exception e ){
 635  
             throw new JSONException( "Error while setting property=" + name + " type " + type, e );
 636  
          }
 637  
       }
 638  
 
 639  
       return root;
 640  
    }
 641  
 
 642  
    /**
 643  
     * Creates a JSONObject from a POJO.<br>
 644  
     * Supports nested maps, POJOs, and arrays/collections.
 645  0
     *
 646  0
     * @param bean An object with POJO conventions
 647  
     * @throws JSONException if the bean can not be converted to a proper
 648  
     *         JSONObject.
 649  0
     */
 650  0
    private static JSONObject _fromBean( Object bean, JsonConfig jsonConfig ) {
 651  0
       if( !addInstance( bean ) ){
 652  
          try{
 653  
             return jsonConfig.getCycleDetectionStrategy()
 654  0
                   .handleRepeatedReferenceAsObject( bean );
 655  0
          }catch( JSONException jsone ){
 656  0
             removeInstance( bean );
 657  
             fireErrorEvent( jsone, jsonConfig );
 658  
             throw jsone;
 659  0
          }catch( RuntimeException e ){
 660  0
             removeInstance( bean );
 661  0
             JSONException jsone = new JSONException( e );
 662  0
             fireErrorEvent( jsone, jsonConfig );
 663  0
             throw jsone;
 664  0
          }
 665  0
       }
 666  0
       fireObjectStartEvent( jsonConfig );
 667  0
 
 668  
       JsonBeanProcessor processor = jsonConfig.findJsonBeanProcessor( bean.getClass() );
 669  0
       if( processor != null ){
 670  
          JSONObject json = null;
 671  0
          try{
 672  0
             json = processor.processBean( bean, jsonConfig );
 673  0
             if( json == null ){
 674  0
                json = (JSONObject) jsonConfig.findDefaultValueProcessor( bean.getClass() )
 675  
                      .getDefaultValue( bean.getClass() );
 676  
                if( json == null ){
 677  0
                   json = new JSONObject( true );
 678  0
                }
 679  0
             }
 680  0
             removeInstance( bean );
 681  0
             fireObjectEndEvent( jsonConfig );
 682  
          }catch( JSONException jsone ){
 683  0
             removeInstance( bean );
 684  
             fireErrorEvent( jsone, jsonConfig );
 685  0
             throw jsone;
 686  0
          }catch( RuntimeException e ){
 687  0
             removeInstance( bean );
 688  0
             JSONException jsone = new JSONException( e );
 689  0
             fireErrorEvent( jsone, jsonConfig );
 690  0
             throw jsone;
 691  
          }
 692  0
          return json;
 693  
       }
 694  0
 
 695  
       Class beanClass = bean.getClass();
 696  0
       PropertyNameProcessor propertyNameProcessor = jsonConfig.findJsonPropertyNameProcessor( beanClass );      
 697  0
       Collection exclusions = jsonConfig.getMergedExcludes( beanClass );
 698  
       JSONObject jsonObject = new JSONObject();
 699  
       try{
 700  0
          PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors( bean );
 701  
          PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
 702  
          for( int i = 0; i < pds.length; i++ ){
 703  0
             boolean bypass = false;
 704  
             String key = pds[i].getName();
 705  0
             if( exclusions.contains( key ) ){
 706  
                continue;
 707  0
             }
 708  
 
 709  
             if( jsonConfig.isIgnoreTransientFields() && isTransientField( key, beanClass ) ){
 710  0
                continue;
 711  
             }
 712  0
 
 713  
             Class type = pds[i].getPropertyType();
 714  0
             try { pds[i].getReadMethod(); }
 715  
             catch( Exception e ) {
 716  
                // bug 2565295
 717  0
                String warning = "Property '" + key + "' of "+ beanClass+" has no read method. SKIPPED";
 718  
                fireWarnEvent( warning, jsonConfig );
 719  
                log.info( warning );
 720  
                continue;
 721  
             }
 722  0
             if( pds[i].getReadMethod() != null ){
 723  0
                Object value = PropertyUtils.getProperty( bean, key );
 724  0
                if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
 725  
                   continue;
 726  
                }
 727  0
                JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(
 728  0
                      beanClass, type, key );
 729  0
                if( jsonValueProcessor != null ){
 730  0
                   value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
 731  
                   bypass = true;
 732  0
                   if( !JsonVerifier.isValidJsonValue( value ) ){
 733  
                      throw new JSONException( "Value is not a valid JSON value. " + value );
 734  0
                   }
 735  
                }
 736  0
                if( propertyNameProcessor != null ){
 737  
                   key = propertyNameProcessor.processPropertyName( beanClass, key );
 738  
                }
 739  0
                setValue( jsonObject, key, value, type, jsonConfig, bypass );
 740  
             }else{
 741  
                String warning = "Property '" + key + "' of "+ beanClass+" has no read method. SKIPPED";
 742  
                fireWarnEvent( warning, jsonConfig );
 743  
                log.info( warning );
 744  0
             }
 745  
          }
 746  0
          // inspect public fields, this operation may fail under
 747  0
          // a SecurityManager so we will eat all exceptions
 748  
          try {
 749  0
             if( !jsonConfig.isIgnorePublicFields() ) {
 750  0
                Field[] fields = beanClass.getFields();
 751  
                for( int i = 0; i < fields.length; i++ ) {
 752  0
                   boolean bypass = false;
 753  
                   Field field = fields[i];
 754  
                   String key = field.getName();
 755  
                   if( exclusions.contains( key ) ) {
 756  
                      continue;
 757  0
                   }
 758  0
 
 759  0
                   if( jsonConfig.isIgnoreTransientFields() && isTransientField( field ) ) {
 760  0
                      continue;
 761  0
                   }
 762  0
 
 763  
                   Class type = field.getType();
 764  0
                   Object value = field.get( bean );
 765  
                   if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ) {
 766  0
                      continue;
 767  0
                   }
 768  
                   JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor( beanClass, type, key );
 769  0
                   if( jsonValueProcessor != null ) {
 770  0
                      value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
 771  
                      bypass = true;
 772  0
                      if( !JsonVerifier.isValidJsonValue( value ) ) {
 773  0
                         throw new JSONException( "Value is not a valid JSON value. " + value );
 774  
                      }
 775  0
                   }
 776  
                   if( propertyNameProcessor != null ) {
 777  
                      key = propertyNameProcessor.processPropertyName( beanClass, key );
 778  0
                   }
 779  0
                   setValue( jsonObject, key, value, type, jsonConfig, bypass );
 780  0
                }
 781  0
             }
 782  
          }
 783  
          catch( Exception e ){
 784  0
             log.trace( "Couldn't read public fields.", e );
 785  
          }
 786  0
       }catch( JSONException jsone ){
 787  
          removeInstance( bean );
 788  
          fireErrorEvent( jsone, jsonConfig );
 789  0
          throw jsone;
 790  0
       }catch( Exception e ){
 791  0
          removeInstance( bean );
 792  
          JSONException jsone = new JSONException( e );
 793  0
          fireErrorEvent( jsone, jsonConfig );
 794  
          throw jsone;
 795  0
       }
 796  
 
 797  0
       removeInstance( bean );
 798  0
       fireObjectEndEvent( jsonConfig );
 799  
       return jsonObject;
 800  
    }
 801  
 
 802  
    private static JSONObject _fromDynaBean( DynaBean bean, JsonConfig jsonConfig ) {
 803  0
       if( bean == null ){
 804  
          fireObjectStartEvent( jsonConfig );
 805  0
          fireObjectEndEvent( jsonConfig );
 806  0
          return new JSONObject( true );
 807  
       }
 808  
 
 809  0
       if( !addInstance( bean ) ){
 810  
          try{
 811  
             return jsonConfig.getCycleDetectionStrategy()
 812  0
                   .handleRepeatedReferenceAsObject( bean );
 813  0
          }catch( JSONException jsone ){
 814  0
             removeInstance( bean );
 815  0
             fireErrorEvent( jsone, jsonConfig );
 816  0
             throw jsone;
 817  0
          }catch( RuntimeException e ){
 818  
             removeInstance( bean );
 819  0
             JSONException jsone = new JSONException( e );
 820  
             fireErrorEvent( jsone, jsonConfig );
 821  
             throw jsone;
 822  
          }
 823  
       }
 824  
       fireObjectStartEvent( jsonConfig );
 825  
 
 826  
       JSONObject jsonObject = new JSONObject();
 827  
       try{
 828  
          DynaProperty[] props = bean.getDynaClass()
 829  
                .getDynaProperties();
 830  
          Collection exclusions = jsonConfig.getMergedExcludes();
 831  0
          PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
 832  
          for( int i = 0; i < props.length; i++ ){
 833  0
             boolean bypass = false;
 834  
             DynaProperty dynaProperty = props[i];
 835  0
             String key = dynaProperty.getName();
 836  0
             if( exclusions.contains( key ) ){
 837  0
                continue;
 838  0
             }
 839  0
             Class type = dynaProperty.getType();
 840  0
             Object value = bean.get( dynaProperty.getName() );
 841  0
             if( jsonPropertyFilter != null && jsonPropertyFilter.apply( bean, key, value ) ){
 842  0
                continue;
 843  0
             }
 844  
             JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor( type, key );
 845  
             if( jsonValueProcessor != null ){
 846  0
                value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
 847  
                bypass = true;
 848  0
                if( !JsonVerifier.isValidJsonValue( value ) ){
 849  0
                   throw new JSONException( "Value is not a valid JSON value. " + value );
 850  0
                }
 851  
             }
 852  0
             setValue( jsonObject, key, value, type, jsonConfig, bypass );
 853  0
          }
 854  0
       }catch( JSONException jsone ){
 855  
          removeInstance( bean );
 856  0
          fireErrorEvent( jsone, jsonConfig );
 857  0
          throw jsone;
 858  
       }catch( RuntimeException e ){
 859  
          removeInstance( bean );
 860  0
          JSONException jsone = new JSONException( e );
 861  0
          fireErrorEvent( jsone, jsonConfig );
 862  0
          throw jsone;
 863  0
       }
 864  0
 
 865  0
       removeInstance( bean );
 866  0
       fireObjectEndEvent( jsonConfig );
 867  0
       return jsonObject;
 868  0
    }
 869  0
 
 870  0
    private static JSONObject _fromJSONObject( JSONObject object, JsonConfig jsonConfig ) {
 871  0
       if( object == null || object.isNullObject() ){
 872  0
          fireObjectStartEvent( jsonConfig );
 873  
          fireObjectEndEvent( jsonConfig );
 874  
          return new JSONObject( true );
 875  0
       }
 876  0
 
 877  0
       if( !addInstance( object ) ){
 878  0
          try{
 879  
             return jsonConfig.getCycleDetectionStrategy()
 880  0
                   .handleRepeatedReferenceAsObject( object );
 881  0
          }catch( JSONException jsone ){
 882  0
             removeInstance( object );
 883  0
             fireErrorEvent( jsone, jsonConfig );
 884  0
             throw jsone;
 885  0
          }catch( RuntimeException e ){
 886  0
             removeInstance( object );
 887  
             JSONException jsone = new JSONException( e );
 888  
             fireErrorEvent( jsone, jsonConfig );
 889  0
             throw jsone;
 890  0
          }
 891  
       }
 892  
       fireObjectStartEvent( jsonConfig );
 893  0
 
 894  0
       JSONArray sa = object.names(jsonConfig);
 895  0
       Collection exclusions = jsonConfig.getMergedExcludes();
 896  
       JSONObject jsonObject = new JSONObject();
 897  0
       PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
 898  0
       for( Iterator i = sa.iterator(); i.hasNext(); ){
 899  0
          Object k =  i.next();
 900  0
          if( k == null ){
 901  0
             throw new JSONException("JSON keys cannot be null.");
 902  0
          }
 903  
          if( !(k instanceof String) && !jsonConfig.isAllowNonStringKeys()) {
 904  
             throw new ClassCastException("JSON keys must be strings.");
 905  
          }
 906  
          String key = String.valueOf( k );
 907  
          if( "null".equals( key )){
 908  
             throw new NullPointerException("JSON keys must not be null nor the 'null' string.");
 909  
          }
 910  
          if( exclusions.contains( key ) ){
 911  
             continue;
 912  
          }
 913  
          Object value = object.opt( key );
 914  
          if( jsonPropertyFilter != null && jsonPropertyFilter.apply( object, key, value ) ){
 915  
             continue;
 916  0
          }
 917  
          if( jsonObject.properties.containsKey( key ) ){
 918  0
             jsonObject.accumulate( key, value, jsonConfig );
 919  0
             firePropertySetEvent( key, value, true, jsonConfig );
 920  0
          }else{
 921  
             jsonObject.setInternal( key, value, jsonConfig );
 922  0
             firePropertySetEvent( key, value, false, jsonConfig );
 923  
          }
 924  0
       }
 925  0
 
 926  0
       removeInstance( object );
 927  0
       fireObjectEndEvent( jsonConfig );
 928  0
       return jsonObject;
 929  
    }
 930  
 
 931  0
    private static JSONObject _fromJSONString( JSONString string, JsonConfig jsonConfig ) {
 932  0
       return _fromJSONTokener( new JSONTokener( string.toJSONString() ), jsonConfig );
 933  
    }
 934  0
 
 935  0
    private static JSONObject _fromJSONTokener( JSONTokener tokener, JsonConfig jsonConfig ) {
 936  0
 
 937  0
       try{
 938  0
          char c;
 939  
          String key;
 940  
          Object value;
 941  
 
 942  
          if( tokener.matches( "null.*" ) ){
 943  
             fireObjectStartEvent( jsonConfig );
 944  0
             fireObjectEndEvent( jsonConfig );
 945  0
             return new JSONObject( true );
 946  0
          }
 947  0
 
 948  0
          if( tokener.nextClean() != '{' ){
 949  0
             throw tokener.syntaxError( "A JSONObject text must begin with '{'" );
 950  0
          }
 951  0
          fireObjectStartEvent( jsonConfig );
 952  
 
 953  
          Collection exclusions = jsonConfig.getMergedExcludes();
 954  0
          PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
 955  0
          JSONObject jsonObject = new JSONObject();
 956  
          for( ;; ){
 957  
             c = tokener.nextClean();
 958  0
             switch( c ){
 959  0
                case 0:
 960  0
                   throw tokener.syntaxError( "A JSONObject text must end with '}'" );
 961  0
                case '}':
 962  
                   fireObjectEndEvent( jsonConfig );
 963  0
                   return jsonObject;
 964  0
                default:
 965  0
                   tokener.back();
 966  0
                   key = tokener.nextValue( jsonConfig )
 967  0
                         .toString();
 968  0
             }
 969  
 
 970  
             /*
 971  0
              * The key is followed by ':'. We will also tolerate '=' or '=>'.
 972  0
              */
 973  
 
 974  0
             c = tokener.nextClean();
 975  
             if( c == '=' ){
 976  
                if( tokener.next() != '>' ){
 977  
                   tokener.back();
 978  0
                }
 979  0
             }else if( c != ':' ){
 980  0
                throw tokener.syntaxError( "Expected a ':' after a key" );
 981  0
             }
 982  0
 
 983  0
             char peek = tokener.peek();
 984  0
             boolean quoted = peek == '"' || peek == '\'';
 985  0
             Object v = tokener.nextValue( jsonConfig );
 986  0
             if( quoted || !JSONUtils.isFunctionHeader( v ) ){
 987  0
                if( exclusions.contains( key ) ){
 988  0
                   switch( tokener.nextClean() ){
 989  0
                      case ';':
 990  0
                      case ',':
 991  
                         if( tokener.nextClean() == '}' ){
 992  0
                            fireObjectEndEvent( jsonConfig );
 993  0
                            return jsonObject;
 994  0
                         }
 995  
                         tokener.back();
 996  
                         break;
 997  
                      case '}':
 998  0
                         fireObjectEndEvent( jsonConfig );
 999  0
                         return jsonObject;
 1000  0
                      default:
 1001  0
                         throw tokener.syntaxError( "Expected a ',' or '}'" );
 1002  
                   }
 1003  
                   continue;
 1004  0
                }
 1005  
                if( jsonPropertyFilter == null || !jsonPropertyFilter.apply( tokener, key, v ) ){
 1006  0
                   if( quoted && v instanceof String && (JSONUtils.mayBeJSON( (String) v ) || JSONUtils.isFunction( v ))){
 1007  
                      v = JSONUtils.DOUBLE_QUOTE + v + JSONUtils.DOUBLE_QUOTE;
 1008  0
                   }
 1009  0
                   if( jsonObject.properties.containsKey( key ) ){
 1010  0
                      jsonObject.accumulate( key, v, jsonConfig );
 1011  0
                      firePropertySetEvent( key, v, true, jsonConfig );
 1012  0
                   }else{
 1013  0
                      jsonObject.element( key, v, jsonConfig );
 1014  0
                      firePropertySetEvent( key, v, false, jsonConfig );
 1015  0
                   }
 1016  0
                }
 1017  
             }else{
 1018  
                // read params if any
 1019  0
                String params = JSONUtils.getFunctionParams( (String) v );
 1020  
                // read function text
 1021  0
                int i = 0;
 1022  
                StringBuffer sb = new StringBuffer();
 1023  0
                for( ;; ){
 1024  
                   char ch = tokener.next();
 1025  0
                   if( ch == 0 ){
 1026  0
                      break;
 1027  0
                   }
 1028  0
                   if( ch == '{' ){
 1029  0
                      i++;
 1030  0
                   }
 1031  0
                   if( ch == '}' ){
 1032  0
                      i--;
 1033  
                   }
 1034  0
                   sb.append( ch );
 1035  0
                   if( i == 0 ){
 1036  0
                      break;
 1037  0
                   }
 1038  
                }
 1039  0
                if( i != 0 ){
 1040  0
                   throw tokener.syntaxError( "Unbalanced '{' or '}' on prop: " + v );
 1041  0
                }
 1042  0
                // trim '{' at start and '}' at end
 1043  0
                String text = sb.toString();
 1044  0
                text = text.substring( 1, text.length() - 1 )
 1045  
                      .trim();
 1046  
                value = new JSONFunction(
 1047  0
                      (params != null) ? StringUtils.split( params, "," ) : null, text );
 1048  
                if( jsonPropertyFilter == null || !jsonPropertyFilter.apply( tokener, key, value ) ){
 1049  0
                   if( jsonObject.properties.containsKey( key ) ){
 1050  0
                      jsonObject.accumulate( key, value, jsonConfig );
 1051  0
                      firePropertySetEvent( key, value, true, jsonConfig );
 1052  0
                   }else{
 1053  0
                      jsonObject.element( key, value, jsonConfig );
 1054  0
                      firePropertySetEvent( key, value, false, jsonConfig );
 1055  0
                   }
 1056  0
                }
 1057  0
             }
 1058  0
 
 1059  
             /*
 1060  0
              * Pairs are separated by ','. We will also tolerate ';'.
 1061  0
              */
 1062  0
 
 1063  
             switch( tokener.nextClean() ){
 1064  
                case ';':
 1065  
                case ',':
 1066  0
                   if( tokener.nextClean() == '}' ){
 1067  0
                      fireObjectEndEvent( jsonConfig );
 1068  0
                      return jsonObject;
 1069  0
                   }
 1070  
                   tokener.back();
 1071  
                   break;
 1072  0
                case '}':
 1073  
                   fireObjectEndEvent( jsonConfig );
 1074  0
                   return jsonObject;
 1075  
                default:
 1076  0
                   throw tokener.syntaxError( "Expected a ',' or '}'" );
 1077  0
             }
 1078  0
          }
 1079  0
       }catch( JSONException jsone ){
 1080  0
          fireErrorEvent( jsone, jsonConfig );
 1081  0
          throw jsone;
 1082  0
       }
 1083  0
    }
 1084  0
 
 1085  
    private static JSONObject _fromMap( Map map, JsonConfig jsonConfig ) {
 1086  
       if( map == null ){
 1087  0
          fireObjectStartEvent( jsonConfig );
 1088  
          fireObjectEndEvent( jsonConfig );
 1089  0
          return new JSONObject( true );
 1090  0
       }
 1091  0
 
 1092  0
       if( !addInstance( map ) ){
 1093  0
          try{
 1094  0
             return jsonConfig.getCycleDetectionStrategy()
 1095  0
                   .handleRepeatedReferenceAsObject( map );
 1096  0
          }catch( JSONException jsone ){
 1097  
             removeInstance( map );
 1098  0
             fireErrorEvent( jsone, jsonConfig );
 1099  0
             throw jsone;
 1100  
          }catch( RuntimeException e ){
 1101  0
             removeInstance( map );
 1102  0
             JSONException jsone = new JSONException( e );
 1103  0
             fireErrorEvent( jsone, jsonConfig );
 1104  
             throw jsone;
 1105  0
          }
 1106  0
       }
 1107  
       fireObjectStartEvent( jsonConfig );
 1108  0
 
 1109  0
       Collection exclusions = jsonConfig.getMergedExcludes();
 1110  0
       JSONObject jsonObject = new JSONObject();
 1111  
       PropertyFilter jsonPropertyFilter = jsonConfig.getJsonPropertyFilter();
 1112  0
       try{
 1113  0
          for( Iterator entries = map.entrySet()
 1114  0
                .iterator(); entries.hasNext(); ){
 1115  
             boolean bypass = false;
 1116  0
             Map.Entry entry = (Map.Entry) entries.next();
 1117  0
             Object k = entry.getKey();
 1118  
             if( k == null ){
 1119  0
                throw new JSONException("JSON keys cannot be null.");
 1120  
             }
 1121  0
             if( !(k instanceof String) && !jsonConfig.isAllowNonStringKeys() ) {
 1122  0
                throw new ClassCastException("JSON keys must be strings.");
 1123  0
             }
 1124  
             String key = String.valueOf( k );
 1125  
             if( "null".equals( key )){
 1126  
                throw new NullPointerException("JSON keys must not be null nor the 'null' string.");
 1127  0
             }
 1128  
             if( exclusions.contains( key ) ){
 1129  
                continue;
 1130  
             }
 1131  
             Object value = entry.getValue();
 1132  
             if( jsonPropertyFilter != null && jsonPropertyFilter.apply( map, key, value ) ){
 1133  
                continue;
 1134  
             }
 1135  
             if( value != null ){
 1136  
                JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor(
 1137  0
                      value.getClass(), key );
 1138  0
                if( jsonValueProcessor != null ){
 1139  0
                   value = jsonValueProcessor.processObjectValue( key, value, jsonConfig );
 1140  0
                   bypass = true;
 1141  
                   if( !JsonVerifier.isValidJsonValue( value ) ){
 1142  
                      throw new JSONException( "Value is not a valid JSON value. " + value );
 1143  0
                   }
 1144  0
                }
 1145  
                setValue( jsonObject, key, value, value.getClass(), jsonConfig, bypass );
 1146  0
             }else{
 1147  
                if( jsonObject.properties.containsKey( key ) ){
 1148  0
                   jsonObject.accumulate( key, JSONNull.getInstance() );
 1149  0
                   firePropertySetEvent( key, JSONNull.getInstance(), true, jsonConfig );
 1150  0
                }else{
 1151  
                   jsonObject.element( key, JSONNull.getInstance() );
 1152  0
                   firePropertySetEvent( key, JSONNull.getInstance(), false, jsonConfig );
 1153  0
                }
 1154  
             }
 1155  0
          }
 1156  
       }catch( JSONException jsone ){
 1157  0
          removeInstance( map );
 1158  0
          fireErrorEvent( jsone, jsonConfig );
 1159  
          throw jsone;
 1160  0
       }catch( RuntimeException e ){
 1161  0
          removeInstance( map );
 1162  
          JSONException jsone = new JSONException( e );
 1163  
          fireErrorEvent( jsone, jsonConfig );
 1164  
          throw jsone;
 1165  
       }
 1166  
 
 1167  
       removeInstance( map );
 1168  
       fireObjectEndEvent( jsonConfig );
 1169  0
       return jsonObject;
 1170  0
    }
 1171  0
 
 1172  0
    private static JSONObject _fromString( String str, JsonConfig jsonConfig ) {
 1173  
       if( str == null || "null".equals( str ) ){
 1174  0
          fireObjectStartEvent( jsonConfig );
 1175  0
          fireObjectEndEvent( jsonConfig );
 1176  
          return new JSONObject( true );
 1177  
       }
 1178  0
       return _fromJSONTokener( new JSONTokener( str ), jsonConfig );
 1179  0
    }
 1180  0
 
 1181  0
    private static Object convertPropertyValueToArray( String key, Object value, Class targetType,
 1182  0
          JsonConfig jsonConfig, Map classMap ) {
 1183  0
       Class innerType = JSONUtils.getInnerComponentType( targetType );
 1184  
       Class targetInnerType = findTargetClass( key, classMap );
 1185  
       if( innerType.equals( Object.class ) && targetInnerType != null
 1186  0
             && !targetInnerType.equals( Object.class ) ){
 1187  0
          innerType = targetInnerType;
 1188  0
       }
 1189  
       JsonConfig jsc = jsonConfig.copy();
 1190  0
       jsc.setRootClass( innerType );
 1191  0
       jsc.setClassMap( classMap );
 1192  
       Object array = JSONArray.toArray( (JSONArray) value, jsc );
 1193  0
       if( innerType.isPrimitive() || JSONUtils.isNumber( innerType )
 1194  0
             || Boolean.class.isAssignableFrom( innerType ) || JSONUtils.isString( innerType ) ){
 1195  
          array = JSONUtils.getMorpherRegistry()
 1196  0
                .morph( Array.newInstance( innerType, 0 )
 1197  
                      .getClass(), array );
 1198  
       }else if( !array.getClass()
 1199  
             .equals( targetType ) ){
 1200  0
          if( !targetType.equals( Object.class ) ){
 1201  0
             Morpher morpher = JSONUtils.getMorpherRegistry()
 1202  0
                   .getMorpherFor( Array.newInstance( innerType, 0 )
 1203  
                         .getClass() );
 1204  0
             if( IdentityObjectMorpher.getInstance()
 1205  0
                   .equals( morpher ) ){
 1206  0
                ObjectArrayMorpher beanMorpher = new ObjectArrayMorpher( new BeanMorpher( innerType,
 1207  
                      JSONUtils.getMorpherRegistry() ) );
 1208  0
                JSONUtils.getMorpherRegistry()
 1209  0
                      .registerMorpher( beanMorpher );
 1210  
             }
 1211  
             array = JSONUtils.getMorpherRegistry()
 1212  
                   .morph( Array.newInstance( innerType, 0 )
 1213  
                         .getClass(), array );
 1214  0
          }
 1215  
       }
 1216  0
       return array;
 1217  0
    }
 1218  
 
 1219  0
    private static List convertPropertyValueToList( String key, Object value, JsonConfig jsonConfig,
 1220  0
          String name, Map classMap ) {
 1221  0
       Class targetClass = findTargetClass( key, classMap );
 1222  
       targetClass = targetClass == null ? findTargetClass( name, classMap ) : targetClass;
 1223  0
       JsonConfig jsc = jsonConfig.copy();
 1224  0
       jsc.setRootClass( targetClass );
 1225  
       jsc.setClassMap( classMap );
 1226  0
       List list = (List) JSONArray.toCollection( (JSONArray) value, jsc );
 1227  0
       return list;
 1228  
    }
 1229  0
 
 1230  0
    private static Collection convertPropertyValueToCollection( String key, Object value, JsonConfig jsonConfig,
 1231  0
          String name, Map classMap, Class collectionType ) {
 1232  
       Class targetClass = findTargetClass( key, classMap );
 1233  0
       targetClass = targetClass == null ? findTargetClass( name, classMap ) : targetClass;
 1234  0
       JsonConfig jsc = jsonConfig.copy();
 1235  0
       jsc.setRootClass( targetClass );
 1236  
       jsc.setClassMap( classMap );
 1237  
       jsc.setCollectionType( collectionType );
 1238  0
       return JSONArray.toCollection( (JSONArray) value, jsc );
 1239  0
    }
 1240  
 
 1241  0
    /**
 1242  
     * Locates a Class associated to a specifi key.<br>
 1243  0
     * The key may be a regexp.
 1244  0
     */
 1245  0
    private static Class findTargetClass( String key, Map classMap ) {
 1246  0
       // try get first
 1247  
       Class targetClass = (Class) classMap.get( key );
 1248  0
       if( targetClass == null ){
 1249  0
          // try with regexp
 1250  
          // this will hit performance as it must iterate over all the keys
 1251  
          // and create a RegexpMatcher for each key
 1252  
          for( Iterator i = classMap.entrySet()
 1253  
                .iterator(); i.hasNext(); ){
 1254  
             Map.Entry entry = (Map.Entry) i.next();
 1255  
             if( RegexpUtils.getMatcher( (String) entry.getKey() )
 1256  
                   .matches( key ) ){
 1257  
                targetClass = (Class) entry.getValue();
 1258  0
                break;
 1259  
             }
 1260  
          }
 1261  0
       }
 1262  0
 
 1263  0
       return targetClass;
 1264  
    }
 1265  0
 
 1266  0
    private static boolean isTransientField( String name, Class beanClass ) {
 1267  
       try{
 1268  0
          return isTransientField(beanClass.getDeclaredField( name ));
 1269  0
       }catch( Exception e ){
 1270  
          // swallow exception
 1271  0
       }
 1272  
       return false;
 1273  0
    }
 1274  0
    
 1275  0
    private static boolean isTransientField( Field field ) {
 1276  0
       return (field.getModifiers() & Modifier.TRANSIENT) == Modifier.TRANSIENT;
 1277  
    }
 1278  
 
 1279  
    private static Object morphPropertyValue( String key, Object value, Class type, Class targetType ) {
 1280  
       Morpher morpher = JSONUtils.getMorpherRegistry()
 1281  0
             .getMorpherFor( targetType );
 1282  0
       if( IdentityObjectMorpher.getInstance()
 1283  0
             .equals( morpher ) ){
 1284  0
          log.warn( "Can't transform property '" + key + "' from " + type.getName() + " into "
 1285  
                + targetType.getName() + ". Will register a default BeanMorpher" );
 1286  
          JSONUtils.getMorpherRegistry()
 1287  0
                .registerMorpher( new BeanMorpher( targetType, JSONUtils.getMorpherRegistry() ) );
 1288  
       }
 1289  0
       value = JSONUtils.getMorpherRegistry()
 1290  
             .morph( targetType, value );
 1291  0
       return value;
 1292  0
    }
 1293  0
 
 1294  0
    /**
 1295  0
     * Sets a property on the target bean.<br>
 1296  0
     * Bean may be a Map or a POJO.
 1297  0
     */
 1298  0
    private static void setProperty( Object bean, String key, Object value, JsonConfig jsonConfig )
 1299  0
          throws Exception {
 1300  
       PropertySetStrategy propertySetStrategy = jsonConfig.getPropertySetStrategy() != null ? jsonConfig.getPropertySetStrategy()
 1301  
             : PropertySetStrategy.DEFAULT;
 1302  0
       propertySetStrategy.setProperty( bean, key, value, jsonConfig );
 1303  
    }
 1304  0
    
 1305  0
    private static void setValue( JSONObject jsonObject, String key, Object value, Class type,
 1306  0
          JsonConfig jsonConfig, boolean bypass ) {
 1307  
       boolean accumulated = false;
 1308  0
       if( value == null ){
 1309  0
          value = jsonConfig.findDefaultValueProcessor( type )
 1310  0
                .getDefaultValue( type );
 1311  0
          if( !JsonVerifier.isValidJsonValue( value ) ){
 1312  0
             throw new JSONException( "Value is not a valid JSON value. " + value );
 1313  0
          }
 1314  0
       }
 1315  
       if( jsonObject.properties.containsKey( key ) ){
 1316  0
          if( String.class.isAssignableFrom( type ) ){
 1317  0
             Object o = jsonObject.opt( key );
 1318  
             if( o instanceof JSONArray ){
 1319  0
                ((JSONArray) o).addString( (String) value );
 1320  0
             }else{
 1321  0
                jsonObject.properties.put( key, new JSONArray().element( o )
 1322  
                      .addString( (String) value ) );
 1323  0
             }
 1324  0
          }else{
 1325  
             jsonObject.accumulate( key, value, jsonConfig );
 1326  0
          }
 1327  0
          accumulated = true;
 1328  0
       }else{
 1329  
          if( bypass || String.class.isAssignableFrom( type ) ){
 1330  0
             jsonObject.properties.put( key, value );
 1331  0
          }else{
 1332  
             jsonObject.setInternal( key, value, jsonConfig );
 1333  0
          }
 1334  0
       }
 1335  0
 
 1336  0
       value = jsonObject.opt( key );
 1337  0
       if( accumulated ){
 1338  
          JSONArray array = (JSONArray) value;
 1339  
          value = array.get( array.size() - 1 );
 1340  0
       }
 1341  0
       firePropertySetEvent( key, value, accumulated, jsonConfig );
 1342  0
    }
 1343  0
 
 1344  0
    // ------------------------------------------------------
 1345  
 
 1346  0
    /** identifies this object as null */
 1347  0
    private boolean nullObject;
 1348  
 
 1349  
    /**
 1350  0
     * The Map where the JSONObject's properties are kept.
 1351  0
     */
 1352  0
    private Map properties;
 1353  0
 
 1354  0
    /**
 1355  0
     * Construct an empty JSONObject.
 1356  0
     */
 1357  0
    public JSONObject() {
 1358  0
       this.properties = new ListOrderedMap();
 1359  0
    }
 1360  0
 
 1361  
    /**
 1362  0
     * Creates a JSONObject that is null.
 1363  0
     */
 1364  0
    public JSONObject( boolean isNull ) {
 1365  
       this();
 1366  
       this.nullObject = isNull;
 1367  
    }
 1368  0
 
 1369  0
    /**
 1370  0
     * Accumulate values under a key. It is similar to the element method except
 1371  0
     * that if there is already an object stored under the key then a JSONArray
 1372  
     * is stored under the key to hold all of the accumulated values. If there is
 1373  0
     * already a JSONArray, then the new value is appended to it. In contrast,
 1374  
     * the replace method replaces the previous value.
 1375  
     *
 1376  
     * @param key A key string.
 1377  
     * @param value An object to be accumulated under the key.
 1378  0
     * @return this.
 1379  0
     * @throws JSONException If the value is an invalid number or if the key is
 1380  0
     *         null.
 1381  
     */
 1382  0
    public JSONObject accumulate( String key, boolean value ) {
 1383  
       return _accumulate( key, value ? Boolean.TRUE : Boolean.FALSE, new JsonConfig() );
 1384  0
    }
 1385  0
 
 1386  0
    /**
 1387  0
     * Accumulate values under a key. It is similar to the element method except
 1388  0
     * that if there is already an object stored under the key then a JSONArray
 1389  
     * is stored under the key to hold all of the accumulated values. If there is
 1390  0
     * already a JSONArray, then the new value is appended to it. In contrast,
 1391  
     * the replace method replaces the previous value.
 1392  
     *
 1393  0
     * @param key A key string.
 1394  
     * @param value An object to be accumulated under the key.
 1395  0
     * @return this.
 1396  0
     * @throws JSONException If the value is an invalid number or if the key is
 1397  
     *         null.
 1398  
     */
 1399  0
    public JSONObject accumulate( String key, double value ) {
 1400  
       return _accumulate( key, new Double( value ), new JsonConfig() );
 1401  0
    }
 1402  
 
 1403  0
    /**
 1404  
     * Accumulate values under a key. It is similar to the element method except
 1405  
     * that if there is already an object stored under the key then a JSONArray
 1406  0
     * is stored under the key to hold all of the accumulated values. If there is
 1407  
     * already a JSONArray, then the new value is appended to it. In contrast,
 1408  
     * the replace method replaces the previous value.
 1409  
     *
 1410  
     * @param key A key string.
 1411  0
     * @param value An object to be accumulated under the key.
 1412  
     * @return this.
 1413  
     * @throws JSONException If the value is an invalid number or if the key is
 1414  
     *         null.
 1415  
     */
 1416  0
    public JSONObject accumulate( String key, int value ) {
 1417  0
       return _accumulate( key, new Integer( value ), new JsonConfig() );
 1418  0
    }
 1419  0
 
 1420  0
    /**
 1421  0
     * Accumulate values under a key. It is similar to the element method except
 1422  0
     * that if there is already an object stored under the key then a JSONArray
 1423  
     * is stored under the key to hold all of the accumulated values. If there is
 1424  
     * already a JSONArray, then the new value is appended to it. In contrast,
 1425  
     * the replace method replaces the previous value.
 1426  
     *
 1427  0
     * @param key A key string.
 1428  0
     * @param value An object to be accumulated under the key.
 1429  0
     * @return this.
 1430  0
     * @throws JSONException If the value is an invalid number or if the key is
 1431  0
     *         null.
 1432  0
     */
 1433  0
    public JSONObject accumulate( String key, long value ) {
 1434  
       return _accumulate( key, new Long( value ), new JsonConfig() );
 1435  
    }
 1436  
 
 1437  
    /**
 1438  
     * Accumulate values under a key. It is similar to the element method except
 1439  
     * that if there is already an object stored under the key then a JSONArray
 1440  
     * is stored under the key to hold all of the accumulated values. If there is
 1441  
     * already a JSONArray, then the new value is appended to it. In contrast,
 1442  
     * the replace method replaces the previous value.
 1443  
     *
 1444  
     * @param key A key string.
 1445  
     * @param value An object to be accumulated under the key.
 1446  
     * @return this.
 1447  
     * @throws JSONException If the value is an invalid number or if the key is
 1448  
     *         null.
 1449  
     */
 1450  
    public JSONObject accumulate( String key, Object value ) {
 1451  
       return _accumulate( key, value, new JsonConfig() );
 1452  
    }
 1453  
 
 1454  
    /**
 1455  
     * Accumulate values under a key. It is similar to the element method except
 1456  
     * that if there is already an object stored under the key then a JSONArray
 1457  
     * is stored under the key to hold all of the accumulated values. If there is
 1458  
     * already a JSONArray, then the new value is appended to it. In contrast,
 1459  
     * the replace method replaces the previous value.
 1460  
     *
 1461  
     * @param key A key string.
 1462  
     * @param value An object to be accumulated under the key.
 1463  
     * @return this.
 1464  
     * @throws JSONException If the value is an invalid number or if the key is
 1465  
     *         null.
 1466  
     */
 1467  
    public JSONObject accumulate( String key, Object value, JsonConfig jsonConfig ) {
 1468  
       return _accumulate( key, value, jsonConfig );
 1469  
    }
 1470  
 
 1471  
    public void accumulateAll( Map map ) {
 1472  
       accumulateAll( map, new JsonConfig() );
 1473  
    }
 1474  
 
 1475  
    public void accumulateAll( Map map, JsonConfig jsonConfig ) {
 1476  0
       if( map instanceof JSONObject ){
 1477  0
          for( Iterator entries = map.entrySet()
 1478  
                .iterator(); entries.hasNext(); ){
 1479  
             Map.Entry entry = (Map.Entry) entries.next();
 1480  
             String key = (String) entry.getKey();
 1481  0
             Object value = entry.getValue();
 1482  0
             accumulate( key, value, jsonConfig );
 1483  0
          }
 1484  0
       }else{
 1485  
          for( Iterator entries = map.entrySet()
 1486  0
                .iterator(); entries.hasNext(); ){
 1487  0
             Map.Entry entry = (Map.Entry) entries.next();
 1488  
             String key = String.valueOf( entry.getKey() );
 1489  0
             Object value = entry.getValue();
 1490  
             accumulate( key, value, jsonConfig );
 1491  
          }
 1492  0
       }
 1493  
    }
 1494  
 
 1495  
    public void clear() {
 1496  
       properties.clear();
 1497  0
    }
 1498  0
 
 1499  0
    public int compareTo( Object obj ) {
 1500  0
       if( obj != null && (obj instanceof JSONObject) ){
 1501  0
          JSONObject other = (JSONObject) obj;
 1502  
          int size1 = size();
 1503  0
          int size2 = other.size();
 1504  
          if( size1 < size2 ){
 1505  
             return -1;
 1506  
          }else if( size1 > size2 ){
 1507  0
             return 1;
 1508  
          }else if( this.equals( other ) ){
 1509  0
             return 0;
 1510  0
          }
 1511  0
       }
 1512  0
       return -1;
 1513  0
    }
 1514  
 
 1515  0
    public boolean containsKey( Object key ) {
 1516  
       return properties.containsKey( key );
 1517  
    }
 1518  
 
 1519  0
    public boolean containsValue( Object value ) {
 1520  
       return containsValue( value, new JsonConfig() );
 1521  0
    }
 1522  
 
 1523  0
    public boolean containsValue( Object value, JsonConfig jsonConfig ) {
 1524  
       try{
 1525  0
          value = processValue( value, jsonConfig );
 1526  0
       }catch( JSONException e ){
 1527  
          return false;
 1528  
       }
 1529  0
       return properties.containsValue( value );
 1530  
    }
 1531  
 
 1532  
    /**
 1533  
     * Remove a name and its value, if present.
 1534  0
     *
 1535  
     * @param key A key string.
 1536  0
     * @return this.
 1537  
     */
 1538  
    public JSONObject discard( String key ) {
 1539  
       verifyIsNull();
 1540  
       this.properties.remove( key );
 1541  
       return this;
 1542  
    }
 1543  
 
 1544  
    /**
 1545  0
     * Put a key/boolean pair in the JSONObject.
 1546  
     *
 1547  0
     * @param key A key string.
 1548  0
     * @param value A boolean which is the value.
 1549  
     * @return this.
 1550  
     * @throws JSONException If the key is null.
 1551  
     */
 1552  0
    public JSONObject element( String key, boolean value ) {
 1553  0
       verifyIsNull();
 1554  0
       return element( key, value ? Boolean.TRUE : Boolean.FALSE );
 1555  
    }
 1556  0
 
 1557  0
    /**
 1558  
     * Put a key/value pair in the JSONObject, where the value will be a
 1559  
     * JSONArray which is produced from a Collection.
 1560  0
     *
 1561  0
     * @param key A key string.
 1562  0
     * @param value A Collection value.
 1563  0
     * @return this.
 1564  0
     * @throws JSONException
 1565  
     */
 1566  0
    public JSONObject element( String key, Collection value ) {
 1567  
       return element( key, value, new JsonConfig() );
 1568  
    }
 1569  0
 
 1570  0
    /**
 1571  
     * Put a key/value pair in the JSONObject, where the value will be a
 1572  0
     * JSONArray which is produced from a Collection.
 1573  
     *
 1574  0
     * @param key A key string.
 1575  0
     * @param value A Collection value.
 1576  
     * @return this.
 1577  0
     * @throws JSONException
 1578  
     */
 1579  
    public JSONObject element( String key, Collection value, JsonConfig jsonConfig ) {
 1580  
       verifyIsNull();
 1581  0
       if( !(value instanceof JSONArray) ){
 1582  0
          value = JSONArray.fromObject( value, jsonConfig );
 1583  0
       }
 1584  0
       return setInternal( key, value, jsonConfig );
 1585  
    }
 1586  0
 
 1587  0
    /**
 1588  
     * Put a key/double pair in the JSONObject.
 1589  
     *
 1590  
     * @param key A key string.
 1591  
     * @param value A double which is the value.
 1592  
     * @return this.
 1593  
     * @throws JSONException If the key is null or if the number is invalid.
 1594  
     */
 1595  
    public JSONObject element( String key, double value ) {
 1596  
       verifyIsNull();
 1597  
       Double d = new Double( value );
 1598  
       JSONUtils.testValidity( d );
 1599  
       return element( key, d );
 1600  
    }
 1601  
 
 1602  0
    /**
 1603  0
     * Put a key/int pair in the JSONObject.
 1604  0
     *
 1605  
     * @param key A key string.
 1606  
     * @param value An int which is the value.
 1607  
     * @return this.
 1608  
     * @throws JSONException If the key is null.
 1609  
     */
 1610  0
    public JSONObject element( String key, int value ) {
 1611  0
       verifyIsNull();
 1612  0
       return element( key, new Integer( value ) );
 1613  
    }
 1614  
 
 1615  
    /**
 1616  
     * Put a key/long pair in the JSONObject.
 1617  
     *
 1618  
     * @param key A key string.
 1619  
     * @param value A long which is the value.
 1620  
     * @return this.
 1621  
     * @throws JSONException If the key is null.
 1622  
     */
 1623  
    public JSONObject element( String key, long value ) {
 1624  
       verifyIsNull();
 1625  
       return element( key, new Long( value ) );
 1626  
    }
 1627  
 
 1628  0
    /**
 1629  
     * Put a key/value pair in the JSONObject, where the value will be a
 1630  
     * JSONObject which is produced from a Map.
 1631  
     *
 1632  
     * @param key A key string.
 1633  
     * @param value A Map value.
 1634  
     * @return this.
 1635  
     * @throws JSONException
 1636  
     */
 1637  
    public JSONObject element( String key, Map value ) {
 1638  
       return element( key, value, new JsonConfig() );
 1639  
    }
 1640  
 
 1641  
    /**
 1642  
     * Put a key/value pair in the JSONObject, where the value will be a
 1643  
     * JSONObject which is produced from a Map.
 1644  
     *
 1645  0
     * @param key A key string.
 1646  
     * @param value A Map value.
 1647  
     * @return this.
 1648  
     * @throws JSONException
 1649  
     */
 1650  
    public JSONObject element( String key, Map value, JsonConfig jsonConfig ) {
 1651  
       verifyIsNull();
 1652  
       if( value instanceof JSONObject ){
 1653  
          return setInternal( key, value, jsonConfig );
 1654  
       }else{
 1655  
          return element( key, JSONObject.fromObject( value, jsonConfig ), jsonConfig );
 1656  
       }
 1657  
    }
 1658  
 
 1659  
    /**
 1660  
     * Put a key/value pair in the JSONObject. If the value is null, then the key
 1661  
     * will be removed from the JSONObject if it is present.<br>
 1662  0
     * If there is a previous value assigned to the key, it will call accumulate.
 1663  
     *
 1664  
     * @param key A key string.
 1665  
     * @param value An object which is the value. It should be of one of these
 1666  
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 1667  
     *        String, or the JSONNull object.
 1668  
     * @return this.
 1669  
     * @throws JSONException If the value is non-finite number or if the key is
 1670  
     *         null.
 1671  
     */
 1672  
    public JSONObject element( String key, Object value ) {
 1673  
       return element( key, value, new JsonConfig() );
 1674  
    }
 1675  
 
 1676  
    /**
 1677  
     * Put a key/value pair in the JSONObject. If the value is null, then the key
 1678  
     * will be removed from the JSONObject if it is present.<br>
 1679  0
     * If there is a previous value assigned to the key, it will call accumulate.
 1680  
     *
 1681  
     * @param key A key string.
 1682  
     * @param value An object which is the value. It should be of one of these
 1683  
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 1684  
     *        String, or the JSONNull object.
 1685  
     * @return this.
 1686  
     * @throws JSONException If the value is non-finite number or if the key is
 1687  
     *         null.
 1688  
     */
 1689  
    public JSONObject element( String key, Object value, JsonConfig jsonConfig ) {
 1690  
       verifyIsNull();
 1691  
       if( key == null ){
 1692  
          throw new JSONException( "Null key." );
 1693  
       }
 1694  
       if( value != null ){
 1695  
          value = processValue( key, value, jsonConfig );
 1696  0
          _setInternal( key, value, jsonConfig );
 1697  
       }else{
 1698  
          remove( key );
 1699  
       }
 1700  
       return this;
 1701  
    }
 1702  
 
 1703  
    /**
 1704  
     * Put a key/value pair in the JSONObject, but only if the key and the value
 1705  
     * are both non-null.
 1706  
     *
 1707  
     * @param key A key string.
 1708  
     * @param value An object which is the value. It should be of one of these
 1709  
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 1710  
     *        String, or the JSONNull object.
 1711  
     * @return this.
 1712  
     * @throws JSONException If the value is a non-finite number.
 1713  0
     */
 1714  
    public JSONObject elementOpt( String key, Object value ) {
 1715  
       return elementOpt( key, value, new JsonConfig() );
 1716  
    }
 1717  0
 
 1718  0
    /**
 1719  
     * Put a key/value pair in the JSONObject, but only if the key and the value
 1720  
     * are both non-null.
 1721  0
     *
 1722  0
     * @param key A key string.
 1723  0
     * @param value An object which is the value. It should be of one of these
 1724  0
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 1725  0
     *        String, or the JSONNull object.
 1726  0
     * @return this.
 1727  0
     * @throws JSONException If the value is a non-finite number.
 1728  0
     */
 1729  
    public JSONObject elementOpt( String key, Object value, JsonConfig jsonConfig ) {
 1730  0
       verifyIsNull();
 1731  0
       if( key != null && value != null ){
 1732  0
          element( key, value, jsonConfig );
 1733  0
       }
 1734  0
       return this;
 1735  0
    }
 1736  0
 
 1737  
    public Set entrySet() {
 1738  0
       return Collections.unmodifiableSet( properties.entrySet() );
 1739  
    }
 1740  
 
 1741  0
    public boolean equals( Object obj ) {
 1742  0
       if( obj == this ){
 1743  
          return true;
 1744  
       }
 1745  0
       if( obj == null ){
 1746  0
          return false;
 1747  0
       }
 1748  0
 
 1749  0
       if( !(obj instanceof JSONObject) ){
 1750  0
          return false;
 1751  0
       }
 1752  0
 
 1753  0
       JSONObject other = (JSONObject) obj;
 1754  0
 
 1755  
       if( isNullObject() ){
 1756  
          if( other.isNullObject() ){
 1757  0
             return true;
 1758  
          }else{
 1759  
             return false;
 1760  
          }
 1761  0
       }else{
 1762  
          if( other.isNullObject() ){
 1763  
             return false;
 1764  
          }
 1765  0
       }
 1766  
 
 1767  
       if( other.size() != size() ){
 1768  
          return false;
 1769  
       }
 1770  0
 
 1771  0
       for( Iterator keys = properties.keySet()
 1772  0
             .iterator(); keys.hasNext(); ){
 1773  0
          String key = (String) keys.next();
 1774  0
          if( !other.properties.containsKey( key ) ){
 1775  
             return false;
 1776  
          }
 1777  
          Object o1 = properties.get( key );
 1778  
          Object o2 = other.properties.get( key );
 1779  
 
 1780  
          if( JSONNull.getInstance()
 1781  
                .equals( o1 ) ){
 1782  
             if( JSONNull.getInstance()
 1783  
                   .equals( o2 ) ){
 1784  0
                continue;
 1785  0
             }else{
 1786  0
                return false;
 1787  
             }
 1788  
          }else{
 1789  
             if( JSONNull.getInstance()
 1790  
                   .equals( o2 ) ){
 1791  
                return false;
 1792  
             }
 1793  
          }
 1794  
 
 1795  
          if( o1 instanceof String && o2 instanceof JSONFunction ){
 1796  
             if( !o1.equals( String.valueOf( o2 ) ) ){
 1797  
                return false;
 1798  0
             }
 1799  0
          }else if( o1 instanceof JSONFunction && o2 instanceof String ){
 1800  
             if( !o2.equals( String.valueOf( o1 ) ) ){
 1801  
                return false;
 1802  
             }
 1803  
          }else if( o1 instanceof JSONObject && o2 instanceof JSONObject ){
 1804  
             if( !o1.equals( o2 ) ){
 1805  
                return false;
 1806  
             }
 1807  
          }else if( o1 instanceof JSONArray && o2 instanceof JSONArray ){
 1808  
             if( !o1.equals( o2 ) ){
 1809  
                return false;
 1810  
             }
 1811  
          }else if( o1 instanceof JSONFunction && o2 instanceof JSONFunction ){
 1812  0
             if( !o1.equals( o2 ) ){
 1813  
                return false;
 1814  
             }
 1815  
          }else{
 1816  
             if( o1 instanceof String ){
 1817  
                if( !o1.equals( String.valueOf( o2 ) ) ){
 1818  
                   return false;
 1819  
                }
 1820  
             }else if( o2 instanceof String ){
 1821  
                if( !o2.equals( String.valueOf( o1 ) ) ){
 1822  
                   return false;
 1823  
                }
 1824  
             }else{
 1825  0
                Morpher m1 = JSONUtils.getMorpherRegistry()
 1826  0
                      .getMorpherFor( o1.getClass() );
 1827  
                Morpher m2 = JSONUtils.getMorpherRegistry()
 1828  0
                      .getMorpherFor( o2.getClass() );
 1829  
                if( m1 != null && m1 != IdentityObjectMorpher.getInstance() ){
 1830  
                   if( !o1.equals( JSONUtils.getMorpherRegistry()
 1831  
                         .morph( o1.getClass(), o2 ) ) ){
 1832  
                      return false;
 1833  
                   }
 1834  
                }else if( m2 != null && m2 != IdentityObjectMorpher.getInstance() ){
 1835  
                   if( !JSONUtils.getMorpherRegistry()
 1836  
                         .morph( o1.getClass(), o1 )
 1837  
                         .equals( o2 ) ){
 1838  
                      return false;
 1839  
                   }
 1840  0
                }else{
 1841  0
                   if( !o1.equals( o2 ) ){
 1842  0
                      return false;
 1843  0
                   }
 1844  
                }
 1845  
             }
 1846  
          }
 1847  
       }
 1848  
       return true;
 1849  
    }
 1850  
 
 1851  
    public Object get( Object key ) {
 1852  
       if( key instanceof String ){
 1853  
          return get( (String) key );
 1854  
       }
 1855  0
       return null;
 1856  0
    }
 1857  
 
 1858  
    /**
 1859  
     * Get the value object associated with a key.
 1860  
     *
 1861  
     * @param key A key string.
 1862  
     * @return The object associated with the key.
 1863  
     * @throws JSONException if this.isNull() returns true.
 1864  
     */
 1865  
    public Object get( String key ) {
 1866  
       verifyIsNull();
 1867  
       return this.properties.get( key );
 1868  0
    }
 1869  0
 
 1870  
    /**
 1871  
     * Get the boolean value associated with a key.
 1872  
     *
 1873  
     * @param key A key string.
 1874  
     * @return The truth.
 1875  
     * @throws JSONException if the value is not a Boolean or the String "true"
 1876  
     *         or "false".
 1877  
     */
 1878  
    public boolean getBoolean( String key ) {
 1879  
       verifyIsNull();
 1880  
       Object o = get( key );
 1881  
       if( o != null ){
 1882  0
          if( o.equals( Boolean.FALSE )
 1883  
                || (o instanceof String && ((String) o).equalsIgnoreCase( "false" )) ){
 1884  
             return false;
 1885  
          }else if( o.equals( Boolean.TRUE )
 1886  
                || (o instanceof String && ((String) o).equalsIgnoreCase( "true" )) ){
 1887  
             return true;
 1888  
          }
 1889  
       }
 1890  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a Boolean." );
 1891  
    }
 1892  
 
 1893  
    /**
 1894  
     * Get the double value associated with a key.
 1895  0
     *
 1896  0
     * @param key A key string.
 1897  0
     * @return The numeric value.
 1898  
     * @throws JSONException if the key is not found or if the value is not a
 1899  0
     *         Number object and cannot be converted to a number.
 1900  
     */
 1901  
    public double getDouble( String key ) {
 1902  
       verifyIsNull();
 1903  
       Object o = get( key );
 1904  
       if( o != null ){
 1905  
          try{
 1906  
             return o instanceof Number ? ((Number) o).doubleValue()
 1907  
                   : Double.parseDouble( (String) o );
 1908  
          }catch( Exception e ){
 1909  
             throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a number." );
 1910  
          }
 1911  
       }
 1912  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a number." );
 1913  
    }
 1914  
 
 1915  
    /**
 1916  
     * Get the int value associated with a key. If the number value is too large
 1917  0
     * for an int, it will be clipped.
 1918  
     *
 1919  
     * @param key A key string.
 1920  
     * @return The integer value.
 1921  
     * @throws JSONException if the key is not found or if the value cannot be
 1922  
     *         converted to an integer.
 1923  
     */
 1924  
    public int getInt( String key ) {
 1925  
       verifyIsNull();
 1926  
       Object o = get( key );
 1927  
       if( o != null ){
 1928  
          return o instanceof Number ? ((Number) o).intValue() : (int) getDouble( key );
 1929  
       }
 1930  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a number." );
 1931  
    }
 1932  
 
 1933  
    /**
 1934  0
     * Get the JSONArray value associated with a key.
 1935  0
     *
 1936  0
     * @param key A key string.
 1937  
     * @return A JSONArray which is the value.
 1938  0
     * @throws JSONException if the key is not found or if the value is not a
 1939  0
     *         JSONArray.
 1940  0
     */
 1941  
    public JSONArray getJSONArray( String key ) {
 1942  0
       verifyIsNull();
 1943  
       Object o = get( key );
 1944  0
       if( o != null && o instanceof JSONArray ){
 1945  
          return (JSONArray) o;
 1946  
       }
 1947  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a JSONArray." );
 1948  
    }
 1949  
 
 1950  
    /**
 1951  
     * Get the JSONObject value associated with a key.
 1952  
     *
 1953  
     * @param key A key string.
 1954  
     * @return A JSONObject which is the value.
 1955  
     * @throws JSONException if the key is not found or if the value is not a
 1956  
     *         JSONObject.
 1957  
     */
 1958  
    public JSONObject getJSONObject( String key ) {
 1959  0
       verifyIsNull();
 1960  
       Object o = get( key );
 1961  
       if( JSONNull.getInstance()
 1962  
             .equals( o ) ){
 1963  
          return new JSONObject( true );
 1964  
       }else if( o instanceof JSONObject ){
 1965  
          return (JSONObject) o;
 1966  
       }
 1967  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a JSONObject." );
 1968  
    }
 1969  
 
 1970  
    /**
 1971  
     * Get the long value associated with a key. If the number value is too long
 1972  
     * for a long, it will be clipped.
 1973  
     *
 1974  0
     * @param key A key string.
 1975  0
     * @return The long value.
 1976  0
     * @throws JSONException if the key is not found or if the value cannot be
 1977  
     *         converted to a long.
 1978  0
     */
 1979  
    public long getLong( String key ) {
 1980  
       verifyIsNull();
 1981  
       Object o = get( key );
 1982  0
       if( o != null ){
 1983  
          return o instanceof Number ? ((Number) o).longValue() : (long) getDouble( key );
 1984  
       }
 1985  
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] is not a number." );
 1986  0
    }
 1987  0
 
 1988  
    /**
 1989  0
     * Get the string associated with a key.
 1990  0
     *
 1991  
     * @param key A key string.
 1992  
     * @return A string which is the value.
 1993  0
     * @throws JSONException if the key is not found.
 1994  0
     */
 1995  
    public String getString( String key ) {
 1996  
       verifyIsNull();
 1997  0
       Object o = get( key );
 1998  
       if( o != null ){
 1999  0
          return o.toString();
 2000  0
       }
 2001  0
       throw new JSONException( "JSONObject[" + JSONUtils.quote( key ) + "] not found." );
 2002  
    }
 2003  0
 
 2004  
    /**
 2005  
     * Determine if the JSONObject contains a specific key.
 2006  0
     *
 2007  0
     * @param key A key string.
 2008  
     * @return true if the key exists in the JSONObject.
 2009  
     */
 2010  
    public boolean has( String key ) {
 2011  0
       verifyIsNull();
 2012  0
       return this.properties.containsKey( key );
 2013  
    }
 2014  
 
 2015  0
    public int hashCode() {
 2016  0
       int hashcode = 19;
 2017  0
       if( isNullObject() ){
 2018  0
          return hashcode + JSONNull.getInstance()
 2019  0
                .hashCode();
 2020  
       }
 2021  0
       for( Iterator entries = properties.entrySet()
 2022  0
             .iterator(); entries.hasNext(); ){
 2023  
          Map.Entry entry = (Map.Entry) entries.next();
 2024  0
          Object key = entry.getKey();
 2025  
          Object value = entry.getValue();
 2026  0
          hashcode += key.hashCode() + JSONUtils.hashCode( value );
 2027  
       }
 2028  0
       return hashcode;
 2029  
    }
 2030  0
 
 2031  
    public boolean isArray() {
 2032  
       return false;
 2033  0
    }
 2034  
 
 2035  0
    public boolean isEmpty() {
 2036  
       // verifyIsNull();
 2037  
       return this.properties.isEmpty();
 2038  
    }
 2039  0
 
 2040  0
    /**
 2041  0
     * Returs if this object is a null JSONObject.
 2042  
     */
 2043  0
    public boolean isNullObject() {
 2044  0
       return nullObject;
 2045  0
    }
 2046  
 
 2047  0
    /**
 2048  0
     * Get an enumeration of the keys of the JSONObject.
 2049  0
     *
 2050  
     * @return An iterator of the keys.
 2051  0
     */
 2052  0
    public Iterator keys() {
 2053  0
       verifyIsNull();
 2054  
       return keySet().iterator();
 2055  0
    }
 2056  0
 
 2057  0
    public Set keySet() {
 2058  
       return Collections.unmodifiableSet( properties.keySet() );
 2059  
    }
 2060  0
 
 2061  0
    /**
 2062  0
     * Produce a JSONArray containing the names of the elements of this
 2063  
     * JSONObject.
 2064  0
     *
 2065  0
     * @return A JSONArray containing the key strings, or null if the JSONObject
 2066  0
     *         is empty.
 2067  
     */
 2068  
    public JSONArray names() {
 2069  0
       return names( new JsonConfig() );
 2070  
    }
 2071  0
    
 2072  
    /**
 2073  0
     * Produce a JSONArray containing the names of the elements of this
 2074  0
     * JSONObject.
 2075  
     *
 2076  0
     * @return A JSONArray containing the key strings, or null if the JSONObject
 2077  
     *         is empty.
 2078  0
     */
 2079  0
    public JSONArray names( JsonConfig jsonConfig ) {
 2080  
       verifyIsNull();
 2081  
       JSONArray ja = new JSONArray();
 2082  0
       Iterator keys = keys();
 2083  
       while( keys.hasNext() ){
 2084  
          ja.element( keys.next(), jsonConfig );
 2085  0
       }
 2086  0
       return ja;
 2087  
    }
 2088  
 
 2089  
    /**
 2090  
     * Get an optional value associated with a key.
 2091  0
     *
 2092  0
     * @param key A key string.
 2093  
     * @return An object which is the value, or null if there is no value.
 2094  
     */
 2095  
    public Object opt( String key ) {
 2096  0
       verifyIsNull();
 2097  0
       return key == null ? null : this.properties.get( key );
 2098  
    }
 2099  0
 
 2100  
    /**
 2101  
     * Get an optional boolean associated with a key. It returns false if there
 2102  
     * is no such key, or if the value is not Boolean.TRUE or the String "true".
 2103  
     *
 2104  
     * @param key A key string.
 2105  
     * @return The truth.
 2106  
     */
 2107  
    public boolean optBoolean( String key ) {
 2108  
       verifyIsNull();
 2109  
       return optBoolean( key, false );
 2110  0
    }
 2111  0
 
 2112  
    /**
 2113  
     * Get an optional boolean associated with a key. It returns the defaultValue
 2114  
     * if there is no such key, or if it is not a Boolean or the String "true" or
 2115  
     * "false" (case insensitive).
 2116  
     *
 2117  
     * @param key A key string.
 2118  
     * @param defaultValue The default.
 2119  
     * @return The truth.
 2120  
     */
 2121  
    public boolean optBoolean( String key, boolean defaultValue ) {
 2122  
       verifyIsNull();
 2123  0
       try{
 2124  0
          return getBoolean( key );
 2125  0
       }catch( Exception e ){
 2126  0
          return defaultValue;
 2127  
       }
 2128  0
    }
 2129  0
 
 2130  
    /**
 2131  0
     * Get an optional double associated with a key, or NaN if there is no such
 2132  
     * key or if its value is not a number. If the value is a string, an attempt
 2133  
     * will be made to evaluate it as a number.
 2134  0
     *
 2135  
     * @param key A string which is the key.
 2136  
     * @return An object which is the value.
 2137  
     */
 2138  
    public double optDouble( String key ) {
 2139  
       verifyIsNull();
 2140  
       return optDouble( key, Double.NaN );
 2141  
    }
 2142  
 
 2143  
    /**
 2144  
     * Get an optional double associated with a key, or the defaultValue if there
 2145  
     * is no such key or if its value is not a number. If the value is a string,
 2146  0
     * an attempt will be made to evaluate it as a number.
 2147  0
     *
 2148  0
     * @param key A key string.
 2149  
     * @param defaultValue The default.
 2150  0
     * @return An object which is the value.
 2151  
     */
 2152  0
    public double optDouble( String key, double defaultValue ) {
 2153  0
       verifyIsNull();
 2154  
       try{
 2155  
          Object o = opt( key );
 2156  0
          return o instanceof Number ? ((Number) o).doubleValue()
 2157  
                : new Double( (String) o ).doubleValue();
 2158  
       }catch( Exception e ){
 2159  
          return defaultValue;
 2160  
       }
 2161  
    }
 2162  
 
 2163  
    /**
 2164  
     * Get an optional int value associated with a key, or zero if there is no
 2165  
     * such key or if the value is not a number. If the value is a string, an
 2166  
     * attempt will be made to evaluate it as a number.
 2167  
     *
 2168  
     * @param key A key string.
 2169  0
     * @return An object which is the value.
 2170  0
     */
 2171  0
    public int optInt( String key ) {
 2172  0
       verifyIsNull();
 2173  
       return optInt( key, 0 );
 2174  0
    }
 2175  
 
 2176  
    /**
 2177  
     * Get an optional int value associated with a key, or the default if there
 2178  
     * is no such key or if the value is not a number. If the value is a string,
 2179  
     * an attempt will be made to evaluate it as a number.
 2180  
     *
 2181  
     * @param key A key string.
 2182  
     * @param defaultValue The default.
 2183  
     * @return An object which is the value.
 2184  
     */
 2185  
    public int optInt( String key, int defaultValue ) {
 2186  0
       verifyIsNull();
 2187  0
       try{
 2188  0
          return getInt( key );
 2189  0
       }catch( Exception e ){
 2190  
          return defaultValue;
 2191  0
       }
 2192  
    }
 2193  
 
 2194  
    /**
 2195  
     * Get an optional JSONArray associated with a key. It returns null if there
 2196  
     * is no such key, or if its value is not a JSONArray.
 2197  
     *
 2198  
     * @param key A key string.
 2199  
     * @return A JSONArray which is the value.
 2200  
     */
 2201  
    public JSONArray optJSONArray( String key ) {
 2202  
       verifyIsNull();
 2203  0
       Object o = opt( key );
 2204  0
       return o instanceof JSONArray ? (JSONArray) o : null;
 2205  0
    }
 2206  
 
 2207  0
    /**
 2208  0
     * Get an optional JSONObject associated with a key. It returns null if there
 2209  0
     * is no such key, or if its value is not a JSONObject.
 2210  
     *
 2211  0
     * @param key A key string.
 2212  
     * @return A JSONObject which is the value.
 2213  
     */
 2214  
    public JSONObject optJSONObject( String key ) {
 2215  
       verifyIsNull();
 2216  
       Object o = opt( key );
 2217  
       return o instanceof JSONObject ? (JSONObject) o : null;
 2218  
    }
 2219  
 
 2220  
    /**
 2221  
     * Get an optional long value associated with a key, or zero if there is no
 2222  
     * such key or if the value is not a number. If the value is a string, an
 2223  
     * attempt will be made to evaluate it as a number.
 2224  0
     *
 2225  0
     * @param key A key string.
 2226  0
     * @return An object which is the value.
 2227  0
     */
 2228  
    public long optLong( String key ) {
 2229  0
       verifyIsNull();
 2230  
       return optLong( key, 0 );
 2231  
    }
 2232  
 
 2233  
    /**
 2234  
     * Get an optional long value associated with a key, or the default if there
 2235  
     * is no such key or if the value is not a number. If the value is a string,
 2236  
     * an attempt will be made to evaluate it as a number.
 2237  
     *
 2238  
     * @param key A key string.
 2239  
     * @param defaultValue The default.
 2240  0
     * @return An object which is the value.
 2241  0
     */
 2242  0
    public long optLong( String key, long defaultValue ) {
 2243  0
       verifyIsNull();
 2244  
       try{
 2245  0
          return getLong( key );
 2246  
       }catch( Exception e ){
 2247  
          return defaultValue;
 2248  
       }
 2249  
    }
 2250  
 
 2251  
    /**
 2252  
     * Get an optional string associated with a key. It returns an empty string
 2253  
     * if there is no such key. If the value is not a string and is not null,
 2254  
     * then it is coverted to a string.
 2255  0
     *
 2256  0
     * @param key A key string.
 2257  
     * @return A string which is the value.
 2258  
     */
 2259  
    public String optString( String key ) {
 2260  0
       verifyIsNull();
 2261  0
       return optString( key, "" );
 2262  0
    }
 2263  
 
 2264  
    /**
 2265  0
     * Get an optional string associated with a key. It returns the defaultValue
 2266  0
     * if there is no such key.
 2267  0
     *
 2268  0
     * @param key A key string.
 2269  0
     * @param defaultValue The default.
 2270  0
     * @return A string which is the value.
 2271  0
     */
 2272  0
    public String optString( String key, String defaultValue ) {
 2273  
       verifyIsNull();
 2274  
       Object o = opt( key );
 2275  
       return o != null ? o.toString() : defaultValue;
 2276  0
    }
 2277  
 
 2278  
    public Object put( Object key, Object value ) {
 2279  
       if( key == null ){
 2280  
          throw new IllegalArgumentException( "key is null." );
 2281  0
       }
 2282  
       Object previous = properties.get( key );
 2283  
       element( String.valueOf( key ), value );
 2284  
       return previous;
 2285  
    }
 2286  
 
 2287  
    public void putAll( Map map ) {
 2288  0
       putAll( map, new JsonConfig() );
 2289  
    }
 2290  
 
 2291  
    public void putAll( Map map, JsonConfig jsonConfig ) {
 2292  
       if( map instanceof JSONObject ){
 2293  
          for( Iterator entries = map.entrySet()
 2294  
                .iterator(); entries.hasNext(); ){
 2295  
             Map.Entry entry = (Map.Entry) entries.next();
 2296  
             String key = (String) entry.getKey();
 2297  0
             Object value = entry.getValue();
 2298  0
             this.properties.put( key, value );
 2299  
          }
 2300  
       }else{
 2301  
          for( Iterator entries = map.entrySet()
 2302  0
                .iterator(); entries.hasNext(); ){
 2303  
             Map.Entry entry = (Map.Entry) entries.next();
 2304  
             String key = String.valueOf( entry.getKey() );
 2305  
             Object value = entry.getValue();
 2306  
             element( key, value, jsonConfig );
 2307  
          }
 2308  
       }
 2309  
    }
 2310  
 
 2311  
    public Object remove( Object key ) {
 2312  
       return properties.remove( key );
 2313  0
    }
 2314  0
 
 2315  0
    /**
 2316  0
     * Remove a name and its value, if present.
 2317  0
     *
 2318  
     * @param key The name to be removed.
 2319  0
     * @return The value that was associated with the name, or null if there was
 2320  
     *         no value.
 2321  
     */
 2322  
    public Object remove( String key ) {
 2323  
       verifyIsNull();
 2324  
       return this.properties.remove( key );
 2325  
    }
 2326  
 
 2327  
    /**
 2328  
     * Get the number of keys stored in the JSONObject.
 2329  
     *
 2330  0
     * @return The number of keys in the JSONObject.
 2331  0
     */
 2332  0
    public int size() {
 2333  0
       // verifyIsNull();
 2334  0
       return this.properties.size();
 2335  
    }
 2336  0
 
 2337  
    /**
 2338  
     * Produce a JSONArray containing the values of the members of this
 2339  
     * JSONObject.
 2340  
     *
 2341  
     * @param names A JSONArray containing a list of key strings. This determines
 2342  
     *        the sequence of the values in the result.
 2343  
     * @return A JSONArray of values.
 2344  
     * @throws JSONException If any of the values are non-finite numbers.
 2345  
     */
 2346  0
    public JSONArray toJSONArray( JSONArray names ) {
 2347  0
       verifyIsNull();
 2348  
       if( names == null || names.size() == 0 ){
 2349  
          return null;
 2350  
       }
 2351  
       JSONArray ja = new JSONArray();
 2352  
       for( int i = 0; i < names.size(); i += 1 ){
 2353  
          ja.element( this.opt( names.getString( i ) ) );
 2354  
       }
 2355  
       return ja;
 2356  
    }
 2357  
 
 2358  0
    /**
 2359  0
     * Make a JSON text of this JSONObject. For compactness, no whitespace is
 2360  
     * added. If this would not result in a syntactically correct JSON text, then
 2361  
     * null will be returned instead.
 2362  
     * <p>
 2363  
     * Warning: This method assumes that the data structure is acyclical.
 2364  
     *
 2365  
     * @return a printable, displayable, portable, transmittable representation
 2366  
     *         of the object, beginning with <code>{</code>&nbsp;<small>(left
 2367  
     *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 2368  
     *         brace)</small>.
 2369  
     */
 2370  
    public String toString() {
 2371  
       if( isNullObject() ){
 2372  0
          return JSONNull.getInstance()
 2373  
                .toString();
 2374  0
       }
 2375  0
       try{
 2376  0
          Iterator keys = keys();
 2377  
          StringBuffer sb = new StringBuffer( "{" );
 2378  
 
 2379  
          while( keys.hasNext() ){
 2380  
             if( sb.length() > 1 ){
 2381  
                sb.append( ',' );
 2382  
             }
 2383  
             Object o = keys.next();
 2384  
             sb.append( JSONUtils.quote( o.toString() ) );
 2385  
             sb.append( ':' );
 2386  
             sb.append( JSONUtils.valueToString( this.properties.get( o ) ) );
 2387  
          }
 2388  
          sb.append( '}' );
 2389  0
          return sb.toString();
 2390  0
       }catch( Exception e ){
 2391  
          return null;
 2392  
       }
 2393  
    }
 2394  
 
 2395  
    /**
 2396  
     * Make a prettyprinted JSON text of this JSONObject.
 2397  
     * <p>
 2398  
     * Warning: This method assumes that the data structure is acyclical.
 2399  
     *
 2400  
     * @param indentFactor The number of spaces to add to each level of
 2401  
     *        indentation.
 2402  
     * @return a printable, displayable, portable, transmittable representation
 2403  0
     *         of the object, beginning with <code>{</code>&nbsp;<small>(left
 2404  
     *         brace)</small> and ending with <code>}</code>&nbsp;<small>(right
 2405  0
     *         brace)</small>.
 2406  0
     * @throws JSONException If the object contains an invalid number.
 2407  
     */
 2408  0
    public String toString( int indentFactor ) {
 2409  0
       if( isNullObject() ){
 2410  
          return JSONNull.getInstance()
 2411  
                .toString();
 2412  
       }
 2413  
       if( indentFactor == 0 ){
 2414  
          return this.toString();
 2415  
       }
 2416  
       return toString( indentFactor, 0 );
 2417  
    }
 2418  
 
 2419  
    /**
 2420  
     * Make a prettyprinted JSON text of this JSONObject.
 2421  
     * <p>
 2422  0
     * Warning: This method assumes that the data structure is acyclical.
 2423  0
     *
 2424  
     * @param indentFactor The number of spaces to add to each level of
 2425  
     *        indentation.
 2426  
     * @param indent The indentation of the top level.
 2427  
     * @return a printable, displayable, transmittable representation of the
 2428  
     *         object, beginning with <code>{</code>&nbsp;<small>(left brace)</small>
 2429  
     *         and ending with <code>}</code>&nbsp;<small>(right brace)</small>.
 2430  
     * @throws JSONException If the object contains an invalid number.
 2431  
     */
 2432  
    public String toString( int indentFactor, int indent ) {
 2433  
       if( isNullObject() ){
 2434  
          return JSONNull.getInstance()
 2435  
                .toString();
 2436  0
       }
 2437  
       int i;
 2438  0
       int n = size();
 2439  0
       if( n == 0 ){
 2440  0
          return "{}";
 2441  
       }
 2442  
       if( indentFactor == 0 ){
 2443  
          return this.toString();
 2444  
       }
 2445  
       Iterator keys = keys();
 2446  
       StringBuffer sb = new StringBuffer( "{" );
 2447  
       int newindent = indent + indentFactor;
 2448  
       Object o;
 2449  
       if( n == 1 ){
 2450  
          o = keys.next();
 2451  
          sb.append( JSONUtils.quote( o.toString() ) );
 2452  0
          sb.append( ": " );
 2453  0
          sb.append( JSONUtils.valueToString( this.properties.get( o ), indentFactor, indent ) );
 2454  0
       }else{
 2455  
          while( keys.hasNext() ){
 2456  
             o = keys.next();
 2457  
             if( sb.length() > 1 ){
 2458  
                sb.append( ",\n" );
 2459  
             }else{
 2460  
                sb.append( '\n' );
 2461  
             }
 2462  
             for( i = 0; i < newindent; i += 1 ){
 2463  
                sb.append( ' ' );
 2464  
             }
 2465  0
             sb.append( JSONUtils.quote( o.toString() ) );
 2466  0
             sb.append( ": " );
 2467  0
             sb.append( JSONUtils.valueToString( this.properties.get( o ), indentFactor, newindent ) );
 2468  
          }
 2469  
          if( sb.length() > 1 ){
 2470  
             sb.append( '\n' );
 2471  
             for( i = 0; i < indent; i += 1 ){
 2472  
                sb.append( ' ' );
 2473  
             }
 2474  
          }
 2475  
          for( i = 0; i < indent; i += 1 ){
 2476  
             sb.insert( 0, ' ' );
 2477  
          }
 2478  
       }
 2479  0
       sb.append( '}' );
 2480  0
       return sb.toString();
 2481  
    }
 2482  
 
 2483  
    public Collection values() {
 2484  
       return Collections.unmodifiableCollection( properties.values() );
 2485  
    }
 2486  
 
 2487  
    /**
 2488  
     * Write the contents of the JSONObject as JSON text to a writer. For
 2489  
     * compactness, no whitespace is added.
 2490  
     * <p>
 2491  
     * Warning: This method assumes that the data structure is acyclical.
 2492  
     *
 2493  0
     * @return The writer.
 2494  
     * @throws JSONException
 2495  0
     */
 2496  0
    public Writer write( Writer writer ) {
 2497  0
       try{
 2498  
          if( isNullObject() ){
 2499  
             writer.write( JSONNull.getInstance()
 2500  
                   .toString() );
 2501  
             return writer;
 2502  
          }
 2503  
 
 2504  
          boolean b = false;
 2505  
          Iterator keys = keys();
 2506  
          writer.write( '{' );
 2507  
 
 2508  
          while( keys.hasNext() ){
 2509  
             if( b ){
 2510  0
                writer.write( ',' );
 2511  0
             }
 2512  
             Object k = keys.next();
 2513  
             writer.write( JSONUtils.quote( k.toString() ) );
 2514  
             writer.write( ':' );
 2515  
             Object v = this.properties.get( k );
 2516  
             if( v instanceof JSONObject ){
 2517  
                ((JSONObject) v).write( writer );
 2518  
             }else if( v instanceof JSONArray ){
 2519  
                ((JSONArray) v).write( writer );
 2520  
             }else{
 2521  
                writer.write( JSONUtils.valueToString( v ) );
 2522  
             }
 2523  0
             b = true;
 2524  0
          }
 2525  0
          writer.write( '}' );
 2526  
          return writer;
 2527  
       }catch( IOException e ){
 2528  
          throw new JSONException( e );
 2529  0
       }
 2530  0
    }
 2531  
 
 2532  0
    private JSONObject _accumulate( String key, Object value, JsonConfig jsonConfig ) {
 2533  0
       if( isNullObject() ){
 2534  0
          throw new JSONException( "Can't accumulate on null object" );
 2535  
       }
 2536  
 
 2537  
       if( !has( key ) ){
 2538  0
          setInternal( key, value, jsonConfig );
 2539  0
       }else{
 2540  
          Object o = opt( key );
 2541  
          if( o instanceof JSONArray ){
 2542  0
             ((JSONArray) o).element( value, jsonConfig );
 2543  0
          }else{
 2544  0
             setInternal( key, new JSONArray().element( o )
 2545  0
                   .element( value, jsonConfig ), jsonConfig );
 2546  0
          }
 2547  0
       }
 2548  0
 
 2549  0
       return this;
 2550  
    }
 2551  0
 
 2552  0
    protected Object _processValue( Object value, JsonConfig jsonConfig ) {
 2553  0
       if( value instanceof JSONTokener ) {
 2554  0
          return _fromJSONTokener( (JSONTokener) value, jsonConfig );
 2555  0
       }
 2556  0
       return super._processValue( value, jsonConfig );
 2557  0
    }
 2558  
 
 2559  0
    /**
 2560  
     * Put a key/value pair in the JSONObject.
 2561  
     *
 2562  0
     * @param key A key string.
 2563  
     * @param value An object which is the value. It should be of one of these
 2564  
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 2565  
     *        String, or the JSONNull object.
 2566  
     * @return this.
 2567  
     * @throws JSONException If the value is non-finite number or if the key is
 2568  
     *         null.
 2569  
     */
 2570  
    private JSONObject _setInternal( String key, Object value, JsonConfig jsonConfig ) {
 2571  
       verifyIsNull();
 2572  
       if( key == null ){
 2573  0
          throw new JSONException( "Null key." );
 2574  0
       }
 2575  
 
 2576  
       if( JSONUtils.isString( value ) && JSONUtils.mayBeJSON( String.valueOf( value ) ) ){
 2577  
          this.properties.put( key, value );
 2578  
       }else{
 2579  
          /*
 2580  
          Object jo = _processValue( value, jsonConfig );
 2581  
          if( CycleDetectionStrategy.IGNORE_PROPERTY_OBJ == jo
 2582  
                || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == jo ){
 2583  
             // do nothing
 2584  0
          }else{
 2585  
             this.properties.put( key, jo );
 2586  
          }
 2587  
          */
 2588  
          if( CycleDetectionStrategy.IGNORE_PROPERTY_OBJ == value
 2589  
                || CycleDetectionStrategy.IGNORE_PROPERTY_ARR == value ){
 2590  
             // do nothing
 2591  
          }else{
 2592  
             this.properties.put( key, value );
 2593  
          }
 2594  
       }
 2595  
 
 2596  
       return this;
 2597  0
    }
 2598  0
 
 2599  0
    private Object processValue( Object value, JsonConfig jsonConfig ) {
 2600  
       if( value != null ){
 2601  0
          JsonValueProcessor processor = jsonConfig.findJsonValueProcessor( value.getClass() );
 2602  0
          if( processor != null ){
 2603  0
             value = processor.processObjectValue( null, value, jsonConfig );
 2604  
             if( !JsonVerifier.isValidJsonValue( value ) ){
 2605  0
                throw new JSONException( "Value is not a valid JSON value. " + value );
 2606  
             }
 2607  
          }
 2608  
       }
 2609  
       return _processValue( value, jsonConfig );
 2610  
    }
 2611  
 
 2612  
    private Object processValue( String key, Object value, JsonConfig jsonConfig ) {
 2613  
       if( value != null ){
 2614  
          JsonValueProcessor processor = jsonConfig.findJsonValueProcessor( value.getClass(), key );
 2615  
          if( processor != null ){
 2616  
             value = processor.processObjectValue( null, value, jsonConfig );
 2617  
             if( !JsonVerifier.isValidJsonValue( value ) ){
 2618  
                throw new JSONException( "Value is not a valid JSON value. " + value );
 2619  
             }
 2620  
          }
 2621  0
       }
 2622  0
       return _processValue( value, jsonConfig );
 2623  
    }
 2624  
 
 2625  
    /**
 2626  0
     * Put a key/value pair in the JSONObject.
 2627  0
     *
 2628  
     * @param key A key string.
 2629  0
     * @param value An object which is the value. It should be of one of these
 2630  0
     *        types: Boolean, Double, Integer, JSONArray, JSONObject, Long,
 2631  0
     *        String, or the JSONNull object.
 2632  
     * @return this.
 2633  0
     * @throws JSONException If the value is non-finite number or if the key is
 2634  0
     *         null.
 2635  0
     */
 2636  0
    private JSONObject setInternal( String key, Object value, JsonConfig jsonConfig ) {
 2637  0
       return _setInternal( key, processValue( key, value, jsonConfig ), jsonConfig );
 2638  0
    }
 2639  0
 
 2640  0
    /**
 2641  0
     * Checks if this object is a "null" object.
 2642  
     */
 2643  
    private void verifyIsNull() {
 2644  
       if( isNullObject() ){
 2645  
          throw new JSONException( "null object" );
 2646  
       }
 2647  
    }
 2648  
 }