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