Aide mémoire : JXTable

Aide mémoire (Développement) / Uncategorized
Petit aide mémoire sur l’utilisation d’une JXTable et de certains choses faisables avec…

1. Le modèle et ses données
2. Le panel
3. Les filtres

Avant toute chose, il faut que le projet référence la librairie swingx.

1. Le Modèle et ses données

Les données du modèle :

/**
 * Item value. 
 * @author pseudo555
 */
public class Item {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Le modèle :

import java.util.ArrayList ;
import java.util.List ;
import javax.swing.table.AbstractTableModel ;

/**
 * Model for the JXTable * @author pseudo555
 */
public class Model extends AbstractTableModel {

    private static final long serialVersionUID = 1L;
    // cache values 	
    public List items = new ArrayList();
    // Array containing column name 	
    private final String[] columnsName = new String[1];
    // Index of each column 	
    public static final int I_NAME = 0;

    public Model() {
        // fill columnsName 		
        this.columnsName[I_NAME] = "Name";
    }

    @Override
    public String getColumnName(final int index) {
        return this.columnsName[index];
    }

    @Override
    public int getColumnCount() {
        return this.columnsName.length;
    }

    /**
     * Retrieve a specific row from model data 
     * @param rowIndex index of the row to retrieve * @return Item
     */
    public Item getItem(final int rowIndex) {
        try {
            return items.get(rowIndex);
        } catch (final ArrayIndexOutOfBoundsException e) {
            return null;
        }
    }

    @Override
    public Object getValueAt(final int rowIndex, final int columnIndex) {
        final Item item = this.getItem(rowIndex);
        if (item == null) {
            return null;
        }
        // according to the column, retrieve the required value	 		
        switch (columnIndex) {
            case I_NAME:
                return item.getName();
            default:
                return "";
        }
    }

    @Override
    public Class getColumnClass(final int columnIndex) {
        // Pour chaque colonne, on définit l'objet utilisé.  		
        // On appliquera un renderer spécial dessus si nécessaire. 		
        switch (columnIndex) {
            case I_NAME:
            default:
                return String.class;
        }
    }

    @Override
    public boolean isCellEditable(final int rowIndex, final int columnIndex) {
        // Définition des colonnes éditables 		
        switch (columnIndex) {
            case I_NAME:
                return true;
            default:
                return false;
        }
    }

    @Override
    public void setValueAt(final Object value, final int rowIndex, final int columnIndex) {
        // only if isCellEditable return true 		
        switch (columnIndex) {
            case I_NAME:
                if (value instanceof String) {
                    items.get(rowIndex).setName((String) value);
                }
                break;
            default:
                break;
        }
    }

    @Override
    public int getRowCount() {
        return items.size();
    }

    /**
     * Add new emty item in model.
     */
    public void addNewItem() {
        items.add(new Item());
        fireTableDataChanged();
    }
}

2. Le panel

import java.awt.BorderLayout ;
import java.awt.GridBagConstraints ;
import java.awt.GridBagLayout ;
import java.awt.Insets ;
import java.awt.event.ActionEvent ;
import java.awt.event.ActionListener ;
import javax.swing.JButton ;
import javax.swing.JLabel ;
import javax.swing.JPanel ;
import javax.swing.JScrollPane ;
import javax.swing.JTextField ;
import javax.swing.RowSorter ;
import javax.swing.event.DocumentEvent ;
import javax.swing.event.DocumentListener ;
import javax.swing.table.TableModel ;
import org.jdesktop.swingx.JXTable ;
import org.jdesktop.swingx.sort.TableSortController ;

/**
 * Main panel, which is composed by a search panel and a filtered table.
 * @author pseudo555
 */
public class Panel extends JPanel {

    private static final long serialVersionUID = 2456293673128172821L;
    // searchPanel definition 	
    private final JPanel searchPanel;
    private final JTextField textField;
    // tablePanel definition 	
    private final JPanel tablePanel;
    private final JXTable table;
    private final Model model;

    /**
     * Create the panel.
     */
    public Panel() {
        model = new Model();
        /* layout management */ {
            GridBagLayout gridBagLayout = new GridBagLayout();
            gridBagLayout.columnWidths = new int[]{0};
            gridBagLayout.rowHeights = new int[]{40, 0};
            gridBagLayout.columnWeights = new double[]{Double.MIN_VALUE};
            gridBagLayout.rowWeights = new double[]{0.0, Double.MIN_VALUE};
            setLayout(gridBagLayout);
        }
        /* add search panel */ {
            //init required component 			
            textField = new JTextField();
            // get panel 			
            searchPanel = getSearchPanel();
            //set layout constraint 			
            GridBagConstraints gbc_panel_1 = new GridBagConstraints();
            gbc_panel_1.insets = new Insets(0, 0, 5, 0);
            gbc_panel_1.fill = GridBagConstraints.BOTH;
            gbc_panel_1.gridx = 0;
            gbc_panel_1.gridy = 0;
            add(searchPanel, gbc_panel_1);
        }
        /* add table panel */ {
            //init required component 			
            table = new JXTable();
            // get panel 			
            tablePanel = getTablePanel();
            //set layout constraint 		
            GridBagConstraints gbc_panel = new GridBagConstraints();
            gbc_panel.fill = GridBagConstraints.BOTH;
            gbc_panel.gridx = 0;
            gbc_panel.gridy = 1;
            add(tablePanel, gbc_panel);
        }
    }

