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 }