Coverage Report - org.apache.commons.events.observable.standard.StandardModificationHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
StandardModificationHandler
0%
0/136
0%
0/74
2.958
StandardModificationHandler$Factory
0%
0/8
0%
0/6
2.958
StandardModificationHandler$PostHolder
0%
0/5
N/A
2.958
StandardModificationHandler$PreHolder
0%
0/5
N/A
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  
 }