001    package data.swing;
002    
003    import data.*;
004    import data.events.*;
005    
006    import util.*;
007    import util.swing.*;
008    
009    import java.beans.*;
010    import java.util.*;
011    import java.io.*;
012    
013    /**
014     * A {@link javax.swing.table.TableModel} that models the contents of a {@link CountingStock}.
015     *
016     * @author Steffen Zschaler
017     * @version 2.0 23/08/1999
018     * @since v2.0
019     */
020    public class CountingStockTableModel extends AbstractTableModel implements HelpableListener,
021            StockChangeListener, CatalogChangeListener, PropertyChangeListener, Serializable {
022    
023        /**
024         * The DataBasket used to determine visibility.
025         *
026         * @serial
027         */
028        protected DataBasket m_dbBasket;
029    
030        /**
031         * The CountingStock being modelled. May be {@link data.filters.CountingStockFilter filtered}.
032         *
033         * @serial
034         */
035        protected CountingStock m_csModel;
036    
037        /**
038         * The Comparator that defines the sorting order of records in the model. It compares the keys of the
039         * actual items.
040         *
041         * @serial
042         */
043        protected Comparator m_cmpComparator = new NaturalComparator();
044    
045        /**
046         * If true, show lines informing about a zero amount of objects.
047         *
048         * @serial
049         */
050        protected boolean m_fShowZeros;
051    
052        /**
053         * The internal model. A list of the items' keys.
054         *
055         * @serial
056         */
057        protected List m_lKeys;
058        
059        /**
060         * set the table's data. Data is {@link data.CountingStock}
061         */
062        public void setData(Object n_csModel) {
063            m_csModel = (CountingStock) n_csModel;
064            updateModel();
065            fireTableDataChanged();         
066        }
067    
068        /**
069         * Create a new CountingStockTableModel.
070         *
071         * @param cs the Stock to be modelled.
072         * @param db the DataBasket to be used to determine visibility.
073         * @param cmp the Comparator defining the sorting order. If <code>null</code> the records will be sorted
074         * according to the natural ordering of their keys.
075         * @param fShowZeros if true, lines informing about a zero amount of objects will be shown.
076         * @param ted a TableEntryDescriptor that can split a {@link Record} into a table's cells.
077         */
078        public CountingStockTableModel(CountingStock cs, DataBasket db, Comparator cmp, boolean fShowZeros,
079                TableEntryDescriptor ted) {
080            super(ted);
081    
082            m_dbBasket = db;
083            m_csModel = cs;
084    
085            if (cmp != null) {
086                m_cmpComparator = cmp;
087            }
088    
089            m_fShowZeros = fShowZeros;
090    
091            listenerList = new ListenerHelper(this);
092    
093            updateModel();
094        }
095    
096        /**
097         * A {@link CountingStockTableModel}'s record.
098         *
099         * <p>The record is basically a combination of a {@link CatalogItem} and a number indicating the number of
100         * objects available.</p>
101         *
102         * @author Steffen Zschaler
103         * @version 2.0 23/08/1999
104         * @since v2.0
105         */
106        public static class Record implements Comparable {
107    
108            /**
109             * The CatalogItem part of the record.
110             */
111            // Changed 11/09/2000-STEFFEN to private to fix F5.
112            private CatalogItem m_ciDescriptor;
113    
114            /**
115             * The number of actually available items.
116             */
117            // Changed 11/09/2000-STEFFEN to private to fix F5.
118            private int m_nCount;
119    
120            /**
121             * Create a new Record.
122             */
123            public Record(CatalogItem ci, int nCount) {
124                super();
125    
126                m_ciDescriptor = ci;
127                m_nCount = nCount;
128            }
129    
130            /**
131             * Compare by descriptor.
132             */
133            public int compareTo(Object o) {
134                return m_ciDescriptor.compareTo(((Record)o).getDescriptor());
135            }
136    
137            /**
138             * Get the CatalogItem describing the items represented by this record.
139             */
140            public CatalogItem getDescriptor() {
141                return m_ciDescriptor;
142            }
143    
144            /**
145             * Get the number of items in this record.
146             */
147            public int getCount() {
148                return m_nCount;
149            }
150        }
151    
152        /**
153         * Get the record at the given index.
154         *
155         * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
156         * @return the {@link Record} to be displayed at the given index. May return <code>null</code> if
157         * either there is no record at the indicated position or an exception occurs.
158         *
159         * @override Never
160         */
161        public Object getRecord(int row) {
162            ((ListenerHelper)listenerList).needModelUpdate();
163    
164            try {
165                if ((row > -1) && (row < getRowCount())) {
166                    String sKey = (String)m_lKeys.get(row);
167    
168                    return new Record(m_csModel.getCatalog(m_dbBasket).get(sKey, m_dbBasket, false),
169                            m_csModel.countItems(sKey, m_dbBasket));
170                } else {
171                    return null;
172                }
173            }
174            catch (VetoException ve) {
175                return null;
176            }
177        }
178    
179        /**
180         * Get the number of records in this model.
181         *
182         * @override Never
183         */
184        public int getRowCount() {
185            ((ListenerHelper)listenerList).needModelUpdate();
186    
187            return m_lKeys.size();
188        }
189    
190        // HelpableListener interface methods
191        /**
192         * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
193         * subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock}, subscribe as
194         * a listener.
195         *
196         * @override Never
197         */
198        public void subscribe() {
199            if (m_csModel instanceof ListenableStock) {
200                ((ListenableStock)m_csModel).addStockChangeListener(this);
201            }
202    
203            if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
204                ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).addCatalogChangeListener(this);
205            }
206        }
207    
208        /**
209         * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
210         * un-subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock},
211         * un-subscribe as a listener.
212         *
213         * @override Never
214         */
215        public void unsubscribe() {
216            if (m_csModel instanceof ListenableStock) {
217                ((ListenableStock)m_csModel).removeStockChangeListener(this);
218            }
219    
220            if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
221                ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).removeCatalogChangeListener(this);
222            }
223        }
224    
225        /**
226         * Update the internal model based on the modelled {@link CountingStock}.
227         *
228         * @override Never
229         */
230        public synchronized void updateModel() {
231    
232            //Use catalog's keys here, as Stock's keys are deleted if an item's count is 0 (this would cause empty
233            //items not to be shown, even if fShowZeros is true)
234            List lKeys = new LinkedList(m_csModel.getCatalog(m_dbBasket).keySet(m_dbBasket));
235            Collections.sort(lKeys, new Comparator() {
236                public int compare(Object o1, Object o2) {
237                    try {
238                        return m_cmpComparator.compare(m_csModel.getCatalog(null).get((String)o1, m_dbBasket, false),
239                                m_csModel.getCatalog(null).get((String)o2, m_dbBasket, false));
240                    }
241                    catch (VetoException ve) {
242                        System.err.println(ve);
243                        return 0;
244                    }
245                }
246            });
247            if (!m_fShowZeros) {
248                for (Iterator i = lKeys.iterator(); i.hasNext(); ) {
249                    String sKey = (String)i.next();
250    
251                    if (m_csModel.countItems(sKey, m_dbBasket) == 0) {
252                        i.remove();
253                    }
254                }
255            }
256    
257            m_lKeys = lKeys;
258        }
259    
260        // StockChangeListener interface methods
261    
262        /**
263         * Update the internal model and inform any listeners according to the received event.
264         *
265         * <p>This method is public as an implementation detail and must not be called directly.</p>
266         *
267         * @override Never
268         */
269        public void addedStockItems(StockChangeEvent e) {
270            if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
271                checkUpdate(e.getAffectedKey());
272            }
273        }
274    
275        /**
276         * Update the internal model and inform any listeners according to the received event.
277         *
278         * <p>This method is public as an implementation detail and must not be called directly.</p>
279         *
280         * @override Never
281         */
282        public void commitAddStockItems(StockChangeEvent e) {
283            checkUpdate(e.getAffectedKey());
284        }
285    
286        /**
287         * Update the internal model and inform any listeners according to the received event.
288         *
289         * <p>This method is public as an implementation detail and must not be called directly.</p>
290         *
291         * @override Never
292         */
293        public void rollbackAddStockItems(StockChangeEvent e) {
294            if (e.getBasket() == m_dbBasket) {
295                checkUpdate(e.getAffectedKey());
296            }
297        }
298    
299        /**
300         * Update the internal model and inform any listeners according to the received event.
301         *
302         * <p>This method is public as an implementation detail and must not be called directly.</p>
303         *
304         * @override Never
305         */
306        public void canRemoveStockItems(StockChangeEvent e) throws VetoException {}
307    
308        /**
309         * Update the internal model and inform any listeners according to the received event.
310         *
311         * <p>This method is public as an implementation detail and must not be called directly.</p>
312         *
313         * @override Never
314         */
315        public void noRemoveStockItems(StockChangeEvent e) {}
316    
317        /**
318         * Update the internal model and inform any listeners according to the received event.
319         *
320         * <p>This method is public as an implementation detail and must not be called directly.</p>
321         *
322         * @override Never
323         */
324        public void removedStockItems(StockChangeEvent e) {
325            checkUpdate(e.getAffectedKey());
326        }
327    
328        /**
329         * Update the internal model and inform any listeners according to the received event.
330         *
331         * <p>This method is public as an implementation detail and must not be called directly.</p>
332         *
333         * @override Never
334         */
335        public void commitRemoveStockItems(StockChangeEvent e) {}
336    
337        /**
338         * Update the internal model and inform any listeners according to the received event.
339         *
340         * <p>This method is public as an implementation detail and must not be called directly.</p>
341         *
342         * @override Never
343         */
344        public void rollbackRemoveStockItems(StockChangeEvent e) {
345            checkUpdate(e.getAffectedKey());
346        }
347    
348        /**
349         * Update the internal model and inform any listeners according to the received event.
350         *
351         * <p>This method is public as an implementation detail and must not be called directly.</p>
352         *
353         * @override Never
354         */
355        public void canEditStockItems(StockChangeEvent e) throws VetoException {}
356    
357        /**
358         * Update the internal model and inform any listeners according to the received event.
359         *
360         * <p>This method is public as an implementation detail and must not be called directly.</p>
361         *
362         * @override Never
363         */
364        public void noEditStockItems(StockChangeEvent e) {}
365    
366        /**
367         * Update the internal model and inform any listeners according to the received event.
368         *
369         * <p>This method is public as an implementation detail and must not be called directly.</p>
370         *
371         * @override Never
372         */
373        public void editingStockItems(StockChangeEvent e) {
374            // never fired, we talk about CountingStocks!
375        }
376    
377        /**
378         * Update the internal model and inform any listeners according to the received event.
379         *
380         * <p>This method is public as an implementation detail and must not be called directly.</p>
381         *
382         * @override Never
383         */
384        public void commitEditStockItems(StockChangeEvent e) {
385            // never fired!
386        }
387    
388        /**
389         * Update the internal model and inform any listeners according to the received event.
390         *
391         * <p>This method is public as an implementation detail and must not be called directly.</p>
392         *
393         * @override Never
394         */
395        public void rollbackEditStockItems(StockChangeEvent e) {
396            // never fired!
397        }
398    
399        // CatalogChangeListener interface methods
400    
401        /**
402         * Update the internal model and inform any listeners according to the received event.
403         *
404         * <p>This method is public as an implementation detail and must not be called directly.</p>
405         *
406         * @override Never
407         */
408        public void addedCatalogItem(CatalogChangeEvent e) {
409            if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
410                checkAdd(e.getAffectedItem().getName());
411            }
412        }
413    
414        /**
415         * Update the internal model and inform any listeners according to the received event.
416         *
417         * <p>This method is public as an implementation detail and must not be called directly.</p>
418         *
419         * @override Never
420         */
421        public void commitedAddCatalogItem(CatalogChangeEvent e) {
422            if (e.getBasket() != m_dbBasket) {
423                checkAdd(e.getAffectedItem().getName());
424            }
425        }
426    
427        /**
428         * Update the internal model and inform any listeners according to the received event.
429         *
430         * <p>This method is public as an implementation detail and must not be called directly.</p>
431         *
432         * @override Never
433         */
434        public void rolledbackAddCatalogItem(CatalogChangeEvent e) {
435            if (e.getBasket() == m_dbBasket) {
436                checkRemove(e.getAffectedItem().getName());
437            }
438        }
439    
440        /**
441         * Update the internal model and inform any listeners according to the received event.
442         *
443         * <p>This method is public as an implementation detail and must not be called directly.</p>
444         *
445         * @override Never
446         */
447        public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {}
448    
449        /**
450         * Update the internal model and inform any listeners according to the received event.
451         *
452         * <p>This method is public as an implementation detail and must not be called directly.</p>
453         *
454         * @override Never
455         */
456        public void noRemoveCatalogItem(CatalogChangeEvent e) {}
457    
458        /**
459         * Update the internal model and inform any listeners according to the received event.
460         *
461         * <p>This method is public as an implementation detail and must not be called directly.</p>
462         *
463         * @override Never
464         */
465        public void removedCatalogItem(CatalogChangeEvent e) {
466            checkRemove(e.getAffectedItem().getName());
467        }
468    
469        /**
470         * Update the internal model and inform any listeners according to the received event.
471         *
472         * <p>This method is public as an implementation detail and must not be called directly.</p>
473         *
474         * @override Never
475         */
476        public void commitedRemoveCatalogItem(CatalogChangeEvent e) {}
477    
478        /**
479         * Update the internal model and inform any listeners according to the received event.
480         *
481         * <p>This method is public as an implementation detail and must not be called directly.</p>
482         *
483         * @override Never
484         */
485        public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) {
486            checkAdd(e.getAffectedItem().getName());
487        }
488    
489        /**
490         * Update the internal model and inform any listeners according to the received event.
491         *
492         * <p>This method is public as an implementation detail and must not be called directly.</p>
493         *
494         * @override Never
495         */
496        public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {}
497    
498        /**
499         * Update the internal model and inform any listeners according to the received event.
500         *
501         * <p>This method is public as an implementation detail and must not be called directly.</p>
502         *
503         * @override Never
504         */
505        public void noEditCatalogItem(CatalogChangeEvent e) {}
506    
507        /**
508         * Update the internal model and inform any listeners according to the received event.
509         *
510         * <p>This method is public as an implementation detail and must not be called directly.</p>
511         *
512         * @override Never
513         */
514        public void editingCatalogItem(CatalogChangeEvent e) {
515            if (e.getBasket() != m_dbBasket) {
516                checkRemove(e.getAffectedItem().getName());
517            } else {
518                e.getAffectedItem().addPropertyChangeListener(this);
519            }
520        }
521    
522        /**
523         * Update the internal model and inform any listeners according to the received event.
524         *
525         * <p>This method is public as an implementation detail and must not be called directly.</p>
526         *
527         * @override Never
528         */
529        public void commitEditCatalogItem(CatalogChangeEvent e) {
530            if (e.getBasket() != m_dbBasket) {
531                checkAdd(e.getAffectedItem().getName());
532            } else {
533                e.getAffectedItem().removePropertyChangeListener(this);
534    
535                updateModel();
536                fireTableDataChanged();
537            }
538        }
539    
540        /**
541         * Update the internal model and inform any listeners according to the received event.
542         *
543         * <p>This method is public as an implementation detail and must not be called directly.</p>
544         *
545         * @override Never
546         */
547        public void rollbackEditCatalogItem(CatalogChangeEvent e) {
548            if (e.getBasket() != m_dbBasket) {
549                checkAdd(e.getAffectedItem().getName());
550            } else {
551                e.getAffectedItem().removePropertyChangeListener(this);
552    
553                updateModel();
554                fireTableDataChanged();
555            }
556        }
557    
558        /**
559         * Update the internal model and inform any listeners according to the received event.
560         *
561         * <p>This method is public as an implementation detail and must not be called directly.</p>
562         *
563         * @override Never
564         */
565        public void propertyChange(PropertyChangeEvent e) {
566            if (e.getSource()instanceof CatalogItem) {
567                checkUpdate(((CatalogItem)e.getSource()).getName());
568            }
569        }
570    
571        /**
572         * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the
573         * internal model.
574         *
575         * @param ci the added CatalogItem
576         *
577         * @override Never
578         */
579        protected synchronized void checkAdd(String sKey) {
580            updateModel();
581    
582            int nIdx = m_lKeys.indexOf(sKey);
583    
584            if (nIdx > -1) {
585                fireTableRowsInserted(nIdx, nIdx);
586            }
587        }
588    
589        /**
590         * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect
591         * to the internal model.
592         *
593         * @param ci the removed CatalogItem
594         *
595         * @override Never
596         */
597        protected synchronized void checkRemove(String sKey) {
598            int nIdx = m_lKeys.indexOf(sKey);
599    
600            updateModel();
601    
602            if (nIdx > -1) {
603                fireTableRowsDeleted(nIdx, nIdx);
604            }
605        }
606    
607        /**
608         * Internal helper method. Check for updates in the given CatalogItem.
609         *
610         * @param ci the updated CatalogItem
611         *
612         * @override Never
613         */
614        protected synchronized void checkUpdate(String sKey) {
615            int nIdx1 = m_lKeys.indexOf(sKey);
616    
617            updateModel();
618    
619            int nIdx2 = m_lKeys.indexOf(sKey);
620    
621            if (nIdx1 == -1) {
622                if (nIdx2 > -1) {
623                    fireTableRowsInserted(nIdx2, nIdx2);
624                } else {
625                    return;
626                }
627            } else {
628                if (nIdx2 > -1) {
629                    if (nIdx1 > nIdx2) {
630                        int nTemp = nIdx2;
631                        nIdx2 = nIdx1;
632                        nIdx1 = nTemp;
633                    }
634    
635                    fireTableRowsUpdated(nIdx1, nIdx2);
636                } else {
637                    fireTableRowsDeleted(nIdx1, nIdx1);
638                }
639            }
640        }
641    }
642    /**
643     * Changes:
644     *
645     * 2003/02/27 by ab023578
646     * Changed updateModel(), comparator now returns stockitem, not only stockitem's id
647     */