001 package edu.nrao.sss.util; 002 003 import java.util.ArrayList; 004 import java.util.Collection; 005 import java.util.List; 006 007 /** 008 * A filter that is composed of other filters. 009 * <p> 010 * The primary purpose of this class is to allow for the logical 011 * (AND / OR) joining of other filters -- including other compound 012 * filters. An individual filter uses only AND logic for its 013 * criteria. In those situations where you need to allow passage 014 * of particles through a filter under condition A OR condition B, 015 * create a filter for each condition and add them to a 016 * <tt>CompoundFilter</tt> using the logical OR operator.</p> 017 * <p> 018 * <b>Version Info:</b> 019 * <table style="margin-left:2em"> 020 * <tr><td>$Revision: 819 $</td></tr> 021 * <tr><td>$Date: 2007-08-13 16:25:44 -0600 (Mon, 13 Aug 2007) $</td></tr> 022 * <tr><td>$Author: dharland $ (last person to modify)</td></tr> 023 * </table></p> 024 * 025 * @author David M. Harland 026 * @since 2007-08-13 027 */ 028 public class CompoundFilter<T> 029 implements Filter<T> 030 { 031 private List<Filter<T>> filters; 032 private LogicalOperator operator; 033 034 /** 035 * Creates a new wide open filter with the logical 036 * {@link LogicalOperator#AND AND} operator. 037 */ 038 public CompoundFilter() 039 { 040 this(null, LogicalOperator.AND); 041 } 042 043 /** 044 * Creates a new compound filter with the given features. 045 * 046 * @param filters the component filters for this compound filter. 047 * If this value is <i>null</i>, it will be treated as 048 * an empty collection. 049 * 050 * @param operator see {@link #setOperator(LogicalOperator)}. 051 * 052 * @throws IllegalArgumentException if {@code operator} is neither 053 * <tt>AND</tt> nor <tt>OR</tt>. 054 */ 055 public CompoundFilter(Collection<? extends Filter<T>> filters, 056 LogicalOperator operator) 057 { 058 validate(operator); 059 this.operator = operator; 060 061 this.filters = new ArrayList<Filter<T>>(); 062 063 if (filters != null) 064 this.filters.addAll(filters); 065 } 066 067 /** 068 * Adds {@code newFilter} as a component of this compound filter. 069 * This filter will hold a reference to {@code newFilter}, so 070 * changes made to it after this call be reflected herein. 071 * 072 * @param newFilter a new component filter. 073 * 074 * @return this compound filter. 075 */ 076 public CompoundFilter<T> add(Filter<T> newFilter) 077 { 078 if (newFilter != null && !filters.contains(newFilter)) 079 filters.add(newFilter); 080 081 return this; 082 } 083 084 /** 085 * Adds each of the filters in the collection as components of this 086 * compound filter. 087 * 088 * @param newFilters a collection of new component filters. 089 * 090 * @return this compound filter. 091 * 092 * @see #add(Filter) 093 */ 094 public CompoundFilter<T> addAll(Collection<? extends Filter<T>> newFilters) 095 { 096 if (newFilters != null) 097 filters.addAll(newFilters); 098 099 return this; 100 } 101 102 /** 103 * The component filter to be removed from this compound filter. 104 * @param unwantedFilter the component filter to be removed. 105 * @return this compound filter. 106 */ 107 public CompoundFilter<T> remove(Filter<T> unwantedFilter) 108 { 109 if (unwantedFilter != null) 110 { 111 while (filters.contains(unwantedFilter)) 112 filters.remove(unwantedFilter); 113 } 114 115 return this; 116 } 117 118 /** 119 * The component filters to be removed from this compound filter. 120 * @param unwantedFilters the component filters to be removed. 121 * @return this compound filter. 122 */ 123 public CompoundFilter<T> 124 removeAll(Collection<? extends Filter<T>> unwantedFilters) 125 { 126 for (Filter<T> unwantedFilter : unwantedFilters) 127 remove(unwantedFilter); 128 129 return this; 130 } 131 132 /** 133 * Removes all component filters from this compound filter. 134 * This action leaves this filter in a wide-open state (i.e., 135 * one that allows passage of all particles). 136 */ 137 public CompoundFilter<T> removeAllFilters() 138 { 139 filters.clear(); 140 141 return this; 142 } 143 144 /** 145 * Sets the operator to use when joining the component filters of this 146 * compound filter. The only two legal values are 147 * {@link LogicalOperator#AND} and {@link LogicalOperator#OR}. 148 * <p> 149 * The operator is used when deciding whether or not a given particle 150 * may pass through this compound filter. In the case of <tt>AND</tt>, 151 * the particle must pass through <i>all</i> the component filters, 152 * while in the case of <tt>OR</tt> it pass through this filter if it 153 * passes through <i>any</i> of the component filters.</p> 154 * 155 * @param newOperator the operator for joining the component filters. 156 * This value must be either {@link LogicalOperator#AND AND} 157 * or {@link LogicalOperator#OR OR}. 158 * 159 * @throws IllegalArgumentException if {@code newOperator} is neither 160 * <tt>AND</tt> nor <tt>OR</tt>. 161 */ 162 public void setOperator(LogicalOperator newOperator) 163 { 164 validate(newOperator); 165 operator = newOperator; 166 } 167 168 /** 169 * Returns <i>true</i> if <tt>newOperator</tt> is a valid value. 170 */ 171 private void validate(LogicalOperator newOperator) 172 { 173 boolean isValid = 174 newOperator != null && (newOperator == LogicalOperator.AND || 175 newOperator == LogicalOperator.OR); 176 if (!isValid) 177 throw new IllegalArgumentException 178 ( 179 newOperator + 180 " is not a valid operator for CompoundFilter. Must be AND or OR." 181 ); 182 } 183 184 /** 185 * Returns <i>true</i> if this filter allows {@code particle} 186 * to pass through. 187 * <p> 188 * This compound filter delegates filtering decisions to its component 189 * filters and uses its logical operator to combine those results. 190 * All component filters are subject to the same operator, either 191 * <tt>AND</tt> or <tt>OR</tt>. Short-circuit logic is used. That is, 192 * when using the <tt>AND</tt> operator, evaluation stops with the 193 * first component that blocks the particle; when using the <tt>OR</tt> 194 * operator, evaluation stops with the first component filter that 195 * allows passage of the particle.</p> 196 * 197 * @param particle an object attempting to pass through this filter. 198 * 199 * @return <i>true</i> if this filter passes {@code particle}. 200 */ 201 public boolean allows(T particle) 202 { 203 //Quick exit if no filters 204 if (filters.size() == 0) 205 return true; 206 207 boolean passesThrough; 208 209 switch (operator) 210 { 211 case AND: 212 passesThrough = true; 213 for (Filter<T> filter : filters) 214 { 215 if (!filter.allows(particle)) 216 { 217 passesThrough = false; //Must pass ALL filters 218 break; 219 } 220 } 221 break; 222 223 case OR: 224 passesThrough = false; 225 for (Filter<T> filter : filters) 226 { 227 if (filter.allows(particle)) 228 { 229 passesThrough = true; //Passing one filter is good enough 230 break; 231 } 232 } 233 break; 234 235 default: 236 throw new RuntimeException("Programmer Error. " + 237 "CompoundFilter.allows does not know about LogicalOperator." + 238 operator.name()); 239 } 240 241 return passesThrough; 242 } 243 244 /** 245 * Returns <i>true</i> if this filter blocks {@code particle} 246 * from passing through. This is 247 * a convenience method that is equivalent to {@code !allows(particle)}. 248 * 249 * @param particle an object attempting to pass through this filter. 250 * @return <i>true</i> if this filter blocks {@code particle}. 251 */ 252 public boolean blocks(T particle) 253 { 254 return !allows(particle); 255 } 256 }