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