1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.collection.LinkedMultiValueMap;
13 
14 import hunt.collection.Collection;
15 import hunt.collection.LinkedHashMap;
16 import hunt.collection.LinkedList;
17 import hunt.collection.List;
18 import hunt.collection.MultiValueMap;
19 import hunt.collection.Map;
20 import hunt.collection.Set;
21 import hunt.Object;
22 import hunt.util.ObjectUtils;
23 
24 import std.range;
25 
26 /**
27  * Simple implementation of {@link MultiValueMap} that wraps a {@link LinkedHashMap},
28  * storing multiple values in a {@link LinkedList}.
29  *
30  * <p>This Map implementation is generally not thread-safe. It is primarily designed
31  * for data structures exposed from request objects, for use in a single thread only.
32  *
33  * @author Arjen Poutsma
34  * @author Juergen Hoeller
35  */
36 class LinkedMultiValueMap(K, V) : MultiValueMap!(K, V) {
37 
38 	private Map!(K, List!(V)) targetMap;
39 
40 
41 	/**
42 	 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}.
43 	 */
44 	this() {
45 		this.targetMap = new LinkedHashMap!(K, List!(V))();
46 	}
47 
48 	/**
49 	 * Create a new LinkedMultiValueMap that wraps a {@link LinkedHashMap}
50 	 * with the given initial capacity.
51 	 * @param initialCapacity the initial capacity
52 	 */
53 	this(int initialCapacity) {
54 		this.targetMap = new LinkedHashMap!(K, List!(V))(initialCapacity);
55 	}
56 
57 	/**
58 	 * Copy constructor: Create a new LinkedMultiValueMap with the same mappings as
59 	 * the specified Map. Note that this will be a shallow copy; its value-holding
60 	 * List entries will get reused and therefore cannot get modified independently.
61 	 * @param otherMap the Map whose mappings are to be placed in this Map
62 	 * @see #clone()
63 	 * @see #deepCopy()
64 	 */
65 	this(Map!(K, List!(V)) otherMap) {
66 		this.targetMap = new LinkedHashMap!(K, List!(V))(otherMap);
67 	}
68 
69 
70 	// MultiValueMap implementation
71 
72 	override
73 	V getFirst(K key) {
74 		List!(V) values = this.targetMap.get(key);
75 		return (values !is null ? values.get(0) : null);
76 	}
77 
78 	override
79 	void add(K key, V value) {
80 		List!(V) values = this.targetMap.computeIfAbsent(key, k => new LinkedList!(V)());
81 		values.add(value);
82 	}
83 
84 	override
85 	void addAll(K key, List!(V) values) {
86 		List!(V) currentValues = this.targetMap.computeIfAbsent(key, k => new LinkedList!(V)());
87 		currentValues.addAll(values);
88 	}
89 
90 	void addAll(Map!(K, List!V) values) {
91 		foreach (K key, List!V value ; values) {
92 			addAll(key, value);
93 		}
94 	}
95 
96 	override
97 	void set(K key, V value) {
98 		List!(V) values = new LinkedList!(V)();
99 		values.add(value);
100 		this.targetMap.put(key, values);
101 	}
102 
103 	override
104 	void setAll(Map!(K, V) values) {
105         foreach (K key, V value ; values) 
106             this.set(key, value);
107 	}
108 
109 	override
110 	Map!(K, V) toSingleValueMap() {
111 		LinkedHashMap!(K, V) singleValueMap = new LinkedHashMap!(K, V)(this.targetMap.size());
112         foreach (K key, List!(V) value ; this.targetMap)
113             singleValueMap.put(key, value.get(0));
114 		
115 		return singleValueMap;
116 	}
117 
118 
119 	// Map implementation
120 
121 	override
122 	int size() {
123 		return this.targetMap.size();
124 	}
125 
126 	override
127 	bool isEmpty() {
128 		return this.targetMap.isEmpty();
129 	}
130 
131 	override
132 	bool containsKey(K key) {
133 		return this.targetMap.containsKey(key);
134 	}
135 
136 	override
137 	bool containsValue(List!(V) value) {
138 		return this.targetMap.containsValue(value);
139 	}
140 
141     List!(V) opIndex(K key) {
142         return get(key);
143     }
144 
145 	override	
146 	List!(V) get(K key) {
147 		return this.targetMap.get(key);
148 	}
149 
150 	override
151 	List!(V) put(K key, List!(V) value) {
152 		return this.targetMap.put(key, value);
153 	}
154 	
155     List!(V) putIfAbsent(K key, List!(V) value) {
156         List!(V) v;
157 
158         if(!containsKey(key))
159             v = put(key, value);
160 
161         return v;
162     }
163 
164 	override
165 	List!(V) remove(K key) {
166 		return this.targetMap.remove(key);
167 	}
168 	
169     bool remove(K key, List!(V) value){
170         List!(V) curValue = get(key);
171         if(curValue != value || !containsKey(key))
172             return false;
173         remove(key);
174         return true;
175     }
176 
177 	override
178 	void putAll(Map!(K, List!(V)) map) {
179 		this.targetMap.putAll(map);
180 	}
181 
182 	override
183 	void clear() {
184 		this.targetMap.clear();
185 	}
186 	
187     bool replace(K key, List!(V) oldValue, List!(V) newValue) {
188         List!(V) curValue = get(key);
189          if(curValue != oldValue || !containsKey(key)){
190             return false;
191         }
192         put(key, newValue);
193         return true;
194     }
195 
196     List!(V) replace(K key, List!(V) value) {
197         List!(V) curValue;
198         if (containsKey(key)) {
199             curValue = put(key, value);
200         }
201         return curValue;
202     }
203 
204     int opApply(scope int delegate(ref K, ref List!(V)) dg)  {
205         return this.targetMap.opApply(dg);
206     }
207     
208     int opApply(scope int delegate(MapEntry!(K, List!(V)) entry) dg) {
209         return this.targetMap.opApply(dg);
210     }
211     
212     InputRange!K byKey() {
213         return this.targetMap.byKey();
214     }
215 
216     InputRange!(List!(V)) byValue() {
217         return this.targetMap.byValue();
218     }
219 
220 	// override
221 	// Set!(K) keySet() {
222 	// 	return this.targetMap.keySet();
223 	// }
224 
225 	// override
226 	List!(V)[] values() {
227 		return this.targetMap.values();
228 	}
229 
230 	// override
231 	// Set<Entry!(K, List!(V))> entrySet() {
232 	// 	return this.targetMap.entrySet();
233 	// }
234 
235 
236 	/**
237 	 * Create a deep copy of this Map.
238 	 * @return a copy of this Map, including a copy of each value-holding List entry
239 	 * @since 4.2
240 	 * @see #clone()
241 	 */
242 	LinkedMultiValueMap!(K, V) deepCopy() {
243 		LinkedMultiValueMap!(K, V) copy = new LinkedMultiValueMap!(K, V)(this.targetMap.size());
244 		foreach(K key, List!(V) value; this.targetMap) {
245 			copy.put(key, new LinkedList!(V)(value));
246 		}
247 		return copy;
248 	}
249 
250 	/**
251 	 * Create a regular copy of this Map.
252 	 * @return a shallow copy of this Map, reusing this Map's value-holding List entries
253 	 * @since 4.2
254 	 * @see LinkedMultiValueMap#LinkedMultiValueMap(Map)
255 	 * @see #deepCopy()
256 	 */
257 
258     mixin CloneMemberTemplate!(typeof(this));	
259 
260     bool opEquals(IObject o) {
261         return opEquals(cast(Object)o);
262     }
263 
264 	override bool opEquals(Object obj) {
265 		return this.targetMap == obj;
266 	}
267 
268 	override size_t toHash() @trusted nothrow {
269 		return this.targetMap.toHash;
270 	}
271 
272 	override string toString() {
273 		return this.targetMap.toString();
274 	}
275 
276 }