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