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      * </p>
165      *
166      * @param map  the map to check, may be null
167      * @return true if the map is empty or null
168      */
169     public static boolean isEmpty(final MultiValuedMap<?, ?> map) {
170         return map == null || map.isEmpty();
171     }
172 
173     /**
174      * Creates a {@link ListValuedMap} with an {@link java.util.ArrayList ArrayList} as
175      * collection class to store the values mapped to a key.
176      *
177      * @param <K> the key type
178      * @param <V> the value type
179      * @return a new {@code ListValuedMap}
180      */
181     public static <K, V> ListValuedMap<K, V> newListValuedHashMap() {
182         return new ArrayListValuedHashMap<>();
183     }
184 
185     /**
186      * Creates a {@link SetValuedMap} with an {@link java.util.HashSet HashSet} as
187      * collection class to store the values mapped to a key.
188      *
189      * @param <K> the key type
190      * @param <V> the value type
191      * @return a new {@link SetValuedMap}
192      */
193     public static <K, V> SetValuedMap<K, V> newSetValuedHashMap() {
194         return new HashSetValuedHashMap<>();
195     }
196 
197     /**
198      * Returns a {@code TransformedMultiValuedMap} backed by the given map.
199      * <p>
200      * This method returns a new {@code MultiValuedMap} (decorating the
201      * specified map) that will transform any new entries added to it. Existing
202      * entries in the specified map will not be transformed. If you want that
203      * behavior, see {@link TransformedMultiValuedMap#transformedMap}.
204      * </p>
205      * <p>
206      * Each object is passed through the transformers as it is added to the Map.
207      * It is important not to use the original map after invoking this method,
208      * as it is a back door for adding untransformed objects.
209      * </p>
210      * <p>
211      * If there are any elements already in the map being decorated, they are
212      * NOT transformed.
213      * </p>
214      *
215      * @param <K> the key type
216      * @param <V> the value type
217      * @param map  the {@link MultiValuedMap} to transform, must not be null, typically empty
218      * @param keyTransformer  the transformer for the map keys, null means no transformation
219      * @param valueTransformer  the transformer for the map values, null means no transformation
220      * @return a transformed {@code MultiValuedMap} backed by the given map
221      * @throws NullPointerException if map is null
222      */
223     public static <K, V> MultiValuedMap<K, V> transformedMultiValuedMap(final MultiValuedMap<K, V> map,
224             final Transformer<? super K, ? extends K> keyTransformer,
225             final Transformer<? super V, ? extends V> valueTransformer) {
226         return TransformedMultiValuedMap.transformingMap(map, keyTransformer, valueTransformer);
227     }
228 
229     /**
230      * Returns an {@code UnmodifiableMultiValuedMap} backed by the given
231      * map.
232      *
233      * @param <K> the key type
234      * @param <V> the value type
235      * @param map  the {@link MultiValuedMap} to decorate, must not be null
236      * @return an unmodifiable {@link MultiValuedMap} backed by the provided map
237      * @throws NullPointerException if map is null
238      */
239     public static <K, V> MultiValuedMap<K, V> unmodifiableMultiValuedMap(
240             final MultiValuedMap<? extends K, ? extends V> map) {
241         return UnmodifiableMultiValuedMap.<K, V>unmodifiableMultiValuedMap(map);
242     }
243 
244     /**
245      * Don't allow instances.
246      */
247     private MultiMapUtils() {
248         // empty
249     }
250 
251 }