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 }