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 Comparable<Record> {
112
113 /**
114 * The CatalogItem part of the record.
115 */
116 // Changed 11/09/2000-STEFFEN to private to fix F5.
117 private CatalogItem m_ciDescriptor;
118
119 /**
120 * The number of actually available items.
121 */
122 // Changed 11/09/2000-STEFFEN to private to fix F5.
123 private int m_nCount;
124
125 /**
126 * Create a new Record.
127 */
128 public Record(CatalogItem ci, int nCount) {
129 super();
130
131 m_ciDescriptor = ci;
132 m_nCount = nCount;
133 }
134
135 /**
136 * Compare by descriptor.
137 */
138 public int compareTo(Record r) {
139 return m_ciDescriptor.compareTo(r.getDescriptor());
140 }
141
142 /**
143 * Get the CatalogItem describing the items represented by this record.
144 */
145 public CatalogItem getDescriptor() {
146 return m_ciDescriptor;
147 }
148
149 /**
150 * Get the number of items in this record.
151 */
152 public int getCount() {
153 return m_nCount;
154 }
155 }
156
157 /**
158 * Get the record at the given index.
159 *
160 * @param row the index for which to retrieve the record. Element of [0, {@link #getRowCount}).
161 * @return the {@link Record} to be displayed at the given index. May return <code>null</code> if
162 * either there is no record at the indicated position or an exception occurs.
163 *
164 * @override Never
165 */
166 public Object getRecord(int row) {
167 ((ListenerHelper)listenerList).needModelUpdate();
168
169 try {
170 if ((row > -1) && (row < getRowCount())) {
171 String sKey = (String)m_lKeys.get(row);
172
173 return new Record(m_csModel.getCatalog(m_dbBasket).get(sKey, m_dbBasket, false),
174 m_csModel.countItems(sKey, m_dbBasket));
175 } else {
176 return null;
177 }
178 }
179 catch (VetoException ve) {
180 return null;
181 }
182 }
183
184 /**
185 * Get the number of records in this model.
186 *
187 * @override Never
188 */
189 public int getRowCount() {
190 ((ListenerHelper)listenerList).needModelUpdate();
191
192 return m_lKeys.size();
193 }
194
195 // HelpableListener interface methods
196 /**
197 * Subscribe as a listener to the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
198 * subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock}, subscribe as
199 * a listener.
200 *
201 * @override Never
202 */
203 public void subscribe() {
204 if (m_csModel instanceof ListenableStock) {
205 ((ListenableStock)m_csModel).addStockChangeListener(this);
206 }
207
208 if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
209 ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).addCatalogChangeListener(this);
210 }
211 }
212
213 /**
214 * Un-Subscribe as a listener from the model. If the modelled {@link Catalog} is a {@link ListenableCatalog},
215 * un-subscribe as a listener. If the modelled {@link CountingStock} is a {@link ListenableStock},
216 * un-subscribe as a listener.
217 *
218 * @override Never
219 */
220 public void unsubscribe() {
221 if (m_csModel instanceof ListenableStock) {
222 ((ListenableStock)m_csModel).removeStockChangeListener(this);
223 }
224
225 if (m_csModel.getCatalog(m_dbBasket)instanceof ListenableCatalog) {
226 ((ListenableCatalog)m_csModel.getCatalog(m_dbBasket)).removeCatalogChangeListener(this);
227 }
228 }
229
230 /**
231 * Update the internal model based on the modelled {@link CountingStock}.
232 *
233 * @override Never
234 */
235 public synchronized void updateModel() {
236
237 //Use catalog's keys here, as Stock's keys are deleted if an item's count is 0 (this would cause empty
238 //items not to be shown, even if fShowZeros is true)
239 List<String> lKeys = new LinkedList<String>(m_csModel.getCatalog(m_dbBasket).keySet(m_dbBasket));
240 Collections.sort(lKeys, new Comparator<String>() {
241 public int compare(String s1, String s2) {
242 try {
243 return m_cmpComparator.compare(m_csModel.getCatalog(null).get(s1, m_dbBasket, false),
244 m_csModel.getCatalog(null).get(s2, m_dbBasket, false));
245 }
246 catch (VetoException ve) {
247 System.err.println(ve);
248 return 0;
249 }
250 }
251 });
252 if (!m_fShowZeros) {
253 for (Iterator i = lKeys.iterator(); i.hasNext(); ) {
254 String sKey = (String)i.next();
255
256 if (m_csModel.countItems(sKey, m_dbBasket) == 0) {
257 i.remove();
258 }
259 }
260 }
261
262 m_lKeys = lKeys;
263 }
264
265 // StockChangeListener interface methods
266
267 /**
268 * Update the internal model and inform any listeners according to the received event.
269 *
270 * <p>This method is public as an implementation detail and must not be called directly.</p>
271 *
272 * @override Never
273 */
274 public void addedStockItems(StockChangeEvent e) {
275 if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
276 checkUpdate(e.getAffectedKey());
277 }
278 }
279
280 /**
281 * Update the internal model and inform any listeners according to the received event.
282 *
283 * <p>This method is public as an implementation detail and must not be called directly.</p>
284 *
285 * @override Never
286 */
287 public void commitAddStockItems(StockChangeEvent e) {
288 checkUpdate(e.getAffectedKey());
289 }
290
291 /**
292 * Update the internal model and inform any listeners according to the received event.
293 *
294 * <p>This method is public as an implementation detail and must not be called directly.</p>
295 *
296 * @override Never
297 */
298 public void rollbackAddStockItems(StockChangeEvent e) {
299 if (e.getBasket() == m_dbBasket) {
300 checkUpdate(e.getAffectedKey());
301 }
302 }
303
304 /**
305 * Update the internal model and inform any listeners according to the received event.
306 *
307 * <p>This method is public as an implementation detail and must not be called directly.</p>
308 *
309 * @override Never
310 */
311 public void canRemoveStockItems(StockChangeEvent e) throws VetoException {}
312
313 /**
314 * Update the internal model and inform any listeners according to the received event.
315 *
316 * <p>This method is public as an implementation detail and must not be called directly.</p>
317 *
318 * @override Never
319 */
320 public void noRemoveStockItems(StockChangeEvent e) {}
321
322 /**
323 * Update the internal model and inform any listeners according to the received event.
324 *
325 * <p>This method is public as an implementation detail and must not be called directly.</p>
326 *
327 * @override Never
328 */
329 public void removedStockItems(StockChangeEvent e) {
330 checkUpdate(e.getAffectedKey());
331 }
332
333 /**
334 * Update the internal model and inform any listeners according to the received event.
335 *
336 * <p>This method is public as an implementation detail and must not be called directly.</p>
337 *
338 * @override Never
339 */
340 public void commitRemoveStockItems(StockChangeEvent e) {}
341
342 /**
343 * Update the internal model and inform any listeners according to the received event.
344 *
345 * <p>This method is public as an implementation detail and must not be called directly.</p>
346 *
347 * @override Never
348 */
349 public void rollbackRemoveStockItems(StockChangeEvent e) {
350 checkUpdate(e.getAffectedKey());
351 }
352
353 /**
354 * Update the internal model and inform any listeners according to the received event.
355 *
356 * <p>This method is public as an implementation detail and must not be called directly.</p>
357 *
358 * @override Never
359 */
360 public void canEditStockItems(StockChangeEvent e) throws VetoException {}
361
362 /**
363 * Update the internal model and inform any listeners according to the received event.
364 *
365 * <p>This method is public as an implementation detail and must not be called directly.</p>
366 *
367 * @override Never
368 */
369 public void noEditStockItems(StockChangeEvent e) {}
370
371 /**
372 * Update the internal model and inform any listeners according to the received event.
373 *
374 * <p>This method is public as an implementation detail and must not be called directly.</p>
375 *
376 * @override Never
377 */
378 public void editingStockItems(StockChangeEvent e) {
379 // never fired, we talk about CountingStocks!
380 }
381
382 /**
383 * Update the internal model and inform any listeners according to the received event.
384 *
385 * <p>This method is public as an implementation detail and must not be called directly.</p>
386 *
387 * @override Never
388 */
389 public void commitEditStockItems(StockChangeEvent e) {
390 // never fired!
391 }
392
393 /**
394 * Update the internal model and inform any listeners according to the received event.
395 *
396 * <p>This method is public as an implementation detail and must not be called directly.</p>
397 *
398 * @override Never
399 */
400 public void rollbackEditStockItems(StockChangeEvent e) {
401 // never fired!
402 }
403
404 // CatalogChangeListener interface methods
405
406 /**
407 * Update the internal model and inform any listeners according to the received event.
408 *
409 * <p>This method is public as an implementation detail and must not be called directly.</p>
410 *
411 * @override Never
412 */
413 public void addedCatalogItem(CatalogChangeEvent e) {
414 if ((e.getBasket() == null) || (e.getBasket() == m_dbBasket)) {
415 checkAdd(e.getAffectedItem().getName());
416 }
417 }
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 commitedAddCatalogItem(CatalogChangeEvent e) {
427 if (e.getBasket() != m_dbBasket) {
428 checkAdd(e.getAffectedItem().getName());
429 }
430 }
431
432 /**
433 * Update the internal model and inform any listeners according to the received event.
434 *
435 * <p>This method is public as an implementation detail and must not be called directly.</p>
436 *
437 * @override Never
438 */
439 public void rolledbackAddCatalogItem(CatalogChangeEvent e) {
440 if (e.getBasket() == m_dbBasket) {
441 checkRemove(e.getAffectedItem().getName());
442 }
443 }
444
445 /**
446 * Update the internal model and inform any listeners according to the received event.
447 *
448 * <p>This method is public as an implementation detail and must not be called directly.</p>
449 *
450 * @override Never
451 */
452 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {}
453
454 /**
455 * Update the internal model and inform any listeners according to the received event.
456 *
457 * <p>This method is public as an implementation detail and must not be called directly.</p>
458 *
459 * @override Never
460 */
461 public void noRemoveCatalogItem(CatalogChangeEvent e) {}
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 removedCatalogItem(CatalogChangeEvent e) {
471 checkRemove(e.getAffectedItem().getName());
472 }
473
474 /**
475 * Update the internal model and inform any listeners according to the received event.
476 *
477 * <p>This method is public as an implementation detail and must not be called directly.</p>
478 *
479 * @override Never
480 */
481 public void commitedRemoveCatalogItem(CatalogChangeEvent e) {}
482
483 /**
484 * Update the internal model and inform any listeners according to the received event.
485 *
486 * <p>This method is public as an implementation detail and must not be called directly.</p>
487 *
488 * @override Never
489 */
490 public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) {
491 checkAdd(e.getAffectedItem().getName());
492 }
493
494 /**
495 * Update the internal model and inform any listeners according to the received event.
496 *
497 * <p>This method is public as an implementation detail and must not be called directly.</p>
498 *
499 * @override Never
500 */
501 public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {}
502
503 /**
504 * Update the internal model and inform any listeners according to the received event.
505 *
506 * <p>This method is public as an implementation detail and must not be called directly.</p>
507 *
508 * @override Never
509 */
510 public void noEditCatalogItem(CatalogChangeEvent e) {}
511
512 /**
513 * Update the internal model and inform any listeners according to the received event.
514 *
515 * <p>This method is public as an implementation detail and must not be called directly.</p>
516 *
517 * @override Never
518 */
519 public void editingCatalogItem(CatalogChangeEvent e) {
520 if (e.getBasket() != m_dbBasket) {
521 checkRemove(e.getAffectedItem().getName());
522 } else {
523 e.getAffectedItem().addPropertyChangeListener(this);
524 }
525 }
526
527 /**
528 * Update the internal model and inform any listeners according to the received event.
529 *
530 * <p>This method is public as an implementation detail and must not be called directly.</p>
531 *
532 * @override Never
533 */
534 public void commitEditCatalogItem(CatalogChangeEvent e) {
535 if (e.getBasket() != m_dbBasket) {
536 checkAdd(e.getAffectedItem().getName());
537 } else {
538 e.getAffectedItem().removePropertyChangeListener(this);
539
540 updateModel();
541 fireTableDataChanged();
542 }
543 }
544
545 /**
546 * Update the internal model and inform any listeners according to the received event.
547 *
548 * <p>This method is public as an implementation detail and must not be called directly.</p>
549 *
550 * @override Never
551 */
552 public void rollbackEditCatalogItem(CatalogChangeEvent e) {
553 if (e.getBasket() != m_dbBasket) {
554 checkAdd(e.getAffectedItem().getName());
555 } else {
556 e.getAffectedItem().removePropertyChangeListener(this);
557
558 updateModel();
559 fireTableDataChanged();
560 }
561 }
562
563 /**
564 * Update the internal model and inform any listeners according to the received event.
565 *
566 * <p>This method is public as an implementation detail and must not be called directly.</p>
567 *
568 * @override Never
569 */
570 public void propertyChange(PropertyChangeEvent e) {
571 if (e.getSource()instanceof CatalogItem) {
572 checkUpdate(((CatalogItem)e.getSource()).getName());
573 }
574 }
575
576 /**
577 * Internal helper method. Check where, if at all, the given CatalogItem has been added with respect to the
578 * internal model.
579 *
580 * @param ci the added CatalogItem
581 *
582 * @override Never
583 */
584 protected synchronized void checkAdd(String sKey) {
585 updateModel();
586
587 int nIdx = m_lKeys.indexOf(sKey);
588
589 if (nIdx > -1) {
590 fireTableRowsInserted(nIdx, nIdx);
591 }
592 }
593
594 /**
595 * Internal helper method. Check from where, if at all, the given CatalogItem has been removed with respect
596 * to the internal model.
597 *
598 * @param ci the removed CatalogItem
599 *
600 * @override Never
601 */
602 protected synchronized void checkRemove(String sKey) {
603 int nIdx = m_lKeys.indexOf(sKey);
604
605 updateModel();
606
607 if (nIdx > -1) {
608 fireTableRowsDeleted(nIdx, nIdx);
609 }
610 }
611
612 /**
613 * Internal helper method. Check for updates in the given CatalogItem.
614 *
615 * @param ci the updated CatalogItem
616 *
617 * @override Never
618 */
619 protected synchronized void checkUpdate(String sKey) {
620 int nIdx1 = m_lKeys.indexOf(sKey);
621
622 updateModel();
623
624 int nIdx2 = m_lKeys.indexOf(sKey);
625
626 if (nIdx1 == -1) {
627 if (nIdx2 > -1) {
628 fireTableRowsInserted(nIdx2, nIdx2);
629 } else {
630 return;
631 }
632 } else {
633 if (nIdx2 > -1) {
634 if (nIdx1 > nIdx2) {
635 int nTemp = nIdx2;
636 nIdx2 = nIdx1;
637 nIdx1 = nTemp;
638 }
639
640 fireTableRowsUpdated(nIdx1, nIdx2);
641 } else {
642 fireTableRowsDeleted(nIdx1, nIdx1);
643 }
644 }
645 }
646 }
647 /**
648 * Changes:
649 *
650 * 2003/02/27 by ab023578
651 * Changed updateModel(), comparator now returns stockitem, not only stockitem's id
652 */