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; 014 015import static org.apache.juneau.internal.StringUtils.*; 016 017import java.io.*; 018import java.lang.reflect.*; 019import java.util.*; 020 021import org.apache.juneau.json.*; 022import org.apache.juneau.parser.*; 023import org.apache.juneau.serializer.*; 024import org.apache.juneau.utils.*; 025 026/** 027 * Java implementation of a JSON array. 028 * 029 * <p> 030 * An extension of {@link LinkedList}, so all methods available to in that class are also available to this class. 031 * 032 * <p> 033 * Note that the use of this class is optional. 034 * The serializers will accept any objects that implement the {@link Collection} interface. 035 * But this class provides some useful additional functionality when working with JSON models constructed from Java 036 * Collections Framework objects. 037 * For example, a constructor is provided for converting a JSON array string directly into a {@link List}. 038 * It also contains accessor methods for to avoid common typecasting when accessing elements in a list. 039 * 040 * <h5 class='section'>Example:</h5> 041 * <p class='bcode w800'> 042 * <jc>// Construct an empty List</jc> 043 * List l = <jk>new</jk> ObjectList(); 044 * 045 * <jc>// Construct a list of objects using various methods</jc> 046 * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>).append(123).append(<jk>true</jk>); 047 * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 048 * l = <jk>new</jk> ObjectList(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> 049 * 050 * <jc>// Construct a list of integers from JSON</jc> 051 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 052 * 053 * <jc>// Construct a list of generic ObjectMap objects from JSON</jc> 054 * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 055 * 056 * <jc>// Construct a list of integers from XML</jc> 057 * String xml = <js>"<array><number>1</number><number>2</number><number>3</number></array>"</js>; 058 * l = <jk>new</jk> ObjectList(xml, DataFormat.<jsf>XML</jsf>); 059 * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(xml); <jc>// Equivalent</jc> 060 * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); <jc>// Equivalent</jc> 061 * l = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, xml); <jc>// Equivalent</jc> 062 * l = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectList.<jk>class</jk>, xml); <jc>// Equivalent</jc> 063 * 064 * <jc>// Construct JSON from ObjectList</jc> 065 * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); 066 * String json = l.toString(); <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc> 067 * json = l.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>); <jc>// Equivalent</jc> 068 * json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(l); <jc>// Equivalent</jc> 069 * 070 * <jc>// Get one of the entries in the list as an Integer</jc> 071 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 072 * Integer i = l.getInt(1); 073 * i = l.get(Integer.<jk>class</jk>, 1); <jc>// Equivalent</jc> 074 * 075 * <jc>// Get one of the entries in the list as an Float</jc> 076 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 077 * Float f = l.getFloat(1); <jc>// Returns 2f </jc> 078 * f = l.get(Float.<jk>class</jk>, 1); <jc>// Equivalent</jc> 079 * 080 * <jc>// Same as above, except converted to a String</jc> 081 * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 082 * String s = l.getString(1); <jc>// Returns "2" </jc> 083 * s = l.get(String.<jk>class</jk>, 1); <jc>// Equivalent</jc> 084 * 085 * <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc> 086 * l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 087 * Person p = l.get(Person.<jk>class</jk>, 0); 088 * 089 * <jc>// Iterate over a list of beans using the elements() method</jc> 090 * ObjectList ObjectList = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 091 * <jk>for</jk> (Person p : ObjectList.elements(Person.<jk>class</jk>) { 092 * <jc>// Do something with p</jc> 093 * } 094 * </p> 095 * 096 * <p> 097 * This class is not thread safe. 098 */ 099public class ObjectList extends LinkedList<Object> { 100 private static final long serialVersionUID = 1L; 101 102 transient BeanSession session = null; 103 private transient PojoRest pojoRest; 104 105 /** 106 * An empty read-only ObjectList. 107 */ 108 public static final ObjectList EMPTY_LIST = new ObjectList() { 109 private static final long serialVersionUID = 1L; 110 111 @Override /* List */ 112 public void add(int location, Object object) { 113 throw new UnsupportedOperationException(); 114 } 115 116 @Override /* List */ 117 public ListIterator<Object> listIterator(final int location) { 118 return Collections.emptyList().listIterator(location); 119 } 120 121 @Override /* List */ 122 public Object remove(int location) { 123 throw new UnsupportedOperationException(); 124 } 125 126 @Override /* List */ 127 public Object set(int location, Object object) { 128 throw new UnsupportedOperationException(); 129 } 130 131 @Override /* List */ 132 public List<Object> subList(int start, int end) { 133 return Collections.emptyList().subList(start, end); 134 } 135 }; 136 137 /** 138 * Construct a JSON array directly from text using the specified parser. 139 * 140 * @param s The string being parsed. 141 * @param p The parser to use to parse the input. 142 * @throws ParseException If the input contains a syntax error or is malformed. 143 */ 144 public ObjectList(CharSequence s, Parser p) throws ParseException { 145 this(p == null ? null : p.createBeanSession()); 146 if (p == null) 147 p = JsonParser.DEFAULT; 148 if (s != null) 149 p.parseIntoCollection(s, this, bs().object()); 150 } 151 152 /** 153 * Shortcut for <code><jk>new</jk> ObjectList(String,JsonParser.<jsf>DEFAULT</jsf>);</code> 154 * 155 * @param s The string being parsed. 156 * @throws ParseException If the input contains a syntax error or is malformed. 157 */ 158 public ObjectList(CharSequence s) throws ParseException { 159 this(s, null); 160 } 161 162 /** 163 * Construct a JSON array directly from a reader using the specified parser. 164 * 165 * @param r 166 * The reader to read from. 167 * Will automatically be wrapped in a {@link BufferedReader} if it isn't already a BufferedReader. 168 * @param p The parser to use to parse the input. 169 * @throws ParseException If the input contains a syntax error or is malformed. 170 * @throws IOException If a problem occurred trying to read from the reader. 171 */ 172 public ObjectList(Reader r, Parser p) throws ParseException, IOException { 173 this(p == null ? null : p.createBeanSession()); 174 parseReader(r, p); 175 } 176 177 /** 178 * Shortcut for <code><jk>new</jk> ObjectList(reader, JsonParser.<jsf>DEFAULT</jsf>)</code>. 179 * 180 * @param r 181 * The reader to read from. 182 * The reader will be wrapped in a {@link BufferedReader} if it isn't already. 183 * @throws ParseException If the input contains a syntax error or is malformed. 184 * @throws IOException If a problem occurred trying to read from the reader. 185 */ 186 public ObjectList(Reader r) throws ParseException, IOException { 187 parseReader(r, JsonParser.DEFAULT); 188 } 189 190 private void parseReader(Reader r, Parser p) throws ParseException { 191 if (p == null) 192 p = JsonParser.DEFAULT; 193 p.parseIntoCollection(r, this, bs().object()); 194 } 195 196 /** 197 * Construct an empty JSON array (an empty {@link LinkedList}). 198 */ 199 public ObjectList() { 200 } 201 202 /** 203 * Construct an empty JSON array (an empty {@link LinkedList}) with the specified bean context. 204 * 205 * @param session The bean context to associate with this object list for creating beans. 206 */ 207 public ObjectList(BeanSession session) { 208 super(); 209 this.session = session; 210 } 211 212 /** 213 * Construct a JSON array and fill it with the specified objects. 214 * 215 * @param o A list of objects to add to this list. 216 */ 217 public ObjectList(Object... o) { 218 super(Arrays.asList(o)); 219 } 220 221 /** 222 * Construct a JSON array and fill it with the specified collection of objects. 223 * 224 * @param c A list of objects to add to this list. 225 */ 226 public ObjectList(Collection<?> c) { 227 super(c); 228 } 229 230 /** 231 * Override the default bean session used for converting POJOs. 232 * 233 * <p> 234 * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases. 235 * 236 * <p> 237 * Useful if you're serializing/parsing beans with transforms defined. 238 * 239 * @param session The new bean session. 240 * @return This object (for method chaining). 241 */ 242 public ObjectList setBeanSession(BeanSession session) { 243 this.session = session; 244 return this; 245 } 246 247 /** 248 * Returns the {@link BeanSession} currently associated with this list. 249 * 250 * @return The {@link BeanSession} currently associated with this list. 251 */ 252 public BeanSession getBeanSession() { 253 return session; 254 } 255 256 /** 257 * Convenience method for adding multiple objects to this list. 258 * 259 * @param o The objects to add to the list. 260 * @return This object (for method chaining). 261 */ 262 public ObjectList append(Object...o) { 263 for (Object o2 : o) 264 add(o2); 265 return this; 266 } 267 268 /** 269 * Convenience method for adding multiple objects to this list. 270 * 271 * <p> 272 * <jk>null</jk> and empty strings are skipped. 273 * 274 * @param o The objects to add to the list. 275 * @return This object (for method chaining). 276 */ 277 public ObjectList appendIfNotEmpty(String...o) { 278 for (String s : o) 279 if (isNotEmpty(s)) 280 add(s); 281 return this; 282 } 283 284 /** 285 * Convenience method for adding multiple objects to this list. 286 * 287 * <p> 288 * <jk>null</jk> values are skipped. 289 * 290 * @param o The objects to add to the list. 291 * @return This object (for method chaining). 292 */ 293 public ObjectList appendIfNotNull(Object...o) { 294 for (Object o2 : o) 295 if (o2 != null) 296 add(o2); 297 return this; 298 } 299 300 /** 301 * Get the entry at the specified index, converted to the specified type. 302 * 303 * <p> 304 * This is the preferred get method for simple types. 305 * 306 * <h5 class='section'>Examples:</h5> 307 * <p class='bcode w800'> 308 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 309 * 310 * <jc>// Value converted to a string.</jc> 311 * String s = l.get(1, String.<jk>class</jk>); 312 * 313 * <jc>// Value converted to a bean.</jc> 314 * MyBean b = l.get(2, MyBean.<jk>class</jk>); 315 * 316 * <jc>// Value converted to a bean array.</jc> 317 * MyBean[] ba = l.get(3, MyBean[].<jk>class</jk>); 318 * 319 * <jc>// Value converted to a linked-list of objects.</jc> 320 * List l1 = l.get(4, LinkedList.<jk>class</jk>); 321 * 322 * <jc>// Value converted to a map of object keys/values.</jc> 323 * Map m1 = l.get(5, TreeMap.<jk>class</jk>); 324 * </p> 325 * 326 * <p> 327 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 328 * 329 * @param index The index into this list. 330 * @param type The type of object to convert the entry to. 331 * @param <T> The type of object to convert the entry to. 332 * @return The converted entry. 333 */ 334 public <T> T get(int index, Class<T> type) { 335 return bs().convertToType(get(index), type); 336 } 337 338 /** 339 * Get the entry at the specified index, converted to the specified type. 340 * 341 * <p> 342 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 343 * 344 * <h5 class='section'>Examples:</h5> 345 * <p class='bcode w800'> 346 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 347 * 348 * <jc>// Value converted to a linked-list of strings.</jc> 349 * List<String> l1 = l.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 350 * 351 * <jc>// Value converted to a linked-list of beans.</jc> 352 * List<MyBean> l2 = l.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 353 * 354 * <jc>// Value converted to a linked-list of linked-lists of strings.</jc> 355 * List<List<String>> l3 = l.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 356 * 357 * <jc>// Value converted to a map of string keys/values.</jc> 358 * Map<String,String> m1 = l.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 359 * 360 * <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc> 361 * Map<String,List<MyBean>> m2 = l.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 362 * </p> 363 * 364 * <p> 365 * <code>Collection</code> classes are assumed to be followed by zero or one objects indicating the element type. 366 * 367 * <p> 368 * <code>Map</code> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 369 * 370 * <p> 371 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 372 * 373 * <p> 374 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 375 * 376 * @param index The index into this list. 377 * @param type The type of object to convert the entry to. 378 * @param args The type arguments of the type to convert the entry to. 379 * @param <T> The type of object to convert the entry to. 380 * @return The converted entry. 381 */ 382 public <T> T get(int index, Type type, Type...args) { 383 return bs().convertToType(get(index), type, args); 384 } 385 386 /** 387 * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>. 388 * 389 * @param index The index. 390 * @return The converted value. 391 */ 392 public String getString(int index) { 393 return get(index, String.class); 394 } 395 396 /** 397 * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>. 398 * 399 * @param index The index. 400 * @return The converted value. 401 * @throws InvalidDataConversionException If value cannot be converted. 402 */ 403 public Integer getInt(int index) { 404 return get(index, Integer.class); 405 } 406 407 /** 408 * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>. 409 * 410 * @param index The index. 411 * @return The converted value. 412 * @throws InvalidDataConversionException If value cannot be converted. 413 */ 414 public Boolean getBoolean(int index) { 415 return get(index, Boolean.class); 416 } 417 418 /** 419 * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>. 420 * 421 * @param index The index. 422 * @return The converted value. 423 * @throws InvalidDataConversionException If value cannot be converted. 424 */ 425 public Long getLong(int index) { 426 return get(index, Long.class); 427 } 428 429 /** 430 * Shortcut for calling <code>get(index, Map.<jk>class</jk>)</code>. 431 * 432 * @param index The index. 433 * @return The converted value. 434 * @throws InvalidDataConversionException If value cannot be converted. 435 */ 436 public Map<?,?> getMap(int index) { 437 return get(index, Map.class); 438 } 439 440 /** 441 * Same as {@link #getMap(int)} except converts the keys and values to the specified types. 442 * 443 * @param index The index. 444 * @param keyType The key type class. 445 * @param valType The value type class. 446 * @return The converted value. 447 * @throws InvalidDataConversionException If value cannot be converted. 448 */ 449 public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) { 450 return bs().convertToType(get(index), Map.class, keyType, valType); 451 } 452 453 /** 454 * Shortcut for calling <code>get(index, List.<jk>class</jk>)</code>. 455 * 456 * @param index The index. 457 * @return The converted value. 458 * @throws InvalidDataConversionException If value cannot be converted. 459 */ 460 public List<?> getList(int index) { 461 return get(index, List.class); 462 } 463 464 /** 465 * Same as {@link #getList(int)} except converts the elements to the specified types. 466 * 467 * @param index The index. 468 * @param elementType The element type class. 469 * @return The converted value. 470 * @throws InvalidDataConversionException If value cannot be converted. 471 */ 472 public <E> List<E> getList(int index, Class<E> elementType) { 473 return bs().convertToType(get(index), List.class, elementType); 474 } 475 476 /** 477 * Shortcut for calling <code>get(index, ObjectMap.<jk>class</jk>)</code>. 478 * 479 * @param index The index. 480 * @return The converted value. 481 * @throws InvalidDataConversionException If value cannot be converted. 482 */ 483 public ObjectMap getObjectMap(int index) { 484 return get(index, ObjectMap.class); 485 } 486 487 /** 488 * Shortcut for calling <code>get(index, ObjectList.<jk>class</jk>)</code>. 489 * 490 * @param index The index. 491 * @return The converted value. 492 * @throws InvalidDataConversionException If value cannot be converted. 493 */ 494 public ObjectList getObjectList(int index) { 495 return get(index, ObjectList.class); 496 } 497 498 /** 499 * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in 500 * this POJO. 501 * 502 * <p> 503 * For example, the following code is equivalent: 504 * </p> 505 * <p class='bcode w800'> 506 * ObjectMap m = getObjectMap(); 507 * 508 * <jc>// Long way</jc> 509 * <jk>long</jk> l = m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).getLong(<js>"baz"</js>); 510 * 511 * <jc>// Using this method</jc> 512 * <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>); 513 * </p> 514 * 515 * <p> 516 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 517 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 518 * 519 * @param path The path to the entry. 520 * @param type The class type. 521 * 522 * @param <T> The class type. 523 * @return The value, or <jk>null</jk> if the entry doesn't exist. 524 */ 525 public <T> T getAt(String path, Class<T> type) { 526 return getPojoRest().get(path, type); 527 } 528 529 /** 530 * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections. 531 * 532 * @param path The path to the entry. 533 * @param type The class type. 534 * @param args The class parameter types. 535 * 536 * @param <T> The class type. 537 * @return The value, or <jk>null</jk> if the entry doesn't exist. 538 */ 539 public <T> T getAt(String path, Type type, Type...args) { 540 return getPojoRest().get(path, type, args); 541 } 542 543 /** 544 * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries 545 * in this POJO. 546 * 547 * <p> 548 * For example, the following code is equivalent: 549 * </p> 550 * <p class='bcode w800'> 551 * ObjectMap m = getObjectMap(); 552 * 553 * <jc>// Long way</jc> 554 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).put(<js>"baz"</js>, 123); 555 * 556 * <jc>// Using this method</jc> 557 * m.putAt(<js>"foo/bar/0/baz"</js>, 123); 558 * </p> 559 * 560 * <p> 561 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 562 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 563 * 564 * @param path The path to the entry. 565 * @param o The new value. 566 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 567 */ 568 public Object putAt(String path, Object o) { 569 return getPojoRest().put(path, o); 570 } 571 572 /** 573 * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays. 574 * 575 * <p> 576 * For example, the following code is equivalent: 577 * </p> 578 * <p class='bcode w800'> 579 * ObjectMap m = getObjectMap(); 580 * 581 * <jc>// Long way</jc> 582 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).append(123); 583 * 584 * <jc>// Using this method</jc> 585 * m.postAt(<js>"foo/bar"</js>, 123); 586 * </p> 587 * 588 * <p> 589 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 590 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 591 * 592 * @param path The path to the entry. 593 * @param o The new value. 594 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 595 */ 596 public Object postAt(String path, Object o) { 597 return getPojoRest().post(path, o); 598 } 599 600 /** 601 * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in 602 * this POJO. 603 * 604 * <p> 605 * For example, the following code is equivalent: 606 * </p> 607 * <p class='bcode w800'> 608 * ObjectMap m = getObjectMap(); 609 * 610 * <jc>// Long way</jc> 611 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(1).remove(<js>"baz"</js>); 612 * 613 * <jc>// Using this method</jc> 614 * m.deleteAt(<js>"foo/bar/0/baz"</js>); 615 * </p> 616 * 617 * <p> 618 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 619 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 620 * 621 * @param path The path to the entry. 622 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 623 */ 624 public Object deleteAt(String path) { 625 return getPojoRest().delete(path); 626 } 627 628 /** 629 * Creates an {@link Iterable} with elements of the specified child type. 630 * 631 * <p> 632 * Attempts to convert the child objects to the correct type if they aren't already the correct type. 633 * 634 * <p> 635 * The <code>next()</code> method on the returned iterator may throw a {@link InvalidDataConversionException} if 636 * the next element cannot be converted to the specified type. 637 * 638 * <p> 639 * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions. 640 * 641 * <h5 class='section'>Example:</h5> 642 * <p class='bcode w800'> 643 * <jc>// Iterate over a list of ObjectMaps.</jc> 644 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>); 645 * for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) { 646 * <jc>// Do something with m.</jc> 647 * } 648 * 649 * <jc>// Iterate over a list of ints.</jc> 650 * ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 651 * for (Integer i : l.elements(Integer.<jk>class</jk>)) { 652 * <jc>// Do something with i.</jc> 653 * } 654 * 655 * <jc>// Iterate over a list of beans.</jc> 656 * <jc>// Automatically converts to beans.</jc> 657 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 658 * for (Person p : l.elements(Person.<jk>class</jk>)) { 659 * <jc>// Do something with p.</jc> 660 * } 661 * </p> 662 * 663 * @param <E> The child object type. 664 * @param childType The child object type. 665 * @return A new <code>Iterable</code> object over this list. 666 */ 667 public <E> Iterable<E> elements(final Class<E> childType) { 668 final Iterator<?> i = iterator(); 669 return new Iterable<E>() { 670 671 @Override /* Iterable */ 672 public Iterator<E> iterator() { 673 return new Iterator<E>() { 674 675 @Override /* Iterator */ 676 public boolean hasNext() { 677 return i.hasNext(); 678 } 679 680 @Override /* Iterator */ 681 public E next() { 682 return bs().convertToType(i.next(), childType); 683 } 684 685 @Override /* Iterator */ 686 public void remove() { 687 i.remove(); 688 } 689 690 }; 691 } 692 }; 693 } 694 695 /** 696 * Returns the {@link ClassMeta} of the class of the object at the specified index. 697 * 698 * @param index An index into this list, zero-based. 699 * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null. 700 */ 701 public ClassMeta<?> getClassMeta(int index) { 702 return bs().getClassMetaForObject(get(index)); 703 } 704 705 private PojoRest getPojoRest() { 706 if (pojoRest == null) 707 pojoRest = new PojoRest(this); 708 return pojoRest; 709 } 710 711 /** 712 * Serialize this array to a string using the specified serializer. 713 * 714 * @param serializer The serializer to use to convert this object to a string. 715 * @return This object as a serialized string. 716 * @throws SerializeException If a problem occurred trying to convert the output. 717 */ 718 public String toString(WriterSerializer serializer) throws SerializeException { 719 return serializer.serialize(this); 720 } 721 722 /** 723 * Returns <jk>true</jk> if this list is unmodifiable. 724 * 725 * @return <jk>true</jk> if this list is unmodifiable. 726 */ 727 public boolean isUnmodifiable() { 728 return false; 729 } 730 731 /** 732 * Returns a modifiable copy of this list if it's unmodifiable. 733 * 734 * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable. 735 */ 736 public ObjectList modifiable() { 737 if (isUnmodifiable()) 738 return new ObjectList(this); 739 return this; 740 } 741 742 /** 743 * Returns an unmodifiable copy of this list if it's modifiable. 744 * 745 * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable. 746 */ 747 public ObjectList unmodifiable() { 748 if (this instanceof UnmodifiableObjectList) 749 return this; 750 return new UnmodifiableObjectList(this); 751 } 752 753 /** 754 * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer. 755 */ 756 @Override /* Object */ 757 public String toString() { 758 try { 759 return this.toString(SimpleJsonSerializer.DEFAULT); 760 } catch (SerializeException e) { 761 return e.getLocalizedMessage(); 762 } 763 } 764 765 /** 766 * Convenience method for serializing this ObjectList to the specified Writer using the JsonSerializer.DEFAULT 767 * serializer. 768 * 769 * @param w The writer to send the serialized contents of this object. 770 * @throws IOException If a problem occurred trying to write to the writer. 771 * @throws SerializeException If a problem occurred trying to convert the output. 772 */ 773 public void serializeTo(Writer w) throws IOException, SerializeException { 774 JsonSerializer.DEFAULT.serialize(this); 775 } 776 777 /** 778 * Converts this object into the specified class type. 779 * 780 * <p> 781 * TODO - The current implementation is very inefficient. 782 * 783 * @param cm The class type to convert this object to. 784 * @return A converted object. 785 */ 786 public Object cast(ClassMeta<?> cm) { 787 try { 788 return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT.serialize(this), cm); 789 } catch (ParseException | SerializeException e) { 790 throw new RuntimeException(e); 791 } 792 } 793 794 private static final class UnmodifiableObjectList extends ObjectList { 795 private static final long serialVersionUID = 1L; 796 797 UnmodifiableObjectList(ObjectList contents) { 798 super(); 799 if (contents != null) { 800 for (Object e : this) { 801 super.add(e); 802 } 803 } 804 } 805 806 @Override /* List */ 807 public void add(int location, Object object) { 808 throw new UnsupportedOperationException("ObjectList is read-only."); 809 } 810 811 @Override /* List */ 812 public Object remove(int location) { 813 throw new UnsupportedOperationException("ObjectList is read-only."); 814 } 815 816 @Override /* List */ 817 public Object set(int location, Object object) { 818 throw new UnsupportedOperationException("ObjectList is read-only."); 819 } 820 821 @Override 822 public final boolean isUnmodifiable() { 823 return true; 824 } 825 } 826 827 BeanSession bs() { 828 if (session == null) 829 session = BeanContext.DEFAULT.createBeanSession(); 830 return session; 831 } 832}