package util.swing;

import java.util.Comparator;

import util.ReverseOrderComparator;

/**
  * A {@link javax.swing.table.TableModel} that models a list of records rather than a matrix of cells.
  *
  * <p>Usage of this TableModel is always recommendable when the data that is to be displayed consists of
  * a list of uniformly structured records and you want to display a selection of attributes for each record.
  * As the data management classes ({@link data.Catalog}, {@link data.Stock}, {@link data.DataBasket}) of the
  * &quot;SalesPoint&quot; framework match this scheme, there are concrete subclasses of this model for each of
  * those classes.</p>
  *
  * <p><code>util.swing.AbstractTableModel</code> will give one row in the table to each record of the model.
  * The record that is to be displayed in a certain row is determined by the {@link #getRecord} method which is
  * <i>abstract</i> and must be overridden in subclasses. Thus, subclasses have the opportunity to define what
  * type (class) of records they use and how they are derived from the actual data source in the background.
  * There's only one more method that subclasses will have to override:
  * {@link javax.swing.table.TableModel#getRowCount}.</p>
  *
  * <p>A {@link TableEntryDescriptor} will be used to determine how individual records are represented in one
  * row of the table model, i.e. how many columns there are and what is displayed in the cells as well as
  * formatting and editing issues.</p>
  *
  * @see JAbstractTable
  *
  * @author Steffen Zschaler
  * @version 2.0 28/07/1999
  * @since v2.0
  */
public abstract class AbstractTableModel extends javax.swing.table.AbstractTableModel {

  /**
    * The {@link TableEntryDescriptor} that is used to split records into columns.
    *
    * @serial
    */
  private TableEntryDescriptor m_tedEntryDescriptor;

  /**
    * Create a new AbstractTableModel.
    *
    * @param ted the {@link TableEntryDescriptor} that is to be used to split records into columns.
    */
  public AbstractTableModel(TableEntryDescriptor ted) {
    super();

    m_tedEntryDescriptor = ted;
  }

  /**
    * Get the {@link TableEntryDescriptor} that is used to split records into columns.
    *
    * @override Never
    */
  public TableEntryDescriptor getEntryDescriptor() {
    return m_tedEntryDescriptor;
  }

  /**
    * Get the number of columns in this {@link javax.swing.table.TableModel}.
    *
    * @return the number of columns in the associated {@link TableEntryDescriptor}.
    *
    * @override Never
    *
    * @see TableEntryDescriptor#getColumnCount
    */
  public int getColumnCount() {
    return m_tedEntryDescriptor.getColumnCount();
  }

  /**
    * Get the name of the given column in this {@link javax.swing.table.TableModel}.
    *
    * @param nIdx the column's index. Columns indices run from 0 to
    * {@link #getColumnCount getColumnCount() - 1}.
    *
    * @return the name of the column in the associated {@link TableEntryDescriptor}.
    *
    * @override Never
    *
    * @see TableEntryDescriptor#getColumnName
    */
  public String getColumnName (int nIdx) {
    return m_tedEntryDescriptor.getColumnName (nIdx);
  }

  /**
    * Get the class of the given column in this {@link javax.swing.table.TableModel}.
    *
    * @param nIdx the column's index. Columns indices run from 0 to
    * {@link #getColumnCount getColumnCount() - 1}.
    *
    * @return the class of the column in the associated {@link TableEntryDescriptor}.
    *
    * @override Never
    *
    * @see TableEntryDescriptor#getColumnClass
    */
  public Class getColumnClass (int nIdx) {
    return m_tedEntryDescriptor.getColumnClass (nIdx);
  }

  /**
    * Get the value of the given cell in this {@link javax.swing.table.TableModel}.
    *
    * <p>First determines the record associated to the row by calling {@link #getRecord}, then calls
    * {@link TableEntryDescriptor#getValueAt getValueAt()} in the associated TableEntryDescriptor.</p>
    *
    * @param row the row index for which to determine the value. This will be passed on to {@link #getRecord}.
    * Row indices run from 0 to {@link javax.swing.table.TableModel#getRowCount getRowCount() - 1}.
    * @param col the column's index. Columns indices run from 0 to
    * {@link #getColumnCount getColumnCount() - 1}.
    *
    * @return the value returned by {@link TableEntryDescriptor#getValueAt}.
    *
    * @override Never
    */
  public Object getValueAt (int row, int col) {
    Object oRecord = getRecord (row);
    if (oRecord != null) {
      return m_tedEntryDescriptor.getValueAt (oRecord, col);
    }
    else {
      return null;
    }
  }

