001 package data.ooimpl;
002
003 import java.util.*;
004
005 import java.io.Serializable;
006
007 import data.events.*;
008 import data.*;
009
010 import log.*;
011
012 import util.*;
013
014 /**
015 * Pure Java implementation of the {@link DataBasket} interface.
016 *
017 * <p>This DataBasket implementation can be used together with the {@link CatalogImpl Catalog} and
018 * {@link StockImpl Stock implementations} that come with the framework as well as with any other data
019 * container that manages all its internal structure on its own, instead of delegating them, e.g. to a data
020 * base. For a data base backing you should use other implementations of {@link Stock}, {@link Catalog} and
021 * {@link DataBasket}, but these are not yet part of the framework.</p>
022 *
023 * @author Steffen Zschaler
024 * @version 2.0 14/06/1999
025 * @since v2.0
026 */
027 public class DataBasketImpl extends Object implements ListenableDataBasket {
028
029 /**
030 * Internal helper class used by {@link DataBasketImpl}, representing a subbasket of a {@link DataBasket}.
031 *
032 * <p>This class has been made protected so that framework users be able to subclass it should the need
033 * arise.</p>
034 *
035 * @author Steffen Zschaler
036 * @version 2.0 14/06/1999
037 * @since v2.0
038 */
039 protected static class SubDataBasket implements Serializable {
040
041 /**
042 * The entries contained in this subbasket.
043 *
044 * <p>This is a map of maps of lists of {@link DataBasketEntry DataBasketEntries}.</p>
045 *
046 * @serial
047 */
048 private Map m_mpmpldbeCategories = new HashMap();
049
050 /**
051 * The owner of this subbasket.
052 *
053 * @serial
054 */
055 private DataBasketImpl m_dbiOwner;
056
057 /**
058 * Create a new subbasket.
059 *
060 * @param dbiOwner the DataBasketImpl instance owning this subbasket.
061 */
062 public SubDataBasket(DataBasketImpl dbiOwner) {
063 super();
064
065 m_dbiOwner = dbiOwner;
066 }
067
068 /**
069 * Check whether the given Object is equal to this subbasket.
070 *
071 * <p>This is overridden to mean identity, because for subbaskets equality and identity are really the
072 * same.</p>
073 *
074 * @override Never
075 */
076 public boolean equals(Object o) {
077 return this == o;
078 }
079
080 /**
081 * Commit all items in this subbasket that match the condition.
082 *
083 * @param dbc the condition that must be matched.
084 *
085 * @see DataBasketEntry#commit
086 *
087 * @override Never
088 */
089 public void commit(DataBasketCondition dbc) {
090 DataBasketEntry dbe = null;
091
092 for (Iterator i = iterator(dbc, true, true); i.hasNext(); ) {
093 try {
094 dbe = (DataBasketEntry)i.next();
095
096 if (!dbe.isHandled()) {
097 dbe.commit();
098 }
099
100 i.remove();
101 }
102 catch (Throwable t) {
103 System.err.println("Exception during commit of <" + dbe + ">:");
104 t.printStackTrace();
105 System.err.println("Continuing committing any other DataBasketEntries.");
106 }
107 }
108 }
109
110 /**
111 * Rollback all entries in this subbasket that match the condition.
112 *
113 * @param dbc the condition to be matched.
114 *
115 * @see DataBasketEntry#rollback
116 *
117 * @override Never
118 */
119 public void rollback(DataBasketCondition dbc) {
120 DataBasketEntry dbe = null;
121
122 for (Iterator i = iterator(dbc, true, true); i.hasNext(); ) {
123 try {
124 dbe = (DataBasketEntry)i.next();
125
126 if (!dbe.isHandled()) {
127 dbe.rollback();
128 }
129
130 i.remove();
131 }
132 catch (Throwable t) {
133 System.err.println("Exception during rollback of <" + dbe + ">:");
134 t.printStackTrace();
135 System.err.println("Continuing rolling back any other DataBasketEntries.");
136 }
137 }
138 }
139
140 /**
141 * Iterate all entries in the subbasket that match the given condition.
142 *
143 * <p>The condition applies to the returned iterator only, it will not be influenced by any future calls
144 * to <code>iterator()</code>.</p>
145 *
146 * @param dbc the condition returned items will have to match. <code>null</code> means match all.
147 * @param fAllowRemove if true,the returned iterator's {@link java.util.Iterator#remove remove()} method * will be enabled. An iterator with its <code>remove()</code> method enabled must never be made publicly
148 * accessible outside of the {@link DataBasketImpl DataBasket}.
149 * @param fShowHandled if true, the iterator will include items that return true from their
150 * {@link DataBasketEntry#isHandled} method. Such an iterator must never be made publicly accessible
151 * outside of the {@link DataBasketImpl DataBasket}.
152 *
153 * @return an iterator that will iterate over all entries in this subbasket that match the given
154 * condition. The iterator will support the <code>remove()</code> method, if <code>fAllowRemove</code> is
155 * true.
156 *
157 * @override Never
158 */
159 public Iterator iterator(final DataBasketCondition dbc, final boolean fAllowRemove,
160 final boolean fShowHandled) {
161
162 // The iterator to be returned
163 class I implements Iterator {
164
165 private Map m_mpmpldbeCategories;
166 private Iterator m_iCategories;
167 private Map m_mpldbeSubCategories;
168 private Iterator m_iSubCategories;
169 private Iterator m_iItems;
170
171 private DataBasketEntry m_dbeCurrent = null;
172 private DataBasketEntry m_dbeNext = null;
173
174 public I(Map mpmpldbeCategories) {
175 super();
176
177 m_mpmpldbeCategories = mpmpldbeCategories;
178
179 // Use a TreeSet to sort the main keys alphabetically.
180 m_iCategories = new TreeSet(m_mpmpldbeCategories.keySet()).iterator();
181 }
182
183 public boolean hasNext() {
184 return findNext(false);
185 }
186
187 public Object next() {
188 if (!findNext(true)) {
189 throw new NoSuchElementException();
190 }
191
192 return m_dbeCurrent;
193 }
194
195 public void remove() {
196 if (!fAllowRemove) {
197 throw new UnsupportedOperationException();
198 } else {
199 if (m_iItems == null) {
200 throw new IllegalStateException();
201 }
202
203 m_iItems.remove();
204 m_dbeCurrent.setOwner(null);
205 m_dbiOwner.fireDBERemoved(m_dbeCurrent);
206 }
207 }
208
209 private boolean findNext(boolean fGet) {
210 // traverse the hierarchy to find the next item that applies
211 // if fGet == true, put the next valid item into m_dbeCurrent
212 do {
213 if (m_iSubCategories != null) {
214 //look if current Category (__MAINKEY:_STOCKITEM_IMPL or __MAINKEY:_CATALOGITEM_IMPL)
215 //contains DataBasketEntries in one of its SubCategories, if so, we're done
216 if (checkSubCategories(fGet)) {
217 return true;
218 }
219 } else {
220 //dummy iterator to prevent possible NullPointerException at the end of the
221 //do-while statement
222 m_iSubCategories = new Iterator() {
223 public boolean hasNext() {
224 return false;
225 }
226
227 public Object next() {
228 return null;
229 }
230
231 public void remove() {}
232 };
233 }
234 //Current Category did not contain DBEs, switch to next Category
235 while ((m_iCategories.hasNext()) && (!m_iSubCategories.hasNext())) {
236 String sCategoryID = (String)m_iCategories.next();
237
238 if (dbc != null) {
239 if ((dbc.getMainKey() == null) || (dbc.getMainKey().equals(sCategoryID))) {
240 m_mpldbeSubCategories = (Map)m_mpmpldbeCategories.get(sCategoryID);
241 m_iSubCategories = m_mpldbeSubCategories.keySet().iterator();
242 }
243 } else {
244 m_mpldbeSubCategories = (Map)m_mpmpldbeCategories.get(sCategoryID);
245 m_iSubCategories = m_mpldbeSubCategories.keySet().iterator();
246 }
247 }
248 }
249 while (m_iSubCategories.hasNext());
250
251 return false;
252 }
253
254 /**
255 *
256 * @param fGet
257 * @return
258 */
259 private boolean checkSubCategories(boolean fGet) {
260 do {
261 if (m_iItems != null) {
262 //look if current SubCategory contains more DataBasketEntries, if so, we're done
263 if (checkItems(fGet)) {
264 return true;
265 }
266 } else {
267 //dummy iterator to prevent possible NullPointerException at the end of the
268 //do-while statement
269 m_iItems = new Iterator() {
270 public boolean hasNext() {
271 return false;
272 }
273
274 public Object next() {
275 return null;
276 }
277
278 public void remove() {}
279 };
280 }
281 //Current SubCategory did not contain DBEs, switch to next SubCategory
282 while ((m_iSubCategories.hasNext()) && (!m_iItems.hasNext())) {
283 String sSubCategoryID = (String)m_iSubCategories.next();
284
285 if (dbc != null) {
286 if ((dbc.getSecondaryKey() == null) ||
287 (dbc.getSecondaryKey().equals(sSubCategoryID))) {
288 List ldbeSubCategory = (List)m_mpldbeSubCategories.get(sSubCategoryID);
289 m_iItems = ldbeSubCategory.iterator();
290 }
291 } else {
292 List ldbeSubCategory = (List)m_mpldbeSubCategories.get(sSubCategoryID);
293 m_iItems = ldbeSubCategory.iterator();
294 }
295 }
296 }
297 while (m_iItems.hasNext());
298
299 return false;
300 }
301
302 /**
303 * Iterate over m_iItems, until a DataBasketEntry that matches the DataBasketCondition is found.
304 * DataBasketEntries of m_iItems have both the same main and secondary key.
305 * @param fGet if true, the found DataBasketEntry is assigned to m_dbeCurrent, otherwise to
306 * m_dbeNext.
307 * @return true, if a DataBasketEntry that matches the DataBasketCondition is found.
308 */
309 private boolean checkItems(boolean fGet) {
310 if (m_dbeNext != null) {
311 if (fGet) {
312 m_dbeCurrent = m_dbeNext;
313 m_dbeNext = null;
314 }
315
316 return true;
317 }
318
319 while (m_iItems.hasNext()) {
320 DataBasketEntry dbe = (DataBasketEntry)m_iItems.next();
321
322 if ((dbe.isHandled()) && (!fShowHandled)) {
323 continue;
324 }
325
326 if (dbc != null) {
327 if ((dbc.getSource() != null) && (dbc.getSource() != dbe.getSource())) {
328 continue;
329 }
330
331 if ((dbc.getDestination() != null) && (dbc.getDestination() != dbe.getDestination())) {
332 continue;
333 }
334
335 if (((dbc.getValue() != null) && (dbc.getValue() == dbe.getValue())) ||
336 ((dbc.getValue() == null) && (dbc.match(dbe)))) {
337
338 if (!fGet) {
339 m_dbeNext = dbe;
340 } else {
341 m_dbeCurrent = dbe;
342 }
343
344 return true;
345 }
346 } else {
347 if (!fGet) {
348 m_dbeNext = dbe;
349 } else {
350 m_dbeCurrent = dbe;
351 }
352
353 return true;
354 }
355 }
356
357 return false;
358 }
359 }
360
361 return new I(m_mpmpldbeCategories);
362 }
363
364 /**
365 * Sum up the values of all entries in this subbasket that match the condition.
366 *
367 * @param dbc the condition to be matched.
368 * @param bev an helper object used to determine the value of each matching DataBasketEntry.
369 * @param vInit the value that is to be used for adding up. All adding is performed by calling
370 * {@link Value#addAccumulating} on this object.
371 *
372 * @return the sum in <code>vInit</code>.
373 *
374 * @override Never
375 */
376 public Value sumSubBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) {
377 for (Iterator i = iterator(dbc, false, false); i.hasNext(); ) {
378 vInit.addAccumulating((Value)bev.getEntryValue((DataBasketEntry)i.next()).clone());
379 }
380
381 return vInit;
382 }
383
384 /**
385 * Put a {@link DataBasketEntry} into the subbasket.
386 *
387 * @param dbe the entry to be put
388 *
389 * @see DataBasketImpl#put
390 *
391 * @override Never
392 */
393 public void put(DataBasketEntryImpl dbe) {
394 Map mpldbeCategory = (Map)m_mpmpldbeCategories.get(dbe.getMainKey());
395
396 if (mpldbeCategory == null) {
397 mpldbeCategory = new HashMap();
398 m_mpmpldbeCategories.put(dbe.getMainKey(), mpldbeCategory);
399 }
400
401 List ldbeSubCategory = (List)mpldbeCategory.get(dbe.getSecondaryKey());
402
403 if (ldbeSubCategory == null) {
404 ldbeSubCategory = new LinkedList();
405 mpldbeCategory.put(dbe.getSecondaryKey(), ldbeSubCategory);
406 }
407
408 ldbeSubCategory.add(dbe);
409
410 dbe.setOwner(m_dbiOwner);
411
412 m_dbiOwner.log(PUT_ACTION, dbe);
413 m_dbiOwner.fireDBEAdded(dbe);
414 }
415
416 /**
417 * Get the first entry in this subbasket that matches the condition, if any.
418 *
419 * @param dbc the condition to be matched
420 *
421 * @return the matching entry, if any.
422 *
423 * @see DataBasketImpl#get
424 *
425 * @override Never
426 */
427 public DataBasketEntry get(DataBasketCondition dbc) {
428 Iterator i = iterator(dbc, false, false);
429
430 if (i.hasNext()) {
431 return (DataBasketEntry)i.next();
432 } else {
433 return null;
434 }
435 }
436 }
437
438 /**
439 * The subbaskets of this DataBasket.
440 *
441 * @serial
442 */
443 protected Map m_mpsdbChildren = new HashMap();
444
445 /**
446 * The monitor used to synchronize access to the list of children.
447 */
448 private transient Object m_oChildrenLock;
449
450 /**
451 * Return the monitor used to synchronize access to the list of children.
452 *
453 * @override Never
454 */
455 protected final Object getChildrenLock() {
456 if (m_oChildrenLock == null) {
457 m_oChildrenLock = new Object();
458 }
459
460 return m_oChildrenLock;
461 }
462
463 /**
464 * The current subbasket.
465 *
466 * @serial
467 */
468 private SubDataBasket m_sdbCurrent;
469
470 /**
471 * The monitor used to synchronize access to the current subbasket.
472 */
473 private transient Object m_oCurrentLock;
474
475 /**
476 * Return the monitor used to synchronize access to the current subbasket.
477 *
478 * @override Never
479 */
480 private final Object getCurrentLock() {
481 if (m_oCurrentLock == null) {
482 m_oCurrentLock = new Object();
483 }
484
485 return m_oCurrentLock;
486 }
487
488 /**
489 * The current log context.
490 *
491 * @serial
492 */
493 private LogContext m_lcLog;
494
495 /**
496 * The current log mode.
497 *
498 * @serial
499 */
500 private int m_nLogMode = LOG_MODE_NONE;
501
502 /**
503 * The monitor synchronizing access to the log related attributes.
504 */
505 private transient Object m_oLogLock;
506
507 /**
508 * Get the monitor synchronizing access to the log related attributes.
509 *
510 * @override Never
511 */
512 private final Object getLogLock() {
513 if (m_oLogLock == null) {
514 m_oLogLock = new Object();
515 }
516
517 return m_oLogLock;
518 }
519
520 /**
521 * The listeners currently listening for events from this DataBasket.
522 *
523 * @serial
524 */
525 protected ListenerHelper m_lhListeners = new ListenerHelper();
526
527 /**
528 * Create a new DataBasketImpl.
529 */
530 public DataBasketImpl() {
531 super();
532
533 setCurrentSubBasket(DEFAULTSUBBASKET_NAME);
534 }
535
536 /**
537 * Rollback the contents of all subbaskets of this DataBasket.
538 *
539 * @override Never
540 */
541 public void rollback() {
542 rollback((DataBasketCondition)null);
543 }
544
545 /**
546 * Commit the contents of all subbaskets of this DataBasket.
547 *
548 * @override Never
549 */
550 public void commit() {
551 commit((DataBasketCondition)null);
552 }
553
554 /**
555 * Rollback all items in all subbaskets that do match the given condition.
556 *
557 * @param dbc the condition to be matched. <code>null</code> means rollback
558 * unconditionally.
559 *
560 * @override Never
561 */
562 public void rollback(DataBasketCondition dbc) {
563 synchronized (getChildrenLock()) {
564 synchronized (getLogLock()) {
565 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) {
566 ((SubDataBasket)i.next()).rollback(dbc);
567 }
568
569 clean();
570 }
571 }
572 }
573
574 /**
575 * Commit all items in all subbaskets that do match the given condition.
576 *
577 * @param dbc the condition to be matched. <code>null</code> means commit
578 * unconditionally.
579 *
580 * @override Never
581 */
582 public void commit(DataBasketCondition dbc) {
583 synchronized (getChildrenLock()) {
584 synchronized (getLogLock()) {
585 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) {
586 ((SubDataBasket)i.next()).commit(dbc);
587 }
588
589 clean();
590 }
591 }
592 }
593
594 /**
595 * Rollback all entries in a given subbasket.
596 *
597 * @param sName the name of the subbasket.
598 *
599 * @override Never
600 */
601 public void rollbackSubBasket(String sName) {
602 SubDataBasket sdb = null;
603
604 synchronized (getChildrenLock()) {
605 sdb = (SubDataBasket)m_mpsdbChildren.get(sName);
606
607 synchronized (getLogLock()) {
608 if (sdb != null) {
609 sdb.rollback(null);
610 }
611
612 clean();
613 }
614 }
615 }
616
617 /**
618 * Rollback the current subbasket's contents.
619 *
620 * @override Never
621 */
622 public void rollbackCurrentSubBasket() {
623 SubDataBasket sdbCurrent = null;
624
625 synchronized (getCurrentLock()) {
626 sdbCurrent = m_sdbCurrent;
627 }
628
629 if (sdbCurrent != null) {
630 synchronized (getChildrenLock()) {
631 synchronized (getLogLock()) {
632 sdbCurrent.rollback(null);
633
634 clean();
635 }
636 }
637 }
638 }
639
640 /**
641 * Commit all entries in a given subbasket.
642 *
643 * @param sName the name of the subbasket.
644 *
645 * @override Never
646 */
647 public void commitSubBasket(String sName) {
648 SubDataBasket sdb = null;
649
650 synchronized (getChildrenLock()) {
651 sdb = (SubDataBasket)m_mpsdbChildren.get(sName);
652
653 synchronized (getLogLock()) {
654 if (sdb != null) {
655 sdb.commit(null);
656 }
657
658 clean();
659 }
660 }
661 }
662
663 /**
664 * Commit the current subbasket's contents.
665 *
666 * @override Never
667 */
668 public void commitCurrentSubBasket() {
669 SubDataBasket sdbCurrent = null;
670
671 synchronized (getCurrentLock()) {
672 sdbCurrent = m_sdbCurrent;
673 }
674
675 if (sdbCurrent != null) {
676 synchronized (getChildrenLock()) {
677 synchronized (getLogLock()) {
678 sdbCurrent.commit(null);
679 clean();
680 }
681 }
682 }
683 }
684
685 /**
686 * Set the current subbasket.
687 *
688 * @param sName the name of the new current subbasket. If the subbasket does not yet exist, it
689 * is created prior to being made the current subbasket.
690 *
691 * @override Never
692 */
693 public void setCurrentSubBasket(String sName) {
694 synchronized (getChildrenLock()) {
695 if (!m_mpsdbChildren.containsKey(sName)) {
696 SubDataBasket sdb = new SubDataBasket(this);
697 m_mpsdbChildren.put(sName, sdb);
698 }
699
700 synchronized (getCurrentLock()) {
701 m_sdbCurrent = (SubDataBasket)m_mpsdbChildren.get(sName);
702 }
703 }
704 }
705
706 /**
707 * Iterate all entries in all subbaskets that match the condition.
708 *
709 * @param dbc the condition to be matched.
710 *
711 * @return an iterator that iterates all the entries in the DataBasket that match the condition. The
712 * iterator will not support the {@link java.util.Iterator#remove remove()} method.
713 *
714 * @override Never
715 */
716 public Iterator iterator(final DataBasketCondition dbc) {
717 class I implements Iterator {
718 private Iterator m_iChildKeys;
719 private Iterator m_iSubBasketItems;
720 private DataBasket m_dbSource;
721
722 public I(Iterator iChildKeys, DataBasket dbSource) {
723 super();
724
725 m_iChildKeys = iChildKeys;
726 m_dbSource = dbSource;
727 }
728
729 public boolean hasNext() {
730 return findNextItem();
731 }
732
733 public Object next() {
734 if (!findNextItem()) {
735 throw new NoSuchElementException();
736 }
737
738 return m_iSubBasketItems.next();
739 }
740
741 public void remove() {
742 throw new UnsupportedOperationException();
743 }
744
745 private boolean findNextItem() {
746 while (((m_iSubBasketItems == null) ||
747 (!m_iSubBasketItems.hasNext())) && (m_iChildKeys.hasNext())) {
748 // try next subbasket
749 String sNextBasket = (String)m_iChildKeys.next();
750 m_iSubBasketItems = m_dbSource.subBasketIterator(sNextBasket, dbc);
751 }
752
753 if ((m_iSubBasketItems == null) || (!m_iSubBasketItems.hasNext())) {
754 // did not find valid next subbasket
755 return false;
756 }
757
758 return true;
759 }
760 }
761
762 return new I(m_mpsdbChildren.keySet().iterator(), this);
763 }
764
765 /**
766 * Iterate all entries in a given subbasket that match the given condition.
767 *
768 * @param dbc the condition to be matched.
769 *
770 * @return an iterator that iterates all the entries in the given subbasket that match the condition. The
771 * iterator will not support the {@link java.util.Iterator#remove remove()} method.
772 *
773 * @override Never
774 */
775 public Iterator subBasketIterator(String sName, DataBasketCondition dbc) {
776 synchronized (getChildrenLock()) {
777 SubDataBasket sdb = (SubDataBasket)m_mpsdbChildren.get(sName);
778
779 return sdb.iterator(dbc, false, false);
780 }
781 }
782
783 /**
784 * Sum up all entries in the DataBasket that match the given condition.
785 *
786 * @param dbc the condition to be matched.
787 * @param bev an object used for determining the value of a {@link DataBasketEntry}.
788 * @param vInit the value that is to be used for adding up. All adding is performed by calling
789 * {@link Value#addAccumulating} on this object.
790 *
791 * @return the sum in <code>vInit</code>.
792 *
793 * @override Never
794 */
795 public Value sumBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) {
796 synchronized (getChildrenLock()) {
797 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) {
798 SubDataBasket sdb = (SubDataBasket)i.next();
799 sdb.sumSubBasket(dbc, bev, vInit);
800 }
801 }
802
803 return vInit;
804 }
805
806 /**
807 * Sum up all entries in a given subbasket that match the given condition.
808 *
809 * @param sName the name of the subbasket that is to be summed up.
810 * @param dbc the condition to be matched.
811 * @param bev an object used for determining the value of a {@link DataBasketEntry}.
812 * @param vInit the value that is to be used for adding up. All adding is performed by calling
813 * {@link Value#addAccumulating} on this object.
814 *
815 * @return the sum in <code>vInit</code>.
816 *
817 * @override Never
818 */
819 public Value sumSubBasket(String sName, DataBasketCondition dbc, BasketEntryValue bev, Value vInit) {
820 SubDataBasket sdb = null;
821
822 synchronized (getChildrenLock()) {
823 sdb = (SubDataBasket)m_mpsdbChildren.get(sName);
824
825 return sdb.sumSubBasket(dbc, bev, vInit);
826 }
827 }
828
829 /**
830 * Sum up all entries in the current subbasket that match the given condition.
831 *
832 * @param dbc the condition to be matched.
833 * @param bev an object used for determining the value of a {@link DataBasketEntry}.
834 * @param vInit the value that is to be used for adding up. All adding is performed by calling
835 * {@link Value#addAccumulating} on this object.
836 *
837 * @return the sum in <code>vInit</code>.
838 *
839 * @override Never
840 */
841 public Value sumCurrentSubBasket(DataBasketCondition dbc, BasketEntryValue bev, Value vInit) {
842 synchronized (getCurrentLock()) {
843 return m_sdbCurrent.sumSubBasket(dbc, bev, vInit);
844 }
845 }
846
847 /**
848 * Put a DataBasketEntry into the current subbasket. DataBasketEntries that are to be put into a
849 * DataBasketImpl must be instances of, or of subclasses of, {@link DataBasketEntryImpl}.
850 *
851 * @param dbe the entry to be put in.
852 *
853 * @exception ClassCastException if <code>! (dbe instanceof {@link DataBasketEntryImpl})</code>.
854 *
855 * @override Never
856 */
857 public void put(DataBasketEntry dbe) {
858 synchronized (getCurrentLock()) {
859 synchronized (getChildrenLock()) {
860 synchronized (getLogLock()) {
861 m_sdbCurrent.put((DataBasketEntryImpl)dbe);
862 }
863 }
864 }
865 }
866
867 /**
868 * Exchange a DataBasketEntry with another.
869 *
870 * @param dbeOrg the original DataBasketEntry, to be replaced.
871 * @param dbeNew the replacement.
872 *
873 * @override Never
874 */
875 public void exchange(final DataBasketEntry dbeOrg, DataBasketEntry dbeNew) {
876 DataBasketCondition dbc = new DataBasketConditionImpl(dbeOrg.getMainKey(), dbeOrg.getSecondaryKey(),
877 dbeOrg.getSource(), dbeOrg.getDestination(), null) {
878 public boolean match(DataBasketEntry dbe) {
879 return (dbe == dbeOrg);
880 }
881 };
882
883 synchronized (getChildrenLock()) {
884 synchronized (getLogLock()) {
885 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) {
886 SubDataBasket sdb = (SubDataBasket)i.next();
887
888 Iterator i1 = sdb.iterator(dbc, true, false);
889 if (i1.hasNext()) {
890 DataBasketEntryImpl dbe = (DataBasketEntryImpl)i1.next();
891
892 i1.remove();
893
894 log(EXCHANGE_REMOVE_ACTION, dbe);
895 sdb.put((DataBasketEntryImpl)dbeNew);
896
897 return;
898 }
899 }
900
901 put(dbeNew);
902 }
903 }
904 }
905
906 /**
907 * Get the first entry in the DataBasket that matches the condition.
908 *
909 * @param dbc the condition
910 *
911 * @override Never
912 */
913 public DataBasketEntry get(DataBasketCondition dbc) {
914 synchronized (getChildrenLock()) {
915 Iterator i = iterator(dbc);
916 if (i.hasNext()) {
917 return (DataBasketEntry)i.next();
918 } else {
919 return null;
920 }
921 }
922 }
923
924 /**
925 * Check whether any entries matching a particular condition are contained in the DataBasket.
926 *
927 * @param dbc the condition
928 *
929 * @return true if an entry that matches the condition could be found.
930 *
931 * @override Never
932 */
933 public boolean contains(DataBasketCondition dbc) {
934 synchronized (getChildrenLock()) {
935 return (iterator(dbc).hasNext());
936 }
937 }
938
939 /**
940 * Set the log context for this DataBasket.
941 *
942 * <p>All operations as defined through {@link #setLogMode} will be logged using the given log context. If
943 * the current log context is <code>null</code> no logging of any kind will occur.</p>
944 *
945 * <p>The actual LogEntries written are defined by the {@link DataBasketEntryImpl DataBasketEntries} that
946 * participate in the activities that are logged. The DataBasket will wrap those LogEntries into LogEntries
947 * that give the type of operation performed.</p>
948 *
949 * @param lcNew the new log context
950 *
951 * @return the previous log context, if any.
952 *
953 * @see #log
954 *
955 * @override Never
956 */
957 public LogContext setLogContext(LogContext lcNew) {
958 synchronized (getLogLock()) {
959 LogContext lc = m_lcLog;
960
961 m_lcLog = lcNew;
962
963 return lc;
964 }
965 }
966
967 /**
968 * Set the log mode for this DataBasket.
969 *
970 * <p>The current log mode decides what operations on the DataBasket are being logged. The default value is
971 * {@link DataBasket#LOG_MODE_NONE}, indicating that no logging occurs. Other possibilities are:</p>
972 *
973 * <ul>
974 * <li><strong>{@link DataBasket#LOG_MODE_ALL}</strong> All operations on the DataBasket are being logged.
975 * </li>
976 * <li><strong>{@link DataBasket#LOG_MODE_COMMITS_ONLY}</strong> Only commits are being logged. There will
977 * be one entry for each single step in the commit process.</li>
978 * <li><strong>{@link DataBasket#LOG_MODE_ROLLBACKS_ONLY}</strong> Only rollbacks are being logged. There
979 * will be one entry for each single step in the rollback process.</li>
980 * </ul>
981 *
982 * <p>The actual LogEntries written are defined by the {@link DataBasketEntryImpl DataBasketEntries} that
983 * participate in the activities that are logged. The DataBasket will wrap those LogEntries into LogEntries
984 * that give the type of operation performed.</p>
985 *
986 * @param nLogMode the new log mode.
987 *
988 * @return the previous log mode.
989 *
990 * @see #log
991 *
992 * @override Never
993 */
994 public int setLogMode(int nLogMode) {
995 synchronized (getLogLock()) {
996 int n = m_nLogMode;
997
998 m_nLogMode = nLogMode;
999
1000 return n;
1001 }
1002 }
1003
1004 /**
1005 * Return the current log mode of the DataBasket. For information on the possible values and their meaning,
1006 * please refer to {@link #setLogMode}.
1007 *
1008 * @override Never
1009 */
1010 public int getLogMode() {
1011 synchronized (getLogLock()) {
1012 return m_nLogMode;
1013 }
1014 }
1015
1016 /**
1017 * Action constant for {@link #log}.
1018 */
1019 public static final int PUT_ACTION = 0;
1020
1021 /**
1022 * Action constant for {@link #log}.
1023 */
1024 public static final int EXCHANGE_REMOVE_ACTION = 1;
1025
1026 /**
1027 * Action constant for {@link #log}.
1028 */
1029 public static final int COMMIT_ACTION = 2;
1030
1031 /**
1032 * Action constant for {@link #log}.
1033 */
1034 public static final int ROLLBACK_ACTION = 3;
1035
1036 /**
1037 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl.
1038 */
1039 public static final LogEntryFilter LOGENTRYFILTER_DATABASKETIMPLACTIONS = new LogEntryFilter() {
1040 public boolean accept(LogEntry le) {
1041 return (le instanceof DataBasketImplLogEntry);
1042 }
1043 };
1044
1045 /**
1046 * A LogEntryFilter that will accept only LogEntries that were not produced by a DataBasketImpl.
1047 */
1048 public static final LogEntryFilter LOGENTRYFILTER_NO_DATABASKETIMPLACTIONS = new LogEntryFilter() {
1049 public boolean accept(LogEntry le) {
1050 return!(le instanceof DataBasketImplLogEntry);
1051 }
1052 };
1053
1054 /**
1055 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that
1056 * describe a <code>put</code> action.
1057 */
1058 public static final LogEntryFilter LOGENTRYFILTER_PUT_ACTIONS = new LogEntryFilter() {
1059 public boolean accept(LogEntry le) {
1060 return ((le instanceof DataBasketImplLogEntry) &&
1061 (((DataBasketImplLogEntry)le).getAction() == PUT_ACTION));
1062 }
1063 };
1064
1065 /**
1066 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that
1067 * describe a <code>exchange_remove</code> action.
1068 */
1069 public static final LogEntryFilter LOGENTRYFILTER_EXCHANGE_REMOVE_ACTIONS = new LogEntryFilter() {
1070 public boolean accept(LogEntry le) {
1071 return ((le instanceof DataBasketImplLogEntry) &&
1072 (((DataBasketImplLogEntry)le).getAction() == EXCHANGE_REMOVE_ACTION));
1073 }
1074 };
1075
1076 /**
1077 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that
1078 * describe a <code>commit</code> action.
1079 */
1080 public static final LogEntryFilter LOGENTRYFILTER_COMMIT_ACTIONS = new LogEntryFilter() {
1081 public boolean accept(LogEntry le) {
1082 return ((le instanceof DataBasketImplLogEntry) &&
1083 (((DataBasketImplLogEntry)le).getAction() == COMMIT_ACTION));
1084 }
1085 };
1086
1087 /**
1088 * A LogEntryFilter that will accept only LogEntries that were produced by a DataBasketImpl and that
1089 * describe a <code>rollback</code> action.
1090 */
1091 public static final LogEntryFilter LOGENTRYFILTER_ROLLBACK_ACTIONS = new LogEntryFilter() {
1092 public boolean accept(LogEntry le) {
1093 return ((le instanceof DataBasketImplLogEntry) &&
1094 (((DataBasketImplLogEntry)le).getAction() == ROLLBACK_ACTION));
1095 }
1096 };
1097
1098 /**
1099 * A LogEntry that describes an action on a DataBasket.
1100 *
1101 * @see DataBasketImpl#log
1102 *
1103 * @author Steffen Zschaler
1104 * @version 2.0 14/07/1999
1105 * @since v2.0
1106 */
1107 public static class DataBasketImplLogEntry extends LogEntry {
1108
1109 /**
1110 * The log entry describing the actual action.
1111 *
1112 * @serial
1113 */
1114 private LogEntry m_leData;
1115
1116 /**
1117 * The action code. One of PUT_ACTION, EXCHANGE_REMOVE_ACTION, COMMIT_ACTION, ROLLBACK_ACTION.
1118 *
1119 * @serial
1120 */
1121 private int m_nAction;
1122
1123 /**
1124 * Helper array for converting action codes into Strings.
1125 */
1126 private static String[] s_asActionNames = {
1127 "PUT_ACTION", "EXCHANGE_REMOVE_ACTION", "COMMIT_ACTION", "ROLLBACK_ACTION"};
1128
1129 /**
1130 * Create a new DataBasketImplLogEntry.
1131 *
1132 * @param The action code. One of {@link DataBasketImpl#PUT_ACTION},
1133 * {@link DataBasketImpl#EXCHANGE_REMOVE_ACTION}, {@link DataBasketImpl#COMMIT_ACTION},
1134 * {@link DataBasketImpl#ROLLBACK_ACTION}.
1135 * @param leData The log entry describing the actual action.
1136 */
1137 public DataBasketImplLogEntry(int nAction, LogEntry leData) {
1138 super();
1139
1140 m_nAction = nAction;
1141 m_leData = leData;
1142 }
1143
1144 /**
1145 * Get the action code.
1146 *
1147 * @override Never
1148 */
1149 public int getAction() {
1150 return m_nAction;
1151 }
1152
1153 /**
1154 * Get the name of the action.
1155 *
1156 * @override Never
1157 */
1158 public String getActionName() {
1159 return s_asActionNames[getAction()];
1160 }
1161
1162 /**
1163 * Get the log entry describing the actual action.
1164 *
1165 * @override Never
1166 */
1167 public LogEntry getData() {
1168 return m_leData;
1169 }
1170
1171 /**
1172 * A short descriptive text of the log entry.
1173 *
1174 * @override Never
1175 */
1176 public String toString() {
1177 return getActionName() + " performed on " + getData();
1178 }
1179 }
1180
1181 /**
1182 * Log the given event wrapped in a LogEntry that describes the action.
1183 *
1184 * <p>If the action needs to be logged in the current log mode, a new {@link DataBasketImplLogEntry} is
1185 * created that wraps the LogEntry produced by the given Loggable. This LogEntry is then logged using the
1186 * current LogContext.</p>
1187 *
1188 * @see #setLogContext
1189 * @see #setLogMode
1190 *
1191 * @override Never
1192 */
1193 public void log(int nAction, Loggable la) {
1194 synchronized (getLogLock()) {
1195 if (m_lcLog != null) {
1196 switch (m_nLogMode) {
1197 case LOG_MODE_NONE:
1198 return;
1199 case LOG_MODE_COMMITS_ONLY:
1200 if (nAction != COMMIT_ACTION) {
1201 return;
1202 }
1203
1204 break;
1205 case LOG_MODE_ROLLBACKS_ONLY:
1206 if (nAction != ROLLBACK_ACTION) {
1207 return;
1208 }
1209 }
1210
1211 final DataBasketImplLogEntry dbile = new DataBasketImplLogEntry(nAction, la.getLogData());
1212
1213 try {
1214 m_lcLog.log(new Loggable() {
1215 public LogEntry getLogData() {
1216 return dbile;
1217 }
1218 });
1219 }
1220 catch (java.io.IOException ioe) {
1221 ioe.printStackTrace();
1222 }
1223 }
1224 }
1225 }
1226
1227 /**
1228 * Return the monitor used to synchronize access to the internal data structures of the DataBasket. Whenever
1229 * a container wants to access a DataBasket implemented in this way, it must <i>first</i> synchronize over
1230 * the DataBasket's monitor and <i>then</i> synchronize over its own monitor(s).
1231 *
1232 * @override Never
1233 */
1234 public Object getDBIMonitor() {
1235 return getChildrenLock();
1236 }
1237
1238 /**
1239 * Remove any items that have already been {@link DataBasketEntry#isHandled handled}, but so far have not
1240 * been removed from the DataBasket.
1241 *
1242 * <p>This method is usually called by the framework only. It assumes, that it is called from within a
1243 * <code>synchronized</code> block synchronizing on the monitor returned by {@link #getDBIMonitor}.</p>
1244 *
1245 * @override Never
1246 */
1247 protected void clean() {
1248 // Remove any items that where handled because of dependencies
1249 for (Iterator i = m_mpsdbChildren.values().iterator(); i.hasNext(); ) {
1250 Iterator j = ((SubDataBasket)i.next()).iterator(null, true, true);
1251
1252 while (j.hasNext()) {
1253 if (((DataBasketEntry)j.next()).isHandled()) {
1254 j.remove();
1255 }
1256 }
1257 }
1258 }
1259
1260 // ListenableDataBasket interface methods
1261
1262 /**
1263 * Add a listener that will receive events when the DataBasket's contents change.
1264 *
1265 * @override Never
1266 */
1267 public void addDataBasketListener(DataBasketListener dbl) {
1268 m_lhListeners.add(DataBasketListener.class, dbl);
1269 }
1270
1271 /**
1272 * Remove a listener that received events when the DataBasket's contents changed.
1273 *
1274 * @override Never
1275 */
1276 public void removeDataBasketListener(DataBasketListener dbl) {
1277 m_lhListeners.remove(DataBasketListener.class, dbl);
1278 }
1279
1280 /**
1281 * Fire an event to all listeners listening to this DataBasket.
1282 *
1283 * @override Never
1284 */
1285 public void fireDataBasketChanged() {
1286 Object[] listeners = m_lhListeners.getListenerList();
1287 DataBasketEvent e = null;
1288
1289 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1290 if (listeners[i] == DataBasketListener.class) {
1291 if (e == null) {
1292 e = new DataBasketEvent(this, null);
1293 }
1294
1295 ((DataBasketListener)listeners[i + 1]).dataBasketChanged(e);
1296 }
1297 }
1298 }
1299
1300 /**
1301 * Fire an event to all listeners listening to this DataBasket.
1302 *
1303 * @override Never
1304 */
1305 protected void fireDBEAdded(DataBasketEntry dbe) {
1306 Object[] listeners = m_lhListeners.getListenerList();
1307 DataBasketEvent e = null;
1308
1309 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1310 if (listeners[i] == DataBasketListener.class) {
1311 if (e == null) {
1312 e = new DataBasketEvent(this, dbe);
1313 }
1314
1315 ((DataBasketListener)listeners[i + 1]).addedDBE(e);
1316 }
1317 }
1318 }
1319
1320 /**
1321 * Fire an event to all listeners listening to this DataBasket.
1322 *
1323 * @override Never
1324 */
1325 protected void fireDBERemoved(DataBasketEntry dbe) {
1326 Object[] listeners = m_lhListeners.getListenerList();
1327 DataBasketEvent e = null;
1328
1329 for (int i = listeners.length - 2; i >= 0; i -= 2) {
1330 if (listeners[i] == DataBasketListener.class) {
1331 if (e == null) {
1332 e = new DataBasketEvent(this, dbe);
1333 }
1334
1335 ((DataBasketListener)listeners[i + 1]).removedDBE(e);
1336 }
1337 }
1338 }
1339 }