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

NEW CLASSES

diff --git a/project.xml b/project.xml index 986f96a10..02f0d1900 100644 --- a/project.xml +++ b/project.xml @@ -221,6 +221,9 @@ Guilhem Lavaux + + Paul Legato + David Leppik diff --git a/src/java/org/apache/commons/collections/list/GrowthList.java b/src/java/org/apache/commons/collections/list/GrowthList.java new file mode 100644 index 000000000..d758ac877 --- /dev/null +++ b/src/java/org/apache/commons/collections/list/GrowthList.java @@ -0,0 +1,185 @@ +/* + * 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.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Decorates another 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"); +// } + +}