001/*
002 * Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025package Torello.Java.ReadOnly;
026
027import java.io.Serializable;
028
029import java.util.function.BiConsumer;
030import java.util.function.Consumer;
031import java.util.function.Predicate;
032import java.util.function.Function;
033
034import Torello.Java.Additional.Tuple2;
035import Torello.JavaDoc.LinkJavaSource;
036
037import java.util.*;
038
039/**
040 * Immutable Wrapper for <CODE>java&#46;util&#46;HashMap</CODE>, found in the "Java Collections
041 * Framework".
042 * 
043 * <EMBED CLASS=globalDefs DATA-JDK=HashMap DATA-ENDING=Map>
044 * <EMBED CLASS='external-html' DATA-FILE-ID=DATA_CLASS>
045 * 
046 * @param <K> the type of keys maintained by this map
047 * @param <V> the type of mapped values
048 */
049@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_MAIN")
050@SuppressWarnings("unchecked")
051public class ReadOnlyHashMap<K, V> implements ReadOnlyMap<K, V>, Serializable
052{
053    // ********************************************************************************************
054    // ********************************************************************************************
055    // Protected & Private Fields, Methods, Statics
056    // ********************************************************************************************
057    // ********************************************************************************************
058
059
060    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
061    protected static final long serialVersionUID = 1;
062
063    // Minor Optimization where new HashMap's that have no contents always re-use this static
064    // instance.  Since this instance is completely empty, the Raw-Types things is irrelevant.
065
066    @SuppressWarnings("rawtypes")
067    private static final HashMap EMPTY_HASH_MAP = new HashMap(0, 0.75f);
068
069    // Singleton & Empty ReadOnlyHashMap, Uses the "Supplier Constructor"
070    @SuppressWarnings("rawtypes")
071    private static final ReadOnlyHashMap EMPTY_READONLY_HASH_MAP =
072        new ReadOnlyHashMap(EMPTY_HASH_MAP);
073
074    // The actual HashMap used by this instance.
075    private final HashMap<K, V> hashMap;
076
077    // TRUE     => This was built using the class ROHashMapBuilder
078    // FALSE    => This was built using the clone() of a standard java.util.HashMap constructor
079
080    private final boolean fromBuilderOrHashMap;
081
082    // Mimics the C++ Keyword/Concept of "Friend Class".   Is "Friends With" ROHashMapBuilder
083    static class AccessBadge { private AccessBadge() { } }
084    private static final AccessBadge friendClassBadge = new AccessBadge();
085
086
087    // ********************************************************************************************
088    // ********************************************************************************************
089    // Builder, Acess-Badge Constructor - and Static "Empty" getter (which is used by the builder)
090    // ********************************************************************************************
091    // ********************************************************************************************
092
093
094    /**
095     * Returns an empty, <B STYLE='color: red;'>singleton</B>, instance of {@code ReadOnlyHashMap}.
096     * @param <X> Returned {@link ReadOnlyMap}'s Key-Type.
097     * @param <Y> Returned {@link ReadOnlyMap}'s Value-Type.  
098     * 
099     * @return An empty map.  Since this map is both empty &amp; read-only, a raw-type singleton 
100     * will suffice for all operations offered by this clas.
101     * 
102     * <EMBED CLASS='external-html' DATA-FILE-ID=EMPTY_SYNCHRONIZED>
103     */
104    public static <X, Y> ReadOnlyHashMap<X, Y> emptyROHM()
105    { return (ReadOnlyHashMap<X, Y>) EMPTY_READONLY_HASH_MAP; }
106
107    // To all the readers out there following along: The "AccessBadge" thing is just a slightly
108    // wimpier substitute for the C++ keyword / concept 'friend' or "Friend Class".  It means this
109    // constructor is (for all intents and purposes) a private-constructor, except for the class
110    // ROHashMapBuilder
111    //
112    // This is the Constructor used by the Builder.  It has a "Zero-Size" Optimization
113
114    ReadOnlyHashMap(ROHashMapBuilder<K, V> rohmb, ROHashMapBuilder.AccessBadge badge)
115    {
116        Objects.requireNonNull(badge, "Access Badge is null.  Requires Friend-Class Badge");
117
118        fromBuilderOrHashMap = true;
119        this.hashMap = rohmb;
120    }
121
122
123    // ********************************************************************************************
124    // ********************************************************************************************
125    // Modified-Original Constructors
126    // ********************************************************************************************
127    // ********************************************************************************************
128
129
130    /**
131     * Copies the contents of parameter {@code 'map'}, and saves saves it, thereby guaranteeing
132     * {@code 'this'} instance is Read-Only and fully-shielded from outside modification.
133     * 
134     * @param map The {@code map} to be copied into {@code 'this'} instance internal and private
135     * {@code 'hashMap'} field.
136     */
137    public ReadOnlyHashMap(Map<K, V> map)
138    {
139        fromBuilderOrHashMap = false;
140
141        // Empty Optimization (throw away, completely, the reference, use static-constant)
142        this.hashMap = (map.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : new HashMap<>(map);
143    }
144
145    /**
146     * If only a small amount of processing needs to be done on the contents of some Java
147     * Map, and using an entire Builder-Class seems disproportionately complex - <I>this
148     * constructor can convert any Java {@code Map} into a {@code ReadOnlyHashMap}, using
149     * a simple {@code 'mapTranslator'}</I>.
150     * 
151     * @param <X> The Key-Type of the User-Provided {@code Map}.
152     * @param <Y> The Value-Type of the User-Provided {@code Map}.
153     * 
154     * @param refHolder This must a non-null instance of {@link Tuple2}.  The provided
155     * {@code Consumer} is just that, a {@code 'Consumer'} rather than a {@code 'Function'}, since
156     * the results of each translation must be assigned to the values inside this tuple in order
157     * for them to be inserted into this {@code ReadOnlyHashMap}.
158     * 
159     * @param map Any Java {@code Map}.
160     * 
161     * @param mapTranslator A function for mapping the iterated elements of Map-Types {@code 'X'}
162     * and {@code 'Y'}, into the actual {@code HashMap's} Key-Type {@code 'K'}, and Value-Type
163     * {@code 'V'}.  The results of this translation must be placed into the fields inside
164     * {@code 'refHolder'}.
165     * 
166     * <BR /><BR />If this parameter is passed null, this method will throw a
167     * {@code NullPointerException}.
168     * 
169     * @param filter An optional filter that can be used to prevent &amp; prohibit any chosen
170     * elements from input {@code 'map'} from being inserted into {@code 'this'}
171     * {@code ReadOnlyTreeMap}.
172     * 
173     * <BR /><BR />This parameter may be passed null, and if it is, it will be silently ignored, 
174     * and all entries present inside {@code 'map'} will be processed and inserted into
175     * {@code 'this'}
176     * 
177     * @param loadFactor <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
178     * 
179     * @throws NullPointerException if either parameter {@code 'i'} or parameter 
180     * {@code 'mapTranslator'} is passed null.
181     * 
182     * @throws IllegalArgumentException if the load factor is nonpositive
183     */
184    public <X, Y> ReadOnlyHashMap(
185            Tuple2<K, V>                refHolder,
186            Map<X, Y>                   map,
187            Consumer<Map.Entry<X, Y>>   mapTranslator,
188            Predicate<Map.Entry<X, Y>>  filter,
189            Float                       loadFactor
190        )
191    {
192        Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'");
193        Objects.requireNonNull(mapTranslator, ROHelpers.NULL_MSG + "'mapTranslator'");
194
195        fromBuilderOrHashMap = false;
196
197        HashMap<K, V> hashMap = (loadFactor != null)
198            ? new HashMap<>(map.size(), loadFactor)
199            : new HashMap<>(map.size());
200
201        if (filter != null)
202
203            for (Map.Entry<X, Y> entry : map.entrySet())
204            {
205                if (! filter.test(entry)) continue;
206                mapTranslator.accept(entry);
207                hashMap.put(refHolder.a, refHolder.b);
208            }
209
210        else
211
212            for (Map.Entry<X, Y> entry : map.entrySet())
213            {
214                mapTranslator.accept(entry);
215                hashMap.put(refHolder.a, refHolder.b);
216            }
217
218        // Empty Optimization (throw away, completely, the reference, use static-constant)
219        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
220    }
221
222
223    // ********************************************************************************************
224    // ********************************************************************************************
225    // New Constructors, January 2024
226    // ********************************************************************************************
227    // ********************************************************************************************
228
229
230    /**
231     * Constructs an instance of {@code ReadOnlyHashMap} that contains the keys present in
232     * parameter {@code 'keys'}, and values generated by {@code 'valueMapper'} - using each of the
233     * {@code 'keys'} as input.
234     * 
235     * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT>
236     * 
237     * @param keys          Any Java {@code Iterable} instance.
238     * @param valueMapper   A user provided function to compute a map value, based on a map key.
239     * @param filter        <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER>
240     * @param loadFactor    <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
241     * @param sizeIfKnown   <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY>
242     *  
243     * @throws NullPointerException if either {@code 'keys'} or {@code 'valueMapper'} are passed
244     * null.
245     */
246    public ReadOnlyHashMap(
247            Iterable<? extends K>               keys,
248            Function<? super K, ? extends V>    valueMapper,
249            Predicate<? super K>                filter,
250            Float                               loadFactor,
251            Integer                             sizeIfKnown
252        )
253    {
254        Objects.requireNonNull(keys, ROHelpers.NULL_MSG + "'keys'");
255        Objects.requireNonNull(valueMapper, ROHelpers.NULL_MSG + "'valueMapper'");
256
257        fromBuilderOrHashMap = false;
258
259        HashMap<K, V> hashMap = new HashMap<>(
260            ((sizeIfKnown == null)  ? 16    : sizeIfKnown),
261            ((loadFactor == null)   ? 0.75f : loadFactor)
262        );
263
264        if (filter == null)
265            for (K key : keys)
266                hashMap.put(key, valueMapper.apply(key));
267
268        else
269            for (K key : keys)
270                if (filter.test(key))
271                    hashMap.put(key, valueMapper.apply(key));
272
273        // Empty Optimization (throw away, completely, the reference, use static-constant)
274        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
275    }
276
277    /**
278     * Constructs an instance of {@code ReadOnlyHashMap} that has been populated by the Key-Value
279     * Pairs left in {@code 'refHolder'} by each invocation of the {@code Runnable} parameter
280     * {@code 'computeNextEntry'}.  Key-Value Pairs are inserted until an invocation of the 
281     * {@code Runnable} leaves null in {@code refHolder.a} and {@code refHolder.b}
282     * 
283     * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT>
284     * 
285     * @param refHolder         <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER>
286     * @param computeNextEntry  <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_RUN>
287     * @param loadFactor        <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
288     * @param sizeIfKnown       <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY>
289     * 
290     * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'} are
291     * passed null.
292     */
293    public ReadOnlyHashMap(
294            Tuple2<K, V>    refHolder,
295            Runnable        computeNextEntry,
296            Float           loadFactor,
297            Integer         sizeIfKnown
298        )
299    {
300        Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'");
301        Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'");
302
303        fromBuilderOrHashMap = false;
304
305        HashMap<K, V> hashMap = new HashMap<>(
306            ((sizeIfKnown == null)  ? 16    : sizeIfKnown),
307            ((loadFactor == null)   ? 0.75f : loadFactor)
308        );
309
310        do
311        {
312            computeNextEntry.run();
313            if ((refHolder.a == null) && (refHolder.b == null)) break;
314            hashMap.put(refHolder.a, refHolder.b);
315        }
316        while (true);
317
318        // Empty Optimization (throw away, completely, the reference, use static-constant)
319        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
320    }
321
322    /**
323     * Populates an instance of {@code ReadOnlyHashMap} by iterating the input {@code 'source'}
324     * iterable, and passing each value returned by that {@code Iterator} to the
325     * {@code 'computeNextEntry'} Java {@code Consumer}.
326     * 
327     * <BR /><BR />It is the programmer's responsibility to properly place each Key-Value Pair 
328     * that is intending to be inserted into the final {@code Map} instance into the
329     * {@code 'refHolder'} instance.  After each invocation of {@code 'computeNextEntry'}, this 
330     * constructor's logic will retrieve the values within {@code 'refHolder.a'} and
331     * {@code 'refHolder.b'} and insert them into this instance internal {@code HashMap}.
332     * 
333     * <EMBED CLASS='external-html' DATA-FILE-ID=LOOK_AT_IT>
334     * 
335     * @param <X>               The type of the elements inside {@code 'source'}
336     * @param source            Any Java {@code Iterable} instance.
337     * @param refHolder         <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER>
338     * @param computeNextEntry  <EMBED CLASS='external-html' DATA-FILE-ID=COMPUTE_NEXT_CONS>
339     * @param filter            <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ITERABLE_FILTER>
340     * @param loadFactor        <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
341     * @param sizeIfKnown       <EMBED CLASS='external-html' DATA-FILE-ID=MAP_TABLE_ICAPACITY>
342     * 
343     * @throws NullPointerException if either {@code 'refHolder'}, {@code 'computeNextEntry'} or
344     * {@code 'source'} are passed null.
345     */
346    public <X> ReadOnlyHashMap(
347            Iterable<X>             source,
348            Tuple2<K, V>            refHolder,
349            Consumer<? super X>     computeNextEntry,
350            Predicate<? super X>    filter,
351            Float                   loadFactor,
352            Integer                 sizeIfKnown
353        )
354    {
355        Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'");
356        Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'");
357
358        fromBuilderOrHashMap = false;
359
360        HashMap<K, V> hashMap = new HashMap<>(
361            ((sizeIfKnown == null)  ? 16    : sizeIfKnown),
362            ((loadFactor == null)   ? 0.75f : loadFactor)
363        );
364
365        X x; // temp var
366        Iterator<X> iter = source.iterator();
367
368        if (filter == null)
369
370            while (iter.hasNext())
371            {
372                computeNextEntry.accept(iter.next());
373                hashMap.put(refHolder.a, refHolder.b);
374            }
375
376        else
377
378            while (iter.hasNext())
379                if (filter.test(x = iter.next()))
380                {
381                    computeNextEntry.accept(x);
382                    hashMap.put(refHolder.a, refHolder.b);
383                }
384
385        // Empty Optimization (throw away, completely, the reference, use static-constant)
386        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
387    }
388
389
390    // ********************************************************************************************
391    // ********************************************************************************************
392    // Array Constructors, March 2024
393    // ********************************************************************************************
394    // ********************************************************************************************
395
396
397    /**
398     * Retrieves elements from the VarArgs Generic-Array parameter {@code 'elements'}, and
399     * subsequently invokes the {@code 'computeNextEntry'} processor to populate this
400     * {@code ReadOnlyHashMap}.
401     * 
402     * @param <X>                  The type of array parameter {@code 'elements'}
403     * @param refHolder            <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER>
404     * @param computeNextEntry     <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS>
405     * @param filter               <EMBED CLASS='external-html' DATA-FILE-ID=MAP_ARRAY_FILTER>
406     * @param loadFactor           <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
407     * @param elements             Any Generic VarArgs-Array
408     * @throws ClassCastException  <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX>
409     * 
410     * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'}
411     * are passed null
412     */
413    public <X> ReadOnlyHashMap(
414            Tuple2<K, V>            refHolder,
415            Consumer<? super X>     computeNextEntry,
416            Predicate<? super X>    filter,
417            Float                   loadFactor,
418            X...                    elements
419        )
420    {
421        Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'");
422        Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'");
423
424        this.fromBuilderOrHashMap = false;
425
426        HashMap<K, V> hashMap =
427            new HashMap<>(elements.length, ((loadFactor == null) ? 0.75f : loadFactor));
428
429        if (filter == null) for (X e : elements)
430        {
431            computeNextEntry.accept(e);
432            hashMap.put(refHolder.a, refHolder.b);
433        }
434
435        else for (X x : elements) if (filter.test(x))
436        {
437            computeNextEntry.accept(x);
438            hashMap.put(refHolder.a, refHolder.b);
439        }
440
441        // Empty Optimization (throw away, completely, the reference, use static-constant)
442        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
443    }
444
445    /**
446     * Retrieves elements from the Java Primitive-Array parameter {@code 'primitiveArray'}, and
447     * subsequently invokes the {@code 'computeNextEntry'} processor to populate this
448     * {@code ReadOnlyHashMap}.
449     * 
450     * @param filter               <EMBED CLASS='external-html' DATA-FILE-ID=PRED_FILT_PRIM>
451     * @param computeNextEntry     <EMBED CLASS='external-html' DATA-FILE-ID=ARR_COMPUTE_NEXT_CONS>
452     * @param refHolder            <EMBED CLASS='external-html' DATA-FILE-ID=REF_HOLDER>
453     * @param loadFactor           <EMBED CLASS='external-html' DATA-FILE-ID=HASH_LOAD_FACTOR>
454     * @param primitiveArray       Any Java Primitive-Array
455     * @throws ClassCastException  <EMBED CLASS='external-html' DATA-FILE-ID=PRIM_ARR_CCEX>
456     * 
457     * @throws NullPointerException if either {@code 'refHolder'} or {@code 'computeNextEntry'}
458     * are passed null
459     * 
460     */
461    @LinkJavaSource(handle="ROHelperPrimitiveArrays", name="buildROMap")
462    public <X> ReadOnlyHashMap(
463            Predicate<?>    filter,
464            Consumer<?>     computeNextEntry,
465            Tuple2<K, V>    refHolder,
466            Float           loadFactor,
467            Object          primitiveArray
468        )
469    {
470        Objects.requireNonNull(refHolder, ROHelpers.NULL_MSG + "'refHolder'");
471        Objects.requireNonNull(computeNextEntry, ROHelpers.NULL_MSG + "'computeNextEntry'");
472
473        this.fromBuilderOrHashMap = false;
474
475        HashMap<K, V> hashMap = ROHelperPrimitiveArrays.buildROMap(
476            primitiveArray,
477            (int arrayLen) -> new HashMap<>(arrayLen, ((loadFactor == null) ? 0.75f : loadFactor)),
478            filter,
479            refHolder,
480            computeNextEntry
481        );
482
483        // Empty Optimization (throw away, completely, the reference, use static-constant)
484        this.hashMap = (hashMap.size() == 0) ? ((HashMap<K, V>) EMPTY_HASH_MAP) : hashMap;
485    }
486
487
488    // ********************************************************************************************
489    // ********************************************************************************************
490    // Original JDK Methods, java.util.HashMap
491    // ********************************************************************************************
492    // ********************************************************************************************
493
494
495    /**
496     * Returns the number of key-value mappings in this map.
497     * @return the number of key-value mappings in this map
498     */
499    public int size()
500    { return this.hashMap.size(); }
501
502    /**
503     * Returns {@code TRUE} if this map contains no key-value mappings.
504     * @return {@code TRUE} if this map contains no key-value mappings
505     */
506    public boolean isEmpty()
507    { return this.hashMap.isEmpty(); }
508
509    /**
510     * Returns the value to which the specified key is mapped, or {@code null} if this map contains
511     * no mapping for the key.
512     *
513     * <BR /><BR />More formally, if this map contains a mapping from a key {@code k} to a value
514     * {@code v} such that {@code (key==null ? k==null : key.equals(k))}, then this method returns
515     * {@code v}; otherwise it returns {@code null}.  (There can be at most one such mapping.)
516     *
517     * <BR /><BR />A return value of {@code null} does not <i>necessarily</i> indicate that the map
518     * contains no mapping for the key; it's also possible that the map explicitly maps the key to
519     * {@code null}. The {@link #containsKey containsKey} operation may be used to distinguish
520     * these two cases.
521     */
522    public V get(Object key)
523    { return this.hashMap.get(key); }
524
525    /**
526     * Returns {@code TRUE} if this map contains a mapping for the specified key.
527     * @param key The key whose presence in this map is to be tested
528     * @return {@code TRUE} if this map contains a mapping for the specified key.
529     */
530    public boolean containsKey(Object key)
531    { return this.hashMap.containsKey(key); }
532
533    /**
534     * Returns {@code TRUE} if this map maps one or more keys to the specified value.
535     * @param value value whose presence in this map is to be tested
536     * @return {@code TRUE} if this map maps one or more keys to the specified value
537     */
538    public boolean containsValue(Object value)
539    { return this.hashMap.containsValue(value); }
540
541
542    // ********************************************************************************************
543    // ********************************************************************************************
544    // The Builder AccessDenied Issue
545    // ********************************************************************************************
546    // ********************************************************************************************
547
548
549    /**
550     * Returns a {@link ReadOnlySet} view of the keys contained in this map. 
551     * @return a set view of the keys contained in this map
552     */
553    @LinkJavaSource(handle="JavaHTMLReadOnlySet")
554    public ReadOnlySet<K> keySet()
555    {
556        return new JavaHTMLReadOnlySet<>(
557            fromBuilderOrHashMap
558                ? ((ROHashMapBuilder<K, V>) this.hashMap)._keySet(friendClassBadge)
559                : this.hashMap.keySet()
560        );
561    }
562
563    /**
564     * Returns a {@link ReadOnlyCollection} view of the values contained in this map.
565     * @return a view of the values contained in this map
566     */
567    @LinkJavaSource(handle="JavaHTMLReadOnlyCollection")
568    public ReadOnlyCollection<V> values()
569    {
570        return new JavaHTMLReadOnlyCollection<>(
571            fromBuilderOrHashMap
572                ? ((ROHashMapBuilder<K, V>) this.hashMap)._values(friendClassBadge)
573                : this.hashMap.values()
574        );
575    }
576
577    /**
578     * Returns a {@link ReadOnlySet} view of the mappings contained in this map.
579     * @return a set view of the mappings contained in this map
580     */
581    @LinkJavaSource(handle="ROHelperEntrySet")
582    public ReadOnlySet<ReadOnlyMap.Entry<K,V>> entrySet()
583    {
584        return ROHelperEntrySet.toReadOnlyEntrySet(
585            fromBuilderOrHashMap
586                ? ((ROHashMapBuilder<K, V>) this.hashMap)._entrySet(friendClassBadge)
587                : this.hashMap.entrySet()
588        );
589    }
590
591
592    // ********************************************************************************************
593    // ********************************************************************************************
594    // Overrides of JDK8 Map extension methods / ReadOnlyMap
595    // ********************************************************************************************
596    // ********************************************************************************************
597
598
599    @Override
600    public V getOrDefault(Object key, V defaultValue)
601    { return this.hashMap.getOrDefault(key, defaultValue); }
602
603    @Override
604    public void forEach(BiConsumer<? super K, ? super V> action)
605    { this.hashMap.forEach(action); }
606
607
608    // ********************************************************************************************
609    // ********************************************************************************************
610    // Convert to java.util Types
611    // ********************************************************************************************
612    // ********************************************************************************************
613
614
615    /**
616     * Clone's {@code 'this'} instance internal {@code HashMap<K, V>} field, and returns it. 
617     * <EMBED CLASS='external-html' DATA-TYPE=HashMap DATA-FILE-ID=CLONE_TO>
618     * 
619     * @return An independent, mutable copy of {@code 'this'} instance' internal
620     * {@code HashMap<K, V>} data-structure.
621     */
622    public HashMap<K, V> cloneToHashMap()
623    {
624        return fromBuilderOrHashMap
625            ? new HashMap<K, V>(this.hashMap)
626            : (HashMap<K, V>) this.hashMap.clone();
627    }
628
629    /**
630     * <BR>Same: Identical to the method {@link #cloneToHashMap()}.
631     * <BR>Returns: Has Return-Type {@code Map<K, V>}, instead.
632     * <BR>Implements: Parent-Class {@link ReadOnlyMap#cloneToMap}
633     */
634    @Torello.JavaDoc.IntoHTMLTable(title="Converts this ReadOnlyHashMap to a java.util.HashMap")
635    public Map<K, V> cloneToMap() { return cloneToHashMap(); }
636
637    // Documented in the Implemented-Interface ReadOnlyMap
638    public Map<K, V> wrapToImmutableMap()
639    { return Collections.unmodifiableMap(this.hashMap); }
640
641
642    // ********************************************************************************************
643    // ********************************************************************************************
644    // java.lang.Object
645    // ********************************************************************************************
646    // ********************************************************************************************
647
648
649    /**
650     * Returns a {@code String} representation of this map. The {@code String} representation
651     * consists of a list of key-value mappings in the order returned by the map's entrySet view's
652     * iterator, enclosed in braces ({@code "{}"}). Adjacent mappings are separated by the
653     * characters {@code ", "} (comma and space).  Each key-value mapping is rendered as the key
654     * followed by an equals sign ({@code "="}) followed by the associated value.  Keys and values
655     * are converted to {@code String's} as by {@code String.valueOf(Object)}.
656     * 
657     * @return a {@code String} representation of this {@code HashMap} 
658     */
659    @LinkJavaSource(handle="ROHelperEntrySet")
660    public String toString()
661    {
662        return ROHelperEntrySet.toString(
663            this.hashMap, // if the map contains itself, it is needed for printing purposes
664            fromBuilderOrHashMap
665                ? ((ROHashMapBuilder<K, V>) this.hashMap)._entrySet(friendClassBadge)
666                : this.hashMap.entrySet()
667        );
668    }
669
670    /**
671     * Compares the specified Object with this Map for equality, as per the definition in the 
672     * class {@code java.util.HashMap}.
673     *
674     * @param  o object to be compared for equality with this {@code ReadOnlyHashMap}.
675     * @return TRUE if the specified Object is equal to this Map
676     */
677    @LinkJavaSource(handle="ROHelperEquals", name="roMapEq")
678    public boolean equals(Object o)
679    { return ROHelperEquals.roMapEq(this, o); }
680
681    /**
682     * Returns the hash code value for this Map as per the definition in the class
683     * {@code java.util.HashMap}.
684     */
685    public int hashCode() 
686    { return this.hashMap.hashCode(); }
687}