United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6316493 : Incoporate feedback on filtering/sorting API

Submit Date:
Updated Date:
Project Name:
Resolved Date:
Affected Versions:
Fixed Versions:

Related Reports

Sub Tasks

We've gotten some external feedback on swing's sorting/filtering API that should be incorporated before beta.



Here's the list of changes:
Two new type parameters will be added:
 * @param <M> the type of the model; for example <code>PersonModel</code>
 * @param <I> the type of the identifier; when using
 *            <code>TableRowSorter</code> this will be <code>Integer</code>
public abstract class RowFilter<M,I>;
They are used in the include method:
    public abstract boolean include(Entry<? extends M, ? extends I> entry);
RowFilter's examples in the javadoc will be updated appropriately:
 * <pre>
 * RowFilter startsWithAFilter = new RowFilter&lt;Object,Object&gt;() {
 *   public boolean include(Entry&lt;? extends Object, ? extends Object&gt; entry) {
 *     for (int i = entry.getValueCount() - 1; i &gt;= 0; i--) {
 *       if (entry.getStringValue(i).startsWith("a")) {
 *         // The value starts with "a", include it
 *         return true;
 *       }
 *     }
 *     // None of the columns start with "a"; return false so that this
 *     // entry is not shown
 *     return false;
 *   }
 * };
 * </pre>
 * <code>RowFilter</code> has two formal type parameters that allow
 * you to create a <code>RowFilter</code> for a specific model. For
 * example, the following assumes a specific model that is wrapping
 * objects of type <code>Person</code>.  Only <code>Person</code>s
 * with an age over 20 will be shown:
 * <pre>
 * RowFilter&lt;PersonModel,Integer&gt; ageFilter = new RowFilter&lt;PersonModel,Integer&gt;() {
 *   public boolean include(Entry&lt;? extends PersonModel, ? extends Integer&gt; entry) {
 *     PersonModel personModel = entry.getModel();
 *     Person person = personModel.getPerson(entry.getIdentifier());
 *     if (person.getAge() &gt; 20) {
 *       // Returning true indicates this row should be shown.
 *       return true;
 *     }
 *     // Age is &lt;= 20, don't show it.
 *     return false;
 *   }
 * };
 * PersonModel model = createPersonModel();
 * TableRowSorter&lt;PersonModel&gt; sorter = new TableRowSorter&lt;PersonModel&gt;(model);
 * sorter.setRowFilter(ageFilter);
 * </pre>
RowFilter's factory methods have been updated as well:
    public static RowFilter<Object,Object> regexFilter(String regex,
                                                       int... indices);
    public static RowFilter<Object,Object> dateFilter(ComparisonType type,
                                            Date date, int... indices);
    public static RowFilter<Object,Object> numberFilter(ComparisonType type,
                                            Number number, int... indices);
    public static <M,I> RowFilter<M,I> orFilter(
            List<RowFilter<M, I>> filters);
    public static <M,I> RowFilter<M,I> andFilter(
            List<RowFilter<M, I>> filters);
    public static <M,I> RowFilter<M,I> notFilter(RowFilter<M,I> filter);
Of note is the orFilter/andFilter methods.  These previously took
varags.  Because of restrictions on arrays and generics they
now take Lists.  This resulted in orFilter/andFilter now throwing an
NPE if passed a null List:
     * @throws NullPointerException if <code>filters</code> is null
