Java tutorial
/* * 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 com.xwtec.xwserver.util.json; import java.beans.PropertyDescriptor; import java.io.IOException; import java.io.Writer; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; 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 org.apache.commons.lang.StringUtils; import com.xwtec.xwserver.util.json.processors.JsonValueProcessor; import com.xwtec.xwserver.util.json.processors.JsonVerifier; import com.xwtec.xwserver.util.json.util.JSONTokener; import com.xwtec.xwserver.util.json.util.JSONUtils; /** * 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 */ @SuppressWarnings({ "unchecked", "serial" }) 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, Collections and Enums. * * @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, Collections and Enums. * * @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 (object instanceof Enum) { return _fromArray((Enum) object, jsonConfig); } else if (object instanceof Annotation || (object != null && object.getClass().isAnnotation())) { throw new JSONException("Unsupported type"); } 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"); } } /** * Get the collection type from a getter or setter, or null if no type was * found.<br/> * Contributed by [Matt Small @ WaveMaker]. */ public static Class[] getCollectionType(PropertyDescriptor pd, boolean useGetter) throws JSONException { Type type; if (useGetter) { Method m = pd.getReadMethod(); type = m.getGenericReturnType(); } else { Method m = pd.getWriteMethod(); Type[] gpts = m.getGenericParameterTypes(); if (1 != gpts.length) { throw new JSONException("method " + m + " is not a standard setter"); } type = gpts[0]; } if (!(type instanceof ParameterizedType)) { return null; // throw new JSONException("type not instanceof ParameterizedType: // "+type.getClass()); } ParameterizedType pType = (ParameterizedType) type; Type[] actualTypes = pType.getActualTypeArguments(); Class[] ret = new Class[actualTypes.length]; for (int i = 0; i < ret.length; i++) { ret[i] = (Class) actualTypes[i]; } return ret; } /** * 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; } /** * Returns a List or a Set taking generics into account.<br/> */ public static Collection toCollection(JSONArray jsonArray) { return toCollection(jsonArray, new JsonConfig()); } /** * Returns a List or a Set taking generics into account.<br/> */ 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 (!value.getClass().isAssignableFrom(type)) { throw new JSONException("can't assign value " + value + " of type " + value.getClass() + " to Collection of type " + type); } 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; } /* public static Collection toCollection( JSONArray jsonArray, JsonConfig jsonConfig ) { Collection collection = null; Class collectionType = jsonConfig.getCollectionType(); Class enclosedType = jsonConfig.getEnclosedType(); 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 ); Class enclosedTypeE = enclosedType; if( null == enclosedTypeE ){ enclosedTypeE = value.getClass(); } if( JSONUtils.isNull( value ) ){ collection.add( null ); }else{ if( JSONArray.class.isAssignableFrom( value.getClass() ) ){ //throw new RuntimeException( "can't have nested collections" ); collection.add( toCollection( (JSONArray) value, jsonConfig ) ); }else if( String.class.isAssignableFrom( enclosedTypeE ) || Boolean.class.isAssignableFrom( enclosedTypeE ) || JSONUtils.isNumber( enclosedTypeE ) || Character.class.isAssignableFrom( enclosedTypeE ) || JSONFunction.class.isAssignableFrom( enclosedTypeE ) ){ if( !value.getClass() .isAssignableFrom( enclosedTypeE ) ){ throw new JSONException( "can't assign value " + value + " of type " + value.getClass() + " to Collection of type " + enclosedTypeE ); } collection.add( value ); }else{ try{ if( JSON.class.isAssignableFrom( enclosedTypeE ) ){ ret.add( JSONObject.toBean( (JSONObject) value ) ); }else{ Object newRoot = enclosedTypeE.newInstance(); ret.add( JSONObject.toBean( (JSONObject) value, newRoot, jsonConfig ) ); } }catch( JSONException jsone ){ throw jsone; }catch( Exception e ){ throw new JSONException( e ); } 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 Enum value. * * @param e A enum value. * @throws JSONException If there is a syntax error. */ private static JSONArray _fromArray(Enum e, JsonConfig jsonConfig) { if (!addInstance(e)) { try { return jsonConfig.getCycleDetectionStrategy().handleRepeatedReferenceAsArray(e); } catch (JSONException jsone) { removeInstance(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } catch (RuntimeException re) { removeInstance(e); JSONException jsone = new JSONException(re); fireErrorEvent(jsone, jsonConfig); throw jsone; } } fireArrayStartEvent(jsonConfig); JSONArray jsonArray = new JSONArray(); if (e != null) { jsonArray.addValue(e, jsonConfig); fireElementAddedEvent(0, jsonArray.get(0), jsonConfig); } else { JSONException jsone = new JSONException("enum value is null"); removeInstance(e); fireErrorEvent(jsone, jsonConfig); throw jsone; } removeInstance(e); 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(); } /** * Write the contents of the JSONArray as JSON text to a writer. For * compactness, no whitespace is added. * <p> * Warning: This method assumes that the data structure is acyclical. * * @return The writer. * @throws JSONException */ public Writer write(Writer writer) { 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 JSONObject) { ((JSONObject) v).write(writer); } else if (v instanceof JSONArray) { ((JSONArray) v).write(writer); } else { writer.write(JSONUtils.valueToString(v)); } b = true; } writer.write(']'); return writer; } 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); } else if (value != null && Enum.class.isAssignableFrom(value.getClass())) { return ((Enum) value).name(); } else if (value instanceof Annotation || (value != null && value.getClass().isAnnotation())) { throw new JSONException("Unsupported type"); } 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(); } } } }