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