How to bind a filtered table using the JGoodies library?

Hello!

I am working for my customer in a Java EE project, where we use JGoodies for binding JTable rows to text fields below the table. This works fine, but in one dialog, there are filtering check boxes. When all the check boxes - three in number - are clicked, all items become visible. If non are checked, the table remains empty. It is possible to filter out some rows, but JGoodies, which uses the table model, is not aware of this filtering. When clicking the first row in the filtered table, the first row elements of the unfiltered table are displayed in the text fields below. Is there a way to circumvent this problem?

To my opinion, the problem is not a JGoodies problem, but binding a text field with JGoodies using the table model of a table that has been filtered.

I attached an example, where I couldn’t manage to bind the text field with the Bindings.bind method, but it shows the problem. I adopted some code found on the Internet for my purposes.

The table is filtered according to the combo box contents. If the GENERAL type of eating is selected, the binding of the text field below is fine. If, however, the combo box selects parts of the table contents, the text field below still gets the information from the (unfiltered) table model. Here, I need information how can I do this in the proper way.

Thank you for your help in advance!

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

/**
* A simplified mouse listener that reacts when the mouse has been clicked or pressed.
*
* @since 2013-08-12
*/
public abstract class ASimpleMouseListener implements MouseListener {

/**
* Event method that is called when the mouse has been clicked or pressed.
*
* @param mouseEvent the mouse event
*/
public abstract void mouseClickedOrPressed(final MouseEvent mouseEvent);

/**
* {@inheritDoc}
*/
@Override
public void mouseClicked(final MouseEvent mouseEvent) {
mouseClickedOrPressed(mouseEvent);
}

/**
* {@inheritDoc}
*/
@Override
public void mousePressed(final MouseEvent mouseEvent) {
mouseClickedOrPressed(mouseEvent);
}

/**
* {@inheritDoc}
*/
@Override
public void mouseReleased(final MouseEvent mouseEvent) { }

/**
* {@inheritDoc}
*/
@Override
public void mouseEntered(final MouseEvent mouseEvent) { }

/**
* {@inheritDoc}
*/
@Override
public void mouseExited(final MouseEvent mouseEvent) { }
}

// ========================================================

/**
* The enumeration for eating.
*
* @since 2014-04-21
*/
public enum EEating {

/** The constant for the general. */
GENERAL,

/** The constant for the lactovegetarian. */
LACTOVEGETARIAN,

/** The constant for the ovolactovegetarian. */
OVOLACTOVEGETARIAN,

/** The constant for the vegan. */
VEGAN,

/** The constant for the vegetarian. */
VEGETARIAN,
;

@Override
public String toString() {
switch (this.ordinal()) {
case 1:
return „LACTOVEGETARIAN“;
case 2:
return „OVOLACTOVEGETARIAN“;
case 3:
return „VEGAN“;
case 4:
return „VEGETARIAN“;
default:
return „GENERAL“;
}
}
}

// ========================================================

import javax.swing.table.AbstractTableModel;

