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}