    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.util.Collection;
    020import java.util.Collections;
    021import java.util.List;
    022import java.util.ListIterator;
    023import java.util.Map;
    024
    025import org.apache.commons.collections4.ListUtils;
    026import org.apache.commons.collections4.ListValuedMap;
    027
    028/**
    029 * Abstract implementation of the {@link ListValuedMap} interface to simplify
    030 * the creation of subclass implementations.
    031 * <p>
    032 * Subclasses specify a Map implementation to use as the internal storage and
    033 * the List implementation to use as values.
    034 *
    035 * @param <K> the type of the keys in this map
    036 * @param <V> the type of the values in this map
    037 * @since 4.1
    038 */
    039public abstract class AbstractListValuedMap<K, V> extends AbstractMultiValuedMap<K, V>
    040        implements ListValuedMap<K, V> {
    041
    042    /**
    043     * Constructor needed for subclass serialisation.
    044     */
    045    protected AbstractListValuedMap() {
    046        super();
    047    }
    048
    049    /**
    050     * A constructor that wraps, not copies
    051     *
    052     * @param map  the map to wrap, must not be null
    053     * @throws NullPointerException if the map is null
    054     */
    055    protected AbstractListValuedMap(final Map<K, ? extends List<V>> map) {
    056        super(map);
    057    }
    058
    059    // -----------------------------------------------------------------------
    060    @Override
    061    @SuppressWarnings("unchecked")
    062    protected Map<K, List<V>> getMap() {
    063        return (Map<K, List<V>>) super.getMap();
    064    }
    065
    066    /**
    067     * Creates a new value collection using the provided factory.
    068     * @return a new list
    069     */
    070    @Override
    071    protected abstract List<V> createCollection();
    072
    073    // -----------------------------------------------------------------------
    074    /**
    075     * Gets the list of values associated with the specified key. This would
    076     * return an empty list in case the mapping is not present
    077     *
    078     * @param key  the key to retrieve
    079     * @return the {@code List} of values, will return an empty {@link List} for no mapping
    080     */
    081    @Override
    082    public List<V> get(final K key) {
    083        return wrappedCollection(key);
    084    }
    085
    086    @Override
    087    List<V> wrappedCollection(final K key) {
    088        return new WrappedList(key);
    089    }
    090
    091    /**
    092     * Removes all values associated with the specified key.
    093     * <p>
    094     * A subsequent <code>get(Object)</code> would return an empty list.
    095     *
    096     * @param key  the key to remove values from
    097     * @return the <code>List</code> of values removed, will return an empty,
    098     *   unmodifiable list for no mapping found.
    099     */
    100    @Override
    101    public List<V> remove(final Object key) {
    102        return ListUtils.emptyIfNull(getMap().remove(key));
    103    }
    104
    105    // -----------------------------------------------------------------------
    106    /**
    107     * Wrapped list to handle add and remove on the list returned by get(object)
    108     */
    109    private class WrappedList extends WrappedCollection implements List<V> {
    110
    111        public WrappedList(final K key) {
    112            super(key);
    113        }
    114
    115        @Override
    116        protected List<V> getMapping() {
    117            return getMap().get(key);
    118        }
    119
    120        @Override
    121        public void add(final int index, final V value) {
    122            List<V> list = getMapping();
    123            if (list == null) {
    124                list = createCollection();
    125                getMap().put(key, list);
    126            }
    127            list.add(index, value);
    128        }
    129
    130        @Override
    131        public boolean addAll(final int index, final Collection<? extends V> c) {
    132            List<V> list = getMapping();
    133            if (list == null) {
    134                list = createCollection();
    135                final boolean changed = list.addAll(index, c);
    136                if (changed) {
    137                    getMap().put(key, list);
    138                }
    139                return changed;
    140            }
    141            return list.addAll(index, c);
    142        }
    143
    144        @Override
    145        public V get(final int index) {
    146            final List<V> list = ListUtils.emptyIfNull(getMapping());
    147            return list.get(index);
    148        }
    149
    150        @Override
    151        public int indexOf(final Object o) {
    152            final List<V> list = ListUtils.emptyIfNull(getMapping());
    153            return list.indexOf(o);
    154        }
    155
    156        @Override
    157        public int lastIndexOf(final Object o) {
    158            final List<V> list = ListUtils.emptyIfNull(getMapping());
    159            return list.lastIndexOf(o);
    160        }
    161
    162        @Override
    163        public ListIterator<V> listIterator() {
    164            return new ValuesListIterator(key);
    165        }
    166
    167        @Override
    168        public ListIterator<V> listIterator(final int index) {
    169            return new ValuesListIterator(key, index);
    170        }
    171
    172        @Override
    173        public V remove(final int index) {
    174            final List<V> list = ListUtils.emptyIfNull(getMapping());
    175            final V value = list.remove(index);
    176            if (list.isEmpty()) {
    177                AbstractListValuedMap.this.remove(key);
    178            }
    179            return value;
    180        }
    181
    182        @Override
    183        public V set(final int index, final V value) {
    184            final List<V> list = ListUtils.emptyIfNull(getMapping());
    185            return list.set(index, value);
    186        }
    187
    188        @Override
    189        public List<V> subList(final int fromIndex, final int toIndex) {
    190            final List<V> list = ListUtils.emptyIfNull(getMapping());
    191            return list.subList(fromIndex, toIndex);
    192        }
    193
    194        @Override
    195        public boolean equals(final Object other) {
    196            final List<V> list = getMapping();
    197            if (list == null) {
    198                return Collections.emptyList().equals(other);
    199            }
    200            if (!(other instanceof List)) {
    201                return false;
    202            }
    203            final List<?> otherList = (List<?>) other;
    204            return ListUtils.isEqualList(list, otherList);
    205        }
    206
    207        @Override
    208        public int hashCode() {
    209            final List<V> list = getMapping();
    210            return ListUtils.hashCodeForList(list);
    211        }
    212
    213    }
    214
    215    /** Values ListIterator */
    216    private class ValuesListIterator implements ListIterator<V> {
    217
    218        private final K key;
    219        private List<V> values;
    220        private ListIterator<V> iterator;
    221
    222        public ValuesListIterator(final K key) {
    223            this.key = key;
    224            this.values = ListUtils.emptyIfNull(getMap().get(key));
    225            this.iterator = values.listIterator();
    226        }
    227
    228        public ValuesListIterator(final K key, final int index) {
    229            this.key = key;
    230            this.values = ListUtils.emptyIfNull(getMap().get(key));
    231            this.iterator = values.listIterator(index);
    232        }
    233
    234        @Override
    235        public void add(final V value) {
    236            if (getMap().get(key) == null) {
    237                final List<V> list = createCollection();
    238                getMap().put(key, list);
    239                this.values = list;
    240                this.iterator = list.listIterator();
    241            }
    242            this.iterator.add(value);
    243        }
    244
    245        @Override
    246        public boolean hasNext() {
    247            return iterator.hasNext();
    248        }
    249
    250        @Override
    251        public boolean hasPrevious() {
    252            return iterator.hasPrevious();
    253        }
    254
    255        @Override
    256        public V next() {
    257            return iterator.next();
    258        }
    259
    260        @Override
    261        public int nextIndex() {
    262            return iterator.nextIndex();
    263        }
    264
    265        @Override
    266        public V previous() {
    267            return iterator.previous();
    268        }
    269
    270        @Override
    271        public int previousIndex() {
    272            return iterator.previousIndex();
    273        }
    274
    275        @Override
    276        public void remove() {
    277            iterator.remove();
    278            if (values.isEmpty()) {
    279                getMap().remove(key);
    280            }
    281        }
    282
    283        @Override
    284        public void set(final V value) {
    285            iterator.set(value);
    286        }
    287
    288    }
    289
    290}