/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.json;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import net.sf.ezmorph.Morpher;
import net.sf.ezmorph.object.IdentityObjectMorpher;
import net.sf.json.processors.JsonValueProcessor;
import net.sf.json.processors.JsonVerifier;
import net.sf.json.util.JSONTokener;
import net.sf.json.util.JSONUtils;
import org.apache.commons.lang.StringUtils;
/**
* A JSONArray is an ordered sequence of values. Its external text form is a
* string wrapped in square brackets with commas separating the values. The
* internal form is an object having <code>get</code> and <code>opt</code>
* methods for accessing the values by index, and <code>element</code> methods
* for adding or replacing values. The values can be any of these types:
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
* <code>Number</code>, <code>String</code>, or the
* <code>JSONNull object</code>.
* <p>
* The constructor can convert a JSON text into a Java object. The
* <code>toString</code> method converts to JSON text.
* <p>
* A <code>get</code> method returns a value if one can be found, and throws
* an exception if one cannot be found. An <code>opt</code> method returns a
* default value instead of throwing an exception, and so is useful for
* obtaining optional values.
* <p>
* The generic <code>get()</code> and <code>opt()</code> methods return an
* object which you can cast or query for type. There are also typed
* <code>get</code> and <code>opt</code> methods that do type checking and
* type coersion for you.
* <p>
* The texts produced by the <code>toString</code> methods strictly conform to
* JSON syntax rules. The constructors are more forgiving in the texts they will
* accept:
* <ul>
* <li>An extra <code>,</code> <small>(comma)</small> may appear just
* before the closing bracket.</li>
* <li>The <code>null</code> value will be inserted when there is
* <code>,</code> <small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code> <small>(single quote)</small>.</li>
* <li>Strings do not need to be quoted at all if they do not begin with a
* quote or single quote, and if they do not contain leading or trailing spaces,
* and if they do not contain any of these characters:
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
* if they are not the reserved words <code>true</code>, <code>false</code>,
* or <code>null</code>.</li>
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small>
* as well as by <code>,</code> <small>(comma)</small>.</li>
* <li>Numbers may have the <code>0-</code> <small>(octal)</small> or
* <code>0x-</code> <small>(hex)</small> prefix.</li>
* <li>Comments written in the slashshlash, slashstar, and hash conventions
* will be ignored.</li>
* </ul>
*
* @author JSON.org
*/
public final class JSONArray extends AbstractJSON implements JSON, List, Comparable {
/**
* Creates a JSONArray.<br>
* Inspects the object type to call the correct JSONArray factory method.
* Accepts JSON formatted strings, arrays and Collections.
*
* @param object
* @throws JSONException if the object can not be converted to a proper
* JSONArray.
*/
public static JSONArray fromObject( Object object ) {
return fromObject( object, new JsonConfig() );
}
/**
* Creates a JSONArray.<br>
* Inspects the object type to call the correct JSONArray factory method.
* Accepts JSON formatted strings, arrays and Collections.
*
* @param object
* @throws JSONException if the object can not be converted to a proper
* JSONArray.
*/
public static JSONArray fromObject( Object object, JsonConfig jsonConfig ) {
if( object instanceof JSONString ){
return _fromJSONString( (JSONString) object, jsonConfig );
}else if( object instanceof JSONArray ){
return _fromJSONArray( (JSONArray) object, jsonConfig );
}else if( object instanceof Collection ){
return _fromCollection( (Collection) object, jsonConfig );
}else if( object instanceof JSONTokener ){
return _fromJSONTokener( (JSONTokener) object, jsonConfig );
}else if( object instanceof String ){
return _fromString( (String) object, jsonConfig );
}else if( object != null && object.getClass()
.isArray() ){
Class type = object.getClass()
.getComponentType();
if( !type.isPrimitive() ){
return _fromArray( (Object[]) object, jsonConfig );
}else{
if( type == Boolean.TYPE ){
return _fromArray( (boolean[]) object, jsonConfig );
}else if( type == Byte.TYPE ){
return _fromArray( (byte[]) object, jsonConfig );
}else if( type == Short.TYPE ){
return _fromArray( (short[]) object, jsonConfig );
}else if( type == Integer.TYPE ){
return _fromArray( (int[]) object, jsonConfig );
}else if( type == Long.TYPE ){
return _fromArray( (long[]) object, jsonConfig );
}else if( type == Float.TYPE ){
return _fromArray( (float[]) object, jsonConfig );
}else if( type == Double.TYPE ){
return _fromArray( (double[]) object, jsonConfig );
}else if( type == Character.TYPE ){
return _fromArray( (char[]) object, jsonConfig );
}else{
throw new JSONException( "Unsupported type" );
}
}
}else if( JSONUtils.isBoolean( object ) || JSONUtils.isFunction( object )
|| JSONUtils.isNumber( object ) || JSONUtils.isNull( object )
|| JSONUtils.isString( object ) || object instanceof JSON ){
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray().element( object, jsonConfig );
fireElementAddedEvent( 0, jsonArray.get( 0 ), jsonConfig );
fireArrayStartEvent( jsonConfig );
return jsonArray;
}else if( JSONUtils.isObject( object ) ){
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray().element( JSONObject.fromObject( object, jsonConfig ) );
fireElementAddedEvent( 0, jsonArray.get( 0 ), jsonConfig );
fireArrayStartEvent( jsonConfig );
return jsonArray;
}else{
throw new JSONException( "Unsupported type" );
}
}
/**
* Returns the number of dimensions suited for a java array.
*/
public static int[] getDimensions( JSONArray jsonArray ) {
// short circuit for empty arrays
if( jsonArray == null || jsonArray.isEmpty() ){
return new int[] { 0 };
}
List dims = new ArrayList();
processArrayDimensions( jsonArray, dims, 0 );
int[] dimensions = new int[dims.size()];
int j = 0;
for( Iterator i = dims.iterator(); i.hasNext(); ){
dimensions[j++] = ((Integer) i.next()).intValue();
}
return dimensions;
}
/**
* Creates a java array from a JSONArray.
*/
public static Object toArray( JSONArray jsonArray ) {
return toArray( jsonArray, new JsonConfig() );
}
/**
* Creates a java array from a JSONArray.
*/
public static Object toArray( JSONArray jsonArray, Class objectClass ) {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setRootClass( objectClass );
return toArray( jsonArray, jsonConfig );
}
/**
* Creates a java array from a JSONArray.<br>
* Any attribute is a JSONObject and matches a key in the classMap, it will
* be converted to that target class.<br>
* The classMap has the following conventions:
* <ul>
* <li>Every key must be an String.</li>
* <li>Every value must be a Class.</li>
* <li>A key may be a regular expression.</li>
* </ul>
*/
public static Object toArray( JSONArray jsonArray, Class objectClass, Map classMap ) {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setRootClass( objectClass );
jsonConfig.setClassMap( classMap );
return toArray( jsonArray, jsonConfig );
}
/**
* Creates a java array from a JSONArray.<br>
*/
public static Object toArray( JSONArray jsonArray, JsonConfig jsonConfig ) {
Class objectClass = jsonConfig.getRootClass();
Map classMap = jsonConfig.getClassMap();
if( jsonArray.size() == 0 ){
return Array.newInstance( objectClass == null ? Object.class : objectClass, 0 );
}
int[] dimensions = JSONArray.getDimensions( jsonArray );
Object array = Array.newInstance( objectClass == null ? Object.class : objectClass,
dimensions );
int size = jsonArray.size();
for( int i = 0; i < size; i++ ){
Object value = jsonArray.get( i );
if( JSONUtils.isNull( value ) ){
Array.set( array, i, null );
}else{
Class type = value.getClass();
if( JSONArray.class.isAssignableFrom( type ) ){
Array.set( array, i, toArray( (JSONArray) value, objectClass, classMap ) );
}else if( String.class.isAssignableFrom( type )
|| Boolean.class.isAssignableFrom( type )
|| Character.class.isAssignableFrom( type )
|| JSONFunction.class.isAssignableFrom( type ) ){
if( objectClass != null && !objectClass.isAssignableFrom( type ) ){
value = JSONUtils.getMorpherRegistry()
.morph( objectClass, value );
}
Array.set( array, i, value );
}else if( JSONUtils.isNumber( type ) ){
if( objectClass != null
&& (Byte.class.isAssignableFrom( objectClass ) || Byte.TYPE.isAssignableFrom( objectClass )) ){
Array.set( array, i, Byte.valueOf( String.valueOf( value ) ) );
}else if( objectClass != null
&& (Short.class.isAssignableFrom( objectClass ) || Short.TYPE.isAssignableFrom( objectClass )) ){
Array.set( array, i, Short.valueOf( String.valueOf( value ) ) );
}else{
Array.set( array, i, value );
}
}else{
if( objectClass != null ){
JsonConfig jsc = jsonConfig.copy();
jsc.setRootClass( objectClass );
jsc.setClassMap( classMap );
Array.set( array, i, JSONObject.toBean( (JSONObject) value, jsc ) );
}else{
Array.set( array, i, JSONObject.toBean( (JSONObject) value ) );
}
}
}
}
return array;
}
/**
* Creates a java array from a JSONArray.<br>
*/
public static Object toArray( JSONArray jsonArray, Object root, JsonConfig jsonConfig ) {
Class objectClass = root.getClass();
if( jsonArray.size() == 0 ){
return Array.newInstance( objectClass, 0 );
}
int[] dimensions = JSONArray.getDimensions( jsonArray );
Object array = Array.newInstance( objectClass == null ? Object.class : objectClass,
dimensions );
int size = jsonArray.size();
for( int i = 0; i < size; i++ ){
Object value = jsonArray.get( i );
if( JSONUtils.isNull( value ) ){
Array.set( array, i, null );
}else{
Class type = value.getClass();
if( JSONArray.class.isAssignableFrom( type ) ){
Array.set( array, i, toArray( (JSONArray) value, root, jsonConfig ) );
}else if( String.class.isAssignableFrom( type )
|| Boolean.class.isAssignableFrom( type ) || JSONUtils.isNumber( type )
|| Character.class.isAssignableFrom( type )
|| JSONFunction.class.isAssignableFrom( type ) ){
if( objectClass != null && !objectClass.isAssignableFrom( type ) ){
value = JSONUtils.getMorpherRegistry()
.morph( objectClass, value );
}
Array.set( array, i, value );
}else{
try{
Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
.newInstance( root.getClass(), null );
Array.set( array, i, JSONObject.toBean( (JSONObject) value, newRoot, jsonConfig ) );
}catch( JSONException jsone ){
throw jsone;
}catch( Exception e ){
throw new JSONException( e );
}
}
}
}
return array;
}
/**
* Creates a Collection from a JSONArray.
*/
public static Collection toCollection( JSONArray jsonArray ) {
return toCollection( jsonArray, new JsonConfig() );
}
/**
* Creates a Collection from a JSONArray.
*/
public static Collection toCollection( JSONArray jsonArray, Class objectClass ) {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setRootClass( objectClass );
return toCollection( jsonArray, jsonConfig );
}
/**
* Returns a List or a Set taking generics into account.<br/> Contributed by
* [Matt Small @ WaveMaker].
*/
public static Collection toCollection( JSONArray jsonArray, JsonConfig jsonConfig ) {
Collection collection = null;
Class collectionType = jsonConfig.getCollectionType();
if( collectionType.isInterface() ){
if( collectionType.equals( List.class ) ){
collection = new ArrayList();
}else if( collectionType.equals( Set.class ) ){
collection = new HashSet();
}else{
throw new JSONException( "unknown interface: " + collectionType );
}
}else{
try{
collection = (Collection) collectionType.newInstance();
}catch( InstantiationException e ){
throw new JSONException( e );
}catch( IllegalAccessException e ){
throw new JSONException( e );
}
}
Class objectClass = jsonConfig.getRootClass();
Map classMap = jsonConfig.getClassMap();
int size = jsonArray.size();
for( int i = 0; i < size; i++ ){
Object value = jsonArray.get( i );
if( JSONUtils.isNull( value ) ){
collection.add( null );
}else{
Class type = value.getClass();
if( JSONArray.class.isAssignableFrom( value.getClass() ) ){
collection.add( toCollection( (JSONArray) value, jsonConfig ) );
}else if( String.class.isAssignableFrom( type )
|| Boolean.class.isAssignableFrom( type ) || JSONUtils.isNumber( type )
|| Character.class.isAssignableFrom( type )
|| JSONFunction.class.isAssignableFrom( type ) ){
if( objectClass != null && !objectClass.isAssignableFrom( type ) ){
value = JSONUtils.getMorpherRegistry()
.morph( objectClass, value );
}
collection.add( value );
}else{
if( objectClass != null ){
JsonConfig jsc = jsonConfig.copy();
jsc.setRootClass( objectClass );
jsc.setClassMap( classMap );
collection.add( JSONObject.toBean( (JSONObject) value, jsc ) );
}else{
collection.add( JSONObject.toBean( (JSONObject) value ) );
}
}
}
}
return collection;
}
/**
* Creates a List from a JSONArray.<br>
*
* @deprecated replaced by toCollection
* @see #toCollection(JSONArray)
*/
public static List toList( JSONArray jsonArray ) {
return toList( jsonArray, new JsonConfig() );
}
/**
* Creates a List from a JSONArray.
*
* @deprecated replaced by toCollection
* @see #toCollection(JSONArray,Class)
*/
public static List toList( JSONArray jsonArray, Class objectClass ) {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setRootClass( objectClass );
return toList( jsonArray, jsonConfig );
}
/**
* Creates a List from a JSONArray.<br>
* Any attribute is a JSONObject and matches a key in the classMap, it will
* be converted to that target class.<br>
* The classMap has the following conventions:
* <ul>
* <li>Every key must be an String.</li>
* <li>Every value must be a Class.</li>
* <li>A key may be a regular expression.</li>
* </ul>
*
* @deprecated replaced by toCollection
* @see #toCollection(JSONArray,Class,Map)
*/
public static List toList( JSONArray jsonArray, Class objectClass, Map classMap ) {
JsonConfig jsonConfig = new JsonConfig();
jsonConfig.setRootClass( objectClass );
jsonConfig.setClassMap( classMap );
return toList( jsonArray, jsonConfig );
}
/**
* Creates a List from a JSONArray.<br>
*
* @deprecated replaced by toCollection
* @see #toCollection(JSONArray,JsonConfig)
*/
public static List toList( JSONArray jsonArray, JsonConfig jsonConfig ) {
if( jsonArray.size() == 0 ){
return new ArrayList();
}
Class objectClass = jsonConfig.getRootClass();
Map classMap = jsonConfig.getClassMap();
List list = new ArrayList();
int size = jsonArray.size();
for( int i = 0; i < size; i++ ){
Object value = jsonArray.get( i );
if( JSONUtils.isNull( value ) ){
list.add( null );
}else{
Class type = value.getClass();
if( JSONArray.class.isAssignableFrom( type ) ){
list.add( toList( (JSONArray) value, objectClass, classMap ) );
}else if( String.class.isAssignableFrom( type )
|| Boolean.class.isAssignableFrom( type ) || JSONUtils.isNumber( type )
|| Character.class.isAssignableFrom( type )
|| JSONFunction.class.isAssignableFrom( type ) ){
if( objectClass != null && !objectClass.isAssignableFrom( type ) ){
value = JSONUtils.getMorpherRegistry()
.morph( objectClass, value );
}
list.add( value );
}else{
if( objectClass != null ){
JsonConfig jsc = jsonConfig.copy();
jsc.setRootClass( objectClass );
jsc.setClassMap( classMap );
list.add( JSONObject.toBean( (JSONObject) value, jsc ) );
}else{
list.add( JSONObject.toBean( (JSONObject) value ) );
}
}
}
}
return list;
}
/**
* Creates a List from a JSONArray.<br>
*/
public static List toList( JSONArray jsonArray, Object root, JsonConfig jsonConfig ) {
if( jsonArray.size() == 0 || root == null ){
return new ArrayList();
}
List list = new ArrayList();
int size = jsonArray.size();
for( int i = 0; i < size; i++ ){
Object value = jsonArray.get( i );
if( JSONUtils.isNull( value ) ){
list.add( null );
}else{
Class type = value.getClass();
if( JSONArray.class.isAssignableFrom( type ) ){
list.add( toList( (JSONArray) value, root, jsonConfig ) );
}else if( String.class.isAssignableFrom( type )
|| Boolean.class.isAssignableFrom( type ) || JSONUtils.isNumber( type )
|| Character.class.isAssignableFrom( type )
|| JSONFunction.class.isAssignableFrom( type ) ){
list.add( value );
}else{
try{
Object newRoot = jsonConfig.getNewBeanInstanceStrategy()
.newInstance( root.getClass(), null );
list.add( JSONObject.toBean( (JSONObject) value, newRoot, jsonConfig ) );
}catch( JSONException jsone ){
throw jsone;
}catch( Exception e ){
throw new JSONException( e );
}
}
}
}
return list;
}
/**
* Construct a JSONArray from an boolean[].<br>
*
* @param array An boolean[] array.
*/
private static JSONArray _fromArray( boolean[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Boolean b = array[i] ? Boolean.TRUE : Boolean.FALSE;
jsonArray.addValue( b, jsonConfig );
fireElementAddedEvent( i, b, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an byte[].<br>
*
* @param array An byte[] array.
*/
private static JSONArray _fromArray( byte[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Number n = JSONUtils.transformNumber( new Byte( array[i] ) );
jsonArray.addValue( n, jsonConfig );
fireElementAddedEvent( i, n, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an char[].<br>
*
* @param array An char[] array.
*/
private static JSONArray _fromArray( char[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Character c = new Character( array[i] );
jsonArray.addValue( c, jsonConfig );
fireElementAddedEvent( i, c, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an double[].<br>
*
* @param array An double[] array.
*/
private static JSONArray _fromArray( double[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
try{
for( int i = 0; i < array.length; i++ ){
Double d = new Double( array[i] );
JSONUtils.testValidity( d );
jsonArray.addValue( d, jsonConfig );
fireElementAddedEvent( i, d, jsonConfig );
}
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an float[].<br>
*
* @param array An float[] array.
*/
private static JSONArray _fromArray( float[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
try{
for( int i = 0; i < array.length; i++ ){
Float f = new Float( array[i] );
JSONUtils.testValidity( f );
jsonArray.addValue( f, jsonConfig );
fireElementAddedEvent( i, f, jsonConfig );
}
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an int[].<br>
*
* @param array An int[] array.
*/
private static JSONArray _fromArray( int[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Number n = new Integer( array[i] );
jsonArray.addValue( n, jsonConfig );
fireElementAddedEvent( i, n, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an long[].<br>
*
* @param array An long[] array.
*/
private static JSONArray _fromArray( long[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Number n = JSONUtils.transformNumber( new Long( array[i] ) );
jsonArray.addValue( n, jsonConfig );
fireElementAddedEvent( i, n, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
// ------------------------------------------------------
private static JSONArray _fromArray( Object[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
try{
for( int i = 0; i < array.length; i++ ){
Object element = array[i];
jsonArray.addValue( element, jsonConfig );
fireElementAddedEvent( i, jsonArray.get( i ), jsonConfig );
}
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
/**
* Construct a JSONArray from an short[].<br>
*
* @param array An short[] array.
*/
private static JSONArray _fromArray( short[] array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
for( int i = 0; i < array.length; i++ ){
Number n = JSONUtils.transformNumber( new Short( array[i] ) );
jsonArray.addValue( n, jsonConfig );
fireElementAddedEvent( i, n, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
private static JSONArray _fromCollection( Collection collection, JsonConfig jsonConfig ) {
if( !addInstance( collection ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( collection );
}catch( JSONException jsone ){
removeInstance( collection );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( collection );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
try{
int i = 0;
for( Iterator elements = collection.iterator(); elements.hasNext(); ){
Object element = elements.next();
jsonArray.addValue( element, jsonConfig );
fireElementAddedEvent( i, jsonArray.get( i++ ), jsonConfig );
}
}catch( JSONException jsone ){
removeInstance( collection );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( collection );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
removeInstance( collection );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
private static JSONArray _fromJSONArray( JSONArray array, JsonConfig jsonConfig ) {
if( !addInstance( array ) ){
try{
return jsonConfig.getCycleDetectionStrategy()
.handleRepeatedReferenceAsArray( array );
}catch( JSONException jsone ){
removeInstance( array );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}catch( RuntimeException e ){
removeInstance( array );
JSONException jsone = new JSONException( e );
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
fireArrayStartEvent( jsonConfig );
JSONArray jsonArray = new JSONArray();
int index = 0;
for( Iterator elements = array.iterator(); elements.hasNext(); ){
Object element = elements.next();
jsonArray.addValue( element, jsonConfig );
fireElementAddedEvent( index++, element, jsonConfig );
}
removeInstance( array );
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
private static JSONArray _fromJSONString( JSONString string, JsonConfig jsonConfig ) {
return _fromJSONTokener( new JSONTokener( string.toJSONString() ), jsonConfig );
}
private static JSONArray _fromJSONTokener( JSONTokener tokener, JsonConfig jsonConfig ) {
JSONArray jsonArray = new JSONArray();
int index = 0;
try{
if( tokener.nextClean() != '[' ){
throw tokener.syntaxError( "A JSONArray text must start with '['" );
}
fireArrayStartEvent( jsonConfig );
if( tokener.nextClean() == ']' ){
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
tokener.back();
for( ;; ){
if( tokener.nextClean() == ',' ){
tokener.back();
jsonArray.elements.add( JSONNull.getInstance() );
fireElementAddedEvent( index, jsonArray.get( index++ ), jsonConfig );
}else{
tokener.back();
Object v = tokener.nextValue( jsonConfig );
if( !JSONUtils.isFunctionHeader( v ) ){
if( v instanceof String && JSONUtils.mayBeJSON( (String) v ) ){
jsonArray.addValue( JSONUtils.DOUBLE_QUOTE + v + JSONUtils.DOUBLE_QUOTE,
jsonConfig );
}else{
jsonArray.addValue( v, jsonConfig );
}
fireElementAddedEvent( index, jsonArray.get( index++ ), jsonConfig );
}else{
// read params if any
String params = JSONUtils.getFunctionParams( (String) v );
// read function text
int i = 0;
StringBuffer sb = new StringBuffer();
for( ;; ){
char ch = tokener.next();
if( ch == 0 ){
break;
}
if( ch == '{' ){
i++;
}
if( ch == '}' ){
i--;
}
sb.append( ch );
if( i == 0 ){
break;
}
}
if( i != 0 ){
throw tokener.syntaxError( "Unbalanced '{' or '}' on prop: " + v );
}
// trim '{' at start and '}' at end
String text = sb.toString();
text = text.substring( 1, text.length() - 1 )
.trim();
jsonArray.addValue( new JSONFunction( (params != null) ? StringUtils.split(
params, "," ) : null, text ), jsonConfig );
fireElementAddedEvent( index, jsonArray.get( index++ ), jsonConfig );
}
}
switch( tokener.nextClean() ){
case ';':
case ',':
if( tokener.nextClean() == ']' ){
fireArrayEndEvent( jsonConfig );
return jsonArray;
}
tokener.back();
break;
case ']':
fireArrayEndEvent( jsonConfig );
return jsonArray;
default:
throw tokener.syntaxError( "Expected a ',' or ']'" );
}
}
}catch( JSONException jsone ){
fireErrorEvent( jsone, jsonConfig );
throw jsone;
}
}
private static JSONArray _fromString( String string, JsonConfig jsonConfig ) {
return _fromJSONTokener( new JSONTokener( string ), jsonConfig );
}
private static void processArrayDimensions( JSONArray jsonArray, List dims, int index ) {
if( dims.size() <= index ){
dims.add( new Integer( jsonArray.size() ) );
}else{
int i = ((Integer) dims.get( index )).intValue();
if( jsonArray.size() > i ){
dims.set( index, new Integer( jsonArray.size() ) );
}
}
for( Iterator i = jsonArray.iterator(); i.hasNext(); ){
Object item = i.next();
if( item instanceof JSONArray ){
processArrayDimensions( (JSONArray) item, dims, index + 1 );
}
}
}
// ------------------------------------------------------
/**
* The List where the JSONArray's properties are kept.
*/
private List elements;
/**
* A flag for XML processing.
*/
private boolean expandElements;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
this.elements = new ArrayList();
}
public void add( int index, Object value ) {
add( index, value, new JsonConfig() );
}
public void add( int index, Object value, JsonConfig jsonConfig ) {
this.elements.add( index, processValue( value, jsonConfig ) );
}
public boolean add( Object value ) {
return add( value, new JsonConfig() );
}
public boolean add( Object value, JsonConfig jsonConfig ) {
element( value, jsonConfig );
return true;
}
public boolean addAll( Collection collection ) {
return addAll( collection, new JsonConfig() );
}
public boolean addAll( Collection collection, JsonConfig jsonConfig ) {
if( collection == null || collection.size() == 0 ){
return false;
}
for( Iterator i = collection.iterator(); i.hasNext(); ){
element( i.next(), jsonConfig );
}
return true;
}
public boolean addAll( int index, Collection collection ) {
return addAll( index, collection, new JsonConfig() );
}
public boolean addAll( int index, Collection collection, JsonConfig jsonConfig ) {
if( collection == null || collection.size() == 0 ){
return false;
}
int offset = 0;
for( Iterator i = collection.iterator(); i.hasNext(); ){
this.elements.add( index + (offset++), processValue( i.next(), jsonConfig ) );
}
return true;
}
public void clear() {
elements.clear();
}
public int compareTo( Object obj ) {
if( obj != null && (obj instanceof JSONArray) ){
JSONArray other = (JSONArray) obj;
int size1 = size();
int size2 = other.size();
if( size1 < size2 ){
return -1;
}else if( size1 > size2 ){
return 1;
}else if( this.equals( other ) ){
return 0;
}
}
return -1;
}
public boolean contains( Object o ) {
return contains( o, new JsonConfig() );
}
public boolean contains( Object o, JsonConfig jsonConfig ) {
return elements.contains( processValue( o, jsonConfig ) );
}
public boolean containsAll( Collection collection ) {
return containsAll( collection, new JsonConfig() );
}
public boolean containsAll( Collection collection, JsonConfig jsonConfig ) {
return elements.containsAll( fromObject( collection, jsonConfig ) );
}
/**
* Remove an element, if present.
*
* @param index the index of the element.
* @return this.
*/
public JSONArray discard( int index ) {
elements.remove( index );
return this;
}
/**
* Remove an element, if present.
*
* @param index the element.
* @return this.
*/
public JSONArray discard( Object o ) {
elements.remove( o );
return this;
}
/**
* Append a boolean value. This increases the array's length by one.
*
* @param value A boolean value.
* @return this.
*/
public JSONArray element( boolean value ) {
return element( value ? Boolean.TRUE : Boolean.FALSE );
}
/**
* Append a value in the JSONArray, where the value will be a JSONArray which
* is produced from a Collection.
*
* @param value A Collection value.
* @return this.
*/
public JSONArray element( Collection value ) {
return element( value, new JsonConfig() );
}
/**
* Append a value in the JSONArray, where the value will be a JSONArray which
* is produced from a Collection.
*
* @param value A Collection value.
* @return this.
*/
public JSONArray element( Collection value, JsonConfig jsonConfig ) {
if( value instanceof JSONArray ){
elements.add( value );
return this;
}else{
return element( _fromCollection( value, jsonConfig ) );
}
}
/**
* Append a double value. This increases the array's length by one.
*
* @param value A double value.
* @throws JSONException if the value is not finite.
* @return this.
*/
public JSONArray element( double value ) {
Double d = new Double( value );
JSONUtils.testValidity( d );
return element( d );
}
/**
* Append an int value. This increases the array's length by one.
*
* @param value An int value.
* @return this.
*/
public JSONArray element( int value ) {
return element( new Integer( value ) );
}
/**
* Put or replace a boolean value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value A boolean value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray element( int index, boolean value ) {
return element( index, value ? Boolean.TRUE : Boolean.FALSE );
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which is
* produced from a Collection.
*
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is not
* finite.
*/
public JSONArray element( int index, Collection value ) {
return element( index, value, new JsonConfig() );
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which is
* produced from a Collection.
*
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is not
* finite.
*/
public JSONArray element( int index, Collection value, JsonConfig jsonConfig ) {
if( value instanceof JSONArray ){
if( index < 0 ){
throw new JSONException( "JSONArray[" + index + "] not found." );
}
if( index < size() ){
elements.set( index, value );
}else{
while( index != size() ){
element( JSONNull.getInstance() );
}
element( value, jsonConfig );
}
return this;
}else{
return element( index, _fromCollection( value, jsonConfig ) );
}
}
/**
* Put or replace a double value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value A double value.
* @return this.
* @throws JSONException If the index is negative or if the value is not
* finite.
*/
public JSONArray element( int index, double value ) {
return element( index, new Double( value ) );
}
/**
* Put or replace an int value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value An int value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray element( int index, int value ) {
return element( index, new Integer( value ) );
}
/**
* Put or replace a long value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value A long value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray element( int index, long value ) {
return element( index, new Long( value ) );
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which
* is produced from a Map.
*
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, Map value ) {
return element( index, value, new JsonConfig() );
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which
* is produced from a Map.
*
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, Map value, JsonConfig jsonConfig ) {
if( value instanceof JSONObject ){
if( index < 0 ){
throw new JSONException( "JSONArray[" + index + "] not found." );
}
if( index < size() ){
elements.set( index, value );
}else{
while( index != size() ){
element( JSONNull.getInstance() );
}
element( value, jsonConfig );
}
return this;
}else{
return element( index, JSONObject.fromObject( value, jsonConfig ) );
}
}
/**
* Put or replace an object value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, Object value ) {
return element( index, value, new JsonConfig() );
}
/**
* Put or replace an object value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, Object value, JsonConfig jsonConfig ) {
JSONUtils.testValidity( value );
if( index < 0 ){
throw new JSONException( "JSONArray[" + index + "] not found." );
}
if( index < size() ){
this.elements.set( index, processValue( value, jsonConfig ) );
}else{
while( index != size() ){
element( JSONNull.getInstance() );
}
element( value, jsonConfig );
}
return this;
}
/**
* Put or replace a String value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.<br>
* The string may be a valid JSON formatted string, in tha case, it will be
* transformed to a JSONArray, JSONObject or JSONNull.
*
* @param index The subscript.
* @param value A String value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, String value ) {
return element( index, value, new JsonConfig() );
}
/**
* Put or replace a String value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.<br>
* The string may be a valid JSON formatted string, in tha case, it will be
* transformed to a JSONArray, JSONObject or JSONNull.
*
* @param index The subscript.
* @param value A String value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray element( int index, String value, JsonConfig jsonConfig ) {
if( index < 0 ){
throw new JSONException( "JSONArray[" + index + "] not found." );
}
if( index < size() ){
if( value == null ){
this.elements.set( index, "" );
}else if( JSONUtils.mayBeJSON( value ) ){
try{
this.elements.set( index, JSONSerializer.toJSON( value, jsonConfig ) );
}catch( JSONException jsone ){
this.elements.set( index, JSONUtils.stripQuotes( value ) );
}
}else{
this.elements.set( index, JSONUtils.stripQuotes( value ) );
}
}else{
while( index != size() ){
element( JSONNull.getInstance() );
}
element( value, jsonConfig );
}
return this;
}
/**
* Append an JSON value. This increases the array's length by one.
*
* @param value An JSON value.
* @return this.
*/
public JSONArray element( JSONNull value ) {
this.elements.add( value );
return this;
}
/**
* Append an JSON value. This increases the array's length by one.
*
* @param value An JSON value.
* @return this.
*/
public JSONArray element( JSONObject value ) {
this.elements.add( value );
return this;
}
/**
* Append an long value. This increases the array's length by one.
*
* @param value A long value.
* @return this.
*/
public JSONArray element( long value ) {
return element( JSONUtils.transformNumber( new Long( value ) ) );
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which
* is produced from a Map.
*
* @param value A Map value.
* @return this.
*/
public JSONArray element( Map value ) {
return element( value, new JsonConfig() );
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which
* is produced from a Map.
*
* @param value A Map value.
* @return this.
*/
public JSONArray element( Map value, JsonConfig jsonConfig ) {
if( value instanceof JSONObject ){
elements.add( value );
return this;
}else{
return element( JSONObject.fromObject( value, jsonConfig ) );
}
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
*/
public JSONArray element( Object value ) {
return element( value, new JsonConfig() );
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
*/
public JSONArray element( Object value, JsonConfig jsonConfig ) {
return addValue( value, jsonConfig );
}
/**
* Append a String value. This increases the array's length by one.<br>
* The string may be a valid JSON formatted string, in tha case, it will be
* transformed to a JSONArray, JSONObject or JSONNull.
*
* @param value A String value.
* @return this.
*/
public JSONArray element( String value ) {
return element( value, new JsonConfig() );
}
/**
* Append a String value. This increases the array's length by one.<br>
* The string may be a valid JSON formatted string, in tha case, it will be
* transformed to a JSONArray, JSONObject or JSONNull.
*
* @param value A String value.
* @return this.
*/
public JSONArray element( String value, JsonConfig jsonConfig ) {
if( value == null ) {
this.elements.add("");
} else if( JSONUtils.hasQuotes( value )) {
this.elements.add(value);
} else if( JSONNull.getInstance().equals( value )) {
this.elements.add( JSONNull.getInstance() );
} else if( JSONUtils.isJsonKeyword(value,jsonConfig)) {
if( jsonConfig.isJavascriptCompliant() && "undefined".equals( value )){
this.elements.add( JSONNull.getInstance() );
}else{
this.elements.add(value);
}
} else if( JSONUtils.mayBeJSON( value ) ){
try{
this.elements.add( JSONSerializer.toJSON( value, jsonConfig ) );
}catch( JSONException jsone ){
this.elements.add( value );
}
} else {
this.elements.add(value);
}
return this;
}
public boolean equals( Object obj ) {
if( obj == this ){
return true;
}
if( obj == null ){
return false;
}
if( !(obj instanceof JSONArray) ){
return false;
}
JSONArray other = (JSONArray) obj;
if( other.size() != size() ){
return false;
}
int max = size();
for( int i = 0; i < max; i++ ){
Object o1 = get( i );
Object o2 = other.get( i );
// handle nulls
if( JSONNull.getInstance()
.equals( o1 ) ){
if( JSONNull.getInstance()
.equals( o2 ) ){
continue;
}else{
return false;
}
}else{
if( JSONNull.getInstance()
.equals( o2 ) ){
return false;
}
}
if( o1 instanceof JSONArray && o2 instanceof JSONArray ){
JSONArray e = (JSONArray) o1;
JSONArray a = (JSONArray) o2;
if( !a.equals( e ) ){
return false;
}
}else{
if( o1 instanceof String && o2 instanceof JSONFunction ){
if( !o1.equals( String.valueOf( o2 ) ) ){
return false;
}
}else if( o1 instanceof JSONFunction && o2 instanceof String ){
if( !o2.equals( String.valueOf( o1 ) ) ){
return false;
}
}else if( o1 instanceof JSONObject && o2 instanceof JSONObject ){
if( !o1.equals( o2 ) ){
return false;
}
}else if( o1 instanceof JSONArray && o2 instanceof JSONArray ){
if( !o1.equals( o2 ) ){
return false;
}
}else if( o1 instanceof JSONFunction && o2 instanceof JSONFunction ){
if( !o1.equals( o2 ) ){
return false;
}
}else{
if( o1 instanceof String ){
if( !o1.equals( String.valueOf( o2 ) ) ){
return false;
}
}else if( o2 instanceof String ){
if( !o2.equals( String.valueOf( o1 ) ) ){
return false;
}
}else{
Morpher m1 = JSONUtils.getMorpherRegistry()
.getMorpherFor( o1.getClass() );
Morpher m2 = JSONUtils.getMorpherRegistry()
.getMorpherFor( o2.getClass() );
if( m1 != null && m1 != IdentityObjectMorpher.getInstance() ){
if( !o1.equals( JSONUtils.getMorpherRegistry()
.morph( o1.getClass(), o2 ) ) ){
return false;
}
}else if( m2 != null && m2 != IdentityObjectMorpher.getInstance() ){
if( !JSONUtils.getMorpherRegistry()
.morph( o1.getClass(), o1 )
.equals( o2 ) ){
return false;
}
}else{
if( !o1.equals( o2 ) ){
return false;
}
}
}
}
}
}
return true;
}
/**
* Get the object value associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return An object value.
*/
public Object get( int index ) {
/*
* Object o = opt( index ); if( o == null ){ throw new JSONException(
* "JSONArray[" + index + "] not found." ); } return o;
*/
return this.elements.get( index );
}
/**
* Get the boolean value associated with an index. The string values "true"
* and "false" are converted to boolean.
*
* @param index The index must be between 0 and size() - 1.
* @return The truth.
* @throws JSONException If there is no value for the index or if the value
* is not convertable to boolean.
*/
public boolean getBoolean( int index ) {
Object o = get( index );
if( o != null ){
if( o.equals( Boolean.FALSE )
|| (o instanceof String && ((String) o).equalsIgnoreCase( "false" )) ){
return false;
}else if( o.equals( Boolean.TRUE )
|| (o instanceof String && ((String) o).equalsIgnoreCase( "true" )) ){
return true;
}
}
throw new JSONException( "JSONArray[" + index + "] is not a Boolean." );
}
/**
* Get the double value associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be
* converted to a number.
*/
public double getDouble( int index ) {
Object o = get( index );
if( o != null ){
try{
return o instanceof Number ? ((Number) o).doubleValue()
: Double.parseDouble( (String) o );
}catch( Exception e ){
throw new JSONException( "JSONArray[" + index + "] is not a number." );
}
}
throw new JSONException( "JSONArray[" + index + "] is not a number." );
}
/**
* Get the int value associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be
* converted to a number. if the value cannot be converted to a
* number.
*/
public int getInt( int index ) {
Object o = get( index );
if( o != null ){
return o instanceof Number ? ((Number) o).intValue() : (int) getDouble( index );
}
throw new JSONException( "JSONArray[" + index + "] is not a number." );
}
/**
* Get the JSONArray associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return A JSONArray value.
* @throws JSONException If there is no value for the index. or if the value
* is not a JSONArray
*/
public JSONArray getJSONArray( int index ) {
Object o = get( index );
if( o != null && o instanceof JSONArray ){
return (JSONArray) o;
}
throw new JSONException( "JSONArray[" + index + "] is not a JSONArray." );
}
/**
* Get the JSONObject associated with an index.
*
* @param index subscript
* @return A JSONObject value.
* @throws JSONException If there is no value for the index or if the value
* is not a JSONObject
*/
public JSONObject getJSONObject( int index ) {
Object o = get( index );
if( JSONNull.getInstance()
.equals( o ) ){
return new JSONObject( true );
}else if( o instanceof JSONObject ){
return (JSONObject) o;
}
throw new JSONException( "JSONArray[" + index + "] is not a JSONObject." );
}
/**
* Get the long value associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be
* converted to a number.
*/
public long getLong( int index ) {
Object o = get( index );
if( o != null ){
return o instanceof Number ? ((Number) o).longValue() : (long) getDouble( index );
}
throw new JSONException( "JSONArray[" + index + "] is not a number." );
}
/**
* Get the string associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return A string value.
* @throws JSONException If there is no value for the index.
*/
public String getString( int index ) {
Object o = get( index );
if( o != null ){
return o.toString();
}
throw new JSONException( "JSONArray[" + index + "] not found." );
}
public int hashCode() {
int hashcode = 29;
for( Iterator e = elements.iterator(); e.hasNext(); ){
Object element = e.next();
hashcode += JSONUtils.hashCode( element );
}
return hashcode;
}
public int indexOf( Object o ) {
return elements.indexOf( o );
}
public boolean isArray() {
return true;
}
public boolean isEmpty() {
return this.elements.isEmpty();
}
public boolean isExpandElements() {
return expandElements;
}
/**
* Returns an Iterator for this JSONArray
*/
public Iterator iterator() {
return new JSONArrayListIterator();
}
/**
* Make a string from the contents of this JSONArray. The
* <code>separator</code> string is inserted between each element. Warning:
* This method assumes that the data structure is acyclical.
*
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join( String separator ) {
return join( separator, false );
}
/**
* Make a string from the contents of this JSONArray. The
* <code>separator</code> string is inserted between each element. Warning:
* This method assumes that the data structure is acyclical.
*
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join( String separator, boolean stripQuotes ) {
int len = size();
StringBuffer sb = new StringBuffer();
for( int i = 0; i < len; i += 1 ){
if( i > 0 ){
sb.append( separator );
}
String value = JSONUtils.valueToString( this.elements.get( i ) );
sb.append( stripQuotes ? JSONUtils.stripQuotes( value ) : value );
}
return sb.toString();
}
public int lastIndexOf( Object o ) {
return elements.lastIndexOf( o );
}
public ListIterator listIterator() {
return listIterator( 0 );
}
public ListIterator listIterator( int index ) {
if( index < 0 || index > size() )
throw new IndexOutOfBoundsException( "Index: " + index );
return new JSONArrayListIterator( index );
}
/**
* Get the optional object value associated with an index.
*
* @param index The index must be between 0 and size() - 1.
* @return An object value, or null if there is no object at that index.
*/
public Object opt( int index ) {
return (index < 0 || index >= size()) ? null : this.elements.get( index );
}
/**
* Get the optional boolean value associated with an index. It returns false
* if there is no value at that index, or if the value is not Boolean.TRUE or
* the String "true".
*
* @param index The index must be between 0 and size() - 1.
* @return The truth.
*/
public boolean optBoolean( int index ) {
return optBoolean( index, false );
}
/**
* Get the optional boolean value associated with an index. It returns the
* defaultValue if there is no value at that index or if it is not a Boolean
* or the String "true" or "false" (case insensitive).
*
* @param index The index must be between 0 and size() - 1.
* @param defaultValue A boolean default.
* @return The truth.
*/
public boolean optBoolean( int index, boolean defaultValue ) {
try{
return getBoolean( index );
}catch( Exception e ){
return defaultValue;
}
}
/**
* Get the optional double value associated with an index. NaN is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
*/
public double optDouble( int index ) {
return optDouble( index, Double.NaN );
}
/**
* Get the optional double value associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index subscript
* @param defaultValue The default value.
* @return The value.
*/
public double optDouble( int index, double defaultValue ) {
try{
return getDouble( index );
}catch( Exception e ){
return defaultValue;
}
}
/**
* Get the optional int value associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
*/
public int optInt( int index ) {
return optInt( index, 0 );
}
/**
* Get the optional int value associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index The index must be between 0 and size() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public int optInt( int index, int defaultValue ) {
try{
return getInt( index );
}catch( Exception e ){
return defaultValue;
}
}
/**
* Get the optional JSONArray associated with an index.
*
* @param index subscript
* @return A JSONArray value, or null if the index has no value, or if the
* value is not a JSONArray.
*/
public JSONArray optJSONArray( int index ) {
Object o = opt( index );
return o instanceof JSONArray ? (JSONArray) o : null;
}
/**
* Get the optional JSONObject associated with an index. Null is returned if
* the key is not found, or null if the index has no value, or if the value
* is not a JSONObject.
*
* @param index The index must be between 0 and size() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject( int index ) {
Object o = opt( index );
return o instanceof JSONObject ? (JSONObject) o : null;
}
/**
* Get the optional long value associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and size() - 1.
* @return The value.
*/
public long optLong( int index ) {
return optLong( index, 0 );
}
/**
* Get the optional long value associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index The index must be between 0 and size() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public long optLong( int index, long defaultValue ) {
try{
return getLong( index );
}catch( Exception e ){
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an
* empty string if there is no value at that index. If the value is not a
* string and is not null, then it is coverted to a string.
*
* @param index The index must be between 0 and size() - 1.
* @return A String value.
*/
public String optString( int index ) {
return optString( index, "" );
}
/**
* Get the optional string associated with an index. The defaultValue is
* returned if the key is not found.
*
* @param index The index must be between 0 and size() - 1.
* @param defaultValue The default value.
* @return A String value.
*/
public String optString( int index, String defaultValue ) {
Object o = opt( index );
return o != null ? o.toString() : defaultValue;
}
public Object remove( int index ) {
return elements.remove( index );
}
public boolean remove( Object o ) {
return elements.remove( o );
}
public boolean removeAll( Collection collection ) {
return removeAll( collection, new JsonConfig() );
}
public boolean removeAll( Collection collection, JsonConfig jsonConfig ) {
return elements.removeAll( fromObject( collection, jsonConfig ) );
}
public boolean retainAll( Collection collection ) {
return retainAll( collection, new JsonConfig() );
}
public boolean retainAll( Collection collection, JsonConfig jsonConfig ) {
return elements.retainAll( fromObject( collection, jsonConfig ) );
}
public Object set( int index, Object value ) {
return set( index, value, new JsonConfig() );
}
public Object set( int index, Object value, JsonConfig jsonConfig ) {
Object previous = get( index );
element( index, value, jsonConfig );
return previous;
}
public void setExpandElements( boolean expandElements ) {
this.expandElements = expandElements;
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int size() {
return this.elements.size();
}
public List subList( int fromIndex, int toIndex ) {
return elements.subList( fromIndex, toIndex );
}
/**
* Produce an Object[] with the contents of this JSONArray.
*/
public Object[] toArray() {
return this.elements.toArray();
}
public Object[] toArray( Object[] array ) {
return elements.toArray( array );
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values of
* this JSONArray.
*
* @param names A JSONArray containing a list of key strings. These will be
* paired with the values.
* @return A JSONObject, or null if there are no names or if this JSONArray
* has no values.
* @throws JSONException If any of the names are null.
*/
public JSONObject toJSONObject( JSONArray names ) {
if( names == null || names.size() == 0 || size() == 0 ){
return null;
}
JSONObject jo = new JSONObject();
for( int i = 0; i < names.size(); i++ ){
jo.element( names.getString( i ), this.opt( i ) );
}
return jo;
}
/**
* Make a JSON text of this JSONArray. For compactness, no unnecessary
* whitespace is added. If it is not possible to produce a syntactically
* correct JSON text then null will be returned instead. This could occur if
* the array contains an invalid number.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable representation of the
* array.
*/
public String toString() {
try{
return '[' + join( "," ) + ']';
}catch( Exception e ){
return null;
}
}
/**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method
* assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @return a printable, displayable, transmittable representation of the
* object, beginning with <code>[</code> <small>(left
* bracket)</small> and ending with <code>]</code> <small>(right
* bracket)</small>.
* @throws JSONException
*/
public String toString( int indentFactor ) {
if( indentFactor == 0 ){
return this.toString();
}
return toString( indentFactor, 0 );
}
/**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method
* assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @param indent The indention of the top level.
* @return a printable, displayable, transmittable representation of the
* array.
* @throws JSONException
*/
public String toString( int indentFactor, int indent ) {
int len = size();
if( len == 0 ){
return "[]";
}
if( indentFactor == 0 ){
return this.toString();
}
int i;
StringBuffer sb = new StringBuffer( "[" );
if( len == 1 ){
sb.append( JSONUtils.valueToString( this.elements.get( 0 ), indentFactor, indent ) );
}else{
int newindent = indent + indentFactor;
sb.append( '\n' );
for( i = 0; i < len; i += 1 ){
if( i > 0 ){
sb.append( ",\n" );
}
for( int j = 0; j < newindent; j += 1 ){
sb.append( ' ' );
}
sb.append( JSONUtils.valueToString( this.elements.get( i ), indentFactor, newindent ) );
}
sb.append( '\n' );
for( i = 0; i < indent; i += 1 ){
sb.append( ' ' );
}
for( i = 0; i < indent; i += 1 ){
sb.insert( 0, ' ' );
}
}
sb.append( ']' );
return sb.toString();
}
protected void write(Writer writer, WritingVisitor visitor) throws IOException {
try{
boolean b = false;
int len = size();
writer.write( '[' );
for( int i = 0; i < len; i += 1 ){
if( b ){
writer.write( ',' );
}
Object v = this.elements.get( i );
if( v instanceof JSON ){
visitor.on( (JSON) v, writer );
}else{
visitor.on( v, writer );
}
b = true;
}
writer.write( ']' );
}catch( IOException e ){
throw new JSONException( e );
}
}
/**
* Adds a String without performing any conversion on it.
*/
protected JSONArray addString( String str ) {
if( str != null ){
elements.add( str );
}
return this;
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
*/
private JSONArray _addValue( Object value, JsonConfig jsonConfig ) {
this.elements.add(value);
return this;
}
protected Object _processValue( Object value, JsonConfig jsonConfig ) {
if( value instanceof JSONTokener ) {
return _fromJSONTokener( (JSONTokener) value, jsonConfig );
}
return super._processValue( value, jsonConfig );
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, JSONFunction, Long, String,
* JSONString or the JSONNull object.
* @return this.
*/
private JSONArray addValue( Object value, JsonConfig jsonConfig ) {
return _addValue( processValue( value, jsonConfig ), jsonConfig );
}
private Object processValue( Object value, JsonConfig jsonConfig ) {
if( value != null ){
JsonValueProcessor jsonValueProcessor = jsonConfig.findJsonValueProcessor( value.getClass() );
if( jsonValueProcessor != null ){
value = jsonValueProcessor.processArrayValue( value, jsonConfig );
if( !JsonVerifier.isValidJsonValue( value ) ){
throw new JSONException( "Value is not a valid JSON value. " + value );
}
}
}
return _processValue( value, jsonConfig );
}
private class JSONArrayListIterator implements ListIterator {
int currentIndex = 0;
int lastIndex = -1;
JSONArrayListIterator() {
}
JSONArrayListIterator( int index ) {
currentIndex = index;
}
public boolean hasNext() {
return currentIndex != size();
}
public Object next() {
try {
Object next = get( currentIndex );
lastIndex = currentIndex++;
return next;
} catch( IndexOutOfBoundsException e ) {
throw new NoSuchElementException();
}
}
public void remove() {
if( lastIndex == -1 )
throw new IllegalStateException();
try {
JSONArray.this.remove( lastIndex );
if( lastIndex < currentIndex ){
currentIndex--;
}
lastIndex = -1;
} catch( IndexOutOfBoundsException e ) {
throw new ConcurrentModificationException();
}
}
public boolean hasPrevious() {
return currentIndex != 0;
}
public Object previous() {
try {
int index = currentIndex - 1;
Object previous = get( index );
lastIndex = currentIndex = index;
return previous;
} catch( IndexOutOfBoundsException e ) {
throw new NoSuchElementException();
}
}
public int nextIndex() {
return currentIndex;
}
public int previousIndex() {
return currentIndex - 1;
}
public void set( Object obj ) {
if( lastIndex == -1 ){
throw new IllegalStateException();
}
try {
JSONArray.this.set( lastIndex, obj );
} catch( IndexOutOfBoundsException ex ) {
throw new ConcurrentModificationException();
}
}
public void add( Object obj ) {
try {
JSONArray.this.add( currentIndex++, obj );
lastIndex = -1;
} catch( IndexOutOfBoundsException ex ) {
throw new ConcurrentModificationException();
}
}
}
}