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 Malformed input encountered. 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 Malformed input encountered. 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 Malformed input encountered. 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 Malformed input encountered. 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 appending another list to this list. 270 * 271 * @param l 272 * The list containing the elements to append to this list. 273 * <br>Can be <jk>null</jk>. 274 * @return This object (for method chaining). 275 */ 276 public ObjectList appendAll(ObjectList l) { 277 if (l != null) 278 addAll(l); 279 return this; 280 } 281 282 /** 283 * Convenience method for adding multiple objects to this list. 284 * 285 * <p> 286 * <jk>null</jk> and empty strings are skipped. 287 * 288 * @param o The objects to add to the list. 289 * @return This object (for method chaining). 290 */ 291 public ObjectList appendIfNotEmpty(String...o) { 292 for (String s : o) 293 if (isNotEmpty(s)) 294 add(s); 295 return this; 296 } 297 298 /** 299 * Convenience method for adding multiple objects to this list. 300 * 301 * <p> 302 * <jk>null</jk> values are skipped. 303 * 304 * @param o The objects to add to the list. 305 * @return This object (for method chaining). 306 */ 307 public ObjectList appendIfNotNull(Object...o) { 308 for (Object o2 : o) 309 if (o2 != null) 310 add(o2); 311 return this; 312 } 313 314 /** 315 * Get the entry at the specified index, converted to the specified type. 316 * 317 * <p> 318 * This is the preferred get method for simple types. 319 * 320 * <h5 class='section'>Examples:</h5> 321 * <p class='bcode w800'> 322 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 323 * 324 * <jc>// Value converted to a string.</jc> 325 * String s = l.get(1, String.<jk>class</jk>); 326 * 327 * <jc>// Value converted to a bean.</jc> 328 * MyBean b = l.get(2, MyBean.<jk>class</jk>); 329 * 330 * <jc>// Value converted to a bean array.</jc> 331 * MyBean[] ba = l.get(3, MyBean[].<jk>class</jk>); 332 * 333 * <jc>// Value converted to a linked-list of objects.</jc> 334 * List l1 = l.get(4, LinkedList.<jk>class</jk>); 335 * 336 * <jc>// Value converted to a map of object keys/values.</jc> 337 * Map m1 = l.get(5, TreeMap.<jk>class</jk>); 338 * </p> 339 * 340 * <p> 341 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 342 * 343 * @param index The index into this list. 344 * @param type The type of object to convert the entry to. 345 * @param <T> The type of object to convert the entry to. 346 * @return The converted entry. 347 */ 348 public <T> T get(int index, Class<T> type) { 349 return bs().convertToType(get(index), type); 350 } 351 352 /** 353 * Get the entry at the specified index, converted to the specified type. 354 * 355 * <p> 356 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 357 * 358 * <h5 class='section'>Examples:</h5> 359 * <p class='bcode w800'> 360 * ObjectList l = <jk>new</jk> ObjectList(<js>"..."</js>); 361 * 362 * <jc>// Value converted to a linked-list of strings.</jc> 363 * List<String> l1 = l.get(1, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 364 * 365 * <jc>// Value converted to a linked-list of beans.</jc> 366 * List<MyBean> l2 = l.get(2, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 367 * 368 * <jc>// Value converted to a linked-list of linked-lists of strings.</jc> 369 * List<List<String>> l3 = l.get(3, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 370 * 371 * <jc>// Value converted to a map of string keys/values.</jc> 372 * Map<String,String> m1 = l.get(4, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 373 * 374 * <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc> 375 * Map<String,List<MyBean>> m2 = l.get(5, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 376 * </p> 377 * 378 * <p> 379 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 380 * 381 * <p> 382 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 383 * 384 * <p> 385 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 386 * 387 * <p> 388 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 389 * 390 * @param index The index into this list. 391 * @param type The type of object to convert the entry to. 392 * @param args The type arguments of the type to convert the entry to. 393 * @param <T> The type of object to convert the entry to. 394 * @return The converted entry. 395 */ 396 public <T> T get(int index, Type type, Type...args) { 397 return bs().convertToType(get(index), type, args); 398 } 399 400 /** 401 * Shortcut for calling <code>get(index, String.<jk>class</jk>)</code>. 402 * 403 * @param index The index. 404 * @return The converted value. 405 */ 406 public String getString(int index) { 407 return get(index, String.class); 408 } 409 410 /** 411 * Shortcut for calling <code>get(index, Integer.<jk>class</jk>)</code>. 412 * 413 * @param index The index. 414 * @return The converted value. 415 * @throws InvalidDataConversionException If value cannot be converted. 416 */ 417 public Integer getInt(int index) { 418 return get(index, Integer.class); 419 } 420 421 /** 422 * Shortcut for calling <code>get(index, Boolean.<jk>class</jk>)</code>. 423 * 424 * @param index The index. 425 * @return The converted value. 426 * @throws InvalidDataConversionException If value cannot be converted. 427 */ 428 public Boolean getBoolean(int index) { 429 return get(index, Boolean.class); 430 } 431 432 /** 433 * Shortcut for calling <code>get(index, Long.<jk>class</jk>)</code>. 434 * 435 * @param index The index. 436 * @return The converted value. 437 * @throws InvalidDataConversionException If value cannot be converted. 438 */ 439 public Long getLong(int index) { 440 return get(index, Long.class); 441 } 442 443 /** 444 * Shortcut for calling <code>get(index, Map.<jk>class</jk>)</code>. 445 * 446 * @param index The index. 447 * @return The converted value. 448 * @throws InvalidDataConversionException If value cannot be converted. 449 */ 450 public Map<?,?> getMap(int index) { 451 return get(index, Map.class); 452 } 453 454 /** 455 * Same as {@link #getMap(int)} except converts the keys and values to the specified types. 456 * 457 * @param index The index. 458 * @param keyType The key type class. 459 * @param valType The value type class. 460 * @return The converted value. 461 * @throws InvalidDataConversionException If value cannot be converted. 462 */ 463 public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) { 464 return bs().convertToType(get(index), Map.class, keyType, valType); 465 } 466 467 /** 468 * Shortcut for calling <code>get(index, List.<jk>class</jk>)</code>. 469 * 470 * @param index The index. 471 * @return The converted value. 472 * @throws InvalidDataConversionException If value cannot be converted. 473 */ 474 public List<?> getList(int index) { 475 return get(index, List.class); 476 } 477 478 /** 479 * Same as {@link #getList(int)} except converts the elements to the specified types. 480 * 481 * @param index The index. 482 * @param elementType The element type class. 483 * @return The converted value. 484 * @throws InvalidDataConversionException If value cannot be converted. 485 */ 486 public <E> List<E> getList(int index, Class<E> elementType) { 487 return bs().convertToType(get(index), List.class, elementType); 488 } 489 490 /** 491 * Shortcut for calling <code>get(index, ObjectMap.<jk>class</jk>)</code>. 492 * 493 * @param index The index. 494 * @return The converted value. 495 * @throws InvalidDataConversionException If value cannot be converted. 496 */ 497 public ObjectMap getObjectMap(int index) { 498 return get(index, ObjectMap.class); 499 } 500 501 /** 502 * Shortcut for calling <code>get(index, ObjectList.<jk>class</jk>)</code>. 503 * 504 * @param index The index. 505 * @return The converted value. 506 * @throws InvalidDataConversionException If value cannot be converted. 507 */ 508 public ObjectList getObjectList(int index) { 509 return get(index, ObjectList.class); 510 } 511 512 /** 513 * Same as {@link #get(int,Class) get(int,Class)}, but the key is a slash-delimited path used to traverse entries in 514 * this POJO. 515 * 516 * <p> 517 * For example, the following code is equivalent: 518 * </p> 519 * <p class='bcode w800'> 520 * ObjectMap m = getObjectMap(); 521 * 522 * <jc>// Long way</jc> 523 * <jk>long</jk> l = m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).getLong(<js>"baz"</js>); 524 * 525 * <jc>// Using this method</jc> 526 * <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>); 527 * </p> 528 * 529 * <p> 530 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 531 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 532 * 533 * @param path The path to the entry. 534 * @param type The class type. 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, Class<T> type) { 540 return getPojoRest().get(path, type); 541 } 542 543 /** 544 * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections. 545 * 546 * @param path The path to the entry. 547 * @param type The class type. 548 * @param args The class parameter types. 549 * 550 * @param <T> The class type. 551 * @return The value, or <jk>null</jk> if the entry doesn't exist. 552 */ 553 public <T> T getAt(String path, Type type, Type...args) { 554 return getPojoRest().get(path, type, args); 555 } 556 557 /** 558 * Same as {@link #set(int,Object) set(int,Object)}, but the key is a slash-delimited path used to traverse entries 559 * in this POJO. 560 * 561 * <p> 562 * For example, the following code is equivalent: 563 * </p> 564 * <p class='bcode w800'> 565 * ObjectMap m = getObjectMap(); 566 * 567 * <jc>// Long way</jc> 568 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(<js>"0"</js>).put(<js>"baz"</js>, 123); 569 * 570 * <jc>// Using this method</jc> 571 * m.putAt(<js>"foo/bar/0/baz"</js>, 123); 572 * </p> 573 * 574 * <p> 575 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 576 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 577 * 578 * @param path The path to the entry. 579 * @param o The new value. 580 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 581 */ 582 public Object putAt(String path, Object o) { 583 return getPojoRest().put(path, o); 584 } 585 586 /** 587 * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays. 588 * 589 * <p> 590 * For example, the following code is equivalent: 591 * </p> 592 * <p class='bcode w800'> 593 * ObjectMap m = getObjectMap(); 594 * 595 * <jc>// Long way</jc> 596 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).append(123); 597 * 598 * <jc>// Using this method</jc> 599 * m.postAt(<js>"foo/bar"</js>, 123); 600 * </p> 601 * 602 * <p> 603 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 604 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 605 * 606 * @param path The path to the entry. 607 * @param o The new value. 608 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 609 */ 610 public Object postAt(String path, Object o) { 611 return getPojoRest().post(path, o); 612 } 613 614 /** 615 * Similar to {@link #remove(int) remove(int)},but the key is a slash-delimited path used to traverse entries in 616 * this POJO. 617 * 618 * <p> 619 * For example, the following code is equivalent: 620 * </p> 621 * <p class='bcode w800'> 622 * ObjectMap m = getObjectMap(); 623 * 624 * <jc>// Long way</jc> 625 * m.getObjectMap(<js>"foo"</js>).getObjectList(<js>"bar"</js>).getObjectMap(1).remove(<js>"baz"</js>); 626 * 627 * <jc>// Using this method</jc> 628 * m.deleteAt(<js>"foo/bar/0/baz"</js>); 629 * </p> 630 * 631 * <p> 632 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 633 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 634 * 635 * @param path The path to the entry. 636 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 637 */ 638 public Object deleteAt(String path) { 639 return getPojoRest().delete(path); 640 } 641 642 /** 643 * Creates an {@link Iterable} with elements of the specified child type. 644 * 645 * <p> 646 * Attempts to convert the child objects to the correct type if they aren't already the correct type. 647 * 648 * <p> 649 * The <c>next()</c> method on the returned iterator may throw a {@link InvalidDataConversionException} if 650 * the next element cannot be converted to the specified type. 651 * 652 * <p> 653 * See {@link BeanSession#convertToType(Object, ClassMeta)} for a description of valid conversions. 654 * 655 * <h5 class='section'>Example:</h5> 656 * <p class='bcode w800'> 657 * <jc>// Iterate over a list of ObjectMaps.</jc> 658 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>); 659 * for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) { 660 * <jc>// Do something with m.</jc> 661 * } 662 * 663 * <jc>// Iterate over a list of ints.</jc> 664 * ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); 665 * for (Integer i : l.elements(Integer.<jk>class</jk>)) { 666 * <jc>// Do something with i.</jc> 667 * } 668 * 669 * <jc>// Iterate over a list of beans.</jc> 670 * <jc>// Automatically converts to beans.</jc> 671 * ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); 672 * for (Person p : l.elements(Person.<jk>class</jk>)) { 673 * <jc>// Do something with p.</jc> 674 * } 675 * </p> 676 * 677 * @param <E> The child object type. 678 * @param childType The child object type. 679 * @return A new <c>Iterable</c> object over this list. 680 */ 681 public <E> Iterable<E> elements(final Class<E> childType) { 682 final Iterator<?> i = iterator(); 683 return new Iterable<E>() { 684 685 @Override /* Iterable */ 686 public Iterator<E> iterator() { 687 return new Iterator<E>() { 688 689 @Override /* Iterator */ 690 public boolean hasNext() { 691 return i.hasNext(); 692 } 693 694 @Override /* Iterator */ 695 public E next() { 696 return bs().convertToType(i.next(), childType); 697 } 698 699 @Override /* Iterator */ 700 public void remove() { 701 i.remove(); 702 } 703 704 }; 705 } 706 }; 707 } 708 709 /** 710 * Returns the {@link ClassMeta} of the class of the object at the specified index. 711 * 712 * @param index An index into this list, zero-based. 713 * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null. 714 */ 715 public ClassMeta<?> getClassMeta(int index) { 716 return bs().getClassMetaForObject(get(index)); 717 } 718 719 private PojoRest getPojoRest() { 720 if (pojoRest == null) 721 pojoRest = new PojoRest(this); 722 return pojoRest; 723 } 724 725 /** 726 * Serialize this array to a string using the specified serializer. 727 * 728 * @param serializer The serializer to use to convert this object to a string. 729 * @return This object as a serialized string. 730 * @throws SerializeException If a problem occurred trying to convert the output. 731 */ 732 public String toString(WriterSerializer serializer) throws SerializeException { 733 return serializer.serialize(this); 734 } 735 736 /** 737 * Returns <jk>true</jk> if this list is unmodifiable. 738 * 739 * @return <jk>true</jk> if this list is unmodifiable. 740 */ 741 public boolean isUnmodifiable() { 742 return false; 743 } 744 745 /** 746 * Returns a modifiable copy of this list if it's unmodifiable. 747 * 748 * @return A modifiable copy of this list if it's unmodifiable, or this list if it is already modifiable. 749 */ 750 public ObjectList modifiable() { 751 if (isUnmodifiable()) 752 return new ObjectList(this); 753 return this; 754 } 755 756 /** 757 * Returns an unmodifiable copy of this list if it's modifiable. 758 * 759 * @return An unmodifiable copy of this list if it's modifiable, or this list if it is already unmodifiable. 760 */ 761 public ObjectList unmodifiable() { 762 if (this instanceof UnmodifiableObjectList) 763 return this; 764 return new UnmodifiableObjectList(this); 765 } 766 767 /** 768 * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer. 769 */ 770 @Override /* Object */ 771 public String toString() { 772 try { 773 return this.toString(SimpleJsonSerializer.DEFAULT); 774 } catch (SerializeException e) { 775 return e.getLocalizedMessage(); 776 } 777 } 778 779 /** 780 * Convenience method for serializing this ObjectList to the specified Writer using the JsonSerializer.DEFAULT 781 * serializer. 782 * 783 * @param w The writer to send the serialized contents of this object. 784 * @throws IOException If a problem occurred trying to write to the writer. 785 * @throws SerializeException If a problem occurred trying to convert the output. 786 */ 787 public void serializeTo(Writer w) throws IOException, SerializeException { 788 JsonSerializer.DEFAULT.serialize(this); 789 } 790 791 /** 792 * Converts this object into the specified class type. 793 * 794 * <p> 795 * TODO - The current implementation is very inefficient. 796 * 797 * @param cm The class type to convert this object to. 798 * @return A converted object. 799 */ 800 public Object cast(ClassMeta<?> cm) { 801 try { 802 return JsonParser.DEFAULT.parse(SimpleJsonSerializer.DEFAULT.serialize(this), cm); 803 } catch (ParseException | SerializeException e) { 804 throw new RuntimeException(e); 805 } 806 } 807 808 private static final class UnmodifiableObjectList extends ObjectList { 809 private static final long serialVersionUID = 1L; 810 811 UnmodifiableObjectList(ObjectList contents) { 812 super(); 813 if (contents != null) { 814 for (Object e : this) { 815 super.add(e); 816 } 817 } 818 } 819 820 @Override /* List */ 821 public void add(int location, Object object) { 822 throw new UnsupportedOperationException("ObjectList is read-only."); 823 } 824 825 @Override /* List */ 826 public Object remove(int location) { 827 throw new UnsupportedOperationException("ObjectList is read-only."); 828 } 829 830 @Override /* List */ 831 public Object set(int location, Object object) { 832 throw new UnsupportedOperationException("ObjectList is read-only."); 833 } 834 835 @Override 836 public final boolean isUnmodifiable() { 837 return true; 838 } 839 } 840 841 BeanSession bs() { 842 if (session == null) 843 session = BeanContext.DEFAULT.createBeanSession(); 844 return session; 845 } 846}