package data.swing; import data.*; import data.events.*; import util.*; import util.swing.*; import java.util.*; import java.io.*; /** * A {@link javax.swing.table.TableModel} that models the contents of a {@link Stock}, representing each * {@link StockItem} as an individual record. * * @author Steffen Zschaler * @version 2.0 23/08/1999 * @since v2.0 */ public class StoringStockTableModel extends util.swing.AbstractTableModel implements HelpableListener, StockChangeListener, Serializable { /** * The Stock that is being modelled. * * @serial */ protected Stock m_stModel; /** * The DataBasket used to determine visibility. * * @serial */ protected DataBasket m_dbBasket; /** * The Comparator that defines the sorting order of records in the model. It compares * {@link StockItem StockItems}. * * @serial */ protected Comparator m_cmpComparator = new NaturalComparator(); /** * The internal model. A list of StockItems. * * @serial */ protected List m_lItems; /** * Create a new StoringStockTableModel. * * @param st the Stock to be modelled. * @param db the DataBasket to be used to determine visibility. * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered * according to the natural ordering of the StockItems. * @param ted a TableEntryDescriptor that can split individual StockItems into a table's cells. */ public StoringStockTableModel (Stock st, DataBasket db, Comparator cmp, TableEntryDescriptor ted) { super (ted); m_stModel = st; m_dbBasket = db; if (cmp != null) { m_cmpComparator = cmp; } listenerList = new ListenerHelper (this); updateModel(); } /** * 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 StockItem} to be displayed at the given index. May return <code>null</code> if * there is no record at the indicated position. * * @override Never */ public Object getRecord (int row) { ((ListenerHelper) listenerList).needModelUpdate(); if ((row > -1) && (row < m_lItems.size())) { return m_lItems.get (row); } else { return null; } } /** * Get the number of records in this model. * * @override Never */ public int getRowCount() { ((ListenerHelper) listenerList).needModelUpdate(); return m_lItems.size(); } // HelpableListener interface methods /** * Subscribe as a listener to the model. If the modelled {@link Stock} is a {@link ListenableStock}, * subscribe as a listener. * * @override Never */ public void subscribe() { if (m_stModel instanceof ListenableStock) { ((ListenableStock) m_stModel).addStockChangeListener (this); } } /** * Un-Subscribe as a listener from the model. If the modelled {@link Stock} is a {@link ListenableStock}, * un-subscribe as a listener. * * @override Never */ public void unsubscribe() { if (m_stModel instanceof ListenableStock) { ((ListenableStock) m_stModel).removeStockChangeListener (this); } } /** * Update the internal model based on the modelled {@link Stock}. * * @override Never */ public void updateModel() { List lItems = new LinkedList(); for (Iterator i = m_stModel.iterator (m_dbBasket, false); i.hasNext();) { lItems.add (i.next()); } Collections.sort (lItems, m_cmpComparator); m_lItems = lItems; } // 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 synchronized void addedStockItems (StockChangeEvent e) { if (e.getBasket() == m_dbBasket) { checkAdd (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 commitAddStockItems (StockChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (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 rollbackAddStockItems (StockChangeEvent e) { if (e.getBasket() == m_dbBasket) { checkRemove (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 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) { checkRemove (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 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) { checkAdd (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 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) { if (e.getBasket() != m_dbBasket) { checkRemove (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 commitEditStockItems (StockChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (e); } else { checkUpdate (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 rollbackEditStockItems (StockChangeEvent e) { if (e.getBasket() != m_dbBasket) { checkAdd (e); } else { checkUpdate (e); } } /** * Internal helper method. Check where, if at all, the indicated StockItems have been added with respect to * the internal model. * * @override Never */ protected void checkAdd (StockChangeEvent e) { updateModel(); if (m_stModel instanceof CountingStock) { fireTableDataChanged(); // for CountingStocks, we cannot clearly identify the rows that changed! } else { int nIdx1 = Integer.MAX_VALUE; int nIdx2 = -1; for (Iterator i = e.getAffectedItems(); i.hasNext();) { int nIdx = m_lItems.indexOf (i.next()); if (nIdx < nIdx1) { nIdx1 = nIdx; } if (nIdx > nIdx2) { nIdx2 = nIdx; } } if (nIdx2 > -1) { fireTableRowsInserted (nIdx1, nIdx2); } } } /** * Internal helper method. Check from where, if at all, the indicated StockItems have been removed with * respect to the internal model. * * @override Never */ protected void checkRemove (StockChangeEvent e) { int nIdx1 = Integer.MAX_VALUE; int nIdx2 = -1; if (!(m_stModel instanceof CountingStock)) { for (Iterator i = e.getAffectedItems(); i.hasNext();) { int nIdx = m_lItems.indexOf (i.next()); if (nIdx < nIdx1) { nIdx1 = nIdx; } if (nIdx > nIdx2) { nIdx2 = nIdx; } } } updateModel(); if (m_stModel instanceof CountingStock) { fireTableDataChanged(); // for CountingStocks, we cannot clearly identify the rows that changed! } else if (nIdx2 > -1) { fireTableRowsDeleted (nIdx1, nIdx2); } } /** * Internal helper method. Check for an update in the indicated StockItems. * * @override Never */ protected void checkUpdate (StockChangeEvent e) { checkRemove (e); checkAdd (e); } }