Coverage Report - org.apache.commons.events.observable.ObservableCollection
 
Classes in this File Line Coverage Branch Coverage Complexity
ObservableCollection
0%
0/57
0%
0/24
1.941
ObservableCollection$ObservableIterator
0%
0/12
0%
0/2
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  
 }