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 extends Object implements Catalog, CatalogChangeListener,
025            ListenableCatalog, HelpableListener {
026    
027        /**
028         * The Catalog that is being filtered.
029         *
030         * @serial
031         */
032        protected Catalog 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 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(CatalogItem 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 CatalogItem remove(CatalogItem 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 CatalogItem 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 CatalogItem get(String sKey, DataBasket db, boolean fForEdit) throws VetoException {
224            CatalogItem 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 {
261            private Iterator m_i;
262            private Object m_oCurrent;
263    
264            public FilteredIterator(Iterator i) {
265                m_i = i;
266            }
267    
268            public boolean hasNext() {
269                return findNext();
270            }
271    
272            public Object next() {
273                if (!findNext()) {
274                    throw new NoSuchElementException();
275                }
276    
277                Object oReturn = m_oCurrent;
278                m_oCurrent = null;
279                return oReturn;
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_oCurrent != null) {
293                    return true;
294                }
295    
296                while (m_i.hasNext()) {
297                    m_oCurrent = m_i.next();
298    
299                    if (match((CatalogItem)m_oCurrent)) {
300                        return true;
301                    }
302                }
303    
304                m_oCurrent = 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 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 keySet(final DataBasket db) {
324            // return filtered set
325            class S extends AbstractSet {
326                public Iterator iterator() {
327                    return new FilteredIterator(m_cOrg.iterator(db, false)) {
328                        public Object next() {
329                            CatalogItem ci = (CatalogItem)super.next();
330                            return ci.getName();
331                        }
332    
333                        public void remove() {
334                            throw new UnsupportedOperationException();
335                        }
336                    };
337                }
338    
339                public int size() {
340                    return CatalogFilter.this.size(db);
341                }
342            }
343    
344            return new S();
345        }
346    
347        /**
348         * Calculate the size of the filtered Catalog.
349         *
350         * @override Never
351         */
352        public int size(DataBasket db) {
353            if (m_cOrg.size(db) > 0) {
354                int nReturn = 0;
355    
356                for (Iterator i = m_cOrg.iterator(db, false); i.hasNext(); ) {
357                    if (match((CatalogItem)i.next())) {
358                        nReturn++;
359                    }
360                }
361    
362                return nReturn;
363            } else {
364                return 0;
365            }
366        }
367    
368        /**
369         * Filter condition.
370         *
371         * @param ci the item to be tested
372         *
373         * @return true if the given item shall be an item of the filtered Catalog.
374         *
375         * @override Always
376         */
377        protected abstract boolean match(CatalogItem ci);
378    
379        // CatalogChangeListener interface methods
380    
381        /**
382         * Translate and propagate the event to all listeners of this Catalog.
383         *
384         * @override Never
385         */
386        public void addedCatalogItem(CatalogChangeEvent e) {
387            fireCatalogItemAdded(e.getAffectedItem(), e.getBasket());
388        }
389    
390        /**
391         * Translate and propagate the event to all listeners of this Catalog.
392         *
393         * @override Never
394         */
395        public void commitedAddCatalogItem(CatalogChangeEvent e) {
396            fireCatalogItemAddCommit(e.getAffectedItem(), e.getBasket());
397        }
398    
399        /**
400         * Translate and propagate the event to all listeners of this Catalog.
401         *
402         * @override Never
403         */
404        public void rolledbackAddCatalogItem(CatalogChangeEvent e) {
405            fireCatalogItemAddRollback(e.getAffectedItem(), e.getBasket());
406        }
407    
408        /**
409         * Translate and propagate the event to all listeners of this Catalog.
410         *
411         * @override Never
412         */
413        public void canRemoveCatalogItem(CatalogChangeEvent e) throws VetoException {
414            fireCanRemoveCatalogItem(e.getAffectedItem(), e.getBasket());
415        }
416    
417        /**
418         * Translate and propagate the event to all listeners of this Catalog.
419         *
420         * @override Never
421         */
422        public void noRemoveCatalogItem(CatalogChangeEvent e) {
423            fireNoRemoveCatalogItem(e.getAffectedItem(), e.getBasket());
424        }
425    
426        /**
427         * Translate and propagate the event to all listeners of this Catalog.
428         *
429         * @override Never
430         */
431        public void removedCatalogItem(CatalogChangeEvent e) {
432            fireCatalogItemRemoved(e.getAffectedItem(), e.getBasket());
433        }
434    
435        /**
436         * Translate and propagate the event to all listeners of this Catalog.
437         *
438         * @override Never
439         */
440        public void commitedRemoveCatalogItem(CatalogChangeEvent e) {
441            fireCatalogItemRemoveCommit(e.getAffectedItem(), e.getBasket());
442        }
443    
444        /**
445         * Translate and propagate the event to all listeners of this Catalog.
446         *
447         * @override Never
448         */
449        public void rolledbackRemoveCatalogItem(CatalogChangeEvent e) {
450            fireCatalogItemRemoveRollback(e.getAffectedItem(), e.getBasket());
451        }
452    
453        /**
454         * Translate and propagate the event to all listeners of this Catalog.
455         *
456         * @override Never
457         */
458        public void canEditCatalogItem(CatalogChangeEvent e) throws VetoException {
459            fireCanEditCatalogItem(e.getAffectedItem(), e.getBasket());
460        }
461    
462        /**
463         * Translate and propagate the event to all listeners of this Catalog.
464         *
465         * @override Never
466         */
467        public void noEditCatalogItem(CatalogChangeEvent e) {
468            fireNoEditCatalogItem(e.getAffectedItem(), e.getBasket());
469        }
470    
471        /**
472         * Translate and propagate the event to all listeners of this Catalog.
473         *
474         * @override Never
475         */
476        public void editingCatalogItem(CatalogChangeEvent e) {
477            fireEditingCatalogItem(e.getAffectedItem(), e.getBasket());
478        }
479    
480        /**
481         * Translate and propagate the event to all listeners of this Catalog.
482         *
483         * @override Never
484         */
485        public void commitEditCatalogItem(CatalogChangeEvent e) {
486            fireCommitEditCatalogItem(e.getAffectedItem(), e.getBasket());
487        }
488    
489        /**
490         * Translate and propagate the event to all listeners of this Catalog.
491         *
492         * @override Never
493         */
494        public void rollbackEditCatalogItem(CatalogChangeEvent e) {
495            fireRollbackEditCatalogItem(e.getAffectedItem(), e.getBasket());
496        }
497    
498        // ListenableCatalog interface methods
499    
500        /**
501         * Add a listener that wishes to receive events when the filtered Catalog changes.
502         *
503         * @override Never
504         */
505        public void addCatalogChangeListener(CatalogChangeListener ccl) {
506            m_lhListeners.add(CatalogChangeListener.class, ccl);
507        }
508    
509        /**
510         * Remove a listener that received events when the filtered Catalog changed.
511         *
512         * @override Never
513         */
514        public void removeCatalogChangeListener(CatalogChangeListener ccl) {
515            m_lhListeners.remove(CatalogChangeListener.class, ccl);
516        }
517    
518        /**
519         * Fire the event to all listeners of this Catalog.
520         *
521         * @override Never
522         */
523        protected void fireCatalogItemAdded(CatalogItem ci, DataBasket db) {
524            Object[] listeners = m_lhListeners.getListenerList();
525            CatalogChangeEvent cce = null;
526    
527            for (int i = listeners.length - 2; i >= 0; i -= 2) {
528                if (listeners[i] == CatalogChangeListener.class) {
529                    if (cce == null) {
530                        cce = new CatalogChangeEvent(this, ci, db);
531                    }
532    
533                    ((CatalogChangeListener)listeners[i + 1]).addedCatalogItem(cce);
534                }
535            }
536        }
537    
538        /**
539         * Fire the event to all listeners of this Catalog.
540         *
541         * @override Never
542         */
543        protected void fireCatalogItemAddCommit(CatalogItem ci, DataBasket db) {
544            Object[] listeners = m_lhListeners.getListenerList();
545            CatalogChangeEvent cce = null;
546    
547            for (int i = listeners.length - 2; i >= 0; i -= 2) {
548                if (listeners[i] == CatalogChangeListener.class) {
549                    if (cce == null) {
550                        cce = new CatalogChangeEvent(this, ci, db);
551                    }
552    
553                    ((CatalogChangeListener)listeners[i + 1]).commitedAddCatalogItem(cce);
554                }
555            }
556        }
557    
558        /**
559         * Fire the event to all listeners of this Catalog.
560         *
561         * @override Never
562         */
563        protected void fireCatalogItemAddRollback(CatalogItem ci, DataBasket db) {
564            Object[] listeners = m_lhListeners.getListenerList();
565            CatalogChangeEvent cce = null;
566    
567            for (int i = listeners.length - 2; i >= 0; i -= 2) {
568                if (listeners[i] == CatalogChangeListener.class) {
569                    if (cce == null) {
570                        cce = new CatalogChangeEvent(this, ci, db);
571                    }
572    
573                    ((CatalogChangeListener)listeners[i + 1]).rolledbackAddCatalogItem(cce);
574                }
575            }
576        }
577    
578        /**
579         * Fire the event to all listeners of this Catalog.
580         *
581         * @override Never
582         */
583        protected void fireCatalogItemRemoved(CatalogItem ci, DataBasket db) {
584            Object[] listeners = m_lhListeners.getListenerList();
585            CatalogChangeEvent cce = null;
586    
587            for (int i = listeners.length - 2; i >= 0; i -= 2) {
588                if (listeners[i] == CatalogChangeListener.class) {
589                    if (cce == null) {
590                        cce = new CatalogChangeEvent(this, ci, db);
591                    }
592    
593                    ((CatalogChangeListener)listeners[i + 1]).removedCatalogItem(cce);
594                }
595            }
596        }
597    
598        /**
599         * Fire the event to all listeners of this Catalog.
600         *
601         * @override Never
602         */
603        protected void fireCatalogItemRemoveCommit(CatalogItem ci, DataBasket db) {
604            Object[] listeners = m_lhListeners.getListenerList();
605            CatalogChangeEvent cce = null;
606    
607            for (int i = listeners.length - 2; i >= 0; i -= 2) {
608                if (listeners[i] == CatalogChangeListener.class) {
609                    if (cce == null) {
610                        cce = new CatalogChangeEvent(this, ci, db);
611                    }
612    
613                    ((CatalogChangeListener)listeners[i + 1]).commitedRemoveCatalogItem(cce);
614                }
615            }
616        }
617    
618        /**
619         * Fire the event to all listeners of this Catalog.
620         *
621         * @override Never
622         */
623        protected void fireCatalogItemRemoveRollback(CatalogItem ci, DataBasket db) {
624            Object[] listeners = m_lhListeners.getListenerList();
625            CatalogChangeEvent cce = null;
626    
627            for (int i = listeners.length - 2; i >= 0; i -= 2) {
628                if (listeners[i] == CatalogChangeListener.class) {
629                    if (cce == null) {
630                        cce = new CatalogChangeEvent(this, ci, db);
631                    }
632    
633                    ((CatalogChangeListener)listeners[i + 1]).rolledbackRemoveCatalogItem(cce);
634                }
635            }
636        }
637    
638        /**
639         * Fire the event to all listeners of this Catalog.
640         *
641         * @override Never
642         */
643        protected void fireCanRemoveCatalogItem(CatalogItem ci, DataBasket db) throws VetoException {
644            Object[] temp = m_lhListeners.getListenerList();
645            Object[] listeners = new Object[temp.length];
646            System.arraycopy(temp, 0, listeners, 0, temp.length);
647    
648            CatalogChangeEvent cce = null;
649    
650            for (int i = listeners.length - 2; i >= 0; i -= 2) {
651                if (listeners[i] == CatalogChangeListener.class) {
652                    if (cce == null) {
653                        cce = new CatalogChangeEvent(this, ci, db);
654                    }
655    
656                    ((CatalogChangeListener)listeners[i + 1]).canRemoveCatalogItem(cce);
657                }
658            }
659        }
660    
661        /**
662         * Fire the event to all listeners of this Catalog.
663         *
664         * @override Never
665         */
666        protected void fireNoRemoveCatalogItem(CatalogItem ci, DataBasket db) {
667            Object[] listeners = m_lhListeners.getListenerList();
668            CatalogChangeEvent cce = null;
669    
670            for (int i = listeners.length - 2; i >= 0; i -= 2) {
671                if (listeners[i] == CatalogChangeListener.class) {
672                    if (cce == null) {
673                        cce = new CatalogChangeEvent(this, ci, db);
674                    }
675    
676                    ((CatalogChangeListener)listeners[i + 1]).noRemoveCatalogItem(cce);
677                }
678            }
679        }
680    
681        /**
682         * Fire the event to all listeners of this Catalog.
683         *
684         * @override Never
685         */
686        protected void fireCanEditCatalogItem(CatalogItem ci, DataBasket db) throws VetoException {
687            Object[] temp = m_lhListeners.getListenerList();
688            Object[] listeners = new Object[temp.length];
689            System.arraycopy(temp, 0, listeners, 0, temp.length);
690    
691            CatalogChangeEvent cce = null;
692    
693            for (int i = listeners.length - 2; i >= 0; i -= 2) {
694                if (listeners[i] == CatalogChangeListener.class) {
695                    if (cce == null) {
696                        cce = new CatalogChangeEvent(this, ci, db);
697                    }
698    
699                    ((CatalogChangeListener)listeners[i + 1]).canEditCatalogItem(cce);
700                }
701            }
702        }
703    
704        /**
705         * Fire the event to all listeners of this Catalog.
706         *
707         * @override Never
708         */
709        protected void fireNoEditCatalogItem(CatalogItem ci, DataBasket db) {
710            Object[] listeners = m_lhListeners.getListenerList();
711            CatalogChangeEvent cce = null;
712    
713            for (int i = listeners.length - 2; i >= 0; i -= 2) {
714                if (listeners[i] == CatalogChangeListener.class) {
715                    if (cce == null) {
716                        cce = new CatalogChangeEvent(this, ci, db);
717                    }
718    
719                    ((CatalogChangeListener)listeners[i + 1]).noEditCatalogItem(cce);
720                }
721            }
722        }
723    
724        /**
725         * Fire the event to all listeners of this Catalog.
726         *
727         * @override Never
728         */
729        protected void fireEditingCatalogItem(CatalogItem ci, DataBasket db) {
730            Object[] listeners = m_lhListeners.getListenerList();
731    
732            CatalogChangeEvent cce = null;
733    
734            for (int i = listeners.length - 2; i >= 0; i -= 2) {
735                if (listeners[i] == CatalogChangeListener.class) {
736                    if (cce == null) {
737                        cce = new CatalogChangeEvent(this, ci, db);
738                    }
739    
740                    ((CatalogChangeListener)listeners[i + 1]).editingCatalogItem(cce);
741                }
742            }
743        }
744    
745        /**
746         * Fire the event to all listeners of this Catalog.
747         *
748         * @override Never
749         */
750        protected void fireCommitEditCatalogItem(CatalogItem ci, DataBasket db) {
751            Object[] listeners = m_lhListeners.getListenerList();
752    
753            CatalogChangeEvent cce = null;
754    
755            for (int i = listeners.length - 2; i >= 0; i -= 2) {
756                if (listeners[i] == CatalogChangeListener.class) {
757                    if (cce == null) {
758                        cce = new CatalogChangeEvent(this, ci, db);
759                    }
760    
761                    ((CatalogChangeListener)listeners[i + 1]).commitEditCatalogItem(cce);
762                }
763            }
764        }
765    
766        /**
767         * Fire the event to all listeners of this Catalog.
768         *
769         * @override Never
770         */
771        protected void fireRollbackEditCatalogItem(CatalogItem ci, DataBasket db) {
772            Object[] listeners = m_lhListeners.getListenerList();
773    
774            CatalogChangeEvent cce = null;
775    
776            for (int i = listeners.length - 2; i >= 0; i -= 2) {
777                if (listeners[i] == CatalogChangeListener.class) {
778                    if (cce == null) {
779                        cce = new CatalogChangeEvent(this, ci, db);
780                    }
781    
782                    ((CatalogChangeListener)listeners[i + 1]).rollbackEditCatalogItem(cce);
783                }
784            }
785        }
786    
787        // HelpableListener interface methods
788        /**
789         * Subscribe as a listener to the source Catalog if that is a {@link ListenableCatalog}.
790         *
791         * @override Never
792         */
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        public void unsubscribe() {
805            if (m_cOrg instanceof ListenableCatalog) {
806                ((ListenableCatalog)m_cOrg).removeCatalogChangeListener(this);
807            }
808        }
809    
810        /**
811         * Empty method body.
812         *
813         * @override Never
814         */
815        public void updateModel() {}
816    }