001 package data.swing;
002
003 import data.*;
004 import data.events.*;
005
006 import util.*;
007 import util.swing.*;
008
009 import java.beans.*;
010 import java.util.*;
011 import java.io.*;
012
013 /**
014 * A {@link javax.swing.table.TableModel} that models the contents of a {@link CountingStock}.
015 *
016 * @author Steffen Zschaler
017 * @version 2.0 23/08/1999
018 * @since v2.0
019 */
020 public class CountingStockTableModel extends AbstractTableModel implements HelpableListener,
021 StockChangeListener, CatalogChangeListener, PropertyChangeListener, Serializable {
022
023 /**
024 * The DataBasket used to determine visibility.
025 *
026 * @serial
027 */
028 protected DataBasket m_dbBasket;
029
030 /**
031 * The CountingStock being modelled. May be {@link data.filters.CountingStockFilter filtered}.
032 *
033 * @serial
034 */
035 protected CountingStock m_csModel;
036
037 /**
038 * The Comparator that defines the sorting order of records in the model. It compares the keys of the
039 * actual items.
040 *
041 * @serial
042 */
043 protected Comparator m_cmpComparator = new NaturalComparator();
044
045 /**
046 * If true, show lines informing about a zero amount of objects.
047 *
048 * @serial
049 */
050 protected boolean m_fShowZeros;
051
052 /**
053 * The internal model. A list of the items' keys.
054 *
055 * @serial
056 */
057 protected List m_lKeys;
058
059 /**
060 * set the table's data. Data is {@link data.CountingStock}
061 */
062 public void setData(Object n_csModel) {
063 m_csModel = (CountingStock) n_csModel;
064 updateModel();
065 fireTableDataChanged();
066 }
067
068 /**
069 * Create a new CountingStockTableModel.
070 *
071 * @param cs the Stock to be modelled.
072 * @param db the DataBasket to be used to determine visibility.
073 * @param cmp the Comparator defining the sorting order. If <code>null</code> the records will be sorted
074 * according to the natural ordering of their keys.
075 * @param fShowZeros if true, lines informing about a zero amount of objects will be shown.
076 * @param ted a TableEntryDescriptor that can split a {@link Record} into a table's cells.
077 */
078 public CountingStockTableModel(CountingStock cs, DataBasket db, Comparator cmp, boolean fShowZeros,
079 TableEntryDescriptor ted) {
080 super(ted);
081
082 m_dbBasket = db;
083 m_csModel = cs;
084
085 if (cmp != null) {
086 m_cmpComparator = cmp;
087 }
088
089 m_fShowZeros = fShowZeros;
090
091 listenerList = new ListenerHelper(this);
092
093 updateModel();
094 }
095
096 /**
097 * A {@link CountingStockTableModel}'s record.
098 *
099 * <p>The record is basically a combination of a {@link CatalogItem} and a number indicating the number of
100 * objects available.</p>
101 *
102 * @author Steffen Zschaler
103 * @version 2.0 23/08/1999
104 * @since v2.0
105 */
106 public static class Record implements Comparable {
107
108 /**
109 * The CatalogItem part of the record.
110 */
111 // Changed 11/09/2000-STEFFEN to private to fix F5.
112 private CatalogItem m_ciDescriptor;
113
114 /**
115 * The number of actually available items.
116 */
117 // Changed 11/09/2000-STEFFEN to private to fix F5.
118 private int m_nCount;
119
120 /**
121 * Create a new Record.
122 */
123 public Record(CatalogItem ci, int nCount) {
124 super();
125
126 m_ciDescriptor = ci;
127 m_nCount = nCount;
128 }
129
130 /**
131 * Compare by descriptor.
132 */
133 public int compareTo(Object o) {
134 return m_ciDescriptor.compareTo(((Record)o).getDescriptor());
135 }
136
137 /**
138 * Get the CatalogItem describing the items represented by this record.
139 */
140 public CatalogItem getDescriptor() {
141 return m_ciDescriptor;
142 }
143
144 /**
145 * Get the number of items in this record.
146 */
147 public int getCount() {
148 return m_nCount;
149 }
150 }
151
152 /**
153 * Get the record at the given index.
154 *
155 * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
156 * @return the {@link Record} to be displayed at the given index. May return <code>null</code> if
157 * either there is no record at the indicated position or an exception occurs.
158 *
159 * @override Never
160 */
161 public Object getRecord(int row) {
162 ((ListenerHelper)listenerList).needModelUpdate();
163
164 try {
165 if ((row > -1) && (row < getRowCount())) {
166 String sKey = (String)m_lKeys.get(row);
167
168 return new Record(m_csModel.getCatalog(m_dbBasket).get(sKey, m_dbBasket, false),
169 m_csModel.countItems(sKey, m_dbBasket));
170 } else {
171 return null;
172 }
173 }
174 catch (VetoException ve) {
175 return null;
176 }
177 }
178
179 /**
180 * Get the number of records in this model.
181 *
182 * @override Never
183 */
184 public int getRowCount() {
185 ((ListenerHelper)listenerList).needModelUpdate();
186
187 return m_lKeys.size();
188 }
189
190 // HelpableListener interface methods
191 /**
192 * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
193 * subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock}, subscribe as
194 * a listener.
195 *
196 * @override Never
197 */
198 public void subscribe() {
199 if (m_csModel instanceof ListenableStock) {
200 ((ListenableStock)m_csModel).addStockChangeListener(this);
201 }
202
203 if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
204 ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).addCatalogChangeListener(this);
205 }
206 }
207
208 /**
209 * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
210 * un-subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock},
211 * un-subscribe as a listener.
212 *
213 * @override Never
214 */
215 public void unsubscribe() {
216 if (m_csModel instanceof ListenableStock) {
217 ((ListenableStock)m_csModel).removeStockChangeListener(this);
218 }
219
220 if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
221 ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).removeCatalogChangeListener(this);
222 }
223 }
224
225 /**
226 * Update the internal model based on the modelled {@link CountingStock}.
227 *
228 * @override Never
229 */
230 public synchronized void updateModel() {
231
232 //Use catalog's keys here, as Stock's keys are deleted if an item's count is 0 (this would cause empty
233 //items not to be shown, even if fShowZeros is true)
234 List lKeys = new LinkedList(m_csModel.getCatalog(m_dbBasket).keySet(m_dbBasket));
235 Collections.sort(lKeys, new Comparator() {
236 public int compare(Object o1, Object o2) {
237 try {
238 return m_cmpComparator.compare(m_csModel.getCatalog(null).get((String)o1, m_dbBasket, false),
239 m_csModel.getCatalog(null).get((String)o2, m_dbBasket, false));
240 }
241 catch (VetoException ve) {
242 System.err.println(ve);
243 return 0;
244 }
245 }
246 });
247 if (!m_fShowZeros) {
248 for (Iterator i = lKeys.iterator(); i.hasNext(); ) {
249 String sKey = (String)i.next();
250
251 if (m_csModel.countItems(sKey, m_dbBasket) == 0) {
252 i.remove();
253 }
254 }
255 }
256
257 m_lKeys = lKeys;
258 }
259
260 // StockChangeListener interface methods
261
262 /**
263 * Update the internal model and inform any listeners according to the received event.
264 *
265 * <p>This method is public as an implementation detail and must not be called directly.</p>
266 *
267 * @override Never
268 */
269 public void addedStockItems(StockChangeEvent e) {
270 if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
271 checkUpdate(e.getAffectedKey());
272 }
273 }
274
275 /**
276 * Update the internal model and inform any listeners according to the received event.
277 *
278 * <p>This method is public as an implementation detail and must not be called directly.</p>
279 *
280 * @override Never
281 */
282 public void commitAddStockItems(StockChangeEvent e) {
283 checkUpdate(e.getAffectedKey());
284 }
285
286 /**
287 * Update the internal model and inform any listeners according to the received event.
288 *
289 * <p>This method is public as an implementation detail and must not be called directly.</p>
290 *
291 * @override Never
292 */
293 public void rollbackAddStockItems(StockChangeEvent e) {
294 if (e.getBasket() == m_dbBasket) {
295 checkUpdate(e.getAffectedKey());
296 }
297 }
298
299 /**
300 * Update the internal model and inform any listeners according to the received event.
301 *
302 * <p>This method is public as an implementation detail and must not be called directly.</p>
303 *
304 * @override Never
305 */
306 public void canRemoveStockItems(StockChangeEvent e) throws VetoException {}
307
308 /**
309 * Update the internal model and inform any listeners according to the received event.
310 *
311 * <p>This method is public as an implementation detail and must not be called directly.</p>
312 *
313 * @override Never
314 */
315 public void noRemoveStockItems(StockChangeEvent e) {}
316
317 /**
318 * Update the internal model and inform any listeners according to the received event.
319 *
320 * <p>This method is public as an implementation detail and must not be called directly.</p>
321 *
322 * @override Never
323 */
324 public void removedStockItems(StockChangeEvent e) {
325 checkUpdate(e.getAffectedKey());
326 }
327
328 /**
329 * Update the internal model and inform any listeners according to the received event.
330 *
331 * <p>This method is public as an implementation detail and must not be called directly.</p>
332 *
333 * @override Never
334 */
335 public void commitRemoveStockItems(StockChangeEvent e) {}
336
337 /**
338 * Update the internal model and inform any listeners according to the received event.
339 *
340 * <p>This method is public as an implementation detail and must not be called directly.</p>
341 *
342 * @override Never
343 */
344 public void rollbackRemoveStockItems(StockChangeEvent e) {
345 checkUpdate(e.getAffectedKey());
346 }
347
348 /**
349 * Update the internal model and inform any listeners according to the received event.
350 *
351 * <p>This method is public as an implementation detail and must not be called directly.</p>
352 *
353 * @override Never
354 */
355 public void canEditStockItems(StockChangeEvent e) throws VetoException {}
356
357 /**
358 * Update the internal model and inform any listeners according to the received event.
359 *
360 * <p>This method is public as an implementation detail and must not be called directly.</p>
361 *
362 * @override Never
363 */
364 public void noEditStockItems(StockChangeEvent e) {}
365
366 /**
367 * Update the internal model and inform any listeners according to the received event.
368 *
369 * <p>This method is public as an implementation detail and must not be called directly.</p>
370 *
371 * @override Never
372 */
373 public void editingStockItems(StockChangeEvent e) {
374 // never fired, we talk about CountingStocks!
375 }
376
377 /**
378 * Update the internal model and inform any listeners according to the received event.
379 *
380 * <p>This method is public as an implementation detail and must not be called directly.</p>
381 *
382 * @override Never
383 */
384 public void commitEditStockItems(StockChangeEvent e) {
385 // never fired!
386 }
387
388 /**
389 * Update the internal model and inform any listeners according to the received event.
390 *
391 * <p>This method is public as an implementation detail and must not be called directly.</p>
392 *
393 * @override Never
394 */
395 public void rollbackEditStockItems(StockChangeEvent e) {
396 // never fired!
397 }
398
399 // CatalogChangeListener interface methods
400
401 /**
402 * Update the internal model and inform any listeners according to the received event.
403 *
404 * <p>This method is public as an implementation detail and must not be called directly.</p>
405 *
406 * @override Never
407 */
408 public void addedCatalogItem(CatalogChangeEvent e) {
409 if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
410 checkAdd(e.getAffectedItem().getName());
411 }
412 }
413
414 /**
415 * Update the internal model and inform any listeners according to the received event.
416 *
417 * <p>This method is public as an implementation detail and must not be called directly.</p>
418 *
419 * @override Never
420 */
421 public void commitedAddCatalogItem(CatalogChangeEvent e) {
422 if (e.getBasket() != m_dbBasket) {
423 checkAdd(e.getAffectedItem().getName());
424 }
425 }
426
427 /**
428 * Update the internal model and inform any listeners according to the received event.
429 *
430 * <p>This method is public as an implementation detail and must not be called directly.</p>
431 *
432 * @override Never
433 */
434 public void rolledbackAddCatalogItem(CatalogChangeEvent e) {
435 if (e.getBasket() == m_dbBasket) {
436 checkRemove(e.getAffectedItem().getName());
437 }
438 }
439
440 /**
441 * Update the internal model and inform any listeners according to the received event.
442 *
443 * <p>This method is public as an implementation detail and must not be called directly.</p>
444 *
445 * @override Never
446 */
447 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {}
448
449 /**
450 * Update the internal model and inform any listeners according to the received event.
451 *
452 * <p>This method is public as an implementation detail and must not be called directly.</p>
453 *
454 * @override Never
455 */
456 public void noRemoveCatalogItem(CatalogChangeEvent e) {}
457
458 /**
459 * Update the internal model and inform any listeners according to the received event.
460 *
461 * <p>This method is public as an implementation detail and must not be called directly.</p>
462 *
463 * @override Never
464 */
465 public void removedCatalogItem(CatalogChangeEvent e) {
466 checkRemove(e.getAffectedItem().getName());
467 }
468
469 /**
470 * Update the internal model and inform any listeners according to the received event.
471 *
472 * <p>This method is public as an implementation detail and must not be called directly.</p>
473 *
474 * @override Never
475 */
476 public void commitedRemoveCatalogItem(CatalogChangeEvent e) {}
477
478 /**
479 * Update the internal model and inform any listeners according to the received event.
480 *
481 * <p>This method is public as an implementation detail and must not be called directly.</p>
482 *
483 * @override Never
484 */
485 public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) {
486 checkAdd(e.getAffectedItem().getName());
487 }
488
489 /**
490 * Update the internal model and inform any listeners according to the received event.
491 *
492 * <p>This method is public as an implementation detail and must not be called directly.</p>
493 *
494 * @override Never
495 */
496 public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {}
497
498 /**
499 * Update the internal model and inform any listeners according to the received event.
500 *
501 * <p>This method is public as an implementation detail and must not be called directly.</p>
502 *
503 * @override Never
504 */
505 public void noEditCatalogItem(CatalogChangeEvent e) {}
506
507 /**
508 * Update the internal model and inform any listeners according to the received event.
509 *
510 * <p>This method is public as an implementation detail and must not be called directly.</p>
511 *
512 * @override Never
513 */
514 public void editingCatalogItem(CatalogChangeEvent e) {
515 if (e.getBasket() != m_dbBasket) {
516 checkRemove(e.getAffectedItem().getName());
517 } else {
518 e.getAffectedItem().addPropertyChangeListener(this);
519 }
520 }
521
522 /**
523 * Update the internal model and inform any listeners according to the received event.
524 *
525 * <p>This method is public as an implementation detail and must not be called directly.</p>
526 *
527 * @override Never
528 */
529 public void commitEditCatalogItem(CatalogChangeEvent e) {
530 if (e.getBasket() != m_dbBasket) {
531 checkAdd(e.getAffectedItem().getName());
532 } else {
533 e.getAffectedItem().removePropertyChangeListener(this);
534
535 updateModel();
536 fireTableDataChanged();
537 }
538 }
539
540 /**
541 * Update the internal model and inform any listeners according to the received event.
542 *
543 * <p>This method is public as an implementation detail and must not be called directly.</p>
544 *
545 * @override Never
546 */
547 public void rollbackEditCatalogItem(CatalogChangeEvent e) {
548 if (e.getBasket() != m_dbBasket) {
549 checkAdd(e.getAffectedItem().getName());
550 } else {
551 e.getAffectedItem().removePropertyChangeListener(this);
552
553 updateModel();
554 fireTableDataChanged();
555 }
556 }
557
558 /**
559 * Update the internal model and inform any listeners according to the received event.
560 *
561 * <p>This method is public as an implementation detail and must not be called directly.</p>
562 *
563 * @override Never
564 */
565 public void propertyChange(PropertyChangeEvent e) {
566 if (e.getSource()instanceof CatalogItem) {
567 checkUpdate(((CatalogItem)e.getSource()).getName());
568 }
569 }
570
571 /**
572 * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the
573 * internal model.
574 *
575 * @param ci the added CatalogItem
576 *
577 * @override Never
578 */
579 protected synchronized void checkAdd(String sKey) {
580 updateModel();
581
582 int nIdx = m_lKeys.indexOf(sKey);
583
584 if (nIdx > -1) {
585 fireTableRowsInserted(nIdx, nIdx);
586 }
587 }
588
589 /**
590 * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect
591 * to the internal model.
592 *
593 * @param ci the removed CatalogItem
594 *
595 * @override Never
596 */
597 protected synchronized void checkRemove(String sKey) {
598 int nIdx = m_lKeys.indexOf(sKey);
599
600 updateModel();
601
602 if (nIdx > -1) {
603 fireTableRowsDeleted(nIdx, nIdx);
604 }
605 }
606
607 /**
608 * Internal helper method. Check for updates in the given CatalogItem.
609 *
610 * @param ci the updated CatalogItem
611 *
612 * @override Never
613 */
614 protected synchronized void checkUpdate(String sKey) {
615 int nIdx1 = m_lKeys.indexOf(sKey);
616
617 updateModel();
618
619 int nIdx2 = m_lKeys.indexOf(sKey);
620
621 if (nIdx1 == -1) {
622 if (nIdx2 > -1) {
623 fireTableRowsInserted(nIdx2, nIdx2);
624 } else {
625 return;
626 }
627 } else {
628 if (nIdx2 > -1) {
629 if (nIdx1 > nIdx2) {
630 int nTemp = nIdx2;
631 nIdx2 = nIdx1;
632 nIdx1 = nTemp;
633 }
634
635 fireTableRowsUpdated(nIdx1, nIdx2);
636 } else {
637 fireTableRowsDeleted(nIdx1, nIdx1);
638 }
639 }
640 }
641 }
642 /**
643 * Changes:
644 *
645 * 2003/02/27 by ab023578
646 * Changed updateModel(), comparator now returns stockitem, not only stockitem's id
647 */