    /**
     * * @return Table panel.
     */
    private JPanel getTablePanel() {
        JPanel tablePanel = new JPanel();
        // layout 		
        tablePanel.setLayout(new BorderLayout());
        //content 		
        JScrollPane scrollPane = new JScrollPane();
        tablePanel.add(scrollPane, BorderLayout.CENTER);
        table.setModel(model);
        scrollPane.setViewportView(table);
        return tablePanel;
    }

    /**
     * * @return Search panel.
     */
    private JPanel getSearchPanel() {
        JPanel search = new JPanel();
        /* search panel Layout */
        {
            GridBagLayout gridBagLayout = new GridBagLayout();
            gridBagLayout.columnWidths = new int[]{50, 250, 30, 20};
            gridBagLayout.rowHeights = new int[]{20};
            gridBagLayout.columnWeights = new double[]{0.0, Double.MIN_VALUE, 0.0, 0.0};
            gridBagLayout.rowWeights = new double[]{1.0};
            search.setLayout(gridBagLayout);
        }
        /* First a search label */ {
            JLabel lblNewLabel = new JLabel("Search:");
            //set layout constraint 			
            GridBagConstraints gbc_lblNewLabel = new GridBagConstraints();
            gbc_lblNewLabel.insets = new Insets(5, 5, 5, 5);
            gbc_lblNewLabel.anchor = GridBagConstraints.EAST;
            gbc_lblNewLabel.gridx = 0;
            gbc_lblNewLabel.gridy = 0;
            search.add(lblNewLabel, gbc_lblNewLabel);
        }
        /* Then a textfield to manage user input */ {
            //set layout constraint 			
            GridBagConstraints gbc_textField = new GridBagConstraints();
            gbc_textField.insets = new Insets(5, 5, 5, 5);
            gbc_textField.fill = GridBagConstraints.BOTH;
            gbc_textField.gridx = 1;
            gbc_textField.gridy = 0;
            search.add(textField, gbc_textField);
            // set automatic filtering on user typing 			
            textField.getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void removeUpdate(DocumentEvent e) {
                    filter();
                }

                @Override
                public void insertUpdate(DocumentEvent e) {
                    filter();
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    filter();
                }
            });
        }
        /* A button which will be used to launch the search */ {
            JButton btnFilter = new JButton("Filter");
            //set layout constraint 			
            GridBagConstraints gbc_btnNewButton = new GridBagConstraints();
            gbc_btnNewButton.insets = new Insets(5, 5, 5, 5);
            gbc_btnNewButton.gridx = 2;
            gbc_btnNewButton.gridy = 0;
            search.add(btnFilter, gbc_btnNewButton);
            // add action on click 			
            btnFilter.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    filter();
                }
            });
        }
        /* A button to add data in the table */ {
            JButton btnNew = new JButton("+");
            //set layout constraint 			
            GridBagConstraints gbc_btnNewButton_1 = new GridBagConstraints();
            gbc_btnNewButton_1.insets = new Insets(5, 5, 5, 5);
            gbc_btnNewButton_1.gridx = 3;
            gbc_btnNewButton_1.gridy = 0;
            search.add(btnNew, gbc_btnNewButton_1);
            // add action on click 			
            btnNew.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    model.addNewItem();
                }
            });
        }
        return search;
    }

    /**
     * * Filter table according to textField.getText().
     */
    private void filter() {
        RowSorter s = table.getRowSorter();
        if (s instanceof TableSortController) {
            @SuppressWarnings("unchecked")
            final TableSortController sorter = (TableSortController) s;
            sorter.setRowFilter(new TextCaseInsensitiveFilter(textField.getText(), Model.I_NAME));
            table.setRowSorter(sorter);
        }
    }
}

3. Les filtres

La classe TextCaseInsensitiveFilter est une classe permettant de filter les données de la table. Voici quelques exemples :

BooleanFilter :

import javax.swing.RowFilter ;

/**
 * Boolean filter for table.
 */
public class BooleanFilter extends RowFilter {

    private final int index;
    private final boolean state;

    /**
     * Constructor. 
     * @param state Boolean state of item to display
     * @param columns Index of column to check
     */
    public BooleanFilter(final boolean state, final int columns) {
        index = columns;
        this.state = state;
    }

