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