001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.collections4.multimap;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.io.ObjectOutputStream;
022import java.util.AbstractCollection;
023import java.util.AbstractMap;
024import java.util.AbstractSet;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.Iterator;
028import java.util.Map;
029import java.util.Map.Entry;
030import java.util.Objects;
031import java.util.Set;
032
033import org.apache.commons.collections4.CollectionUtils;
034import org.apache.commons.collections4.IteratorUtils;
035import org.apache.commons.collections4.MapIterator;
036import org.apache.commons.collections4.MultiSet;
037import org.apache.commons.collections4.MultiValuedMap;
038import org.apache.commons.collections4.Transformer;
039import org.apache.commons.collections4.iterators.AbstractIteratorDecorator;
040import org.apache.commons.collections4.iterators.EmptyMapIterator;
041import org.apache.commons.collections4.iterators.IteratorChain;
042import org.apache.commons.collections4.iterators.LazyIteratorChain;
043import org.apache.commons.collections4.iterators.TransformIterator;
044import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
045import org.apache.commons.collections4.keyvalue.UnmodifiableMapEntry;
046import org.apache.commons.collections4.multiset.AbstractMultiSet;
047import org.apache.commons.collections4.multiset.UnmodifiableMultiSet;
048
049/**
050 * Abstract implementation of the {@link MultiValuedMap} interface to simplify
051 * the creation of subclass implementations.
052 * <p>
053 * Subclasses specify a Map implementation to use as the internal storage.
054 * </p>
055 *
056 * @param <K> the type of the keys in this map
057 * @param <V> the type of the values in this map
058 * @since 4.1
059 */
060public abstract class AbstractMultiValuedMap<K, V> implements MultiValuedMap<K, V> {
061
062    /**
063     * Inner class that provides the AsMap view.
064     */
065    private final class AsMap extends AbstractMap<K, Collection<V>> {
066        final class AsMapEntrySet extends AbstractSet<Map.Entry<K, Collection<V>>> {
067
068            @Override
069            public void clear() {
070                AsMap.this.clear();
071            }
072
073            @Override
074            public boolean contains(final Object o) {
075                return map.entrySet().contains(o);
076            }
077
078            @Override
079            public Iterator<Map.Entry<K, Collection<V>>> iterator() {
080                return new AsMapEntrySetIterator(map.entrySet().iterator());
081            }
082
083            @Override
084            public boolean remove(final Object o) {
085                if (!contains(o)) {
086                    return false;
087                }
088                final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
089                AbstractMultiValuedMap.this.remove(entry.getKey());
090                return true;
091            }
092
093            @Override
094            public int size() {
095                return AsMap.this.size();
096            }
097        }
098
099        /**
100         * EntrySet iterator for the asMap view.
101         */
102        final class AsMapEntrySetIterator extends AbstractIteratorDecorator<Map.Entry<K, Collection<V>>> {
103
104            AsMapEntrySetIterator(final Iterator<Map.Entry<K, Collection<V>>> iterator) {
105                super(iterator);
106            }
107
108            @Override
109            public Map.Entry<K, Collection<V>> next() {
110                final Map.Entry<K, Collection<V>> entry = super.next();
111                final K key = entry.getKey();
112                return new UnmodifiableMapEntry<>(key, wrappedCollection(key));
113            }
114        }
115
116        final transient Map<K, Collection<V>> map;
117
118        AsMap(final Map<K, Collection<V>> map) {
119            this.map = map;
120        }
121
122        @Override
123        public void clear() {
124            AbstractMultiValuedMap.this.clear();
125        }
126
127        @Override
128        public boolean containsKey(final Object key) {
129            return map.containsKey(key);
130        }
131
132        @Override
133        public Set<Map.Entry<K, Collection<V>>> entrySet() {
134            return new AsMapEntrySet();
135        }
136
137        @Override
138        public boolean equals(final Object object) {
139            return this == object || map.equals(object);
140        }
141
142        @Override
143        public Collection<V> get(final Object key) {
144            final Collection<V> collection = map.get(key);
145            if (collection == null) {
146                return null;
147            }
148            @SuppressWarnings("unchecked")
149            final K k = (K) key;
150            return wrappedCollection(k);
151        }
152
153        @Override
154        public int hashCode() {
155            return map.hashCode();
156        }
157
158        @Override
159        public Set<K> keySet() {
160            return AbstractMultiValuedMap.this.keySet();
161        }
162
163        @Override
164        public Collection<V> remove(final Object key) {
165            final Collection<V> collection = map.remove(key);
166            if (collection == null) {
167                return null;
168            }
169
170            final Collection<V> output = createCollection();
171            output.addAll(collection);
172            collection.clear();
173            return output;
174        }
175
176        @Override
177        public int size() {
178            return map.size();
179        }
180
181        @Override
182        public String toString() {
183            return map.toString();
184        }
185    }
186
187    /**
188     * Inner class that provides the Entry<K, V> view
189     */
190    private final class EntryValues extends AbstractCollection<Entry<K, V>> {
191
192        @Override
193        public Iterator<Entry<K, V>> iterator() {
194            return new LazyIteratorChain<Entry<K, V>>() {
195
196                final Collection<K> keysCol = new ArrayList<>(getMap().keySet());
197                final Iterator<K> keyIterator = keysCol.iterator();
198
199                @Override
200                protected Iterator<? extends Entry<K, V>> nextIterator(final int count) {
201                    if (!keyIterator.hasNext()) {
202                        return null;
203                    }
204                    final K key = keyIterator.next();
205                    final Transformer<V, Entry<K, V>> entryTransformer = input -> new MultiValuedMapEntry(key, input);
206                    return new TransformIterator<>(new ValuesIterator(key), entryTransformer);
207                }
208            };
209        }
210
211        @Override
212        public int size() {
213            return AbstractMultiValuedMap.this.size();
214        }
215
216    }
217
218    /**
219     * Inner class that provides a MultiSet<K> keys view.
220     */
221    private final class KeysMultiSet extends AbstractMultiSet<K> {
222
223        private final class MapEntryTransformer implements Transformer<Map.Entry<K, Collection<V>>, MultiSet.Entry<K>> {
224
225            @Override
226            public MultiSet.Entry<K> transform(final Map.Entry<K, Collection<V>> mapEntry) {
227                return new AbstractMultiSet.AbstractEntry<K>() {
228
229                    @Override
230                    public int getCount() {
231                        return mapEntry.getValue().size();
232                    }
233
234                    @Override
235                    public K getElement() {
236                        return mapEntry.getKey();
237                    }
238                };
239            }
240        }
241
242        @Override
243        public boolean contains(final Object o) {
244            return getMap().containsKey(o);
245        }
246
247        @Override
248        protected Iterator<MultiSet.Entry<K>> createEntrySetIterator() {
249            final MapEntryTransformer transformer = new MapEntryTransformer();
250            return IteratorUtils.transformedIterator(map.entrySet().iterator(), transformer);
251        }
252
253        @Override
254        public int getCount(final Object object) {
255            int count = 0;
256            final Collection<V> col = AbstractMultiValuedMap.this.getMap().get(object);
257            if (col != null) {
258                count = col.size();
259            }
260            return count;
261        }
262
263        @Override
264        public boolean isEmpty() {
265            return getMap().isEmpty();
266        }
267
268        @Override
269        public int size() {
270            return AbstractMultiValuedMap.this.size();
271        }
272
273        @Override
274        protected int uniqueElements() {
275            return getMap().size();
276        }
277    }
278
279    /**
280     * Inner class for MultiValuedMap Entries.
281     */
282    private final class MultiValuedMapEntry extends AbstractMapEntry<K, V> {
283
284        MultiValuedMapEntry(final K key, final V value) {
285            super(key, value);
286        }
287
288        @Override
289        public V setValue(final V value) {
290            throw new UnsupportedOperationException();
291        }
292
293    }
294
295    /**
296     * Inner class for MapIterator.
297     */
298    private final class MultiValuedMapIterator implements MapIterator<K, V> {
299
300        private final Iterator<Entry<K, V>> it;
301
302        private Entry<K, V> current;
303
304        MultiValuedMapIterator() {
305            this.it = AbstractMultiValuedMap.this.entries().iterator();
306        }
307
308        @Override
309        public K getKey() {
310            if (current == null) {
311                throw new IllegalStateException();
312            }
313            return current.getKey();
314        }
315
316        @Override
317        public V getValue() {
318            if (current == null) {
319                throw new IllegalStateException();
320            }
321            return current.getValue();
322        }
323
324        @Override
325        public boolean hasNext() {
326            return it.hasNext();
327        }
328
329        @Override
330        public K next() {
331            current = it.next();
332            return current.getKey();
333        }
334
335        @Override
336        public void remove() {
337            it.remove();
338        }
339
340        @Override
341        public V setValue(final V value) {
342            if (current == null) {
343                throw new IllegalStateException();
344            }
345            return current.setValue(value);
346        }
347
348    }
349
350    /**
351     * Inner class that provides the values view.
352     */
353    private final class Values extends AbstractCollection<V> {
354        @Override
355        public void clear() {
356            AbstractMultiValuedMap.this.clear();
357        }
358
359        @Override
360        public Iterator<V> iterator() {
361            final IteratorChain<V> chain = new IteratorChain<>();
362            for (final K k : keySet()) {
363                chain.addIterator(new ValuesIterator(k));
364            }
365            return chain;
366        }
367
368        @Override
369        public int size() {
370            return AbstractMultiValuedMap.this.size();
371        }
372    }
373
374    /**
375     * Inner class that provides the values iterator.
376     */
377    private final class ValuesIterator implements Iterator<V> {
378        private final Object key;
379        private final Collection<V> values;
380        private final Iterator<V> iterator;
381
382        ValuesIterator(final Object key) {
383            this.key = key;
384            this.values = getMap().get(key);
385            this.iterator = values.iterator();
386        }
387
388        @Override
389        public boolean hasNext() {
390            return iterator.hasNext();
391        }
392
393        @Override
394        public V next() {
395            return iterator.next();
396        }
397
398        @Override
399        public void remove() {
400            iterator.remove();
401            if (values.isEmpty()) {
402                AbstractMultiValuedMap.this.remove(key);
403            }
404        }
405    }
406
407    /**
408     * Wrapped collection to handle add and remove on the collection returned
409     * by get(object).
410     * <p>
411     * Currently, the wrapped collection is not cached and has to be retrieved
412     * from the underlying map. This is safe, but not very efficient and
413     * should be improved in subsequent releases. For this purpose, the
414     * scope of this collection is set to package private to simplify later
415     * refactoring.
416     */
417    class WrappedCollection implements Collection<V> {
418
419        protected final K key;
420
421        WrappedCollection(final K key) {
422            this.key = key;
423        }
424
425        @Override
426        public boolean add(final V value) {
427            Collection<V> coll = getMapping();
428            if (coll == null) {
429                coll = createCollection();
430                AbstractMultiValuedMap.this.map.put(key, coll);
431            }
432            return coll.add(value);
433        }
434
435        @Override
436        public boolean addAll(final Collection<? extends V> other) {
437            Collection<V> coll = getMapping();
438            if (coll == null) {
439                coll = createCollection();
440                AbstractMultiValuedMap.this.map.put(key, coll);
441            }
442            return coll.addAll(other);
443        }
444
445        @Override
446        public void clear() {
447            final Collection<V> coll = getMapping();
448            if (coll != null) {
449                coll.clear();
450                AbstractMultiValuedMap.this.remove(key);
451            }
452        }
453
454        @Override
455        public boolean contains(final Object obj) {
456            final Collection<V> coll = getMapping();
457            return coll != null && coll.contains(obj);
458        }
459
460        @Override
461        public boolean containsAll(final Collection<?> other) {
462            final Collection<V> coll = getMapping();
463            return coll != null && coll.containsAll(other);
464        }
465
466        protected Collection<V> getMapping() {
467            return getMap().get(key);
468        }
469
470        @Override
471        public boolean isEmpty() {
472            final Collection<V> coll = getMapping();
473            return coll == null || coll.isEmpty();
474        }
475
476        @Override
477        public Iterator<V> iterator() {
478            final Collection<V> coll = getMapping();
479            if (coll == null) {
480                return IteratorUtils.EMPTY_ITERATOR;
481            }
482            return new ValuesIterator(key);
483        }
484
485        @Override
486        public boolean remove(final Object item) {
487            final Collection<V> coll = getMapping();
488            if (coll == null) {
489                return false;
490            }
491
492            final boolean result = coll.remove(item);
493            if (coll.isEmpty()) {
494                AbstractMultiValuedMap.this.remove(key);
495            }
496            return result;
497        }
498
499        @Override
500        public boolean removeAll(final Collection<?> c) {
501            final Collection<V> coll = getMapping();
502            if (coll == null) {
503                return false;
504            }
505
506            final boolean result = coll.removeAll(c);
507            if (coll.isEmpty()) {
508                AbstractMultiValuedMap.this.remove(key);
509            }
510            return result;
511        }
512
513        @Override
514        public boolean retainAll(final Collection<?> c) {
515            final Collection<V> coll = getMapping();
516            if (coll == null) {
517                return false;
518            }
519
520            final boolean result = coll.retainAll(c);
521            if (coll.isEmpty()) {
522                AbstractMultiValuedMap.this.remove(key);
523            }
524            return result;
525        }
526
527        @Override
528        public int size() {
529            final Collection<V> coll = getMapping();
530            return coll == null ? 0 : coll.size();
531        }
532
533        @Override
534        public Object[] toArray() {
535            final Collection<V> coll = getMapping();
536            if (coll == null) {
537                return CollectionUtils.EMPTY_COLLECTION.toArray();
538            }
539            return coll.toArray();
540        }
541
542        @Override
543        @SuppressWarnings("unchecked")
544        public <T> T[] toArray(final T[] a) {
545            final Collection<V> coll = getMapping();
546            if (coll == null) {
547                return (T[]) CollectionUtils.EMPTY_COLLECTION.toArray(a);
548            }
549            return coll.toArray(a);
550        }
551
552        @Override
553        public String toString() {
554            final Collection<V> coll = getMapping();
555            if (coll == null) {
556                return CollectionUtils.EMPTY_COLLECTION.toString();
557            }
558            return coll.toString();
559        }
560
561    }
562
563    /** The values view */
564    private transient Collection<V> valuesView;
565
566    /** The EntryValues view */
567    private transient EntryValues entryValuesView;
568
569    /** The KeyMultiSet view */
570    private transient MultiSet<K> keysMultiSetView;
571
572    /** The AsMap view */
573    private transient AsMap asMapView;
574
575    /** The map used to store the data */
576    private transient Map<K, Collection<V>> map;
577
578    /**
579     * Constructor needed for subclass serialization.
580     */
581    protected AbstractMultiValuedMap() {
582    }
583
584    /**
585     * Constructor that wraps (not copies).
586     *
587     * @param map  the map to wrap, must not be null
588     * @throws NullPointerException if the map is null
589     */
590    @SuppressWarnings("unchecked")
591    protected AbstractMultiValuedMap(final Map<K, ? extends Collection<V>> map) {
592        this.map = (Map<K, Collection<V>>) Objects.requireNonNull(map, "map");
593    }
594
595    @Override
596    public Map<K, Collection<V>> asMap() {
597        return asMapView != null ? asMapView : (asMapView = new AsMap(map));
598    }
599
600    @Override
601    public void clear() {
602        getMap().clear();
603    }
604
605    @Override
606    public boolean containsKey(final Object key) {
607        return getMap().containsKey(key);
608    }
609
610    @Override
611    public boolean containsMapping(final Object key, final Object value) {
612        final Collection<V> coll = getMap().get(key);
613        return coll != null && coll.contains(value);
614    }
615
616    @Override
617    public boolean containsValue(final Object value) {
618        return values().contains(value);
619    }
620
621    /**
622     * Creates a new Collection typed for a given subclass.
623     *
624     * @return a new Collection typed for a given subclass.
625     */
626    protected abstract Collection<V> createCollection();
627
628    /**
629     * Reads the map in using a custom routine.
630     *
631     * @param in the input stream
632     * @throws IOException any of the usual I/O related exceptions
633     * @throws ClassNotFoundException if the stream contains an object which class cannot be loaded
634     * @throws ClassCastException if the stream does not contain the correct objects
635     */
636    protected void doReadObject(final ObjectInputStream in)
637            throws IOException, ClassNotFoundException {
638        final int entrySize = in.readInt();
639        for (int i = 0; i < entrySize; i++) {
640            @SuppressWarnings("unchecked") // This will fail at runtime if the stream is incorrect
641            final K key = (K) in.readObject();
642            final Collection<V> values = get(key);
643            final int valueSize = in.readInt();
644            for (int j = 0; j < valueSize; j++) {
645                @SuppressWarnings("unchecked") // see above
646                final V value = (V) in.readObject();
647                values.add(value);
648            }
649        }
650    }
651
652    /**
653     * Writes the map out using a custom routine.
654     *
655     * @param out the output stream
656     * @throws IOException any of the usual I/O related exceptions
657     */
658    protected void doWriteObject(final ObjectOutputStream out) throws IOException {
659        out.writeInt(map.size());
660        for (final Map.Entry<K, Collection<V>> entry : map.entrySet()) {
661            out.writeObject(entry.getKey());
662            out.writeInt(entry.getValue().size());
663            for (final V value : entry.getValue()) {
664                out.writeObject(value);
665            }
666        }
667    }
668
669    @Override
670    public Collection<Entry<K, V>> entries() {
671        return entryValuesView != null ? entryValuesView : (entryValuesView = new EntryValues());
672    }
673
674    @Override
675    public boolean equals(final Object obj) {
676        if (this == obj) {
677            return true;
678        }
679        if (obj instanceof MultiValuedMap) {
680            return asMap().equals(((MultiValuedMap<?, ?>) obj).asMap());
681        }
682        return false;
683    }
684
685    /**
686     * Gets the collection of values associated with the specified key. This
687     * would return an empty collection in case the mapping is not present
688     *
689     * @param key the key to retrieve
690     * @return the {@code Collection} of values, will return an empty {@code Collection} for no mapping
691     */
692    @Override
693    public Collection<V> get(final K key) {
694        return wrappedCollection(key);
695    }
696
697    /**
698     * Gets the map being wrapped.
699     *
700     * @return the wrapped map
701     */
702    protected Map<K, ? extends Collection<V>> getMap() {
703        return map;
704    }
705
706    @Override
707    public int hashCode() {
708        return getMap().hashCode();
709    }
710
711    @Override
712    public boolean isEmpty() {
713        return getMap().isEmpty();
714    }
715
716    /**
717     * Returns a {@link MultiSet} view of the key mapping contained in this map.
718     * <p>
719     * Returns a MultiSet of keys with its values count as the count of the MultiSet.
720     * This multiset is backed by the map, so any changes in the map is reflected here.
721     * Any method which modifies this multiset like {@code add}, {@code remove},
722     * {@link Iterator#remove()} etc throws {@code UnsupportedOperationException}.
723     *
724     * @return a bag view of the key mapping contained in this map
725     */
726    @Override
727    public MultiSet<K> keys() {
728        if (keysMultiSetView == null) {
729            keysMultiSetView = UnmodifiableMultiSet.unmodifiableMultiSet(new KeysMultiSet());
730        }
731        return keysMultiSetView;
732    }
733
734    @Override
735    public Set<K> keySet() {
736        return getMap().keySet();
737    }
738
739    @Override
740    public MapIterator<K, V> mapIterator() {
741        if (isEmpty()) {
742            return EmptyMapIterator.emptyMapIterator();
743        }
744        return new MultiValuedMapIterator();
745    }
746
747    /**
748     * Adds the value to the collection associated with the specified key.
749     * <p>
750     * Unlike a normal {@code Map} the previous value is not replaced.
751     * Instead the new value is added to the collection stored against the key.
752     *
753     * @param key the key to store against
754     * @param value the value to add to the collection at the key
755     * @return the value added if the map changed and null if the map did not change
756     */
757    @Override
758    public boolean put(final K key, final V value) {
759        Collection<V> coll = getMap().get(key);
760        if (coll == null) {
761            coll = createCollection();
762            if (coll.add(value)) {
763                map.put(key, coll);
764                return true;
765            }
766            return false;
767        }
768        return coll.add(value);
769    }
770
771    /**
772     * Adds Iterable values to the collection associated with the specified key.
773     *
774     * @param key the key to store against
775     * @param values the values to add to the collection at the key, may not be null
776     * @return true if this map changed
777     * @throws NullPointerException if values is null
778     */
779    @Override
780    public boolean putAll(final K key, final Iterable<? extends V> values) {
781        Objects.requireNonNull(values, "values");
782
783        if (values instanceof Collection<?>) {
784            final Collection<? extends V> valueCollection = (Collection<? extends V>) values;
785            return !valueCollection.isEmpty() && get(key).addAll(valueCollection);
786        }
787        final Iterator<? extends V> it = values.iterator();
788        return it.hasNext() && CollectionUtils.addAll(get(key), it);
789    }
790
791    /**
792     * Copies all of the mappings from the specified map to this map. The effect
793     * of this call is equivalent to that of calling {@link #put(Object,Object)
794     * put(k, v)} on this map once for each mapping from key {@code k} to value
795     * {@code v} in the specified map. The behavior of this operation is
796     * undefined if the specified map is modified while the operation is in
797     * progress.
798     *
799     * @param map mappings to be stored in this map, may not be null
800     * @return true if the map changed as a result of this operation
801     * @throws NullPointerException if map is null
802     */
803    @Override
804    public boolean putAll(final Map<? extends K, ? extends V> map) {
805        Objects.requireNonNull(map, "map");
806        boolean changed = false;
807        for (final Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
808            changed |= put(entry.getKey(), entry.getValue());
809        }
810        return changed;
811    }
812
813    /**
814     * Copies all of the mappings from the specified MultiValuedMap to this map.
815     * The effect of this call is equivalent to that of calling
816     * {@link #put(Object,Object) put(k, v)} on this map once for each mapping
817     * from key {@code k} to value {@code v} in the specified map. The
818     * behavior of this operation is undefined if the specified map is modified
819     * while the operation is in progress.
820     *
821     * @param map mappings to be stored in this map, may not be null
822     * @return true if the map changed as a result of this operation
823     * @throws NullPointerException if map is null
824     */
825    @Override
826    public boolean putAll(final MultiValuedMap<? extends K, ? extends V> map) {
827        Objects.requireNonNull(map, "map");
828        boolean changed = false;
829        for (final Map.Entry<? extends K, ? extends V> entry : map.entries()) {
830            changed |= put(entry.getKey(), entry.getValue());
831        }
832        return changed;
833    }
834
835    /**
836     * Removes all values associated with the specified key.
837     * <p>
838     * A subsequent {@code get(Object)} would return an empty collection.
839     *
840     * @param key  the key to remove values from
841     * @return the {@code Collection} of values removed, will return an
842     *   empty, unmodifiable collection for no mapping found
843     */
844    @Override
845    public Collection<V> remove(final Object key) {
846        return CollectionUtils.emptyIfNull(getMap().remove(key));
847    }
848
849    /**
850     * Removes a specific key/value mapping from the multivalued map.
851     * <p>
852     * The value is removed from the collection mapped to the specified key.
853     * Other values attached to that key are unaffected.
854     * <p>
855     * If the last value for a key is removed, an empty collection would be
856     * returned from a subsequent {@link #get(Object)}.
857     *
858     * @param key the key to remove from
859     * @param value the value to remove
860     * @return true if the mapping was removed, false otherwise
861     */
862    @Override
863    public boolean removeMapping(final Object key, final Object value) {
864        final Collection<V> coll = getMap().get(key);
865        if (coll == null) {
866            return false;
867        }
868        final boolean changed = coll.remove(value);
869        if (coll.isEmpty()) {
870            getMap().remove(key);
871        }
872        return changed;
873    }
874
875    /**
876     * Sets the map being wrapped.
877     * <p>
878     * <strong>NOTE:</strong> this method should only be used during deserialization
879     *
880     * @param map the map to wrap
881     */
882    @SuppressWarnings("unchecked")
883    protected void setMap(final Map<K, ? extends Collection<V>> map) {
884        this.map = (Map<K, Collection<V>>) map;
885    }
886
887    /**
888     * {@inheritDoc}
889     * <p>
890     * This implementation does <strong>not</strong> cache the total size
891     * of the multivalued map, but rather calculates it by iterating
892     * over the entries of the underlying map.
893     */
894    @Override
895    public int size() {
896        // the total size should be cached to improve performance
897        // but this requires that all modifications of the multimap
898        // (including the wrapped collections and entry/value
899        // collections) are tracked.
900        int size = 0;
901        for (final Collection<V> col : getMap().values()) {
902            size += col.size();
903        }
904        return size;
905    }
906
907    @Override
908    public String toString() {
909        return getMap().toString();
910    }
911
912    /**
913     * Gets a collection containing all the values in the map.
914     * <p>
915     * Returns a collection containing all the values from all keys.
916     *
917     * @return a collection view of the values contained in this map
918     */
919    @Override
920    public Collection<V> values() {
921        final Collection<V> vs = valuesView;
922        return vs != null ? vs : (valuesView = new Values());
923    }
924
925    Collection<V> wrappedCollection(final K key) {
926        return new WrappedCollection(key);
927    }
928
929}