    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 *      https://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                values = list;
    068                iterator = list.listIterator();
    069            }
    070            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            final List<V> list = getMapping();
    130            if (list == null) {
    131                final List<V> newList = createCollection();
    132                newList.add(index, value);
    133                getMap().put(key, newList);
    134                return;
    135            }
    136            list.add(index, value);
    137        }
    138
    139        @Override
    140        public boolean addAll(final int index, final Collection<? extends V> c) {
    141            List<V> list = getMapping();
    142            if (list == null) {
    143                list = createCollection();
    144                final boolean changed = list.addAll(index, c);
    145                if (changed) {
    146                    getMap().put(key, list);
    147                }
    148                return changed;
    149            }
    150            return list.addAll(index, c);
    151        }
    152
    153        @Override
    154        public boolean equals(final Object other) {
    155            final List<V> list = getMapping();
    156            if (list == null) {
    157                return Collections.emptyList().equals(other);
    158            }
    159            if (!(other instanceof List)) {
    160                return false;
    161            }
    162            final List<?> otherList = (List<?>) other;
    163            return ListUtils.isEqualList(list, otherList);
    164        }
    165
    166        @Override
    167        public V get(final int index) {
    168            final List<V> list = ListUtils.emptyIfNull(getMapping());
    169            return list.get(index);
    170        }
    171
    172        @Override
    173        protected List<V> getMapping() {
    174            return getMap().get(key);
    175        }
    176
    177        @Override
    178        public int hashCode() {
    179            final List<V> list = getMapping();
    180            return ListUtils.hashCodeForList(list);
    181        }
    182
    183        @Override
    184        public int indexOf(final Object o) {
    185            final List<V> list = ListUtils.emptyIfNull(getMapping());
    186            return list.indexOf(o);
    187        }
    188
    189        @Override
    190        public int lastIndexOf(final Object o) {
    191            final List<V> list = ListUtils.emptyIfNull(getMapping());
    192            return list.lastIndexOf(o);
    193        }
    194
    195        @Override
    196        public ListIterator<V> listIterator() {
    197            return new ValuesListIterator(key);
    198        }
    199
    200        @Override
    201        public ListIterator<V> listIterator(final int index) {
    202            return new ValuesListIterator(key, index);
    203        }
    204
    205        @Override
    206        public V remove(final int index) {
    207            final List<V> list = ListUtils.emptyIfNull(getMapping());
    208            final V value = list.remove(index);
    209            if (list.isEmpty()) {
    210                AbstractListValuedMap.this.remove(key);
    211            }
    212            return value;
    213        }
    214
    215        @Override
    216        public V set(final int index, final V value) {
    217            final List<V> list = ListUtils.emptyIfNull(getMapping());
    218            return list.set(index, value);
    219        }
    220
    221        @Override
    222        public List<V> subList(final int fromIndex, final int toIndex) {
    223            final List<V> list = ListUtils.emptyIfNull(getMapping());
    224            return list.subList(fromIndex, toIndex);
    225        }
    226
    227    }
    228
    229    /**
    230     * Constructor needed for subclass serialization.
    231     */
    232    protected AbstractListValuedMap() {
    233    }
    234
    235    /**
    236     * A constructor that wraps, not copies
    237     *
    238     * @param map  The map to wrap, must not be null
    239     * @throws NullPointerException if the map is null
    240     */
    241    protected AbstractListValuedMap(final Map<K, ? extends List<V>> map) {
    242        super(map);
    243    }
    244
    245    /**
    246     * Creates a new value collection using the provided factory.
    247     *
    248     * @return A new list
    249     */
    250    @Override
    251    protected abstract List<V> createCollection();
    252
    253    /**
    254     * Gets the list of values associated with the specified key. This would
    255     * return an empty list in case the mapping is not present
    256     *
    257     * @param key  The key to retrieve
    258     * @return The {@code List} of values, will return an empty {@link List} for no mapping
    259     */
    260    @Override
    261    public List<V> get(final K key) {
    262        return wrappedCollection(key);
    263    }
    264
    265    @Override
    266    @SuppressWarnings("unchecked")
    267    protected Map<K, List<V>> getMap() {
    268        return (Map<K, List<V>>) super.getMap();
    269    }
    270
    271    /**
    272     * Removes all values associated with the specified key.
    273     * <p>
    274     * A subsequent {@code get(Object)} would return an empty list.
    275     * </p>
    276     *
    277     * @param key  The key to remove values from
    278     * @return The {@code List} of values removed, will return an empty,
    279     *   unmodifiable list for no mapping found.
    280     */
    281    @Override
    282    public List<V> remove(final Object key) {
    283        return ListUtils.emptyIfNull(getMap().remove(key));
    284    }
    285
    286    @Override
    287    List<V> wrappedCollection(final K key) {
    288        return new WrappedList(key);
    289    }
    290
    291}