/*
* codjo.net
*
* Common Apache License 2.0
*/
package net.codjo.utils;
import javax.swing.event.TableModelEvent;
import javax.swing.table.TableModel;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;
import java.util.List;
/**
* Model pour filtrer un tableau.
*
* @author $Author: blazart $
* @version $Revision: 1.3 $
*
*/
public class TableFilter extends TableMap {
private int[] indexes;
private int rowCount = 0;
private Object[][] filters;
private transient PropertyChangeSupport propertyChangeListeners =
new PropertyChangeSupport(this);
/**
* Constructor
*
* @param model Model original filtrer
*/
public TableFilter(TableModel model) {
setModel(model);
}
/**
* Positionne le model a filtrer.
*
* @param model Un nouveau Model
*/
public void setModel(TableModel model) {
super.setModel(model);
reallocateInternalData();
}
/**
* Positionne un filtre sur une colonne.
*
* @param column La colonne filtree
* @param value La valeur du filtre (null = pas de filtre)
*/
public void setFilter(int column, Object value) {
Object oldValue = filters[column];
filters[column] = new Object[] {value};
applyFilter();
propertyChangeListeners.firePropertyChange(Integer.toString(column), oldValue,
filters[column]);
}
/**
* DOCUMENT ME!
*
* @param aValue
* @param aRow
* @param aColumn
*
* @throws IllegalArgumentException TODO
*/
public void setValueAt(Object aValue, int aRow, int aColumn) {
checkModel();
if (aRow > rowCount) {
throw new IllegalArgumentException();
}
model.setValueAt(aValue, indexes[aRow], aColumn);
applyFilter();
}
/**
* Retourne les valeurs de tous les filtres de la colonne.
*
* @param column La colonne
*
* @return Tableau de filtre (ou null)
*/
public Object[] getFilterValueList(int column) {
return filters[column];
}
/**
* Indique si la colonne est filtr�e.
*
* @param column La colonne
*
* @return 'true' si la colonne est filtr�e
*/
public boolean hasFilter(int column) {
return filters[column] != null;
}
/**
* Retourne la premiere valeur du filtre de la colonne.
*
* @param column La colonne
*
* @return Le premier filtre (ou null)
*/
public Object getFilterValue(int column) {
Object[] columnFilter = getFilterValueList(column);
if (columnFilter == null || columnFilter.length == 0) {
return null;
}
return columnFilter[0];
}
/**
* DOCUMENT ME!
*
* @param aRow
* @param aColumn
*
* @return
*/
public Object getValueAt(int aRow, int aColumn) {
checkModel();
if (aRow > rowCount) {
return null;
}
else {
return model.getValueAt(indexes[aRow], aColumn);
}
}
/**
* DOCUMENT ME!
*
* @return The RowCount value
*/
public int getRowCount() {
return rowCount;
}
/**
* Adds a feature to the Filter attribute of the TableFilter object
*
* @param column The feature to be added to the Filter attribute
* @param value The feature to be added to the Filter attribute
*/
public void addFilter(int column, Object value) {
Object oldValue = filters[column];
Object[] a = (filters[column] != null) ? filters[column] : new Object[0];
List columnFilters = new java.util.ArrayList(Arrays.asList(a));
columnFilters.add(value);
filters[column] = columnFilters.toArray();
applyFilter();
propertyChangeListeners.firePropertyChange(Integer.toString(column), oldValue,
filters[column]);
}
/**
* Enleve le filtre de la colonne.
*
* @param column La colonne
*/
public void clearFilter(int column) {
Object oldValue = filters[column];
filters[column] = null;
applyFilter();
propertyChangeListeners.firePropertyChange(Integer.toString(column), oldValue,
filters[column]);
}
/**
* Overview.
*
* <p>
* Description
* </p>
*/
public void clearAllColumnFilter() {
for (int i = 0; i < getColumnCount(); i++) {
clearFilter(i);
}
}
/**
* Overview.
*
* <p>
* Description
* </p>
*
* @param column Description of Parameter
* @param value Description of Parameter
*/
public void removeFilter(int column, Object value) {
Object[] oldValue = filters[column];
if (oldValue == null) {
return;
}
List columnFilters = new java.util.ArrayList(Arrays.asList(oldValue));
if (columnFilters.contains(value)) {
if (columnFilters.size() == 1) {
clearFilter(column);
}
else {
columnFilters.remove(value);
filters[column] = columnFilters.toArray();
applyFilter();
propertyChangeListeners.firePropertyChange(Integer.toString(column),
oldValue, filters[column]);
}
}
else {
// pas de filtre value dans la column !
}
}
/**
* Overview.
*
* <p>
* Description
* </p>
*
* @param column Description of Parameter
* @param value Description of Parameter
*
* @return Description of the Returned Value
*/
public boolean containsFilterValue(int column, Object value) {
Object[] columnFilters = filters[column];
if (columnFilters == null) {
return false;
}
for (int i = 0; i < filters[column].length; i++) {
if (filters[column][i] == value) {
return true;
}
else if (filters[column][i] != null && filters[column][i].equals(value)) {
return true;
}
}
return false;
}
/**
* Modification du model sous jacent.
*
* @param evt
*/
public void tableChanged(TableModelEvent evt) {
reallocateInternalData();
applyFilter();
}
/**
* DOCUMENT ME!
*
* @param column Description of Parameter
* @param l
*/
public synchronized void removePropertyChangeListener(int column,
PropertyChangeListener l) {
propertyChangeListeners.removePropertyChangeListener(Integer.toString(column), l);
}
/**
* Ajoute un PropertyChangeListener pour les Filtres
*
* @param column
* @param l
*/
public synchronized void addPropertyChangeListener(int column,
PropertyChangeListener l) {
propertyChangeListeners.addPropertyChangeListener(Integer.toString(column), l);
}
/**
* Indique si la ligne verifie les filtres.
*
* @param aRow La ligne du model a verifier.
*
* @return <code>true</code> la ligne respecte les filtre (sera affichee)
*/
private boolean isDisplayed(int aRow) {
boolean isDisplayed = true;
for (int ci = 0; ci < getColumnCount(); ci++) {
if (checkColumnFilter(ci, getModel().getValueAt(aRow, ci)) == false) {
return false;
}
}
return true;
}
/**
* Verifie que la valeur "value" correspond au moins a un des filtres.
*
* @param column La colonne
* @param value La valeur
*
* @return 'true' si la valeur est contenu dans les filtres, 'false' sinon.
*/
private boolean checkColumnFilter(int column, Object value) {
if (filters[column] == null) {
return true;
}
return containsFilterValue(column, value);
}
/**
* Applique les filtre. Cette methode lance un evenenment de modification.
*/
private void applyFilter() {
rowCount = 0;
for (int i = 0; i < getModel().getRowCount(); i++) {
if (isDisplayed(i)) {
indexes[rowCount] = i;
rowCount++;
}
}
super.tableChanged(new TableModelEvent(this));
}
/**
* Reallocation des donnees interne.
*/
private void reallocateInternalData() {
Object[][] oldFilters = filters;
filters = new Object[getModel().getColumnCount()][];
if (oldFilters != null) {
for (int i = 0; i < oldFilters.length && i < filters.length; i++) {
filters[i] = oldFilters[i];
}
}
rowCount = getModel().getRowCount();
indexes = new int[rowCount];
for (int row = 0; row < rowCount; row++) {
indexes[row] = row;
}
checkModel();
}
/**
* Verification d'un changement.
*
* @todo a virer ???
*/
private void checkModel() {
if (indexes.length != model.getRowCount()) {
System.err.println("Filter not informed of a change in model.");
}
}
}