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