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    *      https://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                  values = list;
68                  iterator = list.listIterator();
69              }
70              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             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 }