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 }