View Javadoc

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