[COLLECTIONS-530] Added Builder for PredicatedCollection. Thanks to Erik.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/collections/trunk@1654518 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
5d42c95c33
commit
dd5e51e5b3
|
@ -22,6 +22,11 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<release version="4.1" date="TBD" description="">
|
<release version="4.1" date="TBD" description="">
|
||||||
|
<action issue="COLLECTIONS-530" dev="tn" type="fix" due-to="Erik">
|
||||||
|
Added a Builder for "PredicatedCollection". Elements added to the builder
|
||||||
|
that fail the predicate will not throw an IllegalArgumentException. The builder
|
||||||
|
supports creating predicated lists, bags, sets and queues.
|
||||||
|
</action>
|
||||||
<action issue="COLLECTIONS-545" dev="tn" type="fix" due-to="Oswaldo Olivo">
|
<action issue="COLLECTIONS-545" dev="tn" type="fix" due-to="Oswaldo Olivo">
|
||||||
Documented runtime complexity of "CollectionUtils#removeAll(Collection, Collection).
|
Documented runtime complexity of "CollectionUtils#removeAll(Collection, Collection).
|
||||||
</action>
|
</action>
|
||||||
|
|
|
@ -16,9 +16,23 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.commons.collections4.collection;
|
package org.apache.commons.collections4.collection;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.Bag;
|
||||||
import org.apache.commons.collections4.Predicate;
|
import org.apache.commons.collections4.Predicate;
|
||||||
|
import org.apache.commons.collections4.bag.HashBag;
|
||||||
|
import org.apache.commons.collections4.bag.PredicatedBag;
|
||||||
|
import org.apache.commons.collections4.functors.NotNullPredicate;
|
||||||
|
import org.apache.commons.collections4.list.PredicatedList;
|
||||||
|
import org.apache.commons.collections4.queue.PredicatedQueue;
|
||||||
|
import org.apache.commons.collections4.set.PredicatedSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorates another {@link Collection} to validate that additions
|
* Decorates another {@link Collection} to validate that additions
|
||||||
|
@ -28,8 +42,10 @@ import org.apache.commons.collections4.Predicate;
|
||||||
* It is normally created to decorate an empty collection.
|
* It is normally created to decorate an empty collection.
|
||||||
* If an object cannot be added to the collection, an IllegalArgumentException is thrown.
|
* If an object cannot be added to the collection, an IllegalArgumentException is thrown.
|
||||||
* <p>
|
* <p>
|
||||||
* One usage would be to ensure that no null entries are added to the collection.
|
* One usage would be to ensure that no null entries are added to the collection:
|
||||||
* <pre>Collection coll = PredicatedCollection.decorate(new ArrayList(), NotNullPredicate.INSTANCE);</pre>
|
* <pre>
|
||||||
|
* Collection coll = PredicatedCollection.predicatedCollection(new ArrayList(), NotNullPredicate.INSTANCE);
|
||||||
|
* </pre>
|
||||||
* <p>
|
* <p>
|
||||||
* This class is Serializable from Commons Collections 3.1.
|
* This class is Serializable from Commons Collections 3.1.
|
||||||
*
|
*
|
||||||
|
@ -45,6 +61,29 @@ public class PredicatedCollection<E> extends AbstractCollectionDecorator<E> {
|
||||||
/** The predicate to use */
|
/** The predicate to use */
|
||||||
protected final Predicate<? super E> predicate;
|
protected final Predicate<? super E> predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Builder with the given predicate.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @param predicate the predicate to use
|
||||||
|
* @return a new Builder for predicated collections
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> Builder<E> builder(final Predicate<? super E> predicate) {
|
||||||
|
return new Builder<E>(predicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Builder with a NotNullPredicate.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @return a new Builder for predicated collections that ignores null values.
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static <E> Builder<E> notNullBuilder() {
|
||||||
|
return new Builder<E>(NotNullPredicate.<E>notNullPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to create a predicated (validating) collection.
|
* Factory method to create a predicated (validating) collection.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -135,4 +174,230 @@ public class PredicatedCollection<E> extends AbstractCollectionDecorator<E> {
|
||||||
return decorated().addAll(coll);
|
return decorated().addAll(coll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for creating predicated collections.
|
||||||
|
* <p>
|
||||||
|
* Create a Builder with a predicate to validate elements against, then add any elements
|
||||||
|
* to the builder. Elements that fail the predicate will be added to a rejected list.
|
||||||
|
* Finally create or decorate a collection using the createPredicated[List,Set,Bag,Queue] methods.
|
||||||
|
* <p>
|
||||||
|
* An example:
|
||||||
|
* <pre>
|
||||||
|
* Predicate<String> predicate = NotNullPredicate.notNullPredicate();
|
||||||
|
* PredicatedCollectionBuilder<String> builder = PredicatedCollection.builder(predicate);
|
||||||
|
* builder.add("item1");
|
||||||
|
* builder.add(null);
|
||||||
|
* builder.add("item2");
|
||||||
|
* List<String> predicatedList = builder.createPredicatedList();
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* At the end of the code fragment above predicatedList is protected by the predicate supplied
|
||||||
|
* to the builder and it contains item1 and item2.
|
||||||
|
* <p>
|
||||||
|
* More elements can be added to the builder once a predicated collection has been created,
|
||||||
|
* but these elements will not be reflected in already created collections.
|
||||||
|
*
|
||||||
|
* @param <E> the element type
|
||||||
|
* @since 4.1
|
||||||
|
*/
|
||||||
|
public static class Builder<E> {
|
||||||
|
|
||||||
|
/** The predicate to use. */
|
||||||
|
private final Predicate<? super E> predicate;
|
||||||
|
|
||||||
|
/** The buffer containing valid elements. */
|
||||||
|
private final List<E> accepted = new ArrayList<E>();
|
||||||
|
|
||||||
|
/** The buffer containing rejected elements. */
|
||||||
|
private final List<E> rejected = new ArrayList<E>();
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Constructs a PredicatedCollectionBuilder with the specified Predicate.
|
||||||
|
*
|
||||||
|
* @param predicate the predicate to use
|
||||||
|
* @throws IllegalArgumentException if predicate is null
|
||||||
|
*/
|
||||||
|
public Builder(final Predicate<? super E> predicate) {
|
||||||
|
if (predicate == null) {
|
||||||
|
throw new IllegalArgumentException("Predicate must not be null");
|
||||||
|
}
|
||||||
|
this.predicate = predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the item to the builder.
|
||||||
|
* <p>
|
||||||
|
* If the predicate is true, it is added to the list of accepted elements,
|
||||||
|
* otherwise it is added to the rejected list.
|
||||||
|
*
|
||||||
|
* @param item the element to add
|
||||||
|
* @return the PredicatedCollectionBuilder.
|
||||||
|
*/
|
||||||
|
public Builder<E> add(final E item) {
|
||||||
|
if (predicate.evaluate(item)) {
|
||||||
|
accepted.add(item);
|
||||||
|
} else {
|
||||||
|
rejected.add(item);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all elements from the given collection to the builder.
|
||||||
|
* <p>
|
||||||
|
* All elements for which the predicate evaluates to true will be added to the
|
||||||
|
* list of accepted elements, otherwise they are added to the rejected list.
|
||||||
|
*
|
||||||
|
* @param items the elements to add to the builder
|
||||||
|
* @return the PredicatedCollectionBuilder.
|
||||||
|
*/
|
||||||
|
public Builder<E> addAll(final Collection<? extends E> items) {
|
||||||
|
if (items != null) {
|
||||||
|
for (E item : items) {
|
||||||
|
add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new predicated list filled with the accepted elements.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned list.
|
||||||
|
*
|
||||||
|
* @return a new predicated list.
|
||||||
|
*/
|
||||||
|
public List<E> createPredicatedList() {
|
||||||
|
return createPredicatedList(new ArrayList<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates the given list with validating behavior using the predicate. All accepted elements
|
||||||
|
* are appended to the list. If the list already contains elements, they are validated.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned list.
|
||||||
|
*
|
||||||
|
* @param list the List to decorate, must not be null
|
||||||
|
* @return the decorated list.
|
||||||
|
* @throws IllegalArgumentException if list is null or contains invalid elements
|
||||||
|
*/
|
||||||
|
public List<E> createPredicatedList(final List<E> list) {
|
||||||
|
if (list == null) {
|
||||||
|
throw new IllegalArgumentException("list must not be null");
|
||||||
|
}
|
||||||
|
final List<E> predicatedList = PredicatedList.predicatedList(list, predicate);
|
||||||
|
predicatedList.addAll(accepted);
|
||||||
|
return predicatedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new predicated set filled with the accepted elements.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned set.
|
||||||
|
*
|
||||||
|
* @return a new predicated set.
|
||||||
|
*/
|
||||||
|
public Set<E> createPredicatedSet() {
|
||||||
|
return createPredicatedSet(new HashSet<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates the given list with validating behavior using the predicate. All accepted elements
|
||||||
|
* are appended to the set. If the set already contains elements, they are validated.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned set.
|
||||||
|
*
|
||||||
|
* @param set the set to decorate, must not be null
|
||||||
|
* @return the decorated set.
|
||||||
|
* @throws IllegalArgumentException if set is null or contains invalid elements
|
||||||
|
*/
|
||||||
|
public Set<E> createPredicatedSet(final Set<E> set) {
|
||||||
|
if (set == null) {
|
||||||
|
throw new IllegalArgumentException("set must not be null");
|
||||||
|
}
|
||||||
|
final PredicatedSet<E> predicatedSet = PredicatedSet.predicatedSet(set, predicate);
|
||||||
|
predicatedSet.addAll(accepted);
|
||||||
|
return predicatedSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new predicated bag filled with the accepted elements.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned bag.
|
||||||
|
*
|
||||||
|
* @return a new predicated bag.
|
||||||
|
*/
|
||||||
|
public Bag<E> createPredicatedBag() {
|
||||||
|
return createPredicatedBag(new HashBag<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates the given bag with validating behavior using the predicate. All accepted elements
|
||||||
|
* are appended to the bag. If the bag already contains elements, they are validated.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned bag.
|
||||||
|
*
|
||||||
|
* @param bag the bag to decorate, must not be null
|
||||||
|
* @return the decorated bag.
|
||||||
|
* @throws IllegalArgumentException if bag is null or contains invalid elements
|
||||||
|
*/
|
||||||
|
public Bag<E> createPredicatedBag(final Bag<E> bag) {
|
||||||
|
if (bag == null) {
|
||||||
|
throw new IllegalArgumentException("bag must not be null");
|
||||||
|
}
|
||||||
|
final PredicatedBag<E> predicatedBag = PredicatedBag.predicatedBag(bag, predicate);
|
||||||
|
predicatedBag.addAll(accepted);
|
||||||
|
return predicatedBag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new predicated queue filled with the accepted elements.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned queue.
|
||||||
|
*
|
||||||
|
* @return a new predicated queue.
|
||||||
|
*/
|
||||||
|
public Queue<E> createPredicatedQueue() {
|
||||||
|
return createPredicatedQueue(new LinkedList<E>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates the given queue with validating behavior using the predicate. All accepted elements
|
||||||
|
* are appended to the queue. If the queue already contains elements, they are validated.
|
||||||
|
* <p>
|
||||||
|
* The builder is not modified by this method, so it is possible to create more collections
|
||||||
|
* or add more elements afterwards. Further changes will not propagate to the returned queue.
|
||||||
|
*
|
||||||
|
* @param queue the queue to decorate, must not be null
|
||||||
|
* @return the decorated queue.
|
||||||
|
* @throws IllegalArgumentException if queue is null or contains invalid elements
|
||||||
|
*/
|
||||||
|
public Queue<E> createPredicatedQueue(final Queue<E> queue) {
|
||||||
|
if (queue == null) {
|
||||||
|
throw new IllegalArgumentException("queue must not be null");
|
||||||
|
}
|
||||||
|
final PredicatedQueue<E> predicatedQueue = PredicatedQueue.predicatedQueue(queue, predicate);
|
||||||
|
predicatedQueue.addAll(accepted);
|
||||||
|
return predicatedQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an unmodifiable collection containing all rejected elements.
|
||||||
|
*
|
||||||
|
* @return an unmodifiable collection
|
||||||
|
*/
|
||||||
|
public Collection<E> rejectedElements() {
|
||||||
|
return Collections.unmodifiableCollection(rejected);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* 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.collection;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.Bag;
|
||||||
|
import org.apache.commons.collections4.Predicate;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the PredicatedCollection.Builder class.
|
||||||
|
*
|
||||||
|
* @since 4.1
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
public class PredicatedCollectionBuilderTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that passing the Predicate means ending up in the buffer.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void addPass() {
|
||||||
|
PredicatedCollection.Builder<String> builder = PredicatedCollection.notNullBuilder();
|
||||||
|
builder.add("test");
|
||||||
|
Assert.assertEquals(builder.createPredicatedList().size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that failing the Predicate means NOT ending up in the buffer.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void addFail() {
|
||||||
|
PredicatedCollection.Builder<String> builder = PredicatedCollection.notNullBuilder();
|
||||||
|
builder.add((String) null);
|
||||||
|
Assert.assertTrue(builder.createPredicatedList().isEmpty());
|
||||||
|
|
||||||
|
Assert.assertEquals(1, builder.rejectedElements().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that only items that pass the Predicate end up in the buffer.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void addAllPass() {
|
||||||
|
PredicatedCollection.Builder<String> builder = PredicatedCollection.notNullBuilder();
|
||||||
|
builder.addAll(Arrays.asList("test1", null, "test2"));
|
||||||
|
Assert.assertEquals(builder.createPredicatedList().size(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createPredicatedCollectionWithNotNullPredicate() {
|
||||||
|
PredicatedCollection.Builder<String> builder = PredicatedCollection.notNullBuilder();
|
||||||
|
builder.add("test1");
|
||||||
|
builder.add((String) null);
|
||||||
|
|
||||||
|
List<String> predicatedList = builder.createPredicatedList();
|
||||||
|
checkPredicatedCollection1(predicatedList);
|
||||||
|
|
||||||
|
Set<String> predicatedSet = builder.createPredicatedSet();
|
||||||
|
checkPredicatedCollection1(predicatedSet);
|
||||||
|
|
||||||
|
Bag<String> predicatedBag = builder.createPredicatedBag();
|
||||||
|
checkPredicatedCollection1(predicatedBag);
|
||||||
|
|
||||||
|
Queue<String> predicatedQueue = builder.createPredicatedQueue();
|
||||||
|
checkPredicatedCollection1(predicatedQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPredicatedCollection1(final Collection<String> collection) {
|
||||||
|
Assert.assertEquals(1, collection.size());
|
||||||
|
|
||||||
|
collection.add("test2");
|
||||||
|
Assert.assertEquals(2, collection.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
collection.add(null);
|
||||||
|
Assert.fail("Expecting IllegalArgumentException for failing predicate!");
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createPredicatedCollectionWithPredicate() {
|
||||||
|
OddPredicate p = new OddPredicate();
|
||||||
|
PredicatedCollection.Builder<Integer> builder = PredicatedCollection.builder(p);
|
||||||
|
|
||||||
|
builder.add(1);
|
||||||
|
builder.add(2);
|
||||||
|
builder.add(3);
|
||||||
|
|
||||||
|
List<Integer> predicatedList = builder.createPredicatedList();
|
||||||
|
checkPredicatedCollection2(predicatedList);
|
||||||
|
|
||||||
|
Set<Integer> predicatedSet = builder.createPredicatedSet();
|
||||||
|
checkPredicatedCollection2(predicatedSet);
|
||||||
|
|
||||||
|
Bag<Integer> predicatedBag = builder.createPredicatedBag();
|
||||||
|
checkPredicatedCollection2(predicatedBag);
|
||||||
|
|
||||||
|
Queue<Integer> predicatedQueue = builder.createPredicatedQueue();
|
||||||
|
checkPredicatedCollection2(predicatedQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkPredicatedCollection2(final Collection<Integer> collection) {
|
||||||
|
Assert.assertEquals(2, collection.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
collection.add(4);
|
||||||
|
Assert.fail("Expecting IllegalArgumentException for failing predicate!");
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
}
|
||||||
|
Assert.assertEquals(2, collection.size());
|
||||||
|
|
||||||
|
collection.add(5);
|
||||||
|
Assert.assertEquals(3, collection.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class OddPredicate implements Predicate<Integer> {
|
||||||
|
public boolean evaluate(Integer value) {
|
||||||
|
return value % 2 == 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue