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