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.Map;
13 
14 import hunt.collection.Collection;
15 import hunt.collection.Set;
16 import hunt.Exceptions;
17 import hunt.Functions;
18 import hunt.Object;
19 import hunt.util.Common;
20 
21 import std.range.interfaces : InputRange;
22 
23 
24 /**
25 */
26 interface Map(K,V) : Iterable!(K,V), IObject, Cloneable {
27     // Query Operations
28 
29     /**
30      * Returns the number of key-value mappings in this map.  If the
31      * map contains more than <tt>Integer.MAX_VALUE</tt> elements, returns
32      * <tt>Integer.MAX_VALUE</tt>.
33      *
34      * @return the number of key-value mappings in this map
35      */
36     int size();
37 
38     /**
39      * Returns <tt>true</tt> if this map contains no key-value mappings.
40      *
41      * @return <tt>true</tt> if this map contains no key-value mappings
42      */
43     bool isEmpty();
44 
45     /**
46      * Returns <tt>true</tt> if this map contains a mapping for the specified
47      * key.  More formally, returns <tt>true</tt> if and only if
48      * this map contains a mapping for a key <tt>k</tt> such that
49      * <tt>(key==null ? k==null : key.equals(k))</tt>.  (There can be
50      * at most one such mapping.)
51      *
52      * @param key key whose presence in this map is to be tested
53      * @return <tt>true</tt> if this map contains a mapping for the specified
54      *         key
55      * @throws ClassCastException if the key is of an inappropriate type for
56      *         this map
57      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
58      * @throws NullPointerException if the specified key is null and this map
59      *         does not permit null keys
60      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
61      */
62     bool containsKey(K key);
63 
64     /**
65      * Returns <tt>true</tt> if this map maps one or more keys to the
66      * specified value.  More formally, returns <tt>true</tt> if and only if
67      * this map contains at least one mapping to a value <tt>v</tt> such that
68      * <tt>(value==null ? v==null : value.equals(v))</tt>.  This operation
69      * will probably require time linear in the map size for most
70      * implementations of the <tt>Map</tt> interface.
71      *
72      * @param value value whose presence in this map is to be tested
73      * @return <tt>true</tt> if this map maps one or more keys to the
74      *         specified value
75      * @throws ClassCastException if the value is of an inappropriate type for
76      *         this map
77      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
78      * @throws NullPointerException if the specified value is null and this
79      *         map does not permit null values
80      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
81      */
82     bool containsValue(V value);
83 
84     /**
85      * Returns the value to which the specified key is mapped,
86      * or {@code null} if this map contains no mapping for the key.
87      *
88      * <p>More formally, if this map contains a mapping from a key
89      * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
90      * key.equals(k))}, then this method returns {@code v}; otherwise
91      * it returns {@code null}.  (There can be at most one such mapping.)
92      *
93      * <p>If this map permits null values, then a return value of
94      * {@code null} does not <i>necessarily</i> indicate that the map
95      * contains no mapping for the key; it's also possible that the map
96      * explicitly maps the key to {@code null}.  The {@link #containsKey
97      * containsKey} operation may be used to distinguish these two cases.
98      *
99      * @param key the key whose associated value is to be returned
100      * @return the value to which the specified key is mapped, or
101      *         {@code null} if this map contains no mapping for the key
102      * @throws ClassCastException if the key is of an inappropriate type for
103      *         this map
104      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
105      * @throws NullPointerException if the specified key is null and this map
106      *         does not permit null keys
107      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
108      */
109     V get(K key);
110 
111     V opIndex(K key);
112 
113     // Modification Operations
114 
115     /**
116      * Associates the specified value with the specified key in this map
117      * (optional operation).  If the map previously contained a mapping for
118      * the key, the old value is replaced by the specified value.  (A map
119      * <tt>m</tt> is said to contain a mapping for a key <tt>k</tt> if and only
120      * if {@link #containsKey(Object) m.containsKey(k)} would return
121      * <tt>true</tt>.)
122      *
123      * @param key key with which the specified value is to be associated
124      * @param value value to be associated with the specified key
125      * @return the previous value associated with <tt>key</tt>, or
126      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
127      *         (A <tt>null</tt> return can also indicate that the map
128      *         previously associated <tt>null</tt> with <tt>key</tt>,
129      *         if the implementation supports <tt>null</tt> values.)
130      * @throws UnsupportedOperationException if the <tt>put</tt> operation
131      *         is not supported by this map
132      * @throws ClassCastException if the class of the specified key or value
133      *         prevents it from being stored in this map
134      * @throws NullPointerException if the specified key or value is null
135      *         and this map does not permit null keys or values
136      * @throws IllegalArgumentException if some property of the specified key
137      *         or value prevents it from being stored in this map
138      */
139     V put(K key, V value);
140 
141     /**
142      * Removes the mapping for a key from this map if it is present
143      * (optional operation).   More formally, if this map contains a mapping
144      * from key <tt>k</tt> to value <tt>v</tt> such that
145      * <code>(key==null ?  k==null : key.equals(k))</code>, that mapping
146      * is removed.  (The map can contain at most one such mapping.)
147      *
148      * <p>Returns the value to which this map previously associated the key,
149      * or <tt>null</tt> if the map contained no mapping for the key.
150      *
151      * <p>If this map permits null values, then a return value of
152      * <tt>null</tt> does not <i>necessarily</i> indicate that the map
153      * contained no mapping for the key; it's also possible that the map
154      * explicitly mapped the key to <tt>null</tt>.
155      *
156      * <p>The map will not contain a mapping for the specified key once the
157      * call returns.
158      *
159      * @param key key whose mapping is to be removed from the map
160      * @return the previous value associated with <tt>key</tt>, or
161      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
162      * @throws UnsupportedOperationException if the <tt>remove</tt> operation
163      *         is not supported by this map
164      * @throws ClassCastException if the key is of an inappropriate type for
165      *         this map
166      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
167      * @throws NullPointerException if the specified key is null and this
168      *         map does not permit null keys
169      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
170      */
171     V remove(K key);
172 
173 
174     // Bulk Operations
175 
176     /**
177      * Copies all of the mappings from the specified map to this map
178      * (optional operation).  The effect of this call is equivalent to that
179      * of calling {@link #put(Object,Object) put(k, v)} on this map once
180      * for each mapping from key <tt>k</tt> to value <tt>v</tt> in the
181      * specified map.  The behavior of this operation is undefined if the
182      * specified map is modified while the operation is in progress.
183      *
184      * @param m mappings to be stored in this map
185      * @throws UnsupportedOperationException if the <tt>putAll</tt> operation
186      *         is not supported by this map
187      * @throws ClassCastException if the class of a key or value in the
188      *         specified map prevents it from being stored in this map
189      * @throws NullPointerException if the specified map is null, or if
190      *         this map does not permit null keys or values, and the
191      *         specified map contains null keys or values
192      * @throws IllegalArgumentException if some property of a key or value in
193      *         the specified map prevents it from being stored in this map
194      */
195     void putAll(Map!(K, V) m);
196 
197     /**
198      * Removes all of the mappings from this map (optional operation).
199      * The map will be empty after this call returns.
200      *
201      * @throws UnsupportedOperationException if the <tt>clear</tt> operation
202      *         is not supported by this map
203      */
204     void clear();
205 
206     // Views
207 
208     /**
209      * Returns a {@link Collection} view of the values contained in this map.
210      * The collection is backed by the map, so changes to the map are
211      * reflected in the collection, and vice-versa.  If the map is
212      * modified while an iteration over the collection is in progress
213      * (except through the iterator's own <tt>remove</tt> operation),
214      * the results of the iteration are undefined.  The collection
215      * supports element removal, which removes the corresponding
216      * mapping from the map, via the <tt>Iterator.remove</tt>,
217      * <tt>Collection.remove</tt>, <tt>removeAll</tt>,
218      * <tt>retainAll</tt> and <tt>clear</tt> operations.  It does not
219      * support the <tt>add</tt> or <tt>addAll</tt> operations.
220      *
221      * @return a collection view of the values contained in this map
222      */
223     // Collection!V values();
224     V[] values();
225 
226     string toString();
227 
228     
229     // Comparison and hashing
230 
231     /**
232      * Compares the specified object with this map for equality.  Returns
233      * <tt>true</tt> if the given object is also a map and the two maps
234      * represent the same mappings.  More formally, two maps <tt>m1</tt> and
235      * <tt>m2</tt> represent the same mappings if
236      * <tt>m1.entrySet().equals(m2.entrySet())</tt>.  This ensures that the
237      * <tt>equals</tt> method works properly across different implementations
238      * of the <tt>Map</tt> interface.
239      *
240      * @param o object to be compared for equality with this map
241      * @return <tt>true</tt> if the specified object is equal to this map
242      */
243     // bool opEquals(Object o);
244 
245     /**
246      * Returns the hash code value for this map.  The hash code of a map is
247      * defined to be the sum of the hash codes of each entry in the map's
248      * <tt>entrySet()</tt> view.  This ensures that <tt>m1.equals(m2)</tt>
249      * implies that <tt>m1.toHash()==m2.toHash()</tt> for any two maps
250      * <tt>m1</tt> and <tt>m2</tt>, as required by the general contract of
251      * {@link Object#toHash}.
252      *
253      * @return the hash code value for this map
254      * @see MapEntry#toHash()
255      * @see Object#equals(Object)
256      * @see #equals(Object)
257      */
258     size_t toHash() @trusted nothrow;
259 
260     // Defaultable methods
261 
262     /**
263      * Returns the value to which the specified key is mapped, or
264      * {@code defaultValue} if this map contains no mapping for the key.
265      *
266      * @implSpec
267      * The final implementation makes no guarantees about synchronization
268      * or atomicity properties of this method. Any implementation providing
269      * atomicity guarantees must override this method and document its
270      * concurrency properties.
271      *
272      * @param key the key whose associated value is to be returned
273      * @param defaultValue the final mapping of the key
274      * @return the value to which the specified key is mapped, or
275      * {@code defaultValue} if this map contains no mapping for the key
276      * @throws ClassCastException if the key is of an inappropriate type for
277      * this map
278      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
279      * @throws NullPointerException if the specified key is null and this map
280      * does not permit null keys
281      * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
282      */
283     // final V getOrDefault(K key, V defaultValue) {
284     //     V v;
285     //     static if(is(V == class))
286     //     {            
287     //         return (((v = get(key)) !is null) || containsKey(key))
288     //             ? v
289     //             : defaultValue;
290     //     }
291     //     else
292     //     {
293     //         return containsKey(key) ? v : defaultValue;
294     //     }
295     // }
296 
297     /**
298      * Performs the given action for each entry in this map until all entries
299      * have been processed or the action throws an exception.   Unless
300      * otherwise specified by the implementing class, actions are performed in
301      * the order of entry set iteration (if an iteration order is specified.)
302      * Exceptions thrown by the action are relayed to the caller.
303      *
304      * @implSpec
305      * The final implementation is equivalent to, for this {@code map}:
306      * <pre> {@code
307      * for (MapEntry<K, V> entry : map.entrySet())
308      *     action.accept(entry.getKey(), entry.getValue());
309      * }</pre>
310      *
311      * The final implementation makes no guarantees about synchronization
312      * or atomicity properties of this method. Any implementation providing
313      * atomicity guarantees must override this method and document its
314      * concurrency properties.
315      *
316      * @param action The action to be performed for each entry
317      * @throws NullPointerException if the specified action is null
318      * @throws ConcurrentModificationException if an entry is found to be
319      * removed during iteration
320      */
321     int opApply(scope int delegate(ref K, ref V) dg);
322 
323     /// ditto
324     int opApply(scope int delegate(MapEntry!(K, V) entry) dg);
325 
326     InputRange!K byKey();
327 
328     InputRange!V byValue();
329 
330     /**
331      * Replaces each entry's value with the result of invoking the given
332      * function on that entry until all entries have been processed or the
333      * function throws an exception.  Exceptions thrown by the function are
334      * relayed to the caller.
335      *
336      * @implSpec
337      * <p>The final implementation is equivalent to, for this {@code map}:
338      * <pre> {@code
339      * for (MapEntry<K, V> entry : map.entrySet())
340      *     entry.setValue(function.apply(entry.getKey(), entry.getValue()));
341      * }</pre>
342      *
343      * <p>The final implementation makes no guarantees about synchronization
344      * or atomicity properties of this method. Any implementation providing
345      * atomicity guarantees must override this method and document its
346      * concurrency properties.
347      *
348      * @param function the function to apply to each entry
349      * @throws UnsupportedOperationException if the {@code set} operation
350      * is not supported by this map's entry set iterator.
351      * @throws ClassCastException if the class of a replacement value
352      * prevents it from being stored in this map
353      * @throws NullPointerException if the specified function is null, or the
354      * specified replacement value is null, and this map does not permit null
355      * values
356      * @throws ClassCastException if a replacement value is of an inappropriate
357      *         type for this map
358      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
359      * @throws NullPointerException if function or a replacement value is null,
360      *         and this map does not permit null keys or values
361      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
362      * @throws IllegalArgumentException if some property of a replacement value
363      *         prevents it from being stored in this map
364      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
365      * @throws ConcurrentModificationException if an entry is found to be
366      * removed during iteration
367      */
368     // final void replaceAll(BiFunction<K, V, V> function) {
369     //     Objects.requireNonNull(function);
370     //     for (MapEntry<K, V> entry : entrySet()) {
371     //         K k;
372     //         V v;
373     //         try {
374     //             k = entry.getKey();
375     //             v = entry.getValue();
376     //         } catch(IllegalStateException ise) {
377     //             // this usually means the entry is no longer in the map.
378     //             throw new ConcurrentModificationException(ise);
379     //         }
380 
381     //         // ise thrown from function is not a cme.
382     //         v = function.apply(k, v);
383 
384     //         try {
385     //             entry.setValue(v);
386     //         } catch(IllegalStateException ise) {
387     //             // this usually means the entry is no longer in the map.
388     //             throw new ConcurrentModificationException(ise);
389     //         }
390     //     }
391     // }
392 
393     /**
394      * If the specified key is not already associated with a value (or is mapped
395      * to {@code null}) associates it with the given value and returns
396      * {@code null}, else returns the current value.
397      *
398      * @implSpec
399      * The final implementation is equivalent to, for this {@code
400      * map}:
401      *
402      * <pre> {@code
403      * V v = map.get(key);
404      * if (v is null)
405      *     v = map.put(key, value);
406      *
407      * return v;
408      * }</pre>
409      *
410      * <p>The final implementation makes no guarantees about synchronization
411      * or atomicity properties of this method. Any implementation providing
412      * atomicity guarantees must override this method and document its
413      * concurrency properties.
414      *
415      * @param key key with which the specified value is to be associated
416      * @param value value to be associated with the specified key
417      * @return the previous value associated with the specified key, or
418      *         {@code null} if there was no mapping for the key.
419      *         (A {@code null} return can also indicate that the map
420      *         previously associated {@code null} with the key,
421      *         if the implementation supports null values.)
422      * @throws UnsupportedOperationException if the {@code put} operation
423      *         is not supported by this map
424      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
425      * @throws ClassCastException if the key or value is of an inappropriate
426      *         type for this map
427      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
428      * @throws NullPointerException if the specified key or value is null,
429      *         and this map does not permit null keys or values
430      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
431      * @throws IllegalArgumentException if some property of the specified key
432      *         or value prevents it from being stored in this map
433      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
434      */
435     V putIfAbsent(K key, V value);
436 
437     /**
438      * Removes the entry for the specified key only if it is currently
439      * mapped to the specified value.
440      *
441      * @implSpec
442      * The final implementation is equivalent to, for this {@code map}:
443      *
444      * <pre> {@code
445      * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
446      *     map.remove(key);
447      *     return true;
448      * } else
449      *     return false;
450      * }</pre>
451      *
452      * <p>The final implementation makes no guarantees about synchronization
453      * or atomicity properties of this method. Any implementation providing
454      * atomicity guarantees must override this method and document its
455      * concurrency properties.
456      *
457      * @param key key with which the specified value is associated
458      * @param value value expected to be associated with the specified key
459      * @return {@code true} if the value was removed
460      * @throws UnsupportedOperationException if the {@code remove} operation
461      *         is not supported by this map
462      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
463      * @throws ClassCastException if the key or value is of an inappropriate
464      *         type for this map
465      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
466      * @throws NullPointerException if the specified key or value is null,
467      *         and this map does not permit null keys or values
468      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
469      */
470     bool remove(K key, V value);
471 
472 
473     /**
474      * Replaces the entry for the specified key only if currently
475      * mapped to the specified value.
476      *
477      * @implSpec
478      * The final implementation is equivalent to, for this {@code map}:
479      *
480      * <pre> {@code
481      * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
482      *     map.put(key, newValue);
483      *     return true;
484      * } else
485      *     return false;
486      * }</pre>
487      *
488      * The final implementation does not throw NullPointerException
489      * for maps that do not support null values if oldValue is null unless
490      * newValue is also null.
491      *
492      * <p>The final implementation makes no guarantees about synchronization
493      * or atomicity properties of this method. Any implementation providing
494      * atomicity guarantees must override this method and document its
495      * concurrency properties.
496      *
497      * @param key key with which the specified value is associated
498      * @param oldValue value expected to be associated with the specified key
499      * @param newValue value to be associated with the specified key
500      * @return {@code true} if the value was replaced
501      * @throws UnsupportedOperationException if the {@code put} operation
502      *         is not supported by this map
503      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
504      * @throws ClassCastException if the class of a specified key or value
505      *         prevents it from being stored in this map
506      * @throws NullPointerException if a specified key or newValue is null,
507      *         and this map does not permit null keys or values
508      * @throws NullPointerException if oldValue is null and this map does not
509      *         permit null values
510      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
511      * @throws IllegalArgumentException if some property of a specified key
512      *         or value prevents it from being stored in this map
513      */
514     bool replace(K key, V oldValue, V newValue);
515 
516     /**
517      * Replaces the entry for the specified key only if it is
518      * currently mapped to some value.
519      *
520      * @implSpec
521      * The final implementation is equivalent to, for this {@code map}:
522      *
523      * <pre> {@code
524      * if (map.containsKey(key)) {
525      *     return map.put(key, value);
526      * } else
527      *     return null;
528      * }</pre>
529      *
530      * <p>The final implementation makes no guarantees about synchronization
531      * or atomicity properties of this method. Any implementation providing
532      * atomicity guarantees must override this method and document its
533      * concurrency properties.
534       *
535      * @param key key with which the specified value is associated
536      * @param value value to be associated with the specified key
537      * @return the previous value associated with the specified key, or
538      *         {@code null} if there was no mapping for the key.
539      *         (A {@code null} return can also indicate that the map
540      *         previously associated {@code null} with the key,
541      *         if the implementation supports null values.)
542      * @throws UnsupportedOperationException if the {@code put} operation
543      *         is not supported by this map
544      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
545      * @throws ClassCastException if the class of the specified key or value
546      *         prevents it from being stored in this map
547      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
548      * @throws NullPointerException if the specified key or value is null,
549      *         and this map does not permit null keys or values
550      * @throws IllegalArgumentException if some property of the specified key
551      *         or value prevents it from being stored in this map
552      */
553     V replace(K key, V value);
554 
555     /**
556      * If the specified key is not already associated with a value (or is mapped
557      * to {@code null}), attempts to compute its value using the given mapping
558      * function and enters it into this map unless {@code null}.
559      *
560      * <p>If the function returns {@code null} no mapping is recorded. If
561      * the function itself throws an (unchecked) exception, the
562      * exception is rethrown, and no mapping is recorded.  The most
563      * common usage is to construct a new object serving as an initial
564      * mapped value or memoized result, as in:
565      *
566      * <pre> {@code
567      * map.computeIfAbsent(key, k -> new Value(f(k)));
568      * }</pre>
569      *
570      * <p>Or to implement a multi-value map, {@code Map<K,Collection!V>},
571      * supporting multiple values per key:
572      *
573      * <pre> {@code
574      * map.computeIfAbsent(key, k -> new HashSet!V()).add(v);
575      * }</pre>
576      *
577      *
578      * @implSpec
579      * The final implementation is equivalent to the following steps for this
580      * {@code map}, then returning the current value or {@code null} if now
581      * absent:
582      *
583      * <pre> {@code
584      * if (map.get(key) is null) {
585      *     V newValue = mappingFunction.apply(key);
586      *     if (newValue !is null)
587      *         map.put(key, newValue);
588      * }
589      * }</pre>
590      *
591      * <p>The final implementation makes no guarantees about synchronization
592      * or atomicity properties of this method. Any implementation providing
593      * atomicity guarantees must override this method and document its
594      * concurrency properties. In particular, all implementations of
595      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
596      * whether the function is applied once atomically only if the value is not
597      * present.
598      *
599      * @param key key with which the specified value is to be associated
600      * @param mappingFunction the function to compute a value
601      * @return the current (existing or computed) value associated with
602      *         the specified key, or null if the computed value is null
603      * @throws NullPointerException if the specified key is null and
604      *         this map does not support null keys, or the mappingFunction
605      *         is null
606      * @throws UnsupportedOperationException if the {@code put} operation
607      *         is not supported by this map
608      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
609      * @throws ClassCastException if the class of the specified key or value
610      *         prevents it from being stored in this map
611      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
612      */
613 
614     final V computeIfAbsent(K key, Function!(K, V) mappingFunction) {
615         assert(mappingFunction !is null);
616         
617         if (containsKey(key)) {
618             return get(key);
619         }
620         else {
621             V newValue = mappingFunction(key);
622             static if(is(V == class))
623             {
624                 if (newValue !is null) {
625                     put(key, newValue);
626                     return newValue;
627                 }
628                 else
629                     return V.init;
630             }
631             else
632             {
633                 put(key, newValue);
634                 return newValue;
635             }
636         }
637     }
638 
639 
640     /**
641      * If the value for the specified key is present and non-null, attempts to
642      * compute a new mapping given the key and its current mapped value.
643      *
644      * <p>If the function returns {@code null}, the mapping is removed.  If the
645      * function itself throws an (unchecked) exception, the exception is
646      * rethrown, and the current mapping is left unchanged.
647     *
648      * @implSpec
649      * The final implementation is equivalent to performing the following
650      * steps for this {@code map}, then returning the current value or
651      * {@code null} if now absent:
652      *
653      * <pre> {@code
654      * if (map.get(key) !is null) {
655      *     V oldValue = map.get(key);
656      *     V newValue = remappingFunction.apply(key, oldValue);
657      *     if (newValue !is null)
658      *         map.put(key, newValue);
659      *     else
660      *         map.remove(key);
661      * }
662      * }</pre>
663      *
664      * <p>The final implementation makes no guarantees about synchronization
665      * or atomicity properties of this method. Any implementation providing
666      * atomicity guarantees must override this method and document its
667      * concurrency properties. In particular, all implementations of
668      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
669      * whether the function is applied once atomically only if the value is not
670      * present.
671      *
672      * @param key key with which the specified value is to be associated
673      * @param remappingFunction the function to compute a value
674      * @return the new value associated with the specified key, or null if none
675      * @throws NullPointerException if the specified key is null and
676      *         this map does not support null keys, or the
677      *         remappingFunction is null
678      * @throws UnsupportedOperationException if the {@code put} operation
679      *         is not supported by this map
680      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
681      * @throws ClassCastException if the class of the specified key or value
682      *         prevents it from being stored in this map
683      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
684      */
685     // final V computeIfPresent(K key,
686     //         BiFunction<K, V, V> remappingFunction) {
687     //     Objects.requireNonNull(remappingFunction);
688     //     V oldValue;
689     //     if ((oldValue = get(key)) !is null) {
690     //         V newValue = remappingFunction.apply(key, oldValue);
691     //         if (newValue !is null) {
692     //             put(key, newValue);
693     //             return newValue;
694     //         } else {
695     //             remove(key);
696     //             return null;
697     //         }
698     //     } else {
699     //         return null;
700     //     }
701     // }
702 
703     /**
704      * Attempts to compute a mapping for the specified key and its current
705      * mapped value (or {@code null} if there is no current mapping). For
706      * example, to either create or append a {@code string} msg to a value
707      * mapping:
708      *
709      * <pre> {@code
710      * map.compute(key, (k, v) -> (v is null) ? msg : v.concat(msg))}</pre>
711      * (Method {@link #merge merge()} is often simpler to use for such purposes.)
712      *
713      * <p>If the function returns {@code null}, the mapping is removed (or
714      * remains absent if initially absent).  If the function itself throws an
715      * (unchecked) exception, the exception is rethrown, and the current mapping
716      * is left unchanged.
717      *
718      * @implSpec
719      * The final implementation is equivalent to performing the following
720      * steps for this {@code map}, then returning the current value or
721      * {@code null} if absent:
722      *
723      * <pre> {@code
724      * V oldValue = map.get(key);
725      * V newValue = remappingFunction.apply(key, oldValue);
726      * if (oldValue !is null ) {
727      *    if (newValue !is null)
728      *       map.put(key, newValue);
729      *    else
730      *       map.remove(key);
731      * } else {
732      *    if (newValue !is null)
733      *       map.put(key, newValue);
734      *    else
735      *       return null;
736      * }
737      * }</pre>
738      *
739      * <p>The final implementation makes no guarantees about synchronization
740      * or atomicity properties of this method. Any implementation providing
741      * atomicity guarantees must override this method and document its
742      * concurrency properties. In particular, all implementations of
743      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
744      * whether the function is applied once atomically only if the value is not
745      * present.
746      *
747      * @param key key with which the specified value is to be associated
748      * @param remappingFunction the function to compute a value
749      * @return the new value associated with the specified key, or null if none
750      * @throws NullPointerException if the specified key is null and
751      *         this map does not support null keys, or the
752      *         remappingFunction is null
753      * @throws UnsupportedOperationException if the {@code put} operation
754      *         is not supported by this map
755      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
756      * @throws ClassCastException if the class of the specified key or value
757      *         prevents it from being stored in this map
758      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
759      */
760     // final V compute(K key,
761     //         BiFunction<K, V, V> remappingFunction) {
762     //     Objects.requireNonNull(remappingFunction);
763     //     V oldValue = get(key);
764 
765     //     V newValue = remappingFunction.apply(key, oldValue);
766     //     if (newValue is null) {
767     //         // delete mapping
768     //         if (oldValue !is null || containsKey(key)) {
769     //             // something to remove
770     //             remove(key);
771     //             return null;
772     //         } else {
773     //             // nothing to do. Leave things as they were.
774     //             return null;
775     //         }
776     //     } else {
777     //         // add or replace old mapping
778     //         put(key, newValue);
779     //         return newValue;
780     //     }
781     // }
782 
783     /**
784      * If the specified key is not already associated with a value or is
785      * associated with null, associates it with the given non-null value.
786      * Otherwise, replaces the associated value with the results of the given
787      * remapping function, or removes if the result is {@code null}. This
788      * method may be of use when combining multiple mapped values for a key.
789      * For example, to either create or append a {@code string msg} to a
790      * value mapping:
791      *
792      * <pre> {@code
793      * map.merge(key, msg, string::concat)
794      * }</pre>
795      *
796      * <p>If the function returns {@code null} the mapping is removed.  If the
797      * function itself throws an (unchecked) exception, the exception is
798      * rethrown, and the current mapping is left unchanged.
799      *
800      * @implSpec
801      * The final implementation is equivalent to performing the following
802      * steps for this {@code map}, then returning the current value or
803      * {@code null} if absent:
804      *
805      * <pre> {@code
806      * V oldValue = map.get(key);
807      * V newValue = (oldValue is null) ? value :
808      *              remappingFunction.apply(oldValue, value);
809      * if (newValue is null)
810      *     map.remove(key);
811      * else
812      *     map.put(key, newValue);
813      * }</pre>
814      *
815      * <p>The final implementation makes no guarantees about synchronization
816      * or atomicity properties of this method. Any implementation providing
817      * atomicity guarantees must override this method and document its
818      * concurrency properties. In particular, all implementations of
819      * subinterface {@link hunt.concurrency.ConcurrentMap} must document
820      * whether the function is applied once atomically only if the value is not
821      * present.
822      *
823      * @param key key with which the resulting value is to be associated
824      * @param value the non-null value to be merged with the existing value
825      *        associated with the key or, if no existing value or a null value
826      *        is associated with the key, to be associated with the key
827      * @param remappingFunction the function to recompute a value if present
828      * @return the new value associated with the specified key, or null if no
829      *         value is associated with the key
830      * @throws UnsupportedOperationException if the {@code put} operation
831      *         is not supported by this map
832      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
833      * @throws ClassCastException if the class of the specified key or value
834      *         prevents it from being stored in this map
835      *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
836      * @throws NullPointerException if the specified key is null and this map
837      *         does not support null keys or the value or remappingFunction is
838      *         null
839      */
840     // final V merge(K key, V value,
841     //         BiFunction<V, V, V> remappingFunction) {
842     //     Objects.requireNonNull(remappingFunction);
843     //     Objects.requireNonNull(value);
844     //     V oldValue = get(key);
845     //     V newValue = (oldValue is null) ? value :
846     //                remappingFunction.apply(oldValue, value);
847     //     if(newValue is null) {
848     //         remove(key);
849     //     } else {
850     //         put(key, newValue);
851     //     }
852     //     return newValue;
853     // }
854 }
855 
856 
857 /* A map entry (key-value pair).  The <tt>Map.entrySet</tt> method returns
858  * a collection-view of the map, whose elements are of this class.  The
859  * <i>only</i> way to obtain a reference to a map entry is from the
860  * iterator of this collection-view.  These <tt>MapEntry</tt> objects are
861  * valid <i>only</i> for the duration of the iteration; more formally,
862  * the behavior of a map entry is undefined if the backing map has been
863  * modified after the entry was returned by the iterator, except through
864  * the <tt>setValue</tt> operation on the map entry.
865  *
866  * @see Map#entrySet()
867  */
868 interface MapEntry(K,V) : IObject, Comparable!(MapEntry!(K,V)) {
869     /**
870      * Returns the key corresponding to this entry.
871      *
872      * @return the key corresponding to this entry
873      * @throws IllegalStateException implementations may, but are not
874      *         required to, throw this exception if the entry has been
875      *         removed from the backing map.
876      */
877     K getKey();
878 
879     /**
880      * Returns the value corresponding to this entry.  If the mapping
881      * has been removed from the backing map (by the iterator's
882      * <tt>remove</tt> operation), the results of this call are undefined.
883      *
884      * @return the value corresponding to this entry
885      * @throws IllegalStateException implementations may, but are not
886      *         required to, throw this exception if the entry has been
887      *         removed from the backing map.
888      */
889     V getValue();
890 
891     /**
892      * Replaces the value corresponding to this entry with the specified
893      * value (optional operation).  (Writes through to the map.)  The
894      * behavior of this call is undefined if the mapping has already been
895      * removed from the map (by the iterator's <tt>remove</tt> operation).
896      *
897      * @param value new value to be stored in this entry
898      * @return old value corresponding to the entry
899      * @throws UnsupportedOperationException if the <tt>put</tt> operation
900      *         is not supported by the backing map
901      * @throws ClassCastException if the class of the specified value
902      *         prevents it from being stored in the backing map
903      * @throws NullPointerException if the backing map does not permit
904      *         null values, and the specified value is null
905      * @throws IllegalArgumentException if some property of this value
906      *         prevents it from being stored in the backing map
907      * @throws IllegalStateException implementations may, but are not
908      *         required to, throw this exception if the entry has been
909      *         removed from the backing map.
910      */
911     V setValue(V value);
912 
913     /**
914      * Compares the specified object with this entry for equality.
915      * Returns <tt>true</tt> if the given object is also a map entry and
916      * the two entries represent the same mapping.  More formally, two
917      * entries <tt>e1</tt> and <tt>e2</tt> represent the same mapping
918      * if<pre>
919      *     (e1.getKey()==null ?
920      *      e2.getKey()==null : e1.getKey().equals(e2.getKey()))  &amp;&amp;
921      *     (e1.getValue()==null ?
922      *      e2.getValue()==null : e1.getValue().equals(e2.getValue()))
923      * </pre>
924      * This ensures that the <tt>equals</tt> method works properly across
925      * different implementations of the <tt>MapEntry</tt> interface.
926      *
927      * @param o object to be compared for equality with this map entry
928      * @return <tt>true</tt> if the specified object is equal to this map
929      *         entry
930      */
931     // bool opEquals(MapEntry!(K,V) o);
932 
933     /**
934      * Returns the hash code value for this map entry.  The hash code
935      * of a map entry <tt>e</tt> is defined to be: <pre>
936      *     (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
937      *     (e.getValue()==null ? 0 : e.getValue().hashCode())
938      * </pre>
939      * This ensures that <tt>e1.equals(e2)</tt> implies that
940      * <tt>e1.hashCode()==e2.hashCode()</tt> for any two Entries
941      * <tt>e1</tt> and <tt>e2</tt>, as required by the general
942      * contract of <tt>Object.hashCode</tt>.
943      *
944      * @return the hash code value for this map entry
945      * @see Object#hashCode()
946      * @see Object#equals(Object)
947      * @see #equals(Object)
948      */
949     // size_t toHash() @trusted nothrow;
950 
951     /**
952      * Returns a comparator that compares {@link MapEntry} in natural order on key.
953      *
954      * <p>The returned comparator is serializable and throws {@link
955      * NullPointerException} when comparing an entry with a null key.
956      *
957      * @param  !K the {@link Comparable} type of then map keys
958      * @param  !V the type of the map values
959      * @return a comparator that compares {@link MapEntry} in natural order on key.
960      * @see Comparable
961      */
962     // public static <K extends Comparable<K>, V> Comparator<MapEntry!(K,V)> comparingByKey() {
963     //     return (Comparator<MapEntry<K, V>> & Serializable)
964     //         (c1, c2) -> c1.getKey().compareTo(c2.getKey());
965     // }
966 
967     /**
968      * Returns a comparator that compares {@link MapEntry} in natural order on value.
969      *
970      * <p>The returned comparator is serializable and throws {@link
971      * NullPointerException} when comparing an entry with null values.
972      *
973      * @param !K the type of the map keys
974      * @param !V the {@link Comparable} type of the map values
975      * @return a comparator that compares {@link MapEntry} in natural order on value.
976      * @see Comparable
977      */
978     // public static <K, V extends Comparable<V>> Comparator<MapEntry!(K,V)> comparingByValue() {
979     //     return (Comparator<MapEntry<K, V>> & Serializable)
980     //         (c1, c2) -> c1.getValue().compareTo(c2.getValue());
981     // }
982 
983     /**
984      * Returns a comparator that compares {@link MapEntry} by key using the given
985      * {@link Comparator}.
986      *
987      * <p>The returned comparator is serializable if the specified comparator
988      * is also serializable.
989      *
990      * @param  !K the type of the map keys
991      * @param  !V the type of the map values
992      * @param  cmp the key {@link Comparator}
993      * @return a comparator that compares {@link MapEntry} by the key.
994      */
995     // public static <K, V> Comparator<MapEntry<K, V>> comparingByKey(Comparator<K> cmp) {
996     //     Objects.requireNonNull(cmp);
997     //     return (Comparator<MapEntry<K, V>> & Serializable)
998     //         (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
999     // }
1000 
1001     /**
1002      * Returns a comparator that compares {@link MapEntry} by value using the given
1003      * {@link Comparator}.
1004      *
1005      * <p>The returned comparator is serializable if the specified comparator
1006      * is also serializable.
1007      *
1008      * @param  !K the type of the map keys
1009      * @param  !V the type of the map values
1010      * @param  cmp the value {@link Comparator}
1011      * @return a comparator that compares {@link MapEntry} by the value.
1012      */
1013     // public static <K, V> Comparator<MapEntry<K, V>> comparingByValue(Comparator<V> cmp) {
1014     //     Objects.requireNonNull(cmp);
1015     //     return (Comparator<MapEntry<K, V>> & Serializable)
1016     //         (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
1017     // }
1018 }
1019 
1020 import std.conv;
1021 abstract class AbstractMapEntry(K, V) : MapEntry!(K,V) {
1022     
1023     package K key;
1024     package V value;
1025 
1026     this(K key, V value) {
1027         this.key = key;
1028         this.value = value;
1029     }
1030 
1031     /**
1032     * Returns the key.
1033     *
1034     * @return the key
1035     */
1036     K getKey() {
1037         return key;
1038     }
1039 
1040     /**
1041     * Returns the value associated with the key.
1042     *
1043     * @return the value associated with the key
1044     */
1045     V getValue() {
1046         return value;
1047     }
1048 
1049     /**
1050     * Replaces the value currently associated with the key with the given
1051     * value.
1052     *
1053     * @return the value associated with the key before this method was
1054     *         called
1055     */
1056     V setValue(V value) {
1057         V oldValue = this.value;
1058         this.value = value;
1059         return oldValue;
1060     }
1061 
1062     /**
1063     * Returns a string representation of this map entry.  This
1064     * implementation returns the string representation of this
1065     * entry's key followed by the equals character ("<tt>=</tt>")
1066     * followed by the string representation of this entry's value.
1067     *
1068     * @return a string representation of this map entry
1069     */
1070     override string toString() {
1071         return key.to!string() ~ "=" ~ value.to!string();
1072     }
1073 
1074     /**
1075     * Compares the specified object with this entry for equality.
1076     * Returns {@code true} if the given object is also a map entry and
1077     * the two entries represent the same mapping.  More formally, two
1078     * entries {@code e1} and {@code e2} represent the same mapping
1079     * if<pre>
1080     *   (e1.getKey()==null ?
1081     *    e2.getKey()==null :
1082     *    e1.getKey().equals(e2.getKey()))
1083     *   &amp;&amp;
1084     *   (e1.getValue()==null ?
1085     *    e2.getValue()==null :
1086     *    e1.getValue().equals(e2.getValue()))</pre>
1087     * This ensures that the {@code equals} method works properly across
1088     * different implementations of the {@code MapEntry} interface.
1089     *
1090     * @param o object to be compared for equality with this map entry
1091     * @return {@code true} if the specified object is equal to this map
1092     *         entry
1093     * @see    #toHash
1094     */
1095     bool opEquals(IObject o) {
1096         return opEquals(cast(Object) o);
1097     }
1098 
1099     override bool opEquals(Object o) {
1100         if (o is this)
1101             return true;
1102             
1103         MapEntry!(K, V) e = cast(MapEntry!(K, V))o;
1104         if (e !is null) {
1105             if (key == e.getKey() && value == e.getValue())
1106                 return true;
1107         }
1108         return false;
1109     }
1110 
1111     int opCmp(MapEntry!(K, V) o) {
1112         throw new NotImplementedException("opCmp");
1113     }
1114 
1115     alias opCmp = Object.opCmp;
1116 
1117     override size_t toHash() @trusted nothrow {
1118         static if(is(V == interface)) {
1119             return hashOf(key) ^ hashOf(cast(Object)value);
1120         } else {
1121             return hashOf(key) ^ hashOf(value);
1122         }
1123     }
1124 }