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.util.*;
028
029import java.util.function.BiConsumer;
030import java.util.function.BiFunction;
031import java.util.function.Consumer;
032import java.util.function.Function;
033
034/**
035 * A Copy of Java's {@code HashMap} class; used for building a {@link ReadOnlyHashMap}.
036 * Maintains <I>an internal and inaccessible {@code HashMap<E>} instance</I>.  
037 * 
038 * <EMBED CLASS=globalDefs DATA-JDK=HashMap>
039 * <EMBED CLASS='external-html' DATA-A_AN=a DATA-FILE-ID=BUILDERS>
040 * 
041 * @param <K> the type of keys maintained by this map
042 * @param <V> the type of mapped values
043 * @see ReadOnlyHashMap
044 */
045@Torello.JavaDoc.JDHeaderBackgroundImg(EmbedTagFileID="JDHBI_BUILDER")
046public final class ROHashMapBuilder<K, V>
047    extends HashMap<K, V>
048    implements Cloneable, java.io.Serializable
049{
050    // ********************************************************************************************
051    // ********************************************************************************************
052    // Fields & Builder Stuff
053    // ********************************************************************************************
054    // ********************************************************************************************
055
056
057    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUID> */
058    protected static final long serialVersionUID = 1;
059
060    // Exception messages need this
061    private static final String ROHM = "HashMap";
062
063    private boolean built = false;
064
065    // Mimics the C++ Concept of "Friend Class".  This badge is utilized by the "build()" method
066    // directly below this inner-class declaration.  It is the only place where this declaration is
067    // actually used anywhere.  This prohibits access to the constructor that is used to anybody
068    // else
069
070    static class AccessBadge { private AccessBadge() { } }
071    private static final AccessBadge friendClassBadge = new AccessBadge();
072
073    /**
074     * Simply transfers {@code 'this'} instance' internal {@code HashMap} to the
075     * {@code ReadOnlyHashMap} Wrapper-Class.
076     * 
077     * @return a newly constructed {@code ReadOnlyHashMap} "Wrapper-Class", shielding the internal
078     * {@code 'hashMap'} private-field from any modification.
079     */
080    public ReadOnlyHashMap<K, V> build()
081    {
082        this.built = true;
083
084        return (size() == 0)
085            ? ReadOnlyHashMap.emptyROHM()
086            : new ReadOnlyHashMap<>(this, friendClassBadge);
087    }
088
089
090    // ********************************************************************************************
091    // ********************************************************************************************
092    // Modified Constructors
093    // ********************************************************************************************
094    // ********************************************************************************************
095
096
097    /**
098     * Constructs an empty {@code ROHashMapBuilder} with the specified initial capacity and load
099     * factor.
100     * 
101     * <BR /><BR />To create a {@code ROHashMapBuilder} with an initial capacity that accommodates
102     * an expected number of mappings, use {@link #newROHashMapBuilder(int) newROHashMapBuilder}.
103     * 
104     * @param initialCapacity the initial capacity
105     * @param loadFactor the load factor
106     * @throws IllegalArgumentException if the initial capacity is negative or the load factor is
107     * nonpositive
108     */
109    public ROHashMapBuilder(int initialCapacity, float loadFactor)
110    { super(initialCapacity, loadFactor); }
111
112    /**
113     * Constructs an empty {@code ROHashMapBuilder} with the specified initial capacity and the
114     * default load factor (0.75).
115     * 
116     * <BR /><BR />To create a {@code ROHashMapBuilder} with an initial capacity that accommodates
117     * an expected number of mappings, use {@link #newROHashMapBuilder(int) newROHashMapBuilder}.
118     *
119     * @param  initialCapacity the initial capacity.
120     * @throws IllegalArgumentException if the initial capacity is negative.
121     */
122    public ROHashMapBuilder(int initialCapacity)
123    { super(initialCapacity); }
124
125    /**
126     * Constructs an empty {@code ROHashMapBuilder} with the default initial capacity (16) and the
127     * default load factor (0.75).
128     */
129    public ROHashMapBuilder()
130    { super(); }
131
132    /**
133     * Constructs a new {@code ROHashMapBuilder} with the same mappings as the specified
134     * {@code Map}.  The {@code ROHashMapBuilder} is created with default load factor (0.75) and an
135     * initial capacity sufficient to hold the mappings in the specified {@code Map}.
136     *
137     * @param m the map whose mappings are to be placed in this map
138     * @throws NullPointerException if the specified map is null
139     */
140    public ROHashMapBuilder(Map<? extends K, ? extends V> m)
141    { super(m); }
142
143    /**
144     * Creates a new, empty {@code ROHashMapBuilder} suitable for the expected number of mappings.
145     * The returned map uses the default load factor of 0.75, and its initial capacity is generally
146     * large enough so that the expected number of mappings can be added without resizing the map.
147     *
148     * @param numMappings the expected number of mappings
149     * @param <K> the type of keys maintained by the new map
150     * @param <V> the type of mapped values
151     * @return the newly created map
152     * @throws IllegalArgumentException if numMappings is negative
153     */
154    public static <K, V> ROHashMapBuilder<K, V> newROHashMapBuilder(int numMappings)
155    {
156        if (numMappings < 0)
157            throw new IllegalArgumentException("Negative number of mappings: " + numMappings);
158
159        return new ROHashMapBuilder<>(calculateHashMapCapacity(numMappings));
160    }
161
162    // Copied from JDK-21, java.util.HashMap
163    // Calculate initial capacity for HashMap based classes, from expected size and default load
164    // factor (0.75).
165
166    private static int calculateHashMapCapacity(int numMappings)
167    { return (int) Math.ceil(numMappings / (double) /* DEFAULT_LOAD_FACTOR */ 0.75f); }
168
169
170    // ********************************************************************************************
171    // ********************************************************************************************
172    // Original JDK Methods
173    // ********************************************************************************************
174    // ********************************************************************************************
175
176
177    /**
178     * Associates the specified value with the specified key in this map.  If the map previously
179     * contained a mapping for the key, the old value is replaced.
180     * 
181     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
182     *
183     * @param key key with which the specified value is to be associated
184     * @param value value to be associated with the specified key
185     * 
186     * @return the previous value associated with {@code key}, or {@code null} if there was no
187     * mapping for {@code key}.  (A {@code null} return can also indicate that the map previously
188     * associated {@code null} with {@code key}.)
189     */
190    public V put(K key, V value)
191    {
192        if (this.built) throw new AttemptedModificationException(ROHM);
193        return super.put(key, value);
194    }
195
196    /**
197     * Copies all of the mappings from the specified map to this map.  These mappings will replace
198     * any mappings that this map had for any of the keys currently in the specified map.
199     * 
200     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
201     *
202     * @param m mappings to be stored in this map
203     * @throws NullPointerException if the specified map is null
204     */
205    public void putAll(Map<? extends K, ? extends V> m)
206    {
207        if (this.built) throw new AttemptedModificationException(ROHM);
208        super.putAll(m);
209    }
210
211    /**
212     * Removes the mapping for the specified key from this map if present.
213     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
214     * @param key key whose mapping is to be removed from the map
215     * 
216     * @return the previous value associated with {@code key}, or {@code null} if there was no
217     * mapping for {@code key}.  (A {@code null} return can also indicate that the map previously
218     * associated {@code null} with {@code key}.)
219     */
220    public V remove(Object key)
221    {
222        if (this.built) throw new AttemptedModificationException(ROHM);
223        return super.remove(key);
224    }
225
226    /**
227     * Removes all of the mappings from this map.  The map will be empty after this call returns.
228     * 
229     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
230     */
231    public void clear()
232    {
233        if (this.built) throw new AttemptedModificationException(ROHM);
234        super.clear();
235    }
236
237
238    // ********************************************************************************************
239    // ********************************************************************************************
240    // Overrides of JDK8 Map extension methods
241    // ********************************************************************************************
242    // ********************************************************************************************
243
244
245    /**
246     * If the specified key is not already associated with a value (or is mapped to {@code null})
247     * associates it with the given value and returns {@code null}, else returns the current value.
248     *
249     * <BR /><BR />The default implementation is equivalent to, for this {@code map}:
250     *
251     * <BR /><DIV CLASS=SNIP>{@code
252     * V v = map.get(key);
253     * 
254     * if (v == null) v = map.put(key, value);
255     *
256     * return v;
257     * }</DIV>
258     *
259     * <BR /><BR />The default implementation makes no guarantees about synchronization or
260     * atomicity properties of this method. Any implementation providing atomicity guarantees must
261     * override this method and document its concurrency properties.
262     * 
263     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
264     *
265     * @param key key with which the specified value is to be associated
266     * @param value value to be associated with the specified key
267     * 
268     * @return the previous value associated with the specified key, or {@code null} if there was
269     * no mapping for the key.  (A {@code null} return can also indicate that the map previously
270     * associated {@code null} with the key, if the implementation supports null values.)
271     * 
272     * @throws UnsupportedOperationException if the {@code put} operation is not supported by this
273     * map
274     * 
275     * @throws ClassCastException if the key or value is of an inappropriate type for this map
276     * 
277     * @throws NullPointerException if the specified key or value is null, and this map does not
278     * permit null keys or values
279     * 
280     * @throws IllegalArgumentException if some property of the specified key or value prevents it
281     * from being stored in this map
282     */
283    public V putIfAbsent(K key, V value)
284    {
285        if (this.built) throw new AttemptedModificationException(ROHM);
286        return super.putIfAbsent(key, value);
287    }
288
289    /**
290     * Removes the entry for the specified key only if it is currently mapped to the specified
291     * value.
292     *
293     * <BR /><BR />The default implementation is equivalent to, for this {@code map}:
294     *
295     * <BR /><DIV CLASS=SNIP>{@code
296     * if (map.containsKey(key) && Objects.equals(map.get(key), value))
297     * {
298     *     map.remove(key);
299     *     return true;
300     * }
301     * 
302     * return false;
303     * }</DIV>
304     *
305     * <BR /><BR />The default implementation makes no guarantees about synchronization or
306     * atomicity properties of this method. Any implementation providing atomicity guarantees must
307     * override this method and document its concurrency properties.
308     * 
309     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
310     *
311     * @param key key with which the specified value is associated
312     * @param value value expected to be associated with the specified key
313     * @return {@code true} if the value was removed
314     * 
315     * @throws UnsupportedOperationException if the {@code remove} operation is not supported by
316     * this map
317     * 
318     * @throws ClassCastException if the key or value is of an inappropriate type for this map
319     * 
320     * @throws NullPointerException if the specified key or value is null, and this map does not
321     * permit null keys or values
322     */
323    public boolean remove(Object key, Object value)
324    {
325        if (this.built) throw new AttemptedModificationException(ROHM);
326        return super.remove(key, value);
327    }
328
329    /**
330     * Replaces the entry for the specified key only if currently mapped to the specified value.
331     *
332     * <BR /><BR />The default implementation is equivalent to, for this {@code map}:
333     * 
334     * <DIV CLASS=SNIP>{@code
335     * if (map.containsKey(key) && Objects.equals(map.get(key), oldValue))
336     * {
337     *     map.put(key, newValue);
338     *     return true;
339     * }
340     * 
341     * return false;
342     * }</DIV>
343     *
344     * <BR /><BR />The default implementation does not throw {@code NullPointerException} for maps
345     * that do not support null values if oldValue is null unless newValue is also null.
346     *
347     * <BR /><BR />The default implementation makes no guarantees about synchronization or
348     * atomicity properties of this method. Any implementation providing atomicity guarantees must
349     * override this method and document its concurrency properties.
350     * 
351     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
352     *
353     * @param key key with which the specified value is associated
354     * @param oldValue value expected to be associated with the specified key
355     * @param newValue value to be associated with the specified key
356     * @return {@code TRUE} if the value was replaced
357     * 
358     * @throws UnsupportedOperationException if the {@code put} operation is not supported by this
359     * map
360     * 
361     * @throws ClassCastException if the class of a specified key or value prevents it from being
362     * stored in this map
363     * 
364     * @throws NullPointerException if a specified key or newValue is null, and this map does not
365     * permit null keys or values
366     * 
367     * @throws NullPointerException if oldValue is null and this map does not permit null values
368     * 
369     * @throws IllegalArgumentException if some property of a specified key or value prevents it
370     * from being stored in this map
371     */
372    public boolean replace(K key, V oldValue, V newValue)
373    {
374        if (this.built) throw new AttemptedModificationException(ROHM);
375        return super.replace(key, oldValue, newValue);
376    }
377
378    /**
379     * Replaces the entry for the specified key only if it is currently mapped to some value.
380     *
381     * <BR /><BR />The default implementation is equivalent to, for this {@code map}:
382     *
383     * <BR /><DIV CLASS=SNIP>{@code
384     * if (map.containsKey(key)) return map.put(key, value);
385     * return null;
386     * }</DIV>
387     * 
388     * <BR /><BR />The default implementation makes no guarantees about synchronization or
389     * atomicity properties of this method. Any implementation providing atomicity guarantees must
390     * override this method and document its concurrency properties.
391     * 
392     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
393     *
394     * @param key key with which the specified value is associated
395     * @param value value to be associated with the specified key
396     * 
397     * @return the previous value associated with the specified key, or {@code null} if there was
398     * no mapping for the key.  (A {@code null} return can also indicate that the map previously
399     * associated {@code null} with the key, if the implementation supports null values.)
400     * 
401     * @throws UnsupportedOperationException if the {@code put} operation is not supported by this
402     * map
403     * 
404     * @throws ClassCastException if the class of the specified key or value prevents it from being
405     * stored in this map
406     * 
407     * @throws NullPointerException if the specified key or value is null, and this map does not
408     * permit null keys or values
409     * 
410     * @throws IllegalArgumentException if some property of the specified key or value prevents it
411     * from being stored in this map
412     */
413    public V replace(K key, V value)
414    {
415        if (this.built) throw new AttemptedModificationException(ROHM);
416        return super.replace(key, value);
417    }
418
419    /**
420     * If the specified key is not already associated with a value (or is mapped to null), attempts
421     * to compute its value using the given mapping function and enters it into this map unless
422     * null.
423     * 
424     * <BR /><BR />If the mapping function returns null, no mapping is recorded. If the mapping
425     * function itself throws an (unchecked) exception, the exception is rethrown, and no mapping
426     * is recorded. The most common usage is to construct a new object serving as an initial mapped
427     * value or memoized result, as in:
428     * 
429     * <BR /><DIV CLASS=SNIP>{@code
430     * map.computeIfAbsent(key, k -> new Value(f(k)));
431     * }</DIV>
432     * 
433     * <BR /><BR />Or to implement a multi-value map, {@code Map<K,Collection<V>>}, supporting
434     * multiple values per key:
435     * 
436     * <BR /><DIV CLASS=SNIP>{@code
437     * map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
438     * }</DIV>
439     * 
440     * <BR /><BR />The mapping function should not modify this map during computation.
441     * 
442     * <BR /><BR />This method will, on a best-effort basis, throw a
443     * {@link ConcurrentModificationException} if it is detected that the mapping function modifies
444     * this map during computation.
445     * 
446     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
447     * 
448     * @param key key with which the specified value is to be associated
449     * @param mappingFunction the mapping function to compute a value
450     * 
451     * @return the current (existing or computed) value associated with the specified key, or null
452     * if the computed value is null
453     *
454     * @throws ConcurrentModificationException if it is detected that the mapping function modified
455     * this map
456     */
457    public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
458    {
459        if (this.built) throw new AttemptedModificationException(ROHM);
460        return super.computeIfAbsent(key, mappingFunction);
461    }
462
463    /**
464     * If the value for the specified key is present and non-null, attempts to compute a new
465     * mapping given the key and its current mapped value.
466     * 
467     * <BR /><BR />If the remapping function returns null, the mapping is removed. If the remapping
468     * function itself throws an (unchecked) exception, the exception is rethrown, and the current
469     * mapping is left unchanged.
470     * 
471     * <BR /><BR />The remapping function should not modify this map during computation.
472     * 
473     * <BR /><BR />This method will, on a best-effort basis, throw a
474     * {@link ConcurrentModificationException} if it is detected that the remapping function
475     * modifies this map during computation.
476     * 
477     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
478     * 
479     * @param key key with which the specified value is to be associated
480     * @param remappingFunction the remapping function to compute a value
481     * @return the new value associated with the specified key, or null if none
482     * @throws ConcurrentModificationException if it is detected that the remapping function
483     * modified this map
484     */
485    public V computeIfPresent
486        (K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
487    {
488        if (this.built) throw new AttemptedModificationException(ROHM);
489        return super.computeIfPresent(key, remappingFunction);
490    }
491
492    /**
493     * Attempts to compute a mapping for the specified key and its current mapped value (or null if
494     * there is no current mapping). For example, to either create or append a String msg to a
495     * value mapping:
496     * 
497     * <BR ><DIV CLASS=SNIP>{@code
498     * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))
499     * }</DIV>
500     * 
501     * <BR /><BR />(Method merge() is often simpler to use for such purposes.)
502     * 
503     * <BR /><BR />If the remapping function returns null, the mapping is removed (or remains
504     * absent if initially absent). If the remapping function itself throws an (unchecked)
505     * exception, the exception is rethrown, and the current mapping is left unchanged.
506     * 
507     * <BR /><BR />The remapping function should not modify this map during computation.
508     * 
509     * <BR /><BR />This method will, on a best-effort basis, throw a
510     * {@link ConcurrentModificationException} if it is detected that the remapping function
511     * modifies this map during computation.
512     * 
513     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
514     *
515     * @param key key with which the specified value is to be associated
516     * @param remappingFunction the remapping function to compute a value
517     * @return the new value associated with the specified key, or null if none
518     *
519     * @throws ConcurrentModificationException if it is detected that the remapping function
520     * modified this map
521     */
522    public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)
523    {
524        if (this.built) throw new AttemptedModificationException(ROHM);
525        return super.compute(key, remappingFunction);
526    }
527
528    /**
529     * If the specified key is not already associated with a value or is associated with null,
530     * associates it with the given non-null value. Otherwise, replaces the associated value with
531     * the results of the given remapping function, or removes if the result is null. This method
532     * may be of use when combining multiple mapped values for a key. For example, to either create
533     * or append a String msg to a value mapping:
534     * 
535     * <BR /><DIV CLASS=SNIP>{@code
536     * map.merge(key, msg, String::concat)
537     * }</DIV>
538     * 
539     * <BR /><BR />If the remapping function returns null, the mapping is removed. If the remapping
540     * function itself throws an (unchecked) exception, the exception is rethrown, and the current
541     * mapping is left unchanged.
542     * 
543     * <BR /><BR />The remapping function should not modify this map during computation.
544     * 
545     * <BR /><BR />This method will, on a best-effort basis, throw a
546     * {@code ConcurrentModificationException} if it is detected that the remapping function
547     * modifies this map during computation.
548     * 
549     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
550     * 
551     * @param key key with which the resulting value is to be associated
552     * 
553     * @param value the non-null value to be merged with the existing value associated with the key
554     * or, if no existing value or a null value is associated with the key, to be associated with
555     * the key
556     * 
557     * @parma remappingFunction the remapping function to recompute a value if present
558     * 
559     * @return the new value associated with the specified key, or null if no value is associated
560     * with the key
561     * 
562     * @throws ConcurrentModificationException if it is detected that the remapping function
563     * modified this map
564     */
565    public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)
566    {
567        if (this.built) throw new AttemptedModificationException(ROHM);
568        return super.merge(key, value, remappingFunction);
569    }
570
571    /**
572     * Replaces each entry's value with the result of invoking the given function on that entry
573     * until all entries have been processed or the function throws an exception. Exceptions thrown
574     * by the function are relayed to the caller.
575     * 
576     * <BR /><BR />The implementation is equivalent to:
577     * 
578     * <BR /><DIV CLASS=SNIP>{@code
579     * for (Map.Entry<K, V> entry : map.entrySet())
580     *      entry.setValue(function.apply(entry.getKey(), entry.getValue()));
581     * }</DIV>
582     * 
583     * <BR /><BR />No guarantees about synchronization or atomicity properties of this method.
584     * 
585     * <EMBED DATA-TYPE=HashMap CLASS='external-html' DATA-FILE-ID=MUTATOR>
586     * 
587     * @param function the function to apply to each entry
588     * 
589     * @throws UnsupportedOperationException if the set operation is not supported by this map's
590     * entry set iterator.
591     * 
592     * @throws ClassCastException if the class of a replacement value prevents it from being
593     * stored in this map (optional)
594     * 
595     * @throws NullPointerException if the specified function is null, or if a replacement value is
596     * null and this map does not permit null values (optional)
597     * 
598     * @throws IllegalArgumentException if some property of a replacement value prevents it from
599     * being stored in this map (optional)
600     * 
601     * @throws ConcurrentModificationException - if an entry is found to be removed during
602     * iteration
603     */
604    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
605    {
606        if (this.built) throw new AttemptedModificationException(ROHM);
607        super.replaceAll(function);
608    }
609
610
611    // ********************************************************************************************
612    // ********************************************************************************************
613    // Methods inherited from class java.util.HashMap
614    // ********************************************************************************************
615    // ********************************************************************************************
616
617
618    // containsKey, containsValue, entrySet, forEach, get, getOrDefault, isEmpty, keySet, size,
619    // values
620    //
621    // Introspection Only:  containsKey, containsValue, forEach, get, getOrDefault, isEmpty, size
622    // Mutator or Potential Mutator: entrySet, keySet, values
623    //
624    // *** OTHER INHERITED METHODS - ALL READ-ONLY METHODS ***
625    //
626    // Methods inherited from class java.util.AbstractMap: hashCode, toString
627    // Methods inherited from interface java.util.Map: hashCode
628
629    /**
630     * Restricts a back-door into the underlying data-structure.
631     * <EMBED CLASS='external-html'DATA-RET_TYPE=Set DATA-FILE-ID=UNMODIFIABLE>
632     * @return a {@code java.util.Set} that cannot modify this {@code HashMap-Builder}
633     */
634    public Set<Map.Entry<K, V>> entrySet()
635    { return Collections.unmodifiableSet(super.entrySet()); }
636
637    Set<Map.Entry<K, V>> _entrySet(ReadOnlyHashMap.AccessBadge friendClassBadge)
638    { return super.entrySet(); }
639
640    /**
641     * Restricts a back-door into the underlying data-structure.
642     * <EMBED CLASS='external-html'DATA-RET_TYPE=Set DATA-FILE-ID=UNMODIFIABLE>
643     * @return a {@code java.util.Set} that cannot modify this {@code HashMap-Builder}
644     */
645    public Set<K> keySet()
646    { return Collections.unmodifiableSet(super.keySet()); }
647
648    Set<K> _keySet(ReadOnlyHashMap.AccessBadge friendClassBadge)
649    { return super.keySet(); }
650
651    /**
652     * Restricts a back-door into the underlying data-structure.
653     * <EMBED CLASS='external-html'DATA-RET_TYPE=Collection DATA-FILE-ID=UNMODIFIABLE>
654     * @return a {@code java.util.Collection} that cannot modify this {@code HashMap-Builder}
655     */
656    public Collection<V> values()
657    { return Collections.unmodifiableCollection(super.values()); }
658
659    Collection<V> _values(ReadOnlyHashMap.AccessBadge friendClassBadge)
660    { return super.values(); }
661
662
663    // ********************************************************************************************
664    // ********************************************************************************************
665    // java.lang.Object & Cloneable
666    // ********************************************************************************************
667    // ********************************************************************************************
668
669
670    /**
671     * Compares the specified Object with this Builder for equality, as per the definition in the
672     * original class {@code java.util.HashMap}.  Ignores the private field {@code 'built'}.  If
673     * {@code 'this'} has not been built, but {@code 'o'} has been, that fact is ignored during the
674     * equality-comparison.
675     *
676     * @param  o object to be compared for equality with this {@code ROHashMapBuilder} instance
677     * @return true if the specified Object is equal to this Builder
678     */
679    public synchronized boolean equals(Object o)
680    {
681        if (this == o) return true;
682        if (! (o instanceof ROHashMapBuilder)) return false;
683        return super.equals((HashMap) o);
684    }
685
686    /**
687     * Clones this instance' of {@code ROHashMapBuilder}.
688     * 
689     * <BR /><BR />The clone that's returned has had it's internal {@code 'built'} boolean-flag set
690     * {@code FALSE}
691     * 
692     * @return a clone of this builder
693     */
694    public synchronized ROHashMapBuilder<K, V> clone()
695    { return new ROHashMapBuilder<>(this); }
696
697    private ROHashMapBuilder(ROHashMapBuilder<K, V> rohmb)
698    { super(rohmb); this.built=false; }
699}
700