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