001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.functor.core.collection;
018
019 import java.util.Collections;
020 import java.util.Iterator;
021
022 import org.apache.commons.functor.UnaryFunction;
023 import org.apache.commons.functor.UnaryPredicate;
024 import org.apache.commons.functor.core.IsInstance;
025 import org.apache.commons.functor.core.composite.UnaryAnd;
026
027 /**
028 * Adds a fluent filtering API to any {@link Iterable}.
029 *
030 * @version $Revision: 1076450 $ $Date: 2011-03-03 00:07:04 +0100 (Thu, 03 Mar 2011) $
031 *
032 * @param <T>
033 */
034 public class FilteredIterable<T> implements Iterable<T> {
035 @SuppressWarnings({ "rawtypes", "unchecked" })
036 // type irrelevant for empty instance
037 private static final FilteredIterable EMPTY = new FilteredIterable(Collections.EMPTY_LIST) {
038 @Override
039 public FilteredIterable retain(Class type) {
040 return this;
041 }
042
043 @Override
044 public synchronized FilteredIterable retain(UnaryPredicate predicate) {
045 return this;
046 }
047
048 @Override
049 public FilteredIterable retain(Class... ofType) {
050 return this;
051 }
052 };
053
054 private final Iterable<? extends T> iterable;
055 private UnaryAnd<T> predicate;
056
057 /**
058 * Create a new FilteredIterable.
059 * @param iterable wrapped
060 */
061 private FilteredIterable(Iterable<? extends T> iterable) {
062 super();
063 this.iterable = iterable;
064 }
065
066 /**
067 * {@inheritDoc}
068 */
069 public Iterator<T> iterator() {
070 return FilteredIterator.filter(iterable.iterator(), predicate);
071 }
072
073 @Override
074 public String toString() {
075 return "FilteredIterable<" + iterable + ">";
076 }
077
078 /**
079 * Retain only elements matching <code>predicate</code>.
080 * @param predicate filter, non-<code>null</code>
081 * @return <code>this</code>, fluently
082 */
083 public FilteredIterable<T> retain(UnaryPredicate<? super T> predicate) {
084 if (predicate == null) {
085 throw new NullPointerException("filtering predicate was null");
086 }
087 synchronized (this) {
088 if (this.predicate == null) {
089 this.predicate = new UnaryAnd<T>();
090 }
091 this.predicate.and(predicate);
092 }
093 return this;
094 }
095
096 /**
097 * Retain elements of a given type with type-safety.
098 * @param <U>
099 * @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 }