Additionally regexFilter will be changed from using
java.util.regex.Matcher.matches to java.util.regex.Matcher.find.  The
new javadoc will be:
     * Returns a <code>RowFilter</code> that uses a regular
     * expression to determine which entries to include.  Only entries
     * with at least one matching value are included.  For
     * example, the following creates a <code>RowFilter</code> that
     * includes entries with at least one value starting with
     * "a":
     * <pre>
     *   RowFilter.regexFilter("^a");
     * </pre>
     * <p>
     * The returned filter uses {@link java.util.regex.Matcher#find}
     * to test for inclusion.  To test for exact matches use ^ and $
     * to match the beginning and end of the string respectively.  For
     * example, "^foo$" includes only whose string is exactly "foo"
     * and not, for example, "food".  See {@link
     * java.util.regex.Pattern} for a complete description of the
     * supported regular-expression constructs.
     * @param regex the regular expression to filter on
     * @param indices the indices of the values to check.  If not supplied all
     *               values are evaluated
     * @return a <code>RowFilter</code> implementing the specified criteria
     * @throws NullPointerException if <code>regex</code> is
     *         <code>null</code>
     * @throws IllegalArgumentException if any of the columns
     *         contains a value &lt; 0
     * @throws PatternSyntaxException if <code>regex</code> is
     *         not a valid regular expression.
     * @see java.util.regex.Pattern
    public static RowFilter<Object,Object> regexFilter(String regex,
                                                       int... indices) {
        return new RegexFilter(Pattern.compile(regex), indices);
RowFilter's static inner class Entry will also get two type parameters:
     * @param <M> the type of the model; for example <code>PersonModel</code>
     * @param <I> the type of the identifier; when using
     *            <code>TableRowSorter</code> this will be <code>Integer</code>
    public static abstract class Entry<M, I> {}
The type parameters are used in the following methods:
        public abstract M getModel();
        public abstract I getIdentifier();
javax.swing.RowSorter will get one type parameter:
 * @param <M> the type of the underlying model
public abstract class RowSorter<M> {
The new method getModel will use the type parameter:
     * Returns the underlying model.
     * @return the underlying model
    public abstract M getModel();
RowSorter will also get the following new method:
     * Returns the number of rows in the underlying model.
     * @return number of rows in the underlying model
     * @see #getViewRowCount
    public abstract int getModelRowCount();
RowSorter's inner class SortKey will get two new methods:
         * Returns the hash code for this <code>SortKey</code>.
         * @return hash code
        public int hashCode();
         * Returns true if this object equals the specified object.
         * If the specified object is a <code>SortKey</code> and
         * references the same column and sort order, the two objects
         * are equal.
         * @param o the object to compare to
         * @return true if <code>o</code> is equal to this <code>SortKey</code>
        public boolean equals(Object o);
And SortKey's getColumn and getSortOrder methods will be made final:
        public final int getColumn();
        public final SortOrder getSortOrder();
javax.swing.DefaultRowSorter will also get two type parameters:
 * @param <M> the type of the model
 * @param <I> the type of the object passed to the <code>RowFilter</code>
public abstract class DefaultRowSorter<M, I> extends RowSorter<M> {
getModel will be renamed to getModelWrapper:
    protected abstract ModelWrapper<M,I> getModelWrapper();
set/getRowFilter will be updated based on the new type parameters:
    public void setRowFilter(RowFilter<? super M,? super I> filter);
    public RowFilter<? super M,? super I> getRowFilter();
The new method getModelRowCount will be overriden from the superclass:
     * {@inheritDoc}
    public int getModelRowCount();
javax.swing.DefaultRowSorter's static inner class Model will be
renamed to ModelWrapper and get two type parameters:
     * @param <M> the type of the underlying model
     * @param <I> the identifier supplied to the filter
    protected abstract static class ModelWrapper<M,I>;
The pararamter is returned from the getModel and the new getIdentifier
        public abstract M getModel();
         * Returns the identifier for the specified row.  The return value
         * of this is used as the identifier for the
         * <code>RowFilter.Entry</code> that is passed to the
         * <code>RowFilter</code>.
         * @param row the row to return the identifier for, in terms of
         *            the underlying model
         * @return the identifier
         * @see RowFilter.Entry#getIdentifier
        public abstract I getIdentifier(int row);
TableRowSorter will get one formal type parameter:
 * @param <M> the type of the model, which must be an implementation of
 *            <code>TableModel</code>
public class TableRowSorter<M extends TableModel> extends DefaultRowSorter<M, Integer>;
The following methods will make use of it:
    public TableRowSorter(M model);
     * Sets the <code>TableModel</code> to use as the underlying model
     * for this <code>TableRowSorter</code>.  A value of <code>null</code>
     * can be used to set an empty model.
     * @param model the underlying model to use, or <code>null</code>
    public void setModel(M model) {
     * {@inheritDoc}
    public M getModel();
setModel/getModel were previously named set/getTableModel. They have
been renamed to coincide with the getModel method of RowSorter.
And what was getModel is now called getModelWrapper:
     * Returns an implementation of <code>DefaultRowSorter.ModelWrapper</code>
     * wrapping the provided <code>TableModel</code>.
     * @return a <code>DefaultRowSorter.ModelWrapper</code> implementation
     *         wrapping the provided <code>TableModel</code>
    protected ModelWrapper<M,Integer> getModelWrapper();
javax.swing.JTable has been changed to take a specific type of RowSorter:
    public void setRowSorter(RowSorter<? extends TableModel> sorter);
    public RowSorter<? extends TableModel> getRowSorter();

Hardware and Software, Engineered to Work Together