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