001 package data.swing;
002
003 import java.util.*;
004 import java.io.*;
005 import java.beans.*;
006
007 import util.*;
008 import util.swing.*;
009
010 import data.*;
011 import data.events.*;
012
013 /**
014 * A {@link javax.swing.table.TableModel} that models the contents of a {@link Catalog}.
015 *
016 * @author Steffen Zschaler
017 * @version 2.0 23/08/1999
018 * @since v2.0
019 */
020 public class CatalogTableModel extends AbstractTableModel implements HelpableListener, CatalogChangeListener,
021 PropertyChangeListener, Serializable {
022
023 /**
024 * The DataBasket used to determine visibility.
025 *
026 * @serial
027 */
028 protected DataBasket m_dbBasket;
029
030 /**
031 * The Catalog that is being modelled.
032 *
033 * @serial
034 */
035 protected Catalog m_cModel;
036
037 /**
038 * The Comparator that defines the sorting order of records in the model. It compares
039 * {@link CatalogItem CatalogItems}.
040 *
041 * @serial
042 */
043 protected Comparator m_cmpComparator = new NaturalComparator();
044
045 /**
046 * The internal model. A list of the CatalogItems' keys.
047 *
048 * @serial
049 */
050 protected List m_lKeys;
051
052 /**
053 * Set the table's data. Data is {@link data.Catalog}
054 */
055 public void setData(Object n_cModel) {
056 m_cModel = (Catalog) n_cModel;
057 updateModel();
058 fireTableDataChanged();
059 }
060
061 public Comparator getComparator() {
062 return m_cmpComparator;
063 }
064
065 /**
066 * Create a new CatalogTableModel.
067 *
068 * @param c the Catalog to be modelled. May be {@link data.filters.CatalogFilter filtered}.
069 * @param db the DataBasket to be used to determine visibility.
070 * @param cmp a Comparator defining the sort order of the records. If <code>null</code>, records are ordered
071 * according to the natural ordering of the CatalogItems.
072 * @param ted a TableEntryDescriptor that can split individual CatalogItems into a table's cells.
073 */
074 public CatalogTableModel(Catalog c, DataBasket db, Comparator cmp, TableEntryDescriptor ted) {
075 super(ted);
076
077 m_dbBasket = db;
078 m_cModel = c;
079
080 if (cmp != null) {
081 m_cmpComparator = cmp;
082 }
083
084 listenerList = new ListenerHelper(this);
085
086 updateModel();
087 }
088
089 /**
090 * Get the record at the given index.
091 *
092 * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
093 * @return the {@link CatalogItem} to be displayed at the given index. May return <code>null</code> if
094 * either there is no record at the indicated position or an exception occurs.
095 *
096 * @override Never
097 */
098 public Object getRecord(int row) {
099 ((ListenerHelper)listenerList).needModelUpdate();
100
101 try {
102 if ((row > -1) && (row < getRowCount())) {
103 return m_cModel.get((String)m_lKeys.get(row), m_dbBasket, false);
104 } else {
105 return null;
106 }
107 }
108 catch (VetoException ex) {
109 return null;
110 }
111 }
112
113 /**
114 * Get the number of records in this model.
115 *
116 * @override Never
117 */
118 public int getRowCount() {
119 ((ListenerHelper)listenerList).needModelUpdate();
120
121 return m_lKeys.size();
122 }
123
124 // HelpableListener interface methods
125
126 /**
127 * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
128 * subscribe as a listener.
129 *
130 * @override Never
131 */
132 public void subscribe() {
133 if (m_cModel instanceof ListenableCatalog) {
134 ((ListenableCatalog)m_cModel).addCatalogChangeListener(this);
135 }
136 }
137
138 /**
139 * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
140 * un-subscribe as a listener.
141 *
142 * @override Never
143 */
144 public void unsubscribe() {
145 if (m_cModel instanceof ListenableCatalog) {
146 ((ListenableCatalog)m_cModel).removeCatalogChangeListener(this);
147 }
148 }
149
150 /**
151 * Update the internal model based on the modelled {@link Catalog}.
152 *
153 * @override Never
154 */
155 public synchronized void updateModel() {
156 List lKeys = new LinkedList(m_cModel.keySet(m_dbBasket));
157 Collections.sort(lKeys, new Comparator() {
158 public int compare(Object o1, Object o2) {
159 try {
160 return m_cmpComparator.compare(m_cModel.get((String)o1, m_dbBasket, false),
161 m_cModel.get((String)o2, m_dbBasket, false));
162 }
163 catch (VetoException ex) {
164 return 0;
165 }
166 }
167 });
168 m_lKeys = lKeys;
169 }
170
171 // CatalogChangeListener interface methods
172
173 /**
174 * Update the internal model and inform any listeners according to the received event.
175 *
176 * <p>This method is public as an implementation detail and must not be called directly.</p>
177 *
178 * @override Never
179 */
180 public void addedCatalogItem(CatalogChangeEvent e) {
181 if ((e.getBasket() == m_dbBasket) || (e.getBasket() == null)) {
182 checkAdd(e.getAffectedItem());
183 }
184 }
185
186 /**
187 * Update the internal model and inform any listeners according to the received event.
188 *
189 * <p>This method is public as an implementation detail and must not be called directly.</p>
190 *
191 * @override Never
192 */
193 public void commitedAddCatalogItem(CatalogChangeEvent e) {
194 checkAdd(e.getAffectedItem());
195 }
196
197 /**
198 * Update the internal model and inform any listeners according to the received event.
199 *
200 * <p>This method is public as an implementation detail and must not be called directly.</p>
201 *
202 * @override Never
203 */
204 public void rolledbackAddCatalogItem(CatalogChangeEvent e) {
205 if (e.getBasket() == m_dbBasket) {
206 checkRemove(e.getAffectedItem());
207 }
208 }
209
210 /**
211 * Update the internal model and inform any listeners according to the received event.
212 *
213 * <p>This method is public as an implementation detail and must not be called directly.</p>
214 *
215 * @override Never
216 */
217 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {}
218
219 /**
220 * Update the internal model and inform any listeners according to the received event.
221 *
222 * <p>This method is public as an implementation detail and must not be called directly.</p>
223 *
224 * @override Never
225 */
226 public void noRemoveCatalogItem(CatalogChangeEvent e) {}
227
228 /**
229 * Update the internal model and inform any listeners according to the received event.
230 *
231 * <p>This method is public as an implementation detail and must not be called directly.</p>
232 *
233 * @override Never
234 */
235 public void removedCatalogItem(CatalogChangeEvent e) {
236 checkRemove(e.getAffectedItem());
237 }
238
239 /**
240 * Update the internal model and inform any listeners according to the received event.
241 *
242 * <p>This method is public as an implementation detail and must not be called directly.</p>
243 *
244 * @override Never
245 */
246 public void commitedRemoveCatalogItem(CatalogChangeEvent e) {}
247
248 /**
249 * Update the internal model and inform any listeners according to the received event.
250 *
251 * <p>This method is public as an implementation detail and must not be called directly.</p>
252 *
253 * @override Never
254 */
255 public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) {
256 checkAdd(e.getAffectedItem());
257 }
258
259 /**
260 * Update the internal model and inform any listeners according to the received event.
261 *
262 * <p>This method is public as an implementation detail and must not be called directly.</p>
263 *
264 * @override Never
265 */
266 public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {}
267
268 /**
269 * Update the internal model and inform any listeners according to the received event.
270 *
271 * <p>This method is public as an implementation detail and must not be called directly.</p>
272 *
273 * @override Never
274 */
275 public void noEditCatalogItem(CatalogChangeEvent e) {}
276
277 /**
278 * Update the internal model and inform any listeners according to the received event.
279 *
280 * <p>This method is public as an implementation detail and must not be called directly.</p>
281 *
282 * @override Never
283 */
284 public void editingCatalogItem(CatalogChangeEvent e) {
285 if (e.getBasket() != m_dbBasket) {
286 checkRemove(e.getAffectedItem());
287 } else {
288 e.getAffectedItem().addPropertyChangeListener(this);
289 }
290 }
291
292 /**
293 * Update the internal model and inform any listeners according to the received event.
294 *
295 * <p>This method is public as an implementation detail and must not be called directly.</p>
296 *
297 * @override Never
298 */
299 public void commitEditCatalogItem(CatalogChangeEvent e) {
300 if (e.getBasket() != m_dbBasket) {
301 checkAdd(e.getAffectedItem());
302 } else {
303 e.getAffectedItem().removePropertyChangeListener(this);
304
305 updateModel();
306 }
307 }
308
309 /**
310 * Update the internal model and inform any listeners according to the received event.
311 *
312 * <p>This method is public as an implementation detail and must not be called directly.</p>
313 *
314 * @override Never
315 */
316 public void rollbackEditCatalogItem(CatalogChangeEvent e) {
317 if (e.getBasket() != m_dbBasket) {
318 checkAdd(e.getAffectedItem());
319 } else {
320 e.getAffectedItem().removePropertyChangeListener(this);
321
322 updateModel();
323 fireTableDataChanged();
324 }
325 }
326
327 /**
328 * Update the internal model and inform any listeners according to the received event.
329 *
330 * <p>This method is public as an implementation detail and must not be called directly.</p>
331 *
332 * @override Never
333 */
334 public void propertyChange(PropertyChangeEvent e) {
335 if (e.getSource()instanceof CatalogItem) {
336 checkUpdate((CatalogItem)e.getSource());
337 }
338 }
339
340 /**
341 * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the
342 * internal model.
343 *
344 * @param ci the added CatalogItem
345 *
346 * @override Never
347 */
348 protected synchronized void checkAdd(CatalogItem ci) {
349 updateModel();
350
351 int nIdx = m_lKeys.indexOf(ci.getName());
352
353 if (nIdx > -1) {
354 fireTableRowsInserted(nIdx, nIdx);
355 }
356 }
357
358 /**
359 * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect
360 * to the internal model.
361 *
362 * @param ci the removed CatalogItem
363 *
364 * @override Never
365 */
366 protected synchronized void checkRemove(CatalogItem ci) {
367 int nIdx = m_lKeys.indexOf(ci.getName());
368
369 updateModel();
370
371 if (nIdx > -1) {
372 fireTableRowsDeleted(nIdx, nIdx);
373 }
374 }
375
376 /**
377 * Internal helper method. Check for updates in the given CatalogItem.
378 *
379 * @param ci the updated CatalogItem
380 *
381 * @override Never
382 */
383 protected synchronized void checkUpdate(CatalogItem ci) {
384 int nIdx1 = m_lKeys.indexOf(ci.getName());
385
386 updateModel();
387
388 int nIdx2 = m_lKeys.indexOf(ci.getName());
389
390 if (nIdx1 == -1) {
391 if (nIdx2 > -1) {
392 fireTableRowsInserted(nIdx2, nIdx2);
393 } else {
394 return;
395 }
396 } else {
397 if (nIdx2 == -1) {
398 fireTableRowsDeleted(nIdx1, nIdx1);
399 } else {
400 if (nIdx1 > nIdx2) {
401 int nTemp = nIdx2;
402 nIdx2 = nIdx1;
403 nIdx1 = nIdx2;
404 }
405
406 fireTableRowsUpdated(nIdx1, nIdx2);
407 }
408 }
409 }
410 }