package data.swing; import data.*; import data.events.*; import util.*; import util.swing.*; import java.beans.*; import java.util.*; import java.io.*; /** * A {@link javax.swing.table.TableModel} that models the contents of a {@link CountingStock}. * * @author Steffen Zschaler * @version 2.0 23/08/1999 * @since v2.0 */ public class CountingStockTableModel extends AbstractTableModel implements HelpableListener, StockChangeListener, CatalogChangeListener, PropertyChangeListener, Serializable { /** * The DataBasket used to determine visibility. * * @serial */ protected DataBasket m_dbBasket; /** * The CountingStock being modelled. May be {@link data.filters.CountingStockFilter filtered}. * * @serial */ protected CountingStock m_csModel; /** * The Comparator that defines the sorting order of records in the model. It compares the keys of the * actual items. * * @serial */ protected Comparator m_cmpComparator = new NaturalComparator(); /** * If true, show lines informing about a zero amount of objects. * * @serial */ protected boolean m_fShowZeros; /** * The internal model. A list of the items' keys. * * @serial */ protected List m_lKeys; /** * Create a new CountingStockTableModel. * * @param cs the Stock to be modelled. * @param db the DataBasket to be used to determine visibility. * @param cmp the Comparator defining the sorting order. If <code>null</code> the records will be sorted * according to the natural ordering of their keys. * @param fShowZeros if true, lines informing about a zero amount of objects will be shown. * @param ted a TableEntryDescriptor that can split a {@link Record} into a table's cells. */ public CountingStockTableModel (CountingStock cs, DataBasket db, Comparator cmp, boolean fShowZeros, TableEntryDescriptor ted) { super (ted); m_dbBasket = db; m_csModel = cs; if (cmp != null) { m_cmpComparator = cmp; } m_fShowZeros = fShowZeros; listenerList = new ListenerHelper (this); updateModel(); } /** * A {@link CountingStockTableModel}'s record. * * <p>The record is basically a combination of a {@link CatalogItem} and a number indicating the number of * objects available.</p> * * @author Steffen Zschaler * @version 2.0 23/08/1999 * @since v2.0 */ public static class Record implements Comparable { /** * The CatalogItem part of the record. */ // Changed 11/09/2000-STEFFEN to private to fix F5. private CatalogItem m_ciDescriptor; /** * The number of actually available items. */ // Changed 11/09/2000-STEFFEN to private to fix F5. private int m_nCount; /** * Create a new Record. */ public Record (CatalogItem ci, int nCount) { super(); m_ciDescriptor = ci; m_nCount = nCount; } /** * Compare by descriptor. */ public int compareTo (Object o) { return m_ciDescriptor.compareTo (((Record) o).getDescriptor()); } /** * Get the CatalogItem describing the items represented by this record. */ public CatalogItem getDescriptor() { return m_ciDescriptor; } /** * Get the number of items in this record. */ public int getCount() { return m_nCount; } } /** * Get the record at the given index. * * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}). * @return the {@link Record} to be displayed at the given index. May return <code>null</code> if * either there is no record at the indicated position or an exception occurs. * * @override Never */ public Object getRecord (int row) { ((ListenerHelper) listenerList).needModelUpdate(); try { if ((row > -1) && (row < getRowCount())) { String sKey = (String) m_lKeys.get (row); return new Record (m_csModel.getCatalog (m_dbBasket).get (sKey, m_dbBasket, false), m_csModel.countItems (sKey, m_dbBasket)); } else { return null; } } catch (VetoException ve) { return null; } } /** * Get the number of records in this model. * * @override Never */ public int getRowCount() { ((ListenerHelper) listenerList).needModelUpdate(); return m_lKeys.size(); } // HelpableListener interface methods /** * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog}, * subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock}, subscribe as * a listener. * * @override Never */ public void subscribe() { if (m_csModel instanceof ListenableStock) { ((ListenableStock) m_csModel).addStockChangeListener (this); } if (m_csModel.getCatalog (m_dbBasket) instanceof ListenableCatalog) { ((ListenableCatalog) m_csModel.getCatalog (m_dbBasket)).addCatalogChangeListener (this); } } /** * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog}, * un-subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock}, * un-subscribe as a listener. * * @override Never */ public void unsubscribe() { if (m_csModel instanceof ListenableStock) { ((ListenableStock) m_csModel).removeStockChangeListener (this); } if (m_csModel.getCatalog (m_dbBasket) instanceof ListenableCatalog) { ((ListenableCatalog) m_csModel.getCatalog (m_dbBasket)).removeCatalogChangeListener (this); } } /** * Update the internal model based on the modelled {@link CountingStock}. * * @override Never */ public synchronized void updateModel() { List lKeys = new LinkedList (m_csModel.getCatalog (m_dbBasket).keySet (m_dbBasket)); Collections.sort (lKeys, m_cmpComparator); if (!m_fShowZeros) { for (Iterator i = lKeys.iterator(); i.hasNext();) { String sKey = (String) i.next(); if (m_csModel.countItems (sKey, m_dbBasket) == 0) { i.remove(); } } } m_lKeys = lKeys; } // StockChangeListener interface methods /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void addedStockItems (StockChangeEvent e) { if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) { checkUpdate (e.getAffectedKey()); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitAddStockItems (StockChangeEvent e) { checkUpdate (e.getAffectedKey()); } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rollbackAddStockItems (StockChangeEvent e) { if (e.getBasket() == m_dbBasket) { checkUpdate (e.getAffectedKey()); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void canRemoveStockItems (StockChangeEvent e) throws VetoException {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void noRemoveStockItems (StockChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void removedStockItems (StockChangeEvent e) { checkUpdate (e.getAffectedKey()); } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitRemoveStockItems (StockChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rollbackRemoveStockItems (StockChangeEvent e) { checkUpdate (e.getAffectedKey()); } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void canEditStockItems (StockChangeEvent e) throws VetoException {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void noEditStockItems (StockChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void editingStockItems (StockChangeEvent e) { // never fired, we talk about CountingStocks! } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitEditStockItems (StockChangeEvent e) { // never fired! } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rollbackEditStockItems (StockChangeEvent e) { // never fired! } // CatalogChangeListener interface methods /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void addedCatalogItem (CatalogChangeEvent e) { if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) { checkAdd (e.getAffectedItem().getName()); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitedAddCatalogItem (CatalogChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (e.getAffectedItem().getName()); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rolledbackAddCatalogItem (CatalogChangeEvent e) { if (e.getBasket() == m_dbBasket) { checkRemove (e.getAffectedItem().getName()); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void canRemoveCatalogItem (CatalogChangeEvent e) throws VetoException {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void noRemoveCatalogItem (CatalogChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void removedCatalogItem (CatalogChangeEvent e) { checkRemove (e.getAffectedItem().getName()); } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitedRemoveCatalogItem (CatalogChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rolledbackRemoveCatalogItem (CatalogChangeEvent e) { checkAdd (e.getAffectedItem().getName()); } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void canEditCatalogItem (CatalogChangeEvent e) throws VetoException {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void noEditCatalogItem (CatalogChangeEvent e) {} /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void editingCatalogItem (CatalogChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkRemove (e.getAffectedItem().getName()); } else { e.getAffectedItem().addPropertyChangeListener (this); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void commitEditCatalogItem (CatalogChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (e.getAffectedItem().getName()); } else { e.getAffectedItem().removePropertyChangeListener (this); updateModel(); fireTableDataChanged(); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void rollbackEditCatalogItem (CatalogChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (e.getAffectedItem().getName()); } else { e.getAffectedItem().removePropertyChangeListener (this); updateModel(); fireTableDataChanged(); } } /** * Update the internal model and inform any listeners according to the received event. * * <p>This method is public as an implementation detail and must not be called directly.</p> * * @override Never */ public void propertyChange (PropertyChangeEvent e) { if (e.getSource() instanceof CatalogItem) { checkUpdate (((CatalogItem) e.getSource()).getName()); } } /** * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the * internal model. * * @param ci the added CatalogItem * * @override Never */ protected synchronized void checkAdd (String sKey) { updateModel(); int nIdx = m_lKeys.indexOf (sKey); if (nIdx > -1) { fireTableRowsInserted (nIdx, nIdx); } } /** * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect * to the internal model. * * @param ci the removed CatalogItem * * @override Never */ protected synchronized void checkRemove (String sKey) { int nIdx = m_lKeys.indexOf (sKey); updateModel(); if (nIdx > -1) { fireTableRowsDeleted (nIdx, nIdx); } } /** * Internal helper method. Check for updates in the given CatalogItem. * * @param ci the updated CatalogItem * * @override Never */ protected synchronized void checkUpdate (String sKey) { int nIdx1 = m_lKeys.indexOf (sKey); updateModel(); int nIdx2 = m_lKeys.indexOf (sKey); if (nIdx1 == -1) { if (nIdx2 > -1) { fireTableRowsInserted (nIdx2, nIdx2); } else { return; } } else { if (nIdx2 > -1) { if (nIdx1 > nIdx2) { int nTemp = nIdx2; nIdx2 = nIdx1; nIdx1 = nTemp; } fireTableRowsUpdated (nIdx1, nIdx2); } else { fireTableRowsDeleted (nIdx1, nIdx1); } } } }