1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.functor.core.collection;
18
19 import java.util.Collections;
20 import java.util.Iterator;
21
22 import org.apache.commons.functor.UnaryFunction;
23 import org.apache.commons.functor.UnaryPredicate;
24 import org.apache.commons.functor.core.IsInstance;
25 import org.apache.commons.functor.core.composite.UnaryAnd;
26
27 /**
28 * Adds a fluent filtering API to any {@link Iterable}.
29 *
30 * @version $Revision: 1076450 $ $Date: 2011-03-03 00:07:04 +0100 (Thu, 03 Mar 2011) $
31 *
32 * @param <T>
33 */
34 public class FilteredIterable<T> implements Iterable<T> {
35 @SuppressWarnings({ "rawtypes", "unchecked" })
36 // type irrelevant for empty instance
37 private static final FilteredIterable EMPTY = new FilteredIterable(Collections.EMPTY_LIST) {
38 @Override
39 public FilteredIterable retain(Class type) {
40 return this;
41 }
42
43 @Override
44 public synchronized FilteredIterable retain(UnaryPredicate predicate) {
45 return this;
46 }
47
48 @Override
49 public FilteredIterable retain(Class... ofType) {
50 return this;
51 }
52 };
53
54 private final Iterable<? extends T> iterable;
55 private UnaryAnd<T> predicate;
56
57 /**
58 * Create a new FilteredIterable.
59 * @param iterable wrapped
60 */
61 private FilteredIterable(Iterable<? extends T> iterable) {
62 super();
63 this.iterable = iterable;
64 }
65
66 /**
67 * {@inheritDoc}
68 */
69 public Iterator<T> iterator() {
70 return FilteredIterator.filter(iterable.iterator(), predicate);
71 }
72
73 @Override
74 public String toString() {
75 return "FilteredIterable<" + iterable + ">";
76 }
77
78 /**
79 * Retain only elements matching <code>predicate</code>.
80 * @param predicate filter, non-<code>null</code>
81 * @return <code>this</code>, fluently
82 */
83 public FilteredIterable<T> retain(UnaryPredicate<? super T> predicate) {
84 if (predicate == null) {
85 throw new NullPointerException("filtering predicate was null");
86 }
87 synchronized (this) {
88 if (this.predicate == null) {
89 this.predicate = new UnaryAnd<T>();
90 }
91 this.predicate.and(predicate);
92 }
93 return this;
94 }
95
96 /**
97 * Retain elements of a given type with type-safety.
98 * @param <U>
99 * @param type filter, non-<code>null</code>
100 * @return new FilteredIterable instance that delegates to <code>this</code>
101 */
102 public <U> FilteredIterable<U> retain(final Class<U> type) {
103 if (type == null) {
104 throw new NullPointerException("filtered type was null");
105 }
106 return new FilteredIterable<U>(new Iterable<U>() {
107
108 public Iterator<U> iterator() {
109 return TransformedIterator.transform(
110 FilteredIterator.filter(FilteredIterable.this.iterator(), IsInstance.of(type)),
111 new UnaryFunction<T, U>() {
112
113 @SuppressWarnings("unchecked")
114 // this is okay because of the isinstance check
115 public U evaluate(T obj) {
116 return (U) obj;
117 }
118 });
119 }
120
121 });
122 }
123
124 /**
125 * Retain elements of any of specified types.
126 * @param ofType filter, non-<code>null</code>
127 * @return <code>this</code>, fluently
128 */
129 public FilteredIterable<T> retain(final Class<?>... ofType) {
130 if (ofType == null) {
131 throw new NullPointerException("array of filtered types was null");
132 }
133 return retain(new UnaryPredicate<T>() {
134
135 public boolean test(T obj) {
136 for (Class<?> type : ofType) {
137 if (type.isInstance(obj)) {
138 return true;
139 }
140 }
141 return false;
142 }
143 });
144 }
145
146 /**
147 * Get a {@link FilteredIterable} of <code>iterable</code>. If <code>wrapped</code> is <code>null</code>,
148 * result will also be <code>null</code>. A {@link FilteredIterable} argument will be passed back
149 * directly; any other argument will be wrapped in a new {@link FilteredIterable} object.
150 * @param <T>
151 * @param iterable wrapped
152 * @return FilteredIterable
153 */
154 public static <T> FilteredIterable<T> of(Iterable<T> iterable) {
155 if (iterable == null) {
156 return null;
157 }
158 if (iterable instanceof FilteredIterable<?>) {
159 return (FilteredIterable<T>) iterable;
160 }
161 return new FilteredIterable<T>(iterable);
162 }
163
164 /**
165 * Get an empty FilteredIterable.
166 * @param <T>
167 * @return FilteredIterable<T>
168 */
169 @SuppressWarnings("unchecked")
170 public static <T> FilteredIterable<T> empty() {
171 return EMPTY;
172 }
173 }