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