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.junit.bct.BctAssertions.*;
20  import static org.junit.jupiter.api.Assertions.*;
21  
22  import java.util.*;
23  
24  import org.apache.juneau.*;
25  import org.junit.jupiter.api.*;
26  
27  class ReversedList_Test extends TestBase {
28  
29  	//====================================================================================================
30  	// Basic functionality
31  	//====================================================================================================
32  
33  	@Test
34  	void a01_basicGet() {
35  		var original = List.of("A", "B", "C");
36  		var reversed = new ReversedList<>(original);
37  
38  		assertEquals("C", reversed.get(0));
39  		assertEquals("B", reversed.get(1));
40  		assertEquals("A", reversed.get(2));
41  		assertSize(3, reversed);
42  	}
43  
44  	@Test
45  	void a02_basicIteration() {
46  		var original = List.of("A", "B", "C");
47  		var reversed = new ReversedList<>(original);
48  
49  		var result = new ArrayList<String>();
50  		for (String s : reversed) {
51  			result.add(s);
52  		}
53  
54  		assertEquals(List.of("C", "B", "A"), result);
55  	}
56  
57  	@Test
58  	void a03_emptyList() {
59  		var original = List.<String>of();
60  		var reversed = new ReversedList<>(original);
61  
62  		assertEmpty(reversed);
63  		assertFalse(reversed.iterator().hasNext());
64  	}
65  
66  	@Test
67  	void a04_singleElement() {
68  		var original = List.of("A");
69  		var reversed = new ReversedList<>(original);
70  
71  		assertSize(1, reversed);
72  		assertEquals("A", reversed.get(0));
73  	}
74  
75  	//====================================================================================================
76  	// Null handling
77  	//====================================================================================================
78  
79  	@Test
80  	void b01_nullList_throwsException() {
81  		assertThrows(IllegalArgumentException.class, () -> new ReversedList<>(null));
82  	}
83  
84  	@Test
85  	void b02_listWithNulls() {
86  		var original = Arrays.asList("A", null, "C");
87  		var reversed = new ReversedList<>(original);
88  
89  		assertEquals("C", reversed.get(0));
90  		assertNull(reversed.get(1));
91  		assertEquals("A", reversed.get(2));
92  	}
93  
94  	//====================================================================================================
95  	// Index bounds
96  	//====================================================================================================
97  
98  	@Test
99  	void c01_outOfBounds_negative() {
100 		var original = List.of("A", "B", "C");
101 		var reversed = new ReversedList<>(original);
102 
103 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.get(-1));
104 	}
105 
106 	@Test
107 	void c02_outOfBounds_tooLarge() {
108 		var original = List.of("A", "B", "C");
109 		var reversed = new ReversedList<>(original);
110 
111 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.get(3));
112 	}
113 
114 	//====================================================================================================
115 	// Reflection of underlying list changes
116 	//====================================================================================================
117 
118 	@Test
119 	void d01_reflectsUnderlyingChanges() {
120 		var original = new ArrayList<>(Arrays.asList("A", "B", "C"));
121 		var reversed = new ReversedList<>(original);
122 
123 		assertEquals("C", reversed.get(0));
124 
125 		original.add("D");
126 
127 		assertSize(4, reversed);
128 		assertEquals("D", reversed.get(0));
129 		assertEquals("C", reversed.get(1));
130 	}
131 
132 	@Test
133 	void d02_reflectsUnderlyingRemoval() {
134 		var original = new ArrayList<>(Arrays.asList("A", "B", "C"));
135 		var reversed = new ReversedList<>(original);
136 
137 		original.remove(2); // Remove "C"
138 
139 		assertSize(2, reversed);
140 		assertEquals("B", reversed.get(0));
141 		assertEquals("A", reversed.get(1));
142 	}
143 
144 	//====================================================================================================
145 	// Read-only enforcement
146 	//====================================================================================================
147 
148 	@Test
149 	void e01_add_throwsException() {
150 		var original = List.of("A", "B", "C");
151 		var reversed = new ReversedList<>(original);
152 
153 		assertThrows(UnsupportedOperationException.class, () -> reversed.add("D"));
154 	}
155 
156 	@Test
157 	void e02_addAtIndex_throwsException() {
158 		var original = List.of("A", "B", "C");
159 		var reversed = new ReversedList<>(original);
160 
161 		assertThrows(UnsupportedOperationException.class, () -> reversed.add(0, "D"));
162 	}
163 
164 	@Test
165 	void e03_remove_throwsException() {
166 		var original = List.of("A", "B", "C");
167 		var reversed = new ReversedList<>(original);
168 
169 		assertThrows(UnsupportedOperationException.class, () -> reversed.remove(0));
170 	}
171 
172 	@Test
173 	void e04_set_throwsException() {
174 		var original = List.of("A", "B", "C");
175 		var reversed = new ReversedList<>(original);
176 
177 		assertThrows(UnsupportedOperationException.class, () -> reversed.set(0, "D"));
178 	}
179 
180 	@Test
181 	void e05_clear_throwsException() {
182 		var original = List.of("A", "B", "C");
183 		var reversed = new ReversedList<>(original);
184 
185 		assertThrows(UnsupportedOperationException.class, () -> reversed.clear());
186 	}
187 
188 	@Test
189 	void e06_iteratorRemove_throwsException() {
190 		var original = List.of("A", "B", "C");
191 		var reversed = new ReversedList<>(original);
192 
193 		var it = reversed.iterator();
194 		it.next();
195 		assertThrows(UnsupportedOperationException.class, () -> it.remove());
196 	}
197 
198 	//====================================================================================================
199 	// Iterator functionality
200 	//====================================================================================================
201 
202 	@Test
203 	void f01_iterator_traversal() {
204 		var original = List.of("A", "B", "C", "D");
205 		var reversed = new ReversedList<>(original);
206 
207 		var it = reversed.iterator();
208 		assertTrue(it.hasNext());
209 		assertEquals("D", it.next());
210 		assertTrue(it.hasNext());
211 		assertEquals("C", it.next());
212 		assertTrue(it.hasNext());
213 		assertEquals("B", it.next());
214 		assertTrue(it.hasNext());
215 		assertEquals("A", it.next());
216 		assertFalse(it.hasNext());
217 	}
218 
219 	@Test
220 	void f02_listIterator_forward() {
221 		var original = List.of("A", "B", "C");
222 		var reversed = new ReversedList<>(original);
223 
224 		var it = reversed.listIterator();
225 		assertEquals("C", it.next());
226 		assertEquals("B", it.next());
227 		assertEquals("A", it.next());
228 		assertFalse(it.hasNext());
229 	}
230 
231 	@Test
232 	void f03_listIterator_backward() {
233 		var original = List.of("A", "B", "C");
234 		var reversed = new ReversedList<>(original);
235 
236 		var it = reversed.listIterator(3);
237 		assertEquals("A", it.previous());
238 		assertEquals("B", it.previous());
239 		assertEquals("C", it.previous());
240 		assertFalse(it.hasPrevious());
241 	}
242 
243 	@Test
244 	void f04_listIterator_bidirectional() {
245 		var original = List.of("A", "B", "C");
246 		var reversed = new ReversedList<>(original);
247 
248 		var it = reversed.listIterator(1);
249 		assertEquals("B", it.next());
250 		assertEquals("B", it.previous());
251 		assertEquals("C", it.previous());
252 		assertEquals("C", it.next());
253 	}
254 
255 	@Test
256 	void f05_listIterator_indices() {
257 		var original = List.of("A", "B", "C");
258 		var reversed = new ReversedList<>(original);
259 
260 		var it = reversed.listIterator();
261 		assertEquals(-1, it.previousIndex());
262 		assertEquals(0, it.nextIndex());
263 
264 		it.next();
265 		assertEquals(0, it.previousIndex());
266 		assertEquals(1, it.nextIndex());
267 
268 		it.next();
269 		assertEquals(1, it.previousIndex());
270 		assertEquals(2, it.nextIndex());
271 
272 		it.next();
273 		assertEquals(2, it.previousIndex());
274 		assertEquals(3, it.nextIndex());
275 	}
276 
277 	@Test
278 	void f06_listIterator_modificationThrows() {
279 		var original = List.of("A", "B", "C");
280 		var reversed = new ReversedList<>(original);
281 
282 		var it = reversed.listIterator();
283 		it.next();
284 
285 		assertThrows(UnsupportedOperationException.class, () -> it.remove());
286 		assertThrows(UnsupportedOperationException.class, () -> it.set("X"));
287 		assertThrows(UnsupportedOperationException.class, () -> it.add("X"));
288 	}
289 
290 	//====================================================================================================
291 	// SubList functionality
292 	//====================================================================================================
293 
294 	@Test
295 	void g01_subList_basic() {
296 		var original = List.of("A", "B", "C", "D", "E");
297 		var reversed = new ReversedList<>(original);
298 
299 		var subList = reversed.subList(1, 4);
300 
301 		assertSize(3, subList);
302 		assertEquals("D", subList.get(0));
303 		assertEquals("C", subList.get(1));
304 		assertEquals("B", subList.get(2));
305 	}
306 
307 	@Test
308 	void g02_subList_empty() {
309 		var original = List.of("A", "B", "C");
310 		var reversed = new ReversedList<>(original);
311 
312 		var subList = reversed.subList(1, 1);
313 
314 		assertEmpty(subList);
315 	}
316 
317 	@Test
318 	void g03_subList_full() {
319 		var original = List.of("A", "B", "C");
320 		var reversed = new ReversedList<>(original);
321 
322 		var subList = reversed.subList(0, 3);
323 
324 		assertSize(3, subList);
325 		assertEquals("C", subList.get(0));
326 		assertEquals("B", subList.get(1));
327 		assertEquals("A", subList.get(2));
328 	}
329 
330 	@Test
331 	void g04_subList_outOfBounds() {
332 		var original = List.of("A", "B", "C");
333 		var reversed = new ReversedList<>(original);
334 
335 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.subList(-1, 2));
336 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.subList(0, 4));
337 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.subList(2, 1));
338 	}
339 
340 	@Test
341 	void g05_subList_reflectsChanges() {
342 		var original = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E"));
343 		var reversed = new ReversedList<>(original);
344 		var subList = reversed.subList(1, 4);
345 
346 		original.set(3, "X"); // Changes "D" to "X" in original
347 
348 		assertEquals("X", subList.get(0));
349 	}
350 
351 	//====================================================================================================
352 	// Contains and indexOf
353 	//====================================================================================================
354 
355 	@Test
356 	void h01_contains() {
357 		var original = List.of("A", "B", "C");
358 		var reversed = new ReversedList<>(original);
359 
360 		assertTrue(reversed.contains("A"));
361 		assertTrue(reversed.contains("B"));
362 		assertTrue(reversed.contains("C"));
363 		assertFalse(reversed.contains("D"));
364 	}
365 
366 	@Test
367 	void h02_indexOf() {
368 		var original = List.of("A", "B", "C");
369 		var reversed = new ReversedList<>(original);
370 
371 		assertEquals(2, reversed.indexOf("A"));
372 		assertEquals(1, reversed.indexOf("B"));
373 		assertEquals(0, reversed.indexOf("C"));
374 		assertEquals(-1, reversed.indexOf("D"));
375 	}
376 
377 	@Test
378 	void h03_lastIndexOf() {
379 		var original = List.of("A", "B", "A", "C");
380 		var reversed = new ReversedList<>(original);
381 
382 		// Original: ["A", "B", "A", "C"]
383 		// Reversed: ["C", "A", "B", "A"]
384 		// First "A" in reversed is at index 1, last "A" is at index 3
385 		assertEquals(3, reversed.lastIndexOf("A"));
386 		assertEquals(0, reversed.lastIndexOf("C"));
387 	}
388 
389 	//====================================================================================================
390 	// Edge cases
391 	//====================================================================================================
392 
393 	@Test
394 	void i01_largeList() {
395 		var original = new ArrayList<Integer>();
396 		for (var i = 0; i < 1000; i++) {
397 			original.add(i);
398 		}
399 
400 		var reversed = new ReversedList<>(original);
401 
402 		assertSize(1000, reversed);
403 		assertEquals(999, reversed.get(0));
404 		assertEquals(0, reversed.get(999));
405 	}
406 
407 	@Test
408 	void i02_toArray() {
409 		var original = List.of("A", "B", "C");
410 		var reversed = new ReversedList<>(original);
411 
412 		var array = reversed.toArray();
413 
414 		assertEquals(3, array.length);
415 		assertEquals("C", array[0]);
416 		assertEquals("B", array[1]);
417 		assertEquals("A", array[2]);
418 	}
419 
420 	@Test
421 	void i03_toArrayTyped() {
422 		var original = List.of("A", "B", "C");
423 		var reversed = new ReversedList<>(original);
424 
425 		var array = reversed.toArray(new String[0]);
426 
427 		assertEquals(3, array.length);
428 		assertEquals("C", array[0]);
429 		assertEquals("B", array[1]);
430 		assertEquals("A", array[2]);
431 	}
432 
433 	//====================================================================================================
434 	// listIterator with index parameter
435 	//====================================================================================================
436 
437 	@Test
438 	void j01_listIterator_withIndex_start() {
439 		var original = List.of("A", "B", "C", "D");
440 		var reversed = new ReversedList<>(original);
441 
442 		var it = reversed.listIterator(0);
443 		assertEquals("D", it.next());
444 		assertEquals("C", it.next());
445 		assertEquals("B", it.next());
446 		assertEquals("A", it.next());
447 		assertFalse(it.hasNext());
448 	}
449 
450 	@Test
451 	void j02_listIterator_withIndex_middle() {
452 		var original = List.of("A", "B", "C", "D");
453 		var reversed = new ReversedList<>(original);
454 
455 		var it = reversed.listIterator(2);
456 		assertEquals("B", it.next());
457 		assertEquals("A", it.next());
458 		assertFalse(it.hasNext());
459 	}
460 
461 	@Test
462 	void j03_listIterator_withIndex_end() {
463 		var original = List.of("A", "B", "C", "D");
464 		var reversed = new ReversedList<>(original);
465 
466 		var it = reversed.listIterator(4);
467 		assertFalse(it.hasNext());
468 		assertTrue(it.hasPrevious());
469 		assertEquals("A", it.previous());
470 	}
471 
472 	@Test
473 	void j04_listIterator_withIndex_bidirectional() {
474 		var original = List.of("A", "B", "C", "D");
475 		var reversed = new ReversedList<>(original);
476 
477 		var it = reversed.listIterator(2);
478 		assertEquals(2, it.nextIndex());
479 		assertEquals(1, it.previousIndex());
480 		assertEquals("B", it.next());
481 		assertEquals(3, it.nextIndex());
482 		assertEquals(2, it.previousIndex());
483 		assertEquals("B", it.previous());
484 		assertEquals("C", it.previous());
485 	}
486 
487 	@Test
488 	void j05_listIterator_withIndex_outOfBounds_negative() {
489 		var original = List.of("A", "B", "C");
490 		var reversed = new ReversedList<>(original);
491 
492 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.listIterator(-1));
493 	}
494 
495 	@Test
496 	void j06_listIterator_withIndex_outOfBounds_tooLarge() {
497 		var original = List.of("A", "B", "C");
498 		var reversed = new ReversedList<>(original);
499 
500 		assertThrows(IndexOutOfBoundsException.class, () -> reversed.listIterator(4));
501 	}
502 
503 	@Test
504 	void j07_listIterator_withIndex_emptyList() {
505 		var original = List.<String>of();
506 		var reversed = new ReversedList<>(original);
507 
508 		var it = reversed.listIterator(0);
509 		assertFalse(it.hasNext());
510 		assertFalse(it.hasPrevious());
511 	}
512 
513 	//====================================================================================================
514 	// toString(), equals(), hashCode()
515 	//====================================================================================================
516 
517 	@Test
518 	void k01_toString_showsReversedOrder() {
519 		var original = new ArrayList<>(List.of("a", "b", "c"));
520 		var reversed = new ReversedList<>(original);
521 
522 		// ReversedList.toString() should show the reversed order
523 		// The underlying list is ["a", "b", "c"], so reversed should show ["c", "b", "a"]
524 		var expected = "[c, b, a]";
525 		assertEquals(expected, reversed.toString());
526 	}
527 
528 	@Test
529 	void k02_equals_sameContents() {
530 		var original1 = new ArrayList<>(List.of("a", "b", "c"));
531 		var reversed1 = new ReversedList<>(original1);
532 
533 		var original2 = new ArrayList<>(List.of("a", "b", "c"));
534 		var reversed2 = new ReversedList<>(original2);
535 
536 		// ReversedList.equals() compares in reversed order
537 		assertTrue(reversed1.equals(reversed2));
538 		assertTrue(reversed2.equals(reversed1));
539 	}
540 
541 	@Test
542 	void k03_equals_differentContents() {
543 		var original1 = new ArrayList<>(List.of("a", "b", "c"));
544 		var reversed1 = new ReversedList<>(original1);
545 
546 		var original2 = new ArrayList<>(List.of("a", "b", "d"));
547 		var reversed2 = new ReversedList<>(original2);
548 
549 		assertFalse(reversed1.equals(reversed2));
550 		assertFalse(reversed2.equals(reversed1));
551 	}
552 
553 	@Test
554 	void k04_equals_regularList() {
555 		var original = new ArrayList<>(List.of("a", "b", "c"));
556 		var reversed = new ReversedList<>(original);
557 
558 		// A reversed list ["c", "b", "a"] should equal a regular list ["c", "b", "a"]
559 		var regularList = new ArrayList<>(List.of("c", "b", "a"));
560 
561 		assertTrue(reversed.equals(regularList));
562 		assertTrue(regularList.equals(reversed));
563 	}
564 
565 	@Test
566 	void k05_equals_notAList() {
567 		var original = new ArrayList<>(List.of("a", "b", "c"));
568 		var reversed = new ReversedList<>(original);
569 
570 		assertFalse(reversed.equals(null));
571 	}
572 
573 	@Test
574 	void k06_hashCode_sameContents() {
575 		var original1 = new ArrayList<>(List.of("a", "b", "c"));
576 		var reversed1 = new ReversedList<>(original1);
577 
578 		var original2 = new ArrayList<>(List.of("a", "b", "c"));
579 		var reversed2 = new ReversedList<>(original2);
580 
581 		assertEquals(reversed1.hashCode(), reversed2.hashCode());
582 	}
583 
584 	@Test
585 	void k07_hashCode_regularList() {
586 		var original = new ArrayList<>(List.of("a", "b", "c"));
587 		var reversed = new ReversedList<>(original);
588 
589 		// A reversed list ["c", "b", "a"] should have same hash as a regular list ["c", "b", "a"]
590 		var regularList = new ArrayList<>(List.of("c", "b", "a"));
591 
592 		assertEquals(reversed.hashCode(), regularList.hashCode());
593 	}
594 
595 	//====================================================================================================
596 	// Additional coverage for specific lines
597 	//====================================================================================================
598 
599 	@Test
600 	void l01_equals_differentLengths() {
601 		// Line 320: while (e1.hasNext() && e2.hasNext())
602 		// Line 326: return !(e1.hasNext() || e2.hasNext());
603 		// Test when lists have different lengths - one iterator exhausted before the other
604 		var original1 = new ArrayList<>(List.of("a", "b", "c"));
605 		var reversed1 = new ReversedList<>(original1);
606 
607 		var original2 = new ArrayList<>(List.of("a", "b", "c", "d"));
608 		var reversed2 = new ReversedList<>(original2);
609 
610 		// reversed1: ["c", "b", "a"]
611 		// reversed2: ["d", "c", "b", "a"]
612 		// After comparing first 3 elements, e1 is exhausted but e2 has more
613 		// Line 326: return !(e1.hasNext() || e2.hasNext()) should return false
614 		assertFalse(reversed1.equals(reversed2));
615 		assertFalse(reversed2.equals(reversed1));
616 	}
617 
618 	@Test
619 	void l02_equals_oneExhausted() {
620 		// Line 326: return !(e1.hasNext() || e2.hasNext());
621 		// Test when one iterator is exhausted before the other
622 		var original1 = new ArrayList<>(List.of("a", "b"));
623 		var reversed1 = new ReversedList<>(original1);
624 
625 		var original2 = new ArrayList<>(List.of("a", "b", "c"));
626 		var reversed2 = new ReversedList<>(original2);
627 
628 		// reversed1: ["b", "a"]
629 		// reversed2: ["c", "b", "a"]
630 		// After comparing first 2 elements, e1 is exhausted but e2 has more
631 		assertFalse(reversed1.equals(reversed2));
632 	}
633 
634 	@Test
635 	void l03_hashCode_withNullElements() {
636 		// Line 356: hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
637 		// Test hashCode with null elements
638 		var original = new ArrayList<>(Arrays.asList("a", null, "c"));
639 		var reversed = new ReversedList<>(original);
640 
641 		// Calculate expected hashCode manually (null contributes 0)
642 		// Reversed order: ["c", null, "a"]
643 		int expectedHashCode = 1;
644 		expectedHashCode = 31 * expectedHashCode + "c".hashCode();
645 		expectedHashCode = 31 * expectedHashCode + 0; // null
646 		expectedHashCode = 31 * expectedHashCode + "a".hashCode();
647 
648 		assertEquals(expectedHashCode, reversed.hashCode());
649 	}
650 }
651