This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-collections.git
The following commit(s) were added to refs/heads/master by this push: new 634b23dbb COLLECTIONS-842: Deprecate Lists incompatible with Java 21, add new variant of AbstractLinkedList (#485) 634b23dbb is described below commit 634b23dbbbaf64816685b168aedfcfec2dbe00c1 Author: Julian Reschke <julian.resc...@gmx.de> AuthorDate: Tue May 7 15:54:40 2024 +0200 COLLECTIONS-842: Deprecate Lists incompatible with Java 21, add new variant of AbstractLinkedList (#485) --- .../collections4/list/AbstractLinkedList.java | 2 + ...dList.java => AbstractLinkedListForJava21.java} | 32 +-- .../collections4/list/CursorableLinkedList.java | 2 + .../collections4/list/NodeCachingLinkedList.java | 2 + .../DefaultAbstractLinkedListForJava21Test.java | 316 +++++++++++++++++++++ 5 files changed, 338 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java b/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java index aecb2e948..1d66a792f 100644 --- a/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java +++ b/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java @@ -43,7 +43,9 @@ import org.apache.commons.collections4.OrderedIterator; * * @param <E> the type of elements in this list * @since 3.0 + * @deprecated use {@link AbstractLinkedListForJava21} instead */ +@Deprecated public abstract class AbstractLinkedList<E> implements List<E> { /* diff --git a/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java b/src/main/java/org/apache/commons/collections4/list/AbstractLinkedListForJava21.java similarity index 96% copy from src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java copy to src/main/java/org/apache/commons/collections4/list/AbstractLinkedListForJava21.java index aecb2e948..b6563597d 100644 --- a/src/main/java/org/apache/commons/collections4/list/AbstractLinkedList.java +++ b/src/main/java/org/apache/commons/collections4/list/AbstractLinkedListForJava21.java @@ -39,12 +39,14 @@ import org.apache.commons.collections4.OrderedIterator; * Overridable methods are provided to change the storage node and to change how * nodes are added to and removed. Hopefully, all you need for unusual subclasses * is here. - * </p> + * <p> + * This is a copy of AbstractLinkedList, modified to be compatible with Java 21 + * (see COLLECTIONS-842 for details). * * @param <E> the type of elements in this list * @since 3.0 */ -public abstract class AbstractLinkedList<E> implements List<E> { +public abstract class AbstractLinkedListForJava21<E> implements List<E> { /* * Implementation notes: @@ -65,11 +67,11 @@ public abstract class AbstractLinkedList<E> implements List<E> { protected static class LinkedListIterator<E> implements ListIterator<E>, OrderedIterator<E> { /** The parent list */ - protected final AbstractLinkedList<E> parent; + protected final AbstractLinkedListForJava21<E> parent; /** * The node that will be returned by {@link #next()}. If this is equal - * to {@link AbstractLinkedList#header} then there are no more values to return. + * to {@link AbstractLinkedListForJava21#header} then there are no more values to return. */ protected Node<E> next; @@ -103,7 +105,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { * @param fromIndex the index to start at * @throws IndexOutOfBoundsException if fromIndex is less than 0 or greater than the size of the list */ - protected LinkedListIterator(final AbstractLinkedList<E> parent, final int fromIndex) + protected LinkedListIterator(final AbstractLinkedListForJava21<E> parent, final int fromIndex) throws IndexOutOfBoundsException { this.parent = parent; this.expectedModCount = parent.modCount; @@ -219,13 +221,13 @@ public abstract class AbstractLinkedList<E> implements List<E> { } /** - * The sublist implementation for AbstractLinkedList. + * The sublist implementation for AbstractLinkedListForJava21. * * @param <E> the type of elements in this list. */ protected static class LinkedSubList<E> extends AbstractList<E> { /** The main list */ - AbstractLinkedList<E> parent; + AbstractLinkedListForJava21<E> parent; /** Offset from the main list */ int offset; /** Sublist size */ @@ -233,7 +235,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { /** Sublist modCount */ int expectedModCount; - protected LinkedSubList(final AbstractLinkedList<E> parent, final int fromIndex, final int toIndex) { + protected LinkedSubList(final AbstractLinkedListForJava21<E> parent, final int fromIndex, final int toIndex) { if (fromIndex < 0) { throw new IndexOutOfBoundsException("fromIndex = " + fromIndex); } @@ -522,7 +524,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { * If this constructor is used by a serializable subclass then the init() * method must be called. */ - protected AbstractLinkedList() { + protected AbstractLinkedListForJava21() { } /** @@ -530,7 +532,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { * * @param coll the collection to copy */ - protected AbstractLinkedList(final Collection<? extends E> coll) { + protected AbstractLinkedListForJava21(final Collection<? extends E> coll) { init(); addAll(coll); } @@ -561,14 +563,12 @@ public abstract class AbstractLinkedList<E> implements List<E> { return true; } - public boolean addFirst(final E o) { + public void addFirst(final E o) { addNodeAfter(header, o); - return true; } - public boolean addLast(final E o) { + public void addLast(final E o) { addNodeBefore(header, o); - return true; } /** @@ -594,7 +594,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { * {@code value} and inserts it after {@code node}. * <p> * This implementation uses {@link #createNode(Object)} and - * {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}. + * {@link #addNode(AbstractLinkedListForJava21.Node,AbstractLinkedListForJava21.Node)}. * * @param node node to insert after * @param value value of the newly added node @@ -610,7 +610,7 @@ public abstract class AbstractLinkedList<E> implements List<E> { * {@code value} and inserts it before {@code node}. * <p> * This implementation uses {@link #createNode(Object)} and - * {@link #addNode(AbstractLinkedList.Node,AbstractLinkedList.Node)}. + * {@link #addNode(AbstractLinkedListForJava21.Node,AbstractLinkedListForJava21.Node)}. * * @param node node to insert before * @param value value of the newly added node diff --git a/src/main/java/org/apache/commons/collections4/list/CursorableLinkedList.java b/src/main/java/org/apache/commons/collections4/list/CursorableLinkedList.java index 12bce5c70..d39cb1c1a 100644 --- a/src/main/java/org/apache/commons/collections4/list/CursorableLinkedList.java +++ b/src/main/java/org/apache/commons/collections4/list/CursorableLinkedList.java @@ -57,7 +57,9 @@ import java.util.ListIterator; * * @see java.util.LinkedList * @since 1.0 + * @deprecated parent {@link AbstractLinkedList} is source incompatible with List methods added in Java 21 */ +@Deprecated public class CursorableLinkedList<E> extends AbstractLinkedList<E> implements Serializable { /** diff --git a/src/main/java/org/apache/commons/collections4/list/NodeCachingLinkedList.java b/src/main/java/org/apache/commons/collections4/list/NodeCachingLinkedList.java index 1b379dd4d..defd429af 100644 --- a/src/main/java/org/apache/commons/collections4/list/NodeCachingLinkedList.java +++ b/src/main/java/org/apache/commons/collections4/list/NodeCachingLinkedList.java @@ -40,7 +40,9 @@ import java.util.Collection; * </p> * * @since 3.0 + * @deprecated parent {@link AbstractLinkedList} is source incompatible with List methods added in Java 21 */ +@Deprecated public class NodeCachingLinkedList<E> extends AbstractLinkedList<E> implements Serializable { /** Serialization version */ diff --git a/src/test/java/org/apache/commons/collections4/list/DefaultAbstractLinkedListForJava21Test.java b/src/test/java/org/apache/commons/collections4/list/DefaultAbstractLinkedListForJava21Test.java new file mode 100644 index 000000000..79f4c97af --- /dev/null +++ b/src/test/java/org/apache/commons/collections4/list/DefaultAbstractLinkedListForJava21Test.java @@ -0,0 +1,316 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections4.list; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Arrays; +import java.util.List; + +import org.junit.jupiter.api.Test; + +/** + * Test case for {@link AbstractLinkedListForJava21}. + */ +public class DefaultAbstractLinkedListForJava21Test<E> extends AbstractListTest<E> { + + public DefaultAbstractLinkedListForJava21Test() { + super(DefaultAbstractLinkedListForJava21Test.class.getSimpleName()); + } + + protected void checkNodes() { + final AbstractLinkedListForJava21<E> list = getCollection(); + for (int i = 0; i < list.size; i++) { + assertEquals(list.getNode(i, false).next, list.getNode(i + 1, true)); + if (i < list.size - 1) { + assertEquals(list.getNode(i + 1, false).previous, + list.getNode(i, false)); + } + } + } + + @Test + @SuppressWarnings("unchecked") + public void testAddNodeAfter() { + resetEmpty(); + final AbstractLinkedListForJava21<E> list = getCollection(); + if (!isAddSupported()) { + try { + list.addFirst(null); + } catch (final UnsupportedOperationException ex) {} + } + + list.addFirst((E) "value1"); + list.addNodeAfter(list.getNode(0, false), (E) "value2"); + assertEquals("value1", list.getFirst()); + assertEquals("value2", list.getLast()); + list.removeFirst(); + checkNodes(); + list.addNodeAfter(list.getNode(0, false), (E) "value3"); + checkNodes(); + assertEquals("value2", list.getFirst()); + assertEquals("value3", list.getLast()); + list.addNodeAfter(list.getNode(0, false), (E) "value4"); + checkNodes(); + assertEquals("value2", list.getFirst()); + assertEquals("value3", list.getLast()); + assertEquals("value4", list.get(1)); + list.addNodeAfter(list.getNode(2, false), (E) "value5"); + checkNodes(); + assertEquals("value2", list.getFirst()); + assertEquals("value4", list.get(1)); + assertEquals("value3", list.get(2)); + assertEquals("value5", list.getLast()); + } + + @Test + @SuppressWarnings("unchecked") + public void testGetNode() { + resetEmpty(); + final AbstractLinkedListForJava21<E> list = getCollection(); + // get marker + assertEquals(list.getNode(0, true).previous, list.getNode(0, true).next); + assertThrows(IndexOutOfBoundsException.class, () -> list.getNode(0, false), + "Expecting IndexOutOfBoundsException."); + list.addAll( Arrays.asList((E[]) new String[]{"value1", "value2"})); + checkNodes(); + list.addFirst((E) "value0"); + checkNodes(); + list.removeNode(list.getNode(1, false)); + checkNodes(); + assertThrows(IndexOutOfBoundsException.class, () -> list.getNode(2, false), + "Expecting IndexOutOfBoundsException."); + assertThrows(IndexOutOfBoundsException.class, () -> list.getNode(-1, false), + "Expecting IndexOutOfBoundsException."); + assertThrows(IndexOutOfBoundsException.class, () -> list.getNode(3, true), + "Expecting IndexOutOfBoundsException."); + } + + @Test + @SuppressWarnings("unchecked") + public void testRemoveFirst() { + resetEmpty(); + final AbstractLinkedListForJava21<E> list = getCollection(); + if (!isRemoveSupported()) { + try { + list.removeFirst(); + } catch (final UnsupportedOperationException ex) {} + } + + list.addAll(Arrays.asList((E[]) new String[] { "value1", "value2" })); + assertEquals("value1", list.removeFirst()); + checkNodes(); + list.addLast((E) "value3"); + checkNodes(); + assertEquals("value2", list.removeFirst()); + assertEquals("value3", list.removeFirst()); + checkNodes(); + list.addLast((E) "value4"); + checkNodes(); + assertEquals("value4", list.removeFirst()); + checkNodes(); + } + + @Test + @SuppressWarnings("unchecked") + public void testRemoveLast() { + resetEmpty(); + final AbstractLinkedListForJava21<E> list = getCollection(); + if (!isRemoveSupported()) { + try { + list.removeLast(); + } catch (final UnsupportedOperationException ex) {} + } + + list.addAll(Arrays.asList((E[]) new String[] { "value1", "value2" })); + assertEquals("value2", list.removeLast()); + list.addFirst((E) "value3"); + checkNodes(); + assertEquals("value1", list.removeLast()); + assertEquals("value3", list.removeLast()); + list.addFirst((E) "value4"); + checkNodes(); + assertEquals("value4", list.removeFirst()); + } + + @Test + @SuppressWarnings("unchecked") + public void testRemoveNode() { + resetEmpty(); + if (!isAddSupported() || !isRemoveSupported()) { + return; + } + final AbstractLinkedListForJava21<E> list = getCollection(); + + list.addAll(Arrays.asList((E[]) new String[] { "value1", "value2" })); + list.removeNode(list.getNode(0, false)); + checkNodes(); + assertEquals("value2", list.getFirst()); + assertEquals("value2", list.getLast()); + list.addFirst((E) "value1"); + list.addFirst((E) "value0"); + checkNodes(); + list.removeNode(list.getNode(1, false)); + assertEquals("value0", list.getFirst()); + assertEquals("value2", list.getLast()); + checkNodes(); + list.removeNode(list.getNode(1, false)); + assertEquals("value0", list.getFirst()); + assertEquals("value0", list.getLast()); + checkNodes(); + } + + @Override + public String getCompatibilityVersion() { + return null; + } + + @Override + protected boolean skipSerializedCanonicalTests() { + return true; + } + + @Override + public AbstractLinkedListForJava21<E> getCollection() { + return (AbstractLinkedListForJava21<E>) super.getCollection(); + } + + @Override + public List<E> makeObject() { + return new DefaultAbstractLinkedListForJava21<>(); + } + + @Test + @SuppressWarnings("unchecked") + public void testSubList() { + List<E> list = makeObject(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + + assertEquals("[A, B, C, D, E]", list.toString()); + assertEquals("[A, B, C, D, E]", list.subList(0, 5).toString()); + assertEquals("[B, C, D, E]", list.subList(1, 5).toString()); + assertEquals("[C, D, E]", list.subList(2, 5).toString()); + assertEquals("[D, E]", list.subList(3, 5).toString()); + assertEquals("[E]", list.subList(4, 5).toString()); + assertEquals("[]", list.subList(5, 5).toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSubListAddBegin() { + List<E> list = makeObject(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + + final List<E> sublist = list.subList(0, 0); + sublist.add((E) "a"); + assertEquals("[a, A, B, C, D, E]", list.toString()); + assertEquals("[a]", sublist.toString()); + sublist.add((E) "b"); + assertEquals("[a, b, A, B, C, D, E]", list.toString()); + assertEquals("[a, b]", sublist.toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSubListAddEnd() { + List<E> list = makeObject(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + + final List<E> sublist = list.subList(5, 5); + sublist.add((E) "F"); + assertEquals("[A, B, C, D, E, F]", list.toString()); + assertEquals("[F]", sublist.toString()); + sublist.add((E) "G"); + assertEquals("[A, B, C, D, E, F, G]", list.toString()); + assertEquals("[F, G]", sublist.toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSubListAddMiddle() { + List<E> list = makeObject(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + + final List<E> sublist = list.subList(1, 3); + sublist.add((E) "a"); + assertEquals("[A, B, C, a, D, E]", list.toString()); + assertEquals("[B, C, a]", sublist.toString()); + sublist.add((E) "b"); + assertEquals("[A, B, C, a, b, D, E]", list.toString()); + assertEquals("[B, C, a, b]", sublist.toString()); + } + + @Test + @SuppressWarnings("unchecked") + public void testSubListRemove() { + List<E> list = makeObject(); + list.add((E) "A"); + list.add((E) "B"); + list.add((E) "C"); + list.add((E) "D"); + list.add((E) "E"); + + final List<E> sublist = list.subList(1, 4); + assertEquals("[B, C, D]", sublist.toString()); + assertEquals("[A, B, C, D, E]", list.toString()); + sublist.remove("C"); + assertEquals("[B, D]", sublist.toString()); + assertEquals("[A, B, D, E]", list.toString()); + sublist.remove(1); + assertEquals("[B]", sublist.toString()); + assertEquals("[A, B, E]", list.toString()); + sublist.clear(); + assertEquals("[]", sublist.toString()); + assertEquals("[A, E]", list.toString()); + } + + private static class DefaultAbstractLinkedListForJava21<E> extends AbstractLinkedListForJava21<E> { + DefaultAbstractLinkedListForJava21() { + init(); + } + + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + } +}