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

net.sf.cuf.model.ui.ListTableMapper Maven / Gradle / Ivy

The newest version!
package net.sf.cuf.model.ui;

import net.sf.cuf.model.AspectAdapter;
import net.sf.cuf.model.DelegateAccess;
import net.sf.cuf.model.RowColumnChangeEvent;
import net.sf.cuf.model.SelectionInList;
import net.sf.cuf.model.ValueHolder;
import net.sf.cuf.ui.builder.SwingXMLBuilder;
import net.sf.cuf.ui.table.TableFilterPlugin;

import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.TableModelEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import static net.sf.cuf.model.RowColumnChangeEvent.UNKNOWN_ROW_OR_COLUMN;
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 of the SelectionInList ValueModel describes a * row, all entries are assumed to be objects from the same class/interface. * Whenever the either the list or the selection changes, the table is * adjusted accordingly. * The table data is read-only, when the user changes the selection in the * table, the selection holder of the SelectionInList is changed accordingly. * Only single-selection is therefore 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 ListTableMapper extends ListTableMapperBase { /** List of Mapping objects, contains a DelegateAccess object * for each column, never null */ private List mColumnMapping; /** our table filter plugin */ private TableFilterPlugin mTableFilterPlugin; /** shared empty array */ private static final Class[] NO_CLASSES = {}; /** shared empty array */ private static final Object[] NO_OBJECTS = {}; /** * 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 pMapping non-null List containing Mapping objects * @param pSortable true if table is sortable * @throws IllegalArgumentException if a parameter is bogus */ public ListTableMapper(final JTable pTable, final SelectionInList pValueModel, final List pMapping, final boolean pSortable) { this(pTable, pValueModel, pMapping, pSortable, -1); } /** * 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 pMapping non-null List containing Mapping objects * @param pSortable true if table is sortable * @param pColumnForInitialSorting -1 or the column index for initial sorting * @throws IllegalArgumentException if a parameter is bogus */ public ListTableMapper(final JTable pTable, final SelectionInList pValueModel, final List pMapping, final boolean pSortable, final int pColumnForInitialSorting) { this(pTable,pValueModel,pMapping,pSortable,pColumnForInitialSorting,null); } /** * 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 pMapping non-null List containing Mapping objects * @param pSortable true if table is sortable * @param pColumnForInitialSorting -1 or the column index for initial sorting * @param pTableFilterPlugin our table filter plugin, may be null * @throws IllegalArgumentException if a parameter is bogus */ public ListTableMapper(final JTable pTable, final SelectionInList pValueModel, final List pMapping, final boolean pSortable, final int pColumnForInitialSorting, final TableFilterPlugin pTableFilterPlugin) { mColumnMapping= Collections.emptyList(); init(pTable, pValueModel); setColumnMapping(pTable, pMapping, pSortable, pColumnForInitialSorting); mTableFilterPlugin = pTableFilterPlugin; } /* * "our" handling code (=no Swing or ValueModel callback/handling stuff) */ public Object getValueForSortingAt(final int pRowIndex, final int pColumnIndex) { Mapping mapping = mColumnMapping.get(pColumnIndex); DelegateAccess valueModelForSorting= mapping.getValueModelForSorting(); List list = mSelectionInList.getValue(); Object value = list.get(pRowIndex); return valueModelForSorting.getValue(value); } public boolean isColumnSortable(final int pColumnIndex) { Mapping mapping= mColumnMapping.get(pColumnIndex); return mapping.isSortable(); } /** * 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 * @param pColumnForInitialSorting -1 or the column index for initial sorting * @throws IllegalArgumentException if pMapping is null or contains invalid mappings */ private void setColumnMapping(final JTable pTable, final List pMapping, final boolean pSortable, final int pColumnForInitialSorting) { if (pMapping==null) { throw new IllegalArgumentException("mapping must not be null"); } List columnMapping= new ArrayList<>(pMapping.size()); for (Mapping mapping : pMapping) { Mapping myMapping= new Mapping(mapping); columnMapping.add(myMapping); } mColumnMapping= columnMapping; // notify the table to re-create the columns TableModelEvent e= new TableModelEvent(this, TableModelEvent.HEADER_ROW); fireTableChanged(e); // make table sortable with a CufTableRowSorter if (pSortable) { // set sorter and header renderer CufTableRowSorter sorter = attachCufSorterWithHeaderRenderer(pTable); // add Comparator for sorting, if necessary for (int i = 0, n = pMapping.size(); i < n; i++) { Mapping mapping = pMapping.get(i); if(mapping.getComparatorClass() != null) { try { Comparator myComparator = (Comparator) mapping .getComparatorClass() .getConstructor(NO_CLASSES) .newInstance(NO_OBJECTS); sorter.setColumnComparator(i, myComparator); } catch (Exception e1) { throw SwingXMLBuilder.createException("could not instantiate ComparatorClass " + mapping.getComparatorClass() + " for " + mapping.getColumnClass(), e1); } } } // force initial sorting if (pColumnForInitialSorting >= 0 && pColumnForInitialSorting < pTable.getModel().getColumnCount()) { sorter.forceInitialSorting(pColumnForInitialSorting); } } for (int i = 0, n = pMapping.size(); i < n; i++) { Mapping mapping= pMapping.get(i); // set defined column alignment if(mapping.getColumnAlignment() != null) { pTable.getColumnModel().getColumn(i).setCellRenderer( new ColumnAlignmentRenderer(mapping.getColumnAlignment())); } else { // if no alignment defined: set default-alignment per column-class if(mapping.getColumnClass() != null && Boolean.class.isAssignableFrom(mapping.getColumnClass())) { pTable.getColumnModel().getColumn(i).setCellRenderer( new ColumnAlignmentRenderer(ColumnAlignmentRenderer.ALIGN_CENTER)); } else if(mapping.getColumnClass() != null && (Number.class.isAssignableFrom(mapping.getColumnClass()) || Date.class.isAssignableFrom(mapping.getColumnClass()))) { pTable.getColumnModel().getColumn(i).setCellRenderer( new ColumnAlignmentRenderer(ColumnAlignmentRenderer.ALIGN_RIGHT)); } else { pTable.getColumnModel().getColumn(i).setCellRenderer( new ColumnAlignmentRenderer(ColumnAlignmentRenderer.ALIGN_LEFT)); } } // set prefered width for table columns if(mapping.getColumnPrefWidthIntValue() > 0) { // if we don't disable the auto resize, preferred with has no consequences pTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); JTextField dummyTextField = new JTextField(mapping.getColumnPrefWidthIntValue()); pTable.getColumnModel().getColumn(i).setPreferredWidth(dummyTextField.getPreferredSize().width); } } } /******************************************************************/ /* * TableModel callbacks */ public int getColumnCount() { return mColumnMapping.size(); } public String getColumnName(final int pColumnIndex) { return mColumnMapping.get(pColumnIndex).getColumnTitle(); } public Class getColumnClass(final int pColumnIndex) { Class clazz= mColumnMapping.get(pColumnIndex).getColumnClass(); if (clazz==null) clazz= Object.class; return clazz; } public Object getValueAt(final int pRowIndex, final int pColumnIndex) { Mapping mapping = mColumnMapping.get(pColumnIndex); DelegateAccess valueModel= mapping.getValueModel(); List list = mSelectionInList.getValue(); Object value = list.get(pRowIndex); return valueModel.getValue(value); } /** * Checks if the cell is editable * @param pRowIndex the row index * @param pColumnIndex the column index * @return the value */ @Override public boolean isCellEditable(final int pRowIndex, final int pColumnIndex) { final ListTableMapper.Mapping columnMapping= mColumnMapping.get(pColumnIndex); return columnMapping.isEditable(); } /** * Sets value to the cell * @param pValue the value * @param pRowIndex the row index * @param pColumnIndex the column index */ @Override public void setValueAt(final Object pValue, final int pRowIndex, final int pColumnIndex) { final List list = mSelectionInList.getValue(); final Object listEntry = list.get(pRowIndex); final ListTableMapper.Mapping columnMapping = mColumnMapping.get(pColumnIndex); // cast will work, columnMapping.isEditable() will otherwise return false ((AspectAdapter)columnMapping.getValueModel()).getAccessAdapter().setValue(listEntry, pValue); // and now notify the list of the change, but as we can't convert columns we don't provide it mSelectionInList.signalExternalUpdate(new RowColumnChangeEvent(this, pRowIndex, UNKNOWN_ROW_OR_COLUMN)); } /******************************************************************/ /** * Small helper class to describe the attribute/column mapping. */ public static class Mapping { /** name of the table column, never null */ private String mColumnTitle; /** alignment of the table column, may be null */ private String mColumnAlignment; /** prefWidth of the table column or 0 */ private int mColumnPrefWidth; /** class of the table column, may be null */ private Class mColumnClass; /** ValueModel to get the attribute, never null */ private DelegateAccess mValueModel; /** ValueModel to get the attribute in the sorting case, may be null */ private DelegateAccess mValueModelForSorting; /** flag if this column should be sortable */ private boolean mSortable; /** comparator-class for sorting, may be null */ private Class mComparatorClass; /** marker if the column is editable */ private boolean mIsEditable; public Mapping() { mColumnTitle = ""; mColumnAlignment = null; mColumnPrefWidth = 0; mColumnClass = null; mValueModel = new ValueHolder(); mValueModelForSorting= null; mSortable = false; mComparatorClass = null; mIsEditable = false; } public Mapping(final Mapping pCopyFrom) { this(pCopyFrom.getColumnTitle(), pCopyFrom.getColumnAlignment(), pCopyFrom.getColumnPrefWidth(), pCopyFrom.getColumnClass(), pCopyFrom.getValueModel(), pCopyFrom.getValueModelForSorting(), pCopyFrom.isSortable(), pCopyFrom.getComparatorClassName(), pCopyFrom.isEditable()); } public Mapping(final String pColumnTitle) { this(); setColumnTitle(pColumnTitle); } public Mapping(final String pColumnTitle, final Class pColumnClass) { this(); setColumnTitle(pColumnTitle); setColumnClass(pColumnClass); } public Mapping(final String pColumnTitle, final Class pColumnClass, final DelegateAccess pValueModel) { this(); setColumnTitle(pColumnTitle); setColumnClass(pColumnClass); setValueModel (pValueModel); } public Mapping(final String pColumnTitle, final String pColumnAlignment, final Class pColumnClass, final DelegateAccess pValueModel) { this(); setColumnTitle (pColumnTitle); setColumnAlignment (pColumnAlignment); setColumnClass (pColumnClass); setValueModel (pValueModel); } public Mapping(final String pColumnTitle, final String pColumnAlignment, final String pColumnPrefWidth, final Class pColumnClass, final DelegateAccess pValueModel, final DelegateAccess pValueModelForSorting, final boolean pSortable, final String pComparatorClassName, final boolean pEditable) { this(); setColumnTitle (pColumnTitle); setColumnAlignment (pColumnAlignment); setColumnPrefWidth (pColumnPrefWidth); setColumnClass (pColumnClass); setValueModel (pValueModel); setValueModelForSorting(pValueModelForSorting); setSortable (pSortable); setComparatorClassName (pComparatorClassName); setEditable (pEditable); } public String getColumnTitle() { return mColumnTitle; } private void setColumnTitle(final String pColumnTitle) { if (pColumnTitle==null) { throw new IllegalArgumentException("column title must not be null"); } mColumnTitle = pColumnTitle; } public String getColumnAlignment() { return mColumnAlignment; } private void setColumnAlignment(final String pColumnAlignment) { mColumnAlignment = pColumnAlignment; } public String getColumnPrefWidth() { return Integer.toString(mColumnPrefWidth); } public int getColumnPrefWidthIntValue() { return mColumnPrefWidth; } private void setColumnPrefWidth(final String pColumnPrefWidth) { if(pColumnPrefWidth == null) { mColumnPrefWidth= 0; } else { try { mColumnPrefWidth = Integer.parseInt(pColumnPrefWidth); } catch (NumberFormatException e) { throw SwingXMLBuilder.createException("could not parse prefWidth " + pColumnPrefWidth + " of column " + getColumnTitle(), e); } } } public Class getComparatorClass() { return mComparatorClass; } public String getComparatorClassName() { return mComparatorClass == null ? null: mComparatorClass.getName(); } private void setComparatorClassName(final String pComparatorClass) { if (pComparatorClass==null) { return; } try { mComparatorClass = Class.forName(pComparatorClass); } catch (ClassNotFoundException e) { throw new IllegalArgumentException("class "+pComparatorClass+ " not found"); } } public Class getColumnClass() { return mColumnClass; } private void setColumnClass(final Class pColumnClass) { mColumnClass = pColumnClass; } public DelegateAccess getValueModel() { return mValueModel; } private void setValueModel(final DelegateAccess pValueModel) { if (pValueModel==null) { throw new IllegalArgumentException("value model must not be null"); } mValueModel = pValueModel; } public DelegateAccess getValueModelForSorting() { return mValueModelForSorting; } public void setValueModelForSorting(final DelegateAccess pValueModelForSorting) { mValueModelForSorting = pValueModelForSorting; } public boolean isSortable() { return mSortable; } private void setSortable(final boolean pSortable) { mSortable = pSortable; } public boolean isEditable() { return mIsEditable; } private void setEditable(final boolean pEditable) { if (pEditable && (!(mValueModel instanceof AspectAdapter) || !((AspectAdapter)mValueModel).isEditable())) { throw new IllegalArgumentException("mapping should be editable, but mValueModel is not editable: mValueModel= "+mValueModel); } mIsEditable = pEditable; } public boolean equals(final Object pOther) { if (this == pOther) return true; if (!(pOther instanceof Mapping)) return false; final Mapping mapping = (Mapping) pOther; if (mIsEditable != mapping.mIsEditable) return false; if (mColumnPrefWidth != mapping.mColumnPrefWidth) return false; if (mSortable != mapping.mSortable) 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 (mColumnTitle != null ? !mColumnTitle.equals(mapping.mColumnTitle) : mapping.mColumnTitle != null) return false; if (mComparatorClass != null ? !mComparatorClass.equals(mapping.mComparatorClass) : mapping.mComparatorClass != null) return false; if (mValueModel != null ? !mValueModel.equals(mapping.mValueModel) : mapping.mValueModel != null) return false; //noinspection RedundantIfStatement if (mValueModelForSorting != null ? !mValueModelForSorting.equals(mapping.mValueModelForSorting) : mapping.mValueModelForSorting != null) return false; return true; } public int hashCode() { int result; result = (mColumnTitle != null ? mColumnTitle.hashCode() : 0); result = 29 * result + (mColumnAlignment != null ? mColumnAlignment.hashCode() : 0); result = 29 * result + mColumnPrefWidth; result = 29 * result + (mColumnClass != null ? mColumnClass.hashCode() : 0); result = 29 * result + (mValueModel != null ? mValueModel.hashCode() : 0); result = 29 * result + (mValueModelForSorting != null ? mValueModelForSorting.hashCode() : 0); result = 29 * result + (mSortable ? 1 : 0); result = 29 * result + (mComparatorClass != null ? mComparatorClass.hashCode() : 0); result = 29 * result + (mIsEditable ? 1 : 0); return result; } public String toString() { return "ListTableMapper.Mapping{" + "ColumnTitle='" + mColumnTitle + '\'' + ", ColumnAlignment='" + mColumnAlignment + '\'' + ", ColumnPrefWidth=" + mColumnPrefWidth + ", ColumnClass=" + mColumnClass + ", ValueModel=" + mValueModel + ", ValueModelForSorting=" + mValueModelForSorting + ", Sortable=" + mSortable + ", ComparatorClass=" + mComparatorClass + ", IsEditable=" + mIsEditable + '}'; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy