View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.multimap;
18  
19  import java.util.Collection;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.ListIterator;
23  import java.util.Map;
24  
25  import org.apache.commons.collections4.ListUtils;
26  import org.apache.commons.collections4.ListValuedMap;
27  
28  /**
29   * Abstract implementation of the {@link ListValuedMap} interface to simplify
30   * the creation of subclass implementations.
31   * <p>
32   * Subclasses specify a Map implementation to use as the internal storage and
33   * the List implementation to use as values.
34   * </p>
35   *
36   * @param <K> the type of the keys in this map
37   * @param <V> the type of the values in this map
38   * @since 4.1
39   */
40  public abstract class AbstractListValuedMap<K, V> extends AbstractMultiValuedMap<K, V>
41          implements ListValuedMap<K, V> {
42  
43      /** Values ListIterator */
44      private final class ValuesListIterator implements ListIterator<V> {
45  
46          private final K key;
47          private List<V> values;
48          private ListIterator<V> iterator;
49  
50          ValuesListIterator(final K key) {
51              this.key = key;
52              this.values = ListUtils.emptyIfNull(getMap().get(key));
53              this.iterator = values.listIterator();
54          }
55  
56          ValuesListIterator(final K key, final int index) {
57              this.key = key;
58              this.values = ListUtils.emptyIfNull(getMap().get(key));
59              this.iterator = values.listIterator(index);
60          }
61  
62          @Override
63          public void add(final V value) {
64              if (getMap().get(key) == null) {
65                  final List<V> list = createCollection();
66                  getMap().put(key, list);
67                  this.values = list;
68                  this.iterator = list.listIterator();
69              }
70              this.iterator.add(value);
71          }
72  
73          @Override
74          public boolean hasNext() {
75              return iterator.hasNext();
76          }
77  
78          @Override
79          public boolean hasPrevious() {
80              return iterator.hasPrevious();
81          }
82  
83          @Override
84          public V next() {
85              return iterator.next();
86          }
87  
88          @Override
89          public int nextIndex() {
90              return iterator.nextIndex();
91          }
92  
93          @Override
94          public V previous() {
95              return iterator.previous();
96          }
97  
98          @Override
99          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 }