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 */ 017package org.apache.commons.functor.core.collection; 018 019import java.util.Collections; 020import java.util.Iterator; 021 022import org.apache.commons.functor.UnaryFunction; 023import org.apache.commons.functor.UnaryPredicate; 024import org.apache.commons.functor.core.IsInstance; 025import org.apache.commons.functor.core.composite.UnaryAnd; 026 027/** 028 * Adds a fluent filtering API to any {@link Iterable}. 029 * 030 * @version $Revision: 1344786 $ $Date: 2012-05-31 11:55:31 -0400 (Thu, 31 May 2012) $ 031 * 032 * @param <T> the Iterable generic type 033 */ 034public class FilteredIterable<T> implements Iterable<T> { 035 /** 036 * A default {@code FilteredIterable} static instance that iterates over an empty collection. 037 */ 038 @SuppressWarnings({ "rawtypes", "unchecked" }) 039 // type irrelevant for empty instance 040 private static final FilteredIterable EMPTY = new FilteredIterable(Collections.EMPTY_LIST) { 041 /** 042 * {@inheritDoc} 043 */ 044 @Override 045 public FilteredIterable retain(Class type) { 046 return this; 047 } 048 049 /** 050 * {@inheritDoc} 051 */ 052 @Override 053 public FilteredIterable retain(UnaryPredicate predicate) { 054 return this; 055 } 056 057 /** 058 * {@inheritDoc} 059 */ 060 @Override 061 public FilteredIterable retain(Class... ofType) { 062 return this; 063 } 064 }; 065 066 /** 067 * The {@link Iterable} has to be filtered. 068 */ 069 private final Iterable<? extends T> iterable; 070 /** 071 * The predicate used to test input {@link Iterable} elements. 072 */ 073 private UnaryAnd<T> predicate; 074 075 /** 076 * Create a new FilteredIterable. 077 * @param iterable wrapped 078 */ 079 protected FilteredIterable(Iterable<? extends T> iterable) { 080 super(); 081 this.iterable = iterable; 082 } 083 084 /** 085 * {@inheritDoc} 086 */ 087 public Iterator<T> iterator() { 088 UnaryPredicate<T> predicateReference; 089 synchronized (this) { 090 predicateReference = predicate; 091 } 092 return FilteredIterator.filter(iterable.iterator(), predicateReference); 093 } 094 095 /** 096 * {@inheritDoc} 097 */ 098 @Override 099 public String toString() { 100 return "FilteredIterable<" + iterable + ">"; 101 } 102 103 /** 104 * Retain only elements matching <code>predicate</code>. 105 * @param filter filter predicate, non-<code>null</code> 106 * @return <code>this</code>, fluently 107 */ 108 public FilteredIterable<T> retain(UnaryPredicate<? super T> filter) { 109 if (filter == null) { 110 throw new NullPointerException("filtering predicate was null"); 111 } 112 synchronized (this) { 113 if (this.predicate == null) { 114 this.predicate = new UnaryAnd<T>(); 115 } 116 this.predicate.and(filter); 117 } 118 return this; 119 } 120 121 /** 122 * Retain elements of a given type with type-safety. 123 * 124 * @param <U> the input Class generic type. 125 * @param type filter, non-<code>null</code> 126 * @return new FilteredIterable instance that delegates to <code>this</code> 127 */ 128 public <U> FilteredIterable<U> retain(final Class<U> type) { 129 if (type == null) { 130 throw new NullPointerException("filtered type was null"); 131 } 132 return new FilteredIterable<U>(new Iterable<U>() { 133 134 public Iterator<U> iterator() { 135 return TransformedIterator.transform( 136 FilteredIterator.filter(FilteredIterable.this.iterator(), IsInstance.of(type)), 137 new UnaryFunction<T, U>() { 138 139 @SuppressWarnings("unchecked") 140 // this is okay because of the isinstance check 141 public U evaluate(T obj) { 142 return (U) obj; 143 } 144 }); 145 } 146 147 }); 148 } 149 150 /** 151 * Retain elements of any of specified types. 152 * @param ofType filter, non-<code>null</code> 153 * @return <code>this</code>, fluently 154 */ 155 public FilteredIterable<T> retain(final Class<?>... ofType) { 156 if (ofType == null) { 157 throw new NullPointerException("array of filtered types was null"); 158 } 159 return retain(new UnaryPredicate<T>() { 160 161 public boolean test(T obj) { 162 for (Class<?> type : ofType) { 163 if (type.isInstance(obj)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 }); 170 } 171 172 /** 173 * Get a {@link FilteredIterable} of <code>iterable</code>. If <code>wrapped</code> is <code>null</code>, 174 * result will also be <code>null</code>. A {@link FilteredIterable} argument will be passed back 175 * directly; any other argument will be wrapped in a new {@link FilteredIterable} object. 176 * @param <T> the input iterable generic type 177 * @param iterable wrapped 178 * @return FilteredIterable 179 */ 180 public static <T> FilteredIterable<T> of(Iterable<T> iterable) { 181 if (iterable == null) { 182 return null; 183 } 184 if (iterable instanceof FilteredIterable<?>) { 185 return (FilteredIterable<T>) iterable; 186 } 187 return new FilteredIterable<T>(iterable); 188 } 189 190 /** 191 * Get an empty FilteredIterable. 192 * @param <T> the expected {@link Iterable} generic type. 193 * @return FilteredIterable<T> 194 */ 195 @SuppressWarnings("unchecked") 196 public static <T> FilteredIterable<T> empty() { 197 return EMPTY; 198 } 199}