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 }
|