Tuesday, May 3, 2011

Multiple Table filters in Java

As mentioned in the previous post, I have been working with Java TableFilters and RowSorters for some time now and as it turns out, they are more customizable than I imagined in the beginning. Anyways, this time however, I wanted to combine these filters as in use multiple filters together. e.g. Let's say I have a table as follows:
Name Question Answer CorrectAnswer
A Q1 A1 A1
B Q1 A1 A1
C Q1 A1 A1
D Q1 A1 A1
A Q2 A3 A2 <-- wrong answer
B Q2 A2 A2
C Q2 A3 A2 <-- wrong answer

Now let's say I want to filter/sort this data to
1. Display only selected Names
2.Display only rows where Answer matches CorrectAnswer
3. Do 1 and 2 above together or individually

It makes sense to write separate filters for 1 and 2 above and then use them separately or combine them. With my architecture, I was invoking a new filter of required type whenever a related event occurs on the control. But the problem with this approach was, a new filter would be applied on the rowSorter and since the changes made by filter on the visible table are no persistant and in most of the cases you wouldn't want them to be.
So there has to be a way to combine these filters. There's obviously a way you can do that by means of an andFilter as follows:

compoundRowFilter = RowFilter.andFilter(compoundFilterList);

rowSorter.setRowFilter(compoundRowFilter);

Where compoundFilterList is a list of filters you are trying to make work together.

So, what you can do instead of the following:


protected void newFilter(String filterRegExp) {

textFilter = null;

// If current expression doesn't parse, don't update.

try {

textFilter = RowFilter.regexFilter(filterRegExp);

} catch (java.util.regex.PatternSyntaxException e) {

return;

}

rowSorter.setRowFilter(textFilter);

}

is this:

protected void newFilter(String filterRegExp) {

textFilter = null;

// If current expression doesn't parse, don't update.

try {

textFilter = RowFilter.regexFilter(filterRegExp);

} catch (java.util.regex.PatternSyntaxException e) {

return;

}

filterList.add(textFilter);

compoundRowFilter = RowFilter.andFilter(filterList);

rowSorter.setRowFilter(compoundRowFilter);

}
I got this idea from here

Now, this will work fine in most of the cases, unless, your filters are dynamic. e.g. in my case, my filter changes as I type. So, each filter will be "added" to the filterList instead of being updated. This will keep on adding filters to the table as there's no way (or at least it's very difficult to) remove a specific object from a list. Also, I could not find any method that removes the filter altogether from rowSorter.
So, I figured, I should be able update a particular filter object in the list rather than to add it. Again, I found it difficult to update it in the filterList itself. So I decided to have a fixed number of filters in an array of filters and whenever a filter updates, update in it's position in the array and construct a list out of it. Note that, null filters cannot be applied, so I had to make sure that the filter objects aren't null, before adding them to the list:
This is how I did it:

private RowFilter[] compoundFilterArray = new RowFilter [numberOfFiltersYouWant];

And later in the filter code:

protected void newFilter(String filterRegExp) {

textFilter = null;

// If current expression doesn't parse, don't update.

try {

textFilter = RowFilter.regexFilter(filterRegExp);

} catch (java.util.regex.PatternSyntaxException e) {

return;

}

this.compoundFilterArray[1] = textFilter;

applyCompoundFilter();

}


protected void newFilter(final int userAnswerColumnIndex,

final int expectedAnswerColumnIndex) {

incorrectResponseFilter = new RowFilter() {

@Override

public boolean include(Entry entry) {

String actualResponse = entry

.getStringValue(userAnswerColumnIndex);

String expectedResponse = entry

.getStringValue(expectedAnswerColumnIndex);

return expectedResponse.contains(actualResponse);

}

};

// replace the filter with new object, each time the filter is updated

this.compoundFilterArray[0] = incorrectResponseFilter;

applyCompoundFilter();

}



where:

private void applyCompoundFilter() {

ArrayList> compoundFilterList = new ArrayList>();

for(int i = 0; i< compoundFilterArray.length; i++){

if(compoundFilterArray[i] != null){

compoundFilterList.add(compoundFilterArray[i]);

}

}

compoundRowFilter = RowFilter.andFilter(compoundFilterList);

rowSorter.setRowFilter(compoundRowFilter);

}

No comments:

Post a Comment