001    package data.swing;
002    
003    import data.*;
004    import data.events.*;
005    
006    import util.*;
007    import util.swing.*;
008    
009    import java.util.*;
010    import java.io.*;
011    
012    /**
013     * A {@link javax.swing.table.TableModel} that models the contents of a {@link Stock}, representing each
014     * {@link StockItem} as an individual record.
015     *
016     * @author Steffen Zschaler
017     * @version 2.0 23/08/1999
018     * @since v2.0
019     */
020    public class StoringStockTableModel extends util.swing.AbstractTableModel implements HelpableListener,
021            StockChangeListener, Serializable {
022    
023        /**
024         * The Stock that is being modelled.
025         *
026         * @serial
027         */
028        protected Stock m_stModel;
029    
030        /**
031         * The DataBasket used to determine visibility.
032         *
033         * @serial
034         */
035        protected DataBasket m_dbBasket;
036    
037        /**
038         * The Comparator that defines the sorting order of records in the model. It compares
039         * {@link StockItem StockItems}.
040         *
041         * @serial
042         */
043        protected Comparator m_cmpComparator = new NaturalComparator();
044    
045        /**
046         * The internal model. A list of StockItems.
047         *
048         * @serial
049         */
050        protected List m_lItems;
051        
052        /**
053         * Set the table's data. Data is {@link data.StoringStock}
054         */
055            public void setData(Object n_stModel) {
056                    m_stModel = (StoringStock) n_stModel;
057                    updateModel();
058                    fireTableDataChanged();
059            }
060    
061        /**
062         * Create a new StoringStockTableModel.
063         *
064         * @param st the Stock to be modelled.
065         * @param db the DataBasket to be used to determine visibility.
066         * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered
067         * according to the natural ordering of the StockItems.
068         * @param ted a TableEntryDescriptor that can split individual StockItems into a table's cells.
069         */
070        public StoringStockTableModel(Stock st, DataBasket db, Comparator cmp, TableEntryDescriptor ted) {
071            super(ted);
072            m_stModel = st;
073            m_dbBasket = db;
074    
075            if (cmp != null) {
076                m_cmpComparator = cmp;
077            }
078    
079            listenerList = new ListenerHelper(this);
080    
081            updateModel();
082        }
083    
084        /**
085         * Get the record at the given index.
086         *
087         * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
088         * @return the {@link StockItem} to be displayed at the given index. May return <code>null</code> if
089         * there is no record at the indicated position.
090         *
091         * @override Never
092         */
093        public Object getRecord(int row) {
094            ((ListenerHelper)listenerList).needModelUpdate();
095    
096            if ((row > -1) && (row < m_lItems.size())) {
097                return m_lItems.get(row);
098            } else {
099                return null;
100            }
101        }
102    
103        /**
104         * Get the number of records in this model.
105         *
106         * @override Never
107         */
108        public int getRowCount() {
109            ((ListenerHelper)listenerList).needModelUpdate();
110    
111            return m_lItems.size();
112        }
113    
114        // HelpableListener interface methods
115    
116        /**
117         * Subscribe as a listener to the model. If the modelled {@link Stock} is a {@link ListenableStock},
118         * subscribe as a listener.
119         *
120         * @override Never
121         */
122        public void subscribe() {
123            if (m_stModel instanceof ListenableStock) {
124                ((ListenableStock)m_stModel).addStockChangeListener(this);
125            }
126        }
127    
128        /**
129         * Un-Subscribe as a listener from the model. If the modelled {@link Stock} is a {@link ListenableStock},
130         * un-subscribe as a listener.
131         *
132         * @override Never
133         */
134        public void unsubscribe() {
135            if (m_stModel instanceof ListenableStock) {
136                ((ListenableStock)m_stModel).removeStockChangeListener(this);
137            }
138        }
139    
140        /**
141         * Update the internal model based on the modelled {@link Stock}.
142         *
143         * @override Never
144         */
145        public void updateModel() {
146            List lItems = new LinkedList();
147    
148            for (Iterator i = m_stModel.iterator(m_dbBasket, false); i.hasNext(); ) {
149                lItems.add(i.next());
150            }
151    
152            Collections.sort(lItems, m_cmpComparator);
153    
154            m_lItems = lItems;
155        }
156    
157        // StockChangeListener interface methods
158    
159        /**
160         * Update the internal model and inform any listeners according to the received event.
161         *
162         * <p>This method is public as an implementation detail and must not be called directly.</p>
163         *
164         * @override Never
165         */
166        public synchronized void addedStockItems(StockChangeEvent e) {
167            if (e.getBasket() == m_dbBasket) {
168                checkAdd(e);
169            }
170        }
171    
172        /**
173         * Update the internal model and inform any listeners according to the received event.
174         *
175         * <p>This method is public as an implementation detail and must not be called directly.</p>
176         *
177         * @override Never
178         */
179        public void commitAddStockItems(StockChangeEvent e) {
180            if (e.getBasket() != m_dbBasket) {
181                checkAdd(e);
182            }
183        }
184    
185        /**
186         * Update the internal model and inform any listeners according to the received event.
187         *
188         * <p>This method is public as an implementation detail and must not be called directly.</p>
189         *
190         * @override Never
191         */
192        public void rollbackAddStockItems(StockChangeEvent e) {
193            if (e.getBasket() == m_dbBasket) {
194                checkRemove(e);
195            }
196        }
197    
198        /**
199         * Update the internal model and inform any listeners according to the received event.
200         *
201         * <p>This method is public as an implementation detail and must not be called directly.</p>
202         *
203         * @override Never
204         */
205        public void canRemoveStockItems(StockChangeEvent e) throws VetoException {}
206    
207        /**
208         * Update the internal model and inform any listeners according to the received event.
209         *
210         * <p>This method is public as an implementation detail and must not be called directly.</p>
211         *
212         * @override Never
213         */
214        public void noRemoveStockItems(StockChangeEvent e) {}
215    
216        /**
217         * Update the internal model and inform any listeners according to the received event.
218         *
219         * <p>This method is public as an implementation detail and must not be called directly.</p>
220         *
221         * @override Never
222         */
223        public void removedStockItems(StockChangeEvent e) {
224            checkRemove(e);
225        }
226    
227        /**
228         * Update the internal model and inform any listeners according to the received event.
229         *
230         * <p>This method is public as an implementation detail and must not be called directly.</p>
231         *
232         * @override Never
233         */
234        public void commitRemoveStockItems(StockChangeEvent e) {}
235    
236        /**
237         * Update the internal model and inform any listeners according to the received event.
238         *
239         * <p>This method is public as an implementation detail and must not be called directly.</p>
240         *
241         * @override Never
242         */
243        public void rollbackRemoveStockItems(StockChangeEvent e) {
244            checkAdd(e);
245        }
246    
247        /**
248         * Update the internal model and inform any listeners according to the received event.
249         *
250         * <p>This method is public as an implementation detail and must not be called directly.</p>
251         *
252         * @override Never
253         */
254        public void canEditStockItems(StockChangeEvent e) throws VetoException {}
255    
256        /**
257         * Update the internal model and inform any listeners according to the received event.
258         *
259         * <p>This method is public as an implementation detail and must not be called directly.</p>
260         *
261         * @override Never
262         */
263        public void noEditStockItems(StockChangeEvent e) {}
264    
265        /**
266         * Update the internal model and inform any listeners according to the received event.
267         *
268         * <p>This method is public as an implementation detail and must not be called directly.</p>
269         *
270         * @override Never
271         */
272        public void editingStockItems(StockChangeEvent e) {
273            if (e.getBasket() != m_dbBasket) {
274                checkRemove(e);
275            }
276        }
277    
278        /**
279         * Update the internal model and inform any listeners according to the received event.
280         *
281         * <p>This method is public as an implementation detail and must not be called directly.</p>
282         *
283         * @override Never
284         */
285        public void commitEditStockItems(StockChangeEvent e) {
286            if (e.getBasket() != m_dbBasket) {
287                checkAdd(e);
288            } else {
289                checkUpdate(e);
290            }
291        }
292    
293        /**
294         * Update the internal model and inform any listeners according to the received event.
295         *
296         * <p>This method is public as an implementation detail and must not be called directly.</p>
297         *
298         * @override Never
299         */
300        public void rollbackEditStockItems(StockChangeEvent e) {
301            if (e.getBasket() != m_dbBasket) {
302                checkAdd(e);
303            } else {
304                checkUpdate(e);
305            }
306        }
307    
308        /**
309         * Internal helper method. Check where, if at all, the indicated StockItems have been added with respect to
310         * the internal model.
311         *
312         * @override Never
313         */
314        protected void checkAdd(StockChangeEvent e) {
315            updateModel();
316            if (m_stModel instanceof CountingStock) {
317                fireTableDataChanged(); // for CountingStocks, we cannot clearly identify the rows that changed!
318            } else {
319                int nIdx1 = Integer.MAX_VALUE;
320                int nIdx2 = -1;
321    
322                for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
323                    int nIdx = m_lItems.indexOf(i.next());
324    
325                    if (nIdx < nIdx1) {
326                        nIdx1 = nIdx;
327                    }
328    
329                    if (nIdx > nIdx2) {
330                        nIdx2 = nIdx;
331                    }
332                }
333    
334                if (nIdx2 > -1) {
335                    fireTableRowsInserted(nIdx1, nIdx2);
336                }
337            }
338        }
339    
340        /**
341         * Internal helper method. Check from where, if at all, the indicated StockItems have been removed with
342         * respect to the internal model.
343         *
344         * @override Never
345         */
346        protected void checkRemove(StockChangeEvent e) {
347            int nIdx1 = Integer.MAX_VALUE;
348            int nIdx2 = -1;
349    
350            if (!(m_stModel instanceof CountingStock)) {
351                for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
352                    int nIdx = m_lItems.indexOf(i.next());
353    
354                    if (nIdx < nIdx1) {
355                        nIdx1 = nIdx;
356                    }
357    
358                    if (nIdx > nIdx2) {
359                        nIdx2 = nIdx;
360                    }
361                }
362            }
363    
364            updateModel();
365    
366            if (m_stModel instanceof CountingStock) {
367                fireTableDataChanged(); // for CountingStocks, we cannot clearly identify the rows that changed!
368            } else {
369                if (nIdx2 > -1) {
370                    fireTableRowsDeleted(nIdx1, nIdx2);
371                }
372            }
373        }
374    
375        /**
376         * Internal helper method. Check for an update in the indicated StockItems.
377         *
378         * @override Never
379         */
380        protected void checkUpdate(StockChangeEvent e) {
381            checkRemove(e);
382            checkAdd(e);
383        }
384    }