Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
ObservableCollection |
|
| 1.9411764705882353;1.941 | ||||
ObservableCollection$ObservableIterator |
|
| 1.9411764705882353;1.941 |
1 | /* | |
2 | * Copyright 2003-2004 The Apache Software Foundation | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | package org.apache.commons.events.observable; | |
17 | ||
18 | import java.util.Collection; | |
19 | import java.util.Iterator; | |
20 | ||
21 | import org.apache.commons.collections.collection.AbstractCollectionDecorator; | |
22 | import org.apache.commons.collections.iterators.AbstractIteratorDecorator; | |
23 | import org.apache.commons.events.observable.standard.StandardModificationHandler; | |
24 | ||
25 | /** | |
26 | * Decorates a <code>Collection</code> implementation to observe modifications. | |
27 | * <p> | |
28 | * Each modifying method call made on this <code>Collection</code> is forwarded to a | |
29 | * {@link ModificationHandler}. | |
30 | * The handler manages the event, notifying listeners and optionally vetoing changes. | |
31 | * The default handler is {@link StandardModificationHandler}. | |
32 | * See this class for details of configuration available. | |
33 | * | |
34 | * @since Commons Events 1.0 | |
35 | * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $ | |
36 | * | |
37 | * @author Stephen Colebourne | |
38 | */ | |
39 | public class ObservableCollection extends AbstractCollectionDecorator { | |
40 | ||
41 | /** The list of registered factories, checked in reverse order */ | |
42 | 0 | private static ModificationHandlerFactory[] factories = new ModificationHandlerFactory[] { |
43 | ModificationHandler.FACTORY, | |
44 | StandardModificationHandler.FACTORY | |
45 | }; | |
46 | ||
47 | /** The handler to delegate event handling to */ | |
48 | protected final ModificationHandler handler; | |
49 | ||
50 | // ObservableCollection factories | |
51 | //----------------------------------------------------------------------- | |
52 | /** | |
53 | * Factory method to create an observable collection. | |
54 | * <p> | |
55 | * A {@link StandardModificationHandler} will be created. | |
56 | * This can be accessed by {@link #getHandler()} to add listeners. | |
57 | * | |
58 | * @param coll the collection to decorate, must not be null | |
59 | * @return the observed collection | |
60 | * @throws IllegalArgumentException if the collection is null | |
61 | */ | |
62 | public static ObservableCollection decorate(final Collection coll) { | |
63 | 0 | return new ObservableCollection(coll, null); |
64 | } | |
65 | ||
66 | /** | |
67 | * Factory method to create an observable collection using a listener or a handler. | |
68 | * <p> | |
69 | * A lot of functionality is available through this method. | |
70 | * If you don't need the extra functionality, simply implement the | |
71 | * {@link org.apache.commons.events.observable.standard.StandardModificationListener} | |
72 | * interface and pass it in as the second parameter. | |
73 | * <p> | |
74 | * Internally, an <code>ObservableCollection</code> relies on a {@link ModificationHandler}. | |
75 | * The handler receives all the events and processes them, typically by | |
76 | * calling listeners. Different handler implementations can be plugged in | |
77 | * to provide a flexible event system. | |
78 | * <p> | |
79 | * The handler implementation is determined by the listener parameter via | |
80 | * the registered factories. The listener may be a manually configured | |
81 | * <code>ModificationHandler</code> instance. | |
82 | * <p> | |
83 | * The listener is defined as an Object for maximum flexibility. | |
84 | * It does not have to be a listener in the classic JavaBean sense. | |
85 | * It is entirely up to the factory and handler as to how the parameter | |
86 | * is interpretted. An IllegalArgumentException is thrown if no suitable | |
87 | * handler can be found for this listener. | |
88 | * <p> | |
89 | * A <code>null</code> listener will create a {@link StandardModificationHandler}. | |
90 | * | |
91 | * @param coll the collection to decorate, must not be null | |
92 | * @param listener collection listener, may be null | |
93 | * @return the observed collection | |
94 | * @throws IllegalArgumentException if the collection is null | |
95 | * @throws IllegalArgumentException if there is no valid handler for the listener | |
96 | */ | |
97 | public static ObservableCollection decorate( | |
98 | final Collection coll, | |
99 | final Object listener) { | |
100 | ||
101 | 0 | if (coll == null) { |
102 | 0 | throw new IllegalArgumentException("Collection must not be null"); |
103 | } | |
104 | 0 | return new ObservableCollection(coll, listener); |
105 | } | |
106 | ||
107 | // Register for ModificationHandlerFactory | |
108 | //----------------------------------------------------------------------- | |
109 | /** | |
110 | * Registers a handler factory to be used for looking up a listener to | |
111 | * a handler. | |
112 | * <p> | |
113 | * This method is used to add your own event handler to the supplied ones. | |
114 | * Registering the factory will enable the {@link #decorate(Collection, Object)} | |
115 | * method to create your handler. | |
116 | * <p> | |
117 | * Each handler added becomes the first in the lookup chain. Thus it is | |
118 | * possible to override the default setup. | |
119 | * Obviously this should be done with care in a shared web environment! | |
120 | * <p> | |
121 | * This method is not guaranteed to be threadsafe. | |
122 | * It should only be called during initialization. | |
123 | * Problems will occur if two threads call this method at the same time. | |
124 | * | |
125 | * @param factory the factory to add, may be null | |
126 | */ | |
127 | public static void registerFactory(final ModificationHandlerFactory factory) { | |
128 | 0 | if (factory != null) { |
129 | // add at end, as checked in reverse order | |
130 | 0 | ModificationHandlerFactory[] array = new ModificationHandlerFactory[factories.length + 1]; |
131 | 0 | System.arraycopy(factories, 0, array, 0, factories.length); |
132 | 0 | array[factories.length] = factory; |
133 | 0 | factories = array; // atomic operation |
134 | } | |
135 | 0 | } |
136 | ||
137 | // Constructors | |
138 | //----------------------------------------------------------------------- | |
139 | /** | |
140 | * Constructor that wraps (not copies) and takes a handler. | |
141 | * <p> | |
142 | * The handler implementation is determined by the listener parameter via | |
143 | * the registered factories. The listener may be a manually configured | |
144 | * <code>ModificationHandler</code> instance. | |
145 | * | |
146 | * @param coll the collection to decorate, must not be null | |
147 | * @param listener the observing handler, may be null | |
148 | * @throws IllegalArgumentException if the collection is null | |
149 | */ | |
150 | protected ObservableCollection( | |
151 | final Collection coll, | |
152 | final Object listener) { | |
153 | 0 | super(coll); |
154 | 0 | this.handler = createHandler(coll, listener); |
155 | 0 | this.handler.init(this, coll); |
156 | 0 | } |
157 | ||
158 | /** | |
159 | * Constructor used by subclass views, such as subList. | |
160 | * | |
161 | * @param handler the observing handler, may be null | |
162 | * @param coll the collection to decorate, must not be null | |
163 | * @throws IllegalArgumentException if the collection is null | |
164 | */ | |
165 | protected ObservableCollection( | |
166 | final ModificationHandler handler, | |
167 | final Collection coll) { | |
168 | 0 | super(coll); |
169 | 0 | this.handler = handler; |
170 | 0 | } |
171 | ||
172 | /** | |
173 | * Creates a handler subclass based on the specified listener. | |
174 | * <p> | |
175 | * The method is defined in terms of an Object to allow for unusual | |
176 | * listeners, such as a Swing model object. | |
177 | * | |
178 | * @param listener a listener object to create a handler for | |
179 | * @return an instantiated handler with the listener attached | |
180 | * @throws IllegalArgumentException if no suitable handler | |
181 | */ | |
182 | protected ModificationHandler createHandler(final Collection coll, final Object listener) { | |
183 | 0 | if (listener == null) { |
184 | 0 | return new StandardModificationHandler(); |
185 | } | |
186 | 0 | ModificationHandlerFactory[] array = factories; // atomic operation |
187 | 0 | for (int i = array.length - 1; i >= 0 ; i--) { |
188 | 0 | ModificationHandler handler = array[i].createHandler(coll, listener); |
189 | 0 | if (handler != null) { |
190 | 0 | return handler; |
191 | } | |
192 | } | |
193 | 0 | throw new IllegalArgumentException("Unrecognised listener type: " + |
194 | (listener == null ? "null" : listener.getClass().getName())); | |
195 | } | |
196 | ||
197 | // Handler access | |
198 | //----------------------------------------------------------------------- | |
199 | /** | |
200 | * Gets the handler that is observing this collection. | |
201 | * | |
202 | * @return the observing handler, never null | |
203 | */ | |
204 | public ModificationHandler getHandler() { | |
205 | 0 | return handler; |
206 | } | |
207 | ||
208 | // Collection | |
209 | //----------------------------------------------------------------------- | |
210 | public boolean add(Object object) { | |
211 | 0 | boolean result = false; |
212 | 0 | if (handler.preAdd(object)) { |
213 | 0 | result = collection.add(object); |
214 | 0 | handler.postAdd(object, result); |
215 | } | |
216 | 0 | return result; |
217 | } | |
218 | ||
219 | public boolean addAll(Collection coll) { | |
220 | 0 | boolean result = false; |
221 | 0 | if (handler.preAddAll(coll)) { |
222 | 0 | result = collection.addAll(coll); |
223 | 0 | handler.postAddAll(coll, result); |
224 | } | |
225 | 0 | return result; |
226 | } | |
227 | ||
228 | public void clear() { | |
229 | 0 | if (handler.preClear()) { |
230 | 0 | collection.clear(); |
231 | 0 | handler.postClear(); |
232 | } | |
233 | 0 | } |
234 | ||
235 | public Iterator iterator() { | |
236 | 0 | return new ObservableIterator(collection.iterator()); |
237 | } | |
238 | ||
239 | public boolean remove(Object object) { | |
240 | 0 | boolean result = false; |
241 | 0 | if (handler.preRemove(object)) { |
242 | 0 | result = collection.remove(object); |
243 | 0 | handler.postRemove(object, result); |
244 | } | |
245 | 0 | return result; |
246 | } | |
247 | ||
248 | public boolean removeAll(Collection coll) { | |
249 | 0 | boolean result = false; |
250 | 0 | if (handler.preRemoveAll(coll)) { |
251 | 0 | result = collection.removeAll(coll); |
252 | 0 | handler.postRemoveAll(coll, result); |
253 | } | |
254 | 0 | return result; |
255 | } | |
256 | ||
257 | public boolean retainAll(Collection coll) { | |
258 | 0 | boolean result = false; |
259 | 0 | if (handler.preRetainAll(coll)) { |
260 | 0 | result = collection.retainAll(coll); |
261 | 0 | handler.postRetainAll(coll, result); |
262 | } | |
263 | 0 | return result; |
264 | } | |
265 | ||
266 | // Iterator | |
267 | //----------------------------------------------------------------------- | |
268 | /** | |
269 | * Inner class Iterator for the ObservableCollection. | |
270 | */ | |
271 | protected class ObservableIterator extends AbstractIteratorDecorator { | |
272 | ||
273 | 0 | protected int lastIndex = -1; |
274 | protected Object last; | |
275 | ||
276 | 0 | protected ObservableIterator(Iterator iterator) { |
277 | 0 | super(iterator); |
278 | 0 | } |
279 | ||
280 | public Object next() { | |
281 | 0 | last = super.next(); |
282 | 0 | lastIndex++; |
283 | 0 | return last; |
284 | } | |
285 | ||
286 | public void remove() { | |
287 | 0 | if (handler.preRemoveIterated(lastIndex, last)) { |
288 | 0 | iterator.remove(); |
289 | 0 | handler.postRemoveIterated(lastIndex, last); |
290 | 0 | lastIndex--; |
291 | } | |
292 | 0 | } |
293 | } | |
294 | ||
295 | } |