diff --git a/RELEASE-NOTES.html b/RELEASE-NOTES.html index b0b71b3e0..4130ffb31 100644 --- a/RELEASE-NOTES.html +++ b/RELEASE-NOTES.html @@ -43,6 +43,7 @@ If this causes major headaches to anyone please contact commons-dev at jakarta.a
List
to make it seemlessly grow when
+ * indices larger than the list size are used on add and set,
+ * avoiding most IndexOutOfBoundsExceptions.
+ * + * This class avoids errors by growing when a set or add method would + * normally throw an IndexOutOfBoundsException. + * Note that IndexOutOfBoundsException IS returned for invalid negative indices. + *
+ * Trying to set or add to an index larger than the size will cause the list
+ * to grow (using null
elements). Clearly, care must be taken
+ * not to use excessively large indices, as the internal list will grow to
+ * match.
+ *
+ * Trying to use any method other than add or set with an invalid inde will + * call the underlying list and probably result in an IndexOutOfBoundsException. + *
+ * Take care when using this list with null
values, as
+ * null
is the value added when growing the list.
+ *
+ * All sub-lists will access the underlying list directly, and will throw + * IndexOutOfBoundsExceptions. + *
+ * This class differs from {@link LazyList} because here growth occurs on
+ * set and add, where LazyList
grows on get. However, they
+ * could easily be used together by decorating twice.
+ *
+ * @see LazyList
+ * @since Commons Collections 3.2
+ * @version $Revision: 155406 $ $Date$
+ *
+ * @author Stephen Colebourne
+ * @author Paul Legato
+ */
+public class GrowthList extends AbstractSerializableListDecorator {
+
+ /** Serialization version */
+ private static final long serialVersionUID = -3620001881672L;
+
+ /**
+ * Factory method to create a growth list.
+ *
+ * @param list the list to decorate, must not be null
+ * @throws IllegalArgumentException if list is null
+ */
+ public static List decorate(List list) {
+ return new GrowthList(list);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Constructor that uses an ArrayList internally.
+ */
+ public GrowthList() {
+ super(new ArrayList());
+ }
+
+ /**
+ * Constructor that uses an ArrayList internally.
+ *
+ * @param initialSize the initial size of the ArrayList
+ * @throws IllegalArgumentException if initial size is invalid
+ */
+ public GrowthList(int initialSize) {
+ super(new ArrayList(initialSize));
+ }
+
+ /**
+ * Constructor that wraps (not copies).
+ *
+ * @param list the list to decorate, must not be null
+ * @throws IllegalArgumentException if list is null
+ */
+ protected GrowthList(List list) {
+ super(list);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Decorate the add method to perform the growth behaviour.
+ *
+ * If the requested index is greater than the current size, the list will
+ * grow to the new size. Indices between the old size and the requested
+ * size will be filled with null
.
+ *
+ * If the index is less than the current size, the value will be added to + * the underlying list directly. + * If the index is less than zero, the underlying list is called, which + * will probably throw an IndexOutOfBoundsException. + * + * @param index the index to add at + * @param element the object to add at the specified index + * @throws UnsupportedOperationException if the underlying list doesn't implement set + * @throws ClassCastException if the underlying list rejects the element + * @throws IllegalArgumentException if the underlying list rejects the element + */ + public void add(int index, Object element) { + int size = getList().size(); + if (index > size) { + getList().addAll(Collections.nCopies(index - size, null)); + } + getList().add(index, element); + } + + //----------------------------------------------------------------------- + /** + * Decorate the addAll method to perform the growth behaviour. + *
+ * If the requested index is greater than the current size, the list will
+ * grow to the new size. Indices between the old size and the requested
+ * size will be filled with null
.
+ *
+ * If the index is less than the current size, the values will be added to + * the underlying list directly. + * If the index is less than zero, the underlying list is called, which + * will probably throw an IndexOutOfBoundsException. + * + * @param index the index to add at + * @param coll the collection to add at the specified index + * @return true if the list changed + * @throws UnsupportedOperationException if the underlying list doesn't implement set + * @throws ClassCastException if the underlying list rejects the element + * @throws IllegalArgumentException if the underlying list rejects the element + */ + public boolean addAll(int index, Collection coll) { + int size = getList().size(); + boolean result = false; + if (index > size) { + getList().addAll(Collections.nCopies(index - size, null)); + result = true; + } + return (getList().addAll(index, coll) | result); + } + + //----------------------------------------------------------------------- + /** + * Decorate the set method to perform the growth behaviour. + *
+ * If the requested index is greater than the current size, the list will
+ * grow to the new size. Indices between the old size and the requested
+ * size will be filled with null
.
+ *
+ * If the index is less than the current size, the value will be set onto
+ * the underlying list directly.
+ * If the index is less than zero, the underlying list is called, which
+ * will probably throw an IndexOutOfBoundsException.
+ *
+ * @param index the index to set
+ * @param element the object to set at the specified index
+ * @return the object previously at that index
+ * @throws UnsupportedOperationException if the underlying list doesn't implement set
+ * @throws ClassCastException if the underlying list rejects the element
+ * @throws IllegalArgumentException if the underlying list rejects the element
+ */
+ public Object set(int index, Object element) {
+ int size = getList().size();
+ if (index >= size) {
+ getList().addAll(Collections.nCopies((index - size) + 1, null));
+ }
+ return getList().set(index, element);
+ }
+
+}
diff --git a/src/java/org/apache/commons/collections/list/LazyList.java b/src/java/org/apache/commons/collections/list/LazyList.java
index fe298805b..ab50b218d 100644
--- a/src/java/org/apache/commons/collections/list/LazyList.java
+++ b/src/java/org/apache/commons/collections/list/LazyList.java
@@ -46,8 +46,13 @@ import org.apache.commons.collections.Factory;
* instance is the fourth element in the list. The first, second,
* and third element are all set to null
.
*
+ * This class differs from {@link GrowthList} because here growth occurs on
+ * get, where GrowthList
grows on set and add. However, they
+ * could easily be used together by decorating twice.
+ *
* This class is Serializable from Commons Collections 3.1. * + * @see GrowthList * @since Commons Collections 3.0 * @version $Revision$ $Date$ * diff --git a/src/test/org/apache/commons/collections/list/TestAll.java b/src/test/org/apache/commons/collections/list/TestAll.java index e7e7a9440..36bad4a0f 100644 --- a/src/test/org/apache/commons/collections/list/TestAll.java +++ b/src/test/org/apache/commons/collections/list/TestAll.java @@ -46,6 +46,7 @@ public class TestAll extends TestCase { suite.addTest(TestTreeList.suite()); suite.addTest(TestFixedSizeList.suite()); + suite.addTest(TestGrowthList.suite()); suite.addTest(TestPredicatedList.suite()); suite.addTest(TestSetUniqueList.suite()); suite.addTest(TestSynchronizedList.suite()); diff --git a/src/test/org/apache/commons/collections/list/TestGrowthList.java b/src/test/org/apache/commons/collections/list/TestGrowthList.java new file mode 100644 index 000000000..68578b515 --- /dev/null +++ b/src/test/org/apache/commons/collections/list/TestGrowthList.java @@ -0,0 +1,175 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.collections.list; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import junit.framework.Test; +import junit.framework.TestSuite; + +/** + * Extension of {@link TestList} for exercising the {@link GrowthList}. + * + * @since Commons Collections 3.2 + * @version $Revision: 155406 $ $Date$ + * + * @author Stephen Colebourne + */ +public class TestGrowthList extends AbstractTestList { + + public TestGrowthList(String testName) { + super(testName); + } + + public static Test suite() { + return new TestSuite(TestGrowthList.class); + } + + public static void main(String args[]) { + String[] testCaseName = { TestGrowthList.class.getName()}; + junit.textui.TestRunner.main(testCaseName); + } + + public List makeEmptyList() { + return new GrowthList(); + } + + public List makeFullList() { + List list = new ArrayList(); + list.addAll(Arrays.asList(getFullElements())); + return GrowthList.decorate(list); + } + + //----------------------------------------------------------------------- + public void testGrowthAdd() { + Integer one = new Integer(1); + GrowthList grower = new GrowthList(); + assertEquals(0, grower.size()); + grower.add(1, one); + assertEquals(2, grower.size()); + assertEquals(null, grower.get(0)); + assertEquals(one, grower.get(1)); + } + + public void testGrowthAddAll() { + Integer one = new Integer(1); + Integer two = new Integer(2); + Collection coll = new ArrayList(); + coll.add(one); + coll.add(two); + GrowthList grower = new GrowthList(); + assertEquals(0, grower.size()); + grower.addAll(1, coll); + assertEquals(3, grower.size()); + assertEquals(null, grower.get(0)); + assertEquals(one, grower.get(1)); + assertEquals(two, grower.get(2)); + } + + public void testGrowthSet1() { + Integer one = new Integer(1); + GrowthList grower = new GrowthList(); + assertEquals(0, grower.size()); + grower.set(1, one); + assertEquals(2, grower.size()); + assertEquals(null, grower.get(0)); + assertEquals(one, grower.get(1)); + } + + public void testGrowthSet2() { + Integer one = new Integer(1); + GrowthList grower = new GrowthList(); + assertEquals(0, grower.size()); + grower.set(0, one); + assertEquals(1, grower.size()); + assertEquals(one, grower.get(0)); + } + + //----------------------------------------------------------------------- + /** + * Override. + */ + public void testListAddByIndexBoundsChecking() { + List list; + Object element = getOtherElements()[0]; + try { + list = makeEmptyList(); + list.add(-1, element); + fail("List.add should throw IndexOutOfBoundsException [-1]"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + /** + * Override. + */ + public void testListAddByIndexBoundsChecking2() { + List list; + Object element = getOtherElements()[0]; + try { + list = makeFullList(); + list.add(-1, element); + fail("List.add should throw IndexOutOfBoundsException [-1]"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + /** + * Override. + */ + public void testListSetByIndexBoundsChecking() { + List list = makeEmptyList(); + Object element = getOtherElements()[0]; + try { + list.set(-1, element); + fail("List.set should throw IndexOutOfBoundsException [-1]"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + /** + * Override. + */ + public void testListSetByIndexBoundsChecking2() { + List list = makeFullList(); + Object element = getOtherElements()[0]; + try { + list.set(-1, element); + fail("List.set should throw IndexOutOfBoundsException [-1]"); + } catch(IndexOutOfBoundsException e) { + // expected + } + } + + //----------------------------------------------------------------------- + public String getCompatibilityVersion() { + return "3.2"; + } + +// public void testCreate() throws Exception { +// resetEmpty(); +// writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/GrowthList.emptyCollection.version3.2.obj"); +// resetFull(); +// writeExternalFormToDisk((java.io.Serializable) collection, "C:/commons/collections/data/test/GrowthList.fullCollection.version3.2.obj"); +// } + +}