001/** 002 * GRANITE DATA SERVICES 003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S. 004 * 005 * This file is part of the Granite Data Services Platform. 006 * 007 * *** 008 * 009 * Community License: GPL 3.0 010 * 011 * This file is free software: you can redistribute it and/or modify 012 * it under the terms of the GNU General Public License as published 013 * by the Free Software Foundation, either version 3 of the License, 014 * or (at your option) any later version. 015 * 016 * This file is distributed in the hope that it will be useful, but 017 * WITHOUT ANY WARRANTY; without even the implied warranty of 018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 019 * GNU General Public License for more details. 020 * 021 * You should have received a copy of the GNU General Public License 022 * along with this program. If not, see <http://www.gnu.org/licenses/>. 023 * 024 * *** 025 * 026 * Available Commercial License: GraniteDS SLA 1.0 027 * 028 * This is the appropriate option if you are creating proprietary 029 * applications and you are not prepared to distribute and share the 030 * source code of your application under the GPL v3 license. 031 * 032 * Please visit http://www.granitedataservices.com/license for more 033 * details. 034 */ 035package org.granite.client.collection; 036 037import java.beans.PropertyChangeEvent; 038import java.beans.PropertyChangeListener; 039import java.lang.reflect.Method; 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Iterator; 043import java.util.List; 044import java.util.ListIterator; 045 046import org.granite.client.collection.CollectionChangeListener; 047import org.granite.client.collection.CollectionChangeSupport; 048import org.granite.client.collection.ObservableCollection; 049import org.granite.client.collection.CollectionChangeEvent.Kind; 050 051/** 052 * @author William DRAI 053 */ 054public class ObservableList<E> implements List<E>, ObservableCollection { 055 056 protected CollectionChangeSupport ccs = new CollectionChangeSupport(this); 057 private final List<E> wrappedList; 058 059 public ObservableList(List<E> list) { 060 this.wrappedList = list; 061 } 062 063 protected List<E> getWrappedList() { 064 return wrappedList; 065 } 066 067 public void addCollectionChangeListener(CollectionChangeListener listener) { 068 ccs.addCollectionChangeListener(listener); 069 } 070 071 public void removeCollectionChangeListener(CollectionChangeListener listener) { 072 ccs.removeCollectionChangeListener(listener); 073 } 074 075 @Override 076 public boolean add(E element) { 077 int index = wrappedList.size(); 078 boolean added = wrappedList.add(element); 079 if (added) { 080 ccs.fireCollectionChangeEvent(Kind.ADD, index, new Object[] { element }); 081 trackElement(element); 082 } 083 return added; 084 } 085 086 @Override 087 public void add(int index, E element) { 088 wrappedList.add(index, element); 089 ccs.fireCollectionChangeEvent(Kind.ADD, index, new Object[] { element }); 090 trackElement(element); 091 } 092 093 @Override 094 public boolean addAll(Collection<? extends E> elements) { 095 int index = wrappedList.size(); 096 boolean added = wrappedList.addAll(elements); 097 if (added) 098 ccs.fireCollectionChangeEvent(Kind.ADD, index, elements.toArray()); 099 return added; 100 } 101 102 @Override 103 public boolean addAll(int index, Collection<? extends E> elements) { 104 boolean added = wrappedList.addAll(index, elements); 105 if (added) { 106 ccs.fireCollectionChangeEvent(Kind.ADD, index, elements.toArray()); 107 for (Object element : elements) 108 trackElement(element); 109 } 110 return added; 111 } 112 113 @Override 114 public E remove(int index) { 115 E element = wrappedList.remove(index); 116 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 117 untrackElement(element); 118 return element; 119 } 120 121 @Override 122 public boolean remove(Object element) { 123 int index = wrappedList.indexOf(element); 124 boolean removed = wrappedList.remove(element); 125 if (removed) { 126 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 127 untrackElement(element); 128 } 129 return removed; 130 } 131 132 @Override 133 public boolean removeAll(Collection<?> collection) { 134 boolean removed = false; 135 List<Object> removedElements = new ArrayList<Object>(); 136 int index = 0; 137 for (Object element : collection) { 138 if (remove(element)) { 139 removed = true; 140 removedElements.add(element); 141 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 142 untrackElement(element); 143 } 144 index++; 145 } 146 return removed; 147 } 148 149 @Override 150 public boolean retainAll(Collection<?> collection) { 151 boolean changed = false; 152 int index = 0; 153 for (Object element : collection) { 154 if (!wrappedList.contains(element) && remove(element)) { 155 changed = true; 156 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 157 untrackElement(element); 158 } 159 index++; 160 } 161 return changed; 162 } 163 164 @Override 165 public E set(int index, E element) { 166 E oldElement = wrappedList.set(index, element); 167 if (element != oldElement) { 168 ccs.fireCollectionChangeEvent(Kind.REPLACE, index, new Object[] { oldElement, element }); 169 untrackElement(oldElement); 170 trackElement(element); 171 } 172 return oldElement; 173 } 174 175 @Override 176 public void clear() { 177 if (wrappedList.size() == 0) 178 return; 179 Object[] elements = wrappedList.toArray(); 180 wrappedList.clear(); 181 ccs.fireCollectionChangeEvent(Kind.CLEAR, null, elements); 182 for (Object element : elements) 183 untrackElement(element); 184 } 185 186 @Override 187 public boolean contains(Object element) { 188 return wrappedList.contains(element); 189 } 190 191 @Override 192 public boolean containsAll(Collection<?> collection) { 193 return wrappedList.containsAll(collection); 194 } 195 196 @Override 197 public E get(int index) { 198 return wrappedList.get(index); 199 } 200 201 @Override 202 public int indexOf(Object element) { 203 return wrappedList.indexOf(element); 204 } 205 206 @Override 207 public int lastIndexOf(Object element) { 208 return wrappedList.lastIndexOf(element); 209 } 210 211 @Override 212 public boolean isEmpty() { 213 return wrappedList.isEmpty(); 214 } 215 216 @Override 217 public int size() { 218 return wrappedList.size(); 219 } 220 221 @Override 222 public Object[] toArray() { 223 return wrappedList.toArray(); 224 } 225 226 @Override 227 public <T> T[] toArray(T[] array) { 228 return wrappedList.toArray(array); 229 } 230 231 @Override 232 public List<E> subList(int start, int end) { 233 return new ObservableList<E>(wrappedList.subList(start, end)); 234 } 235 236 @Override 237 public Iterator<E> iterator() { 238 return new IteratorWrapper(wrappedList.iterator()); 239 } 240 241 @Override 242 public ListIterator<E> listIterator() { 243 return new ListIteratorWrapper(wrappedList.listIterator()); 244 } 245 246 @Override 247 public ListIterator<E> listIterator(int index) { 248 return new ListIteratorWrapper(wrappedList.listIterator(index), index); 249 } 250 251 252 private class IteratorWrapper implements Iterator<E> { 253 254 private final Iterator<E> wrappedIterator; 255 private int index = -1; 256 private E element; 257 258 public IteratorWrapper(Iterator<E> iterator) { 259 this.wrappedIterator = iterator; 260 } 261 262 public boolean hasNext() { 263 return wrappedIterator.hasNext(); 264 } 265 266 public E next() { 267 index++; 268 element = wrappedIterator.next(); 269 return element; 270 } 271 272 public void remove() { 273 wrappedIterator.remove(); 274 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 275 } 276 } 277 278 private class ListIteratorWrapper implements ListIterator<E> { 279 280 private final ListIterator<E> wrappedIterator; 281 private int index = -1; 282 private E element; 283 284 public ListIteratorWrapper(ListIterator<E> iterator) { 285 this.wrappedIterator = iterator; 286 } 287 288 public ListIteratorWrapper(ListIterator<E> iterator, int index) { 289 this.wrappedIterator = iterator; 290 this.index = index; 291 } 292 293 public void add(E element) { 294 wrappedIterator.add(element); 295 ccs.fireCollectionChangeEvent(Kind.ADD, index, new Object[] { element }); 296 } 297 298 public void remove() { 299 wrappedIterator.remove(); 300 ccs.fireCollectionChangeEvent(Kind.REMOVE, index, new Object[] { element }); 301 } 302 303 public void set(E element) { 304 Object oldElement = this.element; 305 wrappedIterator.set(element); 306 if (element != oldElement) 307 ccs.fireCollectionChangeEvent(Kind.REPLACE, index, new Object[] { oldElement, element }); 308 } 309 310 public boolean hasNext() { 311 return wrappedIterator.hasNext(); 312 } 313 314 public boolean hasPrevious() { 315 return wrappedIterator.hasPrevious(); 316 } 317 318 public E next() { 319 index++; 320 element = wrappedIterator.next(); 321 return element; 322 } 323 324 public int nextIndex() { 325 return wrappedIterator.nextIndex(); 326 } 327 328 public E previous() { 329 index--; 330 element = wrappedIterator.previous(); 331 return element; 332 } 333 334 public int previousIndex() { 335 return wrappedIterator.previousIndex(); 336 } 337 } 338 339 340 private PropertyChangeListener trackingListener = new PropertyChangeListener() { 341 public void propertyChange(PropertyChangeEvent event) { 342 ccs.fireCollectionChangeEvent(Kind.UPDATE, null, new Object[] { event }); 343 } 344 }; 345 346 private void trackElement(Object obj) { 347 if (obj == null) 348 return; 349 try { 350 Method m = obj.getClass().getMethod("addPropertyChangeListener", PropertyChangeListener.class); 351 m.invoke(obj, trackingListener); 352 } 353 catch (Exception e) { 354 } 355 } 356 357 private void untrackElement(Object obj) { 358 if (obj == null) 359 return; 360 try { 361 Method m = obj.getClass().getMethod("removePropertyChangeListener", PropertyChangeListener.class); 362 m.invoke(obj, trackingListener); 363 } 364 catch (Exception e) { 365 } 366 } 367}