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 }