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    * Construct a JSON array directly from text using the specified parser.
139    *
140    * @param s The string being parsed.
141    * @param p The parser to use to parse the input.
142    * @throws ParseException If the input contains a syntax error or is malformed.
143    */
144   public ObjectList(CharSequence s, Parser p) throws ParseException {
145      this(p == null ? null : p.createBeanSession());
146      if (p == null)
147         p = JsonParser.DEFAULT;
148      if (s != null)
149         p.parseIntoCollection(s, this, bs().object());
150   }
151
152   /**
153    * Shortcut for <code><jk>new</jk> ObjectList(String,JsonParser.<jsf>DEFAULT</jsf>);</code>
154    *
155    * @param s The string being parsed.
156    * @throws ParseException If the input contains a syntax error or is malformed.
157    */
158   public ObjectList(CharSequence s) throws ParseException {
159      this(s, null);
160   }
161
162   /**
163    * Construct a JSON array directly from a reader using the specified parser.
164    *
165    * @param r
166    *    The reader to read from.
167    *    Will automatically be wrapped in a {@link BufferedReader} if it isn't already a BufferedReader.
168    * @param p The parser to use to parse the input.
169    * @throws ParseException If the input contains a syntax error or is malformed.
170    * @throws IOException If a problem occurred trying to read from the reader.
171    */
172   public ObjectList(Reader r, Parser p) throws ParseException, IOException {
173      this(p == null ? null : p.createBeanSession());
174      parseReader(r, p);
175   }
176
177   /**
178    * Shortcut for <code><jk>new</jk> ObjectList(reader, JsonParser.<jsf>DEFAULT</jsf>)</code>.
179    *
180    * @param r
181    *    The reader to read from.
182    *    The reader will be wrapped in a {@link BufferedReader} if it isn't already.
183    * @throws ParseException If the input contains a syntax error or is malformed.
184    * @throws IOException If a problem occurred trying to read from the reader.
185    */
186   public ObjectList(Reader r) throws ParseException, IOException {
187      parseReader(r, JsonParser.DEFAULT);
188   }
189
190   private void parseReader(Reader r, Parser p) throws ParseException {
191      if (p == null)
192         p = JsonParser.DEFAULT;
193      p.parseIntoCollection(r, this, bs().object());
194   }
195
196   /**
197    * Construct an empty JSON array (an empty {@link LinkedList}).
198    */
199   public ObjectList() {
200   }
201
202   /**
203    * Construct an empty JSON array (an empty {@link LinkedList}) with the specified bean context.
204    *
205    * @param session The bean context to associate with this object list for creating beans.
206    */
207   public ObjectList(BeanSession session) {
208      super();
209      this.session = session;
210   }
211
212   /**
213    * Construct a JSON array and fill it with the specified objects.
214    *
215    * @param o A list of objects to add to this list.
216    */
217   public ObjectList(Object... o) {
218      super(Arrays.asList(o));
219   }
220
221   /**
222    * Construct a JSON array and fill it with the specified collection of objects.
223    *
224    * @param c A list of objects to add to this list.
225    */
226   public ObjectList(Collection<?> c) {
227      super(c);
228   }
229
230   /**
231    * Override the default bean session used for converting POJOs.
232    *
233    * <p>
234    * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases.
235    *
236    * <p>
237    * Useful if you're serializing/parsing beans with transforms defined.
238    *
239    * @param session The new bean session.
240    * @return This object (for method chaining).
241    */
242   public ObjectList setBeanSession(BeanSession session) {
243      this.session = session;
244      return this;
245   }
246
247   /**
248    * Returns the {@link BeanSession} currently associated with this list.
249    *
250    * @return The {@link BeanSession} currently associated with this list.
251    */
252   public BeanSession getBeanSession() {
253      return session;
254   }
255
256   /**
257    * Convenience method for adding multiple objects to this list.
258    *
259    * @param o The objects to add to the list.
260    * @return This object (for method chaining).
261    */
262   public ObjectList append(Object...o) {
263      for (Object o2 : o)
264         add(o2);
265      return this;
266   }
267
268   /**
269    * Convenience method for adding multiple objects to this list.
270    *
271    * <p>
272    * <jk>null</jk> and empty strings are skipped.
273    *
274    * @param o The objects to add to the list.
275    * @return This object (for method chaining).
276    */
277   public ObjectList appendIfNotEmpty(String...o) {
278      for (String s : o)
279         if (isNotEmpty(s))
280            add(s);
281      return this;
282   }
283
284   /**
285    * Convenience method for adding multiple objects to this list.
286    *
287    * <p>
288    * <jk>null</jk> values are skipped.
289    *
290    * @param o The objects to add to the list.
291    * @return This object (for method chaining).
292    */
293   public ObjectList appendIfNotNull(Object...o) {
294      for (Object o2 : o)
295         if (o2 != null)
296            add(o2);
297      return this;
298   }
299
300   /**
301    * Get the entry at the specified index, converted to the specified type.
302    *
303    * <p>
304    * This is the preferred get method for simple types.
305    *
306    * <h5 class='section'>Examples:</h5>
307    * <p class='bcode w800'>
308    *    ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>);
309    *
310    *    <jc>// Value converted to a string.</jc>
311    *    String s = l.get(1, String.<jk>class</jk>);
312    *
313    *    <jc>// Value converted to a bean.</jc>
314    *    MyBean b = l.get(2, MyBean.<jk>class</jk>);
315    *
316    *    <jc>// Value converted to a bean array.</jc>
317    *    MyBean[] ba = l.get(3, MyBean[].<jk>class</jk>);
318    *
319    *    <jc>// Value converted to a linked-list of objects.</jc>
320    *    List l1 = l.get(4, LinkedList.<jk>class</jk>);
321    *
322    *    <jc>// Value converted to a map of object keys/values.</jc>
323    *    Map m1 = l.get(5, TreeMap.<jk>class</jk>);
324    * </p>
325    *
326    * <p>
327    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
328    *
329    * @param index The index into this list.
330    * @param type The type of object to convert the entry to.
331    * @param <T> The type of object to convert the entry to.
332    * @return The converted entry.
333    */
334   public <T> T get(int index, Class<T> type) {
335      return bs().convertToType(get(index), type);
336   }
337
338   /**
339    * Get the entry at the specified index, converted to the specified type.
340    *
341    * <p>
342    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
343    *
344    * <h5 class='section'>Examples:</h5>
345    * <p class='bcode w800'>
346    *    ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>);
347    *
348    *    <jc>// Value converted to a linked-list of strings.</jc>
349    *    List&lt;String&gt; l1 = l.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
350    *
351    *    <jc>// Value converted to a linked-list of beans.</jc>
352    *    List&lt;MyBean&gt; l2 = l.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
353    *
354    *    <jc>// Value converted to a linked-list of linked-lists of strings.</jc>
355    *    List&lt;List&lt;String&gt;&gt; l3 = l.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
356    *
357    *    <jc>// Value converted to a map of string keys/values.</jc>
358    *    Map&lt;String,String&gt; m1 = l.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
359    *
360    *    <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc>
361    *    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>);
362    * </p>
363    *
364    * <p>
365    * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type.
366    *
367    * <p>
368    * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
369    *
370    * <p>
371    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
372    *
373    * <p>
374    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
375    *
376    * @param index The index into this list.
377    * @param type The type of object to convert the entry to.
378    * @param args The type arguments of the type to convert the entry to.
379    * @param <T> The type of object to convert the entry to.
380    * @return The converted entry.
381    */
382   public <T> T get(int index, Type type, Type...args) {
383      return bs().convertToType(get(index), type, args);
384   }
385
386   /**
387    * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>.
388    *
389    * @param index The index.
390    * @return The converted value.
391    */
392   public String getString(int index) {
393      return get(index, String.class);
394   }
395
396   /**
397    * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>.
398    *
399    * @param index The index.
400    * @return The converted value.
401    * @throws InvalidDataConversionException If value cannot be converted.
402    */
403   public Integer getInt(int index) {
404      return get(index, Integer.class);
405   }
406
407   /**
408    * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>.
409    *
410    * @param index The index.
411    * @return The converted value.
412    * @throws InvalidDataConversionException If value cannot be converted.
413    */
414   public Boolean getBoolean(int index) {
415      return get(index, Boolean.class);
416   }
417
418   /**
419    * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>.
420    *
421    * @param index The index.
422    * @return The converted value.
423    * @throws InvalidDataConversionException If value cannot be converted.
424    */
425   public Long getLong(int index) {
426      return get(index, Long.class);
427   }
428
429   /**
430    * Shortcut for calling <code>get(index, Map.<jk>class</jk>)</code>.
431    *
432    * @param index The index.
433    * @return The converted value.
434    * @throws InvalidDataConversionException If value cannot be converted.
435    */
436   public Map<?,?> getMap(int index) {
437      return get(index, Map.class);
438   }
439
440   /**
441    * Same as {@link #getMap(int)} except converts the keys and values to the specified types.
442    *
443    * @param index The index.
444    * @param keyType The key type class.
445    * @param valType The value type class.
446    * @return The converted value.
447    * @throws InvalidDataConversionException If value cannot be converted.
448    */
449   public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
450      return bs().convertToType(get(index), Map.class, keyType, valType);
451   }
452
453   /**
454    * Shortcut for calling <code>get(index, List.<jk>class</jk>)</code>.
455    *
456    * @param index The index.
457    * @return The converted value.
458    * @throws InvalidDataConversionException If value cannot be converted.
459    */
460   public List<?> getList(int index) {
461      return get(index, List.class);
462   }
463
464   /**
465    * Same as {@link #getList(int)} except converts the elements to the specified types.
466    *
467    * @param index The index.
468    * @param elementType The element type class.
469    * @return The converted value.
470    * @throws InvalidDataConversionException If value cannot be converted.
471    */
472   public <E> List<E> getList(int index, Class<E> elementType) {
473      return bs().convertToType(get(index), List.class, elementType);
474   }
475
476   /**
477    * Shortcut for calling <code>get(index, ObjectMap.<jk>class</jk>)</code>.
478    *
479    * @param index The index.
480    * @return The converted value.
481    * @throws InvalidDataConversionException If value cannot be converted.
482    */
483   public ObjectMap getObjectMap(int index) {
484      return get(index, ObjectMap.class);
485   }
486
487   /**
488    * Shortcut for calling <code>get(index, ObjectList.<jk>class</jk>)</code>.
489    *
490    * @param index The index.
491    * @return The converted value.
492    * @throws InvalidDataConversionException If value cannot be converted.
493    */
494   public ObjectList getObjectList(int index) {
495      return get(index, ObjectList.class);
496   }
497
498   /**
499    * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in
500    * this POJO.
501    *
502    * <p>
503    * For example, the following code is equivalent:
504    * </p>
505    * <p class='bcode w800'>
506    *    ObjectMap m = getObjectMap();
507    *
508    *    <jc>// Long way</jc>
509    *    <jk>long</jk> l = m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).getLong(<js>"baz"</js>);
510    *
511    *    <jc>// Using this method</jc>
512    *    <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>);
513    * </p>
514    *
515    * <p>
516    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
517    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
518    *
519    * @param path The path to the entry.
520    * @param type The class type.
521    *
522    * @param <T> The class type.
523    * @return The value, or <jk>null</jk> if the entry doesn't exist.
524    */
525   public <T> T getAt(String path, Class<T> type) {
526      return getPojoRest().get(path, type);
527   }
528
529   /**
530    * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections.
531    *
532    * @param path The path to the entry.
533    * @param type The class type.
534    * @param args The class parameter types.
535    *
536    * @param <T> The class type.
537    * @return The value, or <jk>null</jk> if the entry doesn't exist.
538    */
539   public <T> T getAt(String path, Type type, Type...args) {
540      return getPojoRest().get(path, type, args);
541   }
542
543   /**
544    * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries
545    * in this POJO.
546    *
547    * <p>
548    * For example, the following code is equivalent:
549    * </p>
550    * <p class='bcode w800'>
551    *    ObjectMap m = getObjectMap();
552    *
553    *    <jc>// Long way</jc>
554    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).put(<js>"baz"</js>, 123);
555    *
556    *    <jc>// Using this method</jc>
557    *    m.putAt(<js>"foo/bar/0/baz"</js>, 123);
558    * </p>
559    *
560    * <p>
561    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
562    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
563    *
564    * @param path The path to the entry.
565    * @param o The new value.
566    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
567    */
568   public Object putAt(String path, Object o) {
569      return getPojoRest().put(path, o);
570   }
571
572   /**
573    * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays.
574    *
575    * <p>
576    * For example, the following code is equivalent:
577    * </p>
578    * <p class='bcode w800'>
579    *    ObjectMap m = getObjectMap();
580    *
581    *    <jc>// Long way</jc>
582    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).append(123);
583    *
584    *    <jc>// Using this method</jc>
585    *    m.postAt(<js>"foo/bar"</js>, 123);
586    * </p>
587    *
588    * <p>
589    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
590    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
591    *
592    * @param path The path to the entry.
593    * @param o The new value.
594    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
595    */
596   public Object postAt(String path, Object o) {
597      return getPojoRest().post(path, o);
598   }
599
600   /**
601    * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in
602    * this POJO.
603    *
604    * <p>
605    * For example, the following code is equivalent:
606    * </p>
607    * <p class='bcode w800'>
608    *    ObjectMap m = getObjectMap();
609    *
610    *    <jc>// Long way</jc>
611    *    m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(1).remove(<js>"baz"</js>);
612    *
613    *    <jc>// Using this method</jc>
614    *    m.deleteAt(<js>"foo/bar/0/baz"</js>);
615    * </p>
616    *
617    * <p>
618    * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various
619    * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays).
620    *
621    * @param path The path to the entry.
622    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
623    */
624   public Object deleteAt(String path) {
625      return getPojoRest().delete(path);
626   }
627
628   /**
629    * Creates an {@link Iterable} with elements of the specified child type.
630    *
631    * <p>
632    * Attempts to convert the child objects to the correct type if they aren't already the correct type.
633    *
634    * <p>
635    * The <code>next()</code> method on the returned iterator may throw a {@link InvalidDataConversionException} if
636    * the next element cannot be converted to the specified type.
637    *
638    * <p>
639    * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions.
640    *
641    * <h5 class='section'>Example:</h5>
642    * <p class='bcode w800'>
643    *    <jc>// Iterate over a list of ObjectMaps.</jc>
644    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>);
645    *    for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) {
646    *       <jc>// Do something with m.</jc>
647    *    }
648    *
649    *    <jc>// Iterate over a list of ints.</jc>
650    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>);
651    *    for (Integer i : l.elements(Integer.<jk>class</jk>)) {
652    *       <jc>// Do something with i.</jc>
653    *    }
654    *
655    *    <jc>// Iterate over a list of beans.</jc>
656    *    <jc>// Automatically converts to beans.</jc>
657    *    ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>);
658    *    for (Person p : l.elements(Person.<jk>class</jk>)) {
659    *       <jc>// Do something with p.</jc>
660    *    }
661    * </p>
662    *
663    * @param <E> The child object type.
664    * @param childType The child object type.
665    * @return A new <code>Iterable</code> object over this list.
666    */
667   public <E> Iterable<E> elements(final Class<E> childType) {
668      final Iterator<?> i = iterator();
669      return new Iterable<E>() {
670
671         @Override /* Iterable */
672         public Iterator<E> iterator() {
673            return new Iterator<E>() {
674
675               @Override /* Iterator */
676               public boolean hasNext() {
677                  return i.hasNext();
678               }
679
680               @Override /* Iterator */
681               public E next() {
682                  return bs().convertToType(i.next(), childType);
683               }
684
685               @Override /* Iterator */
686               public void remove() {
687                  i.remove();
688               }
689
690            };
691         }
692      };
693   }
694
695   /**
696    * Returns the {@link ClassMeta} of the class of the object at the specified index.
697    *
698    * @param index An index into this list, zero-based.
699    * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
700    */
701   public ClassMeta<?> getClassMeta(int index) {
702      return bs().getClassMetaForObject(get(index));
703   }
704
705   private PojoRest getPojoRest() {
706      if (pojoRest == null)
707         pojoRest = new PojoRest(this);
708      return pojoRest;
709   }
710
711   /**
712    * Serialize this array to a string using the specified serializer.
713    *
714    * @param serializer The serializer to use to convert this object to a string.
715    * @return This object as a serialized string.
716    * @throws SerializeException If a problem occurred trying to convert the output.
717    */
718   public String toString(WriterSerializer serializer) throws SerializeException {
719      return serializer.serialize(this);
720   }
721
722   /**
723    * Returns <jk>true</jk> if this list is unmodifiable.
724    *
725    * @return <jk>true</jk> if this list is unmodifiable.
726    */
727   public boolean isUnmodifiable() {
728      return false;
729   }
730
731   /**
732    * Returns a modifiable copy of this list if it's unmodifiable.
733    *
734    * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable.
735    */
736   public ObjectList modifiable() {
737      if (isUnmodifiable())
738         return new ObjectList(this);
739      return this;
740   }
741
742   /**
743    * Returns an unmodifiable copy of this list if it's modifiable.
744    *
745    * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable.
746    */
747   public ObjectList unmodifiable() {
748      if (this instanceof UnmodifiableObjectList)
749         return this;
750      return new UnmodifiableObjectList(this);
751   }
752
753   /**
754    * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer.
755    */
756   @Override /* Object */
757   public String toString() {
758      try {
759         return this.toString(SimpleJsonSerializer.DEFAULT);
760      } catch (SerializeException e) {
761         return e.getLocalizedMessage();
762      }
763   }
764
765   /**
766    * Convenience method for serializing this ObjectList to the specified Writer using the JsonSerializer.DEFAULT
767    * serializer.
768    *
769    * @param w The writer to send the serialized contents of this object.
770    * @throws IOException If a problem occurred trying to write to the writer.
771    * @throws SerializeException If a problem occurred trying to convert the output.
772    */
773   public void serializeTo(Writer w) throws IOException, SerializeException {
774      JsonSerializer.DEFAULT.serialize(this);
775   }
776
777   /**
778    * Converts this object into the specified class type.
779    *
780    * <p>
781    * TODO - The current implementation is very inefficient.
782    *
783    * @param cm The class type to convert this object to.
784    * @return A converted object.
785    */
786   public Object cast(ClassMeta<?> cm) {
787      try {
788         return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT.serialize(this), cm);
789      } catch (ParseException | SerializeException e) {
790         throw new RuntimeException(e);
791      }
792   }
793
794   private static final class UnmodifiableObjectList extends ObjectList {
795      private static final long serialVersionUID = 1L;
796
797      UnmodifiableObjectList(ObjectList contents) {
798         super();
799         if (contents != null) {
800            for (Object e : this) {
801               super.add(e);
802            }
803         }
804      }
805
806      @Override /* List */
807      public void add(int location, Object object) {
808         throw new UnsupportedOperationException("ObjectList is read-only.");
809      }
810
811      @Override /* List */
812      public Object remove(int location) {
813         throw new UnsupportedOperationException("ObjectList is read-only.");
814      }
815
816      @Override /* List */
817      public Object set(int location, Object object) {
818         throw new UnsupportedOperationException("ObjectList is read-only.");
819      }
820
821      @Override
822      public final boolean isUnmodifiable() {
823         return true;
824      }
825   }
826
827   BeanSession bs() {
828      if (session == null)
829         session = BeanContext.DEFAULT.createBeanSession();
830      return session;
831   }
832}