/**
* The improved table sort model
*
* @since 2014-04-21
*/
public class ImprovedTableModel extends AbstractTableModel {

/** Automatically generated serial version UID */
private static final long serialVersionUID = 8348216048683404697L;

/** The column names. */
private final String[] columnNames = { „First Name“,
„Last Name“,
„Sport“,
„# of Years“,
„Vegetarian“,
};

/** The data. */
private final Object[][] data = {
{ „Kathy“, „Smith“, „Snowboarding“, 5.5, EEating.GENERAL },
{ „John“, „Doe“, „Rowing“, 3.03, EEating.VEGETARIAN },
{ „Sue“, „Black“, „Knitting“, 2.78, EEating.GENERAL },
{ „Jane“, „White“, „Speed reading“, 20.7, EEating.LACTOVEGETARIAN },
{ „Jonathan“, „Browny“, „Pool“, 10.01, EEating.OVOLACTOVEGETARIAN },
{ „Joe“, „Brown“, „Pool“, 10.98, EEating.GENERAL },
};

/* (non-Javadoc)
* @see javax.swing.table.TableModel#getColumnCount()
*/
@Override
public int getColumnCount() {
return columnNames.length;
}

/* (non-Javadoc)
* @see javax.swing.table.TableModel#getRowCount()
*/
@Override
public int getRowCount() {
return data.length;
}

/* (non-Javadoc)
* @see javax.swing.table.AbstractTableModel#getColumnName(final int)
*/
@Override
public String getColumnName(final int columnIndex) {
return columnNames[columnIndex];
}

/* (non-Javadoc)
* @see javax.swing.table.TableModel#getValueAt(final int, int)
*/
@Override
public Object getValueAt(final int rowIndex, final int columnIndex) {
return data[rowIndex][columnIndex];
}

/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn’t implement this method,
* then the last column would contain text („true“/„false“),
* rather than a check box.
*/
/* (non-Javadoc)
* @see javax.swing.table.AbstractTableModel#getColumnClass(int)
*/
@Override
@SuppressWarnings(„unchecked“)
public Class getColumnClass(final int columnIndex) {
return getValueAt(0, columnIndex).getClass();
}

/*
* Don’t need to implement this method unless your table’s editable.
*/
/* (non-Javadoc)
* @see javax.swing.table.AbstractTableModel#isCellEditable(int, int)
*/
@Override
public boolean isCellEditable(final int rowIndex, final int columnIndex) {
// Note that the data/cell address is constant,
// no matter where the cell appears on screen.
return (columnIndex < 2) ? false : true;
}

/*
* Don’t need to implement this method unless your table’s
* data can change.
*/
/* (non-Javadoc)
* @see javax.swing.table.AbstractTableModel#setValueAt(java.lang.Object, int, int)
*/
@Override
public void setValueAt(final Object value, final int rowIndex, final int columnIndex) {
data[rowIndex][columnIndex] = value;
}
}

// ========================================================

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.regex.Pattern;

import javax.swing.DefaultRowSorter;
import javax.swing.JComboBox;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.RowSorter;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

import swing.table.filtering.EEating;
import swing.table.filtering.ImprovedTableModel;

import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.list.ArrayListModel;
import com.jgoodies.binding.list.SelectionInList;
import common.view.ASimpleMouseListener;

