001    package data.filters;
002    
003    import data.*;
004    import data.events.*;
005    
006    import util.*;
007    
008    import java.util.*;
009    import java.beans.*;
010    import java.io.*;
011    
012    /**
013     * <i>Abstract</i> superclass of all Stock filters. By using a Stock filter you can present partial views of
014     * a Stock to certain parts of your application, e.g., to the GUI elements. However, you cannot use this Stock
015     * as a replacement for a 'real' Stock, e.g., as an item in another Stock.
016     *
017     * <p>The concrete filter condition is implemented by subclassing either {@link CountingStockFilter} or
018     * {@link StoringStockFilter} and overriding some method. The concrete semantics is documented with the
019     * concrete subclass of AbstractStockFilter.</p>
020     *
021     * @author Steffen Zschaler
022     * @version 2.0 19/08/1999
023     * @since v2.0
024     */
025    public abstract class AbstractStockFilter extends Object implements ListenableStock, StockChangeListener {
026    
027        /**
028         * The Stock that gets filtered.
029         *
030         * @serial
031         */
032        protected Stock m_stSource;
033    
034        /**
035         * The list of listeners of this Stock.
036         *
037         * @serial
038         */
039        protected ListenerHelper m_lhListeners = new ListenerHelper();
040    
041        /**
042         * After reading the default serializable fields of the class, re-establish the listener link to our
043         * source.
044         *
045         * @override Never
046         */
047        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
048            ois.defaultReadObject();
049    
050            if (m_stSource instanceof ListenableStock) {
051                ((ListenableStock)m_stSource).addStockChangeListener(this);
052            }
053        }
054    
055        /**
056         * Create a new AbstractStockFilter. May only be called by subclasses.
057         *
058         * @param stSource the Stock to be filtered.
059         */
060        protected AbstractStockFilter(Stock stSource) {
061            super();
062    
063            m_stSource = stSource;
064    
065            if (stSource instanceof ListenableStock) {
066                ((ListenableStock)stSource).addStockChangeListener(this);
067            }
068        }
069    
070        /**
071         * Add the given item to the source Stock.
072         *
073         * @override Never
074         */
075        public void add(StockItem si, DataBasket db) {
076            m_stSource.add(si, db);
077        }
078    
079        /**
080         * Add the given Stock to the source Stock.
081         *
082         * @override Never
083         */
084        public void addStock(Stock st, DataBasket db, boolean fRemove) {
085            m_stSource.addStock(st, db, fRemove);
086        }
087    
088        /**
089         * Returns <code>(countItems (sKey, db) >= 0)</code>.
090         *
091         * @override Sometimes
092         */
093        public boolean contains(String sKey, DataBasket db) {
094            return (countItems(sKey, db) >= 0);
095        }
096    
097        /**
098         * Remove the given item from the source Stock.
099         *
100         * @override Never
101         */
102        public StockItem remove(String sKey, DataBasket db) throws VetoException {
103            if (contains(sKey, db)) {
104                return m_stSource.remove(sKey, db);
105            } else {
106                return null;
107            }
108        }
109    
110        /**
111         * Remove the given item from the source Stock.
112         *
113         * @override Never
114         */
115        public StockItem remove(StockItem si, DataBasket db) throws VetoException {
116            if (contains(si, db)) {
117                return m_stSource.remove(si, db);
118            } else {
119                return null;
120            }
121        }
122    
123        /**
124         * Create an iterator that will return all items that match the condition.
125         *
126         * @override Never
127         */
128        public Iterator iterator(final DataBasket db, final boolean fForEdit) {
129            class I implements Iterator {
130                private Iterator m_iKeys;
131                private Iterator m_iItems;
132    
133                public I() {
134                    super();
135    
136                    m_iKeys = keySet(db).iterator();
137                }
138    
139                public boolean hasNext() {
140                    return findNext();
141                }
142    
143                public Object next() {
144                    if (!findNext()) {
145                        throw new NoSuchElementException("No more elements in Stock.");
146                    }
147    
148                    return m_iItems.next();
149                }
150    
151                public void remove() {
152                    if (m_iItems == null) {
153                        throw new IllegalStateException();
154                    }
155    
156                    m_iItems.remove();
157                }
158    
159                private boolean findNext() {
160                    if (m_iItems == null) {
161                        if (m_iKeys.hasNext()) {
162                            m_iItems = get((String)m_iKeys.next(), db, fForEdit);
163                        } else {
164                            return false;
165                        }
166                    }
167    
168                    while ((m_iItems.hasNext()) || (m_iKeys.hasNext())) {
169                        if (m_iItems.hasNext()) {
170                            return true;
171                        }
172    
173                        m_iItems = get((String)m_iKeys.next(), db, fForEdit);
174                    }
175    
176                    return false;
177                }
178            }
179    
180            return new I();
181        }
182    
183        /**
184         * Get a filtered key set.
185         *
186         * @override Never
187         */
188        public Set keySet(DataBasket db) {
189            Set stKeys = m_stSource.keySet(db);
190    
191            for (Iterator i = stKeys.iterator(); i.hasNext(); ) {
192                String sKey = (String)i.next();
193    
194                if (!contains(sKey, db)) {
195                    stKeys.remove(sKey);
196                }
197            }
198    
199            return stKeys;
200        }
201    
202        /**
203         * Calculate the total value of the Stock, evaluating only items that match the condition.
204         *
205         * @override Never
206         */
207        public Value sumStock(DataBasket db, CatalogItemValue civ, Value vInit) {
208            Set stKeys = keySet(db);
209    
210            for (Iterator i = stKeys.iterator(); i.hasNext(); ) {
211                String sKey = (String)i.next();
212    
213                try {
214                    vInit.addAccumulating(civ.getValue(getCatalog(db).get(sKey, db,
215                            false)).multiply(countItems(sKey, db)));
216                }
217                catch (VetoException ex) {}
218            }
219    
220            return vInit;
221        }
222    
223        /**
224         * Fill the source Stock.
225         *
226         * @override Never
227         */
228        public Value fillStockWithValue(DataBasket db, Value vTarget, StockFromValueCreator sfvc) {
229            return m_stSource.fillStockWithValue(db, vTarget, sfvc);
230        }
231    
232        /**
233         * Calculate the size of the source Stock, considering only items that match the condition.
234         *
235         * @override Never
236         */
237        public int size(DataBasket db) {
238            Set stKeys = keySet(db);
239            int nSize = 0;
240    
241            for (Iterator i = stKeys.iterator(); i.hasNext(); ) {
242                nSize += countItems((String)i.next(), db);
243            }
244    
245            return nSize;
246        }
247    
248        /**
249         * Get the source Stock's Catalog.
250         *
251         * @override Never
252         */
253        public Catalog getCatalog(DataBasket db) {
254            return m_stSource.getCatalog(db);
255        }
256    
257        // StockItem interface methods
258    
259        /**
260         * Get the source Stock's Stock.
261         *
262         * @override Never
263         */
264        public Stock getStock() {
265            return m_stSource.getStock();
266        }
267    
268        /**
269         * Get the source stock. If the source stock is a StockFilter again,
270         * return this Stock's MainStock.
271         *
272         * @override Never
273         */
274        public Stock getMainStock() {
275            if (m_stSource instanceof AbstractStockFilter) {
276                return ((AbstractStockFilter)m_stSource).getMainStock();
277            }
278    
279            return m_stSource;
280        }
281    
282        /**
283         * Get the source Stock's associated item.
284         *
285         * @override Never
286         */
287        public CatalogItem getAssociatedItem(DataBasket db) {
288            return m_stSource.getAssociatedItem(db);
289        }
290    
291        // Nameable interface methods
292        /**
293         * Attach the NameContext to the source Stock.
294         *
295         * @override Never
296         */
297        public NameContext attach(NameContext nc) {
298            return m_stSource.attach(nc);
299        }
300    
301        /**
302         * Detach the current NameContext from the source Stock.
303         *
304         * @override Never
305         */
306        public NameContext detachNC() {
307            return m_stSource.detachNC();
308        }
309    
310        /**
311         * Set the source Stock's name.
312         *
313         * @override Never
314         */
315        public void setName(String sName, DataBasket db) throws NameContextException {
316            m_stSource.setName(sName, db);
317        }
318    
319        /**
320         * Get the source Stock's name.
321         *
322         * @override Never
323         */
324        public String getName() {
325            return m_stSource.getName();
326        }
327    
328        /**
329         * Register the listener with the source Stock.
330         *
331         * @override Never
332         */
333        public void addPropertyChangeListener(PropertyChangeListener pcl) {
334            m_stSource.addPropertyChangeListener(pcl);
335        }
336    
337        /**
338         * Un-Register the listener with the source Stock.
339         *
340         * @override Never
341         */
342        public void removePropertyChangeListener(PropertyChangeListener pcl) {
343            m_stSource.removePropertyChangeListener(pcl);
344        }
345    
346        /**
347         * Register the listener with the source Stock.
348         *
349         * @override Never
350         */
351        public void addNameListener(PropertyChangeListener pcl) {
352            m_stSource.addNameListener(pcl);
353        }
354    
355        /**
356         * Un-Register the listener with the source Stock.
357         *
358         * @override Never
359         */
360        public void removeNameListener(PropertyChangeListener pcl) {
361            m_stSource.removeNameListener(pcl);
362        }
363    
364        /**
365         * Compare the source Stock to the object.
366         *
367         * @override Never
368         */
369        public int compareTo(Object o) {
370            return m_stSource.compareTo(o);
371        }
372    
373        /**
374         * @override Always
375         */
376        public abstract Object clone();
377    
378        // ListenableStock interface methods
379        /**
380         * Register a listener that will receive events when the Stock's contents change.
381         *
382         * @override Never
383         */
384        public void addStockChangeListener(StockChangeListener scl) {
385            m_lhListeners.add(StockChangeListener.class, scl);
386        }
387    
388        /**
389         * Un-Register a listener that received events when the Stock's contents changed.
390         *
391         * @override Never
392         */
393        public void removeStockChangeListener(StockChangeListener scl) {
394            m_lhListeners.remove(StockChangeListener.class, scl);
395        }
396    
397        // StockChangeListener interface methods
398    
399        /**
400         * Receive the event from the source Stock, translate and propagate it to any listeners.
401         *
402         * @override Never
403         */
404        public void addedStockItems(StockChangeEvent e) {
405            Set stItems = new HashSet();
406            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
407                StockItem si = (StockItem)i.next();
408    
409                if (contains(si, e.getBasket())) {
410                    stItems.add(si);
411                }
412            }
413    
414            fireStockItemsAdded(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
415        }
416    
417        /**
418         * Receive the event from the source Stock, translate and propagate it to any listeners.
419         *
420         * @override Never
421         */
422        public void commitAddStockItems(StockChangeEvent e) {
423            Set stItems = new HashSet();
424            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
425                StockItem si = (StockItem)i.next();
426    
427                if (contains(si, e.getBasket())) {
428                    stItems.add(si);
429                }
430            }
431    
432            fireStockItemsAddCommit(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
433        }
434    
435        /**
436         * Receive the event from the source Stock, translate and propagate it to any listeners.
437         *
438         * @override Never
439         */
440        public void rollbackAddStockItems(StockChangeEvent e) {
441            Set stItems = new HashSet();
442            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
443                stItems.add(i.next());
444            }
445    
446            fireStockItemsAddRollback(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
447        }
448    
449        /**
450         * Receive the event from the source Stock, translate and propagate it to any listeners.
451         *
452         * @override Never
453         */
454        public void canRemoveStockItems(StockChangeEvent e) throws VetoException {
455            Set stItems = new HashSet();
456            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
457                StockItem si = (StockItem)i.next();
458    
459                if (contains(si, e.getBasket())) {
460                    stItems.add(si);
461                }
462            }
463    
464            fireCanRemoveStockItems(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
465        }
466    
467        /**
468         * Receive the event from the source Stock, translate and propagate it to any listeners.
469         *
470         * @override Never
471         */
472        public void noRemoveStockItems(StockChangeEvent e) {
473            Set stItems = new HashSet();
474            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
475                StockItem si = (StockItem)i.next();
476    
477                if (contains(si, e.getBasket())) {
478                    stItems.add(si);
479                }
480            }
481    
482            fireStockItemsNoRemove(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
483        }
484    
485        /**
486         * Receive the event from the source Stock, translate and propagate it to any listeners.
487         *
488         * @override Never
489         */
490        public void removedStockItems(StockChangeEvent e) {
491            Set stItems = new HashSet();
492            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
493                stItems.add(i.next());
494            }
495    
496            fireStockItemsRemoved(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
497        }
498    
499        /**
500         * Receive the event from the source Stock, translate and propagate it to any listeners.
501         *
502         * @override Never
503         */
504        public void commitRemoveStockItems(StockChangeEvent e) {
505            Set stItems = new HashSet();
506            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
507                stItems.add(i.next());
508            }
509    
510            fireStockItemsRemoveCommit(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
511        }
512    
513        /**
514         * Receive the event from the source Stock, translate and propagate it to any listeners.
515         *
516         * @override Never
517         */
518        public void rollbackRemoveStockItems(StockChangeEvent e) {
519            Set stItems = new HashSet();
520            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
521                StockItem si = (StockItem)i.next();
522    
523                if (contains(si, e.getBasket())) {
524                    stItems.add(si);
525                }
526            }
527    
528            fireStockItemsRemoveRollback(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
529        }
530    
531        /**
532         * Receive the event from the source Stock, translate and propagate it to any listeners.
533         *
534         * @override Never
535         */
536        public void canEditStockItems(StockChangeEvent e) throws VetoException {
537            Set stItems = new HashSet();
538            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
539                StockItem si = (StockItem)i.next();
540    
541                if (contains(si, e.getBasket())) {
542                    stItems.add(si);
543                }
544            }
545    
546            fireCanEditStockItems(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
547        }
548    
549        /**
550         * Receive the event from the source Stock, translate and propagate it to any listeners.
551         *
552         * @override Never
553         */
554        public void noEditStockItems(StockChangeEvent e) {
555            Set stItems = new HashSet();
556            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
557                StockItem si = (StockItem)i.next();
558    
559                if (contains(si, e.getBasket())) {
560                    stItems.add(si);
561                }
562            }
563    
564            fireStockItemsNoEdit(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
565        }
566    
567        /**
568         * Receive the event from the source Stock, translate and propagate it to any listeners.
569         *
570         * @override Never
571         */
572        public void editingStockItems(StockChangeEvent e) {
573            Set stItems = new HashSet();
574            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
575                StockItem si = (StockItem)i.next();
576    
577                if (contains(si, e.getBasket())) {
578                    stItems.add(si);
579                }
580            }
581    
582            fireEditingStockItems(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
583        }
584    
585        /**
586         * Receive the event from the source Stock, translate and propagate it to any listeners.
587         *
588         * @override Never
589         */
590        public void commitEditStockItems(StockChangeEvent e) {
591            Set stItems = new HashSet();
592            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
593                StockItem si = (StockItem)i.next();
594    
595                if (contains(si, e.getBasket())) {
596                    stItems.add(si);
597                }
598            }
599    
600            fireStockItemsEditCommit(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
601        }
602    
603        /**
604         * Receive the event from the source Stock, translate and propagate it to any listeners.
605         *
606         * @override Never
607         */
608        public void rollbackEditStockItems(StockChangeEvent e) {
609            Set stItems = new HashSet();
610            for (Iterator i = e.getAffectedItems(); i.hasNext(); ) {
611                StockItem si = (StockItem)i.next();
612    
613                if (contains(si, e.getBasket())) {
614                    stItems.add(si);
615                }
616            }
617    
618            fireStockItemsEditRollback(new StockFilterEvent(this, e.getAffectedKey(), stItems, e.getBasket()));
619        }
620    
621        /**
622         * Fire an event to any listeners.
623         *
624         * @override Never
625         */
626        protected void fireStockItemsAdded(StockChangeEvent e) {
627            Object[] listeners = m_lhListeners.getListenerList();
628    
629            for (int i = listeners.length - 2; i >= 0; i -= 2) {
630                if (listeners[i] == StockChangeListener.class) {
631    
632                    ((StockChangeListener)listeners[i + 1]).addedStockItems(e);
633                }
634            }
635        }
636    
637        /**
638         * Fire an event to any listeners.
639         *
640         * @override Never
641         */
642        protected void fireStockItemsAddCommit(StockChangeEvent e) {
643            Object[] listeners = m_lhListeners.getListenerList();
644    
645            for (int i = listeners.length - 2; i >= 0; i -= 2) {
646                if (listeners[i] == StockChangeListener.class) {
647    
648                    ((StockChangeListener)listeners[i + 1]).commitAddStockItems(e);
649                }
650            }
651        }
652    
653        /**
654         * Fire an event to any listeners.
655         *
656         * @override Never
657         */
658        protected void fireStockItemsAddRollback(StockChangeEvent e) {
659            Object[] listeners = m_lhListeners.getListenerList();
660    
661            for (int i = listeners.length - 2; i >= 0; i -= 2) {
662                if (listeners[i] == StockChangeListener.class) {
663    
664                    ((StockChangeListener)listeners[i + 1]).rollbackAddStockItems(e);
665                }
666            }
667        }
668    
669        /**
670         * Fire an event to any listeners.
671         *
672         * @override Never
673         */
674        protected void fireStockItemsNoRemove(StockChangeEvent e) {
675            Object[] listeners = m_lhListeners.getListenerList();
676    
677            for (int i = listeners.length - 2; i >= 0; i -= 2) {
678                if (listeners[i] == StockChangeListener.class) {
679    
680                    ((StockChangeListener)listeners[i + 1]).noRemoveStockItems(e);
681                }
682            }
683        }
684    
685        /**
686         * Fire an event to any listeners.
687         *
688         * @override Never
689         */
690        protected void fireStockItemsRemoved(StockChangeEvent e) {
691            Object[] listeners = m_lhListeners.getListenerList();
692    
693            for (int i = listeners.length - 2; i >= 0; i -= 2) {
694                if (listeners[i] == StockChangeListener.class) {
695    
696                    ((StockChangeListener)listeners[i + 1]).removedStockItems(e);
697                }
698            }
699        }
700    
701        /**
702         * Fire an event to any listeners.
703         *
704         * @override Never
705         */
706        protected void fireStockItemsRemoveCommit(StockChangeEvent e) {
707            Object[] listeners = m_lhListeners.getListenerList();
708    
709            for (int i = listeners.length - 2; i >= 0; i -= 2) {
710                if (listeners[i] == StockChangeListener.class) {
711    
712                    ((StockChangeListener)listeners[i + 1]).commitRemoveStockItems(e);
713                }
714            }
715        }
716    
717        /**
718         * Fire an event to any listeners.
719         *
720         * @override Never
721         */
722        protected void fireStockItemsRemoveRollback(StockChangeEvent e) {
723            Object[] listeners = m_lhListeners.getListenerList();
724    
725            for (int i = listeners.length - 2; i >= 0; i -= 2) {
726                if (listeners[i] == StockChangeListener.class) {
727    
728                    ((StockChangeListener)listeners[i + 1]).rollbackRemoveStockItems(e);
729                }
730            }
731        }
732    
733        /**
734         * Fire an event to any listeners.
735         *
736         * @override Never
737         */
738        protected void fireCanRemoveStockItems(StockChangeEvent e) throws VetoException {
739            Object[] listeners = m_lhListeners.getListenerList();
740    
741            for (int i = listeners.length - 2; i >= 0; i -= 2) {
742                if (listeners[i] == StockChangeListener.class) {
743                    ((StockChangeListener)listeners[i + 1]).canRemoveStockItems(e);
744                }
745            }
746        }
747    
748        /**
749         * Fire an event to any listeners.
750         *
751         * @override Never
752         */
753        protected void fireCanEditStockItems(StockChangeEvent e) throws VetoException {
754            Object[] listeners = m_lhListeners.getListenerList();
755    
756            for (int i = listeners.length - 2; i >= 0; i -= 2) {
757                if (listeners[i] == StockChangeListener.class) {
758                    ((StockChangeListener)listeners[i + 1]).canEditStockItems(e);
759                }
760            }
761        }
762    
763        /**
764         * Fire an event to any listeners.
765         *
766         * @override Never
767         */
768        protected void fireStockItemsNoEdit(StockChangeEvent e) {
769            Object[] listeners = m_lhListeners.getListenerList();
770    
771            for (int i = listeners.length - 2; i >= 0; i -= 2) {
772                if (listeners[i] == StockChangeListener.class) {
773    
774                    ((StockChangeListener)listeners[i + 1]).noEditStockItems(e);
775                }
776            }
777        }
778    
779        /**
780         * Fire an event to any listeners.
781         *
782         * @override Never
783         */
784        protected void fireEditingStockItems(StockChangeEvent e) {
785            Object[] listeners = m_lhListeners.getListenerList();
786    
787            for (int i = listeners.length - 2; i >= 0; i -= 2) {
788                if (listeners[i] == StockChangeListener.class) {
789    
790                    ((StockChangeListener)listeners[i + 1]).editingStockItems(e);
791                }
792            }
793        }
794    
795        /**
796         * Fire an event to any listeners.
797         *
798         * @override Never
799         */
800        protected void fireStockItemsEditCommit(StockChangeEvent e) {
801            Object[] listeners = m_lhListeners.getListenerList();
802    
803            for (int i = listeners.length - 2; i >= 0; i -= 2) {
804                if (listeners[i] == StockChangeListener.class) {
805    
806                    ((StockChangeListener)listeners[i + 1]).commitEditStockItems(e);
807                }
808            }
809        }
810    
811        /**
812         * Fire an event to any listeners.
813         *
814         * @override Never
815         */
816        protected void fireStockItemsEditRollback(StockChangeEvent e) {
817            Object[] listeners = m_lhListeners.getListenerList();
818    
819            for (int i = listeners.length - 2; i >= 0; i -= 2) {
820                if (listeners[i] == StockChangeListener.class) {
821    
822                    ((StockChangeListener)listeners[i + 1]).rollbackEditStockItems(e);
823                }
824            }
825        }
826    }