001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017import java.io.*;
018import java.lang.reflect.*;
019import java.util.*;
020
021import org.apache.juneau.json.*;
022import org.apache.juneau.parser.*;
023import org.apache.juneau.serializer.*;
024import org.apache.juneau.utils.*;
025
026/**
027 * Java implementation of a JSON array.
028 *
029 * <p>
030 * An extension of {@link LinkedList}, so all methods available to in that class are also available to this class.
031 *
032 * <p>
033 * Note that the use of this class is optional.
034 * The serializers will accept any objects that implement the {@link Collection} interface.
035 * But this class provides some useful additional functionality when working with JSON models constructed from Java
036 * Collections Framework objects.
037 * For example, a constructor is provided for converting a JSON array string directly into a {@link List}.
038 * It also contains accessor methods for to avoid common typecasting when accessing elements in a list.
039 *
040 * <h5 class='section'>Example:</h5>
041 * <p class='bcode w800'>
042 *    <jc>// Construct an empty List</jc>
043 *    List l = <jk>new</jk> ObjectList();
044 *
045 *    <jc>// Construct a list of objects using various methods</jc>
046 *    l = <jk>new</jk> ObjectList().append(<js>"foo"</js>).append(123).append(<jk>true</jk>);
047 *    l = <jk>new</jk> ObjectList().append(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
048 *    l = <jk>new</jk> ObjectList(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
049 *
050 *    <jc>// Construct a list of integers from JSON</jc>
051 *    l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
052 *
053 *    <jc>// Construct a list of generic ObjectMap objects from JSON</jc>
054 *    l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
055 *
056 *    <jc>// Construct a list of integers from XML</jc>
057 *    String xml = <js>"&lt;array&gt;&lt;number&gt;1&lt;/number&gt;&lt;number&gt;2&lt;/number&gt;&lt;number&gt;3&lt;/number&gt;&lt;/array&gt;"</js>;
058 *    l = <jk>new</jk> ObjectList(xml, DataFormat.<jsf>XML</jsf>);
059 *    l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(xml);  <jc>// Equivalent</jc>
060 *    l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml);  <jc>// Equivalent</jc>
061 *    l = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, xml);  <jc>// Equivalent</jc>
062 *    l = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectList.<jk>class</jk>, xml);  <jc>// Equivalent</jc>
063 *
064 *    <jc>// Construct JSON from ObjectList</jc>
065 *    l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
066 *    String json = l.toString();  <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc>
067 *    json = l.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>);  <jc>// Equivalent</jc>
068 *    json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(l);  <jc>// Equivalent</jc>
069 *
070 *    <jc>// Get one of the entries in the list as an Integer</jc>
071 *    l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
072 *    Integer i = l.getInt(1);
073 *    i = l.get(Integer.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
074 *
075 *    <jc>// Get one of the entries in the list as an Float</jc>
076 *    l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
077 *    Float f = l.getFloat(1); <jc>// Returns 2f </jc>
078 *    f = l.get(Float.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
079 *
080 *    <jc>// Same as above, except converted to a String</jc>
081 *    l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
082 *    String s = l.getString(1); <jc>// Returns "2" </jc>
083 *    s = l.get(String.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
084 *
085 *    <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc>
086 *    l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>);
087 *    Person p = l.get(Person.<jk>class</jk>, 0);
088 *
089 *    <jc>// Iterate over a list of beans using the elements() method</jc>
090 *    ObjectList ObjectList = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>);
091 *    <jk>for</jk> (Person p : ObjectList.elements(Person.<jk>class</jk>) {
092 *       <jc>// Do something with p</jc>
093 *    }
094 * </p>
095 *
096 * <p>
097 * This class is not thread safe.
098 */
099public class ObjectList extends LinkedList<Object> {
100   private static final long serialVersionUID = 1L;
101
102   transient BeanSession session = null;
103   private transient PojoRest pojoRest;
104
105   /**
106    * An empty read-only ObjectList.
107    */
108   public static final ObjectList EMPTY_LIST = new ObjectList() {
109      private static final long serialVersionUID = 1L;
110
111      @Override /* List */
112      public void add(int location, Object object) {
113         throw new UnsupportedOperationException();
114      }
115
116      @Override /* List */
117      public ListIterator<Object> listIterator(final int location) {
118         return Collections.emptyList().listIterator(location);
119      }
120
121      @Override /* List */
122      public Object remove(int location) {
123         throw new UnsupportedOperationException();
124      }
125
126      @Override /* List */
127      public Object set(int location, Object object) {
128         throw new UnsupportedOperationException();
129      }
130
131      @Override /* List */
132      public List<Object> subList(int start, int end) {
133         return Collections.emptyList().subList(start, end);
134      }
135   };
136
137   /**
138    * Static constructor from JSON string.
139    *
140    * @param s JSON initialization string.  Can be <jk>null</jk>.
141    * @return A new {@link ObjectList} object, or <jk>null</jk> if the input is <jk>null</jk>.
142    * @throws ParseException Invalid JSON string.
143    */
144   public static ObjectList parse(CharSequence s) throws ParseException {
145      return s == null ? null : new ObjectList(s);
146   }
147
148   /**
149    * Construct a JSON array directly from text using the specified parser.
150    *
151    * @param s The string being parsed.
152    * @param p The parser to use to parse the input.
153    * @throws ParseException Malformed input encountered.
154    */
155   public ObjectList(CharSequence s, Parser p) throws ParseException {
156      this(p == null ? null : p.createBeanSession());
157      if (p == null)
158         p = JsonParser.DEFAULT;
159      if (s != null)
160         p.parseIntoCollection(s, this, bs2().object());
161   }
162
163   /**
164    * Shortcut for <code><jk>new</jk> ObjectList(String,JsonParser.<jsf>DEFAULT</jsf>);</code>
165    *
166    * @param s The string being parsed.
167    * @throws ParseException Malformed input encountered.
168    */
169   public ObjectList(CharSequence s) throws ParseException {
170      this(s, null);
171   }
172
173   /**
174    * Construct a JSON array directly from a reader using the specified parser.
175    *
176    * @param r
177    *    The reader to read from.
178    *    Will automatically be wrapped in a {@link BufferedReader} if it isn't already a BufferedReader.
179    * @param p The parser to use to parse the input.
180    * @throws ParseException Malformed input encountered.
181    * @throws IOException If a problem occurred trying to read from the reader.
182    */
183   public ObjectList(Reader r, Parser p) throws ParseException, IOException {
184      this(p == null ? null : p.createBeanSession());
185      parseReader(r, p);
186   }
187
188   /**
189    * Shortcut for <code><jk>new</jk> ObjectList(reader, JsonParser.<jsf>DEFAULT</jsf>)</code>.
190    *
191    * @param r
192    *    The reader to read from.
193    *    The reader will be wrapped in a {@link BufferedReader} if it isn't already.
194    * @throws ParseException Malformed input encountered.
195    * @throws IOException If a problem occurred trying to read from the reader.
196    */
197   public ObjectList(Reader r) throws ParseException, IOException {
198      parseReader(r, JsonParser.DEFAULT);
199   }
200
201   private void parseReader(Reader r, Parser p) throws ParseException {
202      if (p == null)
203         p = JsonParser.DEFAULT;
204      p.parseIntoCollection(r, this, bs2().object());
205   }
206
207   /**
208    * Construct an empty JSON array (an empty {@link LinkedList}).
209    */
210   public ObjectList() {
211   }
212
213   /**
214    * Construct an empty JSON array (an empty {@link LinkedList}) with the specified bean context.
215    *
216    * @param session The bean context to associate with this object list for creating beans.
217    */
218   public ObjectList(BeanSession session) {
219      super();
220      this.session = session;
221   }
222
223   /**
224    * Construct a JSON array and fill it with the specified objects.
225    *
226    * @param o A list of objects to add to this list.
227    */
228   public ObjectList(Object... o) {
229      super();
230      Collections.addAll(this, o);
231   }
232
233   /**
234    * Construct a JSON array and fill it with the specified collection of objects.
235    *
236    * @param c A list of objects to add to this list.
237    */
238   public ObjectList(Collection<?> c) {
239      super(c);
240   }
241
242   /**
243    * Override the default bean session used for converting POJOs.
244    *
245    * <p>
246    * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases.
247    *
248    * <p>
249    * Useful if you're serializing/parsing beans with transforms defined.
250    *
251    * @param session The new bean session.
252    * @return This object (for method chaining).
253    */
254   public ObjectList setBeanSession(BeanSession session) {
255      this.session = session;
256      return this;
257   }
258
259   /**
260    * Returns the {@link BeanSession} currently associated with this list.
261    *
262    * @return The {@link BeanSession} currently associated with this list.
263    */
264   public BeanSession getBeanSession() {
265      return session;
266   }
267
268   /**
269    * Convenience method for adding multiple objects to this list.
270    *
271    * @param o The objects to add to the list.
272    * @return This object (for method chaining).
273    */
274   public ObjectList append(Object...o) {
275      for (Object o2 : o)
276         add(o2);
277      return this;
278   }
279
280   /**
281    * Convenience method for appending another list to this list.
282    *
283    * @param l
284    *    The list containing the elements to append to this list.
285    *    <br>Can be <jk>null</jk>.
286    * @return This object (for method chaining).
287    */
288   public ObjectList appendAll(ObjectList l) {
289      if (l != null)
290         addAll(l);
291      return this;
292   }
293
294   /**
295    * Convenience method for adding multiple objects to this list.
296    *
297    * <p>
298    * <jk>null</jk> and empty strings are skipped.
299    *
300    * @param o The objects to add to the list.
301    * @return This object (for method chaining).
302    */
303   public ObjectList appendIfNotEmpty(String...o) {
304      for (String s : o)
305         if (isNotEmpty(s))
306            add(s);
307      return this;
308   }
309
310   /**
311    * Convenience method for adding multiple objects to this list.
312    *
313    * <p>
314    * <jk>null</jk> values are skipped.
315    *
316    * @param o The objects to add to the list.
317    * @return This object (for method chaining).
318    */
319   public ObjectList appendIfNotNull(Object...o) {
320      for (Object o2 : o)
321         if (o2 != null)
322            add(o2);
323      return this;
324   }
325
326   /**
327    * Get the entry at the specified index, converted to the specified type.
328    *
329    * <p>
330    * This is the preferred get method for simple types.
331    *
332    * <h5 class='section'>Examples:</h5>
333    * <p class='bcode w800'>
334    *    ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>);
335    *
336    *    <jc>// Value converted to a string.</jc>
337    *    String s = l.get(1, String.<jk>class</jk>);
338    *
339    *    <jc>// Value converted to a bean.</jc>
340    *    MyBean b = l.get(2, MyBean.<jk>class</jk>);
341    *
342    *    <jc>// Value converted to a bean array.</jc>
343    *    MyBean[] ba = l.get(3, MyBean[].<jk>class</jk>);
344    *
345    *    <jc>// Value converted to a linked-list of objects.</jc>
346    *    List l1 = l.get(4, LinkedList.<jk>class</jk>);
347    *
348    *    <jc>// Value converted to a map of object keys/values.</jc>
349    *    Map m1 = l.get(5, TreeMap.<jk>class</jk>);
350    * </p>
351    *
352    * <p>
353    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
354    *
355    * @param index The index into this list.
356    * @param type The type of object to convert the entry to.
357    * @param <T> The type of object to convert the entry to.
358    * @return The converted entry.
359    */
360   public <T> T get(int index, Class<T> type) {
361      return bs2().convertToType(get(index), type);
362   }
363
364   /**
365    * Get the entry at the specified index, converted to the specified type.
366    *
367    * <p>
368    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
369    *
370    * <h5 class='section'>Examples:</h5>
371    * <p class='bcode w800'>
372    *    ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>);
373    *
374    *    <jc>// Value converted to a linked-list of strings.</jc>
375    *    List&lt;String&gt; l1 = l.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
376    *
377    *    <jc>// Value converted to a linked-list of beans.</jc>
378    *    List&lt;MyBean&gt; l2 = l.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
379    *
380    *    <jc>// Value converted to a linked-list of linked-lists of strings.</jc>
381    *    List&lt;List&lt;String&gt;&gt; l3 = l.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
382    *
383    *    <jc>// Value converted to a map of string keys/values.</jc>
384    *    Map&lt;String,String&gt; m1 = l.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
385    *
386    *    <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc>
387    *    Map&lt;String,List&lt;MyBean&gt;&gt; m2 = l.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
388    * </p>
389    *
390    * <p>
391    * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type.
392    *
393    * <p>
394    * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
395    *
396    * <p>
397    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
398    *
399    * <p>
400    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
401    *
402    * @param index The index into this list.
403    * @param type The type of object to convert the entry to.
404    * @param args The type arguments of the type to convert the entry to.
405    * @param <T> The type of object to convert the entry to.
406    * @return The converted entry.
407    */
408   public <T> T get(int index, Type type, Type...args) {
409      return bs2().convertToType(get(index), type, args);
410   }
411
412   /**
413    * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>.
414    *
415    * @param index The index.
416    * @return The converted value.
417    */
418   public String getString(int index) {
419      return get(index, String.class);
420   }
421
422   /**
423    * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>.
424    *
425    * @param index The index.
426    * @return The converted value.
427    * @throws InvalidDataConversionException If value cannot be converted.
428    */
429   public Integer getInt(int index) {
430      return get(index, Integer.class);
431   }
432
433   /**
434    * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>.
435    *
436    * @param index The index.
437    * @return The converted value.
438    * @throws InvalidDataConversionException If value cannot be converted.
439    */
440   public Boolean getBoolean(int index) {
441      return get(index, Boolean.class);
442   }
443
444   /**
445    * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>.
446    *
447    * @param index The index.
448    * @return The converted value.
449    * @throws InvalidDataConversionException If value cannot be converted.
450    */
451   public Long getLong(int index) {
452      return get(index, Long.class);
453   }
454
455   /**
456    * Shortcut for calling <code>get(index, Map.<jk>class</jk>)</code>.
457    *
458    * @param index The index.
459    * @return The converted value.
460    * @throws InvalidDataConversionException If value cannot be converted.
461    */
462   public Map<?,?> getMap(int index) {
463      return get(index, Map.class);
464   }
465
466   /**
467    * Same as {@link #getMap(int)} except converts the keys and values to the specified types.
468    *
469    * @param index The index.
470    * @param keyType The key type class.
471    * @param valType The value type class.
472    * @return The converted value.
473    * @throws InvalidDataConversionException If value cannot be converted.
474    */
475   public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
476      return bs2().convertToType(get(index), Map.class, keyType, valType);
477   }
478
479   /**
480    * Shortcut for calling <code>get(index, List.<jk>class</jk>)</code>.
481    *
482    * @param index The index.
483    * @return The converted value.
484    * @throws InvalidDataConversionException If value cannot be converted.
485    */
486   public List<?> getList(int index) {
487      return get(index, List.class);
488   }
489
490   /**
491    * Same as {@link #getList(int)} except converts the elements to the specified types.
492    *
493    * @param index The index.
494    * @param elementType The element type class.
495    * @return The converted value.
496    * @throws InvalidDataConversionException If value cannot be converted.
497    */
498   public <E> List<E> getList(int index, Class<E> elementType) {
499      return bs2().convertToType(get(index), List.class, elementType);
500   }
501
502   /**
503    * Shortcut for calling <code>get(index, ObjectMap.<jk>class</jk>)</code>.
504    *
505    * @param index The index.
506    * @return The converted value.
507    * @throws InvalidDataConversionException If value cannot be converted.
508    */
509   public ObjectMap getObjectMap(int index) {
510      return get(index, ObjectMap.class);
511   }
512
513   /**
514    * Shortcut for calling <code>get(index, ObjectList.<jk>class</jk>)</code>.
515    *
516    * @param index The index.
517    * @return The converted value.
518    * @throws InvalidDataConversionException If value cannot be converted.
519    */
520   public ObjectList getObjectList(int index) {
521      return get(index, ObjectList.class);
522   }
523
524   /**
525    * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in
526    * this POJO.
527    *
528    * <p>
529    * For example, the following code is equivalent:
530    * </p>
531    * <p class='bcode w800'>
532    *    ObjectMap m = getObjectMap();
533    *
534    *    <jc>// Long way</jc>
535    *    <jk>long</jk> l = m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).getLong(<js>"baz"</js>);
536    *
537    *    <jc>// Using this method</jc>
538    *    <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>);
539    * </p>
540    *
541    * <p>
542    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
543    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
544    *
545    * @param path The path to the entry.
546    * @param type The class type.
547    *
548    * @param <T> The class type.
549    * @return The value, or <jk>null</jk> if the entry doesn't exist.
550    */
551   public <T> T getAt(String path, Class<T> type) {
552      return getPojoRest().get(path, type);
553   }
554
555   /**
556    * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections.
557    *
558    * @param path The path to the entry.
559    * @param type The class type.
560    * @param args The class parameter types.
561    *
562    * @param <T> The class type.
563    * @return The value, or <jk>null</jk> if the entry doesn't exist.
564    */
565   public <T> T getAt(String path, Type type, Type...args) {
566      return getPojoRest().get(path, type, args);
567   }
568
569   /**
570    * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries
571    * in this POJO.
572    *
573    * <p>
574    * For example, the following code is equivalent:
575    * </p>
576    * <p class='bcode w800'>
577    *    ObjectMap m = getObjectMap();
578    *
579    *    <jc>// Long way</jc>
580    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).put(<js>"baz"</js>, 123);
581    *
582    *    <jc>// Using this method</jc>
583    *    m.putAt(<js>"foo/bar/0/baz"</js>, 123);
584    * </p>
585    *
586    * <p>
587    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
588    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
589    *
590    * @param path The path to the entry.
591    * @param o The new value.
592    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
593    */
594   public Object putAt(String path, Object o) {
595      return getPojoRest().put(path, o);
596   }
597
598   /**
599    * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays.
600    *
601    * <p>
602    * For example, the following code is equivalent:
603    * </p>
604    * <p class='bcode w800'>
605    *    ObjectMap m = getObjectMap();
606    *
607    *    <jc>// Long way</jc>
608    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).append(123);
609    *
610    *    <jc>// Using this method</jc>
611    *    m.postAt(<js>"foo/bar"</js>, 123);
612    * </p>
613    *
614    * <p>
615    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
616    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
617    *
618    * @param path The path to the entry.
619    * @param o The new value.
620    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
621    */
622   public Object postAt(String path, Object o) {
623      return getPojoRest().post(path, o);
624   }
625
626   /**
627    * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in
628    * this POJO.
629    *
630    * <p>
631    * For example, the following code is equivalent:
632    * </p>
633    * <p class='bcode w800'>
634    *    ObjectMap m = getObjectMap();
635    *
636    *    <jc>// Long way</jc>
637    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(1).remove(<js>"baz"</js>);
638    *
639    *    <jc>// Using this method</jc>
640    *    m.deleteAt(<js>"foo/bar/0/baz"</js>);
641    * </p>
642    *
643    * <p>
644    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
645    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
646    *
647    * @param path The path to the entry.
648    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
649    */
650   public Object deleteAt(String path) {
651      return getPojoRest().delete(path);
652   }
653
654   /**
655    * Creates an {@link Iterable} with elements of the specified child type.
656    *
657    * <p>
658    * Attempts to convert the child objects to the correct type if they aren't already the correct type.
659    *
660    * <p>
661    * The <c>next()</c> method on the returned iterator may throw a {@link InvalidDataConversionException} if
662    * the next element cannot be converted to the specified type.
663    *
664    * <p>
665    * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions.
666    *
667    * <h5 class='section'>Example:</h5>
668    * <p class='bcode w800'>
669    *    <jc>// Iterate over a list of ObjectMaps.</jc>
670    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>);
671    *    for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) {
672    *       <jc>// Do something with m.</jc>
673    *    }
674    *
675    *    <jc>// Iterate over a list of ints.</jc>
676    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
677    *    for (Integer i : l.elements(Integer.<jk>class</jk>)) {
678    *       <jc>// Do something with i.</jc>
679    *    }
680    *
681    *    <jc>// Iterate over a list of beans.</jc>
682    *    <jc>// Automatically converts to beans.</jc>
683    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>);
684    *    for (Person p : l.elements(Person.<jk>class</jk>)) {
685    *       <jc>// Do something with p.</jc>
686    *    }
687    * </p>
688    *
689    * @param <E> The child object type.
690    * @param childType The child object type.
691    * @return A new <c>Iterable</c> object over this list.
692    */
693   public <E> Iterable<E> elements(final Class<E> childType) {
694      final Iterator<?> i = iterator();
695      return new Iterable<E>() {
696
697         @Override /* Iterable */
698         public Iterator<E> iterator() {
699            return new Iterator<E>() {
700
701               @Override /* Iterator */
702               public boolean hasNext() {
703                  return i.hasNext();
704               }
705
706               @Override /* Iterator */
707               public E next() {
708                  return bs2().convertToType(i.next(), childType);
709               }
710
711               @Override /* Iterator */
712               public void remove() {
713                  i.remove();
714               }
715
716            };
717         }
718      };
719   }
720
721   /**
722    * Returns the {@link ClassMeta} of the class of the object at the specified index.
723    *
724    * @param index An index into this list, zero-based.
725    * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
726    */
727   public ClassMeta<?> getClassMeta(int index) {
728      return bs2().getClassMetaForObject(get(index));
729   }
730
731   private PojoRest getPojoRest() {
732      if (pojoRest == null)
733         pojoRest = new PojoRest(this);
734      return pojoRest;
735   }
736
737   /**
738    * Serialize this array to a string using the specified serializer.
739    *
740    * @param serializer The serializer to use to convert this object to a string.
741    * @return This object as a serialized string.
742    * @throws SerializeException If a problem occurred trying to convert the output.
743    */
744   public String toString(WriterSerializer serializer) throws SerializeException {
745      return serializer.serialize(this);
746   }
747
748   /**
749    * Returns <jk>true</jk> if this list is unmodifiable.
750    *
751    * @return <jk>true</jk> if this list is unmodifiable.
752    */
753   public boolean isUnmodifiable() {
754      return false;
755   }
756
757   /**
758    * Returns a modifiable copy of this list if it's unmodifiable.
759    *
760    * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable.
761    */
762   public ObjectList modifiable() {
763      if (isUnmodifiable())
764         return new ObjectList(this);
765      return this;
766   }
767
768   /**
769    * Returns an unmodifiable copy of this list if it's modifiable.
770    *
771    * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable.
772    */
773   public ObjectList unmodifiable() {
774      if (this instanceof UnmodifiableObjectList)
775         return this;
776      return new UnmodifiableObjectList(this);
777   }
778
779   /**
780    * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer.
781    */
782   @Override /* Object */
783   public String toString() {
784      try {
785         return this.toString(SimpleJsonSerializer.DEFAULT);
786      } catch (SerializeException e) {
787         return e.getLocalizedMessage();
788      }
789   }
790
791   /**
792    * Convenience method for serializing this ObjectList to the specified Writer using the JsonSerializer.DEFAULT
793    * serializer.
794    *
795    * @param w The writer to send the serialized contents of this object.
796    * @throws IOException If a problem occurred trying to write to the writer.
797    * @throws SerializeException If a problem occurred trying to convert the output.
798    */
799   public void serializeTo(Writer w) throws IOException, SerializeException {
800      JsonSerializer.DEFAULT.serialize(this);
801   }
802
803   /**
804    * Converts this object into the specified class type.
805    *
806    * <p>
807    * TODO - The current implementation is very inefficient.
808    *
809    * @param cm The class type to convert this object to.
810    * @return A converted object.
811    */
812   public Object cast(ClassMeta<?> cm) {
813      try {
814         return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT.serialize(this), cm);
815      } catch (ParseException | SerializeException e) {
816         throw new RuntimeException(e);
817      }
818   }
819
820   private static final class UnmodifiableObjectList extends ObjectList {
821      private static final long serialVersionUID = 1L;
822
823      UnmodifiableObjectList(ObjectList contents) {
824         super();
825         if (contents != null) {
826            for (Object e : this) {
827               super.add(e);
828            }
829         }
830      }
831
832      @Override /* List */
833      public void add(int location, Object object) {
834         throw new UnsupportedOperationException("ObjectList is read-only.");
835      }
836
837      @Override /* List */
838      public Object remove(int location) {
839         throw new UnsupportedOperationException("ObjectList is read-only.");
840      }
841
842      @Override /* List */
843      public Object set(int location, Object object) {
844         throw new UnsupportedOperationException("ObjectList is read-only.");
845      }
846
847      @Override
848      public final boolean isUnmodifiable() {
849         return true;
850      }
851   }
852
853   BeanSession bs2() {
854      if (session == null)
855         session = BeanContext.DEFAULT.createBeanSession();
856      return session;
857   }
858}