diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 6055709f0..a8e1fb032 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -28,6 +28,10 @@ The constructors for all Utils classes are now private to prevent instantiation. + + Added methods "forAllButLastDo(Collection, Closure)" and "forAllButLastDo(Iterator, Closure)" + to class "CollectionUtils". + Tree traversal with a TreeListIterator will not be affected anymore by the removal of an element directly after a call to previous(). diff --git a/src/main/java/org/apache/commons/collections4/CollectionUtils.java b/src/main/java/org/apache/commons/collections4/CollectionUtils.java index f7b6d605b..dbd31b28b 100644 --- a/src/main/java/org/apache/commons/collections4/CollectionUtils.java +++ b/src/main/java/org/apache/commons/collections4/CollectionUtils.java @@ -694,6 +694,47 @@ public class CollectionUtils { return closure; } + /** + * Executes the given closure on each but the last element in the collection. + *

+ * If the input collection or closure is null, there is no change made. + * + * @param the type of object the {@link Collection} contains + * @param the closure type + * @param collection the collection to get the input from, may be null + * @param closure the closure to perform, may be null + * @return the last element in the collection, or null if either collection or closure is null + */ + public static > T forAllButLastDo(final Collection collection, + final C closure) { + return collection != null && closure != null ? forAllButLastDo(collection.iterator(), closure) : null; + } + + /** + * Executes the given closure on each but the last element in the collection. + *

+ * If the input collection or closure is null, there is no change made. + * + * @param the type of object the {@link Collection} contains + * @param the closure type + * @param iterator the iterator to get the input from, may be null + * @param closure the closure to perform, may be null + * @return the last element in the collection, or null if either iterator or closure is null + */ + public static > T forAllButLastDo(final Iterator iterator, final C closure) { + if (iterator != null && closure != null) { + while (iterator.hasNext()) { + final T element = iterator.next(); + if (iterator.hasNext()) { + closure.execute(element); + } else { + return element; + } + } + } + return null; + } + /** * Filter the collection by applying a Predicate to each element. If the * predicate returns false, remove the element. diff --git a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java index 1768e611c..a84c51bd5 100644 --- a/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java +++ b/src/test/java/org/apache/commons/collections4/CollectionUtilsTest.java @@ -663,6 +663,61 @@ public class CollectionUtilsTest extends MockTestCase { CollectionUtils.forAllDo(col, testClosure); } + @Test + public void forAllButLastDoCollection() { + final Closure> testClosure = ClosureUtils.invokerClosure("clear"); + final Collection> col = new ArrayList>(); + col.add(collectionA); + col.add(collectionB); + List lastElement = CollectionUtils.forAllButLastDo(col, testClosure); + assertSame(lastElement, collectionB); + assertTrue(collectionA.isEmpty() && !collectionB.isEmpty()); + + col.clear(); + col.add(collectionB); + lastElement = CollectionUtils.forAllButLastDo(col, testClosure); + assertSame(lastElement, collectionB); + assertTrue(!collectionB.isEmpty() ); + + col.clear(); + lastElement = CollectionUtils.forAllButLastDo(col, testClosure); + assertNull(lastElement); + + Collection strings = Arrays.asList(new String[]{"a", "b", "c"}); + final StringBuffer result = new StringBuffer(); + result.append(CollectionUtils.forAllButLastDo(strings, new Closure() { + public void execute(String input) { + result.append(input+";"); + } + })); + assertEquals("a;b;c", result.toString()); + + Collection oneString = Arrays.asList(new String[]{"a"}); + final StringBuffer resultOne = new StringBuffer(); + resultOne.append(CollectionUtils.forAllButLastDo(oneString, new Closure() { + public void execute(String input) { + resultOne.append(input+";"); + } + })); + assertEquals("a", resultOne.toString()); + assertNull(CollectionUtils.forAllButLastDo(strings, null)); + assertNull(CollectionUtils.forAllButLastDo((Collection) null, null)); + } + + @Test + public void forAllButLastDoIterator() { + final Closure> testClosure = ClosureUtils.invokerClosure("clear"); + final Collection> col = new ArrayList>(); + col.add(collectionA); + col.add(collectionB); + List lastElement = CollectionUtils.forAllButLastDo(col.iterator(), testClosure); + assertSame(lastElement, collectionB); + assertTrue(collectionA.isEmpty() && !collectionB.isEmpty()); + + assertNull(CollectionUtils.forAllButLastDo(col.iterator(), null)); + assertNull(CollectionUtils.forAllButLastDo((Collection) null, null)); + } + @Test public void getFromMap() { // Unordered map, entries exist