    @Override
    public boolean include(final Entry value) {
        final Object tr = value.getValue(index);
        if (!(tr instanceof Boolean)) {
            return false;
        }
        return state == (Boolean) tr;
    }
}

IgnoreCaseFilter:

import javax.swing.RowFilter ;

/**
 * Unsensitive text filter for table.
 */
public class IgnoreCaseFilter extends RowFilter {

    private final String matcher;
    private final int index;
    private final boolean state;

    /**
     * Constructor. 
     * @param lookFor String to look for 
     * @param state state of item to display 
     * @param columns Index of column to check
     */
    public IgnoreCaseFilter(final String lookFor, final int columns, final boolean state) {
        if (lookFor == null) {
            throw new IllegalArgumentException("LookFor must be non-null");
        }
        matcher = FilterToolkit.unaccent(lookFor.toUpperCase());
        index = columns;
        this.state = state;
    }

    @Override
    public boolean include(final Entry value) {
        final String text = FilterToolkit.unaccent(value.getStringValue(index).toUpperCase());
        final boolean result = -1 == text.indexOf(matcher);
        if (state) {
            return result;
        }
        return !result;
    }
}

RegexFilter:

/**
 * RegexFilter for table.
 */
public class RegexFilter extends RowFilter {

    private final Pattern pattern;
    private final int index;

    /**
     * Constructor. 
     * @param lookFor String to look for 
     * @param commentRegexChar Escape regex char 
     * @param columns Index of column to check
     */
    public RegexFilter(final String lookFor, final int columns, final boolean commentRegexChar) {
        if (lookFor == null) {
            throw new IllegalArgumentException("LookFor must be non-null");
        }
        pattern = Pattern.compile(commentRegexChar ? fixPatern(lookFor) : lookFor.replace("*", ".*"));
        index = columns;
    }

    @Override
    public boolean include(final Entry value) {
        final String text = FilterToolkit.unaccent(value.getStringValue(index).toUpperCase());
        return pattern.matcher(text).find();
    }

    /**
     * Comment all char that are used in regex, but not in our case.
     * @param lookFor pattern to fix 
     * @return fixed pattern
     */
    private String fixPatern(final String lookFor) {
        return FilterToolkit.escapeRegex(FilterToolkit.unaccent(lookFor.toUpperCase()));
    }
}

MinMaxFilter :

import javax.swing.RowFilter ;

/**
 * Filter on number using range.
 */
public class MinMaxFilter extends RowFilter {

    private final int index;
    private final int start;
    private final int stop;

    /**
     * Constructor. 
     * @param start Min value allowed 
     * @param stop Max value allowed 
     * @param columns Index of column to check
     */
    public MinMaxFilter(final int start, final int stop, final int columns) {
        index = columns;
        this.start = start;
        this.stop = stop;
    }

    @Override
    public boolean include(final Entry value) {
        final Object tr = value.getValue(index);
        if (tr instanceof Integer) {
            final int val = ((Integer) tr).intValue();
            return start <= val && val = stop;
        }
        return false;
    }
}

FilterToolkit :

import java.text.Normalizer ;

/**
 * Toolkit for Filter class.
 */
public final class FilterToolkit {

    /**
     * Private constructor.
     */
    private FilterToolkit() {
    }

    /**
     * Remove all accents from the given string. 
     * @param text String to unaccent 
     * @return fixed input
     */
    public static String unaccent(final String text) {
        final String normalized = Normalizer.normalize(text, Normalizer.Form.NFD);
        return normalized.replaceAll("[^\p{ASCII}]", "");
    }

    /**
     * * Escape Regex special characters from a String. * @param text
     * String to escape * @return fixed input
     */
    public static String escapeRegex(final String text) {
        final StringBuffer sb = new StringBuffer();
        final int n = text.length();
        for (int i = 0; i < n; i++) {
            final char c = text.charAt(i);
            switch (c) {
                case '(':
                    sb.append("\\(");
                    break;
                case ')':
                    sb.append("\\)");
                    break;
                case '[':
                    sb.append("\\[");
                    break;
                case ']':
                    sb.append("\\]");
                    break;
                case '^':
                    sb.append("\\^");
                    break;
                case '$':
                    sb.append("\\$");
                    break;
                case '+':
                    sb.append("\\+");
                    break;
                case '*':
                    sb.append(".*");
                    break;
                case '.':
                    sb.append("\\.");
                    break;
                case '?':
                    sb.append("\\?");
                    break;
                case '\\': 
                    sb.append("\\\\");
                    break;
                case '|':
                    sb.append("\\|");
                    break;
                case '{':
                    sb.append("\\{");
                    break;
                case '}':
                    sb.append("\\}");
                    break;
                default:
                    sb.append(c);
                    break;
            }
        }
        return sb.toString();
    }
}