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 }