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