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}