Dynamic filtering using MatcherEditors

One limitation of filtering with just Matchers is that they don't provide a way to change the filtering logic. For example, in our TextFilterList, the filter logic is different for each character that is typed in the filter edit text field. For our users filter, we would like to change the filtering logic each time a user is selected or deselected.

To implement dynamic filtering, we create a MatcherEditor. This interface provides a simple mechanism for the FilterList to listen for changes to the filtering logic. Whenever the filtering logic is changed, the MatcherEditor creates a new Matcher that models the new logic and fires an event to the listening MatcherEditorListeners. This can be implemented quickly by extending the AbstractMatcherEditor interface.

For our users filter, we create a new IssuesForUsersMatcher whenever the selection changes.

import javax.swing.event.*;
import javax.swing.*;
// glazed lists
import ca.odell.glazedlists.*;
import ca.odell.glazedlists.matchers.*;
import ca.odell.glazedlists.swing.*;
// a simple issues library
import ca.odell.issuezilla.*;

/**
 * This {@link MatcherEditor} matches issues if their user is selected.
 * 
 * @author <a href="mailto:jesse@odel.on.ca">Jesse Wilson</a>
 */
public class UsersSelect extends AbstractMatcherEditor implements ListSelectionListener {
    
    /** a list of users */
    EventList usersEventList;
    EventList usersSelectedList;

    /** a widget for selecting users */
    JList usersJList;
    
    /**
     * Create a {@link IssuesForUsersMatcherEditor} that matches users from the
     * specified {@link EventList} of {@link Issue}s.
     */
    public UsersSelect(EventList source) {
        // derive the users list from the issues list
        EventList usersNonUnique = new IssueToUserList(source);
        usersEventList = new UniqueList(usersNonUnique);

        // create a JList that contains users
        EventListModel usersListModel = new EventListModel(usersEventList);
        usersJList = new JList(usersListModel);

        // create an EventList containing the JList's selection
        EventSelectionModel userSelectionModel = new EventSelectionModel(usersEventList);
        usersJList.setSelectionModel(userSelectionModel);
        usersSelectedList = userSelectionModel.getSelected();
        
        // handle changes to the list's selection
        usersJList.addListSelectionListener(this);
    }
    
    /**
     * Get the widget for selecting users.
     */
    public JList getJList() {
        return usersJList;
    }

    /**
     * When the JList selection changes, create a new Matcher and fire 
     * an event.
     */
    public void valueChanged(ListSelectionEvent e) {
        Matcher newMatcher = new IssuesForUsersMatcher(usersSelectedList);
        fireChanged(newMatcher);
    }
}

Finally, we must configure our new MatcherEditor to be used by a FilterList. For this, we specify our MatcherEditor as an argument to the FilterList's constructor.

        EventList issues = ...
        UsersSelect usersSelect = new UsersSelect(issues);
        FilterList userFilteredIssues = new FilterList(issues, usersSelect);