![JAR search and dependency download from the Maven repository](/logo.png)
net.sf.cuf.model.ui.TableSelectionModel Maven / Gradle / Ivy
The newest version!
package net.sf.cuf.model.ui;
import net.sf.cuf.model.SelectionInList;
import net.sf.cuf.ui.table.TableFilterPlugin;
import javax.swing.JTable;
import javax.swing.event.TableModelEvent;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static net.sf.cuf.ui.table.CufSorterUtil.attachCufSorterWithHeaderRenderer;
/**
* This class maps the content of List of a SelectionInList ValueModel to a
* JTable TableModel and ListSelectionModel.
* Each entry in the list describes a row, all entries are assumed to be objects
* from the same class/interface. The column names and the mapping to the attributes
* of a list entry can be configured.
* Whenever the ValueModel holding either the list or the selection changes,
* the table is adjusted accordingly.
* The table data is read-write if a setter for the attribute exists.
* When the user changes the selection in the
* table, the selection holder of the SelectionInList is changed accordingly.
* Only single-selection is supported.
* The initial selection is taken from the SelectionInList value model.
* FIXME: currently we never unregister from the SelectionInList value model,
* this might lead to memory leaks.
*/
public class TableSelectionModel extends ListTableMapperBase
{
/** Class of a entry in the value model's list, never null */
private Class mListEntryClass;
/** List of Mapping objects, never null */
private List mColumnMapping;
/** our table filter plugin, may be null */
private TableFilterPlugin mTableFilterPlugin;
/** maps primitive types to the matching non-primitve peer class */
private static final Map PRIMITVE_MAPPER;
static
{
PRIMITVE_MAPPER= new HashMap(9);
PRIMITVE_MAPPER.put(Boolean.TYPE, Boolean.class);
PRIMITVE_MAPPER.put(Character.TYPE, Character.class);
PRIMITVE_MAPPER.put(Byte.TYPE, Byte.class);
PRIMITVE_MAPPER.put(Short.TYPE, Short.class);
PRIMITVE_MAPPER.put(Integer.TYPE, Integer.class);
PRIMITVE_MAPPER.put(Long.TYPE, Long.class);
PRIMITVE_MAPPER.put(Float.TYPE, Float.class);
PRIMITVE_MAPPER.put(Double.TYPE, Double.class);
PRIMITVE_MAPPER.put(Void.TYPE, Void.class);
}
/** shared no class argument array. */
private static final Class[] NO_CLASS_ARGS= {};
/** shared no argument array. */
private static final Object[] NO_ARGS = {};
/**
* Create a new adaption object between a JTable and a SelectionInList value model.
* This object is also set as the JTable's TableModel and ListSelectionModel.
* @param pTable the table for which we provide TableModel and ListSelectionModel behaviour
* @param pValueModel the value model that drives the table (data, selection)
* and gets updated by the table (selection only)
* @param pListEntry the class of a list entry in the value model
* @param pMapping non-null List containing Mapping objects
* @throws IllegalArgumentException if a parameter is bogus
*/
public TableSelectionModel(final JTable pTable, final SelectionInList pValueModel, final Class pListEntry, final List pMapping)
{
if (pListEntry==null)
{
throw new IllegalArgumentException("the list entry class must not be null");
}
if (!Modifier.isPublic(pListEntry.getModifiers()))
throw new IllegalArgumentException("the list entry class must be public");
mListEntryClass= pListEntry;
mColumnMapping= Collections.emptyList();
init(pTable, pValueModel);
setColumnMapping(pTable, pMapping, false);
}
/**
* Create a new adaption object between a JTable and a SelectionInList value model.
* This object is also set as the JTable's TableModel and ListSelectionModel.
* @param pTable the table for which we provide TableModel and ListSelectionModel behaviour
* @param pValueModel the value model that drives the table (data, selection)
* and gets updated by the table (selection only)
* @param pListEntry the class of a list entry in the value model
* @param pMapping non-null List containing Mapping objects
* @param pSortable true if table is sortable
* @throws IllegalArgumentException if a parameter is bogus
*/
public TableSelectionModel(final JTable pTable, final SelectionInList pValueModel, final Class pListEntry, final List pMapping, final boolean pSortable)
{
if (pListEntry==null)
{
throw new IllegalArgumentException("the list entry class must not be null");
}
if (!Modifier.isPublic(pListEntry.getModifiers()))
throw new IllegalArgumentException("the list entry class must be public");
mListEntryClass= pListEntry;
mColumnMapping= Collections.emptyList();
init(pTable, pValueModel);
setColumnMapping(pTable, pMapping, pSortable);
}
/**
* Create a new adaption object between a JTable and a SelectionInList value model.
* This object is also set as the JTable's TableModel and ListSelectionModel.
* @param pTable the table for which we provide TableModel and ListSelectionModel behaviour
* @param pValueModel the value model that drives the table (data, selection)
* and gets updated by the table (selection only)
* @param pListEntry the class of a list entry in the value model
* @param pMapping non-null List containing Mapping objects
* @param pSortable true if table is sortable
* @param pTableFilterPlugin our table filter plugin, may be null
* * @throws IllegalArgumentException if a parameter is bogus
*/
public TableSelectionModel(final JTable pTable, final SelectionInList pValueModel, final Class pListEntry, final List pMapping, final boolean pSortable, final TableFilterPlugin pTableFilterPlugin)
{
if (pListEntry==null)
{
throw new IllegalArgumentException("the list entry class must not be null");
}
if (!Modifier.isPublic(pListEntry.getModifiers()))
throw new IllegalArgumentException("the list entry class must be public");
mTableFilterPlugin= pTableFilterPlugin;
mListEntryClass= pListEntry;
mColumnMapping= Collections.emptyList();
init(pTable, pValueModel);
setColumnMapping(pTable, pMapping, pSortable);
}
/*
* "our" handling code
*/
/**
* Getter for TableFilterPlugin
* @return mTableFilterPlugin
*/
public TableFilterPlugin getTableFilterPlugin()
{
return mTableFilterPlugin;
}
/**
* The method maps the attributes of our List entry class to column names
* in the table and the alignments of the columns.
* @param pTable the table for which we set the column alingnments
* @param pMapping non-null List containing Mapping objects
* @param pSortable true if table is sortable
* @throws IllegalArgumentException if pMapping is null or contains invalid mappings
*/
private void setColumnMapping(final JTable pTable, final List pMapping, final boolean pSortable)
{
if (pMapping==null)
{
throw new IllegalArgumentException("mapping must not be null");
}
// make table sortable with a CufTableRowSorter
if (pSortable)
{
attachCufSorterWithHeaderRenderer(pTable);
}
try
{
List columnMapping= new ArrayList<>(pMapping.size());
for (Mapping mapping : pMapping)
{
String attributeName = mapping.getAttributeName();
Method getter;
// try first for the get-getter
String getterName = "get" +
attributeName.substring(0, 1).toUpperCase() +
attributeName.substring(1);
try
{
getter = mListEntryClass.getMethod(getterName, NO_CLASS_ARGS);
}
catch (Exception e)
{
// not found - try the is-getter
getterName = "is" +
attributeName.substring(0, 1).toUpperCase() +
attributeName.substring(1);
try
{
getter = mListEntryClass.getMethod(getterName, NO_CLASS_ARGS);
}
catch (Exception e1)
{
// not found - try the has-getter
getterName = "has" +
attributeName.substring(0, 1).toUpperCase() +
attributeName.substring(1);
try
{
getter = mListEntryClass.getMethod(getterName, NO_CLASS_ARGS);
}
catch (Exception e2)
{
// no getter found - throw the original exception
throw new IllegalArgumentException(e.getMessage(), e);
}
}
}
// determine the column class that the model should return to the JTable
Class columnClassForModel = mapping.getColumnClass();
if (columnClassForModel == null)
{
// use the return type of the getter
columnClassForModel = getter.getReturnType();
if (columnClassForModel.isPrimitive())
{
// if the column is primitive, we must map it to the non-primitive peer,
// otherwise the table rendering doesn't find a suitable renderer
columnClassForModel = (Class) PRIMITVE_MAPPER.get(columnClassForModel);
}
}
Method setter = null;
if (mapping.isEditable())
{
try
{
String setterName = "set" +
attributeName.substring(0, 1).toUpperCase() +
attributeName.substring(1);
// here we do not use the column class that might be defined in the mapping
// (messes up with primitive types)
setter = mListEntryClass.getMethod(setterName, new Class[]{getter.getReturnType()});
}
catch (Exception e)
{
throw new IllegalArgumentException(e.getMessage(), e);
}
}
Mapping myMapping = new Mapping();
myMapping.setAttributeName(mapping.getAttributeName());
myMapping.setColumnName(mapping.getColumnName());
myMapping.setColumnAlignment(mapping.getColumnAlignment());
myMapping.setColumnClass(columnClassForModel);
myMapping.setColumnGetter(getter);
if (setter != null)
{
myMapping.setEditable(true);
myMapping.setColumnSetter(setter);
}
columnMapping.add(myMapping);
}
mColumnMapping= columnMapping;
// notify the table to re-create the columns
TableModelEvent e= new TableModelEvent(this, TableModelEvent.HEADER_ROW);
fireTableChanged(e);
// set column alignments
for (int i = 0, n = pMapping.size(); i < n; i++)
{
Mapping mapping= pMapping.get(i);
if(mapping.getColumnAlignment() != null)
{
pTable.getColumnModel().getColumn(i).setCellRenderer(
new ColumnAlignmentRenderer(
mapping.getColumnAlignment()));
}
}
}
catch (ClassCastException e)
{
throw new IllegalArgumentException(e.getMessage());
}
}
/******************************************************************/
/*
* TableModel callbacks
*/
public int getColumnCount()
{
return mColumnMapping.size();
}
public String getColumnName(final int pColumnIndex)
{
return mColumnMapping.get(pColumnIndex).getColumnName();
}
public Class getColumnClass(final int pColumnIndex)
{
return mColumnMapping.get(pColumnIndex).getColumnClass();
}
public Object getValueAt(final int pRowIndex, final int pColumnIndex)
{
List list = mSelectionInList.getValue();
Object listEntry = list.get(pRowIndex);
Mapping columnMapping= mColumnMapping.get(pColumnIndex);
Method getter = columnMapping.getColumnGetter();
Object back;
try
{
back= getter.invoke(listEntry, NO_ARGS);
}
catch (Exception e)
{
throw new RuntimeException("problems during getting value using "+getter, e);
}
return back;
}
public boolean isCellEditable(final int pRowIndex, final int pColumnIndex)
{
Mapping columnMapping= mColumnMapping.get(pColumnIndex);
return columnMapping.isEditable();
}
public void setValueAt(final Object pValue, final int pRowIndex, final int pColumnIndex)
{
List list = mSelectionInList.getValue();
Object listEntry = list.get(pRowIndex);
Mapping columnMapping= mColumnMapping.get(pColumnIndex);
Method setter = columnMapping.getColumnSetter();
try
{
setter.invoke(listEntry, pValue);
}
catch (Exception e)
{
throw new RuntimeException("problems during setting value using "+setter, e);
}
// and now notify the list of the change
mSelectionInList.signalExternalUpdate();
}
/******************************************************************/
/**
* Small helper class to describe the attribut/column mapping.
*/
public static class Mapping
{
/** name of the attribute, never null */
private String mAttributeName;
/** name of the table column, never null */
private String mColumnName;
/** alignment of the table column, may be null */
private String mColumnAlignment;
/** class of the table column, may be null */
private Class mColumnClass;
/** method to get the attribute, may be null */
private Method mColumnGetter;
/** method to set the attribute, may be null */
private Method mColumnSetter;
/** marker if the column is editable */
private boolean mIsEditable;
public Mapping()
{
mAttributeName = "";
mColumnName = "";
mColumnAlignment= null;
mColumnClass = null;
mColumnGetter = null;
mColumnSetter = null;
mIsEditable = false;
}
public Mapping(final String pAttributeName, final String pColumnName)
{
this();
setAttributeName(pAttributeName);
setColumnName (pColumnName);
}
public Mapping(final String pAttributeName, final String pColumnName, final Class pColumnClass)
{
this();
setAttributeName(pAttributeName);
setColumnName (pColumnName);
setColumnClass (pColumnClass);
}
public Mapping(final String pAttributeName, final boolean pIsEditable, final String pColumnName, final String pColumnAlignment, final Class pColumnClass)
{
this();
setAttributeName (pAttributeName);
setEditable (pIsEditable);
setColumnName (pColumnName);
setColumnAlignment (pColumnAlignment);
setColumnClass (pColumnClass);
}
public String getAttributeName()
{
return mAttributeName;
}
public void setAttributeName(final String pAttributeName)
{
if (pAttributeName==null || "".equals(pAttributeName))
{
throw new IllegalArgumentException("attribute name must not be null or empty");
}
mAttributeName = pAttributeName;
}
public boolean isEditable()
{
return mIsEditable;
}
public void setEditable(final boolean pEditable)
{
mIsEditable = pEditable;
}
public String getColumnName()
{
return mColumnName;
}
public void setColumnName(final String pColumnName)
{
if (pColumnName==null)
{
throw new IllegalArgumentException("column name must not be null");
}
mColumnName = pColumnName;
}
public String getColumnAlignment()
{
return mColumnAlignment;
}
public void setColumnAlignment(final String pColumnAlignment)
{
mColumnAlignment = pColumnAlignment;
}
private Class getColumnClass()
{
return mColumnClass;
}
private void setColumnClass(final Class pColumnClass)
{
mColumnClass = pColumnClass;
}
private Method getColumnGetter()
{
return mColumnGetter;
}
private void setColumnGetter(final Method pColumnGetter)
{
mColumnGetter = pColumnGetter;
}
public Method getColumnSetter()
{
return mColumnSetter;
}
public void setColumnSetter(final Method pColumnSetter)
{
mColumnSetter = pColumnSetter;
}
public boolean equals(final Object pObject)
{
if (this == pObject) return true;
if (pObject == null || getClass() != pObject.getClass()) return false;
final Mapping mapping = (Mapping) pObject;
if (mIsEditable != mapping.mIsEditable) return false;
if (mAttributeName != null ? !mAttributeName.equals(mapping.mAttributeName) : mapping.mAttributeName != null)
return false;
if (mColumnAlignment != null ? !mColumnAlignment.equals(mapping.mColumnAlignment) : mapping.mColumnAlignment != null)
return false;
if (mColumnClass != null ? !mColumnClass.equals(mapping.mColumnClass) : mapping.mColumnClass != null)
return false;
if (mColumnGetter != null ? !mColumnGetter.equals(mapping.mColumnGetter) : mapping.mColumnGetter != null)
return false;
if (mColumnName != null ? !mColumnName.equals(mapping.mColumnName) : mapping.mColumnName != null)
return false;
//noinspection RedundantIfStatement
if (mColumnSetter != null ? !mColumnSetter.equals(mapping.mColumnSetter) : mapping.mColumnSetter != null)
return false;
return true;
}
public int hashCode()
{
int result;
result = (mAttributeName != null ? mAttributeName.hashCode() : 0);
result = 29 * result + (mColumnName != null ? mColumnName.hashCode() : 0);
result = 29 * result + (mColumnAlignment != null ? mColumnAlignment.hashCode() : 0);
result = 29 * result + (mColumnClass != null ? mColumnClass.hashCode() : 0);
result = 29 * result + (mColumnGetter != null ? mColumnGetter.hashCode() : 0);
result = 29 * result + (mColumnSetter != null ? mColumnSetter.hashCode() : 0);
result = 29 * result + (mIsEditable ? 1 : 0);
return result;
}
public String toString()
{
return "Mapping{" +
"AttributeName='" + mAttributeName + '\'' +
", ColumnName='" + mColumnName + '\'' +
", ColumnAlignment='" + mColumnAlignment + '\'' +
", ColumnClass=" + mColumnClass +
", ColumnGetter=" + mColumnGetter +
", ColumnSetter=" + mColumnSetter +
", IsEditable=" + mIsEditable +
'}';
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy