Filter table rows

Filtering table rows according to some criterion is a common requirement in Swing applications. JSE 6 has added a filter and sort mechanism to JTable, using RowFilter and TableRowSorter.

The following example is for JSE 1.5. This example uses Quote objects, which are data-centric objects containing information regarding the current price of a stock.

QuoteFilter is an abstract base class (sometimes called an "ABC") which:


package hirondelle.stocks.table;

import hirondelle.stocks.quotes.Quote;
import java.util.*;

/**
* Allows collections of {@link Quote} objects 
* to be filtered according to a criterion defined by implementors.
* JDK less than 6.
*/
public abstract class QuoteFilter {
  
  /**
  * Defines the criteria by which <tt>aQuote</tt> is accepted or rejected 
  * by this filter.
  */
  abstract public boolean isAcceptable(Quote aQuote);
  
  /**
  * Return a <tt>List</tt> which has the same 
  * iteration order as <tt>aQuotes</tt>, but which includes only those elements 
  * which satisfy {@link #isAcceptable}.
  */
  public final List<Quote> sift(Collection<Quote> aQuotes ){
    /*
    * This is an example of a template method : the general outline is
    * defined here in this abstract base class, but the implementation of 
    * specific steps (in this case the method isAcceptable) is left to 
    * concrete subclasses.
    *
    * Note as well that this method is final, so that no subclass can override this 
    * implementation.
    */
    List<Quote> result = new ArrayList<>();
    for(Quote quote : aQuotes){
      if (isAcceptable(quote)) {
        result.add(quote);
      }
    }
    return result;
  }
}
 

The QuoteFilterFactory class QuoteFilterFactory uses the idea of attaching a "user object" to each node of a JTree. These user objects are the QuoteFilter implementations. When the user clicks on a new node, listeners attached to QuoteFilterFactory are informed of the change, and use getSelectedFilter to fetch the corresponding filter. They then use the filter object to update the list underlying the TableModel, and then finally call fireTableDataChanged, to ensure the change becomes visible to the user.
/**
* Graphical component which allows the end user to select a 
* {@link QuoteFilter}, and informs its listeners of changes 
* to this selection.
* JDK less than 6.
*/
public final class QuoteFilterFactory extends JScrollPane implements Observer {

  /**
  * Constructor.
  *  
  * @param aCurrentPortfolio is observed by this class, since the list of 
  * possible filters displayed by this class depends on its content.
  */
  public QuoteFilterFactory (CurrentPortfolio aCurrentPortfolio){
    fCurrentPortfolio = aCurrentPortfolio;
    //bad practice - 'this' is not fully defined until the constructor has returned:
    fCurrentPortfolio.addObserver(this);
    fSelectedFilter = NO_SELECTION_FILTER;
    initGui();
  }
  
  /**
  * Return the <tt>QuoteFilter</tt> 
  * attached to the currently selected item. If no selection is present, 
  * then return a <tt>QuoteFilter</tt> which accepts all {@link Quote} objects.
  */
  public QuoteFilter getSelectedFilter(){
    assert fSelectedFilter != null : "Filter has null value.";
    return fSelectedFilter;
  }
  
  //elided...

  
  // PRIVATE

  private CurrentPortfolio fCurrentPortfolio;
  
  /**
  * The {@link QuoteFilter} corresponding to the user's 
  * current non-null selection.
  */
  private QuoteFilter fSelectedFilter;
  
  /**
  * The {@link QuoteFilter} corresponding to the absence of 
  * any user selection.
  */
  private QuoteFilter NO_SELECTION_FILTER = 
    new QuoteFilterAcceptAll(Consts.EMPTY_STRING)
  ;

  //elided...

  // All QuoteFilter implementations:
  
  /**
  * No {@link Quote} objects are rejected by this filter.
  *
  *<P> Since more than one tree node uses this filter, text is 
  * passed to the constructor to allows for customized display.
  */
  static private class QuoteFilterAcceptAll extends QuoteFilter {
    QuoteFilterAcceptAll(String aText) {
      fText = aText;
    }
    public boolean isAcceptable(Quote aQuote){
      return true;
    }
    @Override public String toString(){
      return fText;
    }
    private final String fText;
  }
  
  /**
  * {@link Quote} objects are accepted only if they are from a specific 
  * {@link Exchange}.
  */
  static private final class QuoteFilterExchange extends QuoteFilter {
    QuoteFilterExchange(Exchange aExchange) {
      fTargetExchange = aExchange;
    }
    @Override public boolean isAcceptable(Quote aQuote){
      return aQuote.getStock().getExchange() == fTargetExchange;
    }
    @Override public String toString(){
      return fTargetExchange.toString();
    }
    private Exchange fTargetExchange;
  }
  
  /**
  * {@link Quote} objects are accepted only if todays price change is 
  * non-negative.
  */
  static private final class QuoteFilterGainers extends QuoteFilter {
    @Override public boolean isAcceptable(Quote aQuote){
      return aQuote.getChange().doubleValue() >= 0.0;
    }
    @Override public String toString(){
      return GAINERS;
    }
  }

  /** 
  * {@link Quote} objects are accepted only if todays price change is negative.
  */
  static private final class QuoteFilterLosers extends QuoteFilter {
    @Override public boolean isAcceptable(Quote aQuote){
      return aQuote.getChange().doubleValue() < 0.0;
    }
    @Override public String toString(){
      return LOSERS;
    }
  }
  
  /** {@link Quote} objects are accepted only if it is an index.  */
  static private final class QuoteFilterIndex extends QuoteFilter {
    @Override public boolean isAcceptable(Quote aQuote){
      return aQuote.getStock().isIndex();
    }
    @Override public String toString(){
      return INDEX;
    }
  }

  /** {@link Quote} objects are accepted only if it is not an index.  */
  static private final class QuoteFilterNonIndex extends QuoteFilter {
    @Override public boolean isAcceptable(Quote aQuote){
      return !aQuote.getStock().isIndex();
    }
    @Override public String toString(){
      return NON_INDEX;
    }
  }
}
 

See Also :
Observers and listeners
Sort table rows