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