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 DataBasket}.
014     *
015     * @author Steffen Zschaler
016     * @version 2.0 23/08/1999
017     * @since v2.0
018     */
019    public class DataBasketTableModel extends AbstractTableModel implements DataBasketListener, HelpableListener,
020            Serializable {
021    
022        /**
023         * The DataBasket being modelled.
024         *
025         * @serial
026         */
027        protected DataBasket m_dbBasket;
028    
029        /**
030         * The condition specifying the items to be displayed.
031         *
032         * @serial
033         */
034        protected DataBasketCondition m_dbcCondition;
035    
036        /**
037         * A strategy that will group individual DataBasketEntries together for display. If <code>null</code>, no
038         * grouping will occur.
039         *
040         * @serial
041         */
042        protected DataBasketEntryGrouper m_dbegGrouper;
043    
044        /**
045         * The Comparator that defines the sorting order of records in the model. It compares
046         * {@link DataBasketEntry DataBasketEntries}.
047         *
048         * @serial
049         */
050        protected Comparator m_cmpComparator = new SerializableComparator() {
051            public int compare(Object o1, Object o2) {
052                DataBasketEntry dbe1 = (DataBasketEntry)o1;
053                DataBasketEntry dbe2 = (DataBasketEntry)o2;
054    
055                int nRet = dbe1.getMainKey().compareTo(dbe2.getMainKey());
056    
057                if (nRet != 0) {
058                    return nRet;
059                }
060    
061                if ((dbe1.getSecondaryKey()instanceof Comparable) && (dbe2.getSecondaryKey()instanceof Comparable)) {
062                    return ((Comparable)dbe1.getSecondaryKey()).compareTo(dbe2.getSecondaryKey());
063                }
064    
065                return 0;
066            }
067        };
068    
069        /**
070         * The internal model. A list of the DataBasketEntries.
071         *
072         * @serial
073         */
074        protected List m_lEntries;
075        
076        /**
077         * set the table's data. Data is {@link data.DataBasket}
078         */
079        public void setData(Object n_dbBasket) {
080            m_dbBasket = (DataBasket) n_dbBasket;
081            updateModel();
082            fireTableDataChanged();         
083        }
084    
085        /**
086         * Create a new DataBasketTableModel.
087         *
088         * @param db the DataBasket to be modellled.
089         * @param dbc a condition specifying the DataBasketEntries to be part of the model.
090         * @param dbeg a strategy that will group individual DataBasketEntries together for display. If
091         * <code>null</code>, no grouping will occur.
092         * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered
093         * according to the main key of the entries first and of the secondary key second.
094         * @param ted a TableEntryDescriptor that can split individual DataBasketEntries into a table's cells.
095         */
096        public DataBasketTableModel(DataBasket db, DataBasketCondition dbc, DataBasketEntryGrouper dbeg,
097                Comparator cmp, TableEntryDescriptor ted) {
098            super(ted);
099    
100            m_dbBasket = db;
101            m_dbcCondition = dbc;
102            m_dbegGrouper = dbeg;
103    
104            if (cmp != null) {
105                m_cmpComparator = cmp;
106            }
107    
108            updateModel();
109    
110            listenerList = new ListenerHelper(this);
111        }
112    
113        /**
114         * Get the record at the given index.
115         *
116         * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
117         * @return the {@link DataBasketEntry} to be displayed at the given index. May return <code>null</code> if
118         * there is no record at the indicated position.
119         *
120         * @override Never
121         */
122        public Object getRecord(int row) {
123            ((ListenerHelper)listenerList).needModelUpdate();
124    
125            if ((row > -1) && (row < getRowCount())) {
126                return m_lEntries.get(row);
127            } else {
128                return null;
129            }
130        }
131    
132        /**
133         * Get the number of records in this model.
134         *
135         * @override Never
136         */
137        public int getRowCount() {
138            ((ListenerHelper)listenerList).needModelUpdate();
139    
140            return m_lEntries.size();
141        }
142    
143        // HelpableListener interface methods
144    
145        /**
146         * Subscribe as a listener to the model. If the modelled {@link DataBasket} is a
147         * {@link ListenableDataBasket}, subscribe as a listener.
148         *
149         * @override Never
150         */
151        public void subscribe() {
152            if (m_dbBasket instanceof ListenableDataBasket) {
153                ((ListenableDataBasket)m_dbBasket).addDataBasketListener(this);
154            }
155        }
156    
157        /**
158         * Un-Subscribe as a listener from the model. If the modelled {@link DataBasket} is a
159         * {@link ListenableDataBasket}, un-subscribe as a listener.
160         *
161         * @override Never
162         */
163        public void unsubscribe() {
164            if (m_dbBasket instanceof ListenableDataBasket) {
165                ((ListenableDataBasket)m_dbBasket).removeDataBasketListener(this);
166            }
167        }
168    
169        /**
170         * Update the internal model based on the modelled {@link DataBasket}.
171         *
172         * @override Never
173         */
174        public synchronized void updateModel() {
175            List lEntries = new LinkedList();
176    
177            for (Iterator i = m_dbBasket.iterator(m_dbcCondition); i.hasNext(); ) {
178                lEntries.add(i.next());
179            }
180    
181            List lGroupedEntries = null;
182    
183            if (m_dbegGrouper != null) {
184                lGroupedEntries = new LinkedList();
185    
186                while (true) {
187                    Iterator i = lEntries.iterator();
188    
189                    if (i.hasNext()) {
190                        DataBasketEntry dbeGrouped = (DataBasketEntry)i.next();
191                        i.remove();
192    
193                        while (i.hasNext()) {
194                            DataBasketEntry dbe2 = (DataBasketEntry)i.next();
195    
196                            if (m_dbegGrouper.canGroup(dbeGrouped, dbe2)) {
197                                i.remove();
198    
199                                dbeGrouped = m_dbegGrouper.group(dbeGrouped, dbe2);
200                            }
201                        }
202    
203                        lGroupedEntries.add(dbeGrouped);
204                    } else {
205                        break;
206                    }
207                }
208            } else {
209                lGroupedEntries = lEntries;
210            }
211            Collections.sort(lGroupedEntries, m_cmpComparator);
212    
213            m_lEntries = lGroupedEntries;
214        }
215    
216        // DataBasketListener interface methods
217    
218        /**
219         * Update the internal model and inform any listeners according to the received event.
220         *
221         * <p>This method is public as an implementation detail and must not be called directly.</p>
222         *
223         * @override Never
224         */
225        public void addedDBE(DataBasketEvent e) {
226            updateModel();
227            fireTableDataChanged();
228        }
229    
230        /**
231         * Update the internal model and inform any listeners according to the received event.
232         *
233         * <p>This method is public as an implementation detail and must not be called directly.</p>
234         *
235         * @override Never
236         */
237        public void removedDBE(DataBasketEvent e) {
238            updateModel();
239            fireTableDataChanged();
240        }
241    
242        /**
243         * Update the internal model and inform any listeners according to the received event.
244         *
245         * <p>This method is public as an implementation detail and must not be called directly.</p>
246         *
247         * @override Never
248         */
249        public void dataBasketChanged(DataBasketEvent e) {
250            updateModel();
251            fireTableDataChanged();
252        }
253    }