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<CatalogItem> {
261            private Iterator<CatalogItem> m_i;
262            private CatalogItem m_ciCurrent;
263    
264            public FilteredIterator(Iterator<CatalogItem> i) {
265                m_i = i;
266            }
267    
268            public boolean hasNext() {
269                return findNext();
270            }
271    
272            public CatalogItem next() {
273                if (!findNext()) {
274                    throw new NoSuchElementException();
275                }
276    
277                CatalogItem 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((CatalogItem)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<CatalogItem> 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<CatalogItem> 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<CatalogItem> 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(CatalogItem 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 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 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 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 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 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 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 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 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 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 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 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 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 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        protected void fireCatalogItemAdded(CatalogItem ci, DataBasket db) {
510            Object[] listeners = m_lhListeners.getListenerList();
511            CatalogChangeEvent cce = null;
512    
513            for (int i = listeners.length - 2; i >= 0; i -= 2) {
514                if (listeners[i] == CatalogChangeListener.class) {
515                    if (cce == null) {
516                        cce = new CatalogChangeEvent(this, ci, db);
517                    }
518    
519                    ((CatalogChangeListener)listeners[i + 1]).addedCatalogItem(cce);
520                }
521            }
522        }
523    
524        /**
525         * Fire the event to all listeners of this Catalog.
526         *
527         * @override Never
528         */
529        protected void fireCatalogItemAddCommit(CatalogItem ci, DataBasket db) {
530            Object[] listeners = m_lhListeners.getListenerList();
531            CatalogChangeEvent cce = null;
532    
533            for (int i = listeners.length - 2; i >= 0; i -= 2) {
534                if (listeners[i] == CatalogChangeListener.class) {
535                    if (cce == null) {
536                        cce = new CatalogChangeEvent(this, ci, db);
537                    }
538    
539                    ((CatalogChangeListener)listeners[i + 1]).commitedAddCatalogItem(cce);
540                }
541            }
542        }
543    
544        /**
545         * Fire the event to all listeners of this Catalog.
546         *
547         * @override Never
548         */
549        protected void fireCatalogItemAddRollback(CatalogItem ci, DataBasket db) {
550            Object[] listeners = m_lhListeners.getListenerList();
551            CatalogChangeEvent cce = null;
552    
553            for (int i = listeners.length - 2; i >= 0; i -= 2) {
554                if (listeners[i] == CatalogChangeListener.class) {
555                    if (cce == null) {
556                        cce = new CatalogChangeEvent(this, ci, db);
557                    }
558    
559                    ((CatalogChangeListener)listeners[i + 1]).rolledbackAddCatalogItem(cce);
560                }
561            }
562        }
563    
564        /**
565         * Fire the event to all listeners of this Catalog.
566         *
567         * @override Never
568         */
569        protected void fireCatalogItemRemoved(CatalogItem ci, DataBasket db) {
570            Object[] listeners = m_lhListeners.getListenerList();
571            CatalogChangeEvent cce = null;
572    
573            for (int i = listeners.length - 2; i >= 0; i -= 2) {
574                if (listeners[i] == CatalogChangeListener.class) {
575                    if (cce == null) {
576                        cce = new CatalogChangeEvent(this, ci, db);
577                    }
578    
579                    ((CatalogChangeListener)listeners[i + 1]).removedCatalogItem(cce);
580                }
581            }
582        }
583    
584        /**
585         * Fire the event to all listeners of this Catalog.
586         *
587         * @override Never
588         */
589        protected void fireCatalogItemRemoveCommit(CatalogItem ci, DataBasket db) {
590            Object[] listeners = m_lhListeners.getListenerList();
591            CatalogChangeEvent cce = null;
592    
593            for (int i = listeners.length - 2; i >= 0; i -= 2) {
594                if (listeners[i] == CatalogChangeListener.class) {
595                    if (cce == null) {
596                        cce = new CatalogChangeEvent(this, ci, db);
597                    }
598    
599                    ((CatalogChangeListener)listeners[i + 1]).commitedRemoveCatalogItem(cce);
600                }
601            }
602        }
603    
604        /**
605         * Fire the event to all listeners of this Catalog.
606         *
607         * @override Never
608         */
609        protected void fireCatalogItemRemoveRollback(CatalogItem ci, DataBasket db) {
610            Object[] listeners = m_lhListeners.getListenerList();
611            CatalogChangeEvent cce = null;
612    
613            for (int i = listeners.length - 2; i >= 0; i -= 2) {
614                if (listeners[i] == CatalogChangeListener.class) {
615                    if (cce == null) {
616                        cce = new CatalogChangeEvent(this, ci, db);
617                    }
618    
619                    ((CatalogChangeListener)listeners[i + 1]).rolledbackRemoveCatalogItem(cce);
620                }
621            }
622        }
623    
624        /**
625         * Fire the event to all listeners of this Catalog.
626         *
627         * @override Never
628         */
629        protected void fireCanRemoveCatalogItem(CatalogItem ci, DataBasket db) throws VetoException {
630            Object[] temp = m_lhListeners.getListenerList();
631            Object[] listeners = new Object[temp.length];
632            System.arraycopy(temp, 0, listeners, 0, temp.length);
633    
634            CatalogChangeEvent cce = null;
635    
636            for (int i = listeners.length - 2; i >= 0; i -= 2) {
637                if (listeners[i] == CatalogChangeListener.class) {
638                    if (cce == null) {
639                        cce = new CatalogChangeEvent(this, ci, db);
640                    }
641    
642                    ((CatalogChangeListener)listeners[i + 1]).canRemoveCatalogItem(cce);
643                }
644            }
645        }
646    
647        /**
648         * Fire the event to all listeners of this Catalog.
649         *
650         * @override Never
651         */
652        protected void fireNoRemoveCatalogItem(CatalogItem ci, DataBasket db) {
653            Object[] listeners = m_lhListeners.getListenerList();
654            CatalogChangeEvent cce = null;
655    
656            for (int i = listeners.length - 2; i >= 0; i -= 2) {
657                if (listeners[i] == CatalogChangeListener.class) {
658                    if (cce == null) {
659                        cce = new CatalogChangeEvent(this, ci, db);
660                    }
661    
662                    ((CatalogChangeListener)listeners[i + 1]).noRemoveCatalogItem(cce);
663                }
664            }
665        }
666    
667        /**
668         * Fire the event to all listeners of this Catalog.
669         *
670         * @override Never
671         */
672        protected void fireCanEditCatalogItem(CatalogItem ci, DataBasket db) throws VetoException {
673            Object[] temp = m_lhListeners.getListenerList();
674            Object[] listeners = new Object[temp.length];
675            System.arraycopy(temp, 0, listeners, 0, temp.length);
676    
677            CatalogChangeEvent cce = null;
678    
679            for (int i = listeners.length - 2; i >= 0; i -= 2) {
680                if (listeners[i] == CatalogChangeListener.class) {
681                    if (cce == null) {
682                        cce = new CatalogChangeEvent(this, ci, db);
683                    }
684    
685                    ((CatalogChangeListener)listeners[i + 1]).canEditCatalogItem(cce);
686                }
687            }
688        }
689    
690        /**
691         * Fire the event to all listeners of this Catalog.
692         *
693         * @override Never
694         */
695        protected void fireNoEditCatalogItem(CatalogItem ci, DataBasket db) {
696            Object[] listeners = m_lhListeners.getListenerList();
697            CatalogChangeEvent cce = null;
698    
699            for (int i = listeners.length - 2; i >= 0; i -= 2) {
700                if (listeners[i] == CatalogChangeListener.class) {
701                    if (cce == null) {
702                        cce = new CatalogChangeEvent(this, ci, db);
703                    }
704    
705                    ((CatalogChangeListener)listeners[i + 1]).noEditCatalogItem(cce);
706                }
707            }
708        }
709    
710        /**
711         * Fire the event to all listeners of this Catalog.
712         *
713         * @override Never
714         */
715        protected void fireEditingCatalogItem(CatalogItem ci, DataBasket db) {
716            Object[] listeners = m_lhListeners.getListenerList();
717    
718            CatalogChangeEvent cce = null;
719    
720            for (int i = listeners.length - 2; i >= 0; i -= 2) {
721                if (listeners[i] == CatalogChangeListener.class) {
722                    if (cce == null) {
723                        cce = new CatalogChangeEvent(this, ci, db);
724                    }
725    
726                    ((CatalogChangeListener)listeners[i + 1]).editingCatalogItem(cce);
727                }
728            }
729        }
730    
731        /**
732         * Fire the event to all listeners of this Catalog.
733         *
734         * @override Never
735         */
736        protected void fireCommitEditCatalogItem(CatalogItem ci, DataBasket db) {
737            Object[] listeners = m_lhListeners.getListenerList();
738    
739            CatalogChangeEvent cce = null;
740    
741            for (int i = listeners.length - 2; i >= 0; i -= 2) {
742                if (listeners[i] == CatalogChangeListener.class) {
743                    if (cce == null) {
744                        cce = new CatalogChangeEvent(this, ci, db);
745                    }
746    
747                    ((CatalogChangeListener)listeners[i + 1]).commitEditCatalogItem(cce);
748                }
749            }
750        }
751    
752        /**
753         * Fire the event to all listeners of this Catalog.
754         *
755         * @override Never
756         */
757        protected void fireRollbackEditCatalogItem(CatalogItem ci, DataBasket db) {
758            Object[] listeners = m_lhListeners.getListenerList();
759    
760            CatalogChangeEvent cce = null;
761    
762            for (int i = listeners.length - 2; i >= 0; i -= 2) {
763                if (listeners[i] == CatalogChangeListener.class) {
764                    if (cce == null) {
765                        cce = new CatalogChangeEvent(this, ci, db);
766                    }
767    
768                    ((CatalogChangeListener)listeners[i + 1]).rollbackEditCatalogItem(cce);
769                }
770            }
771        }
772    
773        // HelpableListener interface methods
774        /**
775         * Subscribe as a listener to the source Catalog if that is a {@link ListenableCatalog}.
776         *
777         * @override Never
778         */
779        public void subscribe() {
780            if (m_cOrg instanceof ListenableCatalog) {
781                ((ListenableCatalog)m_cOrg).addCatalogChangeListener(this);
782            }
783        }
784    
785        /**
786         * Un-Subscribe as a listener from the source Catalog if that is a {@link ListenableCatalog}.
787         *
788         * @override Never
789         */
790        public void unsubscribe() {
791            if (m_cOrg instanceof ListenableCatalog) {
792                ((ListenableCatalog)m_cOrg).removeCatalogChangeListener(this);
793            }
794        }
795    
796        /**
797         * Empty method body.
798         *
799         * @override Never
800         */
801        public void updateModel() {}
802    }