001 package data.ooimpl; 002 003 import java.lang.reflect.*; 004 import java.util.*; 005 import java.io.*; 006 007 import data.*; 008 import data.events.*; 009 010 import util.*; 011 012 /** 013 * Pure Java implementation of the {@link Catalog} interface. 014 * 015 * <p>CatalogImpl can only work together with DataBaskets that are descendants of {@link DataBasketImpl}.</p> 016 * 017 * @author Steffen Zschaler 018 * @version 2.0 19/08/1999 019 * @since v2.0 020 */ 021 public class CatalogImpl extends CatalogItemImpl implements Catalog, ListenableCatalog, NameContext, 022 SelfManagingDBESource, SelfManagingDBEDestination { 023 024 /** 025 * The listeners that registered to be informed of changes in this Catalog's contents. 026 * 027 * @serial 028 */ 029 protected ListenerHelper m_lhListeners = new ListenerHelper(); 030 031 /** 032 * Modification counter. Will be increased by one for each structural modification. 033 * 034 * @serial 035 */ 036 protected int m_nModCount = Integer.MIN_VALUE; 037 038 /** 039 * The items in this catalog. 040 * 041 * @serial 042 */ 043 private Map m_mpciItems = new HashMap(); 044 045 /** 046 * The items that have been temporaryly removed from this Catalog. 047 * 048 * @serial 049 */ 050 private Map m_mpciTemporaryRemovedItems = new HashMap(); 051 052 /** 053 * The items that have been temporaryly added to this Catalog. 054 * 055 * @serial 056 */ 057 private Map m_mpciTemporaryAddedItems = new HashMap(); 058 059 /** 060 * The items that are currently being edited. 061 * 062 * @serial 063 */ 064 private Map m_mpciEditingItems = new HashMap(); 065 066 /** 067 * The original Catalog, if this is a clone created for editing. 068 * 069 * <p>SoftReference, so that garbage collector will take care of it if the EditCreator is no longer used AND 070 * is no longer referenced by any DataBasket or anything else.</p> 071 */ 072 private transient java.lang.ref.SoftReference m_srciEditCreator = null; 073 074 /** 075 * Listener listening to the creator's parent Catalog to know when editing is finished, if this is a 076 * shallow clone created for editing. 077 * 078 * @serial 079 */ 080 private final CatalogChangeListener m_cclEditingListener = new CatalogChangeAdapter() { 081 public void commitEditCatalogItem(CatalogChangeEvent e) { 082 if (e.getAffectedItem() == CatalogImpl.this) { 083 ((CatalogImpl)e.getSource()).removeCatalogChangeListener(this); 084 } 085 } 086 087 public void rollbackEditCatalogItem(CatalogChangeEvent e) { 088 if (e.getAffectedItem() == CatalogImpl.this) { 089 ((CatalogImpl)e.getSource()).removeCatalogChangeListener(this); 090 091 ((CatalogImpl)m_srciEditCreator.get()).removeCatalogChangeListener(m_cclEditCreatorListener); 092 m_srciEditCreator = null; 093 } 094 } 095 }; 096 097 /** 098 * Listener listening to the creator to follow with any commits or rollbacks, if this is a shallow clone 099 * created for editing. 100 * 101 * @serial 102 */ 103 private final CatalogChangeListener m_cclEditCreatorListener = new CatalogChangeAdapter() { 104 public void commitedAddCatalogItem(CatalogChangeEvent e) { 105 synchronized (getItemsLock()) { 106 if (getTemporaryAddedItemsContainer().containsKey(e.getAffectedItem().getName())) { 107 getTemporaryAddedItemsContainer().remove(e.getAffectedItem().getName()); 108 getItemsContainer().put(e.getAffectedItem().getName(), e.getAffectedItem()); 109 110 m_nModCount++; 111 112 ((CatalogItemImpl)e.getAffectedItem()).setCatalog(CatalogImpl.this); 113 fireCatalogItemAddCommit(e.getAffectedItem(), e.getBasket()); 114 } 115 } 116 } 117 118 public void rolledbackAddCatalogItem(CatalogChangeEvent e) { 119 synchronized (getItemsLock()) { 120 if (getTemporaryAddedItemsContainer().containsKey(e.getAffectedItem().getName())) { 121 getTemporaryAddedItemsContainer().remove(e.getAffectedItem().getName()); 122 123 m_nModCount++; 124 125 ((CatalogItemImpl)e.getAffectedItem()).setCatalog(null); fireCatalogItemAddRollback(e. 126 getAffectedItem(), e.getBasket()); 127 } 128 } 129 } 130 131 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException { 132 throw new VetoException("Please use the editable version of the Catalog for that."); 133 } 134 135 public void commitedRemoveCatalogItem(CatalogChangeEvent e) { 136 synchronized (getItemsLock()) { 137 if (getTemporaryRemovedItemsContainer().containsKey(e.getAffectedItem().getName())) { 138 getTemporaryRemovedItemsContainer().remove(e.getAffectedItem().getName()); 139 140 m_nModCount++; 141 142 ((CatalogItemImpl)e.getAffectedItem()).setCatalog(null); fireCatalogItemRemoveCommit(e. 143 getAffectedItem(), e.getBasket()); 144 } 145 } 146 } 147 148 public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) { 149 synchronized (getItemsLock()) { 150 if (getTemporaryRemovedItemsContainer().containsKey(e.getAffectedItem().getName())) { 151 getTemporaryRemovedItemsContainer().remove(e.getAffectedItem().getName()); 152 getItemsContainer().put(e.getAffectedItem().getName(), e.getAffectedItem()); 153 154 m_nModCount++; 155 156 ((CatalogItemImpl)e.getAffectedItem()).setCatalog(CatalogImpl.this); 157 fireCatalogItemRemoveRollback(e.getAffectedItem(), e.getBasket()); 158 } 159 } 160 } 161 162 public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException { 163 throw new VetoException("Please use the editable version of the Catalog for that."); 164 } 165 166 public void commitEditCatalogItem(CatalogChangeEvent e) { 167 synchronized (getItemsLock()) { 168 if (getEditingItemsContainer().get(e.getAffectedItem().getName()) == e.getAffectedItem()) { 169 getEditingItemsContainer().remove(e.getAffectedItem().getName()); 170 171 fireCommitEditCatalogItem(e.getAffectedItem(), e.getBasket()); 172 } 173 } 174 } 175 176 public void rollbackEditCatalogItem(CatalogChangeEvent e) { 177 synchronized (getItemsLock()) { 178 if (getEditingItemsContainer().get(e.getAffectedItem().getName()) == e.getAffectedItem()) { 179 getEditingItemsContainer().remove(e.getAffectedItem().getName()); 180 181 fireRollbackEditCatalogItem(e.getAffectedItem(), e.getBasket()); 182 } 183 } 184 } 185 }; 186 187 /** 188 * Monitor synchronizing access to the several items maps. 189 */ 190 private transient Object m_oItemsLock; 191 /** 192 * Get the monitor synchronizing access to the several items maps. 193 * 194 * @override Never 195 */ 196 protected final Object getItemsLock() { 197 if (m_oItemsLock == null) { 198 m_oItemsLock = new Object(); 199 } 200 201 return m_oItemsLock; 202 } 203 204 /** 205 * First writes the default serializable data and then the reference to the edit creator, if any. 206 */ 207 private void writeObject(ObjectOutputStream oos) throws IOException { 208 oos.defaultWriteObject(); 209 210 if (m_srciEditCreator != null) { 211 oos.writeObject(m_srciEditCreator.get()); 212 } else { 213 oos.writeObject(null); 214 } 215 } 216 217 /** 218 * First reads the default serializable data and then re-establishes the reference to the edit creator, if 219 * any. 220 */ 221 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 222 ois.defaultReadObject(); 223 224 Object oEditCreator = ois.readObject(); 225 226 if (oEditCreator != null) { 227 m_srciEditCreator = new java.lang.ref.SoftReference(oEditCreator); 228 } 229 } 230 231 /** 232 * Create a new, initially empty CatalogImpl. 233 * 234 * @param sName the name of the Catalog. 235 */ 236 public CatalogImpl(String sName) { 237 super(sName); 238 } 239 240 /** 241 * Get the map of items that are completely contained in this Catalog. 242 * 243 * @override Never 244 */ 245 protected Map getItemsContainer() { 246 return m_mpciItems; 247 } 248 249 /** 250 * Get the map of items that have been temporaryly removed from this Catalog. 251 * 252 * @override Never 253 */ 254 protected Map getTemporaryRemovedItemsContainer() { 255 return m_mpciTemporaryRemovedItems; 256 } 257 258 /** 259 * Get the map of items that have been temporaryly added to this Catalog. 260 * 261 * @override Never 262 */ 263 protected Map getTemporaryAddedItemsContainer() { 264 return m_mpciTemporaryAddedItems; 265 } 266 267 /** 268 * Get the map of items that are currently being edited. 269 * 270 * @override Never 271 */ 272 protected Map getEditingItemsContainer() { 273 return m_mpciEditingItems; 274 } 275 276 /** 277 * Set the map of items that are completely contained in this Catalog. 278 * 279 * <p>Must be called from within lock on {@link #getItemsLock}.</p> 280 * 281 * @override Never 282 */ 283 private void setItemsContainer(Map mpNew) { 284 m_mpciItems = mpNew; 285 } 286 287 /** 288 * Set the map of items that have been temporaryly removed from this Catalog. 289 * 290 * <p>Must be called from within lock on {@link #getItemsLock}.</p> 291 * 292 * @override Never 293 */ 294 private void setTemporaryRemovedItemsContainer(Map mpNew) { 295 m_mpciTemporaryRemovedItems = mpNew; 296 } 297 298 /** 299 * Set the map of items that have been temporaryly added to this Catalog. 300 * 301 * <p>Must be called from within lock on {@link #getItemsLock}.</p> 302 * 303 * @override Never 304 */ 305 private void setTemporaryAddedItemsContainer(Map mpNew) { 306 m_mpciTemporaryAddedItems = mpNew; 307 } 308 309 /** 310 * Set the map of items that are currently being edited. 311 * 312 * <p>Must be called from within lock on {@link #getItemsLock}.</p> 313 * 314 * @override Never 315 */ 316 private void setEditingItemsContainer(Map mpNew) { 317 m_mpciEditingItems = mpNew; 318 } 319 320 // Catalog interface methods 321 322 /** 323 * Add the given item to the Catalog. 324 * 325 * @override Never 326 * 327 * @param ci the CatalogItem to be added. 328 * @param db the DataBasket relative to which to perform the operation. Must be <code>null</code> or a 329 * descendant of {@link DataBasketImpl}. 330 * 331 * @exception NotEditableException if the Catalog is currently not editable. 332 * @exception DuplicateKeyException if a CatalogItem of the same name does already exist in the Catalog. 333 * @exception DataBasketConflictException if the CatalogItem cannot be added because an item of the same 334 * name has already been added/removed using another DataBasket. 335 */ 336 public void add(final CatalogItem ci, DataBasket db) { 337 if (!isEditable()) { 338 throw new NotEditableException(); 339 } 340 341 CatalogItemImpl cii = (CatalogItemImpl)ci; 342 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 343 344 synchronized (oLock) { 345 synchronized (getItemsLock()) { 346 //check if ci is part of catalog 347 if (getItemsContainer().containsKey(ci.getName())) { 348 throw new DuplicateKeyException("Key " + ci.getName() + " already existent in Catalog " + 349 getName() + "."); 350 } 351 //check if ci has already been temporarily added 352 if (getTemporaryAddedItemsContainer().containsKey(ci.getName())) { 353 if ((db != null) && (db.contains(new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, 354 ci.getName(), null, this, ci)))) { 355 throw new DuplicateKeyException("Key " + ci.getName() + 356 " already existent in Catalog " + getName() + "."); 357 } else { 358 throw new DataBasketConflictException("CatalogItem with key " + ci.getName() + 359 " already added to Catalog " + getName() + " using a different DataBasket."); 360 } 361 } 362 //check if ci has been temporarily removed 363 if (getTemporaryRemovedItemsContainer().containsKey(ci.getName())) { 364 //dbc describing entry for ci with this catalog as source 365 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, ci.getName(), 366 this, null, ci); 367 368 if ((db != null) && (db.contains(dbc))) { 369 CatalogItemDataBasketEntry cidbe = (CatalogItemDataBasketEntry)db.get(dbc); 370 //if db says, ci has not been added to another catalog, do a rollback 371 if (cidbe.getDestination() == null) { 372 db.rollback(dbc); 373 m_nModCount++; 374 } else { 375 //Cannot rollback a removed item that was added to another Catalog. 376 //We might end up having one CatalogItem in several Catalogs... 377 throw new DataBasketConflictException("CatalogItem with key " + ci.getName() + 378 " has already been temporarily removed from Catalog " + getName() + 379 " and added to another Catalog."); 380 } 381 return; 382 } else { 383 //ci has been removed using a different DataBasket 384 throw new DataBasketConflictException("CatalogItem with key " + ci.getName() + 385 " already removed from Catalog " + getName() + 386 " using a different DataBasket."); 387 } 388 } 389 390 // everything properly checked -> add the stuff 391 if (db != null) { 392 getTemporaryAddedItemsContainer().put(ci.getName(), ci); 393 //dbc that matches ci if it has a source, i.e. has been removed temporarily from another 394 //catalog (move operation, i.e. via TTFS) 395 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, ci.getName(), null, null, null) { 396 public boolean match(DataBasketEntry dbe) { 397 return ((dbe.getDestination() == null) && (dbe.getSource() != null) && 398 (dbe.getValue() == ci)); 399 } 400 }; 401 402 if (db.contains(dbc)) { 403 CatalogItemDataBasketEntry cidbe = (CatalogItemDataBasketEntry)db.get(dbc); 404 //update the already existing DataBasketEntry 405 cidbe.setDestination(this); 406 if (db instanceof ListenableDataBasket) { 407 ((ListenableDataBasket)db).fireDataBasketChanged(); 408 } 409 } else { 410 //create new DataBasketEntry and save it 411 db.put(new CatalogItemDataBasketEntry(null, this, cii)); 412 } 413 } else { 414 // null DataBasket, so add directly 415 getItemsContainer().put(ci.getName(), ci); 416 } 417 m_nModCount++; 418 cii.setCatalog(this); 419 fireCatalogItemAdded(cii, db); 420 } 421 } 422 } 423 424 /** 425 * Remove the given item from the Catalog. 426 * 427 * @override Never 428 * 429 * @param ci the CatalogItem to be removed. 430 * @param db the DataBasket relative to which to perform the operation. Must be <code>null</code> or a 431 * descendant of {@link DataBasketImpl}. 432 * 433 * @exception NotEditableException if the Catalog is currently not editable. 434 * @exception VetoException if one of the listeners vetos the removal. 435 * @exception DataBasketConflictException if the CatalogItem cannot be removed because an item of the same 436 * name has already been added using another DataBasket. 437 */ 438 // MAY HAVE TO CHECK THAT ci IS REALLY CONTAINED IN THE CATALOG (AND NOT JUST ITS KEY) 439 public CatalogItem remove(final CatalogItem ci, DataBasket db) throws VetoException { 440 if (!isEditable()) { 441 throw new NotEditableException(); 442 } 443 444 CatalogItemImpl cii = (CatalogItemImpl)ci; 445 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 446 447 synchronized (oLock) { 448 synchronized (getItemsLock()) { 449 if (getTemporaryAddedItemsContainer().containsKey(ci.getName())) { 450 // the CatalogItem has been added to this Catalog, but only temporarily up to now 451 // Check whether the adding was really about the same item (not just the same key) 452 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, ci.getName(), 453 null, this, ci); 454 455 if ((db != null) && (db.contains(dbc))) { 456 457 CatalogItemDataBasketEntry cidbe = (CatalogItemDataBasketEntry)db.get(dbc); 458 fireCanRemoveCatalogItem(ci, db); 459 if (cidbe.getSource() == null) { //ci has no other Catalog from which it has been removed temporarily 460 db.rollback(dbc); 461 } else {//ci has no other Catalog from which it has been removed temporarily 462 // only rollback the destination part of the the databasketentry 463 cidbe.setDestination(null); 464 465 getTemporaryAddedItemsContainer().remove(ci.getName()); 466 467 m_nModCount++; 468 469 fireCatalogItemAddRollback(ci, db); 470 471 if (db instanceof ListenableDataBasket) { 472 ((ListenableDataBasket)db).fireDataBasketChanged(); 473 } 474 } 475 476 cii.setCatalog(null); 477 478 return ci; 479 } else { 480 // someone was faster than you... 481 throw new DataBasketConflictException("CatalogItem " + ci.getName() + 482 " has only been temporaryly added to Catalog " + getName() + 483 " using a different DataBasket, and can therefore not be removed."); 484 } 485 } 486 487 if (getTemporaryRemovedItemsContainer().containsKey(ci.getName())) { 488 throw new DataBasketConflictException("CatalogItem " + ci.getName() + 489 " already removed from Catalog " + getName() + "."); 490 } 491 492 if (!getItemsContainer().containsKey(ci.getName())) { 493 // the CatalogItem is not in the Catalog and never was, too. 494 return null; 495 } 496 497 // item is in items container and must be transferred into temporarily removed items 498 499 fireCanRemoveCatalogItem(ci, db); 500 501 if (db != null) { 502 getItemsContainer().remove(ci.getName()); 503 getTemporaryRemovedItemsContainer().put(ci.getName(), ci); 504 505 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, ci.getName(), 506 null, null, null) { 507 public boolean match(DataBasketEntry dbe) { 508 return ((dbe.getSource() == null) && (dbe.getValue() == ci)); 509 } 510 }; 511 512 if (db.contains(dbc)) { 513 ((CatalogItemDataBasketEntry)db.get(dbc)).setSource(this); 514 515 if (db instanceof ListenableDataBasket) { 516 ((ListenableDataBasket)db).fireDataBasketChanged(); 517 } 518 } else { 519 db.put(new CatalogItemDataBasketEntry(this, null, (CatalogItemImpl)ci)); 520 } 521 } else { 522 getItemsContainer().remove(ci.getName()); 523 } 524 525 m_nModCount++; 526 cii.setCatalog(null); 527 fireCatalogItemRemoved(ci, db); 528 return ci; 529 } 530 } 531 } 532 533 /** 534 * Remove the indicated item from the Catalog. 535 * 536 * @override Never 537 * 538 * @param sKey the key of the CatalogItem to be removed. 539 * @param db the DataBasket relative to which to perform the operation. Must be <code>null</code> or a 540 * descendant of {@link DataBasketImpl}. 541 * 542 * @exception NotEditableException if the Catalog is currently not editable. 543 * @exception VetoException if one of the listeners vetos the removal. 544 * @exception DataBasketConflictException if the CatalogItem cannot be removed because an item of the same 545 * name has already been added using another DataBasket. 546 */ 547 public CatalogItem remove(String sKey, DataBasket db) throws VetoException { 548 if (!isEditable()) { 549 throw new NotEditableException(); 550 } 551 552 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 553 554 synchronized (oLock) { 555 synchronized (getItemsLock()) { 556 CatalogItem ci = null; 557 558 if (getItemsContainer().containsKey(sKey)) { 559 ci = (CatalogItem)getItemsContainer().get(sKey); 560 } else { 561 if (getTemporaryAddedItemsContainer().containsKey(sKey)) { 562 ci = (CatalogItem)getTemporaryAddedItemsContainer().get(sKey); 563 } else { 564 if (getTemporaryRemovedItemsContainer().containsKey(sKey)) { 565 throw new DataBasketConflictException("CatalogItem " + sKey + 566 " already removed from Catalog " + getName() + "."); 567 } else { 568 return null; 569 } 570 } 571 } 572 573 return remove(ci, db); 574 } 575 } 576 } 577 578 /** 579 * Get the indicated item from the Catalog. 580 * 581 * @override Never 582 * 583 * @param sKey the key for which to retrieve the item. 584 * @param db the DataBasket relative to which to perform the operation. Must be <code>null</code> or a 585 * descendant of {@link DataBasketImpl}. 586 * @param fForEdit if true, the item will be retrieved for editing. Only in this case changes made to the 587 * CatalogItem can be undone via a rollback of the DataBasket. Therefore the DataBasket must not be null 588 * if a CatalogItem is desired to be editable! 589 * A number of events is fired if fForEdit is true: 590 * <ol> 591 * <li><code>canEditCatalogItem</code> with the original item.</li> 592 * <li>a {@link CatalogItemImpl#getShallowClone shallow clone} of the item is created.</li> 593 * <li><code>editingCatalogItem</code> with the newly created clone.</li> 594 * <li><code>addCatalogItem</code> with the newly created clone and <code>removeCatalogItem</code> with 595 * the original item.</li> 596 * <li>The newly created clone is returned for editing.</li> 597 * </ol> 598 * 599 * @exception NotEditableException if the Catalog is not currently editable, but an attempt is made to edit 600 * one of its items. 601 * @exception VetoException if one of the listeners vetos the editing. 602 * @exception DataBasketConflictException if the CatalogItem cannot be retrieved because it is not visible 603 * to users of the given DataBasket. 604 */ 605 public CatalogItem get(String sKey, DataBasket db, boolean fForEdit) throws VetoException { 606 607 // If we aren't editable ourselves, but try to edit an item: TOO BAD! 608 if ((!isEditable()) && (fForEdit)) { 609 throw new NotEditableException(); 610 } 611 612 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 613 synchronized (oLock) { 614 synchronized (getItemsLock()) { 615 // First check, whether item in question was just added by some transaction 616 if (getTemporaryAddedItemsContainer().containsKey(sKey)) { 617 // It was, so it should only be visible to that transaction 618 CatalogItem ci = (CatalogItem)getTemporaryAddedItemsContainer().get(sKey); 619 620 if (db != null) { 621 // Search in db for ci. 622 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, sKey, 623 null, this, ci); 624 625 DataBasketEntry dbe = db.get(dbc); 626 627 if (dbe != null) { 628 629 if (fForEdit) { 630 if (dbe.getSource() != null) { 631 // This is a little too complex for us... 632 throw new DataBasketConflictException( 633 "Cannot edit a CatalogItem that was removed temporarily from its Catalog."); 634 } else { 635 if (!getEditingItemsContainer().containsKey(sKey)) { 636 fireCanEditCatalogItem(ci, db); 637 638 // No need for cloning, was added temporarily only anyway... 639 getEditingItemsContainer().put(sKey, ci); 640 641 fireEditingCatalogItem(ci, db); 642 } 643 644 return ci; 645 } 646 } else { 647 return ci; 648 } 649 } else { 650 return null; 651 } 652 } else { 653 return null; 654 } 655 } 656 657 // Item is not currently under control of any transaction. Does it exist at all? 658 if (getItemsContainer().containsKey(sKey)) { 659 // Yup! Prepare for retrieval 660 CatalogItem ci = (CatalogItem)getItemsContainer().get(sKey); 661 662 if ((db != null) && (fForEdit)) { 663 // Prepare for editing 664 fireCanEditCatalogItem(ci, db); 665 666 // Create shallow clone, which will be editable. 667 CatalogItemImpl cci = ((CatalogItemImpl)ci).getShallowClone(); 668 669 // Reorganize containers 670 getItemsContainer().remove(sKey); 671 getTemporaryRemovedItemsContainer().put(sKey, ci); 672 getTemporaryAddedItemsContainer().put(sKey, cci); 673 674 // Mark as editable 675 getEditingItemsContainer().put(sKey, cci); 676 677 // Store undo/commit data in db 678 db.put(new CatalogItemDataBasketEntry(this, null, (CatalogItemImpl)ci)); 679 db.put(new CatalogItemDataBasketEntry(null, this, cci)); 680 681 // Relink catalog structure 682 ((CatalogItemImpl)ci).setCatalog(null); 683 ((CatalogItemImpl)cci).setCatalog(this); 684 685 // Fire all events 686 fireEditingCatalogItem(cci, db); 687 fireCatalogItemRemoved(ci, db); 688 fireCatalogItemAdded(cci, db); 689 690 // Notify iterators of change 691 m_nModCount++; 692 693 return cci; 694 } else { 695 return ci; 696 } 697 } 698 } 699 } 700 701 return null; 702 } 703 704 /** 705 * Convenience method, which gets an editable ShallowClone of this Catalog from its parent Catalog and 706 * returns it. If the Catalog is already editable or there is no parent Catalog, no copy will be created 707 * and the original Catalog is returned.<br> 708 * @param db the DataBasket which is passed to get method. Must not be null! 709 * @return this Catalog, if editable, otherwise a clone of this Catalog. 710 * @throws VetoException 711 */ 712 public CatalogImpl getEditableCopy(DataBasket db) throws VetoException { 713 if (isEditable()) { 714 return this; 715 } else { 716 CatalogImpl parent = (CatalogImpl)getCatalog(); 717 parent = parent.getEditableCopy(db); 718 return (CatalogImpl)parent.get(getName(), db, true); 719 } 720 } 721 722 /** 723 * Check whether the Catalog contains a certain CatalogItem. 724 * 725 * <p>Will return true only if an item of the given key is contained in the Catalog and if that item is 726 * visible to users of the given DataBasket.</p> 727 * 728 * @override Never 729 * 730 * @param sKey the key for which to check containment. 731 * @param db the DataBasket that defines visibility of items. Must be <code>null</code> or a 732 * descendant of {@link DataBasketImpl}. 733 */ 734 public boolean contains(String sKey, DataBasket db) { 735 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 736 737 synchronized (oLock) { 738 synchronized (getItemsLock()) { 739 if (getItemsContainer().containsKey(sKey)) { 740 return true; 741 } 742 743 if ((db != null) && (getTemporaryAddedItemsContainer().containsKey(sKey))) { 744 CatalogItem ci = (CatalogItem)getTemporaryAddedItemsContainer().get(sKey); 745 746 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, sKey, null, 747 this, ci); 748 749 return (db.contains(dbc)); 750 } 751 752 return false; 753 } 754 } 755 } 756 757 /** 758 * Return an iterator of all items in the Catalog. 759 * 760 * <p>The iterator will conceptually call {@link #get} for each CatalogItem, using the given parameters.</p> 761 * 762 * @override Never 763 * 764 * @param db the DataBasket that defines visibility. 765 * @param fForEdit if true, the items are retrieved for editing. VetoException will be converted into 766 * <code>UnsupportedOperationException</code>s. 767 */ 768 public Iterator iterator(final DataBasket db, final boolean fForEdit) { 769 final Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object())); 770 771 return new Iterator() { 772 private Iterator m_iItems; 773 private int m_nExpectedModCount; 774 private CatalogItem m_ciCurrent; 775 776 { 777 synchronized (oLock) { 778 synchronized (getItemsLock()) { 779 m_iItems = keySet(db).iterator(); 780 781 m_nExpectedModCount = m_nModCount; 782 } 783 } 784 } 785 786 public boolean hasNext() { 787 return m_iItems.hasNext(); 788 } 789 790 public Object next() { 791 synchronized (oLock) { 792 synchronized (getItemsLock()) { 793 if (m_nModCount != m_nExpectedModCount) { 794 throw new ConcurrentModificationException(); 795 } 796 797 String sKey = (String)m_iItems.next(); 798 799 try { 800 m_ciCurrent = CatalogImpl.this.get(sKey, db, fForEdit); 801 802 if (fForEdit) { 803 m_nExpectedModCount = m_nModCount; 804 } 805 } 806 catch (VetoException ve) { 807 throw new UnsupportedOperationException("VETO: " + ve); 808 } 809 810 return m_ciCurrent; 811 } 812 } 813 } 814 815 public void remove() { 816 synchronized (oLock) { 817 synchronized (getItemsLock()) { 818 if (m_nModCount != m_nExpectedModCount) { 819 throw new ConcurrentModificationException(); 820 } 821 822 if (m_ciCurrent == null) { 823 throw new IllegalStateException(); 824 } 825 826 try { 827 CatalogImpl.this.remove(m_ciCurrent, db); 828 829 m_nExpectedModCount = m_nModCount; 830 831 m_ciCurrent = null; 832 } 833 catch (VetoException ve) { 834 m_ciCurrent = null; 835 836 throw new UnsupportedOperationException(); 837 } 838 } 839 } 840 } 841 }; 842 } 843 844 /** 845 * Get a set of all keys currently in the Catalog. 846 * 847 * <p>This will retrieve a static set that gives the state of the Catalog at the time of the call.</p> 848 * 849 * @param db the DataBasket used to determine visibility of elements. Must be <code>null</code> or a 850 * descendant of {@link DataBasketImpl}. 851 * 852 * @override Never 853 */ 854 public Set keySet(DataBasket db) { 855 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 856 synchronized (oLock) { 857 synchronized (getItemsLock()) { 858 Set stReturn = new TreeSet(getItemsContainer().keySet()); 859 860 if (db != null) { 861 for (Iterator i = getTemporaryAddedItemsContainer().values().iterator(); i.hasNext(); ) { 862 final CatalogItem ci = (CatalogItem)i.next(); 863 864 if (!stReturn.contains(ci.getName())) { 865 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, 866 ci.getName(), null, this, ci); 867 if (db.contains(dbc)) { 868 stReturn.add(ci.getName()); 869 } 870 } 871 } 872 } 873 874 return stReturn; 875 } 876 } 877 } 878 879 /** 880 * Calculate the size of the Catalog. I.e. count the CatalogItems that are visible to users of the given 881 * DataBasket. 882 * 883 * @override Never 884 * 885 * @param db the DataBasket used to determine visibility. Must be <code>null</code> or a 886 * descendant of {@link DataBasketImpl}. 887 */ 888 public int size(DataBasket db) { 889 return keySet(db).size(); 890 } 891 892 /** 893 * Set the Catalog that contains this Catalog. 894 * 895 * @override Never 896 */ 897 void setCatalog(CatalogImpl ci) { 898 super.setCatalog(ci); 899 900 if (ci != null) { 901 // necessary, so that children of clones prepared for editing will always know who their 902 // correct parents are. 903 for (Iterator i = getItemsContainer().values().iterator(); i.hasNext(); ) { 904 ((CatalogItemImpl)i.next()).setCatalog(this); 905 } 906 907 for (Iterator i = getTemporaryAddedItemsContainer().values().iterator(); i.hasNext(); ) { 908 ((CatalogItemImpl)i.next()).setCatalog(this); 909 } 910 } 911 } 912 913 /** 914 * Create a shallow clone of this Catalog. A shallow clone means that the individual items themselves will 915 * not be cloned. 916 * 917 * @override Never Instead override {@link #createPeer}. 918 */ 919 protected CatalogItemImpl getShallowClone() { 920 CatalogImpl ci = createPeer(); 921 synchronized (getItemsLock()) { 922 synchronized (ci.getItemsLock()) { 923 ci.setItemsContainer(new HashMap(getItemsContainer())); 924 ci.setEditingItemsContainer(new HashMap(getEditingItemsContainer())); 925 ci.setTemporaryAddedItemsContainer(new HashMap(getTemporaryAddedItemsContainer())); 926 ci.setTemporaryRemovedItemsContainer(new HashMap(getTemporaryRemovedItemsContainer())); 927 } 928 } 929 // attach as listener to this CatalogImpl 930 ci.m_srciEditCreator = new java.lang.ref.SoftReference(this); 931 addCatalogChangeListener(ci.m_cclEditCreatorListener); 932 if (getCatalog() != null) { 933 ((CatalogImpl)getCatalog()).addCatalogChangeListener(ci.m_cclEditingListener); 934 } 935 return ci; 936 } 937 938 /** 939 * Create and return an empty CatalogImpl of the same name and class. 940 * 941 * @override Always 942 */ 943 public CatalogImpl createPeer() { 944 return new CatalogImpl(getName()); 945 } 946 947 /** 948 * Return a {@link String} representation of this Catalog. 949 * 950 * @override Sometimes 951 */ 952 public String toString() { 953 synchronized (getItemsLock()) { 954 String sReturn = "Catalog \"" + getName() + "\" ["; 955 956 boolean fFirst = true; 957 for (Iterator i = iterator (null, false); i.hasNext();) { 958 sReturn += ((fFirst)?(""):(", ")) + i.next(); 959 fFirst = false; 960 } 961 return sReturn + "]"; 962 } 963 } 964 965 966 // SelfManagingDBESource interface methods 967 968 /** 969 * Commit the removal of a CatalogItem. 970 */ 971 public void commitRemove(DataBasket db, DataBasketEntry dbe) { 972 // The databasket already locks on its own monitor, so we just lock on ours. 973 synchronized (getItemsLock()) { 974 if (dbe.getSource() == this) {//check necessary if method called directly and not via db.commit() 975 CatalogItemImpl cii = (CatalogItemImpl)dbe.getValue(); 976 977 getTemporaryRemovedItemsContainer().remove(cii.getName()); 978 979 if (cii.getCatalog() == this) { 980 cii.setCatalog(null); 981 } 982 983 ((CatalogItemDataBasketEntry)dbe).setSource(null); 984 985 fireCatalogItemRemoveCommit(cii, db); 986 } 987 } 988 } 989 990 /** 991 * Roll back the removal of a CatalogItem. 992 */ 993 public void rollbackRemove(DataBasket db, DataBasketEntry dbe) { 994 // the databasket already locks on its own monitor, so we just lock on ours. 995 synchronized (getItemsLock()) { 996 if (dbe.getSource() == this) {//check necessary if method called directly and not via db.rollback() 997 CatalogItemImpl cii = (CatalogItemImpl)dbe.getValue(); 998 999 if (getTemporaryRemovedItemsContainer().get(cii.getName()) == cii) { 1000 getTemporaryRemovedItemsContainer().remove(cii.getName()); 1001 getItemsContainer().put(cii.getName(), cii); 1002 cii.setCatalog(this); 1003 1004 m_nModCount++; 1005 1006 fireCatalogItemRemoveRollback(cii, db); 1007 } 1008 1009 ((CatalogItemDataBasketEntry)dbe).setSource(null); 1010 } 1011 } 1012 } 1013 1014 // SelfManagingDBEDestination interface methods 1015 /** 1016 * Commit the adding of a CatalogItem. In addition to the <code>addedCatalogItemCommit</code> event this 1017 * may trigger an <code>editingCatalogItemCommit</code> event with the CatalogItem that has been edited. 1018 */ 1019 public void commitAdd(DataBasket db, DataBasketEntry dbe) { 1020 // the databasket already locks on its own monitor, so we just lock on ours. 1021 synchronized (getItemsLock()) { 1022 if (dbe.getDestination() == this) { //check necessary if method called directly and not via db.commit() 1023 CatalogItemImpl cii = (CatalogItemImpl)dbe.getValue(); 1024 1025 if (getTemporaryAddedItemsContainer().get(cii.getName()) == cii) { 1026 getTemporaryAddedItemsContainer().remove(cii.getName()); 1027 1028 getItemsContainer().put(cii.getName(), cii); 1029 cii.setCatalog(this); 1030 1031 m_nModCount++; 1032 1033 fireCatalogItemAddCommit(cii, db); 1034 1035 if (getEditingItemsContainer().remove(cii.getName()) != null) { 1036 fireCommitEditCatalogItem(cii, db); 1037 } 1038 } 1039 1040 ((CatalogItemDataBasketEntry)dbe).setDestination(null); 1041 } 1042 } 1043 } 1044 1045 /** 1046 * Roll back the adding of a CatalogItem. In addition to the <code>addedCatalogItemRollback</code> event 1047 * this may trigger an <code>editingCatalogItemRollback</code> event with the CatalogItem that has been 1048 * edited. 1049 */ 1050 public void rollbackAdd(DataBasket db, DataBasketEntry dbe) { 1051 // the databasket already locks on its own monitor, so we just lock on ours. 1052 synchronized (getItemsLock()) { 1053 if (dbe.getDestination() == this) {//check necessary if method called directly and not via db.rollback() 1054 CatalogItemImpl cii = (CatalogItemImpl)dbe.getValue(); 1055 1056 getTemporaryAddedItemsContainer().remove(cii.getName()); 1057 1058 if (cii.getCatalog() == this) { 1059 cii.setCatalog(null); 1060 } 1061 1062 ((CatalogItemDataBasketEntry)dbe).setDestination(null); 1063 1064 m_nModCount++; 1065 1066 fireCatalogItemAddRollback(cii, db); 1067 1068 if (getEditingItemsContainer().remove(cii.getName()) != null) { 1069 fireRollbackEditCatalogItem(cii, db); 1070 } 1071 } 1072 } 1073 } 1074 1075 // NameContext interface methods 1076 /** 1077 * Check a name change of a CatalogItem in this Catalog. 1078 * 1079 * <p>The name change will be allowed if the item is editable and the new name can be guaranteed to be 1080 * unique.</p> 1081 * 1082 * @override Sometimes Override to enforce stricter naming conventions. 1083 */ 1084 public void checkNameChange(DataBasket db, String sOldName, String sNewName) throws NameContextException { 1085 1086 if (sOldName == sNewName) { 1087 return; 1088 } 1089 1090 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 1091 1092 synchronized (oLock) { 1093 synchronized (getItemsLock()) { 1094 if (!getEditingItemsContainer().containsKey(sOldName)) { 1095 throw new NameContextException( 1096 "Item must be made editable before you can change its name!"); 1097 } 1098 1099 if ((getTemporaryRemovedItemsContainer().containsKey(sNewName)) || 1100 (getItemsContainer().containsKey(sNewName)) || 1101 (getTemporaryAddedItemsContainer().containsKey(sNewName))) { 1102 throw new NameContextException("Name conflict: name \"" + sNewName + 1103 "\" already existent (though maybe temporarily removed!)"); 1104 } 1105 1106 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, sOldName, null, 1107 this, null); 1108 1109 if ((db == null) || (!db.contains(dbc))) { 1110 throw new NameContextException("DataBasket conflict: No corresponding item with name \"" + 1111 sOldName + "\" in the given DataBasket."); 1112 } 1113 } 1114 } 1115 } 1116 1117 /** 1118 * Synchronize the Catalog's internal data with the name change. 1119 * 1120 * @override Never 1121 */ 1122 public void nameHasChanged(DataBasket db, String sOldName, String sNewName) { 1123 if (sOldName == sNewName) { 1124 return; 1125 } 1126 1127 Object oLock = (db == null) ? (new Object()) : (((DataBasketImpl)db).getDBIMonitor()); 1128 1129 synchronized (oLock) { 1130 synchronized (getItemsLock()) { 1131 getEditingItemsContainer().put(sNewName, getEditingItemsContainer().remove(sOldName)); 1132 getTemporaryAddedItemsContainer().put(sNewName, 1133 getTemporaryAddedItemsContainer().remove(sOldName)); 1134 1135 DataBasketCondition dbc = new DataBasketConditionImpl(CATALOG_ITEM_MAIN_KEY, sOldName, null, this, null); 1136 DataBasketEntry dbe = db.get(dbc); 1137 db.exchange(dbe, new CatalogItemDataBasketEntry(null, this, (CatalogItemImpl)dbe.getValue())); 1138 1139 m_nModCount++; 1140 } 1141 } 1142 } 1143 1144 /** 1145 * Return the monitor used to synchronize access to the Catalog's internal data. 1146 * 1147 * @override Never 1148 */ 1149 public final Object getNCMonitor() { 1150 return getItemsLock(); 1151 } 1152 1153 // ListenableCatalog interface methods 1154 1155 /** 1156 * Add a listener that listens for changes in this Catalog's contents. 1157 * 1158 * @override Never 1159 * 1160 * @param ccl the listener 1161 */ 1162 public void addCatalogChangeListener(CatalogChangeListener ccl) { 1163 m_lhListeners.add(CatalogChangeListener.class, ccl); 1164 } 1165 1166 /** 1167 * Remove a listener that listened for changes in this Catalog's contents. 1168 * 1169 * @override Never 1170 * 1171 * @param ccl the listener 1172 */ 1173 public void removeCatalogChangeListener(CatalogChangeListener ccl) { 1174 m_lhListeners.remove(CatalogChangeListener.class, ccl); 1175 } 1176 1177 /** 1178 * Fire an event to all listeners listening to this Catalog. 1179 * 1180 * @override Never 1181 */ 1182 protected void fireCatalogItemAdded(CatalogItem ci, DataBasket db) { 1183 Object[] listeners = m_lhListeners.getListenerList(); 1184 CatalogChangeEvent cce = null; 1185 1186 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1187 if (listeners[i] == CatalogChangeListener.class) { 1188 if (cce == null) { 1189 cce = new CatalogChangeEvent(this, ci, db); 1190 } 1191 1192 ((CatalogChangeListener)listeners[i + 1]).addedCatalogItem(cce); 1193 } 1194 } 1195 } 1196 1197 /** 1198 * Fire an event to all listeners listening to this Catalog. 1199 * 1200 * @override Never 1201 */ 1202 protected void fireCatalogItemAddCommit(CatalogItem ci, DataBasket db) { 1203 Object[] listeners = m_lhListeners.getListenerList(); 1204 CatalogChangeEvent cce = null; 1205 1206 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1207 if (listeners[i] == CatalogChangeListener.class) { 1208 if (cce == null) { 1209 cce = new CatalogChangeEvent(this, ci, db); 1210 } 1211 1212 ((CatalogChangeListener)listeners[i + 1]).commitedAddCatalogItem(cce); 1213 } 1214 } 1215 } 1216 1217 /** 1218 * Fire an event to all listeners listening to this Catalog. 1219 * 1220 * @override Never 1221 */ 1222 protected void fireCatalogItemAddRollback(CatalogItem ci, DataBasket db) { 1223 Object[] listeners = m_lhListeners.getListenerList(); 1224 CatalogChangeEvent cce = null; 1225 1226 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1227 if (listeners[i] == CatalogChangeListener.class) { 1228 if (cce == null) { 1229 cce = new CatalogChangeEvent(this, ci, db); 1230 } 1231 1232 ((CatalogChangeListener)listeners[i + 1]).rolledbackAddCatalogItem(cce); 1233 } 1234 } 1235 } 1236 1237 /** 1238 * Fire an event to all listeners listening to this Catalog. 1239 * 1240 * @override Never 1241 */ 1242 protected void fireCatalogItemRemoved(CatalogItem ci, DataBasket db) { 1243 Object[] listeners = m_lhListeners.getListenerList(); 1244 CatalogChangeEvent cce = null; 1245 1246 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1247 if (listeners[i] == CatalogChangeListener.class) { 1248 if (cce == null) { 1249 cce = new CatalogChangeEvent(this, ci, db); 1250 } 1251 1252 ((CatalogChangeListener)listeners[i + 1]).removedCatalogItem(cce); 1253 } 1254 } 1255 } 1256 1257 /** 1258 * Fire an event to all listeners listening to this Catalog. 1259 * 1260 * @override Never 1261 */ 1262 protected void fireCatalogItemRemoveCommit(CatalogItem ci, DataBasket db) { 1263 Object[] listeners = m_lhListeners.getListenerList(); 1264 CatalogChangeEvent cce = null; 1265 1266 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1267 if (listeners[i] == CatalogChangeListener.class) { 1268 if (cce == null) { 1269 cce = new CatalogChangeEvent(this, ci, db); 1270 } 1271 1272 ((CatalogChangeListener)listeners[i + 1]).commitedRemoveCatalogItem(cce); 1273 } 1274 } 1275 } 1276 1277 /** 1278 * Fire an event to all listeners listening to this Catalog. 1279 * 1280 * @override Never 1281 */ 1282 protected void fireCatalogItemRemoveRollback(CatalogItem ci, DataBasket db) { 1283 Object[] listeners = m_lhListeners.getListenerList(); 1284 CatalogChangeEvent cce = null; 1285 1286 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1287 if (listeners[i] == CatalogChangeListener.class) { 1288 if (cce == null) { 1289 cce = new CatalogChangeEvent(this, ci, db); 1290 } 1291 1292 ((CatalogChangeListener)listeners[i + 1]).rolledbackRemoveCatalogItem(cce); 1293 } 1294 } 1295 } 1296 1297 /** 1298 * Fire an event to all listeners listening to this Catalog. 1299 * 1300 * @override Never 1301 */ 1302 protected void fireCanRemoveCatalogItem(CatalogItem ci, DataBasket db) throws VetoException { 1303 Object[] temp = m_lhListeners.getListenerList(); 1304 Object[] listeners = new Object[temp.length]; 1305 System.arraycopy(temp, 0, listeners, 0, temp.length); 1306 1307 CatalogChangeEvent cce = null; 1308 1309 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1310 if (listeners[i] == CatalogChangeListener.class) { 1311 if (cce == null) { 1312 cce = new CatalogChangeEvent(this, ci, db); 1313 } 1314 1315 try { 1316 ((CatalogChangeListener)listeners[i + 1]).canRemoveCatalogItem(cce); 1317 } 1318 catch (VetoException e) { 1319 for (int j = i; j < listeners.length; j += 2) { 1320 if (listeners[j] == CatalogChangeListener.class) { 1321 ((CatalogChangeListener)listeners[j + 1]).noRemoveCatalogItem(cce); 1322 } 1323 } 1324 1325 throw e; 1326 } 1327 } 1328 } 1329 } 1330 1331 /** 1332 * Fire an event to all listeners listening to this Catalog. 1333 * 1334 * @override Never 1335 */ 1336 protected void fireCanEditCatalogItem(CatalogItem ci, DataBasket db) throws VetoException { 1337 Object[] temp = m_lhListeners.getListenerList(); 1338 Object[] listeners = new Object[temp.length]; 1339 System.arraycopy(temp, 0, listeners, 0, temp.length); 1340 1341 CatalogChangeEvent cce = null; 1342 1343 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1344 if (listeners[i] == CatalogChangeListener.class) { 1345 if (cce == null) { 1346 cce = new CatalogChangeEvent(this, ci, db); 1347 } 1348 1349 try { 1350 ((CatalogChangeListener)listeners[i + 1]).canEditCatalogItem(cce); 1351 } 1352 catch (VetoException e) { 1353 for (int j = i; j < listeners.length; j += 2) { 1354 if (listeners[j] == CatalogChangeListener.class) { 1355 ((CatalogChangeListener)listeners[j + 1]).noEditCatalogItem(cce); 1356 } 1357 } 1358 1359 throw e; 1360 } 1361 } 1362 } 1363 } 1364 1365 /** 1366 * Fire an event to all listeners listening to this Catalog. 1367 * 1368 * @override Never 1369 */ 1370 protected void fireEditingCatalogItem(CatalogItem ci, DataBasket db) { 1371 Object[] listeners = m_lhListeners.getListenerList(); 1372 1373 CatalogChangeEvent cce = null; 1374 1375 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1376 if (listeners[i] == CatalogChangeListener.class) { 1377 if (cce == null) { 1378 cce = new CatalogChangeEvent(this, ci, db); 1379 } 1380 1381 ((CatalogChangeListener)listeners[i + 1]).editingCatalogItem(cce); 1382 } 1383 } 1384 } 1385 1386 /** 1387 * Fire an event to all listeners listening to this Catalog. 1388 * 1389 * @override Never 1390 */ 1391 protected void fireCommitEditCatalogItem(CatalogItem ci, DataBasket db) { 1392 Object[] listeners = m_lhListeners.getListenerList(); 1393 1394 CatalogChangeEvent cce = null; 1395 1396 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1397 if (listeners[i] == CatalogChangeListener.class) { 1398 if (cce == null) { 1399 cce = new CatalogChangeEvent(this, ci, db); 1400 } 1401 1402 ((CatalogChangeListener)listeners[i + 1]).commitEditCatalogItem(cce); 1403 } 1404 } 1405 } 1406 1407 /** 1408 * Fire an event to all listeners listening to this Catalog. 1409 * 1410 * @override Never 1411 */ 1412 protected void fireRollbackEditCatalogItem(CatalogItem ci, DataBasket db) { 1413 Object[] listeners = m_lhListeners.getListenerList(); 1414 1415 CatalogChangeEvent cce = null; 1416 1417 for (int i = listeners.length - 2; i >= 0; i -= 2) { 1418 if (listeners[i] == CatalogChangeListener.class) { 1419 if (cce == null) { 1420 cce = new CatalogChangeEvent(this, ci, db); 1421 } 1422 1423 ((CatalogChangeListener)listeners[i + 1]).rollbackEditCatalogItem(cce); 1424 } 1425 } 1426 } 1427 }