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.marshall.collections;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.lang.reflect.*;
022import java.util.*;
023import java.util.function.*;
024
025import org.apache.juneau.marshall.*;
026import org.apache.juneau.marshall.objecttools.*;
027import org.apache.juneau.marshall.parser.*;
028import org.apache.juneau.marshall.serializer.*;
029
030/**
031 * Neutral, marshaller-agnostic list of objects.
032 *
033 * <p>
034 * This class is the base for {@link JsonList} and the per-marshaller {@code XList} variants. It carries
035 * all marshaller-neutral functionality such as typed accessors, fluent setters, bean integration,
036 * and {@link PathTraversal}-based path navigation. The default {@link #toString()} comes from
037 * {@link LinkedList}; subclasses should override it to produce the appropriate marshalled form.
038 *
039 * <p>
040 * Note that the use of this class is optional. The serializers will accept any objects that implement the
041 * {@link Collection} interface. But this class provides useful additional functionality when working with
042 * data models constructed from Java Collections Framework objects.
043 *
044 * <h5 class='section'>Notes:</h5><ul>
045 *    <li class='warn'>This class is not thread safe.
046 * </ul>
047 *
048 * @serial exclude
049 */
050@SuppressWarnings({
051   "java:S110", // Class has many fields, acceptable for collection implementation
052   "java:S1206", // Inherits equals/hashCode from LinkedList; List equality is element-based
053   "java:S2160" // equals() inherited from LinkedList; element-based equality is correct
054})
055public class MarshalledList extends LinkedList<Object> {
056
057   private static class Unmodifiable extends MarshalledList {
058      private static final long serialVersionUID = 1L;
059
060      @SuppressWarnings({
061         "synthetic-access" // Access to outer class members is intentional
062      })
063      Unmodifiable(MarshalledList contents) {
064         if (nn(contents))
065            contents.forEach(super::add);
066      }
067
068      @Override /* Overridden from List */
069      public boolean add(Object object) {
070         throw uoroex();
071      }
072
073      @Override /* Overridden from List */
074      public void add(int location, Object object) {
075         throw uoroex();
076      }
077
078      @Override /* Overridden from List */
079      public boolean addAll(Collection<?> c) {
080         throw uoroex();
081      }
082
083      @Override /* Overridden from List */
084      public boolean addAll(int location, Collection<?> c) {
085         throw uoroex();
086      }
087
088      @Override /* Overridden from Deque */
089      public void addFirst(Object object) {
090         throw uoroex();
091      }
092
093      @Override /* Overridden from Deque */
094      public void addLast(Object object) {
095         throw uoroex();
096      }
097
098      @Override /* Overridden from List */
099      public void clear() {
100         throw uoroex();
101      }
102
103      @Override
104      public boolean isUnmodifiable() { return true; }
105
106      @Override /* Overridden from List */
107      public Iterator<Object> iterator() {
108         return readOnlyIterator(super.iterator());
109      }
110
111      @Override /* Overridden from List */
112      public ListIterator<Object> listIterator() {
113         return readOnlyListIterator(super.listIterator());
114      }
115
116      @Override /* Overridden from List */
117      public ListIterator<Object> listIterator(int location) {
118         return readOnlyListIterator(super.listIterator(location));
119      }
120
121      @Override /* Overridden from Queue */
122      public boolean offer(Object object) {
123         throw uoroex();
124      }
125
126      @Override /* Overridden from Deque */
127      public boolean offerFirst(Object object) {
128         throw uoroex();
129      }
130
131      @Override /* Overridden from Deque */
132      public boolean offerLast(Object object) {
133         throw uoroex();
134      }
135
136      @Override /* Overridden from Queue */
137      public Object poll() {
138         throw uoroex();
139      }
140
141      @Override /* Overridden from Deque */
142      public Object pollFirst() {
143         throw uoroex();
144      }
145
146      @Override /* Overridden from Deque */
147      public Object pollLast() {
148         throw uoroex();
149      }
150
151      @Override /* Overridden from Deque */
152      public Object pop() {
153         throw uoroex();
154      }
155
156      @Override /* Overridden from Deque */
157      public void push(Object object) {
158         throw uoroex();
159      }
160
161      @Override /* Overridden from Queue */
162      public Object remove() {
163         throw uoroex();
164      }
165
166      @Override /* Overridden from List */
167      public Object remove(int location) {
168         throw uoroex();
169      }
170
171      @Override /* Overridden from List */
172      public boolean remove(Object object) {
173         throw uoroex();
174      }
175
176      @Override /* Overridden from List */
177      public boolean removeAll(Collection<?> c) {
178         throw uoroex();
179      }
180
181      @Override /* Overridden from Deque */
182      public Object removeFirst() {
183         throw uoroex();
184      }
185
186      @Override /* Overridden from Deque */
187      public boolean removeFirstOccurrence(Object object) {
188         throw uoroex();
189      }
190
191      @Override /* Overridden from Collection */
192      public boolean removeIf(Predicate<? super Object> filter) {
193         throw uoroex();
194      }
195
196      @Override /* Overridden from Deque */
197      public Object removeLast() {
198         throw uoroex();
199      }
200
201      @Override /* Overridden from Deque */
202      public boolean removeLastOccurrence(Object object) {
203         throw uoroex();
204      }
205
206      @Override /* Overridden from List */
207      public void replaceAll(UnaryOperator<Object> operator) {
208         throw uoroex();
209      }
210
211      @Override /* Overridden from List */
212      public boolean retainAll(Collection<?> c) {
213         throw uoroex();
214      }
215
216      @Override /* Overridden from List */
217      public Object set(int location, Object object) {
218         throw uoroex();
219      }
220
221      @Override /* Overridden from List */
222      public void sort(Comparator<? super Object> c) {
223         throw uoroex();
224      }
225
226      @Override /* Overridden from List */
227      public List<Object> subList(int start, int end) {
228         return u(new ArrayList<>(this).subList(start, end));
229      }
230
231      private static Iterator<Object> readOnlyIterator(Iterator<Object> it) {
232         return new Iterator<>() {
233            @Override public boolean hasNext() { return it.hasNext(); }
234            @Override public Object next() { return it.next(); }
235            @Override public void remove() { throw uoroex(); }
236            @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); }
237         };
238      }
239
240      private static ListIterator<Object> readOnlyListIterator(ListIterator<Object> it) {
241         return new ListIterator<>() {
242            @Override public boolean hasNext() { return it.hasNext(); }
243            @Override public Object next() { return it.next(); }
244            @Override public boolean hasPrevious() { return it.hasPrevious(); }
245            @Override public Object previous() { return it.previous(); }
246            @Override public int nextIndex() { return it.nextIndex(); }
247            @Override public int previousIndex() { return it.previousIndex(); }
248            @Override public void remove() { throw uoroex(); }
249            @Override public void set(Object e) { throw uoroex(); }
250            @Override public void add(Object e) { throw uoroex(); }
251            @Override public void forEachRemaining(Consumer<? super Object> action) { it.forEachRemaining(action); }
252         };
253      }
254   }
255
256   private static final long serialVersionUID = 1L;
257
258   /**
259    * An empty read-only MarshalledList.
260    *
261    * @serial exclude
262    */
263   @SuppressWarnings({
264      "java:S2386" // Public static final field accessed externally, cannot be protected
265   })
266   public static final MarshalledList EMPTY_LIST = new MarshalledList() {
267      private static final long serialVersionUID = 1L;
268
269      @Override /* Overridden from List */
270      public void add(int location, Object object) {
271         throw uoroex();
272      }
273
274      @Override /* Overridden from List */
275      public ListIterator<Object> listIterator(int location) {
276         return Collections.emptyList().listIterator(location);
277      }
278
279      @Override /* Overridden from List */
280      public Object remove(int location) {
281         throw uoroex();
282      }
283
284      @Override /* Overridden from List */
285      public Object set(int location, Object object) {
286         throw uoroex();
287      }
288
289      @Override /* Overridden from List */
290      public List<Object> subList(int start, int end) {
291         return Collections.emptyList().subList(start, end);
292      }
293   };
294
295   /**
296    * Construct an empty list.
297    *
298    * @return An empty list.
299    */
300   public static MarshalledList create() {
301      return new MarshalledList();
302   }
303
304   /**
305    * Construct a list initialized with the specified collection.
306    *
307    * @param values
308    *    The collection to copy.
309    *    <br>Can be <jk>null</jk> (returns <jk>null</jk>).
310    * @return A new list or <jk>null</jk> if the input was <jk>null</jk>.
311    */
312   public static MarshalledList of(Collection<?> values) {
313      return values == null ? null : new MarshalledList(values);
314   }
315
316   /**
317    * Construct a list initialized with the specified values.
318    *
319    * @param values The values to add to this list.
320    * @return A new list, never <jk>null</jk>.
321    */
322   public static MarshalledList of(Object...values) {
323      return new MarshalledList(values);
324   }
325
326   /**
327    * Convenience method for creating a list of array objects.
328    *
329    * @param values The initial values.
330    * @return A new list.
331    */
332   public static MarshalledList ofArrays(Object[]...values) {
333      var l = new MarshalledList();
334      Collections.addAll(l, values);
335      return l;
336   }
337
338   /**
339    * Convenience method for creating a list of collection objects.
340    *
341    * @param values The initial values.
342    * @return A new list.
343    */
344   public static MarshalledList ofCollections(Collection<?>...values) {
345      var l = new MarshalledList();
346      Collections.addAll(l, values);
347      return l;
348   }
349
350   /**
351    * Construct a list initialized by parsing the specified string with the specified parser.
352    *
353    * @param in The input being parsed.
354    * @param p
355    *    The parser to use to parse the input.
356    *    <br>Must not be <jk>null</jk>.
357    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
358    * @throws ParseException Malformed input encountered.
359    */
360   public static MarshalledList ofString(CharSequence in, Parser p) throws ParseException {
361      return in == null ? new MarshalledList() : new MarshalledList(in, p);
362   }
363
364   /**
365    * Construct a list initialized by parsing the specified reader with the specified parser.
366    *
367    * @param in The reader containing the input being parsed.
368    * @param p
369    *    The parser to use to parse the input.
370    *    <br>Must not be <jk>null</jk>.
371    * @return A new list (empty if the input was <jk>null</jk>), never <jk>null</jk>.
372    * @throws ParseException Malformed input encountered.
373    */
374   public static MarshalledList ofString(java.io.Reader in, Parser p) throws ParseException {
375      return in == null ? new MarshalledList() : new MarshalledList(in, p);
376   }
377
378   transient MarshallingSession session = null;
379
380   private transient PathTraversal pathTraversal;
381
382   /**
383    * Construct an empty list.
384    */
385   public MarshalledList() {}
386
387   /**
388    * Construct an empty list with the specified bean context.
389    *
390    * @param session The bean session to use for creating beans.
391    */
392   public MarshalledList(MarshallingSession session) {
393      this.session = session;
394   }
395
396   /**
397    * Construct a list initialized by parsing the specified string with the specified parser.
398    *
399    * @param in
400    *    The input being parsed.
401    *    <br>Can be <jk>null</jk> (results in an empty list; no parsing occurs).
402    * @param p
403    *    The parser to use to parse the input.
404    *    <br>Must not be <jk>null</jk>.
405    * @throws ParseException Malformed input encountered.
406    */
407   public MarshalledList(CharSequence in, Parser p) throws ParseException {
408      this(reqnn("p", p).getMarshallingContext().getSession());
409      if (nn(in))
410         p.readIntoCollection(in, this, bs().object());
411   }
412
413   /**
414    * Construct a list initialized with the specified collection.
415    *
416    * @param copyFrom
417    *    The collection to copy.
418    *    <br>Can be <jk>null</jk> (results in an empty list).
419    */
420   public MarshalledList(Collection<?> copyFrom) {
421      super(copyFrom);
422   }
423
424   /**
425    * Construct a list initialized with the contents.
426    *
427    * @param entries The entries to add to this list.
428    */
429   public MarshalledList(Object...entries) {
430      Collections.addAll(this, entries);
431   }
432
433   /**
434    * Construct a list initialized by parsing the specified reader with the specified parser.
435    *
436    * @param in
437    *    The reader containing the input being parsed.
438    * @param p
439    *    The parser to use to parse the input.
440    *    <br>Must not be <jk>null</jk>.
441    * @throws ParseException Malformed input encountered.
442    */
443   @SuppressWarnings({
444      "javabugs:S2259" // The parser is null-checked by reqnn() in the delegating constructor call.
445   })
446   public MarshalledList(java.io.Reader in, Parser p) throws ParseException {
447      this(reqnn("p", p).getMarshallingContext().getSession());
448      p.readIntoCollection(in, this, bs().object());
449   }
450
451   /**
452    * Adds all the values in the specified collection to this list.
453    *
454    * @param values The values to add to this list.
455    * @return This object.
456    */
457   public MarshalledList append(Collection<?> values) {
458      if (nn(values))
459         addAll(values);
460      return this;
461   }
462
463   /**
464    * Adds the value to this list.
465    *
466    * @param value The value to add to this list.
467    * @return This object.
468    */
469   public MarshalledList append(Object value) {
470      add(value);
471      return this;
472   }
473
474   /**
475    * Adds all the values in the specified array to this list.
476    *
477    * @param values The values to add to this list.
478    * @return This object.
479    */
480   public MarshalledList append(Object...values) {
481      Collections.addAll(this, values);
482      return this;
483   }
484
485   /**
486    * Adds an entry to this list if the boolean flag is <jk>true</jk>.
487    *
488    * @param flag The boolean flag.
489    * @param value The value to add.
490    * @return This object.
491    */
492   public MarshalledList appendIf(boolean flag, Object value) {
493      if (flag)
494         append(value);
495      return this;
496   }
497
498   /**
499    * Add if predicate matches.
500    *
501    * @param <T> The type being tested.
502    * @param test The predicate to match against.
503    * @param value The value to add if the predicate matches.
504    * @return This object.
505    */
506   public <T> MarshalledList appendIf(Predicate<T> test, T value) {
507      return appendIf(t(test, value), value);
508   }
509
510   /**
511    * Adds all the entries in the specified collection to this list in reverse order.
512    *
513    * @param values The collection to add to this list.
514    * @return This object.
515    */
516   public MarshalledList appendReverse(List<?> values) {
517      if (nn(values))
518         for (ListIterator<?> i = values.listIterator(values.size()); i.hasPrevious();)
519            add(i.previous());
520      return this;
521   }
522
523   /**
524    * Adds the contents of the array to the list in reverse order.
525    *
526    * <p>
527    * i.e. add values from the array from end-to-start order to the end of the list.
528    *
529    * @param values The collection to add to this list.
530    * @return This object.
531    */
532   public MarshalledList appendReverse(Object...values) {
533      if (values != null)
534         for (var i = values.length - 1; i >= 0; i--)
535            add(values[i]);
536      return this;
537   }
538
539   /**
540    * Returns the node addressed by the specified <a class="doclink" href="https://datatracker.ietf.org/doc/html/rfc6901">RFC 6901</a>
541    * JSON Pointer relative to this list.
542    *
543    * <p>
544    * Convenience for <c>MarshalledNode.<jsm>of</jsm>(<jk>this</jk>).at(<jv>pointer</jv>)</c>. A read-miss returns
545    * <jk>null</jk>; an addressed JSON <jk>null</jk> value returns a null node.
546    *
547    * <p>
548    * <b>Beta — API subject to change:</b> This method is part of the next-generation typed tree model layered over the
549    * {@code Marshalled*} collections and may change incompatibly in a future release.
550    *
551    * @param pointer The RFC 6901 JSON Pointer.
552    * @return A node wrapping the addressed value, or <jk>null</jk> if the addressed value is absent.
553    */
554   public MarshalledNode at(String pointer) {
555      return MarshalledNode.of(this).at(pointer);
556   }
557
558   /**
559    * Serializes this list to a string using the specified serializer.
560    *
561    * @param serializer The serializer to use to convert this object to a string.
562    * @return This object as a serialized string.
563    */
564   public String toString(WriterSerializer serializer) {
565      return serializer.toString(this);
566   }
567
568   /**
569    * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in
570    * this POJO.
571    *
572    * <p>
573    * This method uses the {@link PathTraversal} class to perform the lookup, so the map can contain any of the various
574    * class types that the {@link PathTraversal} class supports (e.g. beans, collections, arrays).
575    *
576    * @param path The path to the entry.
577    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
578    */
579   public Object deleteAt(String path) {
580      return getPathTraversal().delete(path);
581   }
582
583   /**
584    * Creates an {@link Iterable} with elements of the specified child type.
585    *
586    * <p>
587    * Attempts to convert the child objects to the correct type if they aren't already the correct type.
588    *
589    * <p>
590    * The <c>next()</c> method on the returned iterator may throw a {@link InvalidDataConversionException} if
591    * the next element cannot be converted to the specified type.
592    *
593    * <p>
594    * See {@link MarshallingSession#convertToType(Object, ClassMeta)} for a description of valid conversions.
595    *
596    * @param <E> The child object type.
597    * @param childType The child object type.
598    * @return A new <c>Iterable</c> object over this list.
599    */
600   public <E> Iterable<E> elements(Class<E> childType) {
601      final Iterator<?> iterator = iterator();
602      return () -> new Iterator<>() {
603
604         @Override /* Overridden from Iterator */
605         public boolean hasNext() {
606            return iterator.hasNext();
607         }
608
609         @Override /* Overridden from Iterator */
610         public E next() {
611            return bs().convertToType(iterator.next(), childType);
612         }
613
614         @Override /* Overridden from Iterator */
615         public void remove() {
616            iterator.remove();
617         }
618
619      };
620   }
621
622   /**
623    * Get the entry at the specified index, converted to the specified type.
624    *
625    * <p>
626    * See {@link MarshallingSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
627    *
628    * @param index The index into this list.
629    * @param type The type of object to convert the entry to.
630    * @param <T> The type of object to convert the entry to.
631    * @return The converted entry.
632    */
633   public <T> T get(int index, Class<T> type) {
634      return bs().convertToType(get(index), type);
635   }
636
637   /**
638    * Get the entry at the specified index, converted to the specified type.
639    *
640    * <p>
641    * See {@link MarshallingSession#convertToType(Object, ClassMeta)} for the list of valid data conversions.
642    *
643    * @param index The index into this list.
644    * @param type The type of object to convert the entry to.
645    * @param args The type arguments of the type to convert the entry to.
646    * @param <T> The type of object to convert the entry to.
647    * @return The converted entry.
648    */
649   public <T> T get(int index, Type type, Type...args) {
650      return bs().convertToType(get(index), type, args);
651   }
652
653   /**
654    * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in
655    * this POJO.
656    *
657    * @param path The path to the entry.
658    * @param type The class type.
659    *
660    * @param <T> The class type.
661    * @return The value, or <jk>null</jk> if the entry doesn't exist.
662    */
663   public <T> T getAt(String path, Class<T> type) {
664      return getPathTraversal().get(path, type);
665   }
666
667   /**
668    * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections.
669    *
670    * @param path The path to the entry.
671    * @param type The class type.
672    * @param args The class parameter types.
673    *
674    * @param <T> The class type.
675    * @return The value, or <jk>null</jk> if the entry doesn't exist.
676    */
677   public <T> T getAt(String path, Type type, Type...args) {
678      return getPathTraversal().get(path, type, args);
679   }
680
681   /**
682    * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>.
683    *
684    * @param index The index.
685    * @return The converted value.
686    * @throws InvalidDataConversionException If value cannot be converted.
687    */
688   public Boolean getBoolean(int index) {
689      return get(index, Boolean.class);
690   }
691
692   /**
693    * Returns the {@link ClassMeta} of the class of the object at the specified index.
694    *
695    * @param index An index into this list, zero-based.
696    * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
697    */
698   @SuppressWarnings({
699      "java:S1452"  // Wildcard required - ClassMeta<?> for element type metadata
700   })
701   public ClassMeta<?> getClassMeta(int index) {
702      return bs().getClassMetaForObject(get(index));
703   }
704
705   /**
706    * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>.
707    *
708    * @param index The index.
709    * @return The converted value.
710    * @throws InvalidDataConversionException If value cannot be converted.
711    */
712   public Integer getInt(int index) {
713      return get(index, Integer.class);
714   }
715
716   /**
717    * Shortcut for calling <code>get(index, MarshalledList.<jk>class</jk>)</code>.
718    *
719    * @param index The index.
720    * @return The converted value.
721    * @throws InvalidDataConversionException If value cannot be converted.
722    */
723   public MarshalledList getList(int index) {
724      return get(index, MarshalledList.class);
725   }
726
727   /**
728    * Same as {@link #getList(int)} except converts the elements to the specified types.
729    *
730    * @param <E> The element type.
731    * @param index The index.
732    * @param elementType The element type class.
733    * @return The converted value.
734    * @throws InvalidDataConversionException If value cannot be converted.
735    */
736   public <E> List<E> getList(int index, Class<E> elementType) {
737      return bs().convertToType(get(index), List.class, elementType);
738   }
739
740   /**
741    * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>.
742    *
743    * @param index The index.
744    * @return The converted value.
745    * @throws InvalidDataConversionException If value cannot be converted.
746    */
747   public Long getLong(int index) {
748      return get(index, Long.class);
749   }
750
751   /**
752    * Shortcut for calling <code>get(index, MarshalledMap.<jk>class</jk>)</code>.
753    *
754    * @param index The index.
755    * @return The converted value.
756    * @throws InvalidDataConversionException If value cannot be converted.
757    */
758   public MarshalledMap getMap(int index) {
759      return get(index, MarshalledMap.class);
760   }
761
762   /**
763    * Same as {@link #getMap(int)} except converts the keys and values to the specified types.
764    *
765    * @param <K> The key type class.
766    * @param <V> The value type class.
767    * @param index The index.
768    * @param keyType The key type class.
769    * @param valType The value type class.
770    * @return The converted value.
771    * @throws InvalidDataConversionException If value cannot be converted.
772    */
773   public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
774      return bs().convertToType(get(index), Map.class, keyType, valType);
775   }
776
777   /**
778    * Returns the {@link MarshallingSession} currently associated with this list.
779    *
780    * @return The {@link MarshallingSession} currently associated with this list.
781    */
782   public MarshallingSession getMarshallingSession() { return session; }
783
784   /**
785    * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>.
786    *
787    * @param index The index.
788    * @return The converted value.
789    */
790   public String getString(int index) {
791      return get(index, String.class);
792   }
793
794   /**
795    * Returns <jk>true</jk> if this list is unmodifiable.
796    *
797    * @return <jk>true</jk> if this list is unmodifiable.
798    */
799   public boolean isUnmodifiable() { return false; }
800
801   /**
802    * Returns a modifiable copy of this list if it's unmodifiable.
803    *
804    * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable.
805    */
806   public MarshalledList modifiable() {
807      if (isUnmodifiable())
808         return new MarshalledList(this);
809      return this;
810   }
811
812   /**
813    * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays.
814    *
815    * <p>
816    * This method uses the {@link PathTraversal} class to perform the lookup, so the map can contain any of the various
817    * class types that the {@link PathTraversal} class supports (e.g. beans, collections, arrays).
818    *
819    * @param path The path to the entry.
820    * @param o The new value.
821    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
822    */
823   public Object postAt(String path, Object o) {
824      return getPathTraversal().post(path, o);
825   }
826
827   /**
828    * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries
829    * in this POJO.
830    *
831    * <p>
832    * This method uses the {@link PathTraversal} class to perform the lookup, so the map can contain any of the various
833    * class types that the {@link PathTraversal} class supports (e.g. beans, collections, arrays).
834    *
835    * @param path The path to the entry.
836    * @param o The new value.
837    * @return The previous value, or <jk>null</jk> if the entry doesn't exist.
838    */
839   public Object putAt(String path, Object o) {
840      return getPathTraversal().put(path, o);
841   }
842
843   /**
844    * Override the default bean session used for converting POJOs.
845    *
846    * <p>
847    * Default is {@link MarshallingContext#DEFAULT}, which is sufficient in most cases.
848    *
849    * <p>
850    * Useful if you're serializing/parsing beans with transforms defined.
851    *
852    * @param session The new bean session.
853    * @return This object.
854    */
855   public MarshalledList session(MarshallingSession session) {
856      this.session = session;
857      return this;
858   }
859
860   /**
861    * Sets the {@link MarshallingSession} currently associated with this list.
862    *
863    * @param value The {@link MarshallingSession} currently associated with this list.
864    * @return This object.
865    */
866   public MarshalledList setBeanSession(MarshallingSession value) {
867      session = value;
868      return this;
869   }
870
871   /**
872    * Returns an unmodifiable copy of this list if it's modifiable.
873    *
874    * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable.
875    */
876   public MarshalledList unmodifiable() {
877      if (this instanceof Unmodifiable this2)
878         return this2;
879      return new Unmodifiable(this);
880   }
881
882   private PathTraversal getPathTraversal() {
883      if (pathTraversal == null)
884         pathTraversal = new PathTraversal(this);
885      return pathTraversal;
886   }
887
888   /**
889    * Returns the {@link MarshallingSession} associated with this list (lazily defaults to {@link MarshallingContext#DEFAULT_SESSION}).
890    *
891    * @return The {@link MarshallingSession} for this list.
892    */
893   protected MarshallingSession bs() {
894      if (session == null)
895         session = MarshallingContext.DEFAULT_SESSION;
896      return session;
897   }
898}