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    }