All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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