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 }