  /**
    * Check whether the given cell is editable in this {@link javax.swing.table.TableModel}.
    *
    * <p>First determines the record associated to the row by calling {@link #getRecord}, then calls
    * {@link TableEntryDescriptor#isElementEditable isElementEditable()} in the associated
    * TableEntryDescriptor.</p>
    *
    * @param row the row index for which to determine editability. This will be passed on to {@link #getRecord}.
    * Row indices run from 0 to {@link javax.swing.table.TableModel#getRowCount getRowCount() - 1}.
    * @param col the column's index. Columns indices run from 0 to
    * {@link #getColumnCount getColumnCount() - 1}.
    *
    * @return the value returned by {@link TableEntryDescriptor#isElementEditable}.
    *
    * @override Never
    */
  public boolean isCellEditable (int row, int col) {
    Object oRecord = getRecord (row);
    if (oRecord != null) {
      return m_tedEntryDescriptor.isElementEditable (oRecord, col);
    }
    else {
      return false;
    }
  }

  /**
    * Set the value of the given cell in this {@link javax.swing.table.TableModel}.
    *
    * <p>First determines the record associated to the row by calling {@link #getRecord}, then calls
    * {@link TableEntryDescriptor#setValueAt setValueAt()} in the associated TableEntryDescriptor.</p>
    *
    * @param oValue the new value for the cell. This will be passed on to
    * {@link TableEntryDescriptor#setValueAt} as the <code>oValue</code> parameter.
    * @param row the row index for which to set the value. This will be passed on to {@link #getRecord}.
    * Row indices run from 0 to {@link javax.swing.table.TableModel#getRowCount getRowCount() - 1}.
    * @param col the column's index. Columns indices run from 0 to
    * {@link #getColumnCount getColumnCount() - 1}.
    *
    * @override Never
    */
  public void setValueAt (Object oValue, int row, int col) {
    Object oRecord = getRecord (row);
    if (oRecord != null) {
      m_tedEntryDescriptor.setValueAt (oRecord, col, oValue);
      
      fireTableCellUpdated (row, col);
    }
  }

  /**
    * Reorders the table by the specified column if that's possible.
    *
    * @param nIdx the index of the column by which to sort
    * @param fAscending if false orders the records in descending order
    *
    * @see #reOrderBy
    *
    * @override Never
    *
    * @since v3.0 12/14/2000
    */
  public void orderByColumn (int nIdx, boolean fAscending) {
    if (m_tedEntryDescriptor.canSortByColumn (nIdx)) {
      Comparator cmp = m_tedEntryDescriptor.getColumnOrder (nIdx);
      
      if (! fAscending) {
        cmp = new ReverseOrderComparator (cmp);
      }
      
      reOrderBy (cmp);
    }
  }
  
  /**
    * Get the record associated to the given row.
    *
    * <p>Subclasses must indicate the class of the record in their documentation.</p>
    *
    * @param row the row index for which to return the record. Row indices run from 0 to
    * {@link javax.swing.table.TableModel#getRowCount getRowCount() - 1}.
    *
    * @return the record associated to the given row. May return <code>null</code>, instead of throwing an exception,
    * if the given index is without its bounds.
    *
    * @override Always You must override this method to define and incorporate your own type of record.
    * Subclasses should specify what class of record is returned.
    */
  public abstract Object getRecord (int row);
  
  /**
    * Reorder the records displayed according to the specified comparator.
    *
    * @param cmp the comparator by which to order.
    *
    * @override Sometimes Override this method if you want sorting by column for your derived models. The
    * default implementation does nothing.
    * Subclasses should specify what class of record is maintained and whether the comparators must compare
    * whole records or just specific attributes.
    *
    * @since v3.0 12/14/2000
    */
  protected void reOrderBy (Comparator cmp) {
  }
}