package com.towel.swing.table.adapter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.Map.Entry;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
/**
* A TableModel that bases in a list of objects and displays them according to a
* list of columns. Several useful methods such as add, addAll, remove,
* removeAll are already supplied, all of them firing the appropriate events to
* the table.
*
* @param <T> The class that the tables works with.
*
* @author Vinicius Godoy
*/
public class ColumnTableModel<T> extends AbstractTableModel
{
@SuppressWarnings("unchecked")
public static <K, V> ColumnTableModel<Map.Entry<K, V>> createMapModel(
Map<K, V> map, String keyHeader, String valueHeader)
{
Column<Map.Entry<K, V>> keyColumn = new AbstractColumn<Map.Entry<K, V>>(
keyHeader, 1)
{
public Object getValue(Entry<K, V> element)
{
return element.getKey();
}
};
Column<Map.Entry<K, V>> valueColumn = new AbstractColumn<Map.Entry<K, V>>(
valueHeader, 1)
{
public Object getValue(Entry<K, V> element)
{
return element.getValue();
}
};
return new ColumnTableModel<Map.Entry<K, V>>(map.entrySet(), keyColumn,
valueColumn);
}
/**
* Apply the column information to the table. New table columns will be
* created and added, with the properties specified in the columns list.
* <p>
* This method ignore the modelIndex() property of the column, and replace
* it by the list order itself.
*
* @param table The table to update.
* @param columns Columns to be created.
*/
public static void applyToTable(JTable table,
List< ? extends Column< ? >> columns)
{
int i = 0;
for (Column<?> c : columns)
{
TableColumn col = new TableColumn(i++, c.getWidth());
col.setHeaderValue(c.getName());
if (c.getRenderer() != null)
col.setCellRenderer(c.getRenderer());
if (c instanceof EditableColumn)
{
EditableColumn<?> ec = (EditableColumn<?>) c;
if (ec.getEditor() != null)
col.setCellEditor(ec.getEditor());
}
table.addColumn(col);
}
}
/**
* Apply the column information to the table. New table columns will be
* created and added, with the properties specified in the columns list.
* <p>
* This method ignore the modelIndex() property of the column, and replace
* it by the list order itself.
*
* @param table The table to update.
* @param columns Columns to be created.
*/
public static void applyToTable(JTable table, Column< ? >... columns)
{
applyToTable(table, Arrays.asList(columns));
}
private List<Column<T>> columns;
private List<T> values;
private boolean isReadOnly = false;
/**
* Create a new column table model.
*
* @param values Values in the model.
* @param columns Columns of the model.
*/
public ColumnTableModel(Collection<T> values,
List< ? extends Column<T>> columns)
{
if (columns == null)
throw new IllegalArgumentException("Columns cannot be null!");
if (columns.size() == 0)
throw new IllegalArgumentException(
"You must provide at least one column!");
if (values == null)
throw new IllegalArgumentException("Values can't be null!");
this.columns = new ArrayList<Column<T>>(columns);
this.values = new ArrayList<T>(values);
}
/**
* Create a new column table model.
*
* @param values Values in the model.
* @param columns Columns of the model.
*/
public ColumnTableModel(Collection<T> values, Column<T>... columns)
{
this(values, Arrays.asList(columns));
}
public ColumnTableModel(Column<T>... columns)
{
this(new ArrayList<T>(), Arrays.asList(columns));
}
public int getColumnCount()
{
return columns.size();
}
public int getRowCount()
{
return values.size();
}
public Object getValueAt(int rowIndex, int columnIndex)
{
return columns.get(columnIndex).getValue(values.get(rowIndex));
}
@Override
public Class< ? > getColumnClass(int columnIndex)
{
return columns.get(columnIndex).getColumnClass();
}
@Override
public String getColumnName(int column)
{
return columns.get(column).getName();
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return !isReadOnly
&& columns.get(columnIndex) instanceof EditableColumn
&& ((EditableColumn<T>) columns.get(columnIndex)).isEditable(values.get(rowIndex));
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
if (aValue == getValueAt(rowIndex, columnIndex))
return;
((EditableColumn<T>) (columns.get(columnIndex))).setValue(
values.get(rowIndex), aValue);
fireTableRowsUpdated(rowIndex, rowIndex);
}
public List<T> getValues()
{
return Collections.unmodifiableList(values);
}
public List<Column<T>> getColumns()
{
return Collections.unmodifiableList(columns);
}
/**
* Clears all data in the model.
*/
public void clear()
{
values.clear();
fireTableDataChanged();
}
/**
* Adds a new element to the model.
*
* @param The element to add.
*/
public void add(T element)
{
values.add(element);
fireTableRowsInserted(values.size() - 1, values.size() - 1);
}
/**
* Adds all the given elements to the model.
*
* @param elements Elements to add.
*/
public void addAll(T... elements)
{
addAll(Arrays.asList(elements));
}
/**
* Adds all the given elements to the model.
*
* @param elements Elements to add.
*/
public void addAll(Collection< ? extends T> elements)
{
for (T t : elements)
add(t);
}
/**
* Remove the element from the model.
*
* @param element The element to remove
* @return True if the model was changed, false if not.
*/
public boolean remove(T element)
{
int index = values.indexOf(element);
if (index == -1)
return false;
remove(index);
return true;
}
/**
* Remove the element from the given model row.
*
* @param element The row of the element to remove
* @return The removed element.
* @throws IndexOutOfBoundException If the row is outside the model
* boundaries.
*/
public T remove(int row)
{
T value = values.remove(row);
fireTableRowsDeleted(row, row);
return value;
}
/**
* Remove all the indices from the model. There's no need to sort the
* indices vector prior to calling this method.
*
* @param indices Indices to remove.
* @return All removed elements.
* @throws IndexOutOfBoundsException If any index is out of the table
* boundaries.
*/
public List<T> removeAll(int... indices)
{
for (int index : indices)
if (index < 0 || index > values.size())
throw new IndexOutOfBoundsException("Index " + index
+ " out of bounds");
SortedSet<Integer> indexes = new TreeSet<Integer>(
new Comparator<Integer>()
{
public int compare(Integer o1, Integer o2)
{
return o2.compareTo(o1);
}
});
for (int index : indices)
indexes.add(index);
List<T> elements = new ArrayList<T>();
for (Integer index : indexes)
elements.add(0, remove(index));
return elements;
}
/**
* @return the number of rows in this table model.
*/
public int getSize()
{
return values.size();
}
/**
* Indicate if data modifications are allowed for this model.
*
* @return True if the model is read-only, false if not.
*/
public boolean isReadOnly()
{
return isReadOnly;
}
/**
* Make this table model read-only.
*
* @param isReadOnly True to make the model read-only, false to allow
* editing.
*/
public void setReadOnly(boolean isReadOnly)
{
this.isReadOnly = isReadOnly;
}
/**
* @param user
* @return
*/
public int indexOf(T element)
{
return values.indexOf(element);
}
public T get(int row)
{
return values.get(row);
}
/**
* @param row
* @param testPlan
*/
public void replace(int row, T element)
{
values.set(row, element);
fireTableRowsUpdated(row, row);
}
public void replaceAll(List< ? extends T> elements)
{
clear();
addAll(elements);
}
public boolean contains(T element)
{
return values.contains(element);
}
public void fireChanged(T element)
{
int indexOf = values.indexOf(element);
if (indexOf == -1)
return;
fireTableRowsUpdated(indexOf, indexOf);
}
public void add(int index, T element)
{
values.add(index, element);
fireTableRowsInserted(index, index);
}
}