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