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 }