View Javadoc

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 }