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