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 }