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 }