001package ezvcard.util;
002
003import java.util.AbstractCollection;
004import java.util.ArrayList;
005import java.util.Collection;
006import java.util.Collections;
007import java.util.ConcurrentModificationException;
008import java.util.Iterator;
009import java.util.LinkedHashMap;
010import java.util.List;
011import java.util.ListIterator;
012import java.util.Map;
013import java.util.Map.Entry;
014import java.util.Set;
015
016/*
017 Copyright (c) 2012-2023, Michael Angstadt
018 All rights reserved.
019
020 Redistribution and use in source and binary forms, with or without
021 modification, are permitted provided that the following conditions are met: 
022
023 1. Redistributions of source code must retain the above copyright notice, this
024 list of conditions and the following disclaimer. 
025 2. Redistributions in binary form must reproduce the above copyright notice,
026 this list of conditions and the following disclaimer in the documentation
027 and/or other materials provided with the distribution. 
028
029 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
030 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
031 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
032 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
033 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
035 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
036 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
037 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
038 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
039
040 The views and conclusions contained in the software and documentation are those
041 of the authors and should not be interpreted as representing official policies, 
042 either expressed or implied, of the FreeBSD Project.
043 */
044
045/*
046 * Copyright (C) 2007 The Guava Authors
047 *
048 * Licensed under the Apache License, Version 2.0 (the "License");
049 * you may not use this file except in compliance with the License.
050 * You may obtain a copy of the License at
051 *
052 * http://www.apache.org/licenses/LICENSE-2.0
053 *
054 * Unless required by applicable law or agreed to in writing, software
055 * distributed under the License is distributed on an "AS IS" BASIS,
056 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
057 * See the License for the specific language governing permissions and
058 * limitations under the License.
059 */
060
061/**
062 * A multimap that uses {@link ArrayList} objects to store its values. The
063 * internal {@link Map} implementation is a {@link LinkedHashMap}.
064 * @author Michael Angstadt
065 * @param <K> the key
066 * @param <V> the value
067 */
068public class ListMultimap<K, V> implements Iterable<Map.Entry<K, List<V>>> {
069        private final Map<K, List<V>> map;
070
071        /**
072         * Creates an empty multimap.
073         */
074        public ListMultimap() {
075                this(new LinkedHashMap<>());
076        }
077
078        /**
079         * Creates an empty multimap.
080         * @param initialCapacity the initial capacity of the underlying map.
081         */
082        public ListMultimap(int initialCapacity) {
083                this(new LinkedHashMap<>(initialCapacity));
084        }
085
086        /**
087         * Creates a copy of an existing multimap.
088         * @param orig the multimap to copy from
089         */
090        public ListMultimap(ListMultimap<K, V> orig) {
091                this(copy(orig.map));
092        }
093
094        private static <K, V> Map<K, List<V>> copy(Map<K, List<V>> orig) {
095                Map<K, List<V>> map = new LinkedHashMap<>(orig.size());
096                for (Map.Entry<K, List<V>> entry : orig.entrySet()) {
097                        List<V> values = new ArrayList<>(entry.getValue());
098                        map.put(entry.getKey(), values);
099                }
100                return map;
101        }
102
103        /**
104         * <p>
105         * Creates a new multimap backed by the given map. Changes made to the given
106         * map will effect the multimap and vice versa.
107         * </p>
108         * <p>
109         * To avoid problems, it is highly recommended that the given map NOT be
110         * modified by anything other than this {@link ListMultimap} class after
111         * being passed into this constructor.
112         * </p>
113         * @param map the backing map
114         */
115        public ListMultimap(Map<K, List<V>> map) {
116                this.map = map;
117        }
118
119        /**
120         * Adds a value to the multimap.
121         * @param key the key
122         * @param value the value to add
123         */
124        public void put(K key, V value) {
125                key = sanitizeKey(key);
126                List<V> list = map.get(key);
127                if (list == null) {
128                        list = new ArrayList<>();
129                        map.put(key, list);
130                }
131                list.add(value);
132        }
133
134        /**
135         * Adds multiple values to the multimap.
136         * @param key the key
137         * @param values the values to add
138         */
139        public void putAll(K key, Collection<V> values) {
140                if (values.isEmpty()) {
141                        return;
142                }
143
144                key = sanitizeKey(key);
145                List<V> list = map.get(key);
146                if (list == null) {
147                        list = new ArrayList<>();
148                        map.put(key, list);
149                }
150                list.addAll(values);
151        }
152
153        /**
154         * Gets the values associated with the key. Changes to the returned list
155         * will update the underlying multimap, and vice versa.
156         * @param key the key
157         * @return the list of values or empty list if the key doesn't exist
158         */
159        public List<V> get(K key) {
160                key = sanitizeKey(key);
161                List<V> value = map.get(key);
162                if (value == null) {
163                        value = new ArrayList<>(0);
164                }
165                return new WrappedList(key, value, null);
166        }
167
168        /**
169         * Gets the first value that's associated with a key.
170         * @param key the key
171         * @return the first value or null if the key doesn't exist
172         */
173        public V first(K key) {
174                key = sanitizeKey(key);
175                List<V> values = map.get(key);
176
177                /*
178                 * The list can be null, but never empty. Empty lists are removed from
179                 * the map.
180                 */
181                return (values == null) ? null : values.get(0);
182        }
183
184        /**
185         * Determines whether the given key exists.
186         * @param key the key
187         * @return true if the key exists, false if not
188         */
189        public boolean containsKey(K key) {
190                key = sanitizeKey(key);
191                return map.containsKey(key);
192        }
193
194        /**
195         * Removes a particular value.
196         * @param key the key
197         * @param value the value to remove
198         * @return true if the multimap contained the value, false if not
199         */
200        public boolean remove(K key, V value) {
201                key = sanitizeKey(key);
202                List<V> values = map.get(key);
203                if (values == null) {
204                        return false;
205                }
206
207                boolean success = values.remove(value);
208                if (values.isEmpty()) {
209                        map.remove(key);
210                }
211                return success;
212        }
213
214        /**
215         * Removes all the values associated with a key
216         * @param key the key to remove
217         * @return the removed values or an empty list if the key doesn't exist
218         * (this list is immutable)
219         */
220        public List<V> removeAll(K key) {
221                key = sanitizeKey(key);
222                List<V> removed = map.remove(key);
223                if (removed == null) {
224                        return Collections.emptyList();
225                }
226
227                List<V> unmodifiableCopy = Collections.unmodifiableList(new ArrayList<>(removed));
228                removed.clear();
229                return unmodifiableCopy;
230        }
231
232        /**
233         * Replaces all values with the given value.
234         * @param key the key
235         * @param value the value with which to replace all existing values, or null
236         * to remove all values
237         * @return the values that were replaced (this list is immutable)
238         */
239        public List<V> replace(K key, V value) {
240                List<V> replaced = removeAll(key);
241                if (value != null) {
242                        put(key, value);
243                }
244                return replaced;
245        }
246
247        /**
248         * Replaces all values with the given values.
249         * @param key the key
250         * @param values the values with which to replace all existing values
251         * @return the values that were replaced (this list is immutable)
252         */
253        public List<V> replace(K key, Collection<V> values) {
254                List<V> replaced = removeAll(key);
255                putAll(key, values);
256                return replaced;
257        }
258
259        /**
260         * Clears all entries from the multimap.
261         */
262        public void clear() {
263                //clear each collection to make previously returned lists empty
264                for (List<V> value : map.values()) {
265                        value.clear();
266                }
267                map.clear();
268        }
269
270        /**
271         * Gets all the keys in the multimap.
272         * @return the keys (this set is immutable)
273         */
274        public Set<K> keySet() {
275                return Collections.unmodifiableSet(map.keySet());
276        }
277
278        /**
279         * Gets all the values in the multimap.
280         * @return the values (this list is immutable)
281         */
282        public List<V> values() {
283                List<V> list = new ArrayList<>();
284                for (List<V> value : map.values()) {
285                        list.addAll(value);
286                }
287                return Collections.unmodifiableList(list);
288        }
289
290        /**
291         * Determines if the multimap is empty or not.
292         * @return true if it's empty, false if not
293         */
294        public boolean isEmpty() {
295                return size() == 0;
296        }
297
298        /**
299         * Gets the number of values in the map.
300         * @return the number of values
301         */
302        public int size() {
303                int size = 0;
304                for (List<V> value : map.values()) {
305                        size += value.size();
306                }
307                return size;
308        }
309
310        /**
311         * Gets an immutable view of the underlying {@link Map} object.
312         * @return an immutable map
313         */
314        public Map<K, List<V>> asMap() {
315                Map<K, List<V>> view = new LinkedHashMap<>(map.size());
316                for (Map.Entry<K, List<V>> entry : map.entrySet()) {
317                        K key = entry.getKey();
318                        List<V> value = entry.getValue();
319                        view.put(key, Collections.unmodifiableList(value));
320                }
321                return Collections.unmodifiableMap(view);
322        }
323
324        /**
325         * Gets the {@link Map} that backs this multimap. This method is here for
326         * performances reasons. The returned map should NOT be modified by anything
327         * other than the {@link ListMultimap} object that owns it.
328         * @return the map
329         */
330        public Map<K, List<V>> getMap() {
331                return map;
332        }
333
334        /**
335         * Modifies a given key before it is used to interact with the internal map.
336         * This method is meant to be overridden by child classes if necessary.
337         * @param key the key
338         * @return the modified key (by default, the key is returned as-is)
339         */
340        protected K sanitizeKey(K key) {
341                return key;
342        }
343
344        /**
345         * Gets an iterator for iterating over the entries in the map. This iterator
346         * iterates over an immutable view of the map.
347         * @return the iterator
348         */
349        //@Override
350        public Iterator<Map.Entry<K, List<V>>> iterator() {
351                final Iterator<Map.Entry<K, List<V>>> it = map.entrySet().iterator();
352                return new Iterator<Map.Entry<K, List<V>>>() {
353                        public boolean hasNext() {
354                                return it.hasNext();
355                        }
356
357                        public Entry<K, List<V>> next() {
358                                final Entry<K, List<V>> next = it.next();
359                                return new Entry<K, List<V>>() {
360                                        public K getKey() {
361                                                return next.getKey();
362                                        }
363
364                                        public List<V> getValue() {
365                                                return Collections.unmodifiableList(next.getValue());
366                                        }
367
368                                        public List<V> setValue(List<V> value) {
369                                                throw new UnsupportedOperationException();
370                                        }
371                                };
372                        }
373
374                        public void remove() {
375                                throw new UnsupportedOperationException();
376                        }
377                };
378        }
379
380        @Override
381        public String toString() {
382                return map.toString();
383        }
384
385        @Override
386        public int hashCode() {
387                return map.hashCode();
388        }
389
390        @Override
391        public boolean equals(Object obj) {
392                if (this == obj) return true;
393                if (obj == null) return false;
394                if (getClass() != obj.getClass()) return false;
395
396                ListMultimap<?, ?> other = (ListMultimap<?, ?>) obj;
397                return map.equals(other.map);
398        }
399
400        /**
401         * Note: This class is a modified version of the
402         * "AbstractMapBasedMultimap.WrappedList" class from the <a
403         * href="https://github.com/google/guava">Guava</a> library.
404         * 
405         * <p>
406         * Collection decorator that stays in sync with the multimap values for a
407         * key. There are two kinds of wrapped collections: full and subcollections.
408         * Both have a delegate pointing to the underlying collection class.
409         *
410         * <p>
411         * Full collections, identified by a null ancestor field, contain all
412         * multimap values for a given key. Its delegate is a value in the
413         * multimap's underlying {@link Map} whenever the delegate is non-empty. The
414         * {@code refreshIfEmpty}, {@code removeIfEmpty}, and {@code addToMap}
415         * methods ensure that the {@code WrappedList} and map remain consistent.
416         *
417         * <p>
418         * A subcollection, such as a sublist, contains some of the values for a
419         * given key. Its ancestor field points to the full wrapped collection with
420         * all values for the key. The subcollection {@code refreshIfEmpty},
421         * {@code removeIfEmpty}, and {@code addToMap} methods call the
422         * corresponding methods of the full wrapped collection.
423         */
424        private class WrappedList extends AbstractCollection<V> implements List<V> {
425                final K key;
426                List<V> delegate;
427                final WrappedList ancestor;
428                final List<V> ancestorDelegate;
429
430                WrappedList(K key, List<V> delegate, WrappedList ancestor) {
431                        this.key = key;
432                        this.delegate = delegate;
433                        this.ancestor = ancestor;
434                        this.ancestorDelegate = (ancestor == null) ? null : ancestor.getDelegate();
435                }
436
437                public boolean addAll(int index, Collection<? extends V> collection) {
438                        if (collection.isEmpty()) {
439                                return false;
440                        }
441                        int oldSize = size(); // calls refreshIfEmpty
442                        boolean changed = getDelegate().addAll(index, collection);
443                        if (changed && oldSize == 0) {
444                                addToMap();
445                        }
446                        return changed;
447                }
448
449                public V get(int index) {
450                        refreshIfEmpty();
451                        return getDelegate().get(index);
452                }
453
454                public V set(int index, V element) {
455                        refreshIfEmpty();
456                        return getDelegate().set(index, element);
457                }
458
459                public void add(int index, V element) {
460                        refreshIfEmpty();
461                        boolean wasEmpty = getDelegate().isEmpty();
462                        getDelegate().add(index, element);
463                        if (wasEmpty) {
464                                addToMap();
465                        }
466                }
467
468                public V remove(int index) {
469                        refreshIfEmpty();
470                        V value = getDelegate().remove(index);
471                        removeIfEmpty();
472                        return value;
473                }
474
475                public int indexOf(Object o) {
476                        refreshIfEmpty();
477                        return getDelegate().indexOf(o);
478                }
479
480                public int lastIndexOf(Object o) {
481                        refreshIfEmpty();
482                        return getDelegate().lastIndexOf(o);
483                }
484
485                public ListIterator<V> listIterator() {
486                        refreshIfEmpty();
487                        return new WrappedListIterator();
488                }
489
490                public ListIterator<V> listIterator(int index) {
491                        refreshIfEmpty();
492                        return new WrappedListIterator(index);
493                }
494
495                public List<V> subList(int fromIndex, int toIndex) {
496                        refreshIfEmpty();
497                        return new WrappedList(getKey(), getDelegate().subList(fromIndex, toIndex), (getAncestor() == null) ? this : getAncestor());
498                }
499
500                /**
501                 * If the delegate collection is empty, but the multimap has values for
502                 * the key, replace the delegate with the new collection for the key.
503                 *
504                 * <p>
505                 * For a subcollection, refresh its ancestor and validate that the
506                 * ancestor delegate hasn't changed.
507                 */
508                void refreshIfEmpty() {
509                        if (ancestor != null) {
510                                ancestor.refreshIfEmpty();
511                                if (ancestor.getDelegate() != ancestorDelegate) {
512                                        throw new ConcurrentModificationException();
513                                }
514                        } else if (delegate.isEmpty()) {
515                                List<V> newDelegate = map.get(key);
516                                if (newDelegate != null) {
517                                        delegate = newDelegate;
518                                }
519                        }
520                }
521
522                /**
523                 * If collection is empty, remove it from
524                 * {@code AbstractMapBasedMultimap.this.map}. For subcollections, check
525                 * whether the ancestor collection is empty.
526                 */
527                void removeIfEmpty() {
528                        if (ancestor != null) {
529                                ancestor.removeIfEmpty();
530                        } else if (delegate.isEmpty()) {
531                                map.remove(key);
532                        }
533                }
534
535                K getKey() {
536                        return key;
537                }
538
539                /**
540                 * Add the delegate to the map. Other {@code WrappedCollection} methods
541                 * should call this method after adding elements to a previously empty
542                 * collection.
543                 *
544                 * <p>
545                 * Subcollection add the ancestor's delegate instead.
546                 */
547                void addToMap() {
548                        if (ancestor != null) {
549                                ancestor.addToMap();
550                        } else {
551                                map.put(key, delegate);
552                        }
553                }
554
555                @Override
556                public int size() {
557                        refreshIfEmpty();
558                        return delegate.size();
559                }
560
561                @Override
562                public boolean equals(Object object) {
563                        if (object == this) {
564                                return true;
565                        }
566                        refreshIfEmpty();
567                        return delegate.equals(object);
568                }
569
570                @Override
571                public int hashCode() {
572                        refreshIfEmpty();
573                        return delegate.hashCode();
574                }
575
576                @Override
577                public String toString() {
578                        refreshIfEmpty();
579                        return delegate.toString();
580                }
581
582                List<V> getDelegate() {
583                        return delegate;
584                }
585
586                @Override
587                public Iterator<V> iterator() {
588                        refreshIfEmpty();
589                        return new WrappedListIterator();
590                }
591
592                @Override
593                public boolean add(V value) {
594                        refreshIfEmpty();
595                        boolean wasEmpty = delegate.isEmpty();
596                        boolean changed = delegate.add(value);
597                        if (changed && wasEmpty) {
598                                addToMap();
599                        }
600                        return changed;
601                }
602
603                WrappedList getAncestor() {
604                        return ancestor;
605                }
606
607                // The following methods are provided for better performance.
608
609                @Override
610                public boolean addAll(Collection<? extends V> collection) {
611                        if (collection.isEmpty()) {
612                                return false;
613                        }
614                        int oldSize = size(); // calls refreshIfEmpty
615                        boolean changed = delegate.addAll(collection);
616                        if (changed && oldSize == 0) {
617                                addToMap();
618                        }
619                        return changed;
620                }
621
622                @Override
623                public boolean contains(Object o) {
624                        refreshIfEmpty();
625                        return delegate.contains(o);
626                }
627
628                @Override
629                public boolean containsAll(Collection<?> c) {
630                        refreshIfEmpty();
631                        return delegate.containsAll(c);
632                }
633
634                @Override
635                public void clear() {
636                        int oldSize = size(); // calls refreshIfEmpty
637                        if (oldSize == 0) {
638                                return;
639                        }
640                        delegate.clear();
641                        removeIfEmpty(); // maybe shouldn't be removed if this is a sublist
642                }
643
644                @Override
645                public boolean remove(Object o) {
646                        refreshIfEmpty();
647                        boolean changed = delegate.remove(o);
648                        if (changed) {
649                                removeIfEmpty();
650                        }
651                        return changed;
652                }
653
654                @Override
655                public boolean removeAll(Collection<?> collection) {
656                        if (collection.isEmpty()) {
657                                return false;
658                        }
659                        refreshIfEmpty();
660                        boolean changed = delegate.removeAll(collection);
661                        if (changed) {
662                                removeIfEmpty();
663                        }
664                        return changed;
665                }
666
667                @Override
668                public boolean retainAll(Collection<?> c) {
669                        refreshIfEmpty();
670                        boolean changed = delegate.retainAll(c);
671                        if (changed) {
672                                removeIfEmpty();
673                        }
674                        return changed;
675                }
676
677                /** ListIterator decorator. */
678                private class WrappedListIterator implements ListIterator<V> {
679                        final ListIterator<V> delegateIterator;
680                        final List<V> originalDelegate = delegate;
681
682                        WrappedListIterator() {
683                                delegateIterator = delegate.listIterator();
684                        }
685
686                        public WrappedListIterator(int index) {
687                                delegateIterator = delegate.listIterator(index);
688                        }
689
690                        public boolean hasPrevious() {
691                                return getDelegateIterator().hasPrevious();
692                        }
693
694                        public V previous() {
695                                return getDelegateIterator().previous();
696                        }
697
698                        public int nextIndex() {
699                                return getDelegateIterator().nextIndex();
700                        }
701
702                        public int previousIndex() {
703                                return getDelegateIterator().previousIndex();
704                        }
705
706                        public void set(V value) {
707                                getDelegateIterator().set(value);
708                        }
709
710                        public void add(V value) {
711                                boolean wasEmpty = isEmpty();
712                                getDelegateIterator().add(value);
713                                if (wasEmpty) {
714                                        addToMap();
715                                }
716                        }
717
718                        /**
719                         * If the delegate changed since the iterator was created, the
720                         * iterator is no longer valid.
721                         */
722                        void validateIterator() {
723                                refreshIfEmpty();
724                                if (delegate != originalDelegate) {
725                                        throw new ConcurrentModificationException();
726                                }
727                        }
728
729                        public boolean hasNext() {
730                                validateIterator();
731                                return delegateIterator.hasNext();
732                        }
733
734                        public V next() {
735                                validateIterator();
736                                return delegateIterator.next();
737                        }
738
739                        public void remove() {
740                                delegateIterator.remove();
741                                removeIfEmpty();
742                        }
743
744                        ListIterator<V> getDelegateIterator() {
745                                validateIterator();
746                                return delegateIterator;
747                        }
748                }
749        }
750}