17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.util.ArrayList; 28 import java.math.BigDecimal; 29 import java.math.BigInteger; 30 import java.util.Date; 31 import java.util.List; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 import java.util.regex.PatternSyntaxException; 35 36 /** 37 * <code>RowFilter</code> is used to filter out entries from the 38 * model so that they are not shown in the view. For example, a 39 * <code>RowFilter</code> associated with a <code>JTable</code> might 40 * only allow rows that contain a column with a specific string. The 41 * meaning of <em>entry</em> depends on the component type. 42 * For example, when a filter is 43 * associated with a <code>JTable</code>, an entry corresponds to a 44 * row; when associated with a <code>JTree</code>, an entry corresponds 45 * to a node. 46 * <p> 47 * Subclasses must override the <code>include</code> method to 48 * indicate whether the entry should be shown in the 49 * view. The <code>Entry</code> argument can be used to obtain the values in 50 * each of the columns in that entry. The following example shows an 51 * <code>include</code> method that allows only entries containing one or 52 * more values starting with the string "a": 53 * <pre> 54 * RowFilter<Object,Object> startsWithAFilter = new RowFilter<Object,Object>() { 55 * public boolean include(Entry<? extends Object, ? extends Object> entry) { 56 * for (int i = entry.getValueCount() - 1; i >= 0; i--) { 57 * if (entry.getStringValue(i).startsWith("a")) { 58 * // The value starts with "a", include it 59 * return true; 60 * } 61 * } 62 * // None of the columns start with "a"; return false so that this 63 * // entry is not shown 64 * return false; 65 * } 66 * }; 67 * </pre> 68 * <code>RowFilter</code> has two formal type parameters that allow 69 * you to create a <code>RowFilter</code> for a specific model. For 70 * example, the following assumes a specific model that is wrapping 71 * objects of type <code>Person</code>. Only <code>Person</code>s 72 * with an age over 20 will be shown: 73 * <pre> 74 * RowFilter<PersonModel,Integer> ageFilter = new RowFilter<PersonModel,Integer>() { 75 * public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) { 76 * PersonModel personModel = entry.getModel(); 77 * Person person = personModel.getPerson(entry.getIdentifier()); 78 * if (person.getAge() > 20) { 79 * // Returning true indicates this row should be shown. 80 * return true; 81 * } 82 * // Age is <= 20, don't show it. 83 * return false; 84 * } 85 * }; 86 * PersonModel model = createPersonModel(); 87 * TableRowSorter<PersonModel> sorter = new TableRowSorter<PersonModel>(model); 88 * sorter.setRowFilter(ageFilter); 89 * </pre> 90 * 91 * @param <M> the type of the model; for example <code>PersonModel</code> 92 * @param <I> the type of the identifier; when using 93 * <code>TableRowSorter</code> this will be <code>Integer</code> 94 * @see javax.swing.table.TableRowSorter 95 * @since 1.6 96 */ 97 public abstract class RowFilter<M,I> { 98 /** 99 * Enumeration of the possible comparison values supported by 100 * some of the default <code>RowFilter</code>s. 101 * 102 * @see RowFilter 103 * @since 1.6 104 */ 105 public enum ComparisonType { 106 /** 107 * Indicates that entries with a value before the supplied 108 * value should be included. 109 */ 110 BEFORE, 111 112 /** 113 * Indicates that entries with a value after the supplied 114 * value should be included. 115 */ 116 AFTER, 117 118 /** 119 * Indicates that entries with a value equal to the supplied 120 * value should be included. 124 /** 125 * Indicates that entries with a value not equal to the supplied 126 * value should be included. 127 */ 128 NOT_EQUAL 129 } 130 131 /** 132 * Throws an IllegalArgumentException if any of the values in 133 * columns are {@literal <} 0. 134 */ 135 private static void checkIndices(int[] columns) { 136 for (int i = columns.length - 1; i >= 0; i--) { 137 if (columns[i] < 0) { 138 throw new IllegalArgumentException("Index must be >= 0"); 139 } 140 } 141 } 142 143 /** 144 * Returns a <code>RowFilter</code> that uses a regular 145 * expression to determine which entries to include. Only entries 146 * with at least one matching value are included. For 147 * example, the following creates a <code>RowFilter</code> that 148 * includes entries with at least one value starting with 149 * "a": 150 * <pre> 151 * RowFilter.regexFilter("^a"); 152 * </pre> 153 * <p> 154 * The returned filter uses {@link java.util.regex.Matcher#find} 155 * to test for inclusion. To test for exact matches use the 156 * characters '^' and '$' to match the beginning and end of the 157 * string respectively. For example, "^foo$" includes only rows whose 158 * string is exactly "foo" and not, for example, "food". See 159 * {@link java.util.regex.Pattern} for a complete description of 160 * the supported regular-expression constructs. 161 * 162 * @param <M> the type of the model to which the {@code RowFilter} applies 163 * @param <I> the type of the identifier passed to the {@code RowFilter} 164 * @param regex the regular expression to filter on 165 * @param indices the indices of the values to check. If not supplied all 166 * values are evaluated 167 * @return a <code>RowFilter</code> implementing the specified criteria 168 * @throws NullPointerException if <code>regex</code> is 169 * <code>null</code> 170 * @throws IllegalArgumentException if any of the <code>indices</code> 171 * are < 0 172 * @throws PatternSyntaxException if <code>regex</code> is 173 * not a valid regular expression. 174 * @see java.util.regex.Pattern 175 */ 176 public static <M,I> RowFilter<M,I> regexFilter(String regex, 177 int... indices) { 178 return new RegexFilter<M, I>(Pattern.compile(regex), indices); 179 } 180 181 /** 182 * Returns a <code>RowFilter</code> that includes entries that 183 * have at least one <code>Date</code> value meeting the specified 184 * criteria. For example, the following <code>RowFilter</code> includes 185 * only entries with at least one date value after the current date: 186 * <pre> 187 * RowFilter.dateFilter(ComparisonType.AFTER, new Date()); 188 * </pre> 189 * 190 * @param <M> the type of the model to which the {@code RowFilter} applies 191 * @param <I> the type of the identifier passed to the {@code RowFilter} 192 * @param type the type of comparison to perform 193 * @param date the date to compare against 194 * @param indices the indices of the values to check. If not supplied all 195 * values are evaluated 196 * @return a <code>RowFilter</code> implementing the specified criteria 197 * @throws NullPointerException if <code>date</code> is 198 * <code>null</code> 199 * @throws IllegalArgumentException if any of the <code>indices</code> 200 * are < 0 or <code>type</code> is 201 * <code>null</code> 202 * @see java.util.Calendar 203 * @see java.util.Date 204 */ 205 public static <M,I> RowFilter<M,I> dateFilter(ComparisonType type, 206 Date date, int... indices) { 207 return new DateFilter<M, I>(type, date.getTime(), indices); 208 } 209 210 /** 211 * Returns a <code>RowFilter</code> that includes entries that 212 * have at least one <code>Number</code> value meeting the 213 * specified criteria. For example, the following 214 * filter will only include entries with at 215 * least one number value equal to 10: 216 * <pre> 217 * RowFilter.numberFilter(ComparisonType.EQUAL, 10); 218 * </pre> 219 * 220 * @param <M> the type of the model to which the {@code RowFilter} applies 221 * @param <I> the type of the identifier passed to the {@code RowFilter} 222 * @param type the type of comparison to perform 223 * @param number a {@code Number} value to compare against 224 * @param indices the indices of the values to check. If not supplied all 225 * values are evaluated 226 * @return a <code>RowFilter</code> implementing the specified criteria 227 * @throws IllegalArgumentException if any of the <code>indices</code> 228 * are < 0, <code>type</code> is <code>null</code> 229 * or <code>number</code> is <code>null</code> 230 */ 231 public static <M,I> RowFilter<M,I> numberFilter(ComparisonType type, 232 Number number, int... indices) { 233 return new NumberFilter<M, I>(type, number, indices); 234 } 235 236 /** 237 * Returns a <code>RowFilter</code> that includes entries if any 238 * of the supplied filters includes the entry. 239 * <p> 240 * The following example creates a <code>RowFilter</code> that will 241 * include any entries containing the string "foo" or the string 242 * "bar": 243 * <pre> 244 * List<RowFilter<Object,Object>> filters = new ArrayList<RowFilter<Object,Object>>(2); 245 * filters.add(RowFilter.regexFilter("foo")); 246 * filters.add(RowFilter.regexFilter("bar")); 247 * RowFilter<Object,Object> fooBarFilter = RowFilter.orFilter(filters); 248 * </pre> 249 * 250 * @param <M> the type of the model to which the {@code RowFilter} applies 251 * @param <I> the type of the identifier passed to the {@code RowFilter} 252 * @param filters the <code>RowFilter</code>s to test 253 * @throws IllegalArgumentException if any of the filters 254 * are <code>null</code> 255 * @throws NullPointerException if <code>filters</code> is null 256 * @return a <code>RowFilter</code> implementing the specified criteria 257 * @see java.util.Arrays#asList 258 */ 259 public static <M,I> RowFilter<M,I> orFilter( 260 Iterable<? extends RowFilter<? super M, ? super I>> filters) { 261 return new OrFilter<M,I>(filters); 262 } 263 264 /** 265 * Returns a <code>RowFilter</code> that includes entries if all 266 * of the supplied filters include the entry. 267 * <p> 268 * The following example creates a <code>RowFilter</code> that will 269 * include any entries containing the string "foo" and the string 270 * "bar": 271 * <pre> 272 * List<RowFilter<Object,Object>> filters = new ArrayList<RowFilter<Object,Object>>(2); 273 * filters.add(RowFilter.regexFilter("foo")); 274 * filters.add(RowFilter.regexFilter("bar")); 275 * RowFilter<Object,Object> fooBarFilter = RowFilter.andFilter(filters); 276 * </pre> 277 * 278 * @param <M> the type of the model the {@code RowFilter} applies to 279 * @param <I> the type of the identifier passed to the {@code RowFilter} 280 * @param filters the <code>RowFilter</code>s to test 281 * @return a <code>RowFilter</code> implementing the specified criteria 282 * @throws IllegalArgumentException if any of the filters 283 * are <code>null</code> 284 * @throws NullPointerException if <code>filters</code> is null 285 * @see java.util.Arrays#asList 286 */ 287 public static <M,I> RowFilter<M,I> andFilter( 288 Iterable<? extends RowFilter<? super M, ? super I>> filters) { 289 return new AndFilter<M,I>(filters); 290 } 291 292 /** 293 * Returns a <code>RowFilter</code> that includes entries if the 294 * supplied filter does not include the entry. 295 * 296 * @param <M> the type of the model to which the {@code RowFilter} applies 297 * @param <I> the type of the identifier passed to the {@code RowFilter} 298 * @param filter the <code>RowFilter</code> to negate 299 * @return a <code>RowFilter</code> implementing the specified criteria 300 * @throws IllegalArgumentException if <code>filter</code> is 301 * <code>null</code> 302 */ 303 public static <M,I> RowFilter<M,I> notFilter(RowFilter<M,I> filter) { 304 return new NotFilter<M,I>(filter); 305 } 306 307 /** 308 * Returns true if the specified entry should be shown; 309 * returns false if the entry should be hidden. 310 * <p> 311 * The <code>entry</code> argument is valid only for the duration of 312 * the invocation. Using <code>entry</code> after the call returns 313 * results in undefined behavior. 314 * 315 * @param entry a non-<code>null</code> object that wraps the underlying 316 * object from the model 317 * @return true if the entry should be shown 318 */ 319 public abstract boolean include(Entry<? extends M, ? extends I> entry); 320 321 // 322 // WARNING: 323 // Because of the method signature of dateFilter/numberFilter/regexFilter 324 // we can NEVER add a method to RowFilter that returns M,I. If we were 325 // to do so it would be possible to get a ClassCastException during normal 326 // usage. 327 // 328 329 /** 330 * An <code>Entry</code> object is passed to instances of 331 * <code>RowFilter</code>, allowing the filter to get the value of the 332 * entry's data, and thus to determine whether the entry should be shown. 333 * An <code>Entry</code> object contains information about the model 334 * as well as methods for getting the underlying values from the model. 335 * 336 * @param <M> the type of the model; for example <code>PersonModel</code> 337 * @param <I> the type of the identifier; when using 338 * <code>TableRowSorter</code> this will be <code>Integer</code> 339 * @see javax.swing.RowFilter 340 * @see javax.swing.DefaultRowSorter#setRowFilter(javax.swing.RowFilter) 341 * @since 1.6 342 */ 343 public abstract static class Entry<M, I> { 344 /** 345 * Creates an <code>Entry</code>. 346 */ 347 public Entry() { 348 } 349 350 /** 351 * Returns the underlying model. 352 * 353 * @return the model containing the data that this entry represents 354 */ 355 public abstract M getModel(); 356 357 /** 358 * Returns the number of values in the entry. For 359 * example, when used with a table this corresponds to the 360 * number of columns. 361 * 362 * @return number of values in the object being filtered 363 */ 364 public abstract int getValueCount(); 365 366 /** 367 * Returns the value at the specified index. This may return 368 * <code>null</code>. When used with a table, index 369 * corresponds to the column number in the model. 370 * 371 * @param index the index of the value to get 372 * @return value at the specified index 373 * @throws IndexOutOfBoundsException if index < 0 or 374 * >= getValueCount 375 */ 376 public abstract Object getValue(int index); 377 378 /** 379 * Returns the string value at the specified index. If 380 * filtering is being done based on <code>String</code> values 381 * this method is preferred to that of <code>getValue</code> 382 * as <code>getValue(index).toString()</code> may return a 383 * different result than <code>getStringValue(index)</code>. 384 * <p> 385 * This implementation calls <code>getValue(index).toString()</code> 386 * after checking for <code>null</code>. Subclasses that provide 387 * different string conversion should override this method if 388 * necessary. 389 * 390 * @param index the index of the value to get 391 * @return {@code non-null} string at the specified index 392 * @throws IndexOutOfBoundsException if index < 0 || 393 * >= getValueCount 394 */ 395 public String getStringValue(int index) { 396 Object value = getValue(index); 397 return (value == null) ? "" : value.toString(); 398 } 399 400 /** 401 * Returns the identifer (in the model) of the entry. 402 * For a table this corresponds to the index of the row in the model, 403 * expressed as an <code>Integer</code>. 404 * 405 * @return a model-based (not view-based) identifier for 406 * this entry 407 */ 408 public abstract I getIdentifier(); 409 } 410 411 412 private abstract static class GeneralFilter<M, I> extends RowFilter<M, I> { 413 private int[] columns; 414 415 GeneralFilter(int[] columns) { 416 checkIndices(columns); 417 this.columns = columns; 418 } 419 420 @Override 421 public boolean include(Entry<? extends M, ? extends I> value){ 422 int count = value.getValueCount(); 423 if (columns.length > 0) { | 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing; 26 27 import java.util.ArrayList; 28 import java.math.BigDecimal; 29 import java.math.BigInteger; 30 import java.util.Date; 31 import java.util.List; 32 import java.util.regex.Matcher; 33 import java.util.regex.Pattern; 34 import java.util.regex.PatternSyntaxException; 35 36 /** 37 * {@code RowFilter} is used to filter out entries from the 38 * model so that they are not shown in the view. For example, a 39 * {@code RowFilter} associated with a {@code JTable} might 40 * only allow rows that contain a column with a specific string. The 41 * meaning of <em>entry</em> depends on the component type. 42 * For example, when a filter is 43 * associated with a {@code JTable}, an entry corresponds to a 44 * row; when associated with a {@code JTree}, an entry corresponds 45 * to a node. 46 * <p> 47 * Subclasses must override the {@code include} method to 48 * indicate whether the entry should be shown in the 49 * view. The {@code Entry} argument can be used to obtain the values in 50 * each of the columns in that entry. The following example shows an 51 * {@code include} method that allows only entries containing one or 52 * more values starting with the string "a": 53 * <pre> 54 * RowFilter<Object,Object> startsWithAFilter = new RowFilter<Object,Object>() { 55 * public boolean include(Entry<? extends Object, ? extends Object> entry) { 56 * for (int i = entry.getValueCount() - 1; i >= 0; i--) { 57 * if (entry.getStringValue(i).startsWith("a")) { 58 * // The value starts with "a", include it 59 * return true; 60 * } 61 * } 62 * // None of the columns start with "a"; return false so that this 63 * // entry is not shown 64 * return false; 65 * } 66 * }; 67 * </pre> 68 * {@code RowFilter} has two formal type parameters that allow 69 * you to create a {@code RowFilter} for a specific model. For 70 * example, the following assumes a specific model that is wrapping 71 * objects of type {@code Person}. Only {@code Person}s 72 * with an age over 20 will be shown: 73 * <pre> 74 * RowFilter<PersonModel,Integer> ageFilter = new RowFilter<PersonModel,Integer>() { 75 * public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) { 76 * PersonModel personModel = entry.getModel(); 77 * Person person = personModel.getPerson(entry.getIdentifier()); 78 * if (person.getAge() > 20) { 79 * // Returning true indicates this row should be shown. 80 * return true; 81 * } 82 * // Age is <= 20, don't show it. 83 * return false; 84 * } 85 * }; 86 * PersonModel model = createPersonModel(); 87 * TableRowSorter<PersonModel> sorter = new TableRowSorter<PersonModel>(model); 88 * sorter.setRowFilter(ageFilter); 89 * </pre> 90 * 91 * @param <M> the type of the model; for example {@code PersonModel} 92 * @param <I> the type of the identifier; when using 93 * {@code TableRowSorter} this will be {@code Integer} 94 * @see javax.swing.table.TableRowSorter 95 * @since 1.6 96 */ 97 public abstract class RowFilter<M,I> { 98 /** 99 * Enumeration of the possible comparison values supported by 100 * some of the default {@code RowFilter}s. 101 * 102 * @see RowFilter 103 * @since 1.6 104 */ 105 public enum ComparisonType { 106 /** 107 * Indicates that entries with a value before the supplied 108 * value should be included. 109 */ 110 BEFORE, 111 112 /** 113 * Indicates that entries with a value after the supplied 114 * value should be included. 115 */ 116 AFTER, 117 118 /** 119 * Indicates that entries with a value equal to the supplied 120 * value should be included. 124 /** 125 * Indicates that entries with a value not equal to the supplied 126 * value should be included. 127 */ 128 NOT_EQUAL 129 } 130 131 /** 132 * Throws an IllegalArgumentException if any of the values in 133 * columns are {@literal <} 0. 134 */ 135 private static void checkIndices(int[] columns) { 136 for (int i = columns.length - 1; i >= 0; i--) { 137 if (columns[i] < 0) { 138 throw new IllegalArgumentException("Index must be >= 0"); 139 } 140 } 141 } 142 143 /** 144 * Returns a {@code RowFilter} that uses a regular 145 * expression to determine which entries to include. Only entries 146 * with at least one matching value are included. For 147 * example, the following creates a {@code RowFilter} that 148 * includes entries with at least one value starting with 149 * "a": 150 * <pre> 151 * RowFilter.regexFilter("^a"); 152 * </pre> 153 * <p> 154 * The returned filter uses {@link java.util.regex.Matcher#find} 155 * to test for inclusion. To test for exact matches use the 156 * characters '^' and '$' to match the beginning and end of the 157 * string respectively. For example, "^foo$" includes only rows whose 158 * string is exactly "foo" and not, for example, "food". See 159 * {@link java.util.regex.Pattern} for a complete description of 160 * the supported regular-expression constructs. 161 * 162 * @param <M> the type of the model to which the {@code RowFilter} applies 163 * @param <I> the type of the identifier passed to the {@code RowFilter} 164 * @param regex the regular expression to filter on 165 * @param indices the indices of the values to check. If not supplied all 166 * values are evaluated 167 * @return a {@code RowFilter} implementing the specified criteria 168 * @throws NullPointerException if {@code regex} is 169 * {@code null} 170 * @throws IllegalArgumentException if any of the {@code indices} 171 * are < 0 172 * @throws PatternSyntaxException if {@code regex} is 173 * not a valid regular expression. 174 * @see java.util.regex.Pattern 175 */ 176 public static <M,I> RowFilter<M,I> regexFilter(String regex, 177 int... indices) { 178 return new RegexFilter<M, I>(Pattern.compile(regex), indices); 179 } 180 181 /** 182 * Returns a {@code RowFilter} that includes entries that 183 * have at least one {@code Date} value meeting the specified 184 * criteria. For example, the following {@code RowFilter} includes 185 * only entries with at least one date value after the current date: 186 * <pre> 187 * RowFilter.dateFilter(ComparisonType.AFTER, new Date()); 188 * </pre> 189 * 190 * @param <M> the type of the model to which the {@code RowFilter} applies 191 * @param <I> the type of the identifier passed to the {@code RowFilter} 192 * @param type the type of comparison to perform 193 * @param date the date to compare against 194 * @param indices the indices of the values to check. If not supplied all 195 * values are evaluated 196 * @return a {@code RowFilter} implementing the specified criteria 197 * @throws NullPointerException if {@code date} is 198 * {@code null} 199 * @throws IllegalArgumentException if any of the {@code indices} 200 * are < 0 or {@code type} is 201 * {@code null} 202 * @see java.util.Calendar 203 * @see java.util.Date 204 */ 205 public static <M,I> RowFilter<M,I> dateFilter(ComparisonType type, 206 Date date, int... indices) { 207 return new DateFilter<M, I>(type, date.getTime(), indices); 208 } 209 210 /** 211 * Returns a {@code RowFilter} that includes entries that 212 * have at least one {@code Number} value meeting the 213 * specified criteria. For example, the following 214 * filter will only include entries with at 215 * least one number value equal to 10: 216 * <pre> 217 * RowFilter.numberFilter(ComparisonType.EQUAL, 10); 218 * </pre> 219 * 220 * @param <M> the type of the model to which the {@code RowFilter} applies 221 * @param <I> the type of the identifier passed to the {@code RowFilter} 222 * @param type the type of comparison to perform 223 * @param number a {@code Number} value to compare against 224 * @param indices the indices of the values to check. If not supplied all 225 * values are evaluated 226 * @return a {@code RowFilter} implementing the specified criteria 227 * @throws IllegalArgumentException if any of the {@code indices} 228 * are < 0, {@code type} is {@code null} 229 * or {@code number} is {@code null} 230 */ 231 public static <M,I> RowFilter<M,I> numberFilter(ComparisonType type, 232 Number number, int... indices) { 233 return new NumberFilter<M, I>(type, number, indices); 234 } 235 236 /** 237 * Returns a {@code RowFilter} that includes entries if any 238 * of the supplied filters includes the entry. 239 * <p> 240 * The following example creates a {@code RowFilter} that will 241 * include any entries containing the string "foo" or the string 242 * "bar": 243 * <pre> 244 * List<RowFilter<Object,Object>> filters = new ArrayList<RowFilter<Object,Object>>(2); 245 * filters.add(RowFilter.regexFilter("foo")); 246 * filters.add(RowFilter.regexFilter("bar")); 247 * RowFilter<Object,Object> fooBarFilter = RowFilter.orFilter(filters); 248 * </pre> 249 * 250 * @param <M> the type of the model to which the {@code RowFilter} applies 251 * @param <I> the type of the identifier passed to the {@code RowFilter} 252 * @param filters the {@code RowFilter}s to test 253 * @throws IllegalArgumentException if any of the filters 254 * are {@code null} 255 * @throws NullPointerException if {@code filters} is null 256 * @return a {@code RowFilter} implementing the specified criteria 257 * @see java.util.Arrays#asList 258 */ 259 public static <M,I> RowFilter<M,I> orFilter( 260 Iterable<? extends RowFilter<? super M, ? super I>> filters) { 261 return new OrFilter<M,I>(filters); 262 } 263 264 /** 265 * Returns a {@code RowFilter} that includes entries if all 266 * of the supplied filters include the entry. 267 * <p> 268 * The following example creates a {@code RowFilter} that will 269 * include any entries containing the string "foo" and the string 270 * "bar": 271 * <pre> 272 * List<RowFilter<Object,Object>> filters = new ArrayList<RowFilter<Object,Object>>(2); 273 * filters.add(RowFilter.regexFilter("foo")); 274 * filters.add(RowFilter.regexFilter("bar")); 275 * RowFilter<Object,Object> fooBarFilter = RowFilter.andFilter(filters); 276 * </pre> 277 * 278 * @param <M> the type of the model the {@code RowFilter} applies to 279 * @param <I> the type of the identifier passed to the {@code RowFilter} 280 * @param filters the {@code RowFilter}s to test 281 * @return a {@code RowFilter} implementing the specified criteria 282 * @throws IllegalArgumentException if any of the filters 283 * are {@code null} 284 * @throws NullPointerException if {@code filters} is null 285 * @see java.util.Arrays#asList 286 */ 287 public static <M,I> RowFilter<M,I> andFilter( 288 Iterable<? extends RowFilter<? super M, ? super I>> filters) { 289 return new AndFilter<M,I>(filters); 290 } 291 292 /** 293 * Returns a {@code RowFilter} that includes entries if the 294 * supplied filter does not include the entry. 295 * 296 * @param <M> the type of the model to which the {@code RowFilter} applies 297 * @param <I> the type of the identifier passed to the {@code RowFilter} 298 * @param filter the {@code RowFilter} to negate 299 * @return a {@code RowFilter} implementing the specified criteria 300 * @throws IllegalArgumentException if {@code filter} is 301 * {@code null} 302 */ 303 public static <M,I> RowFilter<M,I> notFilter(RowFilter<M,I> filter) { 304 return new NotFilter<M,I>(filter); 305 } 306 307 /** 308 * Returns true if the specified entry should be shown; 309 * returns false if the entry should be hidden. 310 * <p> 311 * The {@code entry} argument is valid only for the duration of 312 * the invocation. Using {@code entry} after the call returns 313 * results in undefined behavior. 314 * 315 * @param entry a non-{@code null} object that wraps the underlying 316 * object from the model 317 * @return true if the entry should be shown 318 */ 319 public abstract boolean include(Entry<? extends M, ? extends I> entry); 320 321 // 322 // WARNING: 323 // Because of the method signature of dateFilter/numberFilter/regexFilter 324 // we can NEVER add a method to RowFilter that returns M,I. If we were 325 // to do so it would be possible to get a ClassCastException during normal 326 // usage. 327 // 328 329 /** 330 * An {@code Entry} object is passed to instances of 331 * {@code RowFilter}, allowing the filter to get the value of the 332 * entry's data, and thus to determine whether the entry should be shown. 333 * An {@code Entry} object contains information about the model 334 * as well as methods for getting the underlying values from the model. 335 * 336 * @param <M> the type of the model; for example {@code PersonModel} 337 * @param <I> the type of the identifier; when using 338 * {@code TableRowSorter} this will be {@code Integer} 339 * @see javax.swing.RowFilter 340 * @see javax.swing.DefaultRowSorter#setRowFilter(javax.swing.RowFilter) 341 * @since 1.6 342 */ 343 public abstract static class Entry<M, I> { 344 /** 345 * Creates an {@code Entry}. 346 */ 347 public Entry() { 348 } 349 350 /** 351 * Returns the underlying model. 352 * 353 * @return the model containing the data that this entry represents 354 */ 355 public abstract M getModel(); 356 357 /** 358 * Returns the number of values in the entry. For 359 * example, when used with a table this corresponds to the 360 * number of columns. 361 * 362 * @return number of values in the object being filtered 363 */ 364 public abstract int getValueCount(); 365 366 /** 367 * Returns the value at the specified index. This may return 368 * {@code null}. When used with a table, index 369 * corresponds to the column number in the model. 370 * 371 * @param index the index of the value to get 372 * @return value at the specified index 373 * @throws IndexOutOfBoundsException if index < 0 or 374 * >= getValueCount 375 */ 376 public abstract Object getValue(int index); 377 378 /** 379 * Returns the string value at the specified index. If 380 * filtering is being done based on {@code String} values 381 * this method is preferred to that of {@code getValue} 382 * as {@code getValue(index).toString()} may return a 383 * different result than {@code getStringValue(index)}. 384 * <p> 385 * This implementation calls {@code getValue(index).toString()} 386 * after checking for {@code null}. Subclasses that provide 387 * different string conversion should override this method if 388 * necessary. 389 * 390 * @param index the index of the value to get 391 * @return {@code non-null} string at the specified index 392 * @throws IndexOutOfBoundsException if index < 0 || 393 * >= getValueCount 394 */ 395 public String getStringValue(int index) { 396 Object value = getValue(index); 397 return (value == null) ? "" : value.toString(); 398 } 399 400 /** 401 * Returns the identifer (in the model) of the entry. 402 * For a table this corresponds to the index of the row in the model, 403 * expressed as an {@code Integer}. 404 * 405 * @return a model-based (not view-based) identifier for 406 * this entry 407 */ 408 public abstract I getIdentifier(); 409 } 410 411 412 private abstract static class GeneralFilter<M, I> extends RowFilter<M, I> { 413 private int[] columns; 414 415 GeneralFilter(int[] columns) { 416 checkIndices(columns); 417 this.columns = columns; 418 } 419 420 @Override 421 public boolean include(Entry<? extends M, ? extends I> value){ 422 int count = value.getValueCount(); 423 if (columns.length > 0) { |