Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
StandardModificationHandler |
|
| 2.9583333333333335;2.958 | ||||
StandardModificationHandler$Factory |
|
| 2.9583333333333335;2.958 | ||||
StandardModificationHandler$PostHolder |
|
| 2.9583333333333335;2.958 | ||||
StandardModificationHandler$PreHolder |
|
| 2.9583333333333335;2.958 |
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.standard; | |
17 | ||
18 | import java.util.Collection; | |
19 | ||
20 | import org.apache.commons.events.observable.ModificationEventType; | |
21 | import org.apache.commons.events.observable.ModificationHandler; | |
22 | import org.apache.commons.events.observable.ModificationHandlerFactory; | |
23 | import org.apache.commons.events.observable.ObservableCollection; | |
24 | ||
25 | /** | |
26 | * The standard implementation of a <code>ModificationHandler</code> that | |
27 | * sends standard JavaBean style events to listeners. | |
28 | * <p> | |
29 | * The information gathered by this implementation is all that is available | |
30 | * as parameters or return values. | |
31 | * In addition, the <code>size</code> method is used on the collection. | |
32 | * All objects used are the real objects from the method calls, not clones. | |
33 | * <p> | |
34 | * Each listener can be filtered. There are separate filters for pre and post | |
35 | * modification events. | |
36 | * | |
37 | * @since Commons Events 1.0 | |
38 | * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $ | |
39 | * | |
40 | * @author Stephen Colebourne | |
41 | */ | |
42 | public class StandardModificationHandler extends ModificationHandler { | |
43 | ||
44 | /** The singleton factory */ | |
45 | 0 | public static final ModificationHandlerFactory FACTORY = new Factory(); |
46 | ||
47 | /** A reusable empty holders array. */ | |
48 | 0 | protected static final PreHolder[] EMPTY_PRE_HOLDERS = new PreHolder[0]; |
49 | /** A reusable empty holders array. */ | |
50 | 0 | protected static final PostHolder[] EMPTY_POST_HOLDERS = new PostHolder[0]; |
51 | ||
52 | /** The event mask as to which event types to send on pre events. */ | |
53 | 0 | protected int preMask = ModificationEventType.GROUP_NONE; |
54 | /** The event mask as to which event types to send on post events. */ | |
55 | 0 | protected int postMask = ModificationEventType.GROUP_NONE; |
56 | ||
57 | /** The event listeners. */ | |
58 | 0 | protected PreHolder[] preHolder = EMPTY_PRE_HOLDERS; |
59 | /** The event listeners. */ | |
60 | 0 | protected PostHolder[] postHolder = EMPTY_POST_HOLDERS; |
61 | /** | |
62 | * Temporary store for the size. | |
63 | * This makes the class thread-unsafe, but you should sync collections anyway. | |
64 | */ | |
65 | protected int preSize; | |
66 | ||
67 | // Constructors | |
68 | //----------------------------------------------------------------------- | |
69 | /** | |
70 | * Constructor the creates the handler but leaves it invalid. | |
71 | * <p> | |
72 | * The handler can only be used after it has been properly initialized. | |
73 | * This is normally done automatically by | |
74 | * {@link ObservableCollection#decorate(Collection, Object)}. | |
75 | */ | |
76 | public StandardModificationHandler() { | |
77 | 0 | super(); |
78 | 0 | } |
79 | ||
80 | /** | |
81 | * Constructor the creates the handler but leaves it invalid. | |
82 | * <p> | |
83 | * The handler can only be used after it has been properly initialized. | |
84 | * This is normally done automatically by | |
85 | * {@link ObservableCollection#decorate(Collection, Object)}. | |
86 | * | |
87 | * @param pre the pre listener | |
88 | * @param preMask the mask for the pre listener | |
89 | * @param post the post listener | |
90 | * @param postMask the mask for the post listener | |
91 | */ | |
92 | public StandardModificationHandler( | |
93 | StandardPreModificationListener pre, int preMask, | |
94 | StandardPostModificationListener post, int postMask) { | |
95 | 0 | super(); |
96 | 0 | if (pre != null) { |
97 | 0 | preHolder = new PreHolder[1]; |
98 | 0 | preHolder[0] = new PreHolder(pre, preMask); |
99 | 0 | this.preMask = preMask; |
100 | } | |
101 | 0 | if (post != null) { |
102 | 0 | postHolder = new PostHolder[1]; |
103 | 0 | postHolder[0] = new PostHolder(post, postMask); |
104 | 0 | this.postMask = postMask; |
105 | } | |
106 | 0 | } |
107 | ||
108 | // Pre Listeners | |
109 | //---------------------------------------------------------------------- | |
110 | /** | |
111 | * Gets an array of all the pre listeners active in the handler. | |
112 | * <p> | |
113 | * All listeners will be instances of StandardPreModificationListener. | |
114 | * | |
115 | * @return the listeners | |
116 | */ | |
117 | public synchronized Object[] getPreModificationListeners() { | |
118 | 0 | Object[] lnrs = new Object[preHolder.length]; |
119 | 0 | for (int i = 0; i < preHolder.length; i++) { |
120 | 0 | lnrs[i] = preHolder[i].listener; |
121 | } | |
122 | 0 | return lnrs; |
123 | } | |
124 | ||
125 | /** | |
126 | * Adds a listener to the handler for pre modification events. | |
127 | * <p> | |
128 | * No error occurs if the listener is <code>null</code>. | |
129 | * | |
130 | * @param listener the listener to add, may be null (ignored) | |
131 | * @throws ClassCastException if the listener is not a StandardPreModificationListener | |
132 | */ | |
133 | public void addPreModificationListener(Object listener) { | |
134 | 0 | addPreModificationListener((StandardPreModificationListener) listener, -1); |
135 | 0 | } |
136 | ||
137 | /** | |
138 | * Adds a pre listener to the list held in the handler. | |
139 | * <p> | |
140 | * No error occurs if the listener is <code>null</code>. | |
141 | * | |
142 | * @param listener the listener to add, may be null (ignored) | |
143 | * @param mask the mask for events (0 for none, -1 for all) | |
144 | */ | |
145 | public synchronized void addPreModificationListener(StandardPreModificationListener listener, int mask) { | |
146 | 0 | if (listener != null) { |
147 | 0 | int oldSize = preHolder.length; |
148 | 0 | PreHolder[] array = new PreHolder[oldSize + 1]; |
149 | 0 | System.arraycopy(preHolder, 0, array, 0, oldSize); |
150 | 0 | array[oldSize] = new PreHolder(listener, mask); |
151 | 0 | preHolder = array; |
152 | 0 | calculatePreMask(); |
153 | } | |
154 | 0 | } |
155 | ||
156 | /** | |
157 | * Removes a pre listener to the list held in the handler. | |
158 | * <p> | |
159 | * No error occurs if the listener is not in the list or the type | |
160 | * of the listener is incorrect. | |
161 | * The listener is matched using ==. | |
162 | * | |
163 | * @param listener the listener to remove, may be null (ignored) | |
164 | */ | |
165 | public synchronized void removePreModificationListener(Object listener) { | |
166 | 0 | if (listener != null) { |
167 | 0 | switch (preHolder.length) { |
168 | case 0: | |
169 | 0 | return; |
170 | ||
171 | case 1: | |
172 | 0 | if (preHolder[0].listener == listener) { |
173 | 0 | preHolder = EMPTY_PRE_HOLDERS; |
174 | 0 | calculatePreMask(); |
175 | } | |
176 | 0 | return; |
177 | ||
178 | default: | |
179 | 0 | PreHolder[] array = new PreHolder[preHolder.length - 1]; |
180 | 0 | boolean match = false; |
181 | 0 | for (int i = 0; i < preHolder.length; i++) { |
182 | 0 | if (match) { |
183 | 0 | array[i - 1] = preHolder[i]; |
184 | 0 | } else if (preHolder[i].listener == listener) { |
185 | 0 | match = true; |
186 | } else { | |
187 | 0 | array[i] = preHolder[i]; |
188 | } | |
189 | } | |
190 | 0 | preHolder = array; |
191 | 0 | calculatePreMask(); |
192 | 0 | return; |
193 | } | |
194 | } | |
195 | 0 | } |
196 | ||
197 | /** | |
198 | * Sets the masks of a listener. | |
199 | * <p> | |
200 | * No error occurs if the listener is not in the list. | |
201 | * The listener is matched using ==. | |
202 | * | |
203 | * @param listener the listener to change, may be null | |
204 | * @param mask the new mask (0 for none, -1 for all) | |
205 | */ | |
206 | public synchronized void setPreModificationListenerMask(StandardPreModificationListener listener, int mask) { | |
207 | 0 | if (listener != null) { |
208 | 0 | for (int i = 0; i < preHolder.length; i++) { |
209 | 0 | if (preHolder[i].listener == listener) { |
210 | 0 | preHolder[i].mask = mask; |
211 | 0 | calculatePreMask(); |
212 | 0 | break; |
213 | } | |
214 | } | |
215 | } | |
216 | 0 | } |
217 | ||
218 | /** | |
219 | * Calculate the combined masks. | |
220 | */ | |
221 | protected void calculatePreMask() { | |
222 | 0 | preMask = ModificationEventType.GROUP_NONE; |
223 | 0 | for (int i = 0; i < preHolder.length; i++) { |
224 | 0 | preMask |= preHolder[i].mask; |
225 | } | |
226 | 0 | } |
227 | ||
228 | protected static class PreHolder { | |
229 | final StandardPreModificationListener listener; | |
230 | int mask; | |
231 | ||
232 | 0 | PreHolder(StandardPreModificationListener listener, int mask) { |
233 | 0 | this.listener = listener; |
234 | 0 | this.mask = mask; |
235 | 0 | } |
236 | ||
237 | public String toString() { | |
238 | 0 | return "[" + listener + "," + ModificationEventType.toString(mask) + "]"; |
239 | } | |
240 | ||
241 | } | |
242 | ||
243 | // Post Listeners | |
244 | //---------------------------------------------------------------------- | |
245 | /** | |
246 | * Gets an array of all the post listeners active in the handler. | |
247 | * <p> | |
248 | * All listeners will be instances of StandardModificationListener. | |
249 | * | |
250 | * @return the listeners | |
251 | */ | |
252 | public synchronized Object[] getPostModificationListeners() { | |
253 | 0 | Object[] lnrs = new Object[postHolder.length]; |
254 | 0 | for (int i = 0; i < postHolder.length; i++) { |
255 | 0 | lnrs[i] = postHolder[i].listener; |
256 | } | |
257 | 0 | return lnrs; |
258 | } | |
259 | ||
260 | /** | |
261 | * Adds a listener to the handler for post modification events. | |
262 | * <p> | |
263 | * No error occurs if the listener is <code>null</code>. | |
264 | * | |
265 | * @param listener the listener to add, may be null (ignored) | |
266 | * @throws ClassCastException if the listener is not a StandardPreModificationListener | |
267 | */ | |
268 | public void addPostModificationListener(Object listener) { | |
269 | 0 | addPostModificationListener((StandardPostModificationListener) listener, -1); |
270 | 0 | } |
271 | ||
272 | /** | |
273 | * Adds a post listener to the list held in the handler. | |
274 | * <p> | |
275 | * No error occurs if the listener is <code>null</code>. | |
276 | * | |
277 | * @param listener the listener to add, may be null (ignored) | |
278 | * @param mask the mask for events (0 for none, -1 for all) | |
279 | */ | |
280 | public synchronized void addPostModificationListener(StandardPostModificationListener listener, int mask) { | |
281 | 0 | if (listener != null) { |
282 | 0 | int oldSize = postHolder.length; |
283 | 0 | PostHolder[] array = new PostHolder[oldSize + 1]; |
284 | 0 | System.arraycopy(postHolder, 0, array, 0, oldSize); |
285 | 0 | array[oldSize] = new PostHolder(listener, mask); |
286 | 0 | postHolder = array; |
287 | 0 | calculatePostMask(); |
288 | } | |
289 | 0 | } |
290 | ||
291 | /** | |
292 | * Removes a post listener to the list held in the handler. | |
293 | * <p> | |
294 | * No error occurs if the listener is not in the list or the type | |
295 | * of the listener is incorrect. | |
296 | * The listener is matched using ==. | |
297 | * | |
298 | * @param listener the listener to remove, may be null (ignored) | |
299 | */ | |
300 | public synchronized void removePostModificationListener(Object listener) { | |
301 | 0 | if (listener != null) { |
302 | 0 | switch (postHolder.length) { |
303 | case 0: | |
304 | 0 | return; |
305 | ||
306 | case 1: | |
307 | 0 | if (postHolder[0].listener == listener) { |
308 | 0 | postHolder = EMPTY_POST_HOLDERS; |
309 | 0 | calculatePostMask(); |
310 | } | |
311 | 0 | return; |
312 | ||
313 | default: | |
314 | 0 | PostHolder[] array = new PostHolder[postHolder.length - 1]; |
315 | 0 | boolean match = false; |
316 | 0 | for (int i = 0; i < postHolder.length; i++) { |
317 | 0 | if (match) { |
318 | 0 | array[i - 1] = postHolder[i]; |
319 | 0 | } else if (postHolder[i].listener == listener) { |
320 | 0 | match = true; |
321 | } else { | |
322 | 0 | array[i] = postHolder[i]; |
323 | } | |
324 | } | |
325 | 0 | postHolder = array; |
326 | 0 | calculatePostMask(); |
327 | 0 | return; |
328 | } | |
329 | } | |
330 | 0 | } |
331 | ||
332 | /** | |
333 | * Sets the masks of a listener. | |
334 | * <p> | |
335 | * No error occurs if the listener is not in the list. | |
336 | * The listener is matched using ==. | |
337 | * | |
338 | * @param listener the listener to change, may be null | |
339 | * @param mask the new mask (0 for none, -1 for all) | |
340 | */ | |
341 | public synchronized void setPostModificationListenerMask(StandardPostModificationListener listener, int mask) { | |
342 | 0 | if (listener != null) { |
343 | 0 | for (int i = 0; i < postHolder.length; i++) { |
344 | 0 | if (postHolder[i].listener == listener) { |
345 | 0 | postHolder[i].mask = mask; |
346 | 0 | calculatePostMask(); |
347 | 0 | break; |
348 | } | |
349 | } | |
350 | } | |
351 | 0 | } |
352 | ||
353 | /** | |
354 | * Calculate the combined masks. | |
355 | */ | |
356 | protected void calculatePostMask() { | |
357 | 0 | postMask = ModificationEventType.GROUP_NONE; |
358 | 0 | for (int i = 0; i < postHolder.length; i++) { |
359 | 0 | postMask |= postHolder[i].mask; |
360 | } | |
361 | 0 | } |
362 | ||
363 | protected static class PostHolder { | |
364 | final StandardPostModificationListener listener; | |
365 | int mask; | |
366 | ||
367 | 0 | PostHolder(StandardPostModificationListener listener, int mask) { |
368 | 0 | this.listener = listener; |
369 | 0 | this.mask = mask; |
370 | 0 | } |
371 | ||
372 | public String toString() { | |
373 | 0 | return "[" + listener + "," + ModificationEventType.toString(mask) + "]"; |
374 | } | |
375 | ||
376 | } | |
377 | ||
378 | // Pre event sending | |
379 | //----------------------------------------------------------------------- | |
380 | /** | |
381 | * Handles the pre event. | |
382 | * | |
383 | * @param type the event type to send | |
384 | * @param index the index where the change starts, the method param or derived | |
385 | * @param object the object that will be added/removed/set, the method param or derived | |
386 | * @param repeat the number of repeats of the add/remove, the method param or derived | |
387 | * @param previous the previous value that will be removed/replaced, must exist in coll | |
388 | * @param view the view collection that the change was actioned on, null if no view | |
389 | * @param viewOffset the offset of the subList view, -1 if unknown | |
390 | * @return true to call the decorated collection | |
391 | */ | |
392 | protected boolean preEvent( | |
393 | int type, int index, Object object, | |
394 | int repeat, Object previous, ObservableCollection view, int viewOffset) { | |
395 | ||
396 | 0 | preSize = getObservedCollection().size(); |
397 | 0 | return firePreEvent(type, index, object, repeat, previous, view, viewOffset); |
398 | } | |
399 | ||
400 | /** | |
401 | * Sends the pre event to the listeners. | |
402 | * | |
403 | * @param type the event type to send | |
404 | * @param index the index where the change starts, the method param or derived | |
405 | * @param object the object that will be added/removed/set, the method param or derived | |
406 | * @param repeat the number of repeats of the add/remove, the method param or derived | |
407 | * @param previous the previous value that will be removed/replaced, must exist in coll | |
408 | * @param view the view collection that the change was actioned on, null if no view | |
409 | * @param viewOffset the offset of the subList view, -1 if unknown | |
410 | * @return true to call the decorated collection | |
411 | */ | |
412 | protected boolean firePreEvent( | |
413 | int type, int index, Object object, int repeat, | |
414 | Object previous, ObservableCollection view, int viewOffset) { | |
415 | ||
416 | 0 | if ((preMask & type) > 0) { |
417 | 0 | StandardPreModificationEvent event = null; |
418 | 0 | synchronized (this) { |
419 | 0 | for (int i = 0; i < preHolder.length; i++) { |
420 | 0 | PreHolder holder = preHolder[i]; |
421 | 0 | if ((holder.mask & type) > 0) { |
422 | 0 | if (event == null) { |
423 | 0 | event = new StandardPreModificationEvent( |
424 | getObservedCollection(), this, type, preSize, index, object, | |
425 | repeat, previous, view, viewOffset); | |
426 | } | |
427 | 0 | holder.listener.modificationOccurring(event); |
428 | } | |
429 | } | |
430 | 0 | } |
431 | } | |
432 | 0 | return true; |
433 | } | |
434 | ||
435 | // Post event sending | |
436 | //----------------------------------------------------------------------- | |
437 | /** | |
438 | * Handles the post event. | |
439 | * | |
440 | * @param modified true if the method succeeded in changing the collection | |
441 | * @param type the event type to send | |
442 | * @param index the index where the change starts, the method param or derived | |
443 | * @param object the object that was added/removed/set, the method param or derived | |
444 | * @param repeat the number of repeats of the add/remove, the method param or derived | |
445 | * @param previous the previous value that was removed/replace, must have existed in coll | |
446 | * @param view the view collection that the change was actioned on, null if no view | |
447 | * @param viewOffset the offset of the subList view, -1 if unknown | |
448 | */ | |
449 | protected void postEvent( | |
450 | boolean modified, int type, int index, Object object, | |
451 | int repeat, Object previous, ObservableCollection view, int viewOffset) { | |
452 | ||
453 | 0 | if (modified) { |
454 | 0 | firePostEvent(type, index, object, repeat, previous, view, viewOffset); |
455 | } | |
456 | 0 | } |
457 | ||
458 | /** | |
459 | * Sends the post event to the listeners. | |
460 | * | |
461 | * @param type the event type to send | |
462 | * @param index the index where the change starts, the method param or derived | |
463 | * @param object the object that was added/removed/set, the method param or derived | |
464 | * @param repeat the number of repeats of the add/remove, the method param or derived | |
465 | * @param previous the previous value that was removed/replace, must have existed in coll | |
466 | * @param view the view collection that the change was actioned on, null if no view | |
467 | * @param viewOffset the offset of the subList view, -1 if unknown | |
468 | */ | |
469 | protected void firePostEvent( | |
470 | int type, int index, Object object, int repeat, | |
471 | Object previous, ObservableCollection view, int viewOffset) { | |
472 | ||
473 | 0 | if ((postMask & type) > 0) { |
474 | 0 | StandardPostModificationEvent event = null; |
475 | 0 | synchronized (this) { |
476 | 0 | for (int i = 0; i < postHolder.length; i++) { |
477 | 0 | PostHolder holder = postHolder[i]; |
478 | 0 | if ((holder.mask & type) > 0) { |
479 | 0 | if (event == null) { |
480 | 0 | event = new StandardPostModificationEvent( |
481 | getObservedCollection(), this, type, preSize, index, | |
482 | object, repeat, previous, view, viewOffset); | |
483 | } | |
484 | 0 | holder.listener.modificationOccurred(event); |
485 | } | |
486 | } | |
487 | 0 | } |
488 | } | |
489 | 0 | } |
490 | ||
491 | // Event handling | |
492 | //----------------------------------------------------------------------- | |
493 | /** | |
494 | * Send an event after clear() is called. | |
495 | * <p> | |
496 | * Override to only send event if something actually cleared. | |
497 | */ | |
498 | protected void postClear() { | |
499 | 0 | postEvent(preSize > 0, ModificationEventType.CLEAR, -1, null, 1, null, null, -1); |
500 | 0 | } |
501 | ||
502 | // Factory | |
503 | //----------------------------------------------------------------------- | |
504 | /** | |
505 | * Factory implementation for the StandardModificationHandler. | |
506 | * | |
507 | * @author Stephen Colebourne | |
508 | */ | |
509 | 0 | static class Factory implements ModificationHandlerFactory { |
510 | ||
511 | /** | |
512 | * Creates a StandardModificationHandler using the listener. | |
513 | * | |
514 | * @param coll the collection being decorated | |
515 | * @param listener a listener object to create a handler for | |
516 | * @return an instantiated handler with the listener attached, | |
517 | * or null if the listener type is unsuited to this factory | |
518 | */ | |
519 | public ModificationHandler createHandler(Collection coll, Object listener) { | |
520 | 0 | if (listener instanceof StandardPreModificationListener) { |
521 | 0 | if (listener instanceof StandardPostModificationListener) { |
522 | 0 | return new StandardModificationHandler( |
523 | (StandardPreModificationListener) listener, -1, | |
524 | (StandardPostModificationListener) listener, -1); | |
525 | } else { | |
526 | 0 | return new StandardModificationHandler( |
527 | (StandardPreModificationListener) listener, -1, null, 0); | |
528 | } | |
529 | } | |
530 | 0 | if (listener instanceof StandardPostModificationListener) { |
531 | 0 | return new StandardModificationHandler( |
532 | null, 0, (StandardPostModificationListener) listener, -1); | |
533 | } | |
534 | 0 | return null; |
535 | } | |
536 | } | |
537 | ||
538 | } |