1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.AbstractMap;
13 
14 import hunt.collection.Collection;
15 import hunt.collection.Iterator;
16 import hunt.collection.Map;
17 import hunt.collection.Set;
18 
19 import hunt.Exceptions;
20 import hunt.Object;
21 
22 import std.array;
23 import std.container.array;
24 import std.conv;
25 import std.exception;
26 import std.range;
27 
28 /**
29 */
30 abstract class AbstractMap(K, V) : Map!(K, V) {
31 
32     /**
33      * The number of key-value mappings contained in this map.
34      */
35     protected int _size;
36 
37     /**
38      * Sole constructor.  (For invocation by subclass constructors, typically
39      * implicit.)
40      */
41     protected this() {
42     }
43 
44     // Query Operations
45 
46     /**
47      * Returns the number of key-value mappings in this map.
48      *
49      * @return the number of key-value mappings in this map
50      */
51     int size() {
52         return _size;
53     }
54 
55     /**
56      * Returns <tt>true</tt> if this map contains no key-value mappings.
57      *
58      * @return <tt>true</tt> if this map contains no key-value mappings
59      */
60     bool isEmpty() {
61         return _size == 0;
62     }
63 
64     /**
65      * {@inheritDoc}
66      *
67      * @implSpec
68      * This implementation iterates over <tt>entrySet()</tt> searching
69      * for an entry with the specified value.  If such an entry is found,
70      * <tt>true</tt> is returned.  If the iteration terminates without
71      * finding such an entry, <tt>false</tt> is returned.  Note that this
72      * implementation requires linear time in the size of the map.
73      *
74      * @throws ClassCastException   {@inheritDoc}
75      * @throws NullPointerException {@inheritDoc}
76      */
77     bool containsKey(K key) {
78         throw new UnsupportedOperationException();
79     }
80     /**
81      * {@inheritDoc}
82      *
83      * @implSpec
84      * This implementation iterates over <tt>entrySet()</tt> searching
85      * for an entry with the specified value.  If such an entry is found,
86      * <tt>true</tt> is returned.  If the iteration terminates without
87      * finding such an entry, <tt>false</tt> is returned.  Note that this
88      * implementation requires linear time in the size of the map.
89      *
90      * @throws ClassCastException   {@inheritDoc}
91      * @throws NullPointerException {@inheritDoc}
92      */
93     bool containsValue(V value) {
94         throw new UnsupportedOperationException();
95     }
96 
97     V opIndex(K key) {
98         return get(key);
99     }
100 
101     /**
102      * {@inheritDoc}
103      *
104      * @implSpec
105      * This implementation iterates over <tt>entrySet()</tt> searching
106      * for an entry with the specified key.  If such an entry is found,
107      * the entry's value is returned.  If the iteration terminates without
108      * finding such an entry, <tt>null</tt> is returned.  Note that this
109      * implementation requires linear time in the size of the map; many
110      * implementations will override this method.
111      *
112      * @throws ClassCastException            {@inheritDoc}
113      * @throws NullPointerException          {@inheritDoc}
114      */
115     V get(K key) {
116         throw new UnsupportedOperationException();
117     }
118 
119     /**
120      * Returns the value to which the specified key is mapped, or
121      * {@code defaultValue} if this map contains no mapping for the key.
122      *
123      * @implSpec
124      * The default implementation makes no guarantees about synchronization
125      * or atomicity properties of this method. Any implementation providing
126      * atomicity guarantees must override this method and document its
127      * concurrency properties.
128      *
129      * @param key the key whose associated value is to be returned
130      * @param defaultValue the default mapping of the key
131      * @return the value to which the specified key is mapped, or
132      * {@code defaultValue} if this map contains no mapping for the key
133      * @throws ClassCastException if the key is of an inappropriate type for
134      * this map
135      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
136      * @throws NullPointerException if the specified key is null and this map
137      * does not permit null keys
138      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
139      * @since 1.8
140      */
141     V getOrDefault(K k, V value) {
142         throw new UnsupportedOperationException();
143     }
144 
145     // Modification Operations
146 
147     /**
148      * {@inheritDoc}
149      *
150      * @implSpec
151      * This implementation always throws an
152      * <tt>UnsupportedOperationException</tt>.
153      *
154      * @throws UnsupportedOperationException {@inheritDoc}
155      * @throws ClassCastException            {@inheritDoc}
156      * @throws NullPointerException          {@inheritDoc}
157      * @throws IllegalArgumentException      {@inheritDoc}
158      */
159     V put(K key, V value) {
160         throw new UnsupportedOperationException();
161     }
162 
163     V putIfAbsent(K key, V value) {
164         V v = V.init;
165 
166         if (!containsKey(key))
167             v = put(key, value);
168 
169         return v;
170     }
171 
172     /**
173      * {@inheritDoc}
174      *
175      * @implSpec
176      * This implementation iterates over <tt>entrySet()</tt> searching for an
177      * entry with the specified key.  If such an entry is found, its value is
178      * obtained with its <tt>getValue</tt> operation, the entry is removed
179      * from the collection (and the backing map) with the iterator's
180      * <tt>remove</tt> operation, and the saved value is returned.  If the
181      * iteration terminates without finding such an entry, <tt>null</tt> is
182      * returned.  Note that this implementation requires linear time in the
183      * size of the map; many implementations will override this method.
184      *
185      * <p>Note that this implementation throws an
186      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
187      * iterator does not support the <tt>remove</tt> method and this map
188      * contains a mapping for the specified key.
189      *
190      * @throws UnsupportedOperationException {@inheritDoc}
191      * @throws ClassCastException            {@inheritDoc}
192      * @throws NullPointerException          {@inheritDoc}
193      */
194     V remove(K key) {
195 
196         throw new UnsupportedOperationException();
197         // auto v = key in _dict;
198 
199         // V oldValue = V.init;
200         // if (v !is null) {
201         //     oldValue = *v;
202         //     _dict.remove(key);
203         // }
204         // return oldValue;
205     }
206 
207     bool remove(K key, V value) {
208         V curValue = get(key);
209         if (curValue != value || !containsKey(key))
210             return false;
211         remove(key);
212         return true;
213     }
214 
215     // Bulk Operations
216 
217     /**
218      * {@inheritDoc}
219      *
220      * @implSpec
221      * This implementation iterates over the specified map's
222      * <tt>entrySet()</tt> collection, and calls this map's <tt>put</tt>
223      * operation once for each entry returned by the iteration.
224      *
225      * <p>Note that this implementation throws an
226      * <tt>UnsupportedOperationException</tt> if this map does not support
227      * the <tt>put</tt> operation and the specified map is nonempty.
228      *
229      * @throws UnsupportedOperationException {@inheritDoc}
230      * @throws ClassCastException            {@inheritDoc}
231      * @throws NullPointerException          {@inheritDoc}
232      * @throws IllegalArgumentException      {@inheritDoc}
233      */
234     void putAll(Map!(K, V) m) {
235         // for (MapEntry!(K, V) e : m.entrySet())
236         foreach (K k, V v; m)
237             put(k, v);
238     }
239 
240     /**
241      * {@inheritDoc}
242      *
243      * @implSpec
244      * This implementation calls <tt>entrySet().clear()</tt>.
245      *
246      * <p>Note that this implementation throws an
247      * <tt>UnsupportedOperationException</tt> if the <tt>entrySet</tt>
248      * does not support the <tt>clear</tt> operation.
249      *
250      * @throws UnsupportedOperationException {@inheritDoc}
251      */
252     void clear() {
253         throw new NotImplementedException("");
254     }
255 
256     bool replace(K key, V oldValue, V newValue) {
257         V curValue = get(key);
258         if (curValue != oldValue || !containsKey(key)) {
259             return false;
260         }
261         put(key, newValue);
262         return true;
263     }
264 
265     V replace(K key, V value) {
266         V curValue = V.init;
267         if (containsKey(key)) {
268             curValue = put(key, value);
269         }
270         return curValue;
271     }
272 
273     int opApply(scope int delegate(ref K, ref V) dg) {
274         throw new NotImplementedException();
275     }
276 
277     int opApply(scope int delegate(MapEntry!(K, V) entry) dg) {
278         throw new NotImplementedException();
279     }
280 
281     InputRange!K byKey() {
282         throw new NotImplementedException();
283     }
284 
285     InputRange!V byValue() {
286         throw new NotImplementedException();
287     }
288 
289     // Views
290 
291     /**
292      * Each of these fields are initialized to contain an instance of the
293      * appropriate view the first time this view is requested.  The views are
294      * stateless, so there's no reason to create more than one of each.
295      *
296      * <p>Since there is no synchronization performed while accessing these fields,
297      * it is expected that java.util.Map view classes using these fields have
298      * no non-final fields (or any fields at all except for outer-this). Adhering
299      * to this rule would make the races on these fields benign.
300      *
301      * <p>It is also imperative that implementations read the field only once,
302      * as in:
303      *
304      * <pre> {@code
305      * public Set!K keySet() {
306      *   Set!K ks = keySet;  // single racy read
307      *   if (ks is null) {
308      *     ks = new KeySet();
309      *     keySet = ks;
310      *   }
311      *   return ks;
312      * }
313      *}</pre>
314      */
315     // protected Set!K       _keySet;
316     // protected Collection!V _values;
317 
318     /**
319      * {@inheritDoc}
320      *
321      * @implSpec
322      * This implementation returns a set that subclasses {@link AbstractSet}.
323      * The subclass's iterator method returns a "wrapper object" over this
324      * map's <tt>entrySet()</tt> iterator.  The <tt>size</tt> method
325      * delegates to this map's <tt>size</tt> method and the
326      * <tt>contains</tt> method delegates to this map's
327      * <tt>containsKey</tt> method.
328      *
329      * <p>The set is created the first time this method is called,
330      * and returned in response to all subsequent calls.  No synchronization
331      * is performed, so there is a slight chance that multiple calls to this
332      * method will not all return the same set.
333      */
334     K[] keySet() {
335         Array!K arr;
336         foreach (K key; byKey()) {
337             arr.insertBack(key);
338         }
339         return arr.array();
340     }
341 
342     /**
343      * {@inheritDoc}
344      *
345      * @implSpec
346      * This implementation returns a collection that subclasses {@link
347      * AbstractCollection}.  The subclass's iterator method returns a
348      * "wrapper object" over this map's <tt>entrySet()</tt> iterator.
349      * The <tt>size</tt> method delegates to this map's <tt>size</tt>
350      * method and the <tt>contains</tt> method delegates to this map's
351      * <tt>containsValue</tt> method.
352      *
353      * <p>The collection is created the first time this method is called, and
354      * returned in response to all subsequent calls.  No synchronization is
355      * performed, so there is a slight chance that multiple calls to this
356      * method will not all return the same collection.
357      */
358     V[] values() {
359         // FIXME: Needing refactor or cleanup -@zxp at 9/26/2018, 6:00:54 PM
360         // 
361         // Array!V arr;
362         // foreach(V value; byValue()) {
363         //     arr.insertBack(value);
364         // }
365         // return arr.array();
366         V[] arr;
367         foreach (V value; byValue()) {
368             arr ~= value;
369         }
370         return arr;
371     }
372 
373     // Comparison and hashing
374 
375     /**
376      * Compares the specified object with this map for equality.  Returns
377      * <tt>true</tt> if the given object is also a map and the two maps
378      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
379      * <tt>m2</tt> represent the same mappings if
380      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
381      * <tt>equals</tt> method works properly across different implementations
382      * of the <tt>Map</tt> interface.
383      *
384      * @implSpec
385      * This implementation first checks if the specified object is this map;
386      * if so it returns <tt>true</tt>.  Then, it checks if the specified
387      * object is a map whose size is identical to the size of this map; if
388      * not, it returns <tt>false</tt>.  If so, it iterates over this map's
389      * <tt>entrySet</tt> collection, and checks that the specified map
390      * contains each mapping that this map contains.  If the specified map
391      * fails to contain such a mapping, <tt>false</tt> is returned.  If the
392      * iteration completes, <tt>true</tt> is returned.
393      *
394      * @param o object to be compared for equality with this map
395      * @return <tt>true</tt> if the specified object is equal to this map
396      */
397     override bool opEquals(Object o) {
398         throw new UnsupportedOperationException();
399     }
400 
401     bool opEquals(IObject o) {
402         return opEquals(cast(Object) o);
403     }
404     // bool equals(Object o) {
405     //     if (o == this)
406     //         return true;
407 
408     //     if (!(o instanceof Map))
409     //         return false;
410     //     Map<?,?> m = (Map<?,?>) o;
411     //     if (m.size() != size())
412     //         return false;
413 
414     //     try {
415     //         Iterator<Entry!(K,V)> i = entrySet().iterator();
416     //         while (i.hasNext()) {
417     //             Entry!(K,V) e = i.next();
418     //             K key = e.getKey();
419     //             V value = e.getValue();
420     //             if (value is null) {
421     //                 if (!(m.get(key)==null && m.containsKey(key)))
422     //                     return false;
423     //             } else {
424     //                 if (!value.equals(m.get(key)))
425     //                     return false;
426     //             }
427     //         }
428     //     } catch (ClassCastException unused) {
429     //         return false;
430     //     } catch (NullPointerException unused) {
431     //         return false;
432     //     }
433 
434     //     return true;
435     // }
436 
437     // Iterator!(MapEntry!(K,V)) iterator()
438     // {
439     //     throw new UnsupportedOperationException();
440     // }
441 
442     /**
443      * Returns the hash code value for this map.  The hash code of a map is
444      * defined to be the sum of the hash codes of each entry in the map's
445      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
446      * implies that <tt>m1.toHash()==m2.toHash()</tt> for any two maps
447      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
448      * {@link Object#toHash}.
449      *
450      * @implSpec
451      * This implementation iterates over <tt>entrySet()</tt>, calling
452      * {@link MapEntry#toHash toHash()} on each element (entry) in the
453      * set, and adding up the results.
454      *
455      * @return the hash code value for this map
456      * @see MapEntry#toHash()
457      * @see Object#equals(Object)
458      * @see Set#equals(Object)
459      */
460     override size_t toHash() @trusted nothrow {
461         size_t h = 0;
462         try {
463             foreach (MapEntry!(K, V) i; this) {
464                 h += i.toHash();
465             }
466             // Iterator!(MapEntry!(K,V)) i = this.iterator();
467             // while (i.hasNext())
468             //     h += i.next().toHash();
469         } catch (Exception ex) {
470         }
471         return h;
472     }
473 
474     /**
475      * Returns a string representation of this map.  The string representation
476      * consists of a list of key-value mappings in the order returned by the
477      * map's <tt>entrySet</tt> view's iterator, enclosed in braces
478      * (<tt>"{}"</tt>).  Adjacent mappings are separated by the characters
479      * <tt>", "</tt> (comma and space).  Each key-value mapping is rendered as
480      * the key followed by an equals sign (<tt>"="</tt>) followed by the
481      * associated value.  Keys and values are converted to strings as by
482      * {@link string#valueOf(Object)}.
483      *
484      * @return a string representation of this map
485      */
486     override string toString() {
487         if (isEmpty())
488             return "{}";
489 
490         Appender!string sb;
491         sb.put("{");
492         bool isFirst = true;
493         foreach (K key, V value; this) {
494             if (!isFirst) {
495                 sb.put(", ");
496             }
497             sb.put(key.to!string() ~ "=" ~ value.to!string());
498             isFirst = false;
499         }
500         sb.put("}");
501 
502         return sb.data;
503     }
504 
505     /**
506      * Returns a shallow copy of this <tt>AbstractMap</tt> instance: the keys
507      * and values themselves are not cloned.
508      *
509      * @return a shallow copy of this map
510      */
511     // protected Object clone() throws CloneNotSupportedException {
512     //     AbstractMap<?,?> result = (AbstractMap<?,?>)super.clone();
513     //     result.keySet = null;
514     //     result.values = null;
515     //     return result;
516     // }
517 
518     /**
519      * Utility method for SimpleEntry and SimpleImmutableEntry.
520      * Test for equality, checking for nulls.
521      *
522      * NB: Do not replace with Object.equals until JDK-8015417 is resolved.
523      */
524     // private static bool eq(Object o1, Object o2) {
525     //     return o1 is null ? o2 is null : o1.equals(o2);
526     // }
527 
528     // Implementation Note: SimpleEntry and SimpleImmutableEntry
529     // are distinct unrelated classes, even though they share
530     // some code. Since you can't add or subtract final-ness
531     // of a field in a subclass, they can't share representations,
532     // and the amount of duplicated code is too small to warrant
533     // exposing a common abstract class.
534 
535     /**
536      * An Entry maintaining a key and a value.  The value may be
537      * changed using the <tt>setValue</tt> method.  This class
538      * facilitates the process of building custom map
539      * implementations. For example, it may be convenient to return
540      * arrays of <tt>SimpleEntry</tt> instances in method
541      * <tt>Map.entrySet().toArray</tt>.
542      *
543      * @since 1.6
544      */
545     // static class SimpleEntry!(K,V)
546     //     implements Entry!(K,V), java.io.Serializable
547     // {
548     //     private static final long serialVersionUID = -8499721149061103585L;
549 
550     //     private final K key;
551     //     private V value;
552 
553     //     /**
554     //      * Creates an entry representing a mapping from the specified
555     //      * key to the specified value.
556     //      *
557     //      * @param key the key represented by this entry
558     //      * @param value the value represented by this entry
559     //      */
560     //     SimpleEntry(K key, V value) {
561     //         this.key   = key;
562     //         this.value = value;
563     //     }
564 
565     //     /**
566     //      * Creates an entry representing the same mapping as the
567     //      * specified entry.
568     //      *
569     //      * @param entry the entry to copy
570     //      */
571     //     SimpleEntry(Entry!(K, V) entry) {
572     //         this.key   = entry.getKey();
573     //         this.value = entry.getValue();
574     //     }
575 
576     //     /**
577     //      * Returns the key corresponding to this entry.
578     //      *
579     //      * @return the key corresponding to this entry
580     //      */
581     //     K getKey() {
582     //         return key;
583     //     }
584 
585     //     /**
586     //      * Returns the value corresponding to this entry.
587     //      *
588     //      * @return the value corresponding to this entry
589     //      */
590     //     V getValue() {
591     //         return value;
592     //     }
593 
594     //     /**
595     //      * Replaces the value corresponding to this entry with the specified
596     //      * value.
597     //      *
598     //      * @param value new value to be stored in this entry
599     //      * @return the old value corresponding to the entry
600     //      */
601     //     V setValue(V value) {
602     //         V oldValue = this.value;
603     //         this.value = value;
604     //         return oldValue;
605     //     }
606 
607     //     /**
608     //      * Compares the specified object with this entry for equality.
609     //      * Returns {@code true} if the given object is also a map entry and
610     //      * the two entries represent the same mapping.  More formally, two
611     //      * entries {@code e1} and {@code e2} represent the same mapping
612     //      * if<pre>
613     //      *   (e1.getKey()==null ?
614     //      *    e2.getKey()==null :
615     //      *    e1.getKey().equals(e2.getKey()))
616     //      *   &amp;&amp;
617     //      *   (e1.getValue()==null ?
618     //      *    e2.getValue()==null :
619     //      *    e1.getValue().equals(e2.getValue()))</pre>
620     //      * This ensures that the {@code equals} method works properly across
621     //      * different implementations of the {@code MapEntry} interface.
622     //      *
623     //      * @param o object to be compared for equality with this map entry
624     //      * @return {@code true} if the specified object is equal to this map
625     //      *         entry
626     //      * @see    #toHash
627     //      */
628     //     bool equals(Object o) {
629     //         if (!(o instanceof MapEntry))
630     //             return false;
631     //         MapEntry<?,?> e = (MapEntry<?,?>)o;
632     //         return eq(key, e.getKey()) && eq(value, e.getValue());
633     //     }
634 
635     //     /**
636     //      * Returns the hash code value for this map entry.  The hash code
637     //      * of a map entry {@code e} is defined to be: <pre>
638     //      *   (e.getKey()==null   ? 0 : e.getKey().toHash()) ^
639     //      *   (e.getValue()==null ? 0 : e.getValue().toHash())</pre>
640     //      * This ensures that {@code e1.equals(e2)} implies that
641     //      * {@code e1.toHash()==e2.toHash()} for any two Entries
642     //      * {@code e1} and {@code e2}, as required by the general
643     //      * contract of {@link Object#toHash}.
644     //      *
645     //      * @return the hash code value for this map entry
646     //      * @see    #equals
647     //      */
648     //     size_t toHash() @trusted nothrow {
649     //         return (key   is null ? 0 :   key.toHash()) ^
650     //                (value is null ? 0 : value.toHash());
651     //     }
652 
653     //     /**
654     //      * Returns a string representation of this map entry.  This
655     //      * implementation returns the string representation of this
656     //      * entry's key followed by the equals character ("<tt>=</tt>")
657     //      * followed by the string representation of this entry's value.
658     //      *
659     //      * @return a string representation of this map entry
660     //      */
661     //     string toString() {
662     //         return key ~ "=" ~ value;
663     //     }
664 
665     // }
666 }
667 
668 /**
669 * An Entry maintaining an immutable key and value.  This class
670 * does not support method <tt>setValue</tt>.  This class may be
671 * convenient in methods that return thread-safe snapshots of
672 * key-value mappings.
673 *
674 * @since 1.6
675 */
676 class SimpleImmutableEntry(K, V) : MapEntry!(K, V) {
677 
678     private K key;
679     private V value;
680 
681     /**
682         * Creates an entry representing a mapping from the specified
683         * key to the specified value.
684         *
685         * @param key the key represented by this entry
686         * @param value the value represented by this entry
687         */
688     this(K key, V value) {
689         this.key = key;
690         this.value = value;
691     }
692 
693     /**
694         * Creates an entry representing the same mapping as the
695         * specified entry.
696         *
697         * @param entry the entry to copy
698         */
699     this(MapEntry!(K, V) entry) {
700         this.key = entry.getKey();
701         this.value = entry.getValue();
702     }
703 
704     /**
705         * Returns the key corresponding to this entry.
706         *
707         * @return the key corresponding to this entry
708         */
709     K getKey() {
710         return key;
711     }
712 
713     /**
714         * Returns the value corresponding to this entry.
715         *
716         * @return the value corresponding to this entry
717         */
718     V getValue() {
719         return value;
720     }
721 
722     /**
723         * Replaces the value corresponding to this entry with the specified
724         * value (optional operation).  This implementation simply throws
725         * <tt>UnsupportedOperationException</tt>, as this class implements
726         * an <i>immutable</i> map entry.
727         *
728         * @param value new value to be stored in this entry
729         * @return (Does not return)
730         * @throws UnsupportedOperationException always
731         */
732     V setValue(V value) {
733         throw new UnsupportedOperationException();
734     }
735 
736     /**
737     * Compares the specified object with this entry for equality.
738     * Returns {@code true} if the given object is also a map entry and
739     * the two entries represent the same mapping.  More formally, two
740     * entries {@code e1} and {@code e2} represent the same mapping
741     * if<pre>
742     *   (e1.getKey()==null ?
743     *    e2.getKey()==null :
744     *    e1.getKey().equals(e2.getKey()))
745     *   &amp;&amp;
746     *   (e1.getValue()==null ?
747     *    e2.getValue()==null :
748     *    e1.getValue().equals(e2.getValue()))</pre>
749     * This ensures that the {@code equals} method works properly across
750     * different implementations of the {@code MapEntry} interface.
751     *
752     * @param o object to be compared for equality with this map entry
753     * @return {@code true} if the specified object is equal to this map
754     *         entry
755     * @see    #toHash
756     */
757     override bool opEquals(Object o) {
758         MapEntry!(K, V) e = cast(MapEntry!(K, V)) o;
759         if (e is null)
760             return false;
761         return key == e.getKey() && value == e.getValue();
762     }
763 
764     bool opEquals(IObject o) {
765         return opEquals(cast(Object) o);
766     }
767 
768     /**
769         * Returns the hash code value for this map entry.  The hash code
770         * of a map entry {@code e} is defined to be: <pre>
771         *   (e.getKey()==null   ? 0 : e.getKey().toHash()) ^
772         *   (e.getValue()==null ? 0 : e.getValue().toHash())</pre>
773         * This ensures that {@code e1.equals(e2)} implies that
774         * {@code e1.toHash()==e2.toHash()} for any two Entries
775         * {@code e1} and {@code e2}, as required by the general
776         * contract of {@link Object#toHash}.
777         *
778         * @return the hash code value for this map entry
779         * @see    #equals
780         */
781     override size_t toHash() @trusted nothrow {
782         static if (is(K == class)) {
783             size_t kHash = 0;
784             if (key !is null)
785                 kHash = key.toHash();
786         } else {
787             size_t kHash = hashOf(key);
788         }
789 
790         static if (is(V == class)) {
791             size_t vHash = 0;
792             if (value !is null)
793                 vHash = value.toHash();
794         } else {
795             size_t vHash = hashOf(value);
796         }
797 
798         return kHash ^ vHash;
799     }
800 
801     /**
802         * Returns a string representation of this map entry.  This
803         * implementation returns the string representation of this
804         * entry's key followed by the equals character ("<tt>=</tt>")
805         * followed by the string representation of this entry's value.
806         *
807         * @return a string representation of this map entry
808         */
809     override string toString() {
810         return key.to!string() ~ "=" ~ value.to!string();
811     }
812 
813 }
814 
815 /**
816 */
817 class EmptyMap(K, V) : AbstractMap!(K, V) {
818     // private enum long serialVersionUID = 6428348081105594320L;
819 
820     override int size() {
821         return 0;
822     }
823 
824     override bool isEmpty() {
825         return true;
826     }
827 
828     override bool containsKey(K key) {
829         return false;
830     }
831 
832     // override
833     // bool containsValue(V value) {return false;}
834 
835     override V get(K key) {
836         return V.init;
837     }
838 
839     override K[] keySet() {
840         return null;
841     }
842 
843     override V[] values() {
844         return null;
845     }
846     // Collection!(V) values()              {return emptySet();}
847     // Set!(MapEntry!(K,V)) entrySet()      {return emptySet();}
848 
849     override bool opEquals(Object o) {
850         return (typeid(o) == typeid(Map!(K, V))) && (cast(Map!(K, V)) o).isEmpty();
851     }
852 
853     override size_t toHash() {
854         return 0;
855     }
856 
857     // Override default methods in Map
858     override V getOrDefault(K k, V defaultValue) {
859         return defaultValue;
860     }
861 
862     override int opApply(scope int delegate(ref K, ref V) dg) {
863         return 0;
864     }
865 
866     // override
867     // void replaceAll(BiFunction!(K, V, V) function) {
868     //     Objects.requireNonNull(function);
869     // }
870 
871     // override
872     // V putIfAbsent(K key, V value) {
873     //     throw new UnsupportedOperationException();
874     // }
875 
876     // override
877     // bool remove(Object key, Object value) {
878     //     throw new UnsupportedOperationException();
879     // }
880 
881     // override
882     // bool replace(K key, V oldValue, V newValue) {
883     //     throw new UnsupportedOperationException();
884     // }
885 
886     // override
887     // V replace(K key, V value) {
888     //     throw new UnsupportedOperationException();
889     // }
890 
891     // override
892     // V computeIfAbsent(K key,
893     //         Function!(K, V) mappingFunction) {
894     //     throw new UnsupportedOperationException();
895     // }
896 
897     // override
898     // V computeIfPresent(K key,
899     //         BiFunction!(K, V, V) remappingFunction) {
900     //     throw new UnsupportedOperationException();
901     // }
902 
903     // override
904     // V compute(K key,
905     //         BiFunction!(K, V, V) remappingFunction) {
906     //     throw new UnsupportedOperationException();
907     // }
908 
909     // override
910     // V merge(K key, V value,
911     //         BiFunction!(V, V, V) remappingFunction) {
912     //     throw new UnsupportedOperationException();
913     // }
914 
915     // // Preserves singleton property
916     // private Object readResolve() {
917     //     return EMPTY_MAP;
918     // }
919 }