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
027/**
028 * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap},
029 * storing multiple values in a {@link LinkedList}.
030 *
031 * <p>This Map implementation is generally not thread-safe. It is primarily designed
032 * for data structures exposed from request objects, for use in a single thread only.
033 *
034 * @author Arjen Poutsma
035 * @author Juergen Hoeller
036 * @since 3.0
037 * @param <K> the key type
038 * @param <V> the value element type
039 */
040public class LinkedMultiValueMap<K, V> implements MultiValueMap<K, V>, Serializable, Cloneable {
041
042        private static final long serialVersionUID = 3801124242820219131L;
043
044        private final Map<K, List<V>> targetMap;
045
046
047        /**
048         * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}.
049         */
050        public LinkedMultiValueMap() {
051                this.targetMap = new LinkedHashMap<K, List<V>>();
052        }
053
054        /**
055         * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}
056         * with the given initial capacity.
057         * @param initialCapacity the initial capacity
058         */
059        public LinkedMultiValueMap(int initialCapacity) {
060                this.targetMap = new LinkedHashMap<K, List<V>>(initialCapacity);
061        }
062
063        /**
064         * Copy constructor: Create a new LinkedMultiValueMap with the same mappings as
065         * the specified Map. Note that this will be a shallow copy; its value-holding
066         * List entries will get reused and therefore cannot get modified independently.
067         * @param otherMap the Map whose mappings are to be placed in this Map
068         * @see #clone()
069         * @see #deepCopy()
070         */
071        public LinkedMultiValueMap(Map<K, List<V>> otherMap) {
072                this.targetMap = new LinkedHashMap<K, List<V>>(otherMap);
073        }
074
075
076        // MultiValueMap implementation
077
078        @Override
079        public V getFirst(K key) {
080                List<V> values = this.targetMap.get(key);
081                return (values != null && !values.isEmpty() ? values.get(0) : null);
082        }
083
084        @Override
085        public void add(K key, V value) {
086                List<V> values = this.targetMap.get(key);
087                if (values == null) {
088                        values = new LinkedList<V>();
089                        this.targetMap.put(key, values);
090                }
091                values.add(value);
092        }
093
094        @Override
095        public void set(K key, V value) {
096                List<V> values = new LinkedList<V>();
097                values.add(value);
098                this.targetMap.put(key, values);
099        }
100
101        @Override
102        public void setAll(Map<K, V> values) {
103                for (Entry<K, V> entry : values.entrySet()) {
104                        set(entry.getKey(), entry.getValue());
105                }
106        }
107
108        @Override
109        public Map<K, V> toSingleValueMap() {
110                LinkedHashMap<K, V> singleValueMap = new LinkedHashMap<K,V>(this.targetMap.size());
111                for (Entry<K, List<V>> entry : this.targetMap.entrySet()) {
112                        List<V> values = entry.getValue();
113                        if (values != null && !values.isEmpty()) {
114                                singleValueMap.put(entry.getKey(), values.get(0));
115                        }
116                }
117                return singleValueMap;
118        }
119
120
121        // Map implementation
122
123        @Override
124        public int size() {
125                return this.targetMap.size();
126        }
127
128        @Override
129        public boolean isEmpty() {
130                return this.targetMap.isEmpty();
131        }
132
133        @Override
134        public boolean containsKey(Object key) {
135                return this.targetMap.containsKey(key);
136        }
137
138        @Override
139        public boolean containsValue(Object value) {
140                return this.targetMap.containsValue(value);
141        }
142
143        @Override
144        public List<V> get(Object key) {
145                return this.targetMap.get(key);
146        }
147
148        @Override
149        public List<V> put(K key, List<V> value) {
150                return this.targetMap.put(key, value);
151        }
152
153        @Override
154        public List<V> remove(Object key) {
155                return this.targetMap.remove(key);
156        }
157
158        @Override
159        public void putAll(Map<? extends K, ? extends List<V>> map) {
160                this.targetMap.putAll(map);
161        }
162
163        @Override
164        public void clear() {
165                this.targetMap.clear();
166        }
167
168        @Override
169        public Set<K> keySet() {
170                return this.targetMap.keySet();
171        }
172
173        @Override
174        public Collection<List<V>> values() {
175                return this.targetMap.values();
176        }
177
178        @Override
179        public Set<Entry<K, List<V>>> entrySet() {
180                return this.targetMap.entrySet();
181        }
182
183
184        /**
185         * Create a deep copy of this Map.
186         * @return a copy of this Map, including a copy of each value-holding List entry
187         * @since 4.2
188         * @see #clone()
189         */
190        public LinkedMultiValueMap<K, V> deepCopy() {
191                LinkedMultiValueMap<K, V> copy = new LinkedMultiValueMap<K, V>(this.targetMap.size());
192                for (Map.Entry<K, List<V>> entry : this.targetMap.entrySet()) {
193                        copy.put(entry.getKey(), new LinkedList<V>(entry.getValue()));
194                }
195                return copy;
196        }
197
198        /**
199         * Create a regular copy of this Map.
200         * @return a shallow copy of this Map, reusing this Map's value-holding List entries
201         * @since 4.2
202         * @see LinkedMultiValueMap#LinkedMultiValueMap(Map)
203         * @see #deepCopy()
204         */
205        @Override
206        public LinkedMultiValueMap<K, V> clone() {
207                return new LinkedMultiValueMap<K, V>(this);
208        }
209
210        @Override
211        public boolean equals(Object other) {
212                return (this == other || this.targetMap.equals(other));
213        }
214
215        @Override
216        public int hashCode() {
217                return this.targetMap.hashCode();
218        }
219
220        @Override
221        public String toString() {
222                return this.targetMap.toString();
223        }
224
225}