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.commons.collections;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.ThrowableUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022
023import java.lang.reflect.*;
024import java.util.*;
025import java.util.concurrent.*;
026import java.util.function.*;
027
028/**
029 * A fluent builder for constructing {@link Map} instances with various configuration options.
030 *
031 * <p>
032 * This builder provides a flexible and type-safe way to construct maps with support for adding entries,
033 * other maps, sorting by keys, and applying modifiers like unmodifiable or sparse modes. It's particularly
034 * useful when constructing maps dynamically from multiple sources or with conditional entries.
035 *
036 * <p>
037 * Instances of this builder can be created using {@link #create(Class, Class)} or the convenience method
038 * {@link org.apache.juneau.commons.utils.CollectionUtils#mapb(Class, Class)}.
039 *
040 * <h5 class='section'>Features:</h5>
041 * <ul class='spaced-list'>
042 *    <li>Fluent API - all methods return <c>this</c> for method chaining
043 *    <li>Multiple add methods - single entries, pairs, other maps
044 *    <li>Arbitrary input support - automatic type conversion with {@link #addAny(Object...)}
045 *    <li>Pair adding - {@link #addPairs(Object...)} for varargs key-value pairs
046 *    <li>Filtering support - exclude unwanted entries via {@link #filtered()} or {@link #filtered(BiPredicate)}
047 *    <li>Sorting support - natural key order or custom {@link Comparator}
048 *    <li>Sparse mode - return <jk>null</jk> for empty maps
049 *    <li>Unmodifiable mode - create immutable maps
050 *    <li>Custom conversion functions - type conversion via {@link #keyFunction(Function)} and {@link #valueFunction(Function)}
051 * </ul>
052 *
053 * <h5 class='section'>Examples:</h5>
054 * <p class='bjava'>
055 *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
056 *
057 *    <jc>// Basic usage - returns Map</jc>
058 *    Map&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
059 *       .add(<js>"one"</js>, 1)
060 *       .add(<js>"two"</js>, 2)
061 *       .add(<js>"three"</js>, 3)
062 *       .build();
063 *
064 *    <jc>// Using pairs - returns Map</jc>
065 *    Map&lt;String,String&gt; <jv>props</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, String.<jk>class</jk>)
066 *       .addPairs(<js>"host"</js>, <js>"localhost"</js>, <js>"port"</js>, <js>"8080"</js>)
067 *       .build();
068 *
069 *    <jc>// With sorting by key - returns Map</jc>
070 *    Map&lt;String,Integer&gt; <jv>sorted</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
071 *       .add(<js>"zebra"</js>, 3)
072 *       .add(<js>"apple"</js>, 1)
073 *       .add(<js>"banana"</js>, 2)
074 *       .sorted()
075 *       .build();  <jc>// Returns TreeMap with natural key order</jc>
076 *
077 *    <jc>// Immutable map - returns Map</jc>
078 *    Map&lt;String,String&gt; <jv>config</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, String.<jk>class</jk>)
079 *       .add(<js>"env"</js>, <js>"prod"</js>)
080 *       .add(<js>"region"</js>, <js>"us-west"</js>)
081 *       .unmodifiable()
082 *       .build();
083 *
084 *    <jc>// From multiple sources - returns Map</jc>
085 *    Map&lt;String,Integer&gt; <jv>existing</jv> = Map.of(<js>"a"</js>, 1, <js>"b"</js>, 2);
086 *    Map&lt;String,Integer&gt; <jv>combined</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
087 *       .addAll(<jv>existing</jv>)
088 *       .add(<js>"c"</js>, 3)
089 *       .build();
090 *
091 *    <jc>// Sparse mode - returns null when empty</jc>
092 *    Map&lt;String,String&gt; <jv>maybeNull</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, String.<jk>class</jk>)
093 *       .sparse()
094 *       .build();  <jc>// Returns null, not empty map</jc>
095 *
096 *    <jc>// FluentMap wrapper - use buildFluent()</jc>
097 *    FluentMap&lt;String,Integer&gt; <jv>fluent</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
098 *       .add(<js>"one"</js>, 1)
099 *       .buildFluent();
100 *
101 *    <jc>// FilteredMap - use buildFiltered()</jc>
102 *    FilteredMap&lt;String,Integer&gt; <jv>filtered</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
103 *       .filtered((k, v) -&gt; v &gt; 0)
104 *       .add(<js>"a"</js>, 5)
105 *       .add(<js>"b"</js>, -1)  <jc>// Filtered out</jc>
106 *       .buildFiltered();
107 * </p>
108 *
109 * <h5 class='section'>Thread Safety:</h5>
110 * <p>
111 * This class is <b>not thread-safe</b>. Each builder instance should be used by a single thread or
112 * properly synchronized.
113 *
114 * <h5 class='section'>See Also:</h5><ul>
115 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsCollections">Collections Package</a>
116 *    <li class='jc'>{@link Lists}
117 *    <li class='jc'>{@link Sets}
118 * </ul>
119 *
120 * @param <K> The key type.
121 * @param <V> The value type.
122 */
123public class Maps<K,V> {
124
125   /**
126    * Static creator.
127    *
128    * @param <K> Key type.
129    * @param <V> Value type.
130    * @param keyType The key type. Must not be <jk>null</jk>.
131    * @param valueType The value type. Must not be <jk>null</jk>.
132    * @return A new builder.
133    */
134   public static <K,V> Maps<K,V> create(Class<K> keyType, Class<V> valueType) {
135      return new Maps<>(assertArgNotNull("keyType", keyType), assertArgNotNull("valueType", valueType));
136   }
137
138   /**
139    * Static creator without explicit type parameters.
140    *
141    * <p>
142    * This is a convenience method that creates a builder without requiring explicit type parameters.
143    * The types will be inferred from usage context. Internally uses <c>Object.class</c> for both
144    * key and value types, which allows any types to be added.
145    *
146    * <h5 class='section'>Example:</h5>
147    * <p class='bjava'>
148    *    Map&lt;String, Integer&gt; <jv>map</jv> = Maps.<jsm>create</jsm>()
149    *       .add(<js>"one"</js>, 1)
150    *       .add(<js>"two"</js>, 2)
151    *       .build();
152    * </p>
153    *
154    * @param <K> Key type.
155    * @param <V> Value type.
156    * @return A new builder.
157    */
158   @SuppressWarnings({ "unchecked", "rawtypes" })
159   public static <K,V> Maps<K,V> create() {
160      return new Maps(Object.class, Object.class);
161   }
162
163   private Map<K,V> map;
164   private boolean unmodifiable = false, sparse = false, concurrent = false, ordered = false;
165   private Comparator<K> comparator;
166
167   private BiPredicate<K,V> filter;
168   private Class<K> keyType;
169   private Class<V> valueType;
170
171   private Function<Object,K> keyFunction;
172   private Function<Object,V> valueFunction;
173
174   /**
175    * Constructor.
176    *
177    * @param keyType The key type. Must not be <jk>null</jk>.
178    * @param valueType The value type. Must not be <jk>null</jk>.
179    */
180   public Maps(Class<K> keyType, Class<V> valueType) {
181      this.keyType = assertArgNotNull("keyType", keyType);
182      this.valueType = assertArgNotNull("valueType", valueType);
183   }
184
185   /**
186    * Adds a single entry to this map.
187    *
188    * <p>
189    * Note: Filtering is applied at build time, not when adding entries.
190    *
191    * @param key The map key.
192    * @param value The map value.
193    * @return This object.
194    */
195   public Maps<K,V> add(K key, V value) {
196      if (map == null)
197         map = new LinkedHashMap<>();
198      map.put(key, value);
199      return this;
200   }
201
202   /**
203    * Appends the contents of the specified map into this map.
204    *
205    * <p>
206    * This is a no-op if the value is <jk>null</jk>.
207    *
208    * <p>
209    * Note: Filtering is applied at build time, not when adding entries.
210    *
211    * @param value The map to add to this map.
212    * @return This object.
213    */
214   public Maps<K,V> addAll(Map<K,V> value) {
215      if (nn(value)) {
216         if (map == null)
217            map = new LinkedHashMap<>();
218         map.putAll(value);
219      }
220      return this;
221   }
222
223   /**
224    * Adds arbitrary values to this map.
225    *
226    * <p>
227    * Objects can be any of the following:
228    * <ul>
229    *    <li>Maps of key/value types convertible to the key/value types of this map.
230    * </ul>
231    *
232    * <p>
233    * Each entry from the maps will be added using {@link #add(Object, Object)}, which applies
234    * key/value function conversion if configured. Non-Map objects will cause a {@link RuntimeException} to be thrown.
235    *
236    * @param values The values to add. Can contain <jk>null</jk> values (ignored).
237    * @return This object.
238    * @throws RuntimeException If a non-Map object is provided.
239    */
240   @SuppressWarnings("unchecked")
241   public Maps<K,V> addAny(Object...values) {
242      for (var o : values) {
243         if (nn(o)) {
244            if (o instanceof Map o2) {
245               o2.forEach((k, v) -> {
246                  K key = convertKey(k);
247                  V value = convertValue(v);
248                  add(key, value);
249               });
250            } else {
251               throw rex("Object of type {0} could not be converted to type {1}", cn(o), "Map");
252            }
253         }
254      }
255      return this;
256   }
257
258   /**
259    * Adds a list of key/value pairs to this map.
260    *
261    * @param pairs The pairs to add.
262    * @return This object.
263    */
264   @SuppressWarnings("unchecked")
265   public Maps<K,V> addPairs(Object...pairs) {
266      assertArgNotNull("pairs", pairs);
267      if (pairs.length % 2 != 0)
268         throw illegalArg("Odd number of parameters passed into Maps.addPairs(...)");
269      for (var i = 0; i < pairs.length; i += 2)
270         add((K)pairs[i], (V)pairs[i + 1]);
271      return this;
272   }
273
274   /**
275    * Builds the map.
276    *
277    * <p>
278    * Applies filtering, sorting, ordering, concurrent, unmodifiable, and sparse options.
279    *
280    * <p>
281    * Map type selection:
282    * <ul>
283    *    <li>If {@link #sorted()} is set: Uses {@link TreeMap} (or {@link java.util.concurrent.ConcurrentSkipListMap} if concurrent)
284    *    <li>If {@link #ordered()} is set: Uses {@link LinkedHashMap} (or synchronized LinkedHashMap if concurrent)
285    *    <li>Otherwise: Uses {@link HashMap} (or {@link java.util.concurrent.ConcurrentHashMap} if concurrent)
286    * </ul>
287    *
288    * <p>
289    * If filtering is applied, the result is wrapped in a {@link FilteredMap}.
290    *
291    * @return The built map, or {@code null} if {@link #sparse()} is set and the map is empty.
292    */
293   public Map<K,V> build() {
294
295      if (sparse && e(map))
296         return null;
297
298      var map2 = (Map<K,V>)null;
299
300      if (ordered) {
301         map2 = new LinkedHashMap<>();
302         if (concurrent)
303            map2 = Collections.synchronizedMap(map2);
304      } else if (nn(comparator)) {
305         map2 = concurrent ? new ConcurrentSkipListMap<>(comparator) : new TreeMap<>(comparator);
306      } else {
307         map2 = concurrent ? new ConcurrentHashMap<>() : new HashMap<>();
308      }
309
310      if (nn(filter) || nn(keyFunction) || nn(valueFunction)) {
311         var map3b = FilteredMap.create(keyType, valueType);
312         if (nn(filter))
313            map3b.filter(filter);
314         if (nn(keyFunction))
315            map3b.keyFunction(keyFunction);
316         if (nn(valueFunction))
317            map3b.valueFunction(valueFunction);
318         map2 = map3b.inner(map2).build();
319      }
320
321      if (nn(map))
322         map2.putAll(map);
323
324      if (unmodifiable)
325         map2 = Collections.unmodifiableMap(map2);
326
327      return map2;
328   }
329
330   /**
331    * Builds the map and wraps it in a {@link FluentMap}.
332    *
333    * <p>
334    * This is a convenience method that calls {@link #build()} and wraps the result in a {@link FluentMap}.
335    *
336    * <h5 class='section'>Example:</h5>
337    * <p class='bjava'>
338    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
339    *
340    *    FluentMap&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
341    *       .add(<js>"one"</js>, 1)
342    *       .add(<js>"two"</js>, 2)
343    *       .buildFluent();
344    * </p>
345    *
346    * @return The built map wrapped in a {@link FluentMap}, or {@code null} if {@link #sparse()} is set and the map is empty.
347    */
348   public FluentMap<K,V> buildFluent() {
349      Map<K,V> result = build();
350      return result == null ? null : new FluentMap<>(result);
351   }
352
353   /**
354    * Builds the map as a {@link FilteredMap}.
355    *
356    * <p>
357    * Map type selection:
358    * <ul>
359    *    <li>If {@link #sorted()} is set: Uses {@link TreeMap} (or {@link java.util.concurrent.ConcurrentSkipListMap} if concurrent)
360    *    <li>If {@link #ordered()} is set: Uses {@link LinkedHashMap} (or synchronized LinkedHashMap if concurrent)
361    *    <li>Otherwise: Uses {@link HashMap} (or {@link java.util.concurrent.ConcurrentHashMap} if concurrent)
362    * </ul>
363    *
364    * <h5 class='section'>Example:</h5>
365    * <p class='bjava'>
366    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
367    *
368    *    FilteredMap&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
369    *       .filtered((k, v) -&gt; v != <jk>null</jk> &amp;&amp; v &gt; 0)
370    *       .add(<js>"a"</js>, 5)
371    *       .add(<js>"b"</js>, -1)  <jc>// Will be filtered out</jc>
372    *       .buildFiltered();
373    * </p>
374    *
375    * <p>
376    * Note: If {@link #unmodifiable()} is set, the returned map will be wrapped in an unmodifiable view,
377    * which may cause issues if the FilteredMap tries to modify it internally. It's recommended to avoid
378    * using {@link #unmodifiable()} when calling this method.
379    *
380    * @return The built map as a {@link FilteredMap}, or {@code null} if {@link #sparse()} is set and the map is empty.
381    */
382   public FilteredMap<K,V> buildFiltered() {
383      var m = build();
384      if (m == null)  // sparse mode and empty
385         return null;
386      if (m instanceof FilteredMap<K,V> m2)
387         return m2;
388      // Note that if unmodifiable is true, 'm' will be unmodifiable and will cause an error if you try
389      // to insert a value from within FilteredMap.
390      return FilteredMap.create(keyType, valueType).inner(m).build();
391   }
392
393   /**
394    * Sets the key conversion function for converting keys in {@link #addAny(Object...)}.
395    *
396    * <p>
397    * The function is applied to each key when adding entries from maps in {@link #addAny(Object...)}.
398    *
399    * @param keyFunction The function to convert keys. Must not be <jk>null</jk>.
400    * @return This object.
401    */
402   public Maps<K,V> keyFunction(Function<Object,K> keyFunction) {
403      this.keyFunction = assertArgNotNull("keyFunction", keyFunction);
404      return this;
405   }
406
407   /**
408    * Sets the value conversion function for converting values in {@link #addAny(Object...)}.
409    *
410    * <p>
411    * The function is applied to each value when adding entries from maps in {@link #addAny(Object...)}.
412    *
413    * @param valueFunction The function to convert values. Must not be <jk>null</jk>.
414    * @return This object.
415    */
416   public Maps<K,V> valueFunction(Function<Object,V> valueFunction) {
417      this.valueFunction = assertArgNotNull("valueFunction", valueFunction);
418      return this;
419   }
420
421   /**
422    * Sets both key and value conversion functions.
423    *
424    * <p>
425    * Convenience method for setting both functions at once.
426    *
427    * @param keyFunction The function to convert keys. Must not be <jk>null</jk>.
428    * @param valueFunction The function to convert values. Must not be <jk>null</jk>.
429    * @return This object.
430    */
431   public Maps<K,V> functions(Function<Object,K> keyFunction, Function<Object,V> valueFunction) {
432      this.keyFunction = assertArgNotNull("keyFunction", keyFunction);
433      this.valueFunction = assertArgNotNull("valueFunction", valueFunction);
434      return this;
435   }
436
437   /**
438    * Applies a default filter that excludes common "empty" or "unset" values from being added to the map.
439    *
440    * <p>
441    * The following values are filtered out:
442    * <ul>
443    *    <li>{@code null}
444    *    <li>{@link Boolean#FALSE}
445    *    <li>Numbers with {@code intValue() == -1}
446    *    <li>Empty arrays
447    *    <li>Empty {@link Map Maps}
448    *    <li>Empty {@link Collection Collections}
449    * </ul>
450    *
451    * <h5 class='section'>Example:</h5>
452    * <p class='bjava'>
453    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
454    *
455    *    FluentMap&lt;String,Object&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Object.<jk>class</jk>)
456    *       .filtered()
457    *       .add(<js>"name"</js>, <js>"John"</js>)
458    *       .add(<js>"age"</js>, -1)              <jc>// Filtered out at build time</jc>
459    *       .add(<js>"enabled"</js>, <jk>false</jk>)   <jc>// Filtered out at build time</jc>
460    *       .add(<js>"tags"</js>, <jk>new</jk> String[0]) <jc>// Filtered out at build time</jc>
461    *       .build();
462    * </p>
463    *
464    * @return This object.
465    */
466   public Maps<K,V> filtered() {
467      // @formatter:off
468      return filtered((k, v) -> ! (
469         v == null
470         || (v instanceof Boolean v2 && v2.equals(false))
471         || (v instanceof Number v3 && v3.intValue() == -1)
472         || (isArray(v) && Array.getLength(v) == 0)
473         || (v instanceof Map v2 && v2.isEmpty())
474         || (v instanceof Collection v3 && v3.isEmpty())
475         ));
476      // @formatter:on
477   }
478
479   /**
480    * Applies a filter predicate to entries when building the map.
481    *
482    * <p>
483    * The filter receives both the key and value of each entry. Entries where the predicate returns
484    * {@code true} will be kept; entries where it returns {@code false} will be filtered out.
485    *
486    * <p>
487    * This method can be called multiple times. When called multiple times, all filters are combined
488    * using AND logic - an entry must pass all filters to be kept in the map.
489    *
490    * <p>
491    * Note: Filtering is applied at build time, not when adding entries.
492    *
493    * <h5 class='section'>Example:</h5>
494    * <p class='bjava'>
495    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
496    *
497    *    <jc>// Keep only non-null, non-empty string values</jc>
498    *    Map&lt;String,String&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, String.<jk>class</jk>)
499    *       .filtered((k, v) -&gt; v != <jk>null</jk> &amp;&amp; !v.equals(<js>""</js>))
500    *       .add(<js>"a"</js>, <js>"foo"</js>)
501    *       .add(<js>"b"</js>, <jk>null</jk>)     <jc>// Filtered out at build time</jc>
502    *       .add(<js>"c"</js>, <js>""</js>)       <jc>// Filtered out at build time</jc>
503    *       .build();
504    *
505    *    <jc>// Multiple filters combined with AND</jc>
506    *    Map&lt;String,Integer&gt; <jv>map2</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
507    *       .filtered((k, v) -&gt; v != <jk>null</jk>)           <jc>// First filter</jc>
508    *       .filtered((k, v) -&gt; v &gt; 0)                    <jc>// Second filter (ANDed with first)</jc>
509    *       .filtered((k, v) -&gt; ! k.startsWith(<js>"_"</js>)) <jc>// Third filter (ANDed with previous)</jc>
510    *       .add(<js>"a"</js>, 5)
511    *       .add(<js>"_b"</js>, 10)  <jc>// Filtered out (starts with "_")</jc>
512    *       .add(<js>"c"</js>, -1)   <jc>// Filtered out (not &gt; 0)</jc>
513    *       .build();
514    * </p>
515    *
516    * @param filter The filter predicate. Must not be <jk>null</jk>.
517    * @return This object.
518    */
519   public Maps<K,V> filtered(BiPredicate<K,V> filter) {
520      BiPredicate<K,V> newFilter = assertArgNotNull("filter", filter);
521      if (this.filter == null)
522         this.filter = newFilter;
523      else
524         this.filter = this.filter.and(newFilter);
525      return this;
526   }
527
528   /**
529    * Converts the set into a {@link SortedMap}.
530    *
531    * <p>
532    * Note: If {@link #ordered()} was previously called, calling this method will override it.
533    * The last method called ({@link #ordered()} or {@link #sorted()}) determines the final map type.
534    *
535    * @return This object.
536    */
537   @SuppressWarnings("unchecked")
538   public Maps<K,V> sorted() {
539      return sorted((Comparator<K>)Comparator.naturalOrder());
540   }
541
542   /**
543    * Converts the set into a {@link SortedMap} using the specified comparator.
544    *
545    * <p>
546    * Note: If {@link #ordered()} was previously called, calling this method will override it.
547    * The last method called ({@link #ordered()} or {@link #sorted()}) determines the final map type.
548    *
549    * @param comparator The comparator to use for sorting. Must not be <jk>null</jk>.
550    * @return This object.
551    */
552   public Maps<K,V> sorted(Comparator<K> comparator) {
553      this.comparator = assertArgNotNull("comparator", comparator);
554      ordered = false;
555      return this;
556   }
557
558   /**
559    * When specified, the {@link #build()} method will return <jk>null</jk> if the map is empty.
560    *
561    * <p>
562    * Otherwise {@link #build()} will never return <jk>null</jk>.
563    *
564    * @return This object.
565    */
566   public Maps<K,V> sparse() {
567      sparse = true;
568      return this;
569   }
570
571   /**
572    * When specified, {@link #build()} will return an unmodifiable map.
573    *
574    * @return This object.
575    */
576   public Maps<K,V> unmodifiable() {
577      unmodifiable = true;
578      return this;
579   }
580
581   /**
582    * When specified, {@link #build()} will return a thread-safe map.
583    *
584    * <p>
585    * The thread-safety implementation depends on other settings:
586    * <ul>
587    *    <li>If {@link #sorted()} is set: Uses {@link java.util.concurrent.ConcurrentSkipListMap}
588    *    <li>If {@link #ordered()} is set: Uses {@link java.util.Collections#synchronizedMap(java.util.Map)}
589    *    <li>Otherwise: Uses {@link java.util.concurrent.ConcurrentHashMap}
590    * </ul>
591    *
592    * <p>
593    * This is useful when the map needs to be accessed from multiple threads.
594    *
595    * <h5 class='section'>Example:</h5>
596    * <p class='bjava'>
597    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
598    *
599    *    <jc>// Create a thread-safe map using ConcurrentHashMap</jc>
600    *    Map&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
601    *       .add(<js>"one"</js>, 1)
602    *       .add(<js>"two"</js>, 2)
603    *       .concurrent()
604    *       .build();
605    *
606    *    <jc>// Create a thread-safe ordered map</jc>
607    *    Map&lt;String,Integer&gt; <jv>map2</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
608    *       .ordered()
609    *       .concurrent()
610    *       .add(<js>"one"</js>, 1)
611    *       .build();
612    * </p>
613    *
614    * @return This object.
615    */
616   public Maps<K,V> concurrent() {
617      concurrent = true;
618      return this;
619   }
620
621   /**
622    * Sets whether {@link #build()} should return a thread-safe map.
623    *
624    * <p>
625    * The thread-safety implementation depends on other settings:
626    * <ul>
627    *    <li>If {@link #sorted()} is set: Uses {@link java.util.concurrent.ConcurrentSkipListMap}
628    *    <li>If {@link #ordered()} is set: Uses {@link java.util.Collections#synchronizedMap(java.util.Map)}
629    *    <li>Otherwise: Uses {@link java.util.concurrent.ConcurrentHashMap}
630    * </ul>
631    *
632    * <p>
633    * This is useful when the map needs to be accessed from multiple threads.
634    *
635    * <h5 class='section'>Example:</h5>
636    * <p class='bjava'>
637    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
638    *
639    *    <jc>// Conditionally create a thread-safe map</jc>
640    *    Map&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
641    *       .add(<js>"one"</js>, 1)
642    *       .concurrent(<jv>needsThreadSafety</jv>)
643    *       .build();
644    * </p>
645    *
646    * @param value Whether to make the map thread-safe.
647    * @return This object.
648    */
649   public Maps<K,V> concurrent(boolean value) {
650      concurrent = value;
651      return this;
652   }
653
654   /**
655    * When specified, {@link #build()} will use a {@link LinkedHashMap} to preserve insertion order.
656    *
657    * <p>
658    * If not specified, a {@link HashMap} is used by default (no guaranteed order).
659    *
660    * <p>
661    * Note: If {@link #sorted()} was previously called, calling this method will override it.
662    * The last method called ({@link #ordered()} or {@link #sorted()}) determines the final map type.
663    *
664    * <h5 class='section'>Example:</h5>
665    * <p class='bjava'>
666    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
667    *
668    *    <jc>// Create an ordered map (preserves insertion order)</jc>
669    *    Map&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
670    *       .ordered()
671    *       .add(<js>"one"</js>, 1)
672    *       .add(<js>"two"</js>, 2)
673    *       .build();
674    * </p>
675    *
676    * @return This object.
677    */
678   public Maps<K,V> ordered() {
679      return ordered(true);
680   }
681
682   /**
683    * Sets whether {@link #build()} should use a {@link LinkedHashMap} to preserve insertion order.
684    *
685    * <p>
686    * If <c>false</c> (default), a {@link HashMap} is used (no guaranteed order).
687    * If <c>true</c>, a {@link LinkedHashMap} is used (preserves insertion order).
688    *
689    * <p>
690    * Note: If {@link #sorted()} was previously called, calling this method with <c>true</c> will override it.
691    * The last method called ({@link #ordered()} or {@link #sorted()}) determines the final map type.
692    *
693    * <h5 class='section'>Example:</h5>
694    * <p class='bjava'>
695    *    <jk>import static</jk> org.apache.juneau.commons.utils.CollectionUtils.*;
696    *
697    *    <jc>// Conditionally create an ordered map</jc>
698    *    Map&lt;String,Integer&gt; <jv>map</jv> = <jsm>mapb</jsm>(String.<jk>class</jk>, Integer.<jk>class</jk>)
699    *       .ordered(<jv>preserveOrder</jv>)
700    *       .add(<js>"one"</js>, 1)
701    *       .build();
702    * </p>
703    *
704    * @param value Whether to preserve insertion order.
705    * @return This object.
706    */
707   public Maps<K,V> ordered(boolean value) {
708      ordered = value;
709      if (ordered)
710         comparator = null;
711      return this;
712   }
713
714   /**
715    * Converts a key object to the key type.
716    *
717    * @param o The object to convert.
718    * @return The converted key.
719    */
720   @SuppressWarnings("unchecked")
721   private K convertKey(Object o) {
722      if (keyType.isInstance(o))
723         return (K)o;
724      if (nn(keyFunction))
725         return keyFunction.apply(o);
726      throw rex("Object of type {0} could not be converted to key type {1}", cn(o), cn(keyType));
727   }
728
729   /**
730    * Converts a value object to the value type.
731    *
732    * @param o The object to convert.
733    * @return The converted value.
734    */
735   @SuppressWarnings("unchecked")
736   private V convertValue(Object o) {
737      if (valueType.isInstance(o))
738         return (V)o;
739      if (nn(valueFunction))
740         return valueFunction.apply(o);
741      throw rex("Object of type {0} could not be converted to value type {1}", cn(o), cn(valueType));
742   }
743}