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