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  
25  import org.apache.juneau.*;
26  import org.junit.jupiter.api.*;
27  
28  class Lists_Test extends TestBase {
29  
30  	//-----------------------------------------------------------------------------------------------------------------
31  	// Basic tests
32  	//-----------------------------------------------------------------------------------------------------------------
33  
34  	@Test
35  	void a01_create() {
36  		var b = Lists.create(String.class);
37  		assertNotNull(b);
38  	}
39  
40  	@Test
41  	void a02_addSingle() {
42  		var list = Lists.create(String.class)
43  			.add("a")
44  			.build();
45  
46  		assertList(list, "a");
47  	}
48  
49  	@Test
50  	void a03_addMultiple() {
51  		var list = Lists.create(String.class)
52  			.add("a", "b", "c")
53  			.build();
54  
55  		assertList(list, "a", "b", "c");
56  	}
57  
58  	@Test
59  	void a04_addAll() {
60  		var existing = l("x", "y", "z");
61  		var list = Lists.create(String.class)
62  			.add("a")
63  			.addAll(existing)
64  			.add("b")
65  			.build();
66  
67  		assertList(list, "a", "x", "y", "z", "b");
68  	}
69  
70  	@Test
71  	void a05_addAllNull() {
72  		var list = Lists.create(String.class)
73  			.add("a")
74  			.addAll(null)
75  			.add("b")
76  			.build();
77  
78  		assertList(list, "a", "b");
79  	}
80  
81  	//-----------------------------------------------------------------------------------------------------------------
82  	// Conditional adding
83  	//-----------------------------------------------------------------------------------------------------------------
84  
85  	@Test
86  	void b01_addIf_true() {
87  		var list = Lists.create(String.class)
88  			.add("a")
89  			.addIf(true, "b")
90  			.add("c")
91  			.build();
92  
93  		assertList(list, "a", "b", "c");
94  	}
95  
96  	@Test
97  	void b02_addIf_false() {
98  		var list = Lists.create(String.class)
99  			.add("a")
100 			.addIf(false, "b")
101 			.add("c")
102 			.build();
103 
104 		assertList(list, "a", "c");
105 	}
106 
107 	//-----------------------------------------------------------------------------------------------------------------
108 	// Sorting
109 	//-----------------------------------------------------------------------------------------------------------------
110 
111 	@Test
112 	void c01_sorted_naturalOrder() {
113 		var list = Lists.create(String.class)
114 			.add("c", "a", "b")
115 			.sorted()
116 			.build();
117 
118 		assertList(list, "a", "b", "c");
119 	}
120 
121 	@Test
122 	void c02_sorted_customComparator() {
123 		var list = Lists.create(String.class)
124 			.add("a", "bb", "ccc")
125 			.sorted(Comparator.comparing(String::length))
126 			.build();
127 
128 		assertList(list, "a", "bb", "ccc");
129 	}
130 
131 	@Test
132 	void c03_sorted_integers() {
133 		var list = Lists.create(Integer.class)
134 			.add(5, 2, 8, 1, 9)
135 			.sorted()
136 			.build();
137 
138 		assertList(list, 1, 2, 5, 8, 9);
139 	}
140 
141 	//-----------------------------------------------------------------------------------------------------------------
142 	// Sparse mode
143 	//-----------------------------------------------------------------------------------------------------------------
144 
145 	@Test
146 	void d01_sparse_empty() {
147 		var list = Lists.create(String.class)
148 			.sparse()
149 			.build();
150 
151 		assertNull(list);
152 	}
153 
154 	@Test
155 	void d02_sparse_notEmpty() {
156 		var list = Lists.create(String.class)
157 			.add("a")
158 			.sparse()
159 			.build();
160 
161 		assertNotNull(list);
162 		assertSize(1, list);
163 	}
164 
165 	@Test
166 	void d03_notSparse_empty() {
167 		var list = Lists.create(String.class)
168 			.build();
169 
170 		assertNotNull(list);
171 		assertEmpty(list);
172 	}
173 
174 	//-----------------------------------------------------------------------------------------------------------------
175 	// Unmodifiable
176 	//-----------------------------------------------------------------------------------------------------------------
177 
178 	@Test
179 	void e01_unmodifiable() {
180 		var list = Lists.create(String.class)
181 			.add("a", "b", "c")
182 			.unmodifiable()
183 			.build();
184 
185 		assertSize(3, list);
186 		assertThrows(UnsupportedOperationException.class, () -> list.add("d"));
187 	}
188 
189 	@Test
190 	void e02_modifiable() {
191 		var list = Lists.create(String.class)
192 			.add("a", "b", "c")
193 			.build();
194 
195 		list.add("d");
196 		assertSize(4, list);
197 	}
198 
199 	//-----------------------------------------------------------------------------------------------------------------
200 	// Copy mode
201 	//-----------------------------------------------------------------------------------------------------------------
202 
203 	//-----------------------------------------------------------------------------------------------------------------
204 	// Element type
205 	//-----------------------------------------------------------------------------------------------------------------
206 
207 	@Test
208 	void g01_elementType() {
209 		var b = new Lists<>(String.class);
210 		b.elementType(String.class);
211 
212 		var list = b.add("a", "b").build();
213 		assertSize(2, list);
214 	}
215 
216 	//-----------------------------------------------------------------------------------------------------------------
217 	// Complex scenarios
218 	//-----------------------------------------------------------------------------------------------------------------
219 
220 	@Test
221 	void h01_multipleOperations() {
222 		var existing = l("x", "y");
223 		var list = Lists.create(String.class)
224 			.add("a")
225 			.addAll(existing)
226 			.addIf(true, "b")
227 			.addIf(false, "skip")
228 			.add("c", "d")
229 			.sorted()
230 			.build();
231 
232 		// Should be sorted and not contain "skip"
233 		assertList(list, "a", "b", "c", "d", "x", "y");
234 	}
235 
236 	@Test
237 	void h02_sortedAndUnmodifiable() {
238 		var list = Lists.create(Integer.class)
239 			.add(3, 1, 2)
240 			.sorted()
241 			.unmodifiable()
242 			.build();
243 
244 		assertList(list, 1, 2, 3);
245 		assertThrows(UnsupportedOperationException.class, () -> list.add(4));
246 	}
247 
248 	@Test
249 	void h03_sparseAndSorted() {
250 		var list1 = Lists.create(String.class)
251 			.add("c", "a", "b")
252 			.sorted()
253 			.sparse()
254 			.build();
255 
256 		assertNotNull(list1);
257 		assertList(list1, "a", "b", "c");
258 
259 		var list2 = Lists.create(String.class)
260 			.sorted()
261 			.sparse()
262 			.build();
263 
264 		assertNull(list2);
265 	}
266 
267 	//-----------------------------------------------------------------------------------------------------------------
268 	// Edge cases
269 	//-----------------------------------------------------------------------------------------------------------------
270 
271 	@Test
272 	void i01_buildEmptyList() {
273 		var list = Lists.create(String.class)
274 			.build();
275 
276 		assertNotNull(list);
277 		assertEmpty(list);
278 	}
279 
280 	@Test
281 	void i02_addNullElement() {
282 		var list = Lists.create(String.class)
283 			.add("a")
284 			.add((String)null)
285 			.add("b")
286 			.build();
287 
288 		assertList(list, "a", "<null>", "b");
289 	}
290 
291 	@Test
292 	void i03_duplicateElements() {
293 		var list = Lists.create(String.class)
294 			.add("a", "a", "b", "a")
295 			.build();
296 
297 		assertList(list, "a", "a", "b", "a");  // Lists allow duplicates
298 	}
299 
300 
301 	//-----------------------------------------------------------------------------------------------------------------
302 	// AddAll edge cases
303 	//-----------------------------------------------------------------------------------------------------------------
304 
305 	@Test
306 	void j01_addAll_whenListIsNull() {
307 		// Test addAll when list is null (should create new LinkedList from collection)
308 		var existing = l("a", "b", "c");
309 		var list = Lists.create(String.class)
310 			.addAll(existing)
311 			.build();
312 
313 		assertList(list, "a", "b", "c");
314 	}
315 
316 	//-----------------------------------------------------------------------------------------------------------------
317 	// ElementFunction
318 	//-----------------------------------------------------------------------------------------------------------------
319 
320 	@Test
321 	void k01_elementFunction_withFunction() {
322 		var list = Lists.create(Integer.class)
323 			.elementFunction(o -> {
324 				if (o instanceof String) {
325 					return Integer.parseInt((String)o);
326 				}
327 				return null;
328 			})
329 			.addAny("1", "2", "3")
330 			.build();
331 
332 		assertList(list, 1, 2, 3);
333 	}
334 
335 	@Test
336 	void k02_elementFunction_withConverter() {
337 		var converter = new org.apache.juneau.commons.conversion.Converter() {
338 			@Override
339 			public <T> T convertTo(Class<T> type, Object o) {
340 				if (type == Integer.class && o instanceof String) {
341 					return type.cast(Integer.parseInt((String)o));
342 				}
343 				return null;
344 			}
345 		};
346 
347 		var list = Lists.create(Integer.class)
348 			.elementFunction(o -> converter.convertTo(Integer.class, o))
349 			.addAny("1", "2", "3")
350 			.build();
351 
352 		assertList(list, 1, 2, 3);
353 	}
354 
355 	@Test
356 	void k03_elementFunction_multipleConverters() {
357 		var converter1 = new org.apache.juneau.commons.conversion.Converter() {
358 			@Override
359 			public <T> T convertTo(Class<T> type, Object o) {
360 				return null;  // Doesn't handle this
361 			}
362 		};
363 
364 		var converter2 = new org.apache.juneau.commons.conversion.Converter() {
365 			@Override
366 			public <T> T convertTo(Class<T> type, Object o) {
367 				if (type == Integer.class && o instanceof String) {
368 					return type.cast(Integer.parseInt((String)o));
369 				}
370 				return null;
371 			}
372 		};
373 
374 		var list = Lists.create(Integer.class)
375 			.elementFunction(o -> {
376 				Integer result = converter1.convertTo(Integer.class, o);
377 				if (result != null) return result;
378 				return converter2.convertTo(Integer.class, o);
379 			})
380 			.addAny("1", "2")
381 			.build();
382 
383 		assertList(list, 1, 2);
384 	}
385 
386 	//-----------------------------------------------------------------------------------------------------------------
387 	// AddAny
388 	//-----------------------------------------------------------------------------------------------------------------
389 
390 	@Test
391 	void l01_addAny_withDirectValues() {
392 		var list = Lists.create(String.class)
393 			.addAny("a", "b", "c")
394 			.build();
395 
396 		assertList(list, "a", "b", "c");
397 	}
398 
399 	@Test
400 	void l02_addAny_withCollection() {
401 		var collection = l("a", "b", "c");
402 		var list = Lists.create(String.class)
403 			.addAny(collection)
404 			.build();
405 
406 		assertList(list, "a", "b", "c");
407 	}
408 
409 	@Test
410 	void l03_addAny_withArray() {
411 		var array = new String[]{"a", "b", "c"};
412 		var list = Lists.create(String.class)
413 			.addAny((Object)array)
414 			.build();
415 
416 		assertList(list, "a", "b", "c");
417 	}
418 
419 	@Test
420 	void l04_addAny_withNestedCollection() {
421 		var nested = l(l("a", "b"), l("c", "d"));
422 		var list = Lists.create(String.class)
423 			.addAny(nested)
424 			.build();
425 
426 		assertList(list, "a", "b", "c", "d");
427 	}
428 
429 	@Test
430 	void l05_addAny_withNestedArray() {
431 		var nested = new Object[]{new String[]{"a", "b"}, new String[]{"c", "d"}};
432 		var list = Lists.create(String.class)
433 			.addAny(nested)
434 			.build();
435 
436 		assertList(list, "a", "b", "c", "d");
437 	}
438 
439 	@Test
440 	void l06_addAny_withNullValues() {
441 		var list = Lists.create(String.class)
442 			.addAny("a", null, "b", null, "c")
443 			.build();
444 
445 		assertList(list, "a", "b", "c");
446 	}
447 
448 	@Test
449 	void l07_addAny_withTypeConversion() {
450 		var list = Lists.create(Integer.class)
451 			.elementFunction(o -> {
452 				if (o instanceof String) {
453 					return Integer.parseInt((String)o);
454 				}
455 				return null;
456 			})
457 			.addAny("1", "2", "3")
458 			.build();
459 
460 		assertList(list, 1, 2, 3);
461 	}
462 
463 	@Test
464 	void l08_addAny_withFunctionToCollection() {
465 		// This test verifies that addAny works with collections directly.
466 		// Note: elementFunction is for converting to the element type, not to collections,
467 		// so we test that addAny works with collections directly.
468 		var list = Lists.create(String.class)
469 			.addAny(l("a", "b", "c"))
470 			.build();
471 		assertList(list, "a", "b", "c");
472 	}
473 
474 	@Test
475 	void l09_addAny_noElementType() {
476 		assertThrows(IllegalArgumentException.class, () -> new Lists<String>(null));
477 	}
478 
479 	@Test
480 	void l10_addAny_noElementFunction_throwsException() {
481 		// When elementFunction is null and we try to add a non-matching type, it should throw
482 		assertThrows(RuntimeException.class, () -> {
483 			Lists.create(Integer.class)
484 				.addAny("not-an-integer")
485 				.build();
486 		});
487 	}
488 
489 	@Test
490 	void l11_addAny_elementFunctionReturnsNull() {
491 		// ElementFunction exists but returns null (can't convert)
492 		// Should throw RuntimeException when elementFunction can't convert
493 		assertThrows(RuntimeException.class, () -> {
494 			Lists.create(Integer.class)
495 				.elementFunction(o -> null)  // Can't convert
496 				.addAny("not-an-integer")
497 				.build();
498 		});
499 	}
500 
501 	@Test
502 	void l12_addAny_withNullArray() {
503 		var list = Lists.create(String.class)
504 			.addAny((Object[])null)
505 			.build();
506 
507 		assertEmpty(list);
508 	}
509 
510 	//-----------------------------------------------------------------------------------------------------------------
511 	// Build edge cases
512 	//-----------------------------------------------------------------------------------------------------------------
513 
514 	@Test
515 	void m01_build_sparseWithNullList() {
516 		var list = Lists.create(String.class)
517 			.sparse()
518 			.build();
519 
520 		assertNull(list);
521 	}
522 
523 
524 	@Test
525 	void m03_build_notSparseWithNullList() {
526 		var list = Lists.create(String.class)
527 			.build();
528 
529 		assertNotNull(list);
530 		assertEmpty(list);
531 	}
532 
533 	@Test
534 	void m04_build_sortedWithNullList() {
535 		var list = Lists.create(String.class)
536 			.sorted()
537 			.build();
538 
539 		assertNotNull(list);
540 		assertEmpty(list);
541 	}
542 
543 	@Test
544 	void m05_build_unmodifiableWithNullList() {
545 		var list = Lists.create(String.class)
546 			.unmodifiable()
547 			.build();
548 
549 		assertNotNull(list);
550 		assertThrows(UnsupportedOperationException.class, () -> list.add("a"));
551 	}
552 
553 	//-----------------------------------------------------------------------------------------------------------------
554 	// BuildFluent
555 	//-----------------------------------------------------------------------------------------------------------------
556 
557 	@Test
558 	void n01_buildFluent_returnsFluentList() {
559 		var list = Lists.create(String.class)
560 			.add("a", "b", "c")
561 			.buildFluent();
562 
563 		assertNotNull(list);
564 		assertSize(3, list);
565 		assertList(list, "a", "b", "c");
566 	}
567 
568 	@Test
569 	void n02_buildFluent_sparseEmpty() {
570 		var list = Lists.create(String.class)
571 			.sparse()
572 			.buildFluent();
573 
574 		assertNull(list);
575 	}
576 
577 	@Test
578 	void n03_buildFluent_withSorted() {
579 		var list = Lists.create(String.class)
580 			.add("c", "a", "b")
581 			.sorted()
582 			.buildFluent();
583 
584 		assertNotNull(list);
585 		assertList(list, "a", "b", "c");
586 	}
587 
588 	@Test
589 	void n04_buildFluent_withUnmodifiable() {
590 		var list = Lists.create(String.class)
591 			.add("a", "b", "c")
592 			.unmodifiable()
593 			.buildFluent();
594 
595 		assertNotNull(list);
596 		assertSize(3, list);
597 		assertThrows(UnsupportedOperationException.class, () -> list.add("d"));
598 	}
599 
600 	@Test
601 	void n05_buildFluent_fluentMethods() {
602 		var list = Lists.create(String.class)
603 			.add("a", "b")
604 			.buildFluent();
605 
606 		assertNotNull(list);
607 		// Test that FluentList methods work
608 		list.a("c").aa(l("d", "e"));
609 		assertList(list, "a", "b", "c", "d", "e");
610 	}
611 
612 	//-----------------------------------------------------------------------------------------------------------------
613 	// buildFiltered
614 	//-----------------------------------------------------------------------------------------------------------------
615 
616 	@Test
617 	void o01_buildFiltered_returnsFilteredList() {
618 		var list = Lists.create(String.class)
619 			.add("a", "b", "c")
620 			.buildFiltered();
621 
622 		assertNotNull(list);
623 		assertSize(3, list);
624 		assertList(list, "a", "b", "c");
625 	}
626 
627 	@Test
628 	void o02_buildFiltered_sparseEmpty() {
629 		var list = Lists.create(String.class)
630 			.sparse()
631 			.buildFiltered();
632 
633 		assertNull(list);
634 	}
635 
636 	@Test
637 	void o03_buildFiltered_withFiltering() {
638 		var list = Lists.create(Integer.class)
639 			.filtered(v -> v != null && v > 0)
640 			.add(5, -1, 10, 0)
641 			.buildFiltered();
642 
643 		assertNotNull(list);
644 		assertList(list, 5, 10);
645 	}
646 
647 	//-----------------------------------------------------------------------------------------------------------------
648 	// concurrent
649 	//-----------------------------------------------------------------------------------------------------------------
650 
651 	@Test
652 	void p01_concurrent_createsSynchronizedList() {
653 		var list = Lists.create(String.class)
654 			.add("a", "b", "c")
655 			.concurrent()
656 			.build();
657 
658 		assertNotNull(list);
659 		assertSize(3, list);
660 		// Verify it's synchronized by checking it's wrapped (Collections.synchronizedList returns a wrapper)
661 		assertList(list, "a", "b", "c");
662 	}
663 
664 	@Test
665 	void p02_concurrent_withSorted() {
666 		var list = Lists.create(String.class)
667 			.add("c", "a", "b")
668 			.sorted()
669 			.concurrent()
670 			.build();
671 
672 		assertNotNull(list);
673 		assertList(list, "a", "b", "c");
674 	}
675 
676 	//-----------------------------------------------------------------------------------------------------------------
677 	// filtered
678 	//-----------------------------------------------------------------------------------------------------------------
679 
680 	@Test
681 	void q01_filtered_defaultFiltering() {
682 		var list = Lists.create(Object.class)
683 			.filtered()
684 			.add("a", null, false, -1, new String[0], l(), m())
685 			.build();
686 
687 		assertList(list, "a");
688 	}
689 
690 	@Test
691 	void q02_filtered_withBooleanFalse() {
692 		var list = Lists.create(Boolean.class)
693 			.filtered()
694 			.add(true, false, true)
695 			.build();
696 
697 		assertList(list, true, true);
698 	}
699 
700 	@Test
701 	void q03_filtered_withNumberMinusOne() {
702 		var list = Lists.create(Integer.class)
703 			.filtered()
704 			.add(1, -1, 2, -1, 3)
705 			.build();
706 
707 		assertList(list, 1, 2, 3);
708 	}
709 
710 	@Test
711 	void q04_filtered_withEmptyArray() {
712 		var list = Lists.create(Object.class)
713 			.filtered()
714 			.add("a", new String[0], "b")
715 			.build();
716 
717 		assertList(list, "a", "b");
718 	}
719 
720 	@Test
721 	void q05_filtered_withEmptyMap() {
722 		var list = Lists.create(Object.class)
723 			.filtered()
724 			.add("a", m(), "b")
725 			.build();
726 
727 		assertList(list, "a", "b");
728 	}
729 
730 	@Test
731 	void q06_filtered_withEmptyCollection() {
732 		var list = Lists.create(Object.class)
733 			.filtered()
734 			.add("a", l(), "b")
735 			.build();
736 
737 		assertList(list, "a", "b");
738 	}
739 
740 	//-----------------------------------------------------------------------------------------------------------------
741 	// filtered(Predicate)
742 	//-----------------------------------------------------------------------------------------------------------------
743 
744 	@Test
745 	void r01_filtered_withPredicate() {
746 		var list = Lists.create(Integer.class)
747 			.filtered(v -> v != null && v > 0)
748 			.add(5, -1, 10, 0, 15)
749 			.build();
750 
751 		assertList(list, 5, 10, 15);
752 	}
753 
754 	@Test
755 	void r02_filtered_multipleFilters() {
756 		var list = Lists.create(Integer.class)
757 			.filtered(v -> v != null)
758 			.filtered(v -> v > 0)
759 			.filtered(v -> v < 100)
760 			.add(5, -1, 150, 0, 50, null)
761 			.build();
762 
763 		assertList(list, 5, 50);
764 	}
765 
766 	@Test
767 	void r03_filtered_withStringPredicate() {
768 		var list = Lists.create(String.class)
769 			.filtered(s -> s != null && s.length() > 2)
770 			.add("a", "ab", "abc", "abcd", "")
771 			.build();
772 
773 		assertList(list, "abc", "abcd");
774 	}
775 }
776