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