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.internal.*; 024import org.apache.juneau.json.*; 025import org.apache.juneau.marshall.*; 026import org.apache.juneau.parser.*; 027import org.apache.juneau.serializer.*; 028import org.apache.juneau.transform.*; 029import org.apache.juneau.utils.*; 030 031/** 032 * Java implementation of a JSON object. 033 * 034 * <p> 035 * An extension of {@link LinkedHashMap}, so all methods available in that class are also available to this class. 036 * 037 * <p> 038 * Note that the use of this class is optional for generating JSON. The serializers will accept any objects that implement the 039 * {@link java.util.Map} interface. But this class provides some useful additional functionality when working with 040 * JSON models constructed from Java Collections Framework objects. For example, a constructor is provided for 041 * converting a JSON object string directly into a {@link Map}. It also contains accessor methods for to avoid common 042 * typecasting when accessing elements in a list. 043 * 044 * <h5 class='section'>Example:</h5> 045 * <p class='bcode w800'> 046 * <jc>// Construct an empty Map</jc> 047 * OMap m = OMap.<jsm>of</jsm>(); 048 * 049 * <jc>// Construct a Map from JSON</jc> 050 * m = OMap.<jsm>ofJson</jsm>(<js>"{a:'A',b:{c:'C',d:123}}"</js>); 051 * 052 * <jc>// Construct a Map using the append method</jc> 053 * m = OMap.<jsm>of</jsm>().a(<js>"foo"</js>,<js>"x"</js>).a(<js>"bar"</js>,123).a(<js>"baz"</js>,<jk>true</jk>); 054 * 055 * <jc>// Construct a Map from XML generated by XmlSerializer</jc> 056 * String xml = <js>"<object><a type='string'>A</a><b type='object'><c type='string'>C</c><d type='number'>123</d></b></object>"</js>; 057 * m = OMap.<jsm>of</jsm>(xml, XmlParser.<jsf>DEFAULT</jsf>); 058 * 059 * <jc>// Construct a Map from a URL GET parameter string generated by UrlEncodingParser</jc> 060 * String urlParams = <js>"?a='A'&b={c:'C',d:123}"</js>; 061 * m = OMap.<jsm>of</jsm>(urlParams, UrlEncodingParser.<jsf>DEFAULT</jsf>); 062 * 063 * <jc>// Construct JSON from OMap</jc> 064 * m = OMap.<jsm>ofJson</jsm>(<js>"{foo:'bar'},{baz:[123,true]}"</js>); 065 * json = m.toString(); <jc>// Produces "{foo:'bar'},{baz:[123,true]}"</jc> 066 * json = m.toString(JsonSerializer.<jsf>DEFAULT</jsf>); <jc>// Equivalent</jc> 067 * json = JsonSerializer.<jsf>DEFAULT</jsf>.serialize(m); <jc>// Equivalent</jc> 068 * 069 * <jc>// Get a map entry as an Integer</jc> 070 * m = OMap.<jsm>ofJson</jsm>(<js>"{foo:123}"</js>); 071 * Integer i = m.getInt(<js>"foo"</js>); 072 * i = m.get(Integer.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> 073 * 074 * <jc>// Get a map entry as a Float</jc> 075 * m = OMap.<jsm>ofJson</jsm>(<js>"{foo:123}"</js>); 076 * Float f = m.getFloat(<js>"foo"</js>); 077 * f = m.get(Float.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> 078 * 079 * <jc>// Same as above, except converted to a String</jc> 080 * m = OMap.<jsm>ofJson</jsm>(<js>"{foo:123}"</js>); 081 * String s = m.getString(<js>"foo"</js>); <jc>// Returns "123"</jc> 082 * s = m.get(String.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> 083 * 084 * <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc> 085 * m = OMap.<jsm>ofJson</jsm>(<js>"{person:{name:'John Smith',age:45}}"</js>); 086 * Person p = m.get(Person.<jk>class</jk>, <js>"person"</js>); 087 * 088 * <jc>// Add an inner map</jc> 089 * OMap m1 = OMap.<jsm>ofJson</jsm>(<js>"{a:1}"</js>); 090 * OMap m2 = OMap.<jsm>ofJson</jsm>(<js>"{b:2}"</js>).setInner(m1); 091 * <jk>int</jk> a = m2.getInt(<js>"a"</js>); <jc>// a == 1 </jc> 092 * </p> 093 * 094 * <p> 095 * This class is not thread safe. 096 */ 097public class OMap extends ObjectMap /* In 9.0 - LinkedHashMap<String,Object> */ { 098 private static final long serialVersionUID = 1L; 099 100 private transient BeanSession session; 101 private Map<String,Object> inner; 102 private transient PojoRest pojoRest; 103 104 /** 105 * An empty read-only OMap. 106 */ 107 public static final OMap EMPTY_MAP = new OMap() { 108 109 private static final long serialVersionUID = 1L; 110 111 @Override /* Map */ 112 public Set<Map.Entry<String,Object>> entrySet() { 113 return Collections.<String,Object>emptyMap().entrySet(); 114 } 115 116 @Override /* Map */ 117 public Set<String> keySet() { 118 return Collections.<String,Object>emptyMap().keySet(); 119 } 120 121 @Override /* Map */ 122 public Object put(String key, Object value) { 123 throw new UnsupportedOperationException(); 124 } 125 126 @Override /* Map */ 127 public Object remove(Object key) { 128 throw new UnsupportedOperationException(); 129 } 130 131 @Override /* Map */ 132 public Collection<Object> values() { 133 return Collections.emptyMap().values(); 134 } 135 }; 136 137 //------------------------------------------------------------------------------------------------------------------ 138 // Constructors. 139 //------------------------------------------------------------------------------------------------------------------ 140 141 /** 142 * Construct an empty map. 143 */ 144 public OMap() {} 145 146 /** 147 * Construct an empty map with the specified bean context. 148 * 149 * @param session The bean session to use for creating beans. 150 */ 151 public OMap(BeanSession session) { 152 this.session = session; 153 } 154 155 /** 156 * Construct a map initialized with the specified map. 157 * 158 * @param in 159 * The map to copy. 160 * <br>Can be <jk>null</jk>. 161 * <br>Keys will be converted to strings using {@link Object#toString()}. 162 */ 163 public OMap(Map<?,?> in) { 164 this(); 165 if (in != null) 166 for (Map.Entry<?,?> e : in.entrySet()) 167 put(e.getKey().toString(), e.getValue()); 168 } 169 170 /** 171 * Construct a map initialized with the specified JSON. 172 * 173 * @param json 174 * The JSON text to parse. 175 * <br>Can be normal or simplified JSON. 176 * @throws ParseException Malformed input encountered. 177 */ 178 public OMap(CharSequence json) throws ParseException { 179 this(json, JsonParser.DEFAULT); 180 } 181 182 /** 183 * Construct a map initialized with the specified string. 184 * 185 * @param in 186 * The input being parsed. 187 * <br>Can be <jk>null</jk>. 188 * @param p 189 * The parser to use to parse the input. 190 * <br>If <jk>null</jk>, uses {@link JsonParser}. 191 * @throws ParseException Malformed input encountered. 192 */ 193 public OMap(CharSequence in, Parser p) throws ParseException { 194 this(p == null ? null : p.createBeanSession()); 195 if (p == null) 196 p = JsonParser.DEFAULT; 197 if (! StringUtils.isEmpty(in)) 198 p.parseIntoMap(in, this, bs().string(), bs().object()); 199 } 200 201 /** 202 * Construct a map initialized with the specified reader containing JSON. 203 * 204 * @param json 205 * The reader containing JSON text to parse. 206 * <br>Can contain normal or simplified JSON. 207 * @throws ParseException Malformed input encountered. 208 */ 209 public OMap(Reader json) throws ParseException { 210 parse(json, JsonParser.DEFAULT); 211 } 212 213 /** 214 * Construct a map initialized with the specified string. 215 * 216 * @param in 217 * The reader containing the input being parsed. 218 * <br>Can contain normal or simplified JSON. 219 * @param p 220 * The parser to use to parse the input. 221 * <br>If <jk>null</jk>, uses {@link JsonParser}. 222 * @throws ParseException Malformed input encountered. 223 */ 224 public OMap(Reader in, Parser p) throws ParseException { 225 this(p == null ? null : p.createBeanSession()); 226 parse(in, p); 227 } 228 229 /** 230 * Construct a map initialized with the specified key/value pairs. 231 * 232 * <h5 class='section'>Examples:</h5> 233 * <p class='bcode w800'> 234 * OMap m = <jk>new</jk> OMap(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>); 235 * </p> 236 * 237 * @param keyValuePairs A list of key/value pairs to add to this map. 238 */ 239 public OMap(Object... keyValuePairs) { 240 if (keyValuePairs.length % 2 != 0) 241 throw new RuntimeException("Odd number of parameters passed into OMap(Object...)"); 242 for (int i = 0; i < keyValuePairs.length; i+=2) 243 put(stringify(keyValuePairs[i]), keyValuePairs[i+1]); 244 } 245 246 //------------------------------------------------------------------------------------------------------------------ 247 // Creators. 248 //------------------------------------------------------------------------------------------------------------------ 249 250 /** 251 * Construct an empty map. 252 * 253 * @return An empty map. 254 */ 255 public static OMap of() { 256 return new OMap(); 257 } 258 259 /** 260 * Construct a map initialized with the specified map. 261 * 262 * @param in 263 * The map to copy. 264 * <br>Can be <jk>null</jk>. 265 * <br>Keys will be converted to strings using {@link Object#toString()}. 266 * @return A new map or <jk>null</jk> if the map was <jk>null</jk>. 267 */ 268 public static OMap ofAll(Map<?,?> in) { 269 return in == null ? null : new OMap(in); 270 } 271 272 /** 273 * Construct a map initialized with the specified JSON string. 274 * 275 * @param json 276 * The JSON text to parse. 277 * <br>Can be normal or simplified JSON. 278 * @return A new map or <jk>null</jk> if the string was null. 279 * @throws ParseException Malformed input encountered. 280 */ 281 public static OMap ofJson(CharSequence json) throws ParseException { 282 return json == null ? null : new OMap(json); 283 } 284 285 /** 286 * Construct a map initialized with the specified string. 287 * 288 * @param in 289 * The input being parsed. 290 * <br>Can be <jk>null</jk>. 291 * @param p 292 * The parser to use to parse the input. 293 * <br>If <jk>null</jk>, uses {@link JsonParser}. 294 * @return A new map or <jk>null</jk> if the input was <jk>null</jk>. 295 * @throws ParseException Malformed input encountered. 296 */ 297 public static OMap ofText(CharSequence in, Parser p) throws ParseException { 298 return in == null ? null : new OMap(in, p); 299 } 300 301 /** 302 * Construct a map initialized with the specified reader containing JSON. 303 * 304 * @param json 305 * The reader containing JSON text to parse. 306 * <br>Can contain normal or simplified JSON. 307 * @return A new map or <jk>null</jk> if the input was <jk>null</jk>. 308 * @throws ParseException Malformed input encountered. 309 */ 310 public static OMap ofJson(Reader json) throws ParseException { 311 return json == null ? null : new OMap(json); 312 } 313 314 /** 315 * Construct a map initialized with the specified string. 316 * 317 * @param in 318 * The reader containing the input being parsed. 319 * <br>Can contain normal or simplified JSON. 320 * @param p 321 * The parser to use to parse the input. 322 * <br>If <jk>null</jk>, uses {@link JsonParser}. 323 * @return A new map or <jk>null</jk> if the input was <jk>null</jk>. 324 * @throws ParseException Malformed input encountered. 325 */ 326 public static OMap ofText(Reader in, Parser p) throws ParseException { 327 return in == null ? null : new OMap(in); 328 } 329 330 /** 331 * Construct a map initialized with the specified key/value pairs. 332 * 333 * <h5 class='section'>Examples:</h5> 334 * <p class='bcode w800'> 335 * OMap m = <jk>new</jk> OMap(<js>"key1"</js>,<js>"val1"</js>,<js>"key2"</js>,<js>"val2"</js>); 336 * </p> 337 * 338 * @param keyValuePairs A list of key/value pairs to add to this map. 339 * @return A new map, never <jk>null</jk>. 340 */ 341 public static OMap of(Object... keyValuePairs) { 342 return new OMap(keyValuePairs); 343 } 344 345 //------------------------------------------------------------------------------------------------------------------ 346 // Initializers. 347 //------------------------------------------------------------------------------------------------------------------ 348 349 /** 350 * Set an inner map in this map to allow for chained get calls. 351 * 352 * <p> 353 * If {@link #get(Object)} returns <jk>null</jk>, then {@link #get(Object)} will be called on the inner map. 354 * 355 * <p> 356 * In addition to providing the ability to chain maps, this method also provides the ability to wrap an existing map 357 * inside another map so that you can add entries to the outer map without affecting the values on the inner map. 358 * 359 * <p class='bcode w800'> 360 * OMap m1 = OMap.<jsm>ofJson</jsm>(<js>"{foo:1}"</js>); 361 * OMap m2 = OMap.<jsm>of</jsm>().setInner(m1); 362 * m2.put(<js>"foo"</js>, 2); <jc>// Overwrite the entry</jc> 363 * <jk>int</jk> foo1 = m1.getInt(<js>"foo"</js>); <jc>// foo1 == 1 </jc> 364 * <jk>int</jk> foo2 = m2.getInt(<js>"foo"</js>); <jc>// foo2 == 2 </jc> 365 * </p> 366 * 367 * @param inner 368 * The inner map. 369 * Can be <jk>null</jk> to remove the inner map from an existing map. 370 * @return This object (for method chaining). 371 */ 372 public OMap inner(Map<String,Object> inner) { 373 this.inner = inner; 374 return this; 375 } 376 377 /** 378 * Override the default bean session used for converting POJOs. 379 * 380 * <p> 381 * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases. 382 * 383 * <p> 384 * Useful if you're serializing/parsing beans with transforms defined. 385 * 386 * @param session The new bean session. 387 * @return This object (for method chaining). 388 */ 389 public OMap session(BeanSession session) { 390 this.session = session; 391 return this; 392 } 393 394 //------------------------------------------------------------------------------------------------------------------ 395 // Appenders. 396 //------------------------------------------------------------------------------------------------------------------ 397 398 /** 399 * Add. 400 * 401 * <p> 402 * Adds an entry to this map. 403 * 404 * @param key The key. 405 * @param value The value. 406 * @return This object (for method chaining). 407 */ 408 public OMap a(String key, Object value) { 409 put(key, value); 410 return this; 411 } 412 413 /** 414 * Add. 415 * 416 * <p> 417 * Same as {@link #a(String, Object)} 418 * 419 * @param key The key. 420 * @param value The value. 421 * @return This object (for method chaining). 422 */ 423 @Override 424 public OMap append(String key, Object value) { 425 return a(key,value); 426 } 427 428 /** 429 * Add if. 430 * 431 * <p> 432 * Conditionally adds an entry to this map. 433 * 434 * @param overwrite Overwrite the previous value if there was one. 435 * @param skipNullValue Skip adding the value if the value is <jk>null</jk>. 436 * @param skipEmptyValue Skip adding the value if the value is an empty string. 437 * @param key The key. 438 * @param value The value. 439 * @return This object (for method chaining). 440 */ 441 public OMap aif(boolean overwrite, boolean skipNullValue, boolean skipEmptyValue, String key, Object value) { 442 if (value == null && skipNullValue) 443 return this; 444 if (skipEmptyValue && ObjectUtils.isEmpty(value)) 445 return this; 446 Object current = get(key); 447 if (current == null || overwrite) 448 put(key, value); 449 return this; 450 } 451 452 /** 453 * Add if. 454 * 455 * <p> 456 * Same as {@link #aif(boolean, boolean, boolean, String, Object)}. 457 * 458 * @param overwrite Overwrite the previous value if there was one. 459 * @param skipNullValue Skip adding the value if the value is <jk>null</jk>. 460 * @param skipEmptyValue Skip adding the value if the value is an empty string. 461 * @param key The key. 462 * @param value The value. 463 * @return This object (for method chaining). 464 */ 465 @Override 466 public OMap appendIf(boolean overwrite, boolean skipNullValue, boolean skipEmptyValue, String key, Object value) { 467 return aif(overwrite, skipNullValue, skipEmptyValue, key, value); 468 } 469 470 /** 471 * Add if. 472 * 473 * <p> 474 * Conditionally adds an entry to this map. 475 * 476 * @param flag The boolean value that must be <jk>true</jk> in order to add this entry.. 477 * @param key The key. 478 * @param value The value. 479 * @return This object (for method chaining). 480 */ 481 public OMap aif(boolean flag, String key, Object value) { 482 if (flag) 483 put(key, value); 484 return this; 485 } 486 487 /** 488 * Add if. 489 * 490 * <p> 491 * Same as {@link #aif(boolean,String,Object)}. 492 * 493 * @param flag The boolean value that must be <jk>true</jk> in order to add this entry.. 494 * @param key The key. 495 * @param value The value. 496 * @return This object (for method chaining). 497 */ 498 @Override 499 public OMap appendIf(boolean flag, String key, Object value) { 500 return aif(flag, key, value); 501 } 502 503 /** 504 * Add skip empty. 505 * 506 * <p> 507 * A no-op if the value is <jk>null</jk> or an empty string/map/collection. 508 * 509 * @param key The key. 510 * @param values The values. 511 * @return This object (for method chaining). 512 */ 513 public OMap ase(String key, Object...values) { 514 for (Object v : values) 515 aif(true, true, true, key, v); 516 return this; 517 } 518 519 /** 520 * Add skip empty. 521 * 522 * <p> 523 * Same as {@link #ase(String, Object...)}. 524 * 525 * @param key The key. 526 * @param value The value. 527 * @return This object (for method chaining). 528 */ 529 @Override 530 public OMap appendSkipEmpty(String key, Object value) { 531 return ase(key, value); 532 } 533 534 /** 535 * Add skip false. 536 * 537 * <p> 538 * A no-op if the value is <jk>false</jk>. 539 * 540 * @param key The key. 541 * @param value The value. 542 * @return This object (for method chaining). 543 */ 544 public OMap asf(String key, boolean value) { 545 if (value) 546 a(key, value); 547 return this; 548 } 549 550 /** 551 * Add skip false. 552 * 553 * <p> 554 * Same as {@link #asf(String, boolean)}. 555 * 556 * @param key The key. 557 * @param value The value. 558 * @return This object (for method chaining). 559 */ 560 @Override 561 public OMap appendSkipFalse(String key, boolean value) { 562 return asf(key, value); 563 } 564 565 /** 566 * Add skip minus one. 567 * 568 * <p> 569 * A no-op if the value is <c>-1</c>. 570 * 571 * @param key The key. 572 * @param values The values. 573 * @return This object (for method chaining). 574 */ 575 public OMap asmo(String key, Number...values) { 576 for (Number v : values) 577 if (v != null && v.intValue() != -1) 578 a(key, v); 579 return this; 580 } 581 582 /** 583 * Add skip minus one. 584 * 585 * <p> 586 * Same as {@link #asmo(String, Number...)}. 587 * 588 * @param key The key. 589 * @param value The value. 590 * @return This object (for method chaining). 591 */ 592 @Override 593 public OMap appendSkipMinusOne(String key, Number value) { 594 return asmo(key, value); 595 } 596 597 /** 598 * Add skip null. 599 * 600 * <p> 601 * <jk>null</jk> values are skipped. 602 * 603 * @param key The key. 604 * @param value The value. 605 * @return This object (for method chaining). 606 */ 607 public OMap asn(String key, Object value) { 608 if (value != null) 609 a(key, value); 610 return this; 611 } 612 613 /** 614 * Add skip null. 615 * 616 * <p> 617 * Same as {@link #asn(String, Object)}. 618 * 619 * @param key The key. 620 * @param value The value. 621 * @return This object (for method chaining). 622 */ 623 @Override 624 public OMap appendSkipNull(String key, Object value) { 625 return asn(key, value); 626 } 627 628 /** 629 * Add all. 630 * 631 * <p> 632 * Equivalent to calling {@code putAll(m)}, but returns this map so that the method can be chained. 633 * 634 * @param m The map whose contents should be added to this map. 635 * @return This object (for method chaining). 636 */ 637 public OMap aa(Map<String,Object> m) { 638 if (m != null) 639 putAll(m); 640 return this; 641 } 642 643 /** 644 * Add all. 645 * 646 * <p> 647 * Same as {@link #aa(Map)}. 648 * 649 * @param m The map whose contents should be added to this map. 650 * @return This object (for method chaining). 651 */ 652 @Override 653 public OMap appendAll(Map<String,Object> m) { 654 if (m != null) 655 putAll(m); 656 return this; 657 } 658 659 /** 660 * Add if null. 661 * 662 * <p> 663 * Sets a value in this map if the entry does not exist or the value is <jk>null</jk>. 664 * 665 * @param key The map key. 666 * @param val The value to set if the current value does not exist or is <jk>null</jk>. 667 * @return This object (for method chaining). 668 */ 669 public OMap aifn(String key, Object val) { 670 Object o = get(key); 671 if (o == null) 672 put(key, val); 673 return this; 674 } 675 676 /** 677 * Add if null. 678 * 679 * <p> 680 * Same as {@link #aifn(String, Object)}. 681 * 682 * @param key The map key. 683 * @param val The value to set if the current value does not exist or is <jk>null</jk>. 684 * @return This object (for method chaining). 685 */ 686 public OMap appendIfNull(String key, Object val) { 687 return aifn(key, val); 688 } 689 690 /** 691 * Add if empty. 692 * 693 * <p> 694 * Sets a value in this map if the entry does not exist or the value is <jk>null</jk> or an empty string. 695 * 696 * @param key The map key. 697 * @param val The value to set if the current value does not exist or is <jk>null</jk> or an empty string. 698 * @return This object (for method chaining). 699 */ 700 public OMap aife(String key, Object val) { 701 Object o = get(key); 702 if (o == null || o.toString().isEmpty()) 703 put(key, val); 704 return this; 705 } 706 707 /** 708 * Add if empty. 709 * 710 * <p> 711 * Same as {@link #aife(String, Object)}. 712 * 713 * @param key The map key. 714 * @param val The value to set if the current value does not exist or is <jk>null</jk> or an empty string. 715 * @return This object (for method chaining). 716 */ 717 public OMap appendIfEmpty(String key, Object val) { 718 return aife(key, val); 719 } 720 721 /** 722 * Add if not empty. 723 * 724 * <p> 725 * Adds a mapping if the specified key doesn't exist. 726 * 727 * @param key The map key. 728 * @param val The value to set if the current value does not exist or is <jk>null</jk> or an empty string. 729 * @return This object (for method chaining). 730 */ 731 public OMap aifne(String key, Object val) { 732 if (! containsKey(key)) 733 put(key, val); 734 return this; 735 } 736 737 /** 738 * Add if not empty. 739 * 740 * <p> 741 * Same as {@link #aifne(String, Object)}. 742 * 743 * @param key The map key. 744 * @param val The value to set if the current value does not exist or is <jk>null</jk> or an empty string. 745 * @return This object (for method chaining). 746 */ 747 public OMap appendIfNotEmpty(String key, Object val) { 748 return aifne(key, val); 749 } 750 751 /** 752 * Add if predicate matches. 753 * 754 * @param p The predicate to match against. 755 * @param key The map key. 756 * @param val The value to add if the predicate matches. 757 * @return This object (for method chaining). 758 */ 759 public OMap aif(Predicate<Object> p, String key, Object val) { 760 if (p.test(val)) 761 a(key, val); 762 return this; 763 } 764 765 /** 766 * Add if predicate matches. 767 * 768 * <p> 769 * Same as {@link #aif(Predicate, String, Object)}. 770 * 771 * @param p The predicate to match against. 772 * @param key The map key. 773 * @param val The value to add if the predicate matches. 774 * @return This object (for method chaining). 775 */ 776 public OMap appendIf(Predicate<Object> p, String key, Object val) { 777 return aif(p, key, val); 778 } 779 780 //------------------------------------------------------------------------------------------------------------------ 781 // Retrievers. 782 //------------------------------------------------------------------------------------------------------------------ 783 784 /** 785 * Same as {@link Map#get(Object) get()}, but casts or converts the value to the specified class type. 786 * 787 * <p> 788 * This is the preferred get method for simple types. 789 * 790 * <h5 class='section'>Examples:</h5> 791 * <p class='bcode w800'> 792 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 793 * 794 * <jc>// Value converted to a string.</jc> 795 * String s = m.get(<js>"key1"</js>, String.<jk>class</jk>); 796 * 797 * <jc>// Value converted to a bean.</jc> 798 * MyBean b = m.get(<js>"key2"</js>, MyBean.<jk>class</jk>); 799 * 800 * <jc>// Value converted to a bean array.</jc> 801 * MyBean[] ba = m.get(<js>"key3"</js>, MyBean[].<jk>class</jk>); 802 * 803 * <jc>// Value converted to a linked-list of objects.</jc> 804 * List l = m.get(<js>"key4"</js>, LinkedList.<jk>class</jk>); 805 * 806 * <jc>// Value converted to a map of object keys/values.</jc> 807 * Map m2 = m.get(<js>"key5"</js>, TreeMap.<jk>class</jk>); 808 * </p> 809 * 810 * <p> 811 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 812 * 813 * @param key The key. 814 * @param <T> The class type returned. 815 * @param type The class type returned. 816 * @return The value, or <jk>null</jk> if the entry doesn't exist. 817 */ 818 @Override 819 public <T> T get(String key, Class<T> type) { 820 return getWithDefault(key, (T)null, type); 821 } 822 823 /** 824 * Same as {@link #get(String,Class)}, but allows for complex data types consisting of collections or maps. 825 * 826 * <p> 827 * The type can be a simple type (e.g. beans, strings, numbers) or parameterized type (collections/maps). 828 * 829 * <h5 class='section'>Examples:</h5> 830 * <p class='bcode w800'> 831 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 832 * 833 * <jc>// Value converted to a linked-list of strings.</jc> 834 * List<String> l1 = m.get(<js>"key1"</js>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 835 * 836 * <jc>// Value converted to a linked-list of beans.</jc> 837 * List<MyBean> l2 = m.get(<js>"key2"</js>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>); 838 * 839 * <jc>// Value converted to a linked-list of linked-lists of strings.</jc> 840 * List<List<String>> l3 = m.get(<js>"key3"</js>, LinkedList.<jk>class</jk>, LinkedList.<jk>class</jk>, String.<jk>class</jk>); 841 * 842 * <jc>// Value converted to a map of string keys/values.</jc> 843 * Map<String,String> m1 = m.get(<js>"key4"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, String.<jk>class</jk>); 844 * 845 * <jc>// Value converted to a map containing string keys and values of lists containing beans.</jc> 846 * Map<String,List<MyBean>> m2 = m.get(<js>"key5"</js>, TreeMap.<jk>class</jk>, String.<jk>class</jk>, List.<jk>class</jk>, MyBean.<jk>class</jk>); 847 * </p> 848 * 849 * <p> 850 * <c>Collection</c> classes are assumed to be followed by zero or one objects indicating the element type. 851 * 852 * <p> 853 * <c>Map</c> classes are assumed to be followed by zero or two meta objects indicating the key and value types. 854 * 855 * <p> 856 * The array can be arbitrarily long to indicate arbitrarily complex data structures. 857 * 858 * <p> 859 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 860 * 861 * <ul class='notes'> 862 * <li> 863 * Use the {@link #get(String, Class)} method instead if you don't need a parameterized map/collection. 864 * </ul> 865 * 866 * @param key The key. 867 * @param <T> The class type returned. 868 * @param type The class type returned. 869 * @param args The class type parameters. 870 * @return The value, or <jk>null</jk> if the entry doesn't exist. 871 */ 872 @Override 873 public <T> T get(String key, Type type, Type...args) { 874 return getWithDefault(key, null, type, args); 875 } 876 877 /** 878 * Same as {@link Map#get(Object) get()}, but returns the default value if the key could not be found. 879 * 880 * @param key The key. 881 * @param def The default value if the entry doesn't exist. 882 * @return The value, or the default value if the entry doesn't exist. 883 */ 884 @Override 885 public Object getWithDefault(String key, Object def) { 886 Object o = get(key); 887 return (o == null ? def : o); 888 } 889 890 /** 891 * Same as {@link #get(String,Class)} but returns a default value if the value does not exist. 892 * 893 * @param key The key. 894 * @param def The default value. Can be <jk>null</jk>. 895 * @param <T> The class type returned. 896 * @param type The class type returned. 897 * @return The value, or <jk>null</jk> if the entry doesn't exist. 898 */ 899 @Override 900 public <T> T getWithDefault(String key, T def, Class<T> type) { 901 return getWithDefault(key, def, type, new Type[0]); 902 } 903 904 /** 905 * Same as {@link #get(String,Type,Type...)} but returns a default value if the value does not exist. 906 * 907 * @param key The key. 908 * @param def The default value. Can be <jk>null</jk>. 909 * @param <T> The class type returned. 910 * @param type The class type returned. 911 * @param args The class type parameters. 912 * @return The value, or <jk>null</jk> if the entry doesn't exist. 913 */ 914 @Override 915 public <T> T getWithDefault(String key, T def, Type type, Type...args) { 916 Object o = get(key); 917 if (o == null) 918 return def; 919 T t = bs().convertToType(o, type, args); 920 return t == null ? def : t; 921 } 922 923 /** 924 * Searches for the specified key in this map ignoring case. 925 * 926 * @param key 927 * The key to search for. 928 * For performance reasons, it's preferable that the key be all lowercase. 929 * @return The key, or <jk>null</jk> if map does not contain this key. 930 */ 931 @Override 932 public String findKeyIgnoreCase(String key) { 933 for (String k : keySet()) 934 if (key.equalsIgnoreCase(k)) 935 return k; 936 return null; 937 } 938 939 /** 940 * Same as {@link Map#get(Object) get()}, but converts the raw value to the specified class type using the specified 941 * POJO swap. 942 * 943 * @param key The key. 944 * @param pojoSwap The swap class used to convert the raw type to a transformed type. 945 * @param <T> The transformed class type. 946 * @return The value, or <jk>null</jk> if the entry doesn't exist. 947 * @throws ParseException Malformed input encountered. 948 */ 949 @Override 950 @SuppressWarnings({ "rawtypes", "unchecked" }) 951 public <T> T getSwapped(String key, PojoSwap<T,?> pojoSwap) throws ParseException { 952 try { 953 Object o = super.get(key); 954 if (o == null) 955 return null; 956 PojoSwap swap = pojoSwap; 957 return (T) swap.unswap(bs(), o, null); 958 } catch (ParseException e) { 959 throw e; 960 } catch (Exception e) { 961 throw new ParseException(e); 962 } 963 } 964 965 /** 966 * Returns the value for the first key in the list that has an entry in this map. 967 * 968 * @param keys The keys to look up in order. 969 * @return The value of the first entry whose key exists, or <jk>null</jk> if none of the keys exist in this map. 970 */ 971 @Override 972 public Object find(String...keys) { 973 for (String key : keys) 974 if (containsKey(key)) 975 return get(key); 976 return null; 977 } 978 979 /** 980 * Returns the value for the first key in the list that has an entry in this map. 981 * 982 * <p> 983 * Casts or converts the value to the specified class type. 984 * 985 * <p> 986 * See {@link BeanSession#convertToType(Object, ClassMeta)} for the list of valid data conversions. 987 * 988 * @param type The class type to convert the value to. 989 * @param <T> The class type to convert the value to. 990 * @param keys The keys to look up in order. 991 * @return The value of the first entry whose key exists, or <jk>null</jk> if none of the keys exist in this map. 992 */ 993 @Override 994 public <T> T find(Class<T> type, String...keys) { 995 for (String key : keys) 996 if (containsKey(key)) 997 return get(key, type); 998 return null; 999 } 1000 1001 /** 1002 * Returns the specified entry value converted to a {@link String}. 1003 * 1004 * <p> 1005 * Shortcut for <code>get(key, String.<jk>class</jk>)</code>. 1006 * 1007 * @param key The key. 1008 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1009 */ 1010 @Override 1011 public String getString(String key) { 1012 return get(key, String.class); 1013 } 1014 1015 /** 1016 * Returns the specified entry value converted to a {@link String}. 1017 * 1018 * <p> 1019 * Shortcut for <code>get(key, String[].<jk>class</jk>)</code>. 1020 * 1021 * @param key The key. 1022 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1023 */ 1024 @Override 1025 public String[] getStringArray(String key) { 1026 return getStringArray(key, null); 1027 } 1028 1029 /** 1030 * Same as {@link #getStringArray(String)} but returns a default value if the value cannot be found. 1031 * 1032 * @param key The map key. 1033 * @param def The default value if value is not found. 1034 * @return The value converted to a string array. 1035 */ 1036 @Override 1037 public String[] getStringArray(String key, String[] def) { 1038 Object s = get(key, Object.class); 1039 if (s == null) 1040 return def; 1041 String[] r = null; 1042 if (s instanceof Collection) 1043 r = ArrayUtils.toStringArray((Collection<?>)s); 1044 else if (s instanceof String[]) 1045 r = (String[])s; 1046 else if (s instanceof Object[]) 1047 r = ArrayUtils.toStringArray(Arrays.asList((Object[])s)); 1048 else 1049 r = split(stringify(s)); 1050 return (r.length == 0 ? def : r); 1051 } 1052 1053 /** 1054 * Returns the specified entry value converted to a {@link String}. 1055 * 1056 * <p> 1057 * Shortcut for <code>getWithDefault(key, defVal, String.<jk>class</jk>)</code>. 1058 * 1059 * @param key The key. 1060 * @param defVal The default value if the map doesn't contain the specified mapping. 1061 * @return The converted value, or the default value if the map contains no mapping for this key. 1062 */ 1063 @Override 1064 public String getString(String key, String defVal) { 1065 return getWithDefault(key, defVal, String.class); 1066 } 1067 1068 /** 1069 * Returns the specified entry value converted to an {@link Integer}. 1070 * 1071 * <p> 1072 * Shortcut for <code>get(key, Integer.<jk>class</jk>)</code>. 1073 * 1074 * @param key The key. 1075 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1076 * @throws InvalidDataConversionException If value cannot be converted. 1077 */ 1078 @Override 1079 public Integer getInt(String key) { 1080 return get(key, Integer.class); 1081 } 1082 1083 /** 1084 * Returns the specified entry value converted to an {@link Integer}. 1085 * 1086 * <p> 1087 * Shortcut for <code>getWithDefault(key, defVal, Integer.<jk>class</jk>)</code>. 1088 * 1089 * @param key The key. 1090 * @param defVal The default value if the map doesn't contain the specified mapping. 1091 * @return The converted value, or the default value if the map contains no mapping for this key. 1092 * @throws InvalidDataConversionException If value cannot be converted. 1093 */ 1094 @Override 1095 public Integer getInt(String key, Integer defVal) { 1096 return getWithDefault(key, defVal, Integer.class); 1097 } 1098 1099 /** 1100 * Returns the specified entry value converted to a {@link Long}. 1101 * 1102 * <p> 1103 * Shortcut for <code>get(key, Long.<jk>class</jk>)</code>. 1104 * 1105 * @param key The key. 1106 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1107 * @throws InvalidDataConversionException If value cannot be converted. 1108 */ 1109 @Override 1110 public Long getLong(String key) { 1111 return get(key, Long.class); 1112 } 1113 1114 /** 1115 * Returns the specified entry value converted to a {@link Long}. 1116 * 1117 * <p> 1118 * Shortcut for <code>getWithDefault(key, defVal, Long.<jk>class</jk>)</code>. 1119 * 1120 * @param key The key. 1121 * @param defVal The default value if the map doesn't contain the specified mapping. 1122 * @return The converted value, or the default value if the map contains no mapping for this key. 1123 * @throws InvalidDataConversionException If value cannot be converted. 1124 */ 1125 @Override 1126 public Long getLong(String key, Long defVal) { 1127 return getWithDefault(key, defVal, Long.class); 1128 } 1129 1130 /** 1131 * Returns the specified entry value converted to a {@link Boolean}. 1132 * 1133 * <p> 1134 * Shortcut for <code>get(key, Boolean.<jk>class</jk>)</code>. 1135 * 1136 * @param key The key. 1137 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1138 * @throws InvalidDataConversionException If value cannot be converted. 1139 */ 1140 @Override 1141 public Boolean getBoolean(String key) { 1142 return get(key, Boolean.class); 1143 } 1144 1145 /** 1146 * Returns the specified entry value converted to a {@link Boolean}. 1147 * 1148 * <p> 1149 * Shortcut for <code>getWithDefault(key, defVal, Boolean.<jk>class</jk>)</code>. 1150 * 1151 * @param key The key. 1152 * @param defVal The default value if the map doesn't contain the specified mapping. 1153 * @return The converted value, or the default value if the map contains no mapping for this key. 1154 * @throws InvalidDataConversionException If value cannot be converted. 1155 */ 1156 @Override 1157 public Boolean getBoolean(String key, Boolean defVal) { 1158 return getWithDefault(key, defVal, Boolean.class); 1159 } 1160 1161 /** 1162 * Returns the specified entry value converted to a {@link Map}. 1163 * 1164 * <p> 1165 * Shortcut for <code>get(key, OMap.<jk>class</jk>)</code>. 1166 * 1167 * @param key The key. 1168 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1169 * @throws InvalidDataConversionException If value cannot be converted. 1170 */ 1171 @Override 1172 public OMap getMap(String key) { 1173 return get(key, OMap.class); 1174 } 1175 1176 /** 1177 * Returns the specified entry value converted to a {@link OMap}. 1178 * 1179 * <p> 1180 * Shortcut for <code>getWithDefault(key, defVal, OMap.<jk>class</jk>)</code>. 1181 * 1182 * @param key The key. 1183 * @param defVal The default value if the map doesn't contain the specified mapping. 1184 * @return The converted value, or the default value if the map contains no mapping for this key. 1185 * @throws InvalidDataConversionException If value cannot be converted. 1186 */ 1187 public OMap getMap(String key, OMap defVal) { 1188 return getWithDefault(key, defVal, OMap.class); 1189 } 1190 1191 /** 1192 * Same as {@link #getMap(String)} but creates a new empty {@link OMap} if it doesn't already exist. 1193 * 1194 * @param key The key. 1195 * @param createIfNotExists If mapping doesn't already exist, create one with an empty {@link OMap}. 1196 * @return The converted value, or an empty value if the map contains no mapping for this key. 1197 * @throws InvalidDataConversionException If value cannot be converted. 1198 */ 1199 public OMap getMap(String key, boolean createIfNotExists) { 1200 OMap m = getWithDefault(key, null, OMap.class); 1201 if (m == null && createIfNotExists) { 1202 m = new OMap(); 1203 put(key, m); 1204 } 1205 return m; 1206 } 1207 1208 /** 1209 * Returns the specified entry value converted to a {@link OList}. 1210 * 1211 * <p> 1212 * Shortcut for <code>get(key, OList.<jk>class</jk>)</code>. 1213 * 1214 * @param key The key. 1215 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1216 * @throws InvalidDataConversionException If value cannot be converted. 1217 */ 1218 @Override 1219 public OList getList(String key) { 1220 return get(key, OList.class); 1221 } 1222 1223 /** 1224 * Returns the specified entry value converted to a {@link OList}. 1225 * 1226 * <p> 1227 * Shortcut for <code>getWithDefault(key, defVal, OList.<jk>class</jk>)</code>. 1228 * 1229 * @param key The key. 1230 * @param defVal The default value if the map doesn't contain the specified mapping. 1231 * @return The converted value, or the default value if the map contains no mapping for this key. 1232 * @throws InvalidDataConversionException If value cannot be converted. 1233 */ 1234 public OList getList(String key, OList defVal) { 1235 return getWithDefault(key, defVal, OList.class); 1236 } 1237 1238 /** 1239 * Same as {@link #getList(String)} but creates a new empty {@link OList} if it doesn't already exist. 1240 * 1241 * @param key The key. 1242 * @param createIfNotExists If mapping doesn't already exist, create one with an empty {@link OList}. 1243 * @return The converted value, or an empty value if the map contains no mapping for this key. 1244 * @throws InvalidDataConversionException If value cannot be converted. 1245 */ 1246 public OList getList(String key, boolean createIfNotExists) { 1247 OList m = getWithDefault(key, null, OList.class); 1248 if (m == null && createIfNotExists) { 1249 m = new OList(); 1250 put(key, m); 1251 } 1252 return m; 1253 } 1254 1255 /** 1256 * Returns the first entry that exists converted to a {@link String}. 1257 * 1258 * <p> 1259 * Shortcut for <code>find(String.<jk>class</jk>, keys)</code>. 1260 * 1261 * @param keys The list of keys to look for. 1262 * @return 1263 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1264 * contains no mapping for any of the keys. 1265 */ 1266 @Override 1267 public String findString(String... keys) { 1268 return find(String.class, keys); 1269 } 1270 1271 /** 1272 * Returns the first entry that exists converted to an {@link Integer}. 1273 * 1274 * <p> 1275 * Shortcut for <code>find(Integer.<jk>class</jk>, keys)</code>. 1276 * 1277 * @param keys The list of keys to look for. 1278 * @return 1279 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1280 * contains no mapping for any of the keys. 1281 * @throws InvalidDataConversionException If value cannot be converted. 1282 */ 1283 @Override 1284 public Integer findInt(String... keys) { 1285 return find(Integer.class, keys); 1286 } 1287 1288 /** 1289 * Returns the first entry that exists converted to a {@link Long}. 1290 * 1291 * <p> 1292 * Shortcut for <code>find(Long.<jk>class</jk>, keys)</code>. 1293 * 1294 * @param keys The list of keys to look for. 1295 * @return 1296 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1297 * contains no mapping for any of the keys. 1298 * @throws InvalidDataConversionException If value cannot be converted. 1299 */ 1300 @Override 1301 public Long findLong(String... keys) { 1302 return find(Long.class, keys); 1303 } 1304 1305 /** 1306 * Returns the first entry that exists converted to a {@link Boolean}. 1307 * 1308 * <p> 1309 * Shortcut for <code>find(Boolean.<jk>class</jk>, keys)</code>. 1310 * 1311 * @param keys The list of keys to look for. 1312 * @return 1313 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1314 * contains no mapping for any of the keys. 1315 * @throws InvalidDataConversionException If value cannot be converted. 1316 */ 1317 @Override 1318 public Boolean findBoolean(String... keys) { 1319 return find(Boolean.class, keys); 1320 } 1321 1322 /** 1323 * Returns the first entry that exists converted to a {@link OMap}. 1324 * 1325 * <p> 1326 * Shortcut for <code>find(OMap.<jk>class</jk>, keys)</code>. 1327 * 1328 * @param keys The list of keys to look for. 1329 * @return 1330 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1331 * contains no mapping for any of the keys. 1332 * @throws InvalidDataConversionException If value cannot be converted. 1333 */ 1334 @Override 1335 public OMap findMap(String... keys) { 1336 return find(OMap.class, keys); 1337 } 1338 1339 /** 1340 * Returns the first entry that exists converted to a {@link OList}. 1341 * 1342 * <p> 1343 * Shortcut for <code>find(OList.<jk>class</jk>, keys)</code>. 1344 * 1345 * @param keys The list of keys to look for. 1346 * @return 1347 * The converted value of the first key in the list that has an entry in this map, or <jk>null</jk> if the map 1348 * contains no mapping for any of the keys. 1349 * @throws InvalidDataConversionException If value cannot be converted. 1350 */ 1351 @Override 1352 public OList findList(String... keys) { 1353 return find(OList.class, keys); 1354 } 1355 1356 /** 1357 * Returns the first key in the map. 1358 * 1359 * @return The first key in the map, or <jk>null</jk> if the map is empty. 1360 */ 1361 @Override 1362 public String getFirstKey() { 1363 return isEmpty() ? null : keySet().iterator().next(); 1364 } 1365 1366 /** 1367 * Returns the class type of the object at the specified index. 1368 * 1369 * @param key The key into this map. 1370 * @return 1371 * The data type of the object at the specified key, or <jk>null</jk> if the value is null or does not exist. 1372 */ 1373 @Override 1374 public ClassMeta<?> getClassMeta(String key) { 1375 return bs().getClassMetaForObject(get(key)); 1376 } 1377 1378 /** 1379 * Equivalent to calling <c>get(class,key,def)</c> followed by <c>remove(key);</c> 1380 * @param key The key. 1381 * @param defVal The default value if the map doesn't contain the specified mapping. 1382 * @param type The class type. 1383 * 1384 * @param <T> The class type. 1385 * @return The converted value, or the default value if the map contains no mapping for this key. 1386 * @throws InvalidDataConversionException If value cannot be converted. 1387 */ 1388 @Override 1389 public <T> T removeWithDefault(String key, T defVal, Class<T> type) { 1390 T t = getWithDefault(key, defVal, type); 1391 remove(key); 1392 return t; 1393 } 1394 1395 /** 1396 * Equivalent to calling <code>removeWithDefault(key,<jk>null</jk>,String.<jk>class</jk>)</code>. 1397 * 1398 * @param key The key. 1399 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1400 * @throws InvalidDataConversionException If value cannot be converted. 1401 */ 1402 @Override 1403 public String removeString(String key) { 1404 return removeString(key, null); 1405 } 1406 1407 /** 1408 * Equivalent to calling <code>removeWithDefault(key,def,String.<jk>class</jk>)</code>. 1409 * 1410 * @param key The key. 1411 * @param def The default value if the map doesn't contain the specified mapping. 1412 * @return The converted value, or the default value if the map contains no mapping for this key. 1413 * @throws InvalidDataConversionException If value cannot be converted. 1414 */ 1415 @Override 1416 public String removeString(String key, String def) { 1417 return removeWithDefault(key, def, String.class); 1418 } 1419 1420 /** 1421 * Equivalent to calling <code>removeWithDefault(key,<jk>null</jk>,Integer.<jk>class</jk>)</code>. 1422 * 1423 * @param key The key. 1424 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1425 * @throws InvalidDataConversionException If value cannot be converted. 1426 */ 1427 @Override 1428 public Integer removeInt(String key) { 1429 return removeInt(key, null); 1430 } 1431 1432 /** 1433 * Equivalent to calling <code>removeWithDefault(key,def,Integer.<jk>class</jk>)</code>. 1434 * 1435 * @param key The key. 1436 * @param def The default value if the map doesn't contain the specified mapping. 1437 * @return The converted value, or the default value if the map contains no mapping for this key. 1438 * @throws InvalidDataConversionException If value cannot be converted. 1439 */ 1440 @Override 1441 public Integer removeInt(String key, Integer def) { 1442 return removeWithDefault(key, def, Integer.class); 1443 } 1444 1445 /** 1446 * Equivalent to calling <code>removeWithDefault(key,<jk>null</jk>,Boolean.<jk>class</jk>)</code>. 1447 * 1448 * @param key The key. 1449 * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. 1450 * @throws InvalidDataConversionException If value cannot be converted. 1451 */ 1452 @Override 1453 public Boolean removeBoolean(String key) { 1454 return removeBoolean(key, null); 1455 } 1456 1457 /** 1458 * Equivalent to calling <code>removeWithDefault(key,def,Boolean.<jk>class</jk>)</code>. 1459 * 1460 * @param key The key. 1461 * @param def The default value if the map doesn't contain the specified mapping. 1462 * @return The converted value, or the default value if the map contains no mapping for this key. 1463 * @throws InvalidDataConversionException If value cannot be converted. 1464 */ 1465 @Override 1466 public Boolean removeBoolean(String key, Boolean def) { 1467 return removeWithDefault(key, def, Boolean.class); 1468 } 1469 1470 /** 1471 * Convenience method for removing several keys at once. 1472 * 1473 * @param keys The list of keys to remove. 1474 */ 1475 @Override 1476 public void removeAll(Collection<String> keys) { 1477 for (String k : keys) 1478 remove(k); 1479 } 1480 1481 /** 1482 * Convenience method for removing several keys at once. 1483 * 1484 * @param keys The list of keys to remove. 1485 */ 1486 @Override 1487 public void removeAll(String... keys) { 1488 for (String k : keys) 1489 remove(k); 1490 } 1491 1492 /** 1493 * The opposite of {@link #removeAll(String...)}. 1494 * 1495 * <p> 1496 * Discards all keys from this map that aren't in the specified list. 1497 * 1498 * @param keys The keys to keep. 1499 * @return This map. 1500 */ 1501 @Override 1502 public OMap keepAll(String...keys) { 1503 for (Iterator<String> i = keySet().iterator(); i.hasNext();) { 1504 boolean remove = true; 1505 String key = i.next(); 1506 for (String k : keys) { 1507 if (k.equals(key)) { 1508 remove = false; 1509 break; 1510 } 1511 } 1512 if (remove) 1513 i.remove(); 1514 } 1515 return this; 1516 } 1517 1518 /** 1519 * Returns <jk>true</jk> if the map contains the specified entry and the value is not null nor an empty string. 1520 * 1521 * <p> 1522 * Always returns <jk>false</jk> if the value is not a {@link CharSequence}. 1523 * 1524 * @param key The key. 1525 * @return <jk>true</jk> if the map contains the specified entry and the value is not null nor an empty string. 1526 */ 1527 @Override 1528 public boolean containsKeyNotEmpty(String key) { 1529 Object val = get(key); 1530 if (val == null) 1531 return false; 1532 if (val instanceof CharSequence) 1533 return ! StringUtils.isEmpty(val); 1534 return false; 1535 } 1536 1537 /** 1538 * Returns <jk>true</jk> if this map contains the specified key, ignoring the inner map if it exists. 1539 * 1540 * @param key The key to look up. 1541 * @return <jk>true</jk> if this map contains the specified key. 1542 */ 1543 @Override 1544 public boolean containsOuterKey(Object key) { 1545 return super.containsKey(key); 1546 } 1547 1548 /** 1549 * Returns a copy of this <c>OMap</c> with only the specified keys. 1550 * 1551 * @param keys The keys of the entries to copy. 1552 * @return A new map with just the keys and values from this map. 1553 */ 1554 @Override 1555 public OMap include(String...keys) { 1556 OMap m2 = new OMap(); 1557 for (Map.Entry<String,Object> e : this.entrySet()) 1558 for (String k : keys) 1559 if (k.equals(e.getKey())) 1560 m2.put(k, e.getValue()); 1561 return m2; 1562 } 1563 1564 /** 1565 * Returns a copy of this <c>OMap</c> without the specified keys. 1566 * 1567 * @param keys The keys of the entries not to copy. 1568 * @return A new map without the keys and values from this map. 1569 */ 1570 @Override 1571 public OMap exclude(String...keys) { 1572 OMap m2 = new OMap(); 1573 for (Map.Entry<String,Object> e : this.entrySet()) { 1574 boolean exclude = false; 1575 for (String k : keys) 1576 if (k.equals(e.getKey())) 1577 exclude = true; 1578 if (! exclude) 1579 m2.put(e.getKey(), e.getValue()); 1580 } 1581 return m2; 1582 } 1583 1584 /** 1585 * Converts this map into an object of the specified type. 1586 * 1587 * <p> 1588 * If this map contains a <js>"_type"</js> entry, it must be the same as or a subclass of the <c>type</c>. 1589 * 1590 * @param <T> The class type to convert this map object to. 1591 * @param type The class type to convert this map object to. 1592 * @return The new object. 1593 * @throws ClassCastException 1594 * If the <js>"_type"</js> entry is present and not assignable from <c>type</c> 1595 */ 1596 @Override 1597 @SuppressWarnings("unchecked") 1598 public <T> T cast(Class<T> type) { 1599 BeanSession bs = bs(); 1600 ClassMeta<?> c2 = bs.getClassMeta(type); 1601 String typePropertyName = bs.getBeanTypePropertyName(c2); 1602 ClassMeta<?> c1 = bs.getBeanRegistry().getClassMeta((String)get(typePropertyName)); 1603 ClassMeta<?> c = c1 == null ? c2 : narrowClassMeta(c1, c2); 1604 if (c.isObject()) 1605 return (T)this; 1606 return (T)cast2(c); 1607 } 1608 1609 /** 1610 * Same as {@link #cast(Class)}, except allows you to specify a {@link ClassMeta} parameter. 1611 * 1612 * @param <T> The class type to convert this map object to. 1613 * @param cm The class type to convert this map object to. 1614 * @return The new object. 1615 * @throws ClassCastException 1616 * If the <js>"_type"</js> entry is present and not assignable from <c>type</c> 1617 */ 1618 @Override 1619 @SuppressWarnings({"unchecked"}) 1620 public <T> T cast(ClassMeta<T> cm) { 1621 BeanSession bs = bs(); 1622 ClassMeta<?> c1 = bs.getBeanRegistry().getClassMeta((String)get(bs.getBeanTypePropertyName(cm))); 1623 ClassMeta<?> c = narrowClassMeta(c1, cm); 1624 return (T)cast2(c); 1625 } 1626 1627 //------------------------------------------------------------------------------------------------------------------ 1628 // POJO REST methods. 1629 //------------------------------------------------------------------------------------------------------------------ 1630 1631 /** 1632 * Same as {@link #get(String,Class) get(String,Class)}, but the key is a slash-delimited path used to traverse 1633 * entries in this POJO. 1634 * 1635 * <p> 1636 * For example, the following code is equivalent: 1637 * </p> 1638 * <p class='bcode w800'> 1639 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 1640 * 1641 * <jc>// Long way</jc> 1642 * <jk>long</jk> l = m.getMap(<js>"foo"</js>).getList(<js>"bar"</js>).getMap(<js>"0"</js>).getLong(<js>"baz"</js>); 1643 * 1644 * <jc>// Using this method</jc> 1645 * <jk>long</jk> l = m.getAt(<js>"foo/bar/0/baz"</js>, <jk>long</jk>.<jk>class</jk>); 1646 * </p> 1647 * 1648 * <p> 1649 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 1650 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 1651 * 1652 * @param path The path to the entry. 1653 * @param type The class type. 1654 * 1655 * @param <T> The class type. 1656 * @return The value, or <jk>null</jk> if the entry doesn't exist. 1657 */ 1658 @Override 1659 public <T> T getAt(String path, Class<T> type) { 1660 return getPojoRest().get(path, type); 1661 } 1662 1663 /** 1664 * Same as {@link #getAt(String,Class)}, but allows for conversion to complex maps and collections. 1665 * 1666 * <p> 1667 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 1668 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 1669 * 1670 * @param path The path to the entry. 1671 * @param type The class type. 1672 * @param args The class parameter types. 1673 * 1674 * @param <T> The class type. 1675 * @return The value, or <jk>null</jk> if the entry doesn't exist. 1676 */ 1677 @Override 1678 public <T> T getAt(String path, Type type, Type...args) { 1679 return getPojoRest().get(path, type, args); 1680 } 1681 1682 /** 1683 * Same as <c>put(String,Object)</c>, but the key is a slash-delimited path used to traverse entries in this 1684 * POJO. 1685 * 1686 * <p> 1687 * For example, the following code is equivalent: 1688 * </p> 1689 * <p class='bcode w800'> 1690 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 1691 * 1692 * <jc>// Long way</jc> 1693 * m.getMap(<js>"foo"</js>).getList(<js>"bar"</js>).getMap(<js>"0"</js>).put(<js>"baz"</js>, 123); 1694 * 1695 * <jc>// Using this method</jc> 1696 * m.putAt(<js>"foo/bar/0/baz"</js>, 123); 1697 * </p> 1698 * 1699 * <p> 1700 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 1701 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 1702 * 1703 * @param path The path to the entry. 1704 * @param o The new value. 1705 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 1706 */ 1707 @Override 1708 public Object putAt(String path, Object o) { 1709 return getPojoRest().put(path, o); 1710 } 1711 1712 /** 1713 * Similar to {@link #putAt(String,Object) putAt(String,Object)}, but used to append to collections and arrays. 1714 * 1715 * <p> 1716 * For example, the following code is equivalent: 1717 * </p> 1718 * <p class='bcode w800'> 1719 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 1720 * 1721 * <jc>// Long way</jc> 1722 * m.getMap(<js>"foo"</js>).getList(<js>"bar"</js>).append(123); 1723 * 1724 * <jc>// Using this method</jc> 1725 * m.postAt(<js>"foo/bar"</js>, 123); 1726 * </p> 1727 * 1728 * <p> 1729 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 1730 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 1731 * 1732 * @param path The path to the entry. 1733 * @param o The new value. 1734 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 1735 */ 1736 @Override 1737 public Object postAt(String path, Object o) { 1738 return getPojoRest().post(path, o); 1739 } 1740 1741 /** 1742 * Similar to {@link #remove(Object) remove(Object)}, but the key is a slash-delimited path used to traverse entries 1743 * in this POJO. 1744 * 1745 * <p> 1746 * For example, the following code is equivalent: 1747 * </p> 1748 * <p class='bcode w800'> 1749 * OMap m = OMap.<jsm>ofJson</jsm>(<js>"..."</js>); 1750 * 1751 * <jc>// Long way</jc> 1752 * m.getMap(<js>"foo"</js>).getList(<js>"bar"</js>).getMap(0).remove(<js>"baz"</js>); 1753 * 1754 * <jc>// Using this method</jc> 1755 * m.deleteAt(<js>"foo/bar/0/baz"</js>); 1756 * </p> 1757 * 1758 * <p> 1759 * This method uses the {@link PojoRest} class to perform the lookup, so the map can contain any of the various 1760 * class types that the {@link PojoRest} class supports (e.g. beans, collections, arrays). 1761 * 1762 * @param path The path to the entry. 1763 * @return The previous value, or <jk>null</jk> if the entry doesn't exist. 1764 */ 1765 @Override 1766 public Object deleteAt(String path) { 1767 return getPojoRest().delete(path); 1768 } 1769 1770 //------------------------------------------------------------------------------------------------------------------ 1771 // Other methods. 1772 //------------------------------------------------------------------------------------------------------------------ 1773 1774 /** 1775 * Returns the {@link BeanSession} currently associated with this map. 1776 * 1777 * @return The {@link BeanSession} currently associated with this map. 1778 */ 1779 @Override 1780 public BeanSession getBeanSession() { 1781 return session; 1782 } 1783 1784 /** 1785 * Convenience method for inserting JSON directly into an attribute on this object. 1786 * 1787 * <p> 1788 * The JSON text can be an object (i.e. <js>"{...}"</js>) or an array (i.e. <js>"[...]"</js>). 1789 * 1790 * @param key The key. 1791 * @param json The JSON text that will be parsed into an Object and then inserted into this map. 1792 * @throws ParseException Malformed input encountered. 1793 */ 1794 @Override 1795 public void putJson(String key, String json) throws ParseException { 1796 this.put(key, JsonParser.DEFAULT.parse(json, Object.class)); 1797 } 1798 1799 /** 1800 * Serialize this object into a string using the specified serializer. 1801 * 1802 * @param serializer The serializer to use to convert this object to a string. 1803 * @return This object serialized as a string. 1804 */ 1805 public String asString(WriterSerializer serializer) { 1806 return serializer.toString(this); 1807 } 1808 1809 /** 1810 * Serialize this object to Simplified JSON. 1811 * 1812 * @return This object serialized as a string. 1813 */ 1814 public String asString() { 1815 return SimpleJsonSerializer.DEFAULT.toString(this); 1816 } 1817 1818 /** 1819 * Convenience method for serializing this map to the specified <c>Writer</c> using the 1820 * {@link JsonSerializer#DEFAULT} serializer. 1821 * 1822 * @param w The writer to serialize this object to. 1823 * @return This object (for method chaining). 1824 * @throws IOException If a problem occurred trying to write to the writer. 1825 * @throws SerializeException If a problem occurred trying to convert the output. 1826 */ 1827 public OMap writeTo(Writer w) throws IOException, SerializeException { 1828 JsonSerializer.DEFAULT.serialize(this, w); 1829 return this; 1830 } 1831 1832 /** 1833 * Returns <jk>true</jk> if this map is unmodifiable. 1834 * 1835 * @return <jk>true</jk> if this map is unmodifiable. 1836 */ 1837 @Override 1838 public boolean isUnmodifiable() { 1839 return false; 1840 } 1841 1842 /** 1843 * Returns a modifiable copy of this map if it's unmodifiable. 1844 * 1845 * @return A modifiable copy of this map if it's unmodifiable, or this map if it is already modifiable. 1846 */ 1847 @Override 1848 public OMap modifiable() { 1849 if (isUnmodifiable()) 1850 return new OMap(this); 1851 return this; 1852 } 1853 1854 /** 1855 * Returns an unmodifiable copy of this map if it's modifiable. 1856 * 1857 * @return An unmodifiable copy of this map if it's modifiable, or this map if it is already unmodifiable. 1858 */ 1859 @Override 1860 public OMap unmodifiable() { 1861 if (this instanceof UnmodifiableOMap) 1862 return this; 1863 return new UnmodifiableOMap(this); 1864 } 1865 1866 //------------------------------------------------------------------------------------------------------------------ 1867 // Utility methods. 1868 //------------------------------------------------------------------------------------------------------------------ 1869 1870 private BeanSession bs() { 1871 if (session == null) 1872 session = BeanContext.DEFAULT.createBeanSession(); 1873 return session; 1874 } 1875 1876 private PojoRest getPojoRest() { 1877 if (pojoRest == null) 1878 pojoRest = new PojoRest(this); 1879 return pojoRest; 1880 } 1881 1882 /* 1883 * Combines the class specified by a "_type" attribute with the ClassMeta 1884 * passed in through the cast(ClassMeta) method. 1885 * The rule is that child classes supersede parent classes, and c2 supersedes c1 1886 * if one isn't the parent of another. 1887 */ 1888 private ClassMeta<?> narrowClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) { 1889 if (c1 == null) 1890 return c2; 1891 ClassMeta<?> c = getNarrowedClassMeta(c1, c2); 1892 if (c1.isMap()) { 1893 ClassMeta<?> k = getNarrowedClassMeta(c1.getKeyType(), c2.getKeyType()); 1894 ClassMeta<?> v = getNarrowedClassMeta(c1.getValueType(), c2.getValueType()); 1895 return bs().getClassMeta(c.getInnerClass(), k, v); 1896 } 1897 if (c1.isCollection()) { 1898 ClassMeta<?> e = getNarrowedClassMeta(c1.getElementType(), c2.getElementType()); 1899 return bs().getClassMeta(c.getInnerClass(), e); 1900 } 1901 return c; 1902 } 1903 1904 /* 1905 * If c1 is a child of c2 or the same as c2, returns c1. 1906 * Otherwise, returns c2. 1907 */ 1908 private static ClassMeta<?> getNarrowedClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) { 1909 if (c2 == null || c2.getInfo().isParentOf(c1.getInnerClass())) 1910 return c1; 1911 return c2; 1912 } 1913 1914 /* 1915 * Converts this map to the specified class type. 1916 */ 1917 @SuppressWarnings({"unchecked","rawtypes"}) 1918 private <T> T cast2(ClassMeta<T> cm) { 1919 1920 BeanSession bs = bs(); 1921 try { 1922 Object value = get("value"); 1923 1924 if (cm.isMap()) { 1925 Map m2 = (cm.canCreateNewInstance() ? (Map)cm.newInstance() : new OMap(bs)); 1926 ClassMeta<?> kType = cm.getKeyType(), vType = cm.getValueType(); 1927 for (Map.Entry<String,Object> e : entrySet()) { 1928 Object k = e.getKey(); 1929 Object v = e.getValue(); 1930 if (! k.equals(bs.getBeanTypePropertyName(cm))) { 1931 1932 // Attempt to recursively cast child maps. 1933 if (v instanceof OMap) 1934 v = ((OMap)v).cast(vType); 1935 1936 k = (kType.isString() ? k : bs.convertToType(k, kType)); 1937 v = (vType.isObject() ? v : bs.convertToType(v, vType)); 1938 1939 m2.put(k, v); 1940 } 1941 } 1942 return (T)m2; 1943 1944 } else if (cm.isBean()) { 1945 BeanMap<? extends T> bm = bs.newBeanMap(cm.getInnerClass()); 1946 1947 // Iterate through all the entries in the map and set the individual field values. 1948 for (Map.Entry<String,Object> e : entrySet()) { 1949 String k = e.getKey(); 1950 Object v = e.getValue(); 1951 if (! k.equals(bs.getBeanTypePropertyName(cm))) { 1952 1953 // Attempt to recursively cast child maps. 1954 if (v instanceof OMap) 1955 v = ((OMap)v).cast(bm.getProperty(k).getMeta().getClassMeta()); 1956 1957 bm.put(k, v); 1958 } 1959 } 1960 1961 return bm.getBean(); 1962 1963 } else if (cm.isCollectionOrArray()) { 1964 List items = (List)get("items"); 1965 return bs.convertToType(items, cm); 1966 1967 } else if (value != null) { 1968 return bs.convertToType(value, cm); 1969 } 1970 1971 } catch (Exception e) { 1972 throw new BeanRuntimeException(e, cm.getInnerClass(), 1973 "Error occurred attempting to cast to an object of type ''{0}''", cm.getInnerClass().getName()); 1974 } 1975 1976 throw new BeanRuntimeException(cm.getInnerClass(), 1977 "Cannot convert to class type ''{0}''. Only beans and maps can be converted using this method.", 1978 cm.getInnerClass().getName()); 1979 } 1980 1981 private void parse(Reader r, Parser p) throws ParseException { 1982 if (p == null) 1983 p = JsonParser.DEFAULT; 1984 p.parseIntoMap(r, this, bs().string(), bs().object()); 1985 } 1986 1987 private static final class UnmodifiableOMap extends OMap { 1988 private static final long serialVersionUID = 1L; 1989 1990 UnmodifiableOMap(OMap contents) { 1991 super(); 1992 if (contents != null) { 1993 for (Map.Entry<String,Object> e : contents.entrySet()) { 1994 super.put(e.getKey(), e.getValue()); 1995 } 1996 } 1997 } 1998 1999 @Override 2000 public final Object put(String key, Object val) { 2001 throw new UnsupportedOperationException("OMap is read-only."); 2002 } 2003 2004 @Override 2005 public final Object remove(Object key) { 2006 throw new UnsupportedOperationException("OMap is read-only."); 2007 } 2008 2009 @Override 2010 public final boolean isUnmodifiable() { 2011 return true; 2012 } 2013 } 2014 2015 //------------------------------------------------------------------------------------------------------------------ 2016 // Overridden methods. 2017 //------------------------------------------------------------------------------------------------------------------ 2018 2019 @Override /* Map */ 2020 public Object get(Object key) { 2021 Object o = super.get(key); 2022 if (o == null && inner != null) 2023 o = inner.get(key); 2024 return o; 2025 } 2026 2027 @Override /* Map */ 2028 public boolean containsKey(Object key) { 2029 if (super.containsKey(key)) 2030 return true; 2031 if (inner != null) 2032 return inner.containsKey(key); 2033 return false; 2034 } 2035 2036 @Override /* Map */ 2037 public Set<String> keySet() { 2038 if (inner == null) 2039 return super.keySet(); 2040 LinkedHashSet<String> s = new LinkedHashSet<>(); 2041 s.addAll(inner.keySet()); 2042 s.addAll(super.keySet()); 2043 return s; 2044 } 2045 2046 @Override /* Map */ 2047 public Set<Map.Entry<String,Object>> entrySet() { 2048 if (inner == null) 2049 return super.entrySet(); 2050 2051 final Set<String> keySet = keySet(); 2052 final Iterator<String> keys = keySet.iterator(); 2053 2054 return new AbstractSet<Map.Entry<String,Object>>() { 2055 2056 @Override /* Iterable */ 2057 public Iterator<Map.Entry<String,Object>> iterator() { 2058 2059 return new Iterator<Map.Entry<String,Object>>() { 2060 2061 @Override /* Iterator */ 2062 public boolean hasNext() { 2063 return keys.hasNext(); 2064 } 2065 2066 @Override /* Iterator */ 2067 public Map.Entry<String,Object> next() { 2068 return new Map.Entry<String,Object>() { 2069 String key = keys.next(); 2070 2071 @Override /* Map.Entry */ 2072 public String getKey() { 2073 return key; 2074 } 2075 2076 @Override /* Map.Entry */ 2077 public Object getValue() { 2078 return get(key); 2079 } 2080 2081 @Override /* Map.Entry */ 2082 public Object setValue(Object object) { 2083 return put(key, object); 2084 } 2085 }; 2086 } 2087 2088 @Override /* Iterator */ 2089 public void remove() { 2090 throw new UnsupportedOperationException(); 2091 } 2092 }; 2093 } 2094 2095 @Override /* Set */ 2096 public int size() { 2097 return keySet.size(); 2098 } 2099 }; 2100 } 2101 2102 @Override /* Object */ 2103 public String toString() { 2104 return SimpleJson.DEFAULT.toString(this); 2105 } 2106}