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 }