View Javadoc

1   package org.apache.struts.flow.json;
2   
3   import java.text.ParseException;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.NoSuchElementException;
7   
8   /***
9    * A JSONArray is an ordered sequence of values. Its external form is a string
10   * wrapped in square brackets with commas between the values. The internal form
11   * is an object having get() and opt() methods for accessing the values by
12   * index, and put() methods for adding or replacing values. The values can be
13   * any of these types: Boolean, JSONArray, JSONObject, Number, String, or the
14   * JSONObject.NULL object.
15   * <p>
16   * The constructor can convert a JSON external form string into an
17   * internal form Java object. The toString() method creates an external
18   * form string.
19   * <p>
20   * A get() method returns a value if one can be found, and throws an exception
21   * if one cannot be found. An opt() method returns a default value instead of
22   * throwing an exception, and so is useful for obtaining optional values.
23   * <p>
24   * The generic get() and opt() methods return an object which you can cast or
25   * query for type. There are also typed get() and opt() methods that do typing
26   * checking and type coersion for you.
27   * <p>
28   * The texts produced by the toString() methods are very strict.
29   * The constructors are more forgiving in the texts they will accept.
30   * <ul>
31   * <li>An extra comma may appear just before the closing bracket.</li>
32   * <li>Strings may be quoted with single quotes.</li>
33   * <li>Strings do not need to be quoted at all if they do not contain leading
34   *     or trailing spaces, and if they do not contain any of these characters:
35   *     { } [ ] / \ : , </li>
36   * <li>Numbers may have the 0- (octal) or 0x- (hex) prefix.</li>
37   * </ul>
38   * <p>
39   * Public Domain 2002 JSON.org
40   * @author JSON.org
41   * @version 0.1
42   */
43  public class JSONArray {
44  
45  
46      /***
47       * The getArrayList where the JSONArray's properties are kept.
48       */
49      private ArrayList myArrayList;
50  
51  
52      /***
53       * Construct an empty JSONArray.
54       */
55      public JSONArray() {
56          myArrayList = new ArrayList();
57      }
58  
59  
60      /***
61       * Construct a JSONArray from a JSONTokener.
62       * @exception ParseException A JSONArray must start with '['
63       * @exception ParseException Expected a ',' or ']'
64       * @param x A JSONTokener
65       */
66      public JSONArray(JSONTokener x) throws ParseException {
67          this();
68          if (x.nextClean() != '[') {
69              throw x.syntaxError("A JSONArray must start with '['");
70          }
71          if (x.nextClean() == ']') {
72              return;
73          }
74          x.back();
75          while (true) {
76              myArrayList.add(x.nextValue());
77              switch (x.nextClean()) {
78              case ',':
79                  if (x.nextClean() == ']') {
80                      return;
81                  }
82                  x.back();
83                  break;
84              case ']':
85                  return;
86              default:
87                  throw x.syntaxError("Expected a ',' or ']'");
88              }
89          }
90      }
91  
92  
93      /***
94       * Construct a JSONArray from a source string.
95       * @exception ParseException The string must conform to JSON syntax.
96       * @param string     A string that begins with '[' and ends with ']'.
97       */
98      public JSONArray(String string) throws ParseException {
99          this(new JSONTokener(string));
100     }
101 
102 
103     /***
104      * Construct a JSONArray from a Collection.
105      * @param collection     A Collection.
106      */
107     public JSONArray(Collection collection) {
108         myArrayList = new ArrayList(collection);
109     }
110 
111 
112     /***
113      * Get the object value associated with an index.
114      * @exception NoSuchElementException
115      * @param index subscript
116      *  The index must be between 0 and length() - 1.
117      * @return An object value.
118      */
119     public Object get(int index) throws NoSuchElementException {
120         Object o = opt(index);
121         if (o == null) {
122             throw new NoSuchElementException("JSONArray[" + index +
123                 "] not found.");
124         }
125         return o;
126     }
127 
128 
129     /***
130      * Get the ArrayList which is holding the elements of the JSONArray.
131      * @return      The ArrayList.
132      */
133     ArrayList getArrayList() {
134         return myArrayList;
135     }
136 
137 
138     /***
139      * Get the boolean value associated with an index.
140      * The string values "true" and "false" are converted to boolean.
141      *
142      * @exception NoSuchElementException if the index is not found
143      * @exception ClassCastException
144      * @param index subscript
145      * @return      The truth.
146      */
147     public boolean getBoolean(int index)
148             throws ClassCastException, NoSuchElementException {
149         Object o = get(index);
150         if (o == Boolean.FALSE || o.equals("false")) {
151             return false;
152         } else if (o == Boolean.TRUE || o.equals("true")) {
153             return true;
154         }
155         throw new ClassCastException("JSONArray[" + index +
156             "] not a Boolean.");
157     }
158 
159 
160     /***
161      * Get the double value associated with an index.
162      * @exception NoSuchElementException if the key is not found
163      * @exception NumberFormatException
164      *  if the value cannot be converted to a number.
165      *
166      * @param index subscript
167      * @return      The value.
168      */
169     public double getDouble(int index)
170             throws NoSuchElementException, NumberFormatException {
171         Object o = get(index);
172         if (o instanceof Number) {
173             return ((Number) o).doubleValue();
174         }
175         if (o instanceof String) {
176             return new Double((String)o).doubleValue();
177         }
178         throw new NumberFormatException("JSONObject[" +
179             index + "] is not a number.");
180     }
181 
182 
183     /***
184      * Get the int value associated with an index.
185      * @exception NoSuchElementException if the key is not found
186      * @exception NumberFormatException
187      *  if the value cannot be converted to a number.
188      *
189      * @param index subscript
190      * @return      The value.
191      */
192     public int getInt(int index)
193             throws NoSuchElementException, NumberFormatException {
194         Object o = get(index);
195         if (o instanceof Number) {
196             return ((Number)o).intValue();
197         }
198         return (int)getDouble(index);
199     }
200 
201 
202     /***
203      * Get the JSONArray associated with an index.
204      * @exception NoSuchElementException if the index is not found or if the
205      * value is not a JSONArray
206      * @param index subscript
207      * @return      A JSONArray value.
208      */
209     public JSONArray getJSONArray(int index) throws NoSuchElementException {
210         Object o = get(index);
211         if (o instanceof JSONArray) {
212             return (JSONArray)o;
213         }
214         throw new NoSuchElementException("JSONArray[" + index +
215                 "] is not a JSONArray.");
216     }
217 
218 
219     /***
220      * Get the JSONObject associated with an index.
221      * @exception NoSuchElementException if the index is not found or if the
222      * value is not a JSONObject
223      * @param index subscript
224      * @return      A JSONObject value.
225      */
226     public JSONObject getJSONObject(int index) throws NoSuchElementException {
227         Object o = get(index);
228         if (o instanceof JSONObject) {
229             return (JSONObject)o;
230         }
231         throw new NoSuchElementException("JSONArray[" + index +
232             "] is not a JSONObject.");
233     }
234 
235 
236     /***
237      * Get the string associated with an index.
238      * @exception NoSuchElementException
239      * @param index subscript
240      * @return      A string value.
241      */
242     public String getString(int index) throws NoSuchElementException {
243         return get(index).toString();
244     }
245 
246 
247     /***
248      * Determine if the value is null.
249      * @param index subscript
250      * @return true if the value at the index is null, or if there is no value.
251      */
252     public boolean isNull(int index) {
253         Object o = opt(index);
254         return o == null || o.equals(null);
255     }
256 
257 
258     /***
259      * Make a string from the contents of this JSONArray. The separator string
260      * is inserted between each element.
261      * Warning: This method assumes that the data structure is acyclical.
262      * @param separator A string that will be inserted between the elements.
263      * @return a string.
264      */
265     public String join(String separator) {
266         int i;
267         Object o;
268         StringBuffer sb = new StringBuffer();
269         for (i = 0; i < myArrayList.size(); i += 1) {
270             if (i > 0) {
271                 sb.append(separator);
272             }
273             o = myArrayList.get(i);
274             if (o == null) {
275                 sb.append("null");
276             } else if (o instanceof String) {
277                 sb.append(JSONObject.quote((String)o));
278             } else if (o instanceof Number) {
279                 sb.append(JSONObject.numberToString((Number)o));
280             } else {
281                 sb.append(o.toString());
282             }
283         }
284         return sb.toString();
285     }
286 
287 
288     /***
289      * Get the length of the JSONArray.
290      *
291      * @return The length (or size).
292      */
293     public int length() {
294         return myArrayList.size();
295     }
296 
297 
298     /***
299      * Get the optional object value associated with an index.
300      * @param index subscript
301      * @return      An object value, or null if there is no
302      *              object at that index.
303      */
304     public Object opt(int index) {
305         if (index < 0 || index >= length()) {
306             return null;
307         } else {
308             return myArrayList.get(index);
309         }
310     }
311 
312 
313     /***
314      * Get the optional boolean value associated with an index.
315      * It returns false if there is no value at that index,
316      * or if the value is not Boolean.TRUE or the String "true".
317      *
318      * @param index subscript
319      * @return      The truth.
320      */
321     public boolean optBoolean(int index)  {
322         return optBoolean(index, false);
323     }
324 
325 
326     /***
327      * Get the optional boolean value associated with an index.
328      * It returns the defaultValue if there is no value at that index or if it is not
329      * a Boolean or the String "true" or "false".
330      *
331      * @param index subscript
332      * @param defaultValue     A boolean default.
333      * @return      The truth.
334      */
335     public boolean optBoolean(int index, boolean defaultValue)  {
336         Object o = opt(index);
337         if (o != null) {
338             if (o == Boolean.FALSE || o.equals("false")) {
339                 return false;
340             } else if (o == Boolean.TRUE || o.equals("true")) {
341                 return true;
342             }
343         }
344         return defaultValue;
345     }
346 
347 
348     /***
349      * Get the optional double value associated with an index.
350      * NaN is returned if the index is not found,
351      * or if the value is not a number and cannot be converted to a number.
352      *
353      * @param index subscript
354      * @return      The value.
355      */
356     public double optDouble(int index) {
357         return optDouble(index, Double.NaN);
358     }
359 
360 
361     /***
362      * Get the optional double value associated with an index.
363      * The defaultValue is returned if the index is not found,
364      * or if the value is not a number and cannot be converted to a number.
365      *
366      * @param index subscript
367      * @param defaultValue     The default value.
368      * @return      The value.
369      */
370     public double optDouble(int index, double defaultValue) {
371         Object o = opt(index);
372         if (o != null) {
373             if (o instanceof Number) {
374                 return ((Number) o).doubleValue();
375             }
376             try {
377                 return new Double((String)o).doubleValue();
378             }
379             catch (Exception e) {
380             }
381         }
382         return defaultValue;
383     }
384 
385 
386     /***
387      * Get the optional int value associated with an index.
388      * Zero is returned if the index is not found,
389      * or if the value is not a number and cannot be converted to a number.
390      *
391      * @param index subscript
392      * @return      The value.
393      */
394     public int optInt(int index) {
395         return optInt(index, 0);
396     }
397 
398 
399     /***
400      * Get the optional int value associated with an index.
401      * The defaultValue is returned if the index is not found,
402      * or if the value is not a number and cannot be converted to a number.
403      * @param index subscript
404      * @param defaultValue     The default value.
405      * @return      The value.
406      */
407     public int optInt(int index, int defaultValue) {
408         Object o = opt(index);
409         if (o != null) {
410             if (o instanceof Number) {
411                 return ((Number)o).intValue();
412             }
413             try {
414                 return Integer.parseInt((String)o);
415             }
416             catch (Exception e) {
417             }
418         }
419         return defaultValue;
420     }
421 
422 
423     /***
424      * Get the optional JSONArray associated with an index.
425      * @param index subscript
426      * @return      A JSONArray value, or null if the index has no value,
427      * or if the value is not a JSONArray.
428      */
429     public JSONArray optJSONArray(int index) {
430         Object o = opt(index);
431         if (o instanceof JSONArray) {
432             return (JSONArray)o;
433         }
434         return null;
435     }
436 
437 
438     /***
439      * Get the optional JSONObject associated with an index.
440      * Null is returned if the key is not found, or null if the index has
441      * no value, or if the value is not a JSONObject.
442      *
443      * @param index subscript
444      * @return      A JSONObject value.
445      */
446     public JSONObject optJSONObject(int index) {
447         Object o = opt(index);
448         if (o instanceof JSONObject) {
449             return (JSONObject)o;
450         }
451         return null;
452     }
453 
454 
455     /***
456      * Get the optional string value associated with an index. It returns an
457      * empty string if there is no value at that index. If the value
458      * is not a string and is not null, then it is coverted to a string.
459      *
460      * @param index subscript
461      * @return      A String value.
462      */
463     public String optString(int index){
464         return optString(index, "");
465     }
466 
467 
468     /***
469      * Get the optional string associated with an index.
470      * The defaultValue is returned if the key is not found.
471      *
472      * @param index subscript
473      * @param defaultValue     The default value.
474      * @return      A String value.
475      */
476     public String optString(int index, String defaultValue){
477         Object o = opt(index);
478         if (o != null) {
479             return o.toString();
480         }
481         return defaultValue;
482     }
483 
484 
485     /***
486      * Append a boolean value.
487      *
488      * @param value A boolean value.
489      * @return this.
490      */
491     public JSONArray put(boolean value) {
492         put(new Boolean(value));
493         return this;
494     }
495 
496 
497     /***
498      * Append a double value.
499      *
500      * @param value A double value.
501      * @return this.
502      */
503     public JSONArray put(double value) {
504         put(new Double(value));
505         return this;
506     }
507 
508 
509     /***
510      * Append an int value.
511      *
512      * @param value An int value.
513      * @return this.
514      */
515     public JSONArray put(int value) {
516         put(new Integer(value));
517         return this;
518     }
519 
520 
521     /***
522      * Append an object value.
523      * @param value An object value.  The value should be a
524      *  Boolean, Double, Integer, JSONArray, JSObject, or String, or the
525      *  JSONObject.NULL object.
526      * @return this.
527      */
528     public JSONArray put(Object value) {
529         myArrayList.add(value);
530         return this;
531     }
532 
533 
534     /***
535      * Put or replace a boolean value in the JSONArray.
536      * @exception NoSuchElementException The index must not be negative.
537      * @param index subscript The subscript. If the index is greater than the length of
538      *  the JSONArray, then null elements will be added as necessary to pad
539      *  it out.
540      * @param value A boolean value.
541      * @return this.
542      */
543     public JSONArray put(int index, boolean value) {
544         put(index, new Boolean(value));
545         return this;
546     }
547 
548 
549     /***
550      * Put or replace a double value.
551      * @exception NoSuchElementException The index must not be negative.
552      * @param index subscript The subscript. If the index is greater than the length of
553      *  the JSONArray, then null elements will be added as necessary to pad
554      *  it out.
555      * @param value A double value.
556      * return this.
557      */
558     public JSONArray put(int index, double value) {
559         put(index, new Double(value));
560         return this;
561     }
562 
563 
564     /***
565      * Put or replace an int value.
566      * @exception NoSuchElementException The index must not be negative.
567      * @param index subscript The subscript. If the index is greater than the length of
568      *  the JSONArray, then null elements will be added as necessary to pad
569      *  it out.
570      * @param value An int value.
571      * @return this.
572      */
573     public JSONArray put(int index, int value) {
574         put(index, new Integer(value));
575         return this;
576     }
577 
578 
579     /***
580      * Put or replace an object value in the JSONArray.
581      * @exception NoSuchElementException The index must not be negative.
582      * @param index The subscript. If the index is greater than the length of
583      *  the JSONArray, then null elements will be added as necessary to pad
584      *  it out.
585      * @param value An object value.
586      * return this.
587      */
588     public JSONArray put(int index, Object value)
589             throws NoSuchElementException, NullPointerException {
590         if (index < 0) {
591             throw new NoSuchElementException("JSONArray[" + index +
592                 "] not found.");
593         } else if (value == null) {
594             throw new NullPointerException();
595         } else if (index < length()) {
596             myArrayList.set(index, value);
597         } else {
598             while (index != length()) {
599                 put(null);
600             }
601             put(value);
602         }
603         return this;
604     }
605 
606 
607     /***
608      * Produce a JSONObject by combining a JSONArray of names with the values
609      * of this JSONArray.
610      * @param names A JSONArray containing a list of key strings. These will be
611      * paired with the values.
612      * @return A JSONObject, or null if there are no names or if this JSONArray
613      * has no values.
614      */
615     public JSONObject toJSONObject(JSONArray names) {
616         if (names == null || names.length() == 0 || length() == 0) {
617             return null;
618         }
619         JSONObject jo = new JSONObject();
620         for (int i = 0; i < names.length(); i += 1) {
621             jo.put(names.getString(i), this.opt(i));
622         }
623         return jo;
624     }
625 
626 
627     /***
628      * Make an JSON external form string of this JSONArray. For compactness, no
629      * unnecessary whitespace is added.
630      * Warning: This method assumes that the data structure is acyclical.
631      *
632      * @return a printable, displayable, transmittable
633      *  representation of the array.
634      */
635     public String toString() {
636         return '[' + join(",") + ']';
637     }
638 
639 
640     /***
641      * Make a prettyprinted JSON string of this JSONArray.
642      * Warning: This method assumes that the data structure is non-cyclical.
643      * @param indentFactor The number of spaces to add to each level of
644      *  indentation.
645      * @return a printable, displayable, transmittable
646      *  representation of the object, beginning with '[' and ending with ']'.
647      */
648     public String toString(int indentFactor) {
649         return toString(indentFactor, 0);
650     }
651 
652 
653     /***
654      * Make a prettyprinted string of this JSONArray.
655      * Warning: This method assumes that the data structure is non-cyclical.
656      * @param indentFactor The number of spaces to add to each level of
657      *  indentation.
658      * @param indent The indention of the top level.
659      * @return a printable, displayable, transmittable
660      *  representation of the array.
661      */
662     String toString(int indentFactor, int indent) {
663         int i;
664         Object o;
665         String pad = "";
666         StringBuffer sb = new StringBuffer();
667         indent += indentFactor;
668         for (i = 0; i < indent; i += 1) {
669             pad += ' ';
670         }
671         sb.append("[\n");
672         for (i = 0; i < myArrayList.size(); i += 1) {
673             if (i > 0) {
674                 sb.append(",\n");
675             }
676             sb.append(pad);
677             o = myArrayList.get(i);
678             if (o == null) {
679                 sb.append("null");
680             } else if (o instanceof String) {
681                 sb.append(JSONObject.quote((String) o));
682             } else if (o instanceof Number) {
683                 sb.append(JSONObject.numberToString((Number) o));
684             } else if (o instanceof JSONObject) {
685                 sb.append(((JSONObject)o).toString(indentFactor, indent));
686             } else if (o instanceof JSONArray) {
687                 sb.append(((JSONArray)o).toString(indentFactor, indent));
688             } else {
689                 sb.append(o.toString());
690             }
691         }
692         sb.append(']');
693         return sb.toString();
694     }
695 }