GriffonFXCollections.java
001 /*
002  * SPDX-License-Identifier: Apache-2.0
003  *
004  * Copyright 2008-2017 the original author or authors.
005  *
006  * Licensed under the Apache License, Version 2.0 (the "License");
007  * you may not use this file except in compliance with the License.
008  * You may obtain a copy of the License at
009  *
010  *     http://www.apache.org/licenses/LICENSE-2.0
011  *
012  * Unless required by applicable law or agreed to in writing, software
013  * distributed under the License is distributed on an "AS IS" BASIS,
014  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015  * See the License for the specific language governing permissions and
016  * limitations under the License.
017  */
018 package griffon.javafx.collections;
019 
020 import griffon.javafx.beans.binding.UIThreadAware;
021 import javafx.application.Platform;
022 import javafx.collections.ListChangeListener;
023 import javafx.collections.MapChangeListener;
024 import javafx.collections.ObservableList;
025 import javafx.collections.ObservableMap;
026 import javafx.collections.ObservableSet;
027 import javafx.collections.SetChangeListener;
028 
029 import javax.annotation.Nonnull;
030 
031 import static java.util.Objects.requireNonNull;
032 
033 /**
034  @author Andres Almiray
035  @since 2.10.0
036  */
037 public final class GriffonFXCollections {
038     private static final String ERROR_SOURCE_NULL = "Argument 'source' must not be null";
039 
040     private GriffonFXCollections() {
041         // prevent instantiation
042     }
043 
044     /**
045      * Wraps an <tt>ObservableList</tt>, publishing updates inside the UI thread.
046      *
047      @param source the <tt>ObservableList</tt> to be wrapped
048      @param <E>    the list's parameter type.
049      *
050      @return a new <tt>ObservableList</tt>
051      */
052     @Nonnull
053     public static <E> ObservableList<E> uiThreadAwareObservableList(@Nonnull ObservableList<E> source) {
054         requireNonNull(source, ERROR_SOURCE_NULL);
055         return source instanceof UIThreadAware ? source : new UIThreadAwareObservableList<>(source);
056     }
057 
058     private static class UIThreadAwareObservableList<E> extends DelegatingObservableList<E> implements UIThreadAware {
059         protected UIThreadAwareObservableList(ObservableList<E> delegate) {
060             super(delegate);
061         }
062 
063         @Override
064         protected void sourceChanged(@Nonnull final ListChangeListener.Change<? extends E> c) {
065             if (Platform.isFxApplicationThread()) {
066                 fireChange(c);
067             else {
068                 Platform.runLater(() -> fireChange(c));
069             }
070         }
071     }
072 
073     /**
074      * Wraps an <tt>ObservableSet</tt>, publishing updates inside the UI thread.
075      *
076      @param source the <tt>ObservableSet</tt> to be wrapped
077      @param <E>    the set's parameter type.
078      *
079      @return a new <tt>ObservableSet</tt>
080      */
081     @Nonnull
082     public static <E> ObservableSet<E> uiThreadAwareObservableSet(@Nonnull ObservableSet<E> source) {
083         requireNonNull(source, ERROR_SOURCE_NULL);
084         return source instanceof UIThreadAware ? source : new UIThreadAwareObservableSet<>(source);
085     }
086 
087     private static class UIThreadAwareObservableSet<E> extends DelegatingObservableSet<E> implements UIThreadAware {
088         protected UIThreadAwareObservableSet(ObservableSet<E> delegate) {
089             super(delegate);
090         }
091 
092         @Override
093         protected void sourceChanged(@Nonnull final SetChangeListener.Change<? extends E> c) {
094             if (Platform.isFxApplicationThread()) {
095                 fireChange(c);
096             else {
097                 Platform.runLater(() -> fireChange(c));
098             }
099         }
100     }
101 
102     /**
103      * Wraps an <tt>ObservableMap</tt>, publishing updates inside the UI thread.
104      *
105      @param source the <tt>ObservableMap</tt> to be wrapped
106      @param <K>    the type of keys maintained by the map
107      @param <V>    the type of mapped values
108      *
109      @return a new <tt>ObservableMap</tt>
110      */
111     @Nonnull
112     public static <K, V> ObservableMap<K, V> uiThreadAwareObservableMap(@Nonnull ObservableMap<K, V> source) {
113         requireNonNull(source, ERROR_SOURCE_NULL);
114         return source instanceof UIThreadAware ? source : new UIThreadAwareObservableMap<>(source);
115     }
116 
117     private static class UIThreadAwareObservableMap<K, V> extends DelegatingObservableMap<K, V> implements UIThreadAware {
118         protected UIThreadAwareObservableMap(ObservableMap<K, V> delegate) {
119             super(delegate);
120         }
121 
122         @Override
123         protected void sourceChanged(@Nonnull final MapChangeListener.Change<? extends K, ? extends V> c) {
124             if (Platform.isFxApplicationThread()) {
125                 fireChange(c);
126             else {
127                 Platform.runLater(() -> fireChange(c));
128             }
129         }
130     }
131 
132     @Nonnull
133     public static <E> ObservableStream<E> observableStream(@Nonnull ObservableList<E> list) {
134         return new ListObservableStream<>(list);
135     }
136 
137     @Nonnull
138     public static <E> ObservableStream<E> observableStream(@Nonnull ObservableSet<E> set) {
139         return new SetObservableStream<>(set);
140     }
141 
142     @Nonnull
143     public static <K, V> ObservableStream<V> observableStream(@Nonnull ObservableMap<K, V> map) {
144         return new MapObservableStream<>(map);
145     }
146 }