/**
* The table sort demo.
*/
public class ImprovedTableFilterBindingDemo extends JPanel {

/**
* The Class WidgetToolkit.
*
* @author Ahmet Ekrem Saban
*/
public class WidgetToolkit {

/** The description. */
private String description;

/** The name. */
private String name;

/** The producer. */
private String producer;

/**
* Instantiates a new widget toolkit.
*
* @param name the name
* @param description the description
* @param producer the producer
*/
public WidgetToolkit(String name, String description, String producer) {
this.description = description;
this.name = name;
this.producer = producer;
}

/**
* Gets the description.
*
* @return the description
*/
public String getDescription() {
return description;
}

/**
* Gets the name.
*
* @return the name
*/
public String getName() {
return name;
}

/**
* Gets the producer.
*
* @return the producer
*/
public String getProducer() {
return producer;
}

/**
* Sets the description.
*
* @param description the new description
*/
public void setDescription(String description) {
this.description = description;
}

/**
* Sets the name.
*
* @param name the new name
*/
public void setName(String name) {
this.name = name;
}

/**
* Sets the producer.
*
* @param producer the new producer
*/
public void setProducer(String producer) {
this.producer = producer;
}

}

/** The constant serialVersionUID. */
private static final long serialVersionUID = 2843557005187163601L;

/** The surname column index. */
private final byte SURNAME_COLUMN_INDEX = 1;

/** The food type combo box. */
private final JComboBox<EEating> foodTypeComboBox;

/** The surname text field. */
private final JFormattedTextField surnameTextField;

/** The table row sorter. */
private final RowSorter<? extends TableModel> tableRowSorter;

/** The JTable. */
private JTable jTable;

/** The table model. */
private final TableModel tableModel = new ImprovedTableModel();

/** The array list model. */
private final ArrayListModel<WidgetToolkit> arrayListModel;

/**
* Instantiates a new table sort demo.
*/
public ImprovedTableFilterBindingDemo() {
super(new GridLayout(3, 1));

getTable().setPreferredScrollableViewportSize(new Dimension(500, 70));
getTable().setFillsViewportHeight(true);
getTable().setAutoCreateRowSorter(true);

System.out.println(„Test EEating first…“);
final EEating[] eatings = EEating.values();

for (byte y = 0; y < eatings.length; y++) {
System.out.println("EEating: " + eatings[y].toString());
}

this.arrayListModel = new ArrayListModel<WidgetToolkit>();

this.arrayListModel.add(new WidgetToolkit(„Swing“, „Is a Java API“, „Sun“));
this.arrayListModel.add(new WidgetToolkit(„Flash“, „Is NOT a Java API“, „Macromedia“));
this.arrayListModel.add(new WidgetToolkit(„SWT“, „Is a Java API“, „Eclipse“));
this.arrayListModel.add(new WidgetToolkit(„QT“, „Is NOT a Java API“, „Trolltech“));
this.arrayListModel.add(new WidgetToolkit(„AWT“, „Is a Java API“, „Sun“));

final SelectionInList<?> selectionInList =
new SelectionInList<ArrayListModel<WidgetToolkit>>(this.arrayListModel);

final JList<?> jList = new JList<Object>();

Bindings.bind(jList, selectionInList);

final ImprovedTableModel model = new ImprovedTableModel();

tableRowSorter = new TableRowSorter<ImprovedTableModel>(model);

// Create the scroll pane and add the table to it.
final JScrollPane scrollPane = new JScrollPane(getTable());
foodTypeComboBox = new JComboBox<EEating>();

foodTypeComboBox.addItem(EEating.GENERAL);
foodTypeComboBox.addItem(EEating.LACTOVEGETARIAN);
foodTypeComboBox.addItem(EEating.OVOLACTOVEGETARIAN);
foodTypeComboBox.addItem(EEating.VEGETARIAN);
// Add the scroll pane to this panel.
add(foodTypeComboBox);
add(scrollPane);

surnameTextField = new JFormattedTextField();

add(surnameTextField);

foodTypeComboBox.addActionListener(new ActionListener() {

@Override
public void actionPerformed(final ActionEvent ae) {
newFilter();
}
});

getTable().addMouseListener(new ASimpleMouseListener() {

@Override
public void mouseClickedOrPressed(MouseEvent mouseEvent) {
surnameTextField.setText((String) getTable().getModel().getValueAt(
getTable().getSelectedRow(), SURNAME_COLUMN_INDEX));
}
});

}

/**
* New filter.
*/
@SuppressWarnings(„unchecked“)
protected void newFilter() {
getTable().setRowSorter(tableRowSorter);
final String BEGIN = „(?i).*?\b(“, END = „)\b.*“;
final StringBuilder regularExpression = new StringBuilder("(");

final EEating selection = (EEating) foodTypeComboBox.getSelectedItem();

switch (selection) {
case LACTOVEGETARIAN:
regularExpression.append(BEGIN + EEating.LACTOVEGETARIAN + END);
break;
case OVOLACTOVEGETARIAN:
regularExpression.append(BEGIN + EEating.OVOLACTOVEGETARIAN + END);
break;
case VEGAN:
regularExpression.append(BEGIN + EEating.VEGAN + END);
break;
case VEGETARIAN:
regularExpression.append(BEGIN + EEating.VEGETARIAN + END);
break;
default:
regularExpression.append(".*");
}

regularExpression.append(")");
System.out.println(„newFilter(): RegEx = „“ + regularExpression.toString() + „““);
final RowFilter<ImprovedTableModel, Object> rowFilter =
RowFilter.regexFilter(Pattern.compile(
regularExpression.toString(), Pattern.CASE_INSENSITIVE).toString()
, 4);

((DefaultRowSorter<ImprovedTableModel, Integer&gt:wink: tableRowSorter).setRowFilter(rowFilter);
tableRowSorter.modelStructureChanged();
}

/**
* Gets the table.
*
* @return the table
*/
private JTable getTable() {
if (jTable == null) {
jTable = new JTable(tableModel );
}
return jTable;
}

/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
// Create and set up the window.
JFrame frame = new JFrame(„TableSortDemo“);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

// Create and set up the content pane.
ImprovedTableFilterBindingDemo newContentPane = new ImprovedTableFilterBindingDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);

// Display the window.
frame.pack();
frame.setVisible(true);
}

/**
* The main method.
*
* @param arguments the arguments
*/
public static void main(final String[] arguments) {
// Schedule a job for the event-dispatching thread:
// creating and showing this application’s GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
}