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