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