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    }