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