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