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