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.collections;
014
015import static org.apache.juneau.common.internal.ThrowableUtils.*;
016import static org.apache.juneau.internal.ConsumerUtils.*;
017
018import java.io.*;
019import java.lang.reflect.*;
020import java.util.*;
021import java.util.function.*;
022
023import org.apache.juneau.*;
024import org.apache.juneau.common.internal.*;
025import org.apache.juneau.json.*;
026import org.apache.juneau.marshaller.*;
027import org.apache.juneau.objecttools.*;
028import org.apache.juneau.parser.*;
029import org.apache.juneau.serializer.*;
030
031/**
032 * Java implementation of a JSON array.
033 *
034 * <p>
035 * An extension of {@link LinkedList}, so all methods available to in that class are also available to this class.
036 *
037 * <p>
038 * Note that the use of this class is optional for generating JSON.  The serializers will accept any objects that implement the
039 * {@link Collection} interface.  But this class provides some useful additional functionality when working with JSON
040 * models constructed from Java Collections Framework objects.  For example, a constructor is provided for converting a
041 * JSON array string directly into a {@link List}.  It also contains accessor methods for to avoid common typecasting
042 * when accessing elements in a list.
043 *
044 * <h5 class='section'>Example:</h5>
045 * <p class='bjava'>
046 *    <jc>// Construct an empty List</jc>
047 *    JsonList <jv>list</jv> = JsonList.<jsm>of</jsm>();
048 *
049 *    <jc>// Construct a list of objects using various methods</jc>
050 *    <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>).a(123).a(<jk>true</jk>);
051 *    <jv>list</jv> = JsonList.<jsm>of</jsm>().a(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
052 *    <jv>list</jv> = JsonList.<jsm>of</jsm>(<js>"foo"</js>, 123, <jk>true</jk>);  <jc>// Equivalent</jc>
053 *
054 *    <jc>// Construct a list of integers from JSON</jc>
055 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
056 *
057 *    <jc>// Construct a list of generic JsonMap objects from JSON</jc>
058 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
059 *
060 *    <jc>// Construct a list of integers from XML</jc>
061 *    String <jv>xml</jv> = <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>;
062 *    <jv>list</jv> = JsonList.<jsm>of</jsm>(<jv>xml</jv>, XmlParser.<jsf>DEFAULT</jsf>);
063 *    <jv>list</jv> = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(<jv>xml</jv>);  <jc>// Equivalent</jc>
064 *    <jv>list</jv> = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
065 *    <jv>list</jv> = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
066 *    <jv>list</jv> = XmlParser.<jsf>DEFAULT</jsf>.parse(JsonList.<jk>class</jk>, <jv>xml</jv>);  <jc>// Equivalent</jc>
067 *
068 *    <jc>// Construct JSON from JsonList</jc>
069 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:'bing'}]"</js>);
070 *    String <jv>json</jv> = <jv>list</jv>.toString();  <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc>
071 *    <jv>json</jv> = <jv>list</jv>.toString(JsonSerializer.<jsf>DEFAULT</jsf>);  <jc>// Equivalent</jc>
072 *    <jv>json</jv> = JsonSerializer.<jsf>DEFAULT</jsf>.serialize(<jv>list</jv>);  <jc>// Equivalent</jc>
073 *
074 *    <jc>// Get one of the entries in the list as an Integer</jc>
075 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
076 *    Integer <jv>integer</jv> = <jv>list</jv>.getInt(1);
077 *    <jv>list</jv> = <jv>list</jv>.get(Integer.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
078 *
079 *    <jc>// Get one of the entries in the list as an Float</jc>
080 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
081 *    Float <jv>_float</jv> = <jv>list</jv>.getFloat(1); <jc>// Returns 2f </jc>
082 *    <jv>_float</jv> = <jv>list</jv>.get(Float.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
083 *
084 *    <jc>// Same as above, except converted to a String</jc>
085 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
086 *    String <jv>string</jv> = <jv>list</jv>.getString(1); <jc>// Returns "2" </jc>
087 *    <jv>string</jv> = <jv>list</jv>.get(String.<jk>class</jk>, 1);  <jc>// Equivalent</jc>
088 *
089 *    <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc>
090 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
091 *    Person <jv>person</jv> = <jv>list</jv>.get(Person.<jk>class</jk>, 0);
092 *
093 *    <jc>// Iterate over a list of beans using the elements() method</jc>
094 *    <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
095 *    <jk>for</jk> (Person <jv>person</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>) {
096 *       <jc>// Do something with p</jc>
097 *    }
098 * </p>
099 *
100 * <h5 class='section'>Notes:</h5><ul>
101 *    <li class='warn'>This class is not thread safe.
102 * </ul>
103 *
104 * <h5 class='section'>See Also:</h5><ul>
105
106 * </ul>
107 *
108 * @serial exclude
109 */
110public class JsonList extends LinkedList<Object> {
111
112   //-----------------------------------------------------------------------------------------------------------------
113   // Static
114   //-----------------------------------------------------------------------------------------------------------------
115
116   private static final long serialVersionUID = 1L;
117
118   /**
119    * Parses a string that can consist of either a JSON array or comma-delimited list.
120    *
121    * <p>
122    * The type of string is auto-detected.
123    *
124    * @param s The string to parse.
125    * @return The parsed string.
126    * @throws ParseException Malformed input encountered.
127    */
128   public static JsonList ofJsonOrCdl(String s) throws ParseException {
129      if (StringUtils.isEmpty(s))
130         return null;
131      if (! StringUtils.isJsonArray(s, true))
132         return new JsonList((Object[])StringUtils.split(s.trim(), ','));
133      return new JsonList(s);
134   }
135
136   //-----------------------------------------------------------------------------------------------------------------
137   // Instance
138   //-----------------------------------------------------------------------------------------------------------------
139
140   transient BeanSession session = null;
141   private transient ObjectRest objectRest;
142
143   /**
144    * An empty read-only JsonList.
145    *
146    * @serial exclude
147    */
148   public static final JsonList EMPTY_LIST = new JsonList() {
149      private static final long serialVersionUID = 1L;
150
151      @Override /* List */
152      public void add(int location, Object object) {
153         throw new UnsupportedOperationException("Not supported on read-only object.");
154      }
155
156      @Override /* List */
157      public ListIterator<Object> listIterator(final int location) {
158         return Collections.emptyList().listIterator(location);
159      }
160
161      @Override /* List */
162      public Object remove(int location) {
163         throw new UnsupportedOperationException("Not supported on read-only object.");
164      }
165
166      @Override /* List */
167      public Object set(int location, Object object) {
168         throw new UnsupportedOperationException("Not supported on read-only object.");
169      }
170
171      @Override /* List */
172      public List<Object> subList(int start, int end) {
173         return Collections.emptyList().subList(start, end);
174      }
175   };
176
177   //------------------------------------------------------------------------------------------------------------------
178   // Constructors
179   //------------------------------------------------------------------------------------------------------------------
180
181   /**
182    * Construct an empty list.
183    */
184   public JsonList() {}
185
186   /**
187    * Construct an empty list with the specified bean context.
188    *
189    * @param session The bean session to use for creating beans.
190    */
191   public JsonList(BeanSession session) {
192      this.session = session;
193   }
194
195   /**
196    * Construct a list initialized with the specified list.
197    *
198    * @param copyFrom
199    *    The list to copy.
200    *    <br>Can be <jk>null</jk>.
201    */
202   public JsonList(Collection<?> copyFrom) {
203      super(copyFrom);
204   }
205
206   /**
207    * Construct a list initialized with the specified JSON.
208    *
209    * @param json
210    *    The JSON text to parse.
211    *    <br>Can be normal or simplified JSON.
212    * @throws ParseException Malformed input encountered.
213    */
214   public JsonList(CharSequence json) throws ParseException {
215      this(json, JsonParser.DEFAULT);
216   }
217
218   /**
219    * Construct a list initialized with the specified string.
220    *
221    * @param in
222    *    The input being parsed.
223    *    <br>Can be <jk>null</jk>.
224    * @param p
225    *    The parser to use to parse the input.
226    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
227    * @throws ParseException Malformed input encountered.
228    */
229   public JsonList(CharSequence in, Parser p) throws ParseException {
230      this(p == null ? BeanContext.DEFAULT_SESSION : p.getBeanContext().getSession());
231      if (p == null)
232         p = JsonParser.DEFAULT;
233      if (in != null)
234         p.parseIntoCollection(in, this, bs().object());
235   }
236
237   /**
238    * Construct a list initialized with the specified reader containing JSON.
239    *
240    * @param json
241    *    The reader containing JSON text to parse.
242    *    <br>Can contain normal or simplified JSON.
243    * @throws ParseException Malformed input encountered.
244    */
245   public JsonList(Reader json) throws ParseException {
246      parse(json, JsonParser.DEFAULT);
247   }
248
249   /**
250    * Construct a list initialized with the specified string.
251    *
252    * @param in
253    *    The reader containing the input being parsed.
254    *    <br>Can contain normal or simplified JSON.
255    * @param p
256    *    The parser to use to parse the input.
257    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
258    * @throws ParseException Malformed input encountered.
259    */
260   public JsonList(Reader in, Parser p) throws ParseException {
261      this(p == null ? BeanContext.DEFAULT_SESSION : p.getBeanContext().getSession());
262      parse(in, p);
263   }
264
265   /**
266    * Construct a list initialized with the contents.
267    *
268    * @param entries The entries to add to this list.
269    */
270   public JsonList(Object... entries) {
271      Collections.addAll(this, entries);
272   }
273
274   //------------------------------------------------------------------------------------------------------------------
275   // Creators
276   //------------------------------------------------------------------------------------------------------------------
277
278   /**
279    * Construct an empty list.
280    *
281    * @return An empty list.
282    */
283   public static JsonList create() {
284      return new JsonList();
285   }
286
287   /**
288    * Construct a list initialized with the specified list.
289    *
290    * @param values
291    *    The list to copy.
292    *    <br>Can be <jk>null</jk>.
293    * @return A new list or <jk>null</jk> if the list was <jk>null</jk>.
294    */
295   public static JsonList of(Collection<?> values) {
296      return values == null ? null : new JsonList(values);
297   }
298
299   /**
300    * Convenience method for creating a list of collection objects.
301    *
302    * @param values The initial values.
303    * @return A new list.
304    */
305   public static JsonList ofCollections(Collection<?>...values) {
306      JsonList l = new JsonList();
307      for (Collection<?> v : values)
308         l.add(v);
309      return l;
310   }
311
312   /**
313    * Convenience method for creating a list of array objects.
314    *
315    * @param values The initial values.
316    * @return A new list.
317    */
318   public static JsonList ofArrays(Object[]...values) {
319      JsonList l = new JsonList();
320      for (Object[] v : values)
321         l.add(v);
322      return l;
323   }
324
325   /**
326    * Construct a list initialized with the specified JSON string.
327    *
328    * @param json
329    *    The JSON text to parse.
330    *    <br>Can be normal or simplified JSON.
331    * @return A new list or <jk>null</jk> if the string was null.
332    * @throws ParseException Malformed input encountered.
333    */
334   public static JsonList ofJson(CharSequence json) throws ParseException {
335      return json == null ? null : new JsonList(json);
336   }
337
338   /**
339    * Construct a list initialized with the specified string.
340    *
341    * @param in
342    *    The input being parsed.
343    *    <br>Can be <jk>null</jk>.
344    * @param p
345    *    The parser to use to parse the input.
346    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
347    * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
348    * @throws ParseException Malformed input encountered.
349    */
350   public static JsonList ofText(CharSequence in, Parser p) throws ParseException {
351      return in == null ? null : new JsonList(in, p);
352   }
353
354   /**
355    * Construct a list initialized with the specified reader containing JSON.
356    *
357    * @param json
358    *    The reader containing JSON text to parse.
359    *    <br>Can contain normal or simplified JSON.
360    * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
361    * @throws ParseException Malformed input encountered.
362    */
363   public static JsonList ofJson(Reader json) throws ParseException {
364      return json == null ? null : new JsonList(json);
365   }
366
367   /**
368    * Construct a list initialized with the specified string.
369    *
370    * @param in
371    *    The reader containing the input being parsed.
372    *    <br>Can contain normal or simplified JSON.
373    * @param p
374    *    The parser to use to parse the input.
375    *    <br>If <jk>null</jk>, uses {@link JsonParser}.
376    * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
377    * @throws ParseException Malformed input encountered.
378    */
379   public static JsonList ofText(Reader in, Parser p) throws ParseException {
380      return in == null ? null : new JsonList(in);
381   }
382
383   /**
384    * Construct a list initialized with the specified values.
385    *
386    * @param values The values to add to this list.
387    * @return A new list, never <jk>null</jk>.
388    */
389   public static JsonList of(Object... values) {
390      return new JsonList(values);
391   }
392
393   //------------------------------------------------------------------------------------------------------------------
394   // Initializers
395   //------------------------------------------------------------------------------------------------------------------
396
397   /**
398    * Override the default bean session used for converting POJOs.
399    *
400    * <p>
401    * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases.
402    *
403    * <p>
404    * Useful if you're serializing/parsing beans with transforms defined.
405    *
406    * @param session The new bean session.
407    * @return This object.
408    */
409   public JsonList session(BeanSession session) {
410      this.session = session;
411      return this;
412   }
413
414   //------------------------------------------------------------------------------------------------------------------
415   // Appenders
416   //------------------------------------------------------------------------------------------------------------------
417
418   /**
419    * Adds the value to this list.
420    *
421    * @param value The value to add to this list.
422    * @return This object.
423    */
424   public JsonList append(Object value) {
425      add(value);
426      return this;
427   }
428
429   /**
430    * Adds all the values in the specified array to this list.
431    *
432    * @param values The values to add to this list.
433    * @return This object.
434    */
435   public JsonList append(Object...values) {
436      Collections.addAll(this, values);
437      return this;
438   }
439
440   /**
441    * Adds all the values in the specified collection to this list.
442    *
443    * @param values The values to add to this list.
444    * @return This object.
445    */
446   public JsonList append(Collection<?> values) {
447      if (values != null)
448         addAll(values);
449      return this;
450   }
451
452   /**
453    * Adds an entry to this list if the boolean flag is <jk>true</jk>.
454    *
455    * @param flag The boolean flag.
456    * @param value The value to add.
457    * @return This object.
458    */
459   public JsonList appendIf(boolean flag, Object value) {
460      if (flag)
461         append(value);
462      return this;
463   }
464
465   /**
466    * Adds all the entries in the specified collection to this list in reverse order.
467    *
468    * @param values The collection to add to this list.
469    * @return This object.
470    */
471   public JsonList appendReverse(List<?> values) {
472      for (ListIterator<?> i = values.listIterator(values.size()); i.hasPrevious();)
473         add(i.previous());
474      return this;
475   }
476
477   /**
478    * Adds the contents of the array to the list in reverse order.
479    *
480    * <p>
481    * i.e. add values from the array from end-to-start order to the end of the list.
482    *
483    * @param values The collection to add to this list.
484    * @return This object.
485    */
486   public JsonList appendReverse(Object...values) {
487      for (int i = values.length - 1; i >= 0; i--)
488         add(values[i]);
489      return this;
490   }
491
492   /**
493    * Add if predicate matches.
494    *
495    * @param <T> The type being tested.
496    * @param test The predicate to match against.
497    * @param value The value to add if the predicate matches.
498    * @return This object.
499    */
500   public  <T> JsonList appendIf(Predicate<T> test, T value) {
501      return appendIf(test(test, value), value);
502   }
503
504   //------------------------------------------------------------------------------------------------------------------
505   // Retrievers
506   //------------------------------------------------------------------------------------------------------------------
507
508   /**
509    * Get the entry at the specified index, converted to the specified type.
510    *
511    * <p>
512    * This is the preferred get method for simple types.
513    *
514    * <h5 class='section'>Examples:</h5>
515    * <p class='bjava'>
516    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
517    *
518    *    <jc>// Value converted to a string.</jc>
519    *    String <jv>string</jv> = <jv>list</jv>.get(1, String.<jk>class</jk>);
520    *
521    *    <jc>// Value converted to a bean.</jc>
522    *    MyBean <jv>bean</jv> = <jv>list</jv>.get(2, MyBean.<jk>class</jk>);
523    *
524    *    <jc>// Value converted to a bean array.</jc>
525    *    MyBean[] <jv>beanArray</jv> = <jv>list</jv>.get(3, MyBean[].<jk>class</jk>);
526    *
527    *    <jc>// Value converted to a linked-list of objects.</jc>
528    *    List <jv>list2</jv> = <jv>list</jv>.get(4, LinkedList.<jk>class</jk>);
529    *
530    *    <jc>// Value converted to a map of object keys/values.</jc>
531    *    Map <jv>map</jv> = <jv>list</jv>.get(5, TreeMap.<jk>class</jk>);
532    * </p>
533    *
534    * <p>
535    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
536    *
537    * @param index The index into this list.
538    * @param type The type of object to convert the entry to.
539    * @param <T> The type of object to convert the entry to.
540    * @return The converted entry.
541    */
542   public <T> T get(int index, Class<T> type) {
543      return bs().convertToType(get(index), type);
544   }
545
546   /**
547    * Get the entry at the specified index, converted to the specified type.
548    *
549    * <p>
550    * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps).
551    *
552    * <h5 class='section'>Examples:</h5>
553    * <p class='bjava'>
554    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
555    *
556    *    <jc>// Value converted to a linked-list of strings.</jc>
557    *    List&lt;String&gt; <jv>list1</jv> = <jv>list</jv>.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
558    *
559    *    <jc>// Value converted to a linked-list of beans.</jc>
560    *    List&lt;MyBean&gt; <jv>list2</jv> = <jv>list</jv>.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
561    *
562    *    <jc>// Value converted to a linked-list of linked-lists of strings.</jc>
563    *    List&lt;List&lt;String&gt;&gt; <jv>list3</jv> = <jv>list</jv>.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>);
564    *
565    *    <jc>// Value converted to a map of string keys/values.</jc>
566    *    Map&lt;String,String&gt; <jv>map1</jv> = <jv>list</jv>.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>);
567    *
568    *    <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc>
569    *    Map&lt;String,List&lt;MyBean&gt;&gt; <jv>map2</jv> = <jv>list</jv>.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>);
570    * </p>
571    *
572    * <p>
573    * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type.
574    *
575    * <p>
576    * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types.
577    *
578    * <p>
579    * The array can be arbitrarily long to indicate arbitrarily complex data structures.
580    *
581    * <p>
582    * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
583    *
584    * @param index The index into this list.
585    * @param type The type of object to convert the entry to.
586    * @param args The type arguments of the type to convert the entry to.
587    * @param <T> The type of object to convert the entry to.
588    * @return The converted entry.
589    */
590   public <T> T get(int index, Type type, Type...args) {
591      return bs().convertToType(get(index), type, args);
592   }
593
594   /**
595    * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>.
596    *
597    * @param index The index.
598    * @return The converted value.
599    */
600   public String getString(int index) {
601      return get(index, String.class);
602   }
603
604   /**
605    * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>.
606    *
607    * @param index The index.
608    * @return The converted value.
609    * @throws InvalidDataConversionException If value cannot be converted.
610    */
611   public Integer getInt(int index) {
612      return get(index, Integer.class);
613   }
614
615   /**
616    * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>.
617    *
618    * @param index The index.
619    * @return The converted value.
620    * @throws InvalidDataConversionException If value cannot be converted.
621    */
622   public Boolean getBoolean(int index) {
623      return get(index, Boolean.class);
624   }
625
626   /**
627    * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>.
628    *
629    * @param index The index.
630    * @return The converted value.
631    * @throws InvalidDataConversionException If value cannot be converted.
632    */
633   public Long getLong(int index) {
634      return get(index, Long.class);
635   }
636
637   /**
638    * Shortcut for calling <code>get(index, JsonMap.<jk>class</jk>)</code>.
639    *
640    * @param index The index.
641    * @return The converted value.
642    * @throws InvalidDataConversionException If value cannot be converted.
643    */
644   public JsonMap getMap(int index) {
645      return get(index, JsonMap.class);
646   }
647
648   /**
649    * Same as {@link #getMap(int)} except converts the keys and values to the specified types.
650    *
651    * @param <K> The key type class.
652    * @param <V> The value type class.
653    * @param index The index.
654    * @param keyType The key type class.
655    * @param valType The value type class.
656    * @return The converted value.
657    * @throws InvalidDataConversionException If value cannot be converted.
658    */
659   public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
660      return bs().convertToType(get(index), Map.class, keyType, valType);
661   }
662
663   /**
664    * Shortcut for calling <code>get(index, JsonList.<jk>class</jk>)</code>.
665    *
666    * @param index The index.
667    * @return The converted value.
668    * @throws InvalidDataConversionException If value cannot be converted.
669    */
670   public JsonList getList(int index) {
671      return get(index, JsonList.class);
672   }
673
674   /**
675    * Same as {@link #getList(int)} except converts the elements to the specified types.
676    *
677    * @param <E> The element type.
678    * @param index The index.
679    * @param elementType The element type class.
680    * @return The converted value.
681    * @throws InvalidDataConversionException If value cannot be converted.
682    */
683   public <E> List<E> getList(int index, Class<E> elementType) {
684      return bs().convertToType(get(index), List.class, elementType);
685   }
686
687   //------------------------------------------------------------------------------------------------------------------
688   // POJO REST methods.
689   //------------------------------------------------------------------------------------------------------------------
690
691   /**
692    * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in
693    * this POJO.
694    *
695    * <p>
696    * For example, the following code is equivalent:
697    * </p>
698    * <p class='bjava'>
699    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
700    *
701    *    <jc>// Long way</jc>
702    *    <jk>long</jk> <jv>long1</jv> = <jv>list</jv>.getMap(<js>"0"</js>).getLong(<js>"baz"</js>);
703    *
704    *    <jc>// Using this method</jc>
705    *    <jk>long</jk> <jv>long2</jv> = <jv>list</jv>.getAt(<js>"0/baz"</js>, <jk>long</jk>.<jk>class</jk>);
706    * </p>
707    *
708    * <p>
709    * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
710    * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
711    *
712    * @param path The path to the entry.
713    * @param type The class type.
714    *
715    * @param <T> The class type.
716    * @return The value, or <jk>null</jk> if the entry doesn't exist.
717    */
718   public <T> T getAt(String path, Class<T> type) {
719      return getObjectRest().get(path, type);
720   }
721
722   /**
723    * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections.
724    *
725    * @param path The path to the entry.
726    * @param type The class type.
727    * @param args The class parameter types.
728    *
729    * @param <T> The class type.
730    * @return The value, or <jk>null</jk> if the entry doesn't exist.
731    */
732   public <T> T getAt(String path, Type type, Type...args) {
733      return getObjectRest().get(path, type, args);
734   }
735
736   /**
737    * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries
738    * in this POJO.
739    *
740    * <p>
741    * For example, the following code is equivalent:
742    * </p>
743    * <p class='bjava'>
744    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
745    *
746    *    <jc>// Long way</jc>
747    *    <jv>list</jv>.getMap(<js>"0"</js>).put(<js>"baz"</js>, 123);
748    *
749    *    <jc>// Using this method</jc>
750    *    <jv>list</jv>.putAt(<js>"0/baz"</js>, 123);
751    * </p>
752    *
753    * <p>
754    * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
755    * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
756    *
757    * @param path The path to the entry.
758    * @param o The new value.
759    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
760    */
761   public Object putAt(String path, Object o) {
762      return getObjectRest().put(path, o);
763   }
764
765   /**
766    * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays.
767    *
768    * <p>
769    * For example, the following code is equivalent:
770    * </p>
771    * <p class='bjava'>
772    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
773    *
774    *    <jc>// Long way</jc>
775    *    <jv>list</jv>.getMap(0).getList(<js>"bar"</js>).append(123);
776    *
777    *    <jc>// Using this method</jc>
778    *    <jv>list</jv>.postAt(<js>"0/bar"</js>, 123);
779    * </p>
780    *
781    * <p>
782    * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
783    * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
784    *
785    * @param path The path to the entry.
786    * @param o The new value.
787    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
788    */
789   public Object postAt(String path, Object o) {
790      return getObjectRest().post(path, o);
791   }
792
793   /**
794    * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in
795    * this POJO.
796    *
797    * <p>
798    * For example, the following code is equivalent:
799    * </p>
800    * <p class='bjava'>
801    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"..."</js>);
802    *
803    *    <jc>// Long way</jc>
804    *    <jv>list</jv>.getMap(0).getList(<js>"bar"</js>).delete(0);
805    *
806    *    <jc>// Using this method</jc>
807    *    <jv>list</jv>.deleteAt(<js>"0/bar/0"</js>);
808    * </p>
809    *
810    * <p>
811    * This method uses the {@link ObjectRest} class to perform the lookup, so the map can contain any of the various
812    * class types that the {@link ObjectRest} class supports (e.g. beans, collections, arrays).
813    *
814    * @param path The path to the entry.
815    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
816    */
817   public Object deleteAt(String path) {
818      return getObjectRest().delete(path);
819   }
820
821   //------------------------------------------------------------------------------------------------------------------
822   // Other methods
823   //------------------------------------------------------------------------------------------------------------------
824
825   /**
826    * Returns the {@link BeanSession} currently associated with this list.
827    *
828    * @return The {@link BeanSession} currently associated with this list.
829    */
830   public BeanSession getBeanSession() {
831      return session;
832   }
833
834   /**
835    * Sets the {@link BeanSession} currently associated with this list.
836    *
837    * @param value The {@link BeanSession} currently associated with this list.
838    * @return This object.
839    */
840   public JsonList setBeanSession(BeanSession value) {
841      this.session = value;
842      return this;
843   }
844
845   /**
846    * Creates an {@link Iterable} with elements of the specified child type.
847    *
848    * <p>
849    * Attempts to convert the child objects to the correct type if they aren't already the correct type.
850    *
851    * <p>
852    * The <c>next()</c> method on the returned iterator may throw a {@link InvalidDataConversionException} if
853    * the next element cannot be converted to the specified type.
854    *
855    * <p>
856    * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions.
857    *
858    * <h5 class='section'>Example:</h5>
859    * <p class='bjava'>
860    *    <jc>// Iterate over a list of JsonMaps.</jc>
861    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{foo:'bar'},{baz:123}]"</js>);
862    *    <jk>for</jk> (JsonMap <jv>map</jv> : <jv>list</jv>.elements(JsonMap.<jk>class</jk>)) {
863    *       <jc>// Do something with map.</jc>
864    *    }
865    *
866    *    <jc>// Iterate over a list of ints.</jc>
867    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[1,2,3]"</js>);
868    *    <jk>for</jk> (Integer <jv>i</jv> : <jv>list</jv>.elements(Integer.<jk>class</jk>)) {
869    *       <jc>// Do something with i.</jc>
870    *    }
871    *
872    *    <jc>// Iterate over a list of beans.</jc>
873    *    <jc>// Automatically converts to beans.</jc>
874    *    JsonList <jv>list</jv> = JsonList.<jsm>ofJson</jsm>(<js>"[{name:'John Smith',age:45}]"</js>);
875    *    <jk>for</jk> (Person <jv>p</jv> : <jv>list</jv>.elements(Person.<jk>class</jk>)) {
876    *       <jc>// Do something with p.</jc>
877    *    }
878    * </p>
879    *
880    * @param <E> The child object type.
881    * @param childType The child object type.
882    * @return A new <c>Iterable</c> object over this list.
883    */
884    public <E> Iterable<E> elements(final Class<E> childType) {
885        final Iterator<?> iterator = iterator();
886        return () -> new Iterator<>() {
887
888            @Override /* Iterator */
889            public boolean hasNext() {
890                return iterator.hasNext();
891            }
892
893            @Override /* Iterator */
894            public E next() {
895                return bs().convertToType(iterator.next(), childType);
896            }
897
898            @Override /* Iterator */
899            public void remove() {
900                iterator.remove();
901            }
902
903        };
904    }
905
906   /**
907    * Returns the {@link ClassMeta} of the class of the object at the specified index.
908    *
909    * @param index An index into this list, zero-based.
910    * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
911    */
912   public ClassMeta<?> getClassMeta(int index) {
913      return bs().getClassMetaForObject(get(index));
914   }
915
916   /**
917    * Serialize this array to a string using the specified serializer.
918    *
919    * @param serializer The serializer to use to convert this object to a string.
920    * @return This object as a serialized string.
921    */
922   public String asString(WriterSerializer serializer) {
923      return serializer.toString(this);
924   }
925
926   /**
927    * Serialize this array to Simplified JSON.
928    *
929    * @return This object as a serialized string.
930    */
931   public String asString() {
932      return Json5Serializer.DEFAULT.toString(this);
933   }
934
935   /**
936    * Returns <jk>true</jk> if this list is unmodifiable.
937    *
938    * @return <jk>true</jk> if this list is unmodifiable.
939    */
940   public boolean isUnmodifiable() {
941      return false;
942   }
943
944   /**
945    * Returns a modifiable copy of this list if it's unmodifiable.
946    *
947    * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable.
948    */
949   public JsonList modifiable() {
950      if (isUnmodifiable())
951         return new JsonList(this);
952      return this;
953   }
954
955   /**
956    * Returns an unmodifiable copy of this list if it's modifiable.
957    *
958    * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable.
959    */
960   public JsonList unmodifiable() {
961      if (this instanceof UnmodifiableJsonList)
962         return this;
963      return new UnmodifiableJsonList(this);
964   }
965
966   /**
967    * Convenience method for serializing this JsonList to the specified Writer using the JsonSerializer.DEFAULT
968    * serializer.
969    *
970    * @param w The writer to send the serialized contents of this object.
971    * @return This object.
972    * @throws IOException If a problem occurred trying to write to the writer.
973    * @throws SerializeException If a problem occurred trying to convert the output.
974    */
975   public JsonList writeTo(Writer w) throws IOException, SerializeException {
976      JsonSerializer.DEFAULT.serialize(this, w);
977      return this;
978   }
979
980   /**
981    * Converts this object into the specified class type.
982    *
983    * <p>
984    * TODO - The current implementation is very inefficient.
985    *
986    * @param cm The class type to convert this object to.
987    * @return A converted object.
988    */
989   public Object cast(ClassMeta<?> cm) {
990      try {
991         return JsonParser.DEFAULT.parse(Json5Serializer.DEFAULT.serialize(this), cm);
992      } catch (ParseException | SerializeException e) {
993         throw asRuntimeException(e);
994      }
995   }
996
997   //------------------------------------------------------------------------------------------------------------------
998   // Utility methods
999   //------------------------------------------------------------------------------------------------------------------
1000
1001   private void parse(Reader r, Parser p) throws ParseException {
1002      if (p == null)
1003         p = JsonParser.DEFAULT;
1004      p.parseIntoCollection(r, this, bs().object());
1005   }
1006
1007   private ObjectRest getObjectRest() {
1008      if (objectRest == null)
1009         objectRest = new ObjectRest(this);
1010      return objectRest;
1011   }
1012
1013   BeanSession bs() {
1014      if (session == null)
1015         session = BeanContext.DEFAULT_SESSION;
1016      return session;
1017   }
1018
1019   private static final class UnmodifiableJsonList extends JsonList {
1020      private static final long serialVersionUID = 1L;
1021
1022      UnmodifiableJsonList(JsonList contents) {
1023         if (contents != null)
1024            this.forEach(super::add);
1025      }
1026
1027      @Override /* List */
1028      public void add(int location, Object object) {
1029         throw new UnsupportedOperationException("Not supported on read-only object.");
1030      }
1031
1032      @Override /* List */
1033      public Object remove(int location) {
1034         throw new UnsupportedOperationException("Not supported on read-only object.");
1035      }
1036
1037      @Override /* List */
1038      public Object set(int location, Object object) {
1039         throw new UnsupportedOperationException("Not supported on read-only object.");
1040      }
1041
1042      @Override
1043      public boolean isUnmodifiable() {
1044         return true;
1045      }
1046   }
1047
1048   //------------------------------------------------------------------------------------------------------------------
1049   // Overridden methods.
1050   //------------------------------------------------------------------------------------------------------------------
1051
1052   /**
1053    * A synonym for {@link #toString()}
1054    *
1055    * @return This object as a JSON string.
1056    */
1057   public String asJson() {
1058      return toString();
1059   }
1060
1061   @Override /* Object */
1062   public String toString() {
1063      return Json5.of(this);
1064   }
1065}