[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>
|
||||
|
||||
<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">
|
||||
Documented runtime complexity of "CollectionUtils#removeAll(Collection, Collection).
|
||||
</action>
|
||||
|
|
|
@ -16,9 +16,23 @@
|
|||
*/
|
||||
package org.apache.commons.collections4.collection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.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
|
||||
|
@ -28,8 +42,10 @@ import org.apache.commons.collections4.Predicate;
|
|||
* It is normally created to decorate an empty collection.
|
||||
* If an object cannot be added to the collection, an IllegalArgumentException is thrown.
|
||||
* <p>
|
||||
* 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>
|
||||
* One usage would be to ensure that no null entries are added to the collection:
|
||||
* <pre>
|
||||
* Collection coll = PredicatedCollection.predicatedCollection(new ArrayList(), NotNullPredicate.INSTANCE);
|
||||
* </pre>
|
||||
* <p>
|
||||
* This class is Serializable from Commons Collections 3.1.
|
||||
*
|
||||
|
@ -45,6 +61,29 @@ public class PredicatedCollection<E> extends AbstractCollectionDecorator<E> {
|
|||
/** The predicate to use */
|
||||
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.
|
||||
* <p>
|
||||
|
@ -135,4 +174,230 @@ public class PredicatedCollection<E> extends AbstractCollectionDecorator<E> {
|
|||
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