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.Collection;
019    import java.util.List;
020    import java.util.ListIterator;
021    
022    import org.apache.commons.collections.iterators.AbstractListIteratorDecorator;
023    
024    /**
025     * Decorates a <code>List</code> implementation to observe modifications.
026     * <p>
027     * Each modifying method call made on this <code>List</code> is forwarded to a
028     * {@link ModificationHandler}.
029     * The handler manages the event, notifying listeners and optionally vetoing changes.
030     * The default handler is
031     * {@link org.apache.commons.events.observable.standard.StandardModificationHandler StandardModificationHandler}.
032     * See this class for details of configuration available.
033     * <p>
034     * All indices on events returned by <code>subList</code> are relative to the
035     * base <code>List</code>.
036     *
037     * @since Commons Events 1.0
038     * @version $Revision: 155443 $ $Date: 2005-02-26 13:19:51 +0000 (Sat, 26 Feb 2005) $
039     * 
040     * @author Stephen Colebourne
041     */
042    public class ObservableList extends ObservableCollection implements List {
043    
044        // Factories
045        //-----------------------------------------------------------------------
046        /**
047         * Factory method to create an observable list.
048         * <p>
049         * A {@link org.apache.commons.events.observable.standard.StandardModificationHandler} will be created.
050         * This can be accessed by {@link #getHandler()} to add listeners.
051         *
052         * @param list  the list to decorate, must not be null
053         * @return the observed List
054         * @throws IllegalArgumentException if the list is null
055         */
056        public static ObservableList decorate(final List list) {
057            return new ObservableList(list, null);
058        }
059    
060        /**
061         * Factory method to create an observable list using a listener or a handler.
062         * <p>
063         * A lot of functionality is available through this method.
064         * If you don't need the extra functionality, simply implement the
065         * {@link org.apache.commons.events.observable.standard.StandardModificationListener}
066         * interface and pass it in as the second parameter.
067         * <p>
068         * Internally, an <code>ObservableList</code> relies on a {@link ModificationHandler}.
069         * The handler receives all the events and processes them, typically by
070         * calling listeners. Different handler implementations can be plugged in
071         * to provide a flexible event system.
072         * <p>
073         * The handler implementation is determined by the listener parameter via
074         * the registered factories. The listener may be a manually configured 
075         * <code>ModificationHandler</code> instance.
076         * <p>
077         * The listener is defined as an Object for maximum flexibility.
078         * It does not have to be a listener in the classic JavaBean sense.
079         * It is entirely up to the factory and handler as to how the parameter
080         * is interpretted. An IllegalArgumentException is thrown if no suitable
081         * handler can be found for this listener.
082         * <p>
083         * A <code>null</code> listener will create a
084         * {@link org.apache.commons.events.observable.standard.StandardModificationHandler}.
085         *
086         * @param list  the list to decorate, must not be null
087         * @param listener  list listener, may be null
088         * @return the observed list
089         * @throws IllegalArgumentException if the list is null
090         * @throws IllegalArgumentException if there is no valid handler for the listener
091         */
092        public static ObservableList decorate(
093                final List list,
094                final Object listener) {
095            
096            if (list == null) {
097                throw new IllegalArgumentException("List must not be null");
098            }
099            return new ObservableList(list, listener);
100        }
101    
102        // Constructors
103        //-----------------------------------------------------------------------
104        /**
105         * Constructor that wraps (not copies) and takes a handler.
106         * <p>
107         * The handler implementation is determined by the listener parameter via
108         * the registered factories. The listener may be a manually configured 
109         * <code>ModificationHandler</code> instance.
110         * 
111         * @param list  the list to decorate, must not be null
112         * @param listener  the listener, may be null
113         * @throws IllegalArgumentException if the list is null
114         */
115        protected ObservableList(
116                final List list,
117                final Object listener) {
118            super(list, listener);
119        }
120        
121        /**
122         * Constructor used by subclass views, such as subList.
123         * 
124         * @param handler  the handler to use, must not be null
125         * @param list  the subList to decorate, must not be null
126         * @throws IllegalArgumentException if the list is null
127         */
128        protected ObservableList(
129                final ModificationHandler handler,
130                final List list) {
131            super(handler, list);
132        }
133        
134        /**
135         * Typecast the collection to a List.
136         * 
137         * @return the wrapped collection as a List
138         */
139        private List getList() {
140            return (List) getCollection();
141        }
142    
143        // List API
144        //-----------------------------------------------------------------------
145        public Object get(int index) {
146            return getList().get(index);
147        }
148    
149        public int indexOf(Object object) {
150            return getList().indexOf(object);
151        }
152    
153        public int lastIndexOf(Object object) {
154            return getList().lastIndexOf(object);
155        }
156    
157        //-----------------------------------------------------------------------
158        public void add(int index, Object object) {
159            if (handler.preAddIndexed(index, object)) {
160                getList().add(index, object);
161                handler.postAddIndexed(index, object);
162            }
163        }
164    
165        public boolean addAll(int index, Collection coll) {
166            boolean result = false;
167            if (handler.preAddAllIndexed(index, coll)) {
168                result = getList().addAll(index, coll);
169                handler.postAddAllIndexed(index, coll, result);
170            }
171            return result;
172        }
173    
174        public Object remove(int index) {
175            Object result = null;
176            if (handler.preRemoveIndexed(index)) {
177                result = getList().remove(index);
178                handler.postRemoveIndexed(index, result);
179            }
180            return result;
181        }
182    
183        public Object set(int index, Object object) {
184            Object result = null;
185            if (handler.preSetIndexed(index, object)) {
186                result = getList().set(index, object);
187                handler.postSetIndexed(index, object, result);
188            }
189            return result;
190        }
191    
192        public ListIterator listIterator() {
193            return new ObservableListIterator(getList().listIterator());
194        }
195    
196        public ListIterator listIterator(int index) {
197            return new ObservableListIterator(getList().listIterator(index));
198        }
199    
200        /**
201         * Returns a subList view on the original base <code>List</code>.
202         * <p>
203         * Changes to the subList affect the underlying List. Change events will
204         * return change indices relative to the underlying List, not the subList.
205         * 
206         * @param fromIndex  inclusive start index of the range
207         * @param toIndex  exclusive end index of the range
208         * @return the subList view
209         */
210        public List subList(int fromIndex, int toIndex) {
211            List subList = getList().subList(fromIndex, toIndex);
212            return new ObservableList(subList, getHandler().createSubListHandler(fromIndex, toIndex));
213        }
214    
215        // ListIterator
216        //-----------------------------------------------------------------------
217        /**
218         * Inner class ListIterator for the ObservableList.
219         */
220        protected class ObservableListIterator extends AbstractListIteratorDecorator {
221            
222            protected Object last;
223            
224            protected ObservableListIterator(ListIterator iterator) {
225                super(iterator);
226            }
227            
228            public Object next() {
229                last = super.next();
230                return last;
231            }
232    
233            public Object previous() {
234                last = iterator.previous();
235                return last;
236            }
237    
238            public void remove() {
239                int index = iterator.previousIndex();
240                if (handler.preRemoveIterated(index, last)) {
241                    iterator.remove();
242                    handler.postRemoveIterated(index, last);
243                }
244            }
245            
246            public void add(Object object) {
247                int index = iterator.nextIndex();
248                if (handler.preAddIterated(index, object)) {
249                    iterator.add(object);
250                    handler.postAddIterated(index, object);
251                }
252            }
253    
254            public void set(Object object) {
255                int index = iterator.previousIndex();
256                if (handler.preSetIterated(index, object, last)) {
257                    iterator.set(object);
258                    handler.postSetIterated(index, object, last);
259                }
260            }
261        }
262    
263    }