This class provides generic filtering and sorting capabilities which are compatible with the standard Java collections.

Concepts | Collection Filters | Dynamic Filters | Sorting

Concepts

Filtering is a fairly straightforward concept. Given a collection of objects, iterate over the collection and determine if the specific object should be included or discarded based on specific filter criteria.

Sorting is also pretty straightforward. Given an ordered collection of objects, reorder the objects such that each one is relative to the others based on specific criteria. The Java collections already support basic sorting via the java.lang.Comparable and java.util.Comparator interfaces. This package provides a way to dynamically order collections without actually writing the comparison routines, based on the values of given object properties.

Collection Filters

This package supports different types of filtering, depending on where the filtering should occur. The first, and simplest, type of filter allows the user to provide a class implementing the Filter interface to provide a specific, hard-coded test.

For example, given a List instance containing String instances, the application would like to only display those strings which start with the literal 'filterme'. While the {@link com.townleyenterprises.filter.Filters} class provides a static method to encapsulate the details, an alternate solution to the problem could be provided by the following code:

	Iterator collIterator = stringList.iterator();
	FilteredIterator fi = new FilteredIterator(collIterator,
		new Filter() {
			public boolean doFilter(Object o)
			{
				if(!(o instanceof String))
					return false;

				String s = (String)o;
				return s.startsWith("filterme");
			}
		});
	
	// print our strings
	while(fi.hasNext())
		System.out.println(fi.next());

Example 1: A Simple Filter

The mechanics of applying the filter are encapsulated in the {@link com.townleyenterprises.filter.FilteredIterator} instance which is used to provide a Decorator or Wrapper for a standard Java Iterator instance. Internally, the FilteredIterator is accessing the next element and applying the filter. If the filter succeeds, the element is returned when the user of the iterator calls the next method.

The full power of the filtering mechanism in the package comes from the various logical filters. Using combinations of the AND, OR and NOT filters, complicated expression parse trees may be created and applied as easily as the simple filter shown in Example 1.

To apply the logical filters to a collection of user defined objects in order to select only a part of the collection, the same procedure is employed as for the simple filters. This approach is illustrated in Example 2 below:

	// filter1 and filter2 are defined earlier
	LogicalAndFilter and = new LogicalAndFilter();
	and.addFilter(filter1);
	and.addFilter(filter2);
	FilteredIterator fi = new FilteredIterator(list.iterator(), and);

	while(fi.hasNext())
		System.out.println(fi.next());

Example 2: Applying Logical Filters

In Example 2, only those objects in the collection where both filter1 and filter2 return true will be printed. The logical OR filter is equivalent in setup and use to the logical AND filter, but it will include objects in the results if either filter evaluates true.

Dynamic Filters

The filters described in Example 1 and Example 2 are tightly bound to the application in which they are used. The filter implementation must know the underlying structure of the objects so that it can perform the filtering. This approach is not always desirable for general use selections such as a string property being equal to another. For this reason, the package provides an implementation of the QueryObject design pattern to address these general cases.

The dynamic filters rely heavily on the java.lang.Comparable interface and can only be used with classes which implement it. Fortunately, all of the interesting types in the J2SDK implement it, so it works automatically with all of the primitive and built in wrappers for the primitive types. The {@link com.townleyenterprises.filter.QueryFilter} class can be used for all but substring searches if exact matches are required. For example, given a custom class with properties code and value, to find all of the objects which had a code of 'EUR' and values of less than 1000.00, a filter could be created and applied to the collection as illustrated in Example 3.

	QueryFilter code = new QueryFilter(Widget.class,
				"code", QueryOperator.EQ, "EUR");
	QueryFilter value = new QueryFilter(Widget.class,
				"value", QueryOperator.LT,
				new BigDecimal("1000.00"));
	LogicalAndFilter and = new LogicalAndFilter();
	and.addFilter(code);
	and.addFilter(value);

	Collection widgets = Filters.filter(list, and);

Example 3: Using Dynamic Filters

The resulting collection of widgets would all have currency codes of 'EUR' and have values of less than 1000.00.

Sorting

Sorting is often used in conjunction with filtering, but the sorting facilities do not depend on the filtering mechanisms. Like the QueryFilter, the {@link com.townleyenterprises.filter.PropertySorter} class depends on the underlying objects implementing the java.lang.Comparable interface. Like the QueryFilter, the PropertySorter automatically works with any of the base types provided by the Java language.

An example of using the sorting facilities outside of filtering would be when working with a Java Swing table whose underlying table model is represented by a list of custom objects. For simplicity, take the Widget class used for Example 3. If the Widget instances were displayed in a sortable table. The available properties are Stock No, Description, Quantity and Color in addition to the Code and Value from above. In order to sort the columns to see which ones should be discounted, the following code could be used:

	// define the sort so we can se the largest quantity
	// on hand at the top of the list

	SortSpecification[] sort = {
		new SortSpecification("quantity", SortOrder.DESCENDING),
		new SortSpecification("description"),
		new SortSpecification("code"),
		new SortSpecification("value")
	};

	Collections.sort(tableData, new PropertySorter(Widget.class, sort));

Example 4: Sorting Collections

Additionally, filtering and sorting can be done at the same time using the static {@link com.townleyenterprises.filter.Filters} utility class. To combine Examples 3 and 4, only the following changes would need to be made:

	QueryFilter code = new QueryFilter(Widget.class,
				"code", QueryOperator.EQ, "EUR");
	QueryFilter value = new QueryFilter(Widget.class,
				"value", QueryOperator.LT,
				new BigDecimal("1000.00"));
	LogicalAndFilter and = new LogicalAndFilter();
	and.addFilter(code);
	and.addFilter(value);

	// define the sort so we can se the largest quantity
	// on hand at the top of the list

	SortSpecification[] sort = {
		new SortSpecification("quantity", SortOrder.DESCENDING),
		new SortSpecification("description"),
		new SortSpecification("code"),
		new SortSpecification("value")
	};

	List widgets = Filters.filter(list, and, sort);

Example 5: Dynamic Filtering and Sorting

Limitations

At the present time, the following limitations/issues are present in the package: