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;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Set;
24  
25  import org.apache.commons.collections4.bag.HashBag;
26  import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
27  import org.apache.commons.collections4.multimap.HashSetValuedHashMap;
28  import org.apache.commons.collections4.multimap.TransformedMultiValuedMap;
29  import org.apache.commons.collections4.multimap.UnmodifiableMultiValuedMap;
30  
31  /**
32   * Provides utility methods and decorators for {@link MultiValuedMap} instances.
33   * <p>
34   * It contains various type safe and null safe methods. Additionally, it provides
35   * the following decorators:
36   * </p>
37   * <ul>
38   *   <li>{@link #unmodifiableMultiValuedMap(MultiValuedMap)}</li>
39   *   <li>{@link #transformedMultiValuedMap(MultiValuedMap, Transformer, Transformer)}</li>
40   * </ul>
41   *
42   * @since 4.1
43   */
44  public class MultiMapUtils {
45  
46      /**
47       * An empty {@link UnmodifiableMultiValuedMap}.
48       */
49      @SuppressWarnings({ "rawtypes", "unchecked" })
50      public static final MultiValuedMap EMPTY_MULTI_VALUED_MAP =
51              UnmodifiableMultiValuedMap.unmodifiableMultiValuedMap(new ArrayListValuedHashMap(0, 0));
52  
53      /**
54       * Returns an immutable empty {@code MultiValuedMap} if the argument is
55       * {@code null}, or the argument itself otherwise.
56       *
57       * @param <K> the type of key in the map
58       * @param <V> the type of value in the map
59       * @param map  the map, may be null
60       * @return an empty {@link MultiValuedMap} if the argument is null
61       */
62      @SuppressWarnings("unchecked")
63      public static <K, V> MultiValuedMap<K, V> emptyIfNull(final MultiValuedMap<K, V> map) {
64          return map == null ? EMPTY_MULTI_VALUED_MAP : map;
65      }
66  
67      /**
68       * Returns immutable EMPTY_MULTI_VALUED_MAP with generic type safety.
69       *
70       * @param <K> the type of key in the map
71       * @param <V> the type of value in the map
72       * @return immutable and empty {@code MultiValuedMap}
73       */
74      @SuppressWarnings("unchecked")
75      public static <K, V> MultiValuedMap<K, V> emptyMultiValuedMap() {
76          return EMPTY_MULTI_VALUED_MAP;
77      }
78  
79      // Null safe methods
80  
81      /**
82       * Gets a Collection from {@code MultiValuedMap} in a null-safe manner.
83       *
84       * @param <K> the key type
85       * @param <V> the value type
86       * @param map  the {@link MultiValuedMap} to use
87       * @param key  the key to look up
88       * @return the Collection in the {@link MultiValuedMap}, or null if input map is null
89       */
90      public static <K, V> Collection<V> getCollection(final MultiValuedMap<K, V> map, final K key) {
91          if (map != null) {
92              return map.get(key);
93          }
94          return null;
95      }
96  
97      /**
98       * Gets a Bag from {@code MultiValuedMap} in a null-safe manner.
99       *
100      * @param <K> the key type
101      * @param <V> the value type
102      * @param map  the {@link MultiValuedMap} to use
103      * @param key  the key to look up
104      * @return the Collection in the {@link MultiValuedMap} as Bag, or null if input map is null
105      */
106     public static <K, V> Bag<V> getValuesAsBag(final MultiValuedMap<K, V> map, final K key) {
107         if (map != null) {
108             final Collection<V> col = map.get(key);
109             if (col instanceof Bag) {
110                 return (Bag<V>) col;
111             }
112             return new HashBag<>(col);
113         }
114         return null;
115     }
116 
117     /**
118      * Gets a List from {@code MultiValuedMap} in a null-safe manner.
119      *
120      * @param <K> the key type
121      * @param <V> the value type
122      * @param map  the {@link MultiValuedMap} to use
123      * @param key  the key to look up
124      * @return the Collection in the {@link MultiValuedMap} as List, or null if input map is null
125      */
126     public static <K, V> List<V> getValuesAsList(final MultiValuedMap<K, V> map, final K key) {
127         if (map != null) {
128             final Collection<V> col = map.get(key);
129             if (col instanceof List) {
130                 return (List<V>) col;
131             }
132             return new ArrayList<>(col);
133         }
134         return null;
135     }
136 
137     // TODO: review the getValuesAsXXX methods - depending on the actual MultiValuedMap type, changes
138     // to the returned collection might update the backing map. This should be clarified and/or prevented.
139 
140     /**
141      * Gets a Set from {@code MultiValuedMap} in a null-safe manner.
142      *
143      * @param <K> the key type
144      * @param <V> the value type
145      * @param map  the {@link MultiValuedMap} to use
146      * @param key  the key to look up
147      * @return the Collection in the {@link MultiValuedMap} as Set, or null if input map is null
148      */
149     public static <K, V> Set<V> getValuesAsSet(final MultiValuedMap<K, V> map, final K key) {
150         if (map != null) {
151             final Collection<V> col = map.get(key);
152             if (col instanceof Set) {
153                 return (Set<V>) col;
154             }
155             return new HashSet<>(col);
156         }
157         return null;
158     }
159 
160     /**
161      * Null-safe check if the specified {@code MultiValuedMap} is empty.
162      * <p>
163      * If the provided map is null, returns true.
164      *
165      * @param map  the map to check, may be null
166      * @return true if the map is empty or null
167      */
168     public static boolean isEmpty(final MultiValuedMap<?, ?> map) {
169         return map == null || map.isEmpty();
170     }
171 
172     /**
173      * Creates a {@link ListValuedMap} with an {@link java.util.ArrayList ArrayList} as
174      * collection class to store the values mapped to a key.
175      *
176      * @param <K> the key type
177      * @param <V> the value type
178      * @return a new {@code ListValuedMap}
179      */
180     public static <K, V> ListValuedMap<K, V> newListValuedHashMap() {
181         return new ArrayListValuedHashMap<>();
182     }
183 
184     /**
185      * Creates a {@link SetValuedMap} with an {@link java.util.HashSet HashSet} as
186      * collection class to store the values mapped to a key.
187      *
188      * @param <K> the key type
189      * @param <V> the value type
190      * @return a new {@link SetValuedMap}
191      */
192     public static <K, V> SetValuedMap<K, V> newSetValuedHashMap() {
193         return new HashSetValuedHashMap<>();
194     }
195 
196     /**
197      * Returns a {@code TransformedMultiValuedMap} backed by the given map.
198      * <p>
199      * This method returns a new {@code MultiValuedMap} (decorating the
200      * specified map) that will transform any new entries added to it. Existing
201      * entries in the specified map will not be transformed. If you want that
202      * behavior, see {@link TransformedMultiValuedMap#transformedMap}.
203      * <p>
204      * Each object is passed through the transformers as it is added to the Map.
205      * It is important not to use the original map after invoking this method,
206      * as it is a back door for adding untransformed objects.
207      * <p>
208      * If there are any elements already in the map being decorated, they are
209      * NOT transformed.
210      *
211      * @param <K> the key type
212      * @param <V> the value type
213      * @param map  the {@link MultiValuedMap} to transform, must not be null, typically empty
214      * @param keyTransformer  the transformer for the map keys, null means no transformation
215      * @param valueTransformer  the transformer for the map values, null means no transformation
216      * @return a transformed {@code MultiValuedMap} backed by the given map
217      * @throws NullPointerException if map is null
218      */
219     public static <K, V> MultiValuedMap<K, V> transformedMultiValuedMap(final MultiValuedMap<K, V> map,
220             final Transformer<? super K, ? extends K> keyTransformer,
221             final Transformer<? super V, ? extends V> valueTransformer) {
222         return TransformedMultiValuedMap.transformingMap(map, keyTransformer, valueTransformer);
223     }
224 
225     /**
226      * Returns an {@code UnmodifiableMultiValuedMap} backed by the given
227      * map.
228      *
229      * @param <K> the key type
230      * @param <V> the value type
231      * @param map  the {@link MultiValuedMap} to decorate, must not be null
232      * @return an unmodifiable {@link MultiValuedMap} backed by the provided map
233      * @throws NullPointerException if map is null
234      */
235     public static <K, V> MultiValuedMap<K, V> unmodifiableMultiValuedMap(
236             final MultiValuedMap<? extends K, ? extends V> map) {
237         return UnmodifiableMultiValuedMap.<K, V>unmodifiableMultiValuedMap(map);
238     }
239 
240     /**
241      * Don't allow instances.
242      */
243     private MultiMapUtils() {}
244 
245 }