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 */