001    /*
002     * Copyright 2003-2004 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.events.observable;
017    
018    import java.util.Set;
019    
020    import org.apache.commons.collections.Bag;
021    
022    /**
023     * Decorates a <code>Bag</code> implementation to observe modifications.
024     * <p>
025     * Each modifying method call made on this <code>Bag</code> is forwarded to a
026     * {@link ModificationHandler}.
027     * The handler manages the event, notifying listeners and optionally vetoing changes.
028     * The default handler is
029     * {@link org.apache.commons.events.observable.standard.StandardModificationHandler StandardModificationHandler}.
030     * See this class for details of configuration available.
031     * <p>
032     * NOTE: The {@link #uniqueSet()} method returns a <code>Set</code> that is
033     * NOT observed. This is because the set should be unmodifiable.
034     *
035     * @since Commons Events 1.0
036     * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $
037     * 
038     * @author Stephen Colebourne
039     */
040    public class ObservableBag extends ObservableCollection implements Bag {
041        
042        // Factories
043        //-----------------------------------------------------------------------
044        /**
045         * Factory method to create an observable bag.
046         * <p>
047         * A {@link org.apache.commons.events.observable.standard.StandardModificationHandler} will be created.
048         * This can be accessed by {@link #getHandler()} to add listeners.
049         *
050         * @param bag  the bag to decorate, must not be null
051         * @return the observed Bag
052         * @throws IllegalArgumentException if the bag is null
053         */
054        public static ObservableBag decorate(final Bag bag) {
055            return new ObservableBag(bag, null);
056        }
057    
058        /**
059         * Factory method to create an observable bag using a listener or a handler.
060         * <p>
061         * A lot of functionality is available through this method.
062         * If you don't need the extra functionality, simply implement the
063         * {@link org.apache.commons.events.observable.standard.StandardModificationListener}
064         * interface and pass it in as the second parameter.
065         * <p>
066         * Internally, an <code>ObservableBag</code> relies on a {@link ModificationHandler}.
067         * The handler receives all the events and processes them, typically by
068         * calling listeners. Different handler implementations can be plugged in
069         * to provide a flexible event system.
070         * <p>
071         * The handler implementation is determined by the listener parameter via
072         * the registered factories. The listener may be a manually configured 
073         * <code>ModificationHandler</code> instance.
074         * <p>
075         * The listener is defined as an Object for maximum flexibility.
076         * It does not have to be a listener in the classic JavaBean sense.
077         * It is entirely up to the factory and handler as to how the parameter
078         * is interpretted. An IllegalArgumentException is thrown if no suitable
079         * handler can be found for this listener.
080         * <p>
081         * A <code>null</code> listener will create a
082         * {@link org.apache.commons.events.observable.standard.StandardModificationHandler}.
083         *
084         * @param bag  the bag to decorate, must not be null
085         * @param listener  bag listener, may be null
086         * @return the observed bag
087         * @throws IllegalArgumentException if the bag is null
088         * @throws IllegalArgumentException if there is no valid handler for the listener
089         */
090        public static ObservableBag decorate(
091                final Bag bag,
092                final Object listener) {
093            
094            if (bag == null) {
095                throw new IllegalArgumentException("Bag must not be null");
096            }
097            return new ObservableBag(bag, listener);
098        }
099    
100        // Constructors
101        //-----------------------------------------------------------------------
102        /**
103         * Constructor that wraps (not copies).
104         * <p>
105         * The handler implementation is determined by the listener parameter via
106         * the registered factories. The listener may be a manually configured 
107         * <code>ModificationHandler</code> instance.
108         * 
109         * @param bag  the bag to decorate, must not be null
110         * @param listener  the listener, may be null
111         * @throws IllegalArgumentException if the bag is null
112         */
113        protected ObservableBag(
114                final Bag bag,
115                final Object listener) {
116            super(bag, listener);
117        }
118        
119        /**
120         * Typecast the collection to a Bag.
121         * 
122         * @return the wrapped collection as a Bag
123         */
124        private Bag getBag() {
125            return (Bag) getCollection();
126        }
127    
128        // Bag API
129        //-----------------------------------------------------------------------
130        public int getCount(Object object) {
131            return getBag().getCount(object);
132        }
133    
134        public Set uniqueSet() {
135            return getBag().uniqueSet();
136        }
137    
138        //-----------------------------------------------------------------------
139        public boolean add(Object object) {
140            // override as Bag violates Collection contract
141            boolean result = false;
142            if (handler.preAdd(object)) {
143                result = collection.add(object);
144                handler.postAdd(object, true);  // true, as result is misleading
145            }
146            return result;
147        }
148    
149        public boolean add(Object object, int nCopies) {
150            boolean result = false;
151            if (handler.preAddNCopies(object, nCopies)) {
152                result = getBag().add(object, nCopies);
153                handler.postAddNCopies(object, nCopies, result);
154            }
155            return result;
156        }
157    
158        public boolean remove(Object object, int nCopies) {
159            boolean result = false;
160            if (handler.preRemoveNCopies(object, nCopies)) {
161                result = getBag().remove(object, nCopies);
162                handler.postRemoveNCopies(object, nCopies, result);
163            }
164            return result;
165        }
166    
167    }