001 package data.ooimpl;
002
003 import data.*;
004 import data.events.*;
005
006 import java.util.*;
007
008 /**
009 * Pure Java implementation of the {@link StoringStock} interface.
010 *
011 * @author Steffen Zschaler
012 * @version 2.0 19/08/1999
013 * @since v2.0
014 */
015 public class StoringStockImpl extends StockImpl implements StoringStock {
016
017 /**
018 * Modification counter. Increases by one with every structural modification.
019 *
020 * @serial
021 */
022 protected int m_nModCount = Integer.MIN_VALUE;
023
024 /**
025 * Listens to the Stock's Catalog to ensure referential integrity.
026 *
027 * @serial
028 */
029 protected CatalogChangeListener m_cclReferentialIntegrityListener;
030
031 /**
032 * true, if StockImpl's CatalogItemNameListener was already replaced by an enhanced version.
033 * Used internally only.
034 *
035 * @serial
036 */
037 private boolean m_fChangedCatalogItemNameListener = false;
038
039 /**
040 * Enhanced CatalogItemNameListener, updating the names of the actual StockItems whenever a name change
041 * occurs.
042 *
043 * @author Steffen Zschaler
044 * @version 2.0 19/08/1999
045 * @since v2.0
046 */
047 class SSICatalogItemNameListener extends CatalogItemNameListener {
048 protected void nameChangeOccurred(String sOld, String sNew) {
049 super.nameChangeOccurred(sOld, sNew);
050
051 List lAdded = (List)getTemporaryAddedItemsContainer().get(sNew);
052 if (lAdded != null) {
053 for (Iterator i = lAdded.iterator(); i.hasNext(); ) {
054 StockItemImpl sii = (StockItemImpl)i.next();
055
056 sii.internalSetName(sNew);
057 }
058 }
059
060 List lItems = (List)getItemsContainer().get(sNew);
061 if (lItems != null) {
062 for (Iterator i = lItems.iterator(); i.hasNext(); ) {
063 StockItemImpl sii = (StockItemImpl)i.next();
064
065 sii.internalSetName(sNew);
066 }
067 }
068
069 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sNew);
070 if (lRemoved != null) {
071 for (Iterator i = lRemoved.iterator(); i.hasNext(); ) {
072 StockItemImpl sii = (StockItemImpl)i.next();
073
074 sii.internalSetName(sNew);
075 }
076 }
077
078 List lRefIntegr = (List)getRefIntegrItemsContainer().get(sNew);
079 if (lRefIntegr != null) {
080 for (Iterator i = lRefIntegr.iterator(); i.hasNext(); ) {
081 StockItemImpl sii = (StockItemImpl)i.next();
082
083 sii.internalSetName(sNew);
084 }
085 }
086 }
087 }
088
089 /**
090 * Create a new, initially empty StoringStockImpl.
091 *
092 * @param sName the name of the new Stock.
093 * @param ciRef the Catalog that is being referenced by the Stock.
094 */
095 public StoringStockImpl(String sName, CatalogImpl ciRef) {
096 super(sName, ciRef);
097
098 // Enhanced version.
099 m_sclEditCreatorListener = new StockChangeAdapter() {
100 public void commitAddStockItems(StockChangeEvent e) {
101 synchronized (getItemsLock()) {
102 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
103
104 List lAdded = (List)getTemporaryAddedItemsContainer().get(sii.getName());
105
106 if (lAdded == null) {
107 return;
108 }
109
110 if (lAdded.remove(sii)) {
111 List lItems = (List)getItemsContainer().get(sii.getName());
112
113 if (lItems == null) {
114 lItems = new LinkedList();
115 getItemsContainer().put(sii.getName(), lItems);
116 }
117 lItems.add(sii);
118
119 sii.setStock(StoringStockImpl.this);
120
121 fireStockItemsAddCommit(new StoringStockChangeEvent(StoringStockImpl.this, sii,
122 e.getBasket()));
123 }
124 }
125 }
126
127 public void rollbackAddStockItems(StockChangeEvent e) {
128 synchronized (getItemsLock()) {
129 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
130
131 List lAdded = (List)getTemporaryAddedItemsContainer().get(sii.getName());
132
133 if (lAdded == null) {
134 return;
135 }
136
137 if (lAdded.remove(sii)) {
138 if (sii.getStock() == StoringStockImpl.this) {
139 sii.setStock(null);
140 }
141
142 fireStockItemsAddRollback(new StoringStockChangeEvent(StoringStockImpl.this, sii,
143 e.getBasket()));
144 }
145 }
146 }
147
148 public void canRemoveStockItems(StockChangeEvent e) throws VetoException {
149 throw new VetoException("Please use the editable version for this!");
150 }
151
152 public void commitRemoveStockItems(StockChangeEvent e) {
153 synchronized (getItemsLock()) {
154 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
155
156 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sii.getName());
157
158 if (lRemoved == null) {
159 return;
160 }
161
162 if (lRemoved.remove(sii)) {
163 if (sii.getStock() == StoringStockImpl.this) {
164 sii.setStock(null);
165 }
166
167 fireStockItemsRemoveCommit(new StoringStockChangeEvent(StoringStockImpl.this, sii,
168 e.getBasket()));
169 }
170 }
171 }
172
173 public void rollbackRemoveStockItems(StockChangeEvent e) {
174 synchronized (getItemsLock()) {
175 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
176
177 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sii.getName());
178
179 if (lRemoved == null) {
180 return;
181 }
182
183 if (lRemoved.remove(sii)) {
184 List lItems = (List)getItemsContainer().get(sii.getName());
185
186 if (lItems == null) {
187 lItems = new LinkedList();
188 getItemsContainer().put(sii.getName(), lItems);
189 }
190 lItems.add(sii);
191
192 sii.setStock(StoringStockImpl.this);
193
194 fireStockItemsRemoveCommit(new StoringStockChangeEvent(StoringStockImpl.this, sii,
195 e.getBasket()));
196 }
197 }
198 }
199
200 public void canEditStockItems(StockChangeEvent e) throws VetoException {
201 throw new VetoException("Please use the editable version for this!");
202 }
203
204 public void commitEditStockItems(StockChangeEvent e) {
205 synchronized (getItemsLock()) {
206 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
207
208 List lEditing = (List)getEditingItemsContainer().get(sii.getName());
209
210 if (lEditing == null) {
211 return;
212 }
213
214 if (lEditing.remove(sii)) {
215 fireStockItemsEditCommit(new StoringStockChangeEvent(StoringStockImpl.this, sii,
216 e.getBasket()));
217 }
218 }
219 }
220
221 public void rollbackEditStockItems(StockChangeEvent e) {
222 synchronized (getItemsLock()) {
223 StockItemImpl sii = (StockItemImpl)e.getAffectedItems().next();
224
225 List lEditing = (List)getEditingItemsContainer().get(sii.getName());
226
227 if (lEditing == null) {
228 return;
229 }
230
231 if (lEditing.remove(sii)) {
232 fireStockItemsEditRollback(new StoringStockChangeEvent(StoringStockImpl.this, sii,
233 e.getBasket()));
234 }
235 }
236 }
237 };
238 }
239
240 /**
241 * Overridden because of referential integrity.
242 *
243 * @override Never
244 */
245 protected void internalSetCatalog(CatalogImpl ciRef) {
246 if (m_ciCatalog != null) {
247 m_ciCatalog.removeCatalogChangeListener(m_cclReferentialIntegrityListener);
248 }
249
250 if (!m_fChangedCatalogItemNameListener) {
251 m_fChangedCatalogItemNameListener = true;
252
253 m_cinlCatalogItemNameListener = new SSICatalogItemNameListener();
254 }
255
256 super.internalSetCatalog(ciRef);
257
258 if (m_ciCatalog != null) {
259 if (m_cclReferentialIntegrityListener == null) {
260 initReferentialIntegrityListener();
261 }
262
263 m_ciCatalog.addCatalogChangeListener(m_cclReferentialIntegrityListener);
264 }
265 }
266
267 /**
268 * Internal helper function.
269 *
270 * @override Never
271 */
272 private void initReferentialIntegrityListener() {
273 m_cclReferentialIntegrityListener = new CatalogChangeAdapter() {
274 public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {
275 // DataBasket already locks on its monitor
276 synchronized (getItemsLock()) {
277 String sKey = e.getAffectedItem().getName();
278 DataBasket db = e.getBasket();
279
280 List lAdded = (List)getTemporaryAddedItemsContainer().get(sKey);
281 if ((lAdded != null) && (lAdded.size() > 0)) {
282 throw new VetoException("Stock \"" + getName() +
283 "\": Having temporarily added items of key \"" + sKey + "\".");
284 }
285
286 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sKey);
287 if (lRemoved != null) {
288 if ((db == null) && (lRemoved.size() > 0)) {
289 throw new VetoException("Stock \"" + getName() +
290 "\": Having temporarily removed items that are in a different DataBasket.");
291 }
292
293 for (Iterator i = lRemoved.iterator(); i.hasNext(); ) {
294 StockItem si = (StockItem)i.next();
295
296 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey,
297 StoringStockImpl.this, null, si);
298 if (!db.contains(dbc)) {
299 throw new VetoException("Stock \"" + getName() +
300 "\": Having temporarily removed items that are in a different DataBasket.");
301 }
302 }
303 }
304
305 List lItems = (List)getItemsContainer().get(sKey);
306 if ((lItems != null) && (lItems.size() > 0)) {
307 List lRefIntegr = new LinkedList(lItems);
308 getRefIntegrItemsContainer().put(sKey, lRefIntegr);
309
310 for (Iterator i = lRefIntegr.iterator(); i.hasNext(); ) {
311 remove((StockItem)i.next(), db);
312 }
313 }
314 }
315 }
316
317 public void noRemoveCatalogItem(CatalogChangeEvent e) {
318 synchronized (getItemsLock()) {
319 String sKey = e.getAffectedItem().getName();
320 DataBasket db = e.getBasket();
321
322 List lRefIntegr = (List)getRefIntegrItemsContainer().remove(sKey);
323 if (lRefIntegr != null) {
324 for (Iterator i = lRefIntegr.iterator(); i.hasNext(); ) {
325 add((StockItem)i.next(), db);
326 }
327 }
328 }
329 }
330
331 public void removedCatalogItem(CatalogChangeEvent e) {
332 synchronized (getItemsLock()) {
333 // clean up
334 getRefIntegrItemsContainer().remove(e.getAffectedItem().getName());
335 }
336 }
337
338 public void commitedRemoveCatalogItem(CatalogChangeEvent e) {
339 synchronized (getItemsLock()) {
340 if (!((Catalog)e.getSource()).contains(e.getAffectedItem().getName(), e.getBasket())) {
341 ciGoneForEver(e);
342 }
343 }
344 }
345
346 public void rollbackAddCatalogItem(CatalogChangeEvent e) {
347 synchronized (getItemsLock()) {
348 DataBasketCondition dbc = new DataBasketConditionImpl(CatalogItemImpl.
349 CATALOG_ITEM_MAIN_KEY, e.getAffectedItem().getName(), (CatalogImpl)e.getSource(), null, null);
350
351 if (!e.getBasket().contains(dbc)) {
352 ciGoneForEver(e);
353 }
354 }
355 }
356
357 private void ciGoneForEver(CatalogChangeEvent e) {
358 String sKey = e.getAffectedItem().getName();
359 DataBasket db = e.getBasket();
360
361 if (getTemporaryAddedItemsContainer().containsKey(sKey)) {
362 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey, null,
363 StoringStockImpl.this, null);
364
365 // Rollback all items temporarily added to this Stock
366 // StoringStocks produce DataBasketEntries that may have both source and dest set,
367 // so we must rollback only the destination part.
368 for (Iterator i = db.iterator(dbc); i.hasNext(); ) {
369 ((StoringStockItemDBEntry)i.next()).rollbackDestination();
370 }
371 }
372
373 getItemsContainer().remove(sKey);
374 getRefIntegrItemsContainer().remove(sKey);
375
376 if (getTemporaryRemovedItemsContainer().containsKey(sKey)) {
377 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey,
378 StoringStockImpl.this, null, null);
379
380 // Commit all items temporaryly removed from this Stock
381 // StoringStocks produce DataBasketEntries that may have both source and dest set,
382 // so we must commit only the source part.
383 for (Iterator i = db.iterator(dbc); i.hasNext(); ) {
384 ((StoringStockItemDBEntry)i.next()).commitSource();
385 }
386 }
387 }
388
389 // The actual instance of the associated Catalog will have changed, for children of the given key that
390 // are Stocks.
391 public void editingCatalogItem(CatalogChangeEvent e) {
392 relinkCatalog(e, STARTEDIT_ACTION);
393 }
394
395 // The actual instance of the associated Catalog will have to be changed back.
396 public void rollbackEditCatalogItem(CatalogChangeEvent e) {
397 relinkCatalog(e, ROLLBACK_ACTION);
398 }
399
400 public void commitEditCatalogItem(CatalogChangeEvent e) {
401 relinkCatalog(e, COMMIT_ACTION);
402 }
403
404 void relinkCatalog(CatalogChangeEvent e, int nAction) {
405 DataBasket db = e.getBasket();
406
407 synchronized (getItemsLock()) {
408 List l = (List)getItemsContainer().get(e.getAffectedItem().getName());
409
410 if (l != null) {
411 for (Iterator i = l.iterator(); i.hasNext(); ) {
412 StockItemImpl sii = (StockItemImpl)i.next();
413
414 sii.relinkCatalog(db, nAction);
415 }
416 }
417
418 l = (List)getTemporaryAddedItemsContainer().get(e.getAffectedItem().getName());
419
420 if (l != null) {
421 for (Iterator i = l.iterator(); i.hasNext(); ) {
422 StockItemImpl sii = (StockItemImpl)i.next();
423
424 sii.relinkCatalog(db, nAction);
425 }
426 }
427 }
428 }
429
430 public void rollbackRemoveCatalogItem(CatalogChangeEvent e) {
431 reEstablishStockCatalogLink(e.getAffectedItem().getName());
432 }
433 };
434 }
435
436 /**
437 * Private helper function re-establishing the Stock-Catalog connection if any items in this Stock should be
438 * Stocks themselves.
439 *
440 * @override Never
441 */
442 private void reEstablishStockCatalogLink(String sKey) {
443 synchronized (getItemsLock()) {
444 List lAdded = (List)getTemporaryAddedItemsContainer().get(sKey);
445 if (lAdded != null) {
446 for (Iterator i = lAdded.iterator(); i.hasNext(); ) {
447 // we call setStock again, which for other Stocks will adjust their Catalog link accordingly.
448 ((StockItemImpl)i.next()).setStock(StoringStockImpl.this);
449 }
450 }
451
452 List lItems = (List)getItemsContainer().get(sKey);
453 if (lItems != null) {
454 for (Iterator i = lItems.iterator(); i.hasNext(); ) {
455 // we call setStock again, which for other Stocks will adjust their Catalog link accordingly.
456 ((StockItemImpl)i.next()).setStock(StoringStockImpl.this);
457 }
458 }
459 }
460 }
461
462 // Stock interface methods
463
464 /**
465 * Add an item to the Stock.
466 *
467 * <p>The item will only be visible to users of the same DataBasket. Only after a {@link DataBasket#commit}
468 * was performed on the DataBasket, the item will become visible to other users.</p>
469 *
470 * <p>A <code>addedStockItems</code> event will be fired.</p>
471 *
472 * @param si the item to be added.
473 * @param db the DataBasket relative to which the item will be added. Must be either <code>null</code> or a
474 * descendant of {@link DataBasketImpl}.
475 *
476 * @override Never
477 *
478 * @exception CatalogConflictException if the items key is not contained in the corresponding {@link Catalog}.
479 * @exception DataBasketConflictException if the item has already been added/removed using another DataBasket.
480 */
481 public void add(StockItem si, DataBasket db) {
482 Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object()));
483
484 synchronized (oLock) {
485 synchronized (getItemsLock()) {
486 if ((getCatalog(db) != null) && (!getCatalog(db).contains(si.getName(), db))) {
487 throw new CatalogConflictException("Couldn't find key \"" + si.getName() +
488 "\" in Catalog \"" + getCatalog(db).getName() + "\"");
489 }
490
491 List lAdded = (List)getTemporaryAddedItemsContainer().get(si.getName());
492 if ((lAdded != null) && (lAdded.contains(si))) {
493 throw new DataBasketConflictException("Cannot add item that has already been added.");
494 }
495
496 List lItems = (List)getItemsContainer().get(si.getName());
497 if ((lItems != null) && (lItems.contains(si))) {
498 return;
499 }
500
501 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(si.getName());
502 if ((lRemoved != null) && (lRemoved.contains(si))) {
503
504 DataBasketCondition dbc = DataBasketConditionImpl.specificStockItem((StockItem)lRemoved.
505 get(lRemoved.indexOf(si)));
506
507 if ((db == null) || (!db.contains(dbc))) {
508 throw new DataBasketConflictException(
509 "Cannot add item that was removed using a different DataBasket.");
510 } else {
511 DataBasketEntry dbe = db.get(dbc);
512
513 if (dbe.getDestination() == null) {
514 // just rollback the prior remove action!
515
516 db.rollback(dbc);
517
518 return;
519 } else {
520 throw new DataBasketConflictException(
521 "Cannot add item that was removed and added to another Stock!");
522 }
523 }
524 }
525
526 // all checked, so add the stuff
527 if (db != null) {
528 if (lAdded == null) {
529 lAdded = new LinkedList();
530 getTemporaryAddedItemsContainer().put(si.getName(), lAdded);
531 }
532
533 lAdded.add(si);
534
535 // put information into databasket! Make sure there's only one DBE for each StockItem!
536 DataBasketCondition dbc = DataBasketConditionImpl.specificStockItem(si);
537 StockItemDBEntry sidbe = (StockItemDBEntry)db.get(dbc);
538
539 if (sidbe != null) {
540 db.exchange(sidbe, new StoringStockItemDBEntry((StoringStockImpl)sidbe.getSource(), this,
541 (StockItemImpl)si));
542 } else {
543 db.put(new StoringStockItemDBEntry(null, this, (StockItemImpl)si));
544 }
545
546 } else {
547 if (lItems == null) {
548 lItems = new LinkedList();
549 getItemsContainer().put(si.getName(), lItems);
550 }
551
552 lItems.add(si);
553 }
554
555 m_nModCount++;
556
557 ((StockItemImpl)si).setStock(this);
558 fireStockItemsAdded(new StoringStockChangeEvent(this, (StockItemImpl)si, db));
559 }
560 }
561 }
562
563 /**
564 * Iterate all items with a given key.
565 *
566 * <p>This method, together with {@link Stock#iterator} is the only way of accessing the individual
567 * {@link StockItem StockItems} contained in a Stock. The iterator will deliver all items that have the
568 * specified key and are visible using the given DataBasket. Depending on the <code>fForEdit</code>
569 * parameter, the items will be retrieved in different ways. See {@link DataBasket} for an explanation of
570 * the different possibilities.</p>
571 *
572 * <p><code>canEditStockItems</code> and <code>editingStockItems</code> events will be fired if
573 * <code>fForEdit</code> == true. {@link VetoException VetoExceptions} will be converted into
574 * <code>UnSupportedOperationException</code>s.</p>
575 *
576 * @override Never
577 *
578 * @param sKey the key for which to retrieve the StockItems.
579 * @param db the DataBasket relative to which to retrieve the StockItems. Must be either <code>null</code>
580 * or a descendant of {@link DataBasketImpl}.
581 * @param fForEdit if true, the StockItems will be retrieved for editing.
582 */
583 public Iterator get(final String sKey, final DataBasket db, final boolean fForEdit) {
584 final Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object()));
585
586 class I implements Iterator {
587 private Iterator m_iItems;
588 private int m_nExpectedModCount;
589
590 private StockItemImpl m_siiCurrent;
591
592 public I() {
593 super();
594
595 synchronized (oLock) {
596 synchronized (getItemsLock()) {
597 List lItems = (List)getItemsContainer().get(sKey);
598
599 if (lItems != null) {
600 lItems = new LinkedList(lItems);
601 } else {
602 lItems = new LinkedList();
603 }
604
605 if (db != null) {
606 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey, null,
607 StoringStockImpl.this, null);
608 for (Iterator i = db.iterator(dbc); i.hasNext(); ) {
609 DataBasketEntry dbe = (DataBasketEntry)i.next();
610
611 lItems.add(dbe.getValue());
612 }
613 }
614
615 m_iItems = lItems.iterator();
616 m_nExpectedModCount = m_nModCount;
617 }
618 }
619 }
620
621 public boolean hasNext() {
622 return m_iItems.hasNext();
623 }
624
625 public Object next() {
626 synchronized (oLock) {
627 synchronized (getItemsContainer()) {
628 if (m_nExpectedModCount != m_nModCount) {
629 throw new ConcurrentModificationException();
630 }
631
632 m_siiCurrent = (StockItemImpl)m_iItems.next();
633
634 if ((fForEdit) && (db != null)) {
635 //if item is temporarily added, return it
636 List lAdded = (List)getTemporaryAddedItemsContainer().get(sKey);
637 if ((lAdded != null) && (lAdded.contains(m_siiCurrent))) {
638 return m_siiCurrent;
639 }
640
641 try {
642 fireCanEditStockItems(new StoringStockChangeEvent(StoringStockImpl.this,
643 m_siiCurrent, db));
644 }
645 catch (VetoException ve) {
646 return null;
647 }
648 //otherwise move item from mItems to mTemporaryRemoved
649 List lItems = (List)getItemsContainer().get(sKey);
650 lItems.remove(m_siiCurrent);
651 if (lItems.size() == 0) {
652 getItemsContainer().remove(sKey);
653 }
654
655 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sKey);
656 if (lRemoved == null) {
657 lRemoved = new LinkedList();
658 getTemporaryRemovedItemsContainer().put(sKey, lRemoved);
659 }
660 lRemoved.add(m_siiCurrent);
661 //clone item
662 StockItemImpl siiRemoved = m_siiCurrent;
663 m_siiCurrent = siiRemoved.getShallowClone();
664 //add clone to mTemporaryAdded and mEditingItems
665 if (lAdded == null) {
666 lAdded = new LinkedList();
667 getTemporaryAddedItemsContainer().put(sKey, lAdded);
668 }
669 lAdded.add(m_siiCurrent);
670
671 List lEdit = (List)getEditingItemsContainer().get(sKey);
672 if (lEdit == null) {
673 lEdit = new LinkedList();
674 getEditingItemsContainer().put(sKey, lEdit);
675 }
676 lEdit.add(m_siiCurrent);
677
678 siiRemoved.setStock(null);
679 m_siiCurrent.setStock(StoringStockImpl.this);
680
681 // put information into databasket, making sure there's only one entry per StockItem
682 DataBasketCondition dbc = DataBasketConditionImpl.specificStockItem(siiRemoved);
683 StockItemDBEntry sidbe = (StockItemDBEntry)db.get(dbc);
684
685 if (sidbe != null) {
686 db.exchange(sidbe, new StoringStockItemDBEntry(StoringStockImpl.this,
687 (StoringStockImpl)sidbe.getDestination(), siiRemoved));
688 } else {
689 db.put(new StoringStockItemDBEntry(StoringStockImpl.this, null, siiRemoved));
690 }
691
692 db.put(new StoringStockItemDBEntry(null, StoringStockImpl.this, m_siiCurrent));
693
694 fireEditingStockItems(new StoringStockChangeEvent(StoringStockImpl.this,
695 m_siiCurrent, db));
696 fireStockItemsRemoved(new StoringStockChangeEvent(StoringStockImpl.this,
697 siiRemoved, db));
698 fireStockItemsAdded(new StoringStockChangeEvent(StoringStockImpl.this,
699 m_siiCurrent, db));
700
701 //Allows only ONE iterator at a time to call next() with fForEdit enabled
702 //because this method adds and removes StockItems, so other iterators have to
703 //be informed via the increased modifiaction counter
704 m_nExpectedModCount = (++m_nModCount);
705 }
706
707 return m_siiCurrent;
708 }
709 }
710 }
711
712 public void remove() {
713 synchronized (oLock) {
714 synchronized (getItemsLock()) {
715 if (m_nModCount != m_nExpectedModCount) {
716 throw new ConcurrentModificationException();
717 }
718
719 if (m_siiCurrent == null) {
720 throw new IllegalStateException();
721 }
722
723 try {
724 StoringStockImpl.this.remove(m_siiCurrent, db);
725
726 m_nExpectedModCount = m_nModCount;
727
728 m_siiCurrent = null;
729 }
730 catch (VetoException ve) {
731 m_siiCurrent = null;
732
733 throw new UnsupportedOperationException("VETO: " + ve);
734 }
735 }
736 }
737 }
738 }
739
740 if ((getCatalog(db) != null) && (!getCatalog(db).contains(sKey, db))) {
741 return new Iterator() {
742 public boolean hasNext() {
743 return false;
744 }
745
746 public Object next() {
747 throw new NoSuchElementException();
748 }
749
750 public void remove() {}
751 };
752 }
753
754 return new I();
755 }
756
757 /**
758 * Count the StockItems with a given key that are visible using a given DataBasket.
759 *
760 * @override Never
761 *
762 * @param sKey the key for which to count the StockItems.
763 * @param db the DataBasket that is used to determine visibility. Must be either <code>null</code> or a
764 * descendant of {@link DataBasketImpl}.
765 */
766 public int countItems(String sKey, DataBasket db) {
767 Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object()));
768
769 synchronized (oLock) {
770 synchronized (getItemsLock()) {
771 if ((getCatalog(db) != null) && (!getCatalog(db).contains(sKey, db))) {
772 return 0;
773 }
774
775 int nCount = 0;
776
777 List lItems = (List)getItemsContainer().get(sKey);
778
779 if (lItems != null) {
780 nCount += lItems.size();
781 }
782
783 if ((getTemporaryAddedItemsContainer().containsKey(sKey)) && (db != null)) {
784 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey, null, this, null);
785 BasketEntryValue bev = BasketEntryValues.COUNT_ITEMS;
786 IntegerValue ivCount = new IntegerValue(0);
787
788 db.sumBasket(dbc, bev, ivCount);
789
790 nCount += ivCount.getValue().intValue();
791 }
792
793 return nCount;
794 }
795 }
796 }
797
798 /**
799 * Remove one StockItem with the specified key from the Stock.
800 *
801 * <p>If there are any StockItems with the specified key, one will be removed. There is no guarantee as to
802 * which StockItem will be removed. The removed item, if any, will be returned.</p>
803 *
804 * <p><code>canRemoveStockItems</code> and <code>removedStockItems</code> events will be fired.</p>
805 *
806 * @override Never
807 *
808 * @param sKey the key for which to remove an item.
809 * @param db the DataBasket relative to which to remove the item. Must be either <code>null</code> or a
810 * descendant of {@link DataBasketImpl}.
811 *
812 * @return the removed item
813 *
814 * @exception VetoException if a listener vetoed the removal.
815 * @exception DataBasketConflictException if the item cannot be removed due to conflicts from DataBasket
816 * usage.
817 */
818 public StockItem remove(String sKey, DataBasket db) throws VetoException {
819 Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object()));
820
821 synchronized (oLock) {
822 synchronized (getItemsLock()) {
823 List lAdded = (List)getTemporaryAddedItemsContainer().get(sKey);
824
825 if ((lAdded != null) && (db != null)) {
826 DataBasketCondition dbc = new DataBasketConditionImpl(STOCK_ITEM_MAIN_KEY, sKey, null, this, null);
827
828 StockItemDBEntry sidbe = (StockItemDBEntry)db.get(dbc);
829
830 if (sidbe != null) {
831 //remove and return first temporarily added item
832 return remove((StockItem)sidbe.getValue(), db);
833 }
834 }
835
836 List lItems = (List)getItemsContainer().get(sKey);
837
838 if (lItems != null) {
839 /*
840 * 06/27/2000-STEFFEN: Had to add checking for lItems.size here, as apparently I sometimes
841 * keep the vector even if it is empty.
842 * I don't think, it should do that, but I need to check again.
843 * Checked, apparently remove (si, db) also doesn't clean up the list. This is pretty memory
844 * ineffective, but needs some effort to fix it. For the moment, just worked around it.
845 */
846 if (lItems.size() > 0) {
847 //remove and return last added item
848 return remove((StockItem)lItems.get(lItems.size() - 1), db);
849 }
850 }
851 }
852 }
853
854 return null;
855 }
856
857 /**
858 * Remove the given StockItem from the Stock.
859 *
860 * <p>If the given StockItem is contained in the Stock, it will be removed. The removed item, if any, will
861 * be returned.</p>
862 *
863 * <p><code>canRemoveStockItems</code> and <code>removedStockItems</code> events will be fired.</p>
864 *
865 * @override Never
866 *
867 * @param si the StockItem to be removed.
868 * @param db the DataBasket relative to which to remove the item. Must be either <code>null</code> or a
869 * descendant of {@link DataBasketImpl}.
870 *
871 * @return the removed item
872 *
873 * @exception VetoException if a listener vetoed the removal.
874 * @exception DataBasketConflictException if the item cannot be removed due to conflicts from DataBasket
875 * usage.
876 */
877 public StockItem remove(StockItem si, DataBasket db) throws VetoException {
878 Object oLock = ((db != null) ? (((DataBasketImpl)db).getDBIMonitor()) : (new Object()));
879
880 synchronized (oLock) {
881 synchronized (getItemsLock()) {
882 List lAdded = (List)getTemporaryAddedItemsContainer().get(si.getName());
883 if ((lAdded != null) && (lAdded.contains(si))) {
884 DataBasketCondition dbc = DataBasketConditionImpl.specificStockItem((StockItem)lAdded.get(
885 lAdded.indexOf(si)));
886
887 StockItemDBEntry sidbe = null;
888
889 if (db != null) {
890 sidbe = (StockItemDBEntry)db.get(dbc);
891 }
892
893 if (sidbe == null) {
894 throw new DataBasketConflictException(
895 "Cannot remove StockItem that was added using a different DataBasket!");
896 } else {
897 fireCanRemoveStockItems(new StoringStockChangeEvent(this, (StockItemImpl)si, db));
898
899 if (sidbe.getSource() == null) {
900 db.rollback(dbc);
901 } else {
902 // remove only the destination part of it:
903 db.exchange(sidbe, new StoringStockItemDBEntry((StoringStockImpl)sidbe.getSource(), null,
904 (StockItemImpl)sidbe.getValue()));
905
906 si = (StockItem)lAdded.get(lAdded.indexOf(si));
907 lAdded.remove(si);
908
909 m_nModCount++;
910
911 fireStockItemsAddRollback(new StoringStockChangeEvent(this, (StockItemImpl)si, db));
912 }
913
914 ((StockItemImpl)si).setStock(null);
915
916 return si;
917 }
918 }
919
920 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(si.getName());
921 if ((lRemoved != null) && (lRemoved.contains(si))) {
922 throw new DataBasketConflictException(
923 "Cannot remove an item that has already been removed!");
924 }
925
926 List lItems = (List)getItemsContainer().get(si.getName());
927 if ((lItems == null) || (!lItems.contains(si))) {
928 return null;
929 }
930
931 // remove from items container, making sure there's always only one DataBasket entry for each stockitem
932
933 fireCanRemoveStockItems(new StoringStockChangeEvent(this,
934 (StockItemImpl)lItems.get(lItems.indexOf(si)), db));
935
936 si = (StockItem)lItems.get(lItems.indexOf(si));
937 lItems.remove(si);
938
939 if (db != null) {
940 if (lRemoved == null) {
941 lRemoved = new LinkedList();
942 getTemporaryRemovedItemsContainer().put(si.getName(), lRemoved);
943 }
944
945 lRemoved.add(si);
946
947 DataBasketCondition dbc = DataBasketConditionImpl.specificStockItem(si);
948 StockItemDBEntry sidbe = (StockItemDBEntry)db.get(dbc);
949
950 if (sidbe != null) {
951 db.exchange(sidbe, new StoringStockItemDBEntry(this,
952 (StoringStockImpl)sidbe.getDestination(), (StockItemImpl)si));
953 } else {
954 db.put(new StoringStockItemDBEntry(this, null, (StockItemImpl)si));
955 }
956 }
957
958 m_nModCount++;
959
960 ((StockItemImpl)si).setStock(null);
961 fireStockItemsRemoved(new StoringStockChangeEvent(this, (StockItemImpl)si, db));
962
963 return si;
964 }
965 }
966 }
967
968 // StockImpl methods
969
970 /**
971 * Overridden to accomodate for specific usage of memory.
972 *
973 * @override Never
974 */
975 protected void fillShallowClone(StockImpl stiClone) {
976 synchronized (getItemsLock()) {
977 synchronized (stiClone.getItemsLock()) {
978 stiClone.setItemsContainer(new HashMap());
979 for (Iterator i = getItemsContainer().keySet().iterator(); i.hasNext(); ) {
980 String sKey = (String)i.next();
981
982 stiClone.getItemsContainer().put(sKey, ((LinkedList)getItemsContainer().get(sKey)).clone());
983 // shallow clone of LinkedList
984 }
985
986 stiClone.setTemporaryAddedItemsContainer(new HashMap());
987 for (Iterator i = getTemporaryAddedItemsContainer().keySet().iterator(); i.hasNext(); ) {
988 String sKey = (String)i.next();
989
990 stiClone.getTemporaryAddedItemsContainer().put(sKey,
991 ((LinkedList)getTemporaryAddedItemsContainer().get(sKey)).clone());
992 }
993
994 stiClone.setTemporaryRemovedItemsContainer(new HashMap());
995 for (Iterator i = getTemporaryRemovedItemsContainer().keySet().iterator(); i.hasNext(); ) {
996 String sKey = (String)i.next();
997
998 stiClone.getTemporaryRemovedItemsContainer().put(sKey,
999 ((LinkedList)getTemporaryRemovedItemsContainer().get(sKey)).clone());
1000 }
1001
1002 stiClone.setEditingItemsContainer(new HashMap());
1003 for (Iterator i = getEditingItemsContainer().keySet().iterator(); i.hasNext(); ) {
1004 String sKey = (String)i.next();
1005
1006 stiClone.getEditingItemsContainer().put(sKey,
1007 ((LinkedList)getEditingItemsContainer().get(sKey)).clone());
1008 }
1009
1010 stiClone.setRefIntegrItemsContainer(new HashMap());
1011 for (Iterator i = getRefIntegrItemsContainer().keySet().iterator(); i.hasNext(); ) {
1012 String sKey = (String)i.next();
1013
1014 stiClone.getRefIntegrItemsContainer().put(sKey,
1015 ((LinkedList)getRefIntegrItemsContainer().get(sKey)).clone());
1016 }
1017
1018 stiClone.setRefIntegrEditContainer(new HashMap());
1019 for (Iterator i = getRefIntegrEditContainer().keySet().iterator(); i.hasNext(); ) {
1020 String sKey = (String)i.next();
1021
1022 stiClone.getRefIntegrEditContainer().put(sKey,
1023 ((LinkedList)getRefIntegrEditContainer().get(sKey)).clone());
1024 }
1025 }
1026 }
1027 }
1028
1029 /**
1030 * @override Always
1031 */
1032 protected StockImpl createPeer() {
1033 StoringStockImpl ssiPeer = new StoringStockImpl(getName(), m_ciCatalog);
1034 ssiPeer.m_dbCatalogValidator = m_dbCatalogValidator;
1035
1036 return ssiPeer;
1037 }
1038
1039 /**
1040 * Set the Stock and adjust the Catalog link for all Stocks that are contained in this Stock.
1041 *
1042 * @override Never
1043 */
1044 protected void setStock(StockImpl sti) {
1045 super.setStock(sti);
1046
1047 if (sti != null) {
1048 synchronized (getItemsLock()) {
1049 Set stKeys = getItemsContainer().keySet();
1050 stKeys.addAll(getTemporaryAddedItemsContainer().keySet());
1051
1052 for (Iterator i = stKeys.iterator(); i.hasNext(); ) {
1053 reEstablishStockCatalogLink((String)i.next());
1054 }
1055 }
1056 }
1057 }
1058
1059 /**
1060 * Helper method used to maintain StockImpl - CatalogImpl links in nested Stocks/Catalogs. For internal use only.
1061 *
1062 * @param db the DataBasket that is protecting this activity.
1063 * @param nAction the action that occurred. Can be either {@link #COMMIT_ACTION}, {@link #ROLLBACK_ACTION},
1064 * {@link #STARTEDIT_ACTION}.
1065 */
1066 void relinkCatalog(DataBasket db, int nAction) {
1067 super.relinkCatalog(db, nAction);
1068
1069 if (nAction == ROLLBACK_ACTION) {
1070 // Additionally refresh the links in all child stocks.
1071 synchronized (getItemsLock()) {
1072 for (Iterator i = getItemsContainer().values().iterator(); i.hasNext(); ) {
1073 List l = (List)i.next();
1074
1075 for (Iterator j = l.iterator(); j.hasNext(); ) {
1076 StockItemImpl sii = (StockItemImpl)j.next();
1077
1078 sii.relinkCatalog(db, nAction);
1079 }
1080 }
1081
1082 for (Iterator i = getTemporaryAddedItemsContainer().values().iterator(); i.hasNext(); ) {
1083 List l = (List)i.next();
1084
1085 for (Iterator j = l.iterator(); j.hasNext(); ) {
1086 StockItemImpl sii = (StockItemImpl)j.next();
1087
1088 sii.relinkCatalog(db, nAction);
1089 }
1090 }
1091 }
1092 }
1093 }
1094
1095 // SelfManagingDBESource interface methods
1096
1097 /**
1098 * Commit the removal of a StockItem.
1099 *
1100 * <p>A <code>commitRemoveStockItems</code> will be fired.</p>
1101 *
1102 * @override Never
1103 */
1104 public void commitRemove(DataBasket db, DataBasketEntry dbe) {
1105 // DataBasket is already locking on its monitor so we just lock on ours
1106 synchronized (getItemsLock()) {
1107 StockItemImpl sii = (StockItemImpl)dbe.getValue();
1108
1109 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sii.getName());
1110 if (lRemoved != null) {
1111 lRemoved.remove(sii);
1112
1113 if (lRemoved.size() == 0) {
1114 getTemporaryRemovedItemsContainer().remove(sii.getName());
1115 }
1116
1117 if (sii.getStock() == this) {
1118 sii.setStock(null);
1119 }
1120 fireStockItemsRemoveCommit(new StoringStockChangeEvent(this, sii, db));
1121 }
1122 }
1123 }
1124
1125 /**
1126 * Rollback the removal of a StockItem.
1127 *
1128 * <p>A <code>rollbackRemoveStockItems</code> will be fired. Also, the Stock will try to make sure, that
1129 * a corresponding CatalogItem exists.</p>
1130 *
1131 * @override Never
1132 */
1133 public void rollbackRemove(DataBasket db, DataBasketEntry dbe) {
1134 synchronized (getItemsLock()) {
1135 prepareReferentialIntegrity(db, dbe);
1136
1137 StockItemImpl sii = (StockItemImpl)dbe.getValue();
1138
1139 List lRemoved = (List)getTemporaryRemovedItemsContainer().get(sii.getName());
1140 if (lRemoved != null) {
1141 lRemoved.remove(sii);
1142
1143 if (lRemoved.size() == 0) {
1144 getTemporaryRemovedItemsContainer().remove(sii.getName());
1145 }
1146
1147 List lItems = (List)getItemsContainer().get(sii.getName());
1148 if (lItems == null) {
1149 lItems = new LinkedList();
1150 getItemsContainer().put(sii.getName(), lItems);
1151 }
1152
1153 lItems.add(sii);
1154
1155 sii.setStock(this);
1156
1157 m_nModCount++;
1158
1159 fireStockItemsRemoveRollback(new StoringStockChangeEvent(this, sii, db));
1160 }
1161 }
1162 }
1163
1164 // SelfManagingDBEDestination interface methods
1165
1166 /**
1167 * Commit the adding of a StockItem.
1168 *
1169 * <p>A <code>commitAddStockItems</code> will be fired. A <code>commitEditStockItems</code> event may be
1170 * fired as a consequence of this method. Also, the Stock will try to make sure, that a corresponding
1171 * CatalogItem exists.</p>
1172 *
1173 * @override Never
1174 */
1175 public void commitAdd(DataBasket db, DataBasketEntry dbe) {
1176 synchronized (getItemsLock()) {
1177 prepareReferentialIntegrity(db, dbe);
1178
1179 StockItemImpl sii = (StockItemImpl)dbe.getValue();
1180
1181 List lAdded = (List)getTemporaryAddedItemsContainer().get(sii.getName());
1182 if (lAdded != null) {
1183 lAdded.remove(sii);
1184
1185 if (lAdded.size() == 0) {
1186 getTemporaryAddedItemsContainer().remove(sii.getName());
1187 }
1188
1189 List lItems = (List)getItemsContainer().get(sii.getName());
1190 if (lItems == null) {
1191 lItems = new LinkedList();
1192 getItemsContainer().put(sii.getName(), lItems);
1193 }
1194
1195 lItems.add(sii);
1196
1197 sii.setStock(this);
1198
1199 m_nModCount++;
1200
1201 fireStockItemsAddCommit(new StoringStockChangeEvent(this, sii, db));
1202
1203 List lEdit = (List)getEditingItemsContainer().get(sii.getName());
1204 if ((lEdit != null) && (lEdit.remove(sii))) {
1205 if (lEdit.size() == 0) {
1206 getEditingItemsContainer().remove(sii.getName());
1207 }
1208
1209 fireStockItemsEditCommit(new StoringStockChangeEvent(this, sii, db));
1210 }
1211 }
1212 }
1213 }
1214
1215 /**
1216 * Rollback the adding of a StockItem.
1217 *
1218 * <p>A <code>commitAddStockItems</code> will be fired. A <code>commitEditStockItems</code> event may be
1219 * fired as a consequence of this method.</p>
1220 *
1221 * @override Never
1222 */
1223 public void rollbackAdd(DataBasket db, DataBasketEntry dbe) {
1224 synchronized (getItemsLock()) {
1225 StockItemImpl sii = (StockItemImpl)dbe.getValue();
1226
1227 List lAdded = (List)getTemporaryAddedItemsContainer().get(sii.getName());
1228 if (lAdded != null) {
1229 lAdded.remove(sii);
1230
1231 if (lAdded.size() == 0) {
1232 getTemporaryAddedItemsContainer().remove(sii.getName());
1233 }
1234
1235 if (sii.getStock() == this) {
1236 sii.setStock(null);
1237 }
1238
1239 m_nModCount++;
1240
1241 fireStockItemsAddRollback(new StoringStockChangeEvent(this, sii, db));
1242
1243 List lEdit = (List)getEditingItemsContainer().get(sii.getName());
1244 if ((lEdit != null) && (lEdit.remove(sii))) {
1245 if (lEdit.size() == 0) {
1246 getEditingItemsContainer().remove(sii.getName());
1247 }
1248
1249 fireStockItemsEditRollback(new StoringStockChangeEvent(this, sii, db));
1250 }
1251 }
1252 }
1253 }
1254 }