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