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    
011    /**
012     * A filter for Catalogs.
013     *
014     * <p>CatalogFilters can be used to present partial views of a Catalog to parts of your application, e.g.,
015     * GUI elements. However, you cannot use a CatalogFilter as a replacement for a 'real' Catalog, e.g., as an
016     * item in another Catalog.</p>
017     *
018     * <p>The actual filter condition is defined by overriding method {@link #match}.</p>
019     *
020     * @author Steffen Zschaler
021     * @version 2.0 19/08/1999
022     * @since v2.0
023     */
024    public abstract class CatalogFilter<T extends CatalogItem> extends Object implements Catalog<T>, CatalogChangeListener<T>,
025            ListenableCatalog<T>, HelpableListener {
026    
027        /**
028         * The Catalog that is being filtered.
029         *
030         * @serial
031         */
032        protected Catalog<T> m_cOrg;
033    
034        /**
035         * The listeners that listen for events from this Catalog.
036         *
037         * @serial
038         */
039        protected ListenerHelper m_lhListeners = new ListenerHelper(this);
040    
041        /**
042         * Create a new CatalogFilter.
043         *
044         * @param cOrg the Catalog to be filtered.
045         */
046        public CatalogFilter(Catalog<T> cOrg) {
047            super();
048    
049            m_cOrg = cOrg;
050        }
051    
052        /**
053         * Compare the source Catalog to the given object.
054         *
055         * @override Never
056         */
057        public int compareTo(Object o) {
058            return m_cOrg.compareTo(o);
059        }
060    
061        /**
062         * Detach the current name context from the source Catalog.
063         *
064         * @override Never
065         */
066        public NameContext detachNC() {
067            return m_cOrg.detachNC();
068        }
069    
070        /**
071         * Attach the given name context to the source Catalog.
072         *
073         * @override Never
074         */
075        public NameContext attach(NameContext nc) {
076            return m_cOrg.attach(nc);
077        }
078    
079        /**
080         * Get the source Catalog's name.
081         *
082         * @override Never
083         */
084        public String getName() {
085            return m_cOrg.getName();
086        }
087    
088        /**
089         * Set the source Catalog's name.
090         *
091         * @override Never
092         */
093        public void setName(String sName, DataBasket db) throws NameContextException {
094            m_cOrg.setName(sName, db);
095        }
096    
097        /**
098         * Get the source Catalog's value.
099         *
100         * @override Never
101         */
102        public Value getValue() {
103            return m_cOrg.getValue();
104        }
105    
106        /**
107         * Register the listener with the source Catalog.
108         *
109         * @override Never
110         */
111        public void addPropertyChangeListener(PropertyChangeListener pcl) {
112            m_cOrg.addPropertyChangeListener(pcl);
113        }
114    
115        /**
116         * Un-Register the listener with the source Catalog.
117         *
118         * @override Never
119         */
120        public void removePropertyChangeListener(PropertyChangeListener pcl) {
121            m_cOrg.removePropertyChangeListener(pcl);
122        }
123    
124        /**
125         * Register the listener with the source Catalog.
126         *
127         * @override Never
128         */
129        public void addNameListener(PropertyChangeListener pcl) {
130            m_cOrg.addNameListener(pcl);
131        }
132    
133        /**
134         * Un-Register the listener with the source Catalog.
135         *
136         * @override Never
137         */
138        public void removeNameListener(PropertyChangeListener pcl) {
139            m_cOrg.removeNameListener(pcl);
140        }
141    
142        /**
143         * Register the listener with the source Catalog.
144         *
145         * @override Never
146         */
147        public void addValueListener(PropertyChangeListener pcl) {
148            m_cOrg.addValueListener(pcl);
149        }
150    
151        /**
152         * Un-Register the listener with the source Catalog.
153         *
154         * @override Never
155         */
156        public void removeValueListener(PropertyChangeListener pcl) {
157            m_cOrg.removeValueListener(pcl);
158        }
159    
160        /**
161         * Get the source Catalog's Catalog.
162         *
163         * @override Never
164         */
165        public Catalog getCatalog() {
166            return m_cOrg.getCatalog();
167        }
168    
169        /**
170         * Get the source catalog. If the source catalog is a CatalogFilter again,
171         * return this Catalog's MainCatalog.
172         *
173         * @override Never
174         */
175        public Catalog getMainCatalog() {
176            if (m_cOrg instanceof CatalogFilter) {
177                return ((CatalogFilter)m_cOrg).getMainCatalog();
178            }
179    
180            return m_cOrg;
181        }
182    
183        /**
184         * Add the given item to the source Catalog.
185         *
186         * @override Never
187         */
188        public void add(T ci, DataBasket db) {
189            m_cOrg.add(ci, db);
190        }
191    
192        /**
193         * Remove the given item from the source Catalog if it is contained in the filtered Catalog.
194         *
195         * @override Never
196         */
197        public T remove(T ci, DataBasket db) throws VetoException {
198            if (match(ci)) {
199                return m_cOrg.remove(ci, db);
200            } else {
201                return null;
202            }
203        }
204    
205        /**
206         * Remove the given item from the source Catalog if it is contained in the filtered Catalog.
207         *
208         * @override Never
209         */
210        public T remove(String sKey, DataBasket db) throws VetoException {
211            if (get(sKey, db, false) != null) {
212                return m_cOrg.remove(sKey, db);
213            } else {
214                return null;
215            }
216        }
217    
218        /**
219         * Get the indicated item from the source Catalog if it is contained in the filtered Catalog.
220         *
221         * @override Never
222         */
223        public T get(String sKey, DataBasket db, boolean fForEdit) throws VetoException {
224            T ci = m_cOrg.get(sKey, db, fForEdit);
225    
226            if (match(ci)) {
227                return ci;
228            } else {
229                return null;
230            }
231        }
232    
233        /**
234         * Check whether the indicated item is contained in the filtered Catalog.
235         *
236         * @override Never
237         */
238        public boolean contains(String sKey, DataBasket db) {
239            if (m_cOrg.contains(sKey, db)) {
240                try {
241                    CatalogItem ci = get(sKey, db, false);
242    
243                    return (ci != null);
244                }
245                catch (VetoException e) {
246                    return false;
247                }
248            } else {
249                return false;
250            }
251        }
252    
253        /**
254         * An iterator that returns only items that are contained in the filtered Catalog.
255         *
256         * @author Steffen Zschaler
257         * @version 2.0 19/08/1999
258         * @since v2.0
259         */
260        private class FilteredIterator implements Iterator<T> {
261            private Iterator<T> m_i;
262            private T m_ciCurrent;
263    
264            public FilteredIterator(Iterator<T> i) {
265                m_i = i;
266            }
267    
268            public boolean hasNext() {
269                return findNext();
270            }
271    
272            public T next() {
273                if (!findNext()) {
274                    throw new NoSuchElementException();
275                }
276    
277                T ciReturn = m_ciCurrent;
278                m_ciCurrent = null;
279                return ciReturn;
280            }
281    
282            public void remove() {
283                m_i.remove(); // may have to be more sophisticated !
284            }
285    
286            /**
287             * Find the next item that matches the condition. Helper function.
288             *
289             * @return true if there was still a next item
290             */
291            private boolean findNext() {
292                if (m_ciCurrent != null) {
293                    return true;
294                }
295    
296                while (m_i.hasNext()) {
297                    m_ciCurrent = m_i.next();
298    
299                    if (match(m_ciCurrent)) {
300                        return true;
301                    }
302                }
303    
304                m_ciCurrent = null;
305                return false;
306            }
307        }
308        
309        /**
310         * Get an iterator of all items that are contained in the filtered Catalog.
311         *
312         * @override Never
313         */
314            public Iterator<T> iterator(DataBasket db, boolean fForEdit) {
315            return new FilteredIterator(m_cOrg.iterator(db, fForEdit));
316        }
317    
318        /**
319         * Return a set that contains all keys for which a CatalogItem is contained in the filtered Catalog.
320         *
321         * @override Never
322         */
323        public Set<String> keySet(final DataBasket db) {      
324            AbstractSet<String> m_set = new HashSet<String>();
325            Iterator<T> it = new FilteredIterator(m_cOrg.iterator(db, false));
326            while(it.hasNext())
327            {
328                    m_set.add(it.next().getName());
329            }
330            return m_set;
331        }
332    
333        /**
334         * Calculate the size of the filtered Catalog.
335         *
336         * @override Never
337         */
338        public int size(DataBasket db) {
339            if (m_cOrg.size(db) > 0) {
340                int nReturn = 0;
341    
342                for (Iterator<T> i = m_cOrg.iterator(db, false); i.hasNext(); ) {
343                    if (match(i.next())) {
344                        nReturn++;
345                    }
346                }
347    
348                return nReturn;
349            } else {
350                return 0;
351            }
352        }
353    
354        /**
355         * Filter condition.
356         *
357         * @param ci the item to be tested
358         *
359         * @return true if the given item shall be an item of the filtered Catalog.
360         *
361         * @override Always
362         */
363        protected abstract boolean match(T ci);
364    
365        // CatalogChangeListener interface methods
366    
367        /**
368         * Translate and propagate the event to all listeners of this Catalog.
369         *
370         * @override Never
371         */
372        public void addedCatalogItem(CatalogChangeEvent<T> e) {
373            fireCatalogItemAdded(e.getAffectedItem(), e.getBasket());
374        }
375    
376        /**
377         * Translate and propagate the event to all listeners of this Catalog.
378         *
379         * @override Never
380         */
381        public void commitedAddCatalogItem(CatalogChangeEvent<T> e) {
382            fireCatalogItemAddCommit(e.getAffectedItem(), e.getBasket());
383        }
384    
385        /**
386         * Translate and propagate the event to all listeners of this Catalog.
387         *
388         * @override Never
389         */
390        public void rolledbackAddCatalogItem(CatalogChangeEvent<T> e) {
391            fireCatalogItemAddRollback(e.getAffectedItem(), e.getBasket());
392        }
393    
394        /**
395         * Translate and propagate the event to all listeners of this Catalog.
396         *
397         * @override Never
398         */
399        public void canRemoveCatalogItem(CatalogChangeEvent<T> e) throws VetoException {
400            fireCanRemoveCatalogItem(e.getAffectedItem(), e.getBasket());
401        }
402    
403        /**
404         * Translate and propagate the event to all listeners of this Catalog.
405         *
406         * @override Never
407         */
408        public void noRemoveCatalogItem(CatalogChangeEvent<T> e) {
409            fireNoRemoveCatalogItem(e.getAffectedItem(), e.getBasket());
410        }
411    
412        /**
413         * Translate and propagate the event to all listeners of this Catalog.
414         *
415         * @override Never
416         */
417        public void removedCatalogItem(CatalogChangeEvent<T> e) {
418            fireCatalogItemRemoved(e.getAffectedItem(), e.getBasket());
419        }
420    
421        /**
422         * Translate and propagate the event to all listeners of this Catalog.
423         *
424         * @override Never
425         */
426        public void commitedRemoveCatalogItem(CatalogChangeEvent<T> e) {
427            fireCatalogItemRemoveCommit(e.getAffectedItem(), e.getBasket());
428        }
429    
430        /**
431         * Translate and propagate the event to all listeners of this Catalog.
432         *
433         * @override Never
434         */
435        public void rolledbackRemoveCatalogItem(CatalogChangeEvent<T> e) {
436            fireCatalogItemRemoveRollback(e.getAffectedItem(), e.getBasket());
437        }
438    
439        /**
440         * Translate and propagate the event to all listeners of this Catalog.
441         *
442         * @override Never
443         */
444        public void canEditCatalogItem(CatalogChangeEvent<T> e) throws VetoException {
445            fireCanEditCatalogItem(e.getAffectedItem(), e.getBasket());
446        }
447    
448        /**
449         * Translate and propagate the event to all listeners of this Catalog.
450         *
451         * @override Never
452         */
453        public void noEditCatalogItem(CatalogChangeEvent<T> e) {
454            fireNoEditCatalogItem(e.getAffectedItem(), e.getBasket());
455        }
456    
457        /**
458         * Translate and propagate the event to all listeners of this Catalog.
459         *
460         * @override Never
461         */
462        public void editingCatalogItem(CatalogChangeEvent<T> e) {
463            fireEditingCatalogItem(e.getAffectedItem(), e.getBasket());
464        }
465    
466        /**
467         * Translate and propagate the event to all listeners of this Catalog.
468         *
469         * @override Never
470         */
471        public void commitEditCatalogItem(CatalogChangeEvent<T> e) {
472            fireCommitEditCatalogItem(e.getAffectedItem(), e.getBasket());
473        }
474    
475        /**
476         * Translate and propagate the event to all listeners of this Catalog.
477         *
478         * @override Never
479         */
480        public void rollbackEditCatalogItem(CatalogChangeEvent<T> e) {
481            fireRollbackEditCatalogItem(e.getAffectedItem(), e.getBasket());
482        }
483    
484        // ListenableCatalog interface methods
485    
486        /**
487         * Add a listener that wishes to receive events when the filtered Catalog changes.
488         *
489         * @override Never
490         */
491        public void addCatalogChangeListener(CatalogChangeListener ccl) {
492            m_lhListeners.add(CatalogChangeListener.class, ccl);
493        }
494    
495        /**
496         * Remove a listener that received events when the filtered Catalog changed.
497         *
498         * @override Never
499         */
500        public void removeCatalogChangeListener(CatalogChangeListener ccl) {
501            m_lhListeners.remove(CatalogChangeListener.class, ccl);
502        }
503    
504        /**
505         * Fire the event to all listeners of this Catalog.
506         *
507         * @override Never
508         */
509        @SuppressWarnings("unchecked")
510            protected void fireCatalogItemAdded(T ci, DataBasket db) {
511            Object[] listeners = m_lhListeners.getListenerList();
512            CatalogChangeEvent cce = null;
513    
514            for (int i = listeners.length - 2; i >= 0; i -= 2) {
515                if (listeners[i] == CatalogChangeListener.class) {
516                    if (cce == null) {
517                        cce = new CatalogChangeEvent<T>(this, ci, db);
518                    }
519    
520                    ((CatalogChangeListener)listeners[i + 1]).addedCatalogItem(cce);
521                }
522            }
523        }
524    
525        /**
526         * Fire the event to all listeners of this Catalog.
527         *
528         * @override Never
529         */
530        @SuppressWarnings("unchecked")
531        protected void fireCatalogItemAddCommit(T ci, DataBasket db) {
532            Object[] listeners = m_lhListeners.getListenerList();
533            CatalogChangeEvent cce = null;
534    
535            for (int i = listeners.length - 2; i >= 0; i -= 2) {
536                if (listeners[i] == CatalogChangeListener.class) {
537                    if (cce == null) {
538                        cce = new CatalogChangeEvent<T>(this, ci, db);
539                    }
540    
541                    ((CatalogChangeListener)listeners[i + 1]).commitedAddCatalogItem(cce);
542                }
543            }
544        }
545    
546        /**
547         * Fire the event to all listeners of this Catalog.
548         *
549         * @override Never
550         */
551        @SuppressWarnings("unchecked")
552        protected void fireCatalogItemAddRollback(T ci, DataBasket db) {
553            Object[] listeners = m_lhListeners.getListenerList();
554            CatalogChangeEvent cce = null;
555    
556            for (int i = listeners.length - 2; i >= 0; i -= 2) {
557                if (listeners[i] == CatalogChangeListener.class) {
558                    if (cce == null) {
559                        cce = new CatalogChangeEvent<T>(this, ci, db);
560                    }
561    
562                    ((CatalogChangeListener)listeners[i + 1]).rolledbackAddCatalogItem(cce);
563                }
564            }
565        }
566    
567        /**
568         * Fire the event to all listeners of this Catalog.
569         *
570         * @override Never
571         */
572        @SuppressWarnings("unchecked")
573        protected void fireCatalogItemRemoved(T ci, DataBasket db) {
574            Object[] listeners = m_lhListeners.getListenerList();
575            CatalogChangeEvent cce = null;
576    
577            for (int i = listeners.length - 2; i >= 0; i -= 2) {
578                if (listeners[i] == CatalogChangeListener.class) {
579                    if (cce == null) {
580                        cce = new CatalogChangeEvent<T>(this, ci, db);
581                    }
582    
583                    ((CatalogChangeListener)listeners[i + 1]).removedCatalogItem(cce);
584                }
585            }
586        }
587    
588        /**
589         * Fire the event to all listeners of this Catalog.
590         *
591         * @override Never
592         */
593        @SuppressWarnings("unchecked")
594        protected void fireCatalogItemRemoveCommit(T ci, DataBasket db) {
595            Object[] listeners = m_lhListeners.getListenerList();
596            CatalogChangeEvent cce = null;
597    
598            for (int i = listeners.length - 2; i >= 0; i -= 2) {
599                if (listeners[i] == CatalogChangeListener.class) {
600                    if (cce == null) {
601                        cce = new CatalogChangeEvent<T>(this, ci, db);
602                    }
603    
604                    ((CatalogChangeListener)listeners[i + 1]).commitedRemoveCatalogItem(cce);
605                }
606            }
607        }
608    
609        /**
610         * Fire the event to all listeners of this Catalog.
611         *
612         * @override Never
613         */
614        @SuppressWarnings("unchecked")
615        protected void fireCatalogItemRemoveRollback(T ci, DataBasket db) {
616            Object[] listeners = m_lhListeners.getListenerList();
617            CatalogChangeEvent cce = null;
618    
619            for (int i = listeners.length - 2; i >= 0; i -= 2) {
620                if (listeners[i] == CatalogChangeListener.class) {
621                    if (cce == null) {
622                        cce = new CatalogChangeEvent<T>(this, ci, db);
623                    }
624    
625                    ((CatalogChangeListener)listeners[i + 1]).rolledbackRemoveCatalogItem(cce);
626                }
627            }
628        }
629    
630        /**
631         * Fire the event to all listeners of this Catalog.
632         *
633         * @override Never
634         */
635        @SuppressWarnings("unchecked")
636        protected void fireCanRemoveCatalogItem(T ci, DataBasket db) throws VetoException {
637            Object[] temp = m_lhListeners.getListenerList();
638            Object[] listeners = new Object[temp.length];
639            System.arraycopy(temp, 0, listeners, 0, temp.length);
640    
641            CatalogChangeEvent cce = null;
642    
643            for (int i = listeners.length - 2; i >= 0; i -= 2) {
644                if (listeners[i] == CatalogChangeListener.class) {
645                    if (cce == null) {
646                        cce = new CatalogChangeEvent<T>(this, ci, db);
647                    }
648    
649                    ((CatalogChangeListener)listeners[i + 1]).canRemoveCatalogItem(cce);
650                }
651            }
652        }
653    
654        /**
655         * Fire the event to all listeners of this Catalog.
656         *
657         * @override Never
658         */
659        @SuppressWarnings("unchecked")
660        protected void fireNoRemoveCatalogItem(T ci, DataBasket db) {
661            Object[] listeners = m_lhListeners.getListenerList();
662            CatalogChangeEvent cce = null;
663    
664            for (int i = listeners.length - 2; i >= 0; i -= 2) {
665                if (listeners[i] == CatalogChangeListener.class) {
666                    if (cce == null) {
667                        cce = new CatalogChangeEvent<T>(this, ci, db);
668                    }
669    
670                    ((CatalogChangeListener)listeners[i + 1]).noRemoveCatalogItem(cce);
671                }
672            }
673        }
674    
675        /**
676         * Fire the event to all listeners of this Catalog.
677         *
678         * @override Never
679         */
680        @SuppressWarnings("unchecked")
681        protected void fireCanEditCatalogItem(T ci, DataBasket db) throws VetoException {
682            Object[] temp = m_lhListeners.getListenerList();
683            Object[] listeners = new Object[temp.length];
684            System.arraycopy(temp, 0, listeners, 0, temp.length);
685    
686            CatalogChangeEvent cce = null;
687    
688            for (int i = listeners.length - 2; i >= 0; i -= 2) {
689                if (listeners[i] == CatalogChangeListener.class) {
690                    if (cce == null) {
691                        cce = new CatalogChangeEvent<T>(this, ci, db);
692                    }
693    
694                    ((CatalogChangeListener)listeners[i + 1]).canEditCatalogItem(cce);
695                }
696            }
697        }
698    
699        /**
700         * Fire the event to all listeners of this Catalog.
701         *
702         * @override Never
703         */
704        @SuppressWarnings("unchecked")
705        protected void fireNoEditCatalogItem(T ci, DataBasket db) {
706            Object[] listeners = m_lhListeners.getListenerList();
707            CatalogChangeEvent cce = null;
708    
709            for (int i = listeners.length - 2; i >= 0; i -= 2) {
710                if (listeners[i] == CatalogChangeListener.class) {
711                    if (cce == null) {
712                        cce = new CatalogChangeEvent<T>(this, ci, db);
713                    }
714    
715                    ((CatalogChangeListener)listeners[i + 1]).noEditCatalogItem(cce);
716                }
717            }
718        }
719    
720        /**
721         * Fire the event to all listeners of this Catalog.
722         *
723         * @override Never
724         */
725        @SuppressWarnings("unchecked")
726        protected void fireEditingCatalogItem(T ci, DataBasket db) {
727            Object[] listeners = m_lhListeners.getListenerList();
728    
729            CatalogChangeEvent cce = null;
730    
731            for (int i = listeners.length - 2; i >= 0; i -= 2) {
732                if (listeners[i] == CatalogChangeListener.class) {
733                    if (cce == null) {
734                        cce = new CatalogChangeEvent<T>(this, ci, db);
735                    }
736    
737                    ((CatalogChangeListener)listeners[i + 1]).editingCatalogItem(cce);
738                }
739            }
740        }
741    
742        /**
743         * Fire the event to all listeners of this Catalog.
744         *
745         * @override Never
746         */
747        @SuppressWarnings("unchecked")
748        protected void fireCommitEditCatalogItem(T ci, DataBasket db) {
749            Object[] listeners = m_lhListeners.getListenerList();
750    
751            CatalogChangeEvent cce = null;
752    
753            for (int i = listeners.length - 2; i >= 0; i -= 2) {
754                if (listeners[i] == CatalogChangeListener.class) {
755                    if (cce == null) {
756                        cce = new CatalogChangeEvent<T>(this, ci, db);
757                    }
758    
759                    ((CatalogChangeListener)listeners[i + 1]).commitEditCatalogItem(cce);
760                }
761            }
762        }
763    
764        /**
765         * Fire the event to all listeners of this Catalog.
766         *
767         * @override Never
768         */
769        @SuppressWarnings("unchecked")
770        protected void fireRollbackEditCatalogItem(T ci, DataBasket db) {
771            Object[] listeners = m_lhListeners.getListenerList();
772    
773            CatalogChangeEvent cce = null;
774    
775            for (int i = listeners.length - 2; i >= 0; i -= 2) {
776                if (listeners[i] == CatalogChangeListener.class) {
777                    if (cce == null) {
778                        cce = new CatalogChangeEvent<T>(this, ci, db);
779                    }
780    
781                    ((CatalogChangeListener)listeners[i + 1]).rollbackEditCatalogItem(cce);
782                }
783            }
784        }
785    
786        // HelpableListener interface methods
787        /**
788         * Subscribe as a listener to the source Catalog if that is a {@link ListenableCatalog}.
789         *
790         * @override Never
791         */
792        @SuppressWarnings("unchecked")
793        public void subscribe() {
794            if (m_cOrg instanceof ListenableCatalog) {
795                ((ListenableCatalog)m_cOrg).addCatalogChangeListener(this);
796            }
797        }
798    
799        /**
800         * Un-Subscribe as a listener from the source Catalog if that is a {@link ListenableCatalog}.
801         *
802         * @override Never
803         */
804        @SuppressWarnings("unchecked")
805        public void unsubscribe() {
806            if (m_cOrg instanceof ListenableCatalog) {
807                ((ListenableCatalog)m_cOrg).removeCatalogChangeListener(this);
808            }
809        }
810    
811        /**
812         * Empty method body.
813         *
814         * @override Never
815         */
816        public void updateModel() {}
817    }