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