GlazedListsJavaFX.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.ObservableElementList;
020 import ca.odell.glazedlists.TransformedList;
021 import ca.odell.glazedlists.impl.gui.ThreadProxyEventList;
022 import griffon.plugins.glazedlists.ColumnReader;
023 import griffon.plugins.glazedlists.javafx.gui.DefaultFXTableFormat;
024 import griffon.plugins.glazedlists.javafx.gui.FXTableFormat;
025 import griffon.plugins.glazedlists.javafx.models.DefaultFXTableViewModel;
026 import griffon.plugins.glazedlists.javafx.models.FXTableViewModel;
027 import javafx.application.Platform;
028 import javafx.collections.ObservableList;
029 
030 import javax.annotation.Nonnull;
031 import java.util.List;
032 
033 import static griffon.util.GriffonClassUtils.requireState;
034 import static java.util.Objects.requireNonNull;
035 
036 /**
037  * A factory for creating all sorts of objects to be used with Glazed Lists and JavaFX.
038  *
039  @author Andres Almriay
040  */
041 public final class GlazedListsJavaFX {
042     private GlazedListsJavaFX() {
043 
044     }
045 
046     @Nonnull
047     public static <E extends PropertyContainer> ObservableElementList.Connector<E> propertyContainerConnector() {
048         return new PropertyContainerConnector<>();
049     }
050 
051     @Nonnull
052     public static <E extends PropertyContainer> ObservableElementList.Connector<E> propertyContainerConnector(@Nonnull String... propertyNames) {
053         return new PropertyContainerConnector<>(propertyNames);
054     }
055 
056     /**
057      * Wraps the source in an {@link EventList} that fires all of its update
058      * events from the JavaFX application thread.
059      *
060      @param source the {@link EventList} to be wrapped. Must not be null.
061      @return a wrapped {@link EventList} that fires all of its update events inside the JavaFX application thread.
062      */
063     @Nonnull
064     public static <E> TransformedList<E, E> createJavaFXThreadProxyList(@Nonnull EventList<E> source) {
065         return new JavaFXThreadProxyEventList<>(requireNonNull(source, "Argument 'source' must not be null"));
066     }
067 
068     /**
069      * Creates a {@link griffon.plugins.glazedlists.javafx.gui.FXTableFormat}.
070      */
071     public static <T> FXTableFormat<T> tableFormat(@Nonnull String[] propertyNames) {
072         return new DefaultFXTableFormat<>(propertyNames);
073     }
074 
075     /**
076      * Creates a {@link griffon.plugins.glazedlists.javafx.gui.FXTableFormat}.
077      */
078     public static <T> FXTableFormat<T> tableFormat(@Nonnull String[] propertyNames, @Nonnull String[] columnLabels, @Nonnull ColumnReader[] columnReaders) {
079         return new DefaultFXTableFormat<>(propertyNames, columnLabels, columnReaders);
080     }
081 
082     /**
083      * Creates a {@link griffon.plugins.glazedlists.javafx.gui.FXTableFormat}.
084      */
085     public static <T> FXTableFormat<T> tableFormat(@Nonnull FXTableFormat.Options... options) {
086         return new DefaultFXTableFormat<>(options);
087     }
088 
089     /**
090      * Creates a {@link griffon.plugins.glazedlists.javafx.gui.FXTableFormat}.
091      */
092     public static <T> FXTableFormat<T> tableFormat(@Nonnull List<FXTableFormat.Options> options) {
093         requireNonNull(options, "Argument 'options' must not be null");
094         requireState(options.size() 0"Argument 'options' must not be empty");
095         return new DefaultFXTableFormat<>(options.toArray(new FXTableFormat.Options[options.size()]));
096     }
097 
098     /**
099      * Creates a new table model that extracts column data from the given
100      <code>source</code> using the the given <code>tableFormat</code>.
101      <p>
102      <p>The returned table model is <strong>not thread-safe</strong>. Unless otherwise
103      * noted, all methods are only safe to be called from the JavaFX application thread.
104      * To do this programmatically, use {@link javafx.application.Platform#runLater(Runnable)} and
105      * wrap the source list (or some part of the source list's pipeline) using
106      {@link GlazedListsJavaFX#createJavaFXThreadProxyList(EventList)}.</p>
107      *
108      @param source      the EventList that provides the row objects
109      @param tableFormat the object responsible for extracting column data
110      *                    from the row objects
111      */
112     public static <E> FXTableViewModel<E> eventTableViewModel(@Nonnull EventList<E> source, @Nonnull FXTableFormat<? super E> tableFormat) {
113         return eventTableViewModel(new EventObservableList<>(source), tableFormat);
114     }
115 
116     /**
117      * Creates a new table model that extracts column data from the given
118      <code>source</code> using the the given <code>tableFormat</code>.
119      <p>
120      <p>The returned table model is <strong>not thread-safe</strong>. Unless otherwise
121      * noted, all methods are only safe to be called from the JavaFX application thread.
122      * To do this programmatically, use {@link javafx.application.Platform#runLater(Runnable)} and
123      * wrap the source list (or some part of the source list's pipeline) using
124      {@link GlazedListsJavaFX#createJavaFXThreadProxyList(EventList)}.</p>
125      *
126      @param source      the ObservableList that provides the row objects
127      @param tableFormat the object responsible for extracting column data
128      *                    from the row objects
129      */
130     public static <E> FXTableViewModel<E> eventTableViewModel(@Nonnull ObservableList<E> source, @Nonnull FXTableFormat<? super E> tableFormat) {
131         return new DefaultFXTableViewModel<>(source, tableFormat);
132     }
133 
134     /**
135      * Creates a new table model that extracts column data from the given <code>source</code>
136      * using the the given <code>tableFormat</code>. While holding a read lock,
137      * this method wraps the source list using
138      {@link GlazedListsJavaFX#createJavaFXThreadProxyList(EventList)}.
139      <p>
140      * The returned table model is <strong>not thread-safe</strong>. Unless otherwise noted, all
141      * methods are only safe to be called from the event dispatch thread.
142      </p>
143      *
144      @param source      the EventList that provides the row objects
145      @param tableFormat the object responsible for extracting column data from the row objects
146      */
147     public static <E> FXTableViewModel<E> eventTableViewModelWithThreadProxyList(@Nonnull EventList<E> source, @Nonnull FXTableFormat<? super E> tableFormat) {
148         EventList<E> proxySource = createJavaFXThreadProxyList(source);
149         return new DefaultFXTableViewModel<>(new EventObservableList<>(proxySource), tableFormat);
150     }
151 
152     private static class JavaFXThreadProxyEventList<E> extends ThreadProxyEventList<E> {
153         /**
154          * Create a {@link JavaFXThreadProxyEventList} which delivers changes to the
155          * given <code>source</code> on the JavaFX application thread.
156          *
157          @param source the {@link EventList} for which to proxy events
158          */
159         private JavaFXThreadProxyEventList(@Nonnull EventList<E> source) {
160             super(source);
161         }
162 
163         @Override
164         protected void schedule(Runnable runnable) {
165             requireNonNull(runnable, "Argument 'runnable' must not be null");
166             if (Platform.isFxApplicationThread()) {
167                 runnable.run();
168             else {
169                 Platform.runLater(runnable);
170             }
171         }
172     }
173 }