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