EventObservableList.java
001 /*
002  * Copyright 2014-2016 the original author or authors.
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 griffon.plugins.glazedlists.javafx;
017 
018 import ca.odell.glazedlists.EventList;
019 import ca.odell.glazedlists.event.ListEvent;
020 import ca.odell.glazedlists.event.ListEventListener;
021 import javafx.beans.InvalidationListener;
022 import javafx.collections.ListChangeListener;
023 import javafx.collections.ObservableList;
024 
025 import javax.annotation.Nonnull;
026 import java.util.AbstractList;
027 import java.util.ArrayList;
028 import java.util.Collection;
029 import java.util.Iterator;
030 import java.util.List;
031 import java.util.ListIterator;
032 import java.util.function.Supplier;
033 
034 import static ca.odell.glazedlists.event.ListEvent.DELETE;
035 import static ca.odell.glazedlists.event.ListEvent.INSERT;
036 import static ca.odell.glazedlists.event.ListEvent.UPDATE;
037 import static java.util.Arrays.asList;
038 import static java.util.Collections.singletonList;
039 import static java.util.Collections.synchronizedList;
040 import static java.util.Objects.requireNonNull;
041 
042 /**
043  * An adapter from {@link EventList} to {@link ObservableList}.
044  <p>
045  <p>Reordering events are currently ignored.</p>
046  *
047  @author Andres Almiray
048  */
049 public class EventObservableList<E> extends AbstractList<E> implements ObservableList<E> {
050     private static final String ERROR_LISTENER_NULL = "Argument 'listener' must not be null";
051     private static final int[] EMPTY_INT_ARRAY = new int[0];
052 
053     private final EventList<E> delegate;
054     private final List<InvalidationListener> invalidationListeners = synchronizedList(new ArrayList<>());
055     private final List<ListChangeListener<? super E>> listChangeListeners = synchronizedList(new ArrayList<>());
056 
057     private final ListEventListener<E> listEventListener = changes -> {
058         synchronized (invalidationListeners) {
059             for (Iterator<InvalidationListener> it = new ReverseIterator<>(invalidationListeners); it.hasNext()) {
060                 it.next().invalidated(EventObservableList.this);
061             }
062         }
063 
064         synchronized (listChangeListeners) {
065             for (Iterator<ListChangeListener<? super E>> it = new ReverseIterator<>(listChangeListeners); it.hasNext()) {
066                 ListEvent<E> changes_copy = changes.copy();
067 
068                 // TODO: handle reordering
069 
070                 ListChangeListener.Change<E> change = new ChangeAdapter(EventObservableList.this, changes_copy);
071 
072                 if (it.hasNext()) {
073                     it.next().onChanged(change);
074                 }
075             }
076         }
077     };
078 
079     public EventObservableList(@Nonnull EventList<E> delegate) {
080         this.delegate = requireNonNull(delegate, "Argument 'delegate' must not be null");
081         this.delegate.addListEventListener(listEventListener);
082     }
083 
084     /**
085      * Disposing an EventList will make it eligible for garbage collection.
086      * Some EventLists install themselves as listeners to related objects so
087      * disposing them is necessary.
088      <p>
089      <p><strong><font color="#FF0000">Warning:</font></strong> It is an error
090      * to call any method on an {@link EventList} after it has been disposed.
091      */
092     public void dispose() {
093         delegate.removeListEventListener(listEventListener);
094     }
095 
096     private <T> T supplyInsideLock(@Nonnull Supplier<T> supplier) {
097         delegate.getReadWriteLock().writeLock().lock();
098         try {
099             return supplier.get();
100         finally {
101             delegate.getReadWriteLock().writeLock().unlock();
102         }
103     }
104 
105     private <T> void runInsideLock(@Nonnull Runnable runnable) {
106         delegate.getReadWriteLock().writeLock().lock();
107         try {
108             runnable.run();
109         finally {
110             delegate.getReadWriteLock().writeLock().unlock();
111         }
112     }
113 
114     /**
115      * {@inheritDoc}
116      */
117     @Override
118     public void addListener(ListChangeListener<? super E> listener) {
119         requireNonNull(listener, ERROR_LISTENER_NULL);
120         if (!listChangeListeners.contains(listener)) {
121             listChangeListeners.add(listener);
122         }
123     }
124 
125     /**
126      * {@inheritDoc}
127      */
128     @Override
129     public void removeListener(ListChangeListener<? super E> listener) {
130         requireNonNull(listener, ERROR_LISTENER_NULL);
131         listChangeListeners.remove(listener);
132     }
133 
134     /**
135      * {@inheritDoc}
136      */
137     @Override
138     public void addListener(InvalidationListener listener) {
139         requireNonNull(listener, ERROR_LISTENER_NULL);
140         if (!invalidationListeners.contains(listener)) {
141             invalidationListeners.add(listener);
142         }
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     @Override
149     public void removeListener(InvalidationListener listener) {
150         requireNonNull(listener, ERROR_LISTENER_NULL);
151         invalidationListeners.remove(listener);
152     }
153 
154     /**
155      * {@inheritDoc}
156      */
157     @Override
158     @SuppressWarnings("unchecked")
159     public boolean addAll(E... elements) {
160         return supplyInsideLock(() -> delegate.addAll(asList(elements)));
161     }
162 
163     /**
164      * {@inheritDoc}
165      */
166     @Override
167     @SuppressWarnings("unchecked")
168     public boolean setAll(E... elements) {
169         return setAll(asList(elements));
170     }
171 
172     @Override
173     public boolean setAll(Collection<? extends E> elements) {
174         return supplyInsideLock(() -> {
175             clear();
176             return addAll(elements);
177         });
178     }
179 
180     /**
181      * {@inheritDoc}
182      */
183     @Override
184     @SuppressWarnings("unchecked")
185     public boolean removeAll(E... elements) {
186         return removeAll(asList(elements));
187     }
188 
189     /**
190      * {@inheritDoc}
191      */
192     @Override
193     @SuppressWarnings("unchecked")
194     public boolean retainAll(E... elements) {
195         return retainAll(asList(elements));
196     }
197 
198     /**
199      * {@inheritDoc}
200      */
201     @Override
202     public boolean addAll(Collection<? extends E> c) {
203         return supplyInsideLock(() -> delegate.addAll(c));
204     }
205 
206     /**
207      * {@inheritDoc}
208      */
209     @Override
210     public boolean removeAll(Collection<?> c) {
211         return supplyInsideLock(() -> delegate.removeAll(c));
212     }
213 
214     /**
215      * {@inheritDoc}
216      */
217     @Override
218     public boolean retainAll(Collection<?> c) {
219         return supplyInsideLock(() -> delegate.retainAll(c));
220     }
221 
222     /**
223      * {@inheritDoc}
224      */
225     @Override
226     public void remove(int from, int to) {
227         runInsideLock(() -> {
228             if ((to - from<= 0) {
229                 return;
230             }
231             for (int i = (to - 1); i >= from; i--) {
232                 remove(i);
233             }
234         });
235     }
236 
237     /**
238      * {@inheritDoc}
239      */
240     @Override
241     public E get(int index) {
242         return supplyInsideLock(() -> delegate.get(index));
243     }
244 
245     /**
246      * {@inheritDoc}
247      */
248     @Override
249     public int size() {
250         return supplyInsideLock(delegate::size);
251     }
252 
253     /**
254      * {@inheritDoc}
255      */
256     @Override
257     public boolean isEmpty() {
258         return supplyInsideLock(delegate::isEmpty);
259     }
260 
261     /**
262      * {@inheritDoc}
263      */
264     @Override
265     public boolean contains(Object o) {
266         return supplyInsideLock(() -> delegate.contains(o));
267     }
268 
269     /**
270      * {@inheritDoc}
271      */
272     @Override
273     @SuppressWarnings("NullableProblems")
274     public Object[] toArray() {
275         return supplyInsideLock(delegate::toArray);
276     }
277 
278     /**
279      * {@inheritDoc}
280      */
281     @Override
282     @SuppressWarnings({"NullableProblems""SuspiciousToArrayCall"})
283     public <T> T[] toArray(T[] a) {
284         return supplyInsideLock(() -> delegate.toArray(a));
285     }
286 
287     /**
288      * {@inheritDoc}
289      */
290     @Override
291     public boolean remove(Object o) {
292         return supplyInsideLock(() -> delegate.remove(o));
293     }
294 
295     /**
296      * {@inheritDoc}
297      */
298     @Override
299     @SuppressWarnings("NullableProblems")
300     public boolean containsAll(Collection<?> c) {
301         return supplyInsideLock(() -> delegate.containsAll(c));
302     }
303 
304     /**
305      * {@inheritDoc}
306      */
307     @Override
308     public boolean add(E e) {
309         return supplyInsideLock(() -> delegate.add(e));
310     }
311 
312     /**
313      * {@inheritDoc}
314      */
315     @Override
316     public E set(int index, E element) {
317         return supplyInsideLock(() -> delegate.set(index, element));
318     }
319 
320     /**
321      * {@inheritDoc}
322      */
323     @Override
324     public void add(int index, E element) {
325         runInsideLock(() -> delegate.add(index, element));
326     }
327 
328     /**
329      * {@inheritDoc}
330      */
331     @Override
332     public E remove(int index) {
333         return supplyInsideLock(() -> delegate.remove(index));
334     }
335 
336     /**
337      * {@inheritDoc}
338      */
339     @Override
340     public int indexOf(Object o) {
341         return supplyInsideLock(() -> delegate.indexOf(o));
342     }
343 
344     /**
345      * {@inheritDoc}
346      */
347     @Override
348     public int lastIndexOf(Object o) {
349         return supplyInsideLock(() -> delegate.lastIndexOf(o));
350     }
351 
352     /**
353      * {@inheritDoc}
354      */
355     @Override
356     public void clear() {
357         runInsideLock(delegate::clear);
358     }
359 
360     /**
361      * {@inheritDoc}
362      */
363     @Override
364     @SuppressWarnings("NullableProblems")
365     public boolean addAll(int index, Collection<? extends E> c) {
366         return supplyInsideLock(() -> delegate.addAll(index, c));
367     }
368 
369     /**
370      * {@inheritDoc}
371      */
372     @Override
373     @SuppressWarnings("NullableProblems")
374     public Iterator<E> iterator() {
375         return supplyInsideLock(delegate::iterator);
376     }
377 
378     /**
379      * {@inheritDoc}
380      */
381     @Override
382     @SuppressWarnings("NullableProblems")
383     public ListIterator<E> listIterator() {
384         return supplyInsideLock(() -> delegate.listIterator());
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     @Override
391     @SuppressWarnings("NullableProblems")
392     public ListIterator<E> listIterator(int index) {
393         return supplyInsideLock(() -> delegate.listIterator(index));
394     }
395 
396     /**
397      * {@inheritDoc}
398      */
399     @Override
400     @SuppressWarnings("NullableProblems")
401     public List<E> subList(int fromIndex, int toIndex) {
402         return supplyInsideLock(() -> delegate.subList(fromIndex, toIndex));
403     }
404 
405     private static class ReverseIterator<T> implements Iterator<T>, Iterable<T> {
406         private final ListIterator<T> delegate;
407 
408         public ReverseIterator(List<T> list) {
409             this.delegate = list.listIterator(list.size());
410         }
411 
412         @Override
413         public Iterator<T> iterator() {
414             return this;
415         }
416 
417         @Override
418         public boolean hasNext() {
419             return delegate.hasPrevious();
420         }
421 
422         @Override
423         public T next() {
424             return delegate.previous();
425         }
426 
427         @Override
428         public void remove() {
429             delegate.remove();
430         }
431     }
432 
433     private class ChangeAdapter extends ListChangeListener.Change<E> {
434         private final ListEvent<E> changes;
435 
436         public ChangeAdapter(EventObservableList<E> list, ListEvent<E> changes) {
437             super(list);
438             this.changes = changes;
439         }
440 
441         @Override
442         public boolean next() {
443             return changes.nextBlock();
444         }
445 
446         @Override
447         public void reset() {
448             changes.reset();
449         }
450 
451         @Override
452         public int getFrom() {
453             return changes.getBlockStartIndex();
454         }
455 
456         @Override
457         public int getTo() {
458             return changes.getBlockEndIndex() 1;
459         }
460 
461         @Override
462         public List<E> getRemoved() {
463             return singletonList(changes.getOldValue());
464         }
465 
466         @Override
467         public boolean wasAdded() {
468             return changes.getType() == INSERT;
469         }
470 
471         @Override
472         public boolean wasRemoved() {
473             return changes.getType() == DELETE;
474         }
475 
476         @Override
477         public boolean wasUpdated() {
478             return changes.getType() == UPDATE;
479         }
480 
481         @Override
482         protected int[] getPermutation() {
483             return EMPTY_INT_ARRAY;
484         }
485     }
486 }