001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.util;
018
019import java.io.Serializable;
020import java.util.Collection;
021import java.util.LinkedHashMap;
022import java.util.LinkedList;
023import java.util.List;
024import java.util.Map;
025import java.util.Set;
026
027import org.springframework.lang.Nullable;
028
029/**
030 * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap},
031 * storing multiple values in a {@link LinkedList}.
032 *
033 * <p>This Map implementation is generally not thread-safe. It is primarily designed
034 * for data structures exposed from request objects, for use in a single thread only.
035 *
036 * @author Arjen Poutsma
037 * @author Juergen Hoeller
038 * @since 3.0
039 * @param <K> the key type
040 * @param <V> the value element type
041 */
042public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V>, Serializable, Cloneable {
043
044        private static final long serialVersionUID = 3801124242820219131L;
045
046        private final Map<K, List<V>> targetMap;
047
048
049        /**
050         * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}.
051         */
052        public LinkedMultiValueMap() {
053                this.targetMap = new LinkedHashMap<>();
054        }
055
056        /**
057         * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}
058         * with the given initial capacity.
059         * @param initialCapacity the initial capacity
060         */
061        public LinkedMultiValueMap(int initialCapacity) {
062                this.targetMap = new LinkedHashMap<>(initialCapacity);
063        }
064
065        /**
066         * Copy constructor: Create a new LinkedMultiValueMap with the same mappings as
067         * the specified Map. Note that this will be a shallow copy; its value-holding
068         * List entries will get reused and therefore cannot get modified independently.
069         * @param otherMap the Map whose mappings are to be placed in this Map
070         * @see #clone()
071         * @see #deepCopy()
072         */
073        public LinkedMultiValueMap(Map<K, List<V>> otherMap) {
074                this.targetMap = new LinkedHashMap<>(otherMap);
075        }
076
077
078        // MultiValueMap implementation
079
080        @Override
081        @Nullable
082        public V getFirst(K key) {
083                List<V> values = this.targetMap.get(key);
084                return (values != null && !values.isEmpty() ? values.get(0) : null);
085        }
086
087        @Override
088        public void add(K key, @Nullable V value) {
089                List<V> values = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>());
090                values.add(value);
091        }
092
093        @Override
094        public void addAll(K key, List<? extends V> values) {
095                List<V> currentValues = this.targetMap.computeIfAbsent(key, k -> new LinkedList<>());
096                currentValues.addAll(values);
097        }
098
099        @Override
100        public void addAll(MultiValueMap<K, V> values) {
101                for (Entry<K, List<V>> entry : values.entrySet()) {
102                        addAll(entry.getKey(), entry.getValue());
103                }
104        }
105
106        @Override
107        public void set(K key, @Nullable V value) {
108                List<V> values = new LinkedList<>();
109                values.add(value);
110                this.targetMap.put(key, values);
111        }
112
113        @Override
114        public void setAll(Map<K, V> values) {
115                values.forEach(this::set);
116        }
117
118        @Override
119        public Map<K, V> toSingleValueMap() {
120                Map<K, V> singleValueMap = new LinkedHashMap<>(this.targetMap.size());
121                this.targetMap.forEach((key, values) -> {
122                        if (values != null && !values.isEmpty()) {
123                                singleValueMap.put(key, values.get(0));
124                        }
125                });
126                return singleValueMap;
127        }
128
129
130        // Map implementation
131
132        @Override
133        public int size() {
134                return this.targetMap.size();
135        }
136
137        @Override
138        public boolean isEmpty() {
139                return this.targetMap.isEmpty();
140        }
141
142        @Override
143        public boolean containsKey(Object key) {
144                return this.targetMap.containsKey(key);
145        }
146
147        @Override
148        public boolean containsValue(Object value) {
149                return this.targetMap.containsValue(value);
150        }
151
152        @Override
153        @Nullable
154        public List<V> get(Object key) {
155                return this.targetMap.get(key);
156        }
157
158        @Override
159        @Nullable
160        public List<V> put(K key, List<V> value) {
161                return this.targetMap.put(key, value);
162        }
163
164        @Override
165        @Nullable
166        public List<V> remove(Object key) {
167                return this.targetMap.remove(key);
168        }
169
170        @Override
171        public void putAll(Map<? extends K, ? extends List<V>> map) {
172                this.targetMap.putAll(map);
173        }
174
175        @Override
176        public void clear() {
177                this.targetMap.clear();
178        }
179
180        @Override
181        public Set<K> keySet() {
182                return this.targetMap.keySet();
183        }
184
185        @Override
186        public Collection<List<V>> values() {
187                return this.targetMap.values();
188        }
189
190        @Override
191        public Set<Entry<K, List<V>>> entrySet() {
192                return this.targetMap.entrySet();
193        }
194
195        @Override
196        public boolean equals(@Nullable Object other) {
197                return (this == other || this.targetMap.equals(other));
198        }
199
200        @Override
201        public int hashCode() {
202                return this.targetMap.hashCode();
203        }
204
205        @Override
206        public String toString() {
207                return this.targetMap.toString();
208        }
209
210
211        /**
212         * Create a deep copy of this Map.
213         * @return a copy of this Map, including a copy of each value-holding List entry
214         * (consistently using an independent modifiable {@link LinkedList} for each entry)
215         * along the lines of {@code MultiValueMap.addAll} semantics
216         * @since 4.2
217         * @see #addAll(MultiValueMap)
218         * @see #clone()
219         */
220        public LinkedMultiValueMap<K, V> deepCopy() {
221                LinkedMultiValueMap<K, V> copy = new LinkedMultiValueMap<>(size());
222                forEach((key, values) -> copy.put(key, new LinkedList<>(values)));
223                return copy;
224        }
225
226        /**
227         * Create a regular copy of this Map.
228         * @return a shallow copy of this Map, reusing this Map's value-holding List entries
229         * (even if some entries are shared or unmodifiable) along the lines of standard
230         * {@code Map.put} semantics
231         * @since 4.2
232         * @see #put(Object, List)
233         * @see #putAll(Map)
234         * @see LinkedMultiValueMap#LinkedMultiValueMap(Map)
235         * @see #deepCopy()
236         */
237        @Override
238        public LinkedMultiValueMap<K, V> clone() {
239                return new LinkedMultiValueMap<>(this);
240        }
241
242}