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>"&lt;object&gt;&lt;a type='string'&gt;A&lt;/a&gt;&lt;b type='object'&gt;&lt;c type='string'&gt;C&lt;/c&gt;&lt;d type='number'&gt;123&lt;/d&gt;&lt;/b&gt;&lt;/object&gt;"</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'&amp;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&lt;String&gt; 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&lt;MyBean&gt; 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&lt;List&lt;String&gt;&gt; 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&lt;String,String&gt; 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&lt;String,List&lt;MyBean&gt;&gt; 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}