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.juneau.commons.collections;
18  
19  import static org.apache.juneau.commons.utils.CollectionUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.util.*;
24  import java.util.concurrent.ConcurrentHashMap;
25  
26  import org.apache.juneau.*;
27  import org.junit.jupiter.api.*;
28  
29  class FluentMap_Test extends TestBase {
30  
31  	//====================================================================================================
32  	// Basic functionality - a(K key, V value)
33  	//====================================================================================================
34  
35  	@Test
36  	void a01_addSingleEntry() {
37  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
38  		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
39  
40  		assertSize(3, map);
41  		assertEquals("value1", map.get("key1"));
42  		assertEquals("value2", map.get("key2"));
43  		assertEquals("value3", map.get("key3"));
44  	}
45  
46  	@Test
47  	void a02_addSingleEntry_returnsThis() {
48  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
49  		var result = map.a("key1", "value1");
50  
51  		assertSame(map, result);
52  	}
53  
54  	@Test
55  	void a03_addSingleEntry_nullValue() {
56  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
57  		map.a("key1", "value1").a("key2", null).a("key3", "value3");
58  
59  		assertSize(3, map);
60  		assertEquals("value1", map.get("key1"));
61  		assertNull(map.get("key2"));
62  		assertEquals("value3", map.get("key3"));
63  	}
64  
65  	@Test
66  	void a04_addSingleEntry_nullKey() {
67  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
68  		map.a(null, "value1").a("key2", "value2");
69  
70  		assertSize(2, map);
71  		assertEquals("value1", map.get(null));
72  		assertEquals("value2", map.get("key2"));
73  	}
74  
75  	@Test
76  	void a05_addSingleEntry_updateExisting() {
77  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
78  		map.a("key1", "value1");
79  		map.a("key1", "value1-updated");
80  
81  		assertSize(1, map);
82  		assertEquals("value1-updated", map.get("key1"));
83  	}
84  
85  	//====================================================================================================
86  	// a(Map) method
87  	//====================================================================================================
88  
89  	@Test
90  	void b01_addMap() {
91  		var map = new FluentMap<>(new LinkedHashMap<String, String>());
92  		var other = map("key1", "value1", "key2", "value2", "key3", "value3");
93  		map.aa(other);
94  
95  		assertSize(3, map);
96  		assertEquals("value1", map.get("key1"));
97  		assertEquals("value2", map.get("key2"));
98  		assertEquals("value3", map.get("key3"));
99  	}
100 
101 	@Test
102 	void b02_addMap_returnsThis() {
103 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
104 		var other = map("key1", "value1", "key2", "value2");
105 		var result = map.aa(other);
106 
107 		assertSame(map, result);
108 	}
109 
110 	@Test
111 	void b03_addMap_nullMap() {
112 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
113 		map.a("key1", "value1");
114 		map.aa((Map<String, String>)null);
115 		map.a("key2", "value2");
116 
117 		assertSize(2, map);
118 		assertEquals("value1", map.get("key1"));
119 		assertEquals("value2", map.get("key2"));
120 	}
121 
122 	@Test
123 	void b04_addMap_emptyMap() {
124 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
125 		map.a("key1", "value1");
126 		map.aa(map());
127 		map.a("key2", "value2");
128 
129 		assertSize(2, map);
130 		assertEquals("value1", map.get("key1"));
131 		assertEquals("value2", map.get("key2"));
132 	}
133 
134 	@Test
135 	void b05_addMap_multipleCalls() {
136 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
137 		map.aa(map("key1", "value1", "key2", "value2"));
138 		map.aa(map("key3", "value3", "key4", "value4"));
139 
140 		assertSize(4, map);
141 		assertEquals("value1", map.get("key1"));
142 		assertEquals("value2", map.get("key2"));
143 		assertEquals("value3", map.get("key3"));
144 		assertEquals("value4", map.get("key4"));
145 	}
146 
147 	@Test
148 	void b06_addMap_overwritesExisting() {
149 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
150 		map.a("key1", "value1");
151 		map.aa(map("key1", "value1-updated", "key2", "value2"));
152 
153 		assertSize(2, map);
154 		assertEquals("value1-updated", map.get("key1"));
155 		assertEquals("value2", map.get("key2"));
156 	}
157 
158 	//====================================================================================================
159 	// ai(boolean, K, V) method
160 	//====================================================================================================
161 
162 	@Test
163 	void c01_ai_conditionTrue() {
164 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
165 		map.a("key1", "value1").ai(true, "key2", "value2").a("key3", "value3");
166 
167 		assertSize(3, map);
168 		assertEquals("value1", map.get("key1"));
169 		assertEquals("value2", map.get("key2"));
170 		assertEquals("value3", map.get("key3"));
171 	}
172 
173 	@Test
174 	void c02_ai_conditionFalse() {
175 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
176 		map.a("key1", "value1").ai(false, "key2", "value2").a("key3", "value3");
177 
178 		assertSize(2, map);
179 		assertEquals("value1", map.get("key1"));
180 		assertFalse(map.containsKey("key2"));
181 		assertEquals("value3", map.get("key3"));
182 	}
183 
184 	@Test
185 	void c03_ai_returnsThis() {
186 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
187 		var result1 = map.ai(true, "key1", "value1");
188 		var result2 = map.ai(false, "key2", "value2");
189 
190 		assertSame(map, result1);
191 		assertSame(map, result2);
192 	}
193 
194 	@Test
195 	void c04_ai_conditionalBuilding() {
196 		boolean includeDebug = true;
197 		boolean includeTest = false;
198 
199 		var map = new FluentMap<>(new LinkedHashMap<String, String>())
200 			.a("host", "localhost")
201 			.a("port", "8080")
202 			.ai(includeDebug, "debug", "true")
203 			.ai(includeTest, "test", "true");
204 
205 		assertSize(3, map);
206 		assertEquals("localhost", map.get("host"));
207 		assertEquals("8080", map.get("port"));
208 		assertEquals("true", map.get("debug"));
209 		assertFalse(map.containsKey("test"));
210 	}
211 
212 	//====================================================================================================
213 	// Method chaining
214 	//====================================================================================================
215 
216 	@Test
217 	void d01_methodChaining() {
218 		var map = new FluentMap<>(new LinkedHashMap<String, String>())
219 			.a("key1", "value1")
220 			.a("key2", "value2")
221 			.ai(true, "key3", "value3")
222 			.ai(false, "key4", "value4")
223 			.aa(map("key5", "value5", "key6", "value6"));
224 
225 		assertSize(5, map);
226 		assertEquals("value1", map.get("key1"));
227 		assertEquals("value2", map.get("key2"));
228 		assertEquals("value3", map.get("key3"));
229 		assertEquals("value5", map.get("key5"));
230 		assertEquals("value6", map.get("key6"));
231 		assertFalse(map.containsKey("key4"));
232 	}
233 
234 	//====================================================================================================
235 	// Map interface methods
236 	//====================================================================================================
237 
238 	@Test
239 	void e01_mapInterface_get() {
240 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
241 		map.a("key1", "value1").a("key2", "value2");
242 
243 		assertEquals("value1", map.get("key1"));
244 		assertEquals("value2", map.get("key2"));
245 		assertNull(map.get("key3"));
246 	}
247 
248 	@Test
249 	void e02_mapInterface_put() {
250 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
251 		assertNull(map.put("key1", "value1"));
252 		assertEquals("value1", map.put("key1", "value1-updated"));
253 
254 		assertSize(1, map);
255 		assertEquals("value1-updated", map.get("key1"));
256 	}
257 
258 	@Test
259 	void e03_mapInterface_putAll() {
260 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
261 		map.a("key1", "value1");
262 		map.putAll(map("key2", "value2", "key3", "value3"));
263 
264 		assertSize(3, map);
265 		assertEquals("value1", map.get("key1"));
266 		assertEquals("value2", map.get("key2"));
267 		assertEquals("value3", map.get("key3"));
268 	}
269 
270 	@Test
271 	void e04_mapInterface_remove() {
272 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
273 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
274 
275 		assertEquals("value2", map.remove("key2"));
276 		assertSize(2, map);
277 		assertEquals("value1", map.get("key1"));
278 		assertFalse(map.containsKey("key2"));
279 		assertEquals("value3", map.get("key3"));
280 	}
281 
282 	@Test
283 	void e05_mapInterface_containsKey() {
284 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
285 		map.a("key1", "value1").a("key2", "value2");
286 
287 		assertTrue(map.containsKey("key1"));
288 		assertTrue(map.containsKey("key2"));
289 		assertFalse(map.containsKey("key3"));
290 	}
291 
292 	@Test
293 	void e06_mapInterface_containsValue() {
294 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
295 		map.a("key1", "value1").a("key2", "value2");
296 
297 		assertTrue(map.containsValue("value1"));
298 		assertTrue(map.containsValue("value2"));
299 		assertFalse(map.containsValue("value3"));
300 	}
301 
302 	@Test
303 	void e07_mapInterface_size() {
304 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
305 		assertEquals(0, map.size());
306 
307 		map.a("key1", "value1");
308 		assertEquals(1, map.size());
309 
310 		map.a("key2", "value2");
311 		assertEquals(2, map.size());
312 	}
313 
314 	@Test
315 	void e08_mapInterface_isEmpty() {
316 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
317 		assertTrue(map.isEmpty());
318 
319 		map.a("key1", "value1");
320 		assertFalse(map.isEmpty());
321 	}
322 
323 	@Test
324 	void e09_mapInterface_clear() {
325 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
326 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
327 
328 		map.clear();
329 		assertTrue(map.isEmpty());
330 		assertSize(0, map);
331 	}
332 
333 	@Test
334 	void e10_mapInterface_keySet() {
335 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
336 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
337 
338 		var keySet = map.keySet();
339 		assertSize(3, keySet);
340 		assertTrue(keySet.contains("key1"));
341 		assertTrue(keySet.contains("key2"));
342 		assertTrue(keySet.contains("key3"));
343 	}
344 
345 	@Test
346 	void e11_mapInterface_values() {
347 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
348 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
349 
350 		var values = map.values();
351 		assertSize(3, values);
352 		assertTrue(values.contains("value1"));
353 		assertTrue(values.contains("value2"));
354 		assertTrue(values.contains("value3"));
355 	}
356 
357 	@Test
358 	void e12_mapInterface_entrySet() {
359 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
360 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
361 
362 		var entrySet = map.entrySet();
363 		assertSize(3, entrySet);
364 
365 		var found = new LinkedHashSet<String>();
366 		for (var entry : entrySet) {
367 			found.add(entry.getKey() + "=" + entry.getValue());
368 		}
369 		assertTrue(found.contains("key1=value1"));
370 		assertTrue(found.contains("key2=value2"));
371 		assertTrue(found.contains("key3=value3"));
372 	}
373 
374 	//====================================================================================================
375 	// Different map implementations
376 	//====================================================================================================
377 
378 	@Test
379 	void f01_hashMap() {
380 		var map = new FluentMap<>(new HashMap<String, String>());
381 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
382 
383 		assertSize(3, map);
384 	}
385 
386 	@Test
387 	void f02_treeMap() {
388 		var map = new FluentMap<>(new TreeMap<String, String>());
389 		map.a("zebra", "value3").a("apple", "value1").a("banana", "value2");
390 
391 		assertSize(3, map);
392 		// TreeMap maintains sorted order
393 		var keys = new ArrayList<>(map.keySet());
394 		assertEquals(List.of("apple", "banana", "zebra"), keys);
395 	}
396 
397 	@Test
398 	void f03_concurrentHashMap() {
399 		var map = new FluentMap<>(new ConcurrentHashMap<String, String>());
400 		map.a("key1", "value1").a("key2", "value2").a("key3", "value3");
401 
402 		assertSize(3, map);
403 	}
404 
405 	//====================================================================================================
406 	// Edge cases
407 	//====================================================================================================
408 
409 	@Test
410 	void g01_emptyMap() {
411 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
412 
413 		assertTrue(map.isEmpty());
414 		assertSize(0, map);
415 		assertFalse(map.containsKey("anything"));
416 		assertNull(map.get("anything"));
417 	}
418 
419 	@Test
420 	void g02_nullKeyAndValue() {
421 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
422 		map.a(null, null).a("key1", "value1");
423 
424 		assertSize(2, map);
425 		assertNull(map.get(null));
426 		assertTrue(map.containsKey(null));
427 		assertEquals("value1", map.get("key1"));
428 	}
429 
430 	@Test
431 	void g03_updateWithNullValue() {
432 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
433 		map.a("key1", "value1");
434 		map.a("key1", null);
435 
436 		assertSize(1, map);
437 		assertTrue(map.containsKey("key1"));
438 		assertNull(map.get("key1"));
439 	}
440 
441 	//====================================================================================================
442 	// toString(), equals(), hashCode()
443 	//====================================================================================================
444 
445 	@Test
446 	void w01_toString_delegatesToUnderlyingMap() {
447 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
448 		map.a("key1", "value1").a("key2", "value2");
449 
450 		var underlyingMap = new LinkedHashMap<String, String>();
451 		underlyingMap.put("key1", "value1");
452 		underlyingMap.put("key2", "value2");
453 
454 		assertEquals(underlyingMap.toString(), map.toString());
455 	}
456 
457 	@Test
458 	void w02_equals_delegatesToUnderlyingMap() {
459 		var map1 = new FluentMap<>(new LinkedHashMap<String, String>());
460 		map1.a("key1", "value1").a("key2", "value2");
461 
462 		var map2 = new LinkedHashMap<String, String>();
463 		map2.put("key1", "value1");
464 		map2.put("key2", "value2");
465 
466 		assertTrue(map1.equals(map2));
467 		assertTrue(map2.equals(map1));
468 	}
469 
470 	@Test
471 	void w03_equals_differentContents_returnsFalse() {
472 		var map1 = new FluentMap<>(new LinkedHashMap<String, String>());
473 		map1.a("key1", "value1");
474 
475 		var map2 = new LinkedHashMap<String, String>();
476 		map2.put("key1", "value2");
477 
478 		assertFalse(map1.equals(map2));
479 		assertFalse(map2.equals(map1));
480 	}
481 
482 	@Test
483 	void w04_hashCode_delegatesToUnderlyingMap() {
484 		var map = new FluentMap<>(new LinkedHashMap<String, String>());
485 		map.a("key1", "value1").a("key2", "value2");
486 
487 		var underlyingMap = new LinkedHashMap<String, String>();
488 		underlyingMap.put("key1", "value1");
489 		underlyingMap.put("key2", "value2");
490 
491 		assertEquals(underlyingMap.hashCode(), map.hashCode());
492 	}
493 }
494