dialog) {
387 | return DialogSource.fromDialogSource(dialog);
388 | }
389 | }
390 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/observers/BindingObserver.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.observers;
17 |
18 | import io.reactivex.rxjava3.core.Observer;
19 | import io.reactivex.rxjava3.disposables.Disposable;
20 | import io.reactivex.rxjava3.functions.Consumer;
21 | import io.reactivex.rxjava3.functions.Function;
22 | import io.reactivex.rxjava3.observables.ConnectableObservable;
23 | import javafx.beans.binding.Binding;
24 | import javafx.beans.value.ObservableValue;
25 | import javafx.collections.ObservableList;
26 |
27 | final class BindingObserver extends ObservableListenerHelper implements Observer, ObservableValue, Binding {
28 |
29 | private final Function unmaskingFunction;
30 | private final Consumer onError;
31 | private final ConnectableObservable obs;
32 | private boolean connected = false;
33 | private Disposable disposable;
34 | private S value;
35 |
36 | BindingObserver(Function unmaskingFunction, Consumer onError) {
37 | this.unmaskingFunction = unmaskingFunction;
38 | this.onError = onError;
39 | this.obs = null;
40 | }
41 |
42 | BindingObserver(Function unmaskingFunction, ConnectableObservable obs, Consumer onError) {
43 | this.unmaskingFunction = unmaskingFunction;
44 | this.onError = onError;
45 | this.obs = obs;
46 | }
47 |
48 | @Override
49 | public void onSubscribe(Disposable d) {
50 | this.disposable = d;
51 | }
52 |
53 | @Override
54 | public void onComplete() {
55 | //do nothing
56 | }
57 |
58 | @Override
59 | public void onError(Throwable e) {
60 | try {
61 | onError.accept(e);
62 | } catch (Throwable e1) {
63 | e1.printStackTrace();
64 | }
65 | }
66 |
67 | @Override
68 | public void onNext(T t) {
69 | try {
70 | value = unmaskingFunction.apply(t);
71 | fireChange();
72 | } catch (Throwable e) {
73 | onError(e);
74 | }
75 | }
76 |
77 | @Override
78 | public S getValue() {
79 | if (!connected && obs != null) {
80 | obs.connect();
81 | connected = true;
82 | }
83 | return value;
84 | }
85 |
86 | @Override
87 | public boolean isValid() {
88 | return true;
89 | }
90 |
91 | @Override
92 | public void invalidate() {
93 | //does nothing
94 | }
95 |
96 | @Override
97 | public ObservableList> getDependencies() {
98 | throw new UnsupportedOperationException();
99 | }
100 |
101 | @Override
102 | public void dispose() {
103 | if (disposable != null) {
104 | disposable.dispose();
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/observers/BindingSubscriber.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.observers;
17 |
18 | import org.reactivestreams.Subscriber;
19 | import org.reactivestreams.Subscription;
20 |
21 | import io.reactivex.rxjava3.flowables.ConnectableFlowable;
22 | import io.reactivex.rxjava3.functions.Consumer;
23 | import io.reactivex.rxjava3.functions.Function;
24 | import javafx.beans.binding.Binding;
25 | import javafx.beans.value.ObservableValue;
26 | import javafx.collections.ObservableList;
27 |
28 | final class BindingSubscriber extends ObservableListenerHelper implements Subscriber, ObservableValue, Binding {
29 |
30 | private final Function unmaskingFunction;
31 | private final Consumer onError;
32 | private final ConnectableFlowable obs;
33 | private boolean connected = false;
34 | private Subscription subscription;
35 | private S value;
36 |
37 | BindingSubscriber(Function unmaskingFunction, Consumer onError) {
38 | this.unmaskingFunction = unmaskingFunction;
39 | this.onError = onError;
40 | this.obs = null;
41 | }
42 |
43 | BindingSubscriber(Function unmaskingFunction, ConnectableFlowable obs, Consumer onError) {
44 | this.unmaskingFunction = unmaskingFunction;
45 | this.onError = onError;
46 | this.obs = obs;
47 | }
48 |
49 | @Override
50 | public void onSubscribe(Subscription s) {
51 | this.subscription = s;
52 | this.subscription.request(Long.MAX_VALUE);
53 | }
54 |
55 | @Override
56 | public void onComplete() {
57 | //do nothing
58 | }
59 |
60 | @Override
61 | public void onError(Throwable e) {
62 | try {
63 | onError.accept(e);
64 | } catch (Throwable e1) {
65 | e1.printStackTrace();
66 | }
67 | }
68 |
69 | @Override
70 | public void onNext(T t) {
71 | try {
72 | value = unmaskingFunction.apply(t);
73 | fireChange();
74 | } catch (Throwable e) {
75 | onError(e);
76 | }
77 | }
78 |
79 | @Override
80 | public S getValue() {
81 | if (!connected && obs != null) {
82 | obs.connect();
83 | connected = true;
84 | }
85 | return value;
86 | }
87 |
88 | @Override
89 | public boolean isValid() {
90 | return true;
91 | }
92 |
93 | @Override
94 | public void invalidate() {
95 | //does nothing
96 | }
97 |
98 | @Override
99 | public ObservableList> getDependencies() {
100 | throw new UnsupportedOperationException();
101 | }
102 |
103 | @Override
104 | public void dispose() {
105 | if (subscription != null) {
106 | subscription.cancel();
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/observers/JavaFxObserver.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.observers;
17 |
18 | import java.util.Optional;
19 |
20 | import org.pdfsam.rxjavafx.observables.JavaFxObservable;
21 |
22 | import io.reactivex.rxjava3.core.Observable;
23 | import io.reactivex.rxjava3.functions.Consumer;
24 | import io.reactivex.rxjava3.observables.ConnectableObservable;
25 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
26 | import javafx.beans.binding.Binding;
27 | import javafx.beans.value.ObservableValue;
28 |
29 | public enum JavaFxObserver {
30 | ;//no instances
31 |
32 | /**
33 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
34 | */
35 | public static Binding toBinding(Observable obs) {
36 | return toBinding(obs, JavaFxObserver::onError);
37 | }
38 |
39 | /**
40 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
41 | */
42 | public static Binding toBinding(Observable obs, Consumer onErrorAction) {
43 | BindingObserver bindingObserver = new BindingObserver<>(t -> t, onErrorAction);
44 | obs.subscribe(bindingObserver);
45 | return bindingObserver;
46 | }
47 |
48 | /**
49 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
50 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
51 | */
52 | public static Binding toNullBinding(Observable obs, T nullSentinel) {
53 | return toNullBinding(obs, nullSentinel, JavaFxObserver::onError);
54 | }
55 |
56 | /**
57 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
58 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
59 | */
60 | public static Binding toNullBinding(Observable obs, T nullSentinel, Consumer onErrorAction) {
61 | if (nullSentinel == null) {
62 | throw new NullPointerException("The null value sentinel must not be null.");
63 | }
64 | BindingObserver bindingObserver = new BindingObserver<>(t -> t == nullSentinel ? null : t, onErrorAction);
65 | obs.subscribe(bindingObserver);
66 | return bindingObserver;
67 | }
68 |
69 | /**
70 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
71 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
72 | */
73 | public static Binding toNullableBinding(Observable> obs) {
74 | return toNullableBinding(obs, JavaFxObserver::onError);
75 | }
76 |
77 | /**
78 | * Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
79 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
80 | */
81 | public static Binding toNullableBinding(Observable> obs, Consumer onErrorAction) {
82 | BindingObserver, T> bindingObserver = new BindingObserver<>(o -> o.orElse(null), onErrorAction);
83 | obs.subscribe(bindingObserver);
84 | return bindingObserver;
85 | }
86 |
87 | /**
88 | * Turns an Observable into an lazy JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
89 | */
90 | public static Binding toLazyBinding(Observable obs) {
91 | return toLazyBinding(obs, JavaFxObserver::onError);
92 | }
93 |
94 | /**
95 | * Turns an Observable into an eager JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
96 | */
97 | public static Binding toLazyBinding(Observable obs, Consumer onErrorAction) {
98 | ConnectableObservable published = obs.publish();
99 | BindingObserver bindingObserver = new BindingObserver<>(t -> t, published, onErrorAction);
100 | published.subscribe(bindingObserver);
101 | return bindingObserver;
102 | }
103 |
104 | /**
105 | * Turns an Observable into an eager JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
106 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
107 | */
108 | public static Binding toLazyNullBinding(Observable obs, T nullSentinel) {
109 | return toLazyNullBinding(obs, nullSentinel, JavaFxObserver::onError);
110 | }
111 |
112 | /**
113 | * Turns an Observable into an eager JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
114 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
115 | */
116 | public static Binding toLazyNullBinding(Observable obs, T nullSentinel, Consumer onErrorAction) {
117 | if (nullSentinel == null) {
118 | throw new NullPointerException("The null value sentinel must not be null.");
119 | }
120 | ConnectableObservable published = obs.publish();
121 | BindingObserver bindingObserver = new BindingObserver<>(t -> t == nullSentinel ? null : t, published, onErrorAction);
122 | published.subscribe(bindingObserver);
123 | return bindingObserver;
124 | }
125 |
126 | /**
127 | * Turns an Observable into an lazy JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
128 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
129 | */
130 | public static Binding toLazyNullableBinding(Observable> obs) {
131 | return toLazyNullableBinding(obs, JavaFxObserver::onError);
132 | }
133 |
134 | /**
135 | * Turns an Observable into an lazy JavaFX Binding that subscribes to the Observable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
136 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
137 | */
138 | public static Binding toLazyNullableBinding(Observable> obs, Consumer onErrorAction) {
139 | ConnectableObservable> published = obs.publish();
140 | BindingObserver, T> bindingObserver = new BindingObserver<>(o -> o.orElse(null), published, onErrorAction);
141 | published.subscribe(bindingObserver);
142 | return bindingObserver;
143 | }
144 |
145 | private static void onError(Throwable t) {
146 | RxJavaPlugins.onError(t);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/observers/JavaFxSubscriber.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.observers;
17 |
18 | import java.util.Optional;
19 |
20 | import org.pdfsam.rxjavafx.observables.JavaFxObservable;
21 |
22 | import io.reactivex.rxjava3.core.Flowable;
23 | import io.reactivex.rxjava3.flowables.ConnectableFlowable;
24 | import io.reactivex.rxjava3.functions.Consumer;
25 | import io.reactivex.rxjava3.plugins.RxJavaPlugins;
26 | import javafx.beans.binding.Binding;
27 | import javafx.beans.value.ObservableValue;
28 |
29 | public enum JavaFxSubscriber {
30 | ;//no instances
31 |
32 | /**
33 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
34 | */
35 | public static Binding toBinding(Flowable flowable) {
36 | return toBinding(flowable, JavaFxSubscriber::onError);
37 | }
38 |
39 | /**
40 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
41 | */
42 | public static Binding toBinding(Flowable flowable, Consumer onErrorAction) {
43 | BindingSubscriber bindingSubscriber = new BindingSubscriber<>(t -> t, onErrorAction);
44 | flowable.subscribe(bindingSubscriber);
45 | return bindingSubscriber;
46 | }
47 |
48 | /**
49 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
50 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
51 | */
52 | public static Binding toNullBinding(Flowable flowable, T nullSentinel) {
53 | return toNullBinding(flowable, nullSentinel, JavaFxSubscriber::onError);
54 | }
55 |
56 | /**
57 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
58 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
59 | */
60 | public static Binding toNullBinding(Flowable flowable, T nullSentinel, Consumer onErrorAction) {
61 | if (nullSentinel == null) {
62 | throw new NullPointerException("The null value sentinel must not be null.");
63 | }
64 | BindingSubscriber bindingSubscriber = new BindingSubscriber<>(t -> t == nullSentinel ? null : t, onErrorAction);
65 | flowable.subscribe(bindingSubscriber);
66 | return bindingSubscriber;
67 | }
68 |
69 | /**
70 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
71 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
72 | */
73 | public static Binding toNullableBinding(Flowable> flowable) {
74 | return toNullableBinding(flowable, JavaFxSubscriber::onError);
75 | }
76 |
77 | /**
78 | * Turns an Flowable into an eager JavaFX Binding that subscribes immediately to the Flowable. Calling the Binding's dispose() method will handle the unsubscription.
79 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
80 | */
81 | public static Binding toNullableBinding(Flowable> flowable, Consumer onErrorAction) {
82 | BindingSubscriber, T> bindingSubscriber = new BindingSubscriber<>(o -> o.orElse(null), onErrorAction);
83 | flowable.subscribe(bindingSubscriber);
84 | return bindingSubscriber;
85 | }
86 |
87 | /**
88 | * Turns an Flowable into an lazy JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
89 | */
90 | public static Binding toLazyBinding(Flowable flowable) {
91 | return toLazyBinding(flowable, JavaFxSubscriber::onError);
92 | }
93 |
94 | /**
95 | * Turns an Flowable into an eager JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
96 | */
97 | public static Binding toLazyBinding(Flowable flowable, Consumer onErrorAction) {
98 | ConnectableFlowable published = flowable.publish();
99 | BindingSubscriber bindingSubscriber = new BindingSubscriber<>(t -> t, published, onErrorAction);
100 | published.subscribe(bindingSubscriber);
101 | return bindingSubscriber;
102 | }
103 |
104 | /**
105 | * Turns an Flowable into an eager JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
106 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
107 | */
108 | public static Binding toLazyNullBinding(Flowable flowable, T nullSentinel) {
109 | return toLazyNullBinding(flowable, nullSentinel, JavaFxSubscriber::onError);
110 | }
111 |
112 | /**
113 | * Turns an Flowable into an eager JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
114 | * This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
115 | */
116 | public static Binding toLazyNullBinding(Flowable flowable, T nullSentinel, Consumer onErrorAction) {
117 | if (nullSentinel == null) {
118 | throw new NullPointerException("The null value sentinel must not be null.");
119 | }
120 | ConnectableFlowable published = flowable.publish();
121 | BindingSubscriber bindingSubscriber = new BindingSubscriber<>(t -> t == nullSentinel ? null : t, published, onErrorAction);
122 | published.subscribe(bindingSubscriber);
123 | return bindingSubscriber;
124 | }
125 |
126 | /**
127 | * Turns an Flowable into an lazy JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
128 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
129 | */
130 | public static Binding toLazyNullableBinding(Flowable> flowable) {
131 | return toLazyNullableBinding(flowable, JavaFxSubscriber::onError);
132 | }
133 |
134 | /**
135 | * Turns an Flowable into an lazy JavaFX Binding that subscribes to the Flowable when its getValue() is called. Calling the Binding's dispose() method will handle the unsubscription.
136 | * This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
137 | */
138 | public static Binding toLazyNullableBinding(Flowable> flowable, Consumer onErrorAction) {
139 | ConnectableFlowable> published = flowable.publish();
140 | BindingSubscriber, T> bindingSubscriber = new BindingSubscriber<>(o -> o.orElse(null), published, onErrorAction);
141 | published.subscribe(bindingSubscriber);
142 | return bindingSubscriber;
143 | }
144 |
145 | private static void onError(Throwable t) {
146 | RxJavaPlugins.onError(t);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/observers/ObservableListenerHelper.java:
--------------------------------------------------------------------------------
1 | package org.pdfsam.rxjavafx.observers;
2 |
3 | import java.util.Arrays;
4 | import java.util.Objects;
5 |
6 | import javafx.beans.InvalidationListener;
7 | import javafx.beans.value.ChangeListener;
8 | import javafx.beans.value.ObservableValue;
9 |
10 | public abstract class ObservableListenerHelper implements ObservableValue {
11 | private boolean sentinel;
12 | private int invalidationSize = 0;
13 | private int size = 0;
14 | private Object listener = null;
15 | private T value = null;
16 |
17 | /**
18 | * {@inheritDoc}
19 | */
20 | @Override
21 | public void addListener(InvalidationListener listener) {
22 | Objects.requireNonNull(listener);
23 | if (size == 0) {
24 | sentinel = false;
25 | invalidationSize = 1;
26 | this.listener = listener;
27 | } else if (size == 1) {
28 | sentinel = false;
29 | if (invalidationSize == 1) {
30 | invalidationSize = 2;
31 | this.listener = new Object[]{this.listener, listener};
32 | } else {
33 | invalidationSize = 1;
34 | this.listener = new Object[]{listener, this.listener};
35 | }
36 | } else {
37 | Object[] l = (Object[]) this.listener;
38 |
39 | if (l.length <= size + 1 || sentinel) {
40 | sentinel = false;
41 | l = Arrays.copyOf(l, l.length * 3 / 2 + 1);
42 | this.listener = l;
43 | }
44 | if (size > invalidationSize) {
45 | System.arraycopy(l, invalidationSize, l, invalidationSize + 1, size - invalidationSize);
46 | }
47 | l[invalidationSize] = listener;
48 | invalidationSize++;
49 | }
50 | size++;
51 | }
52 |
53 | /**
54 | * {@inheritDoc}
55 | */
56 | @Override
57 | public void removeListener(InvalidationListener listener) {
58 | Objects.requireNonNull(listener);
59 | if (0 < invalidationSize) {
60 | if (size == 1) {
61 | if (invalidationSize == 1 && this.listener.equals(listener)) {
62 | sentinel = false;
63 | this.listener = null;
64 | invalidationSize--;
65 | size--;
66 | }
67 | } else if (size == 2) {
68 | Object[] l = (Object[]) this.listener;
69 | if (listener.equals(l[0])) {
70 | sentinel = false;
71 | invalidationSize--;
72 | size--;
73 | this.listener = l[1];
74 | } else if (invalidationSize == 2 && listener.equals(l[1])) {
75 | sentinel = false;
76 | invalidationSize--;
77 | size--;
78 | this.listener = l[0];
79 | }
80 | } else {
81 | Object[] l = (Object[]) this.listener;
82 |
83 | for (int i = 0; i < invalidationSize; i++) {
84 | if (listener.equals(l[i])) {
85 | if (sentinel) {
86 | sentinel = false;
87 | l = Arrays.copyOf(l, l.length);
88 | this.listener = l;
89 | }
90 | if (i + 1 < size) {
91 | System.arraycopy(l, i + 1, l, i, size - i - 1);
92 | } else {
93 | l[i] = null;
94 | }
95 | invalidationSize--;
96 | size--;
97 | break;
98 | }
99 | }
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * {@inheritDoc}
106 | */
107 | @Override
108 | public void addListener(ChangeListener super T> listener) {
109 | Objects.requireNonNull(listener);
110 | if (size == 0) {
111 | sentinel = false;
112 | this.listener = listener;
113 | this.value = getValue();
114 | } else if (size == 1) {
115 | sentinel = false;
116 | this.listener = new Object[]{this.listener, listener};
117 | } else {
118 | Object[] l = (Object[]) this.listener;
119 | if (l.length <= size + 1) { // test for sentinel not required as we put the new listener behind this.size, thus it won't be fired
120 | sentinel = false;
121 | l = Arrays.copyOf(l, l.length * 3 / 2 + 1);
122 | this.listener = l;
123 | }
124 | l[size] = listener;
125 | }
126 | if (invalidationSize == size) {
127 | this.value = getValue();
128 | }
129 | size++;
130 | }
131 |
132 | /**
133 | * {@inheritDoc}
134 | */
135 | @Override
136 | public void removeListener(ChangeListener super T> listener) {
137 | Objects.requireNonNull(listener);
138 | if (invalidationSize < size) {
139 | if (size == 1) {
140 | sentinel = false;
141 | size--;
142 | this.listener = null;
143 | this.value = null;
144 | } else if (size == 2) {
145 | Object[] l = (Object[]) this.listener;
146 | if (listener.equals(l[1])) {
147 | sentinel = false;
148 | size--;
149 | this.listener = l[0];
150 | if (invalidationSize == 1) {
151 | this.value = null;
152 | }
153 | } else if (invalidationSize == 0 && listener.equals(l[1])) {
154 | sentinel = false;
155 | size--;
156 | this.listener = l[1];
157 | }
158 | } else {
159 | Object[] l = (Object[]) this.listener;
160 |
161 | for (int i = invalidationSize; i < size; i++) {
162 | if (listener.equals(l[i])) {
163 | if (sentinel) {
164 | sentinel = false;
165 | l = Arrays.copyOf(l, l.length);
166 | this.listener = l;
167 | }
168 | if (i + 1 < size) {
169 | System.arraycopy(l, i + 1, l, i, size - i - 1);
170 | } else {
171 | l[i] = null;
172 | }
173 | size--;
174 | if (size == invalidationSize) {
175 | this.value = null;
176 | }
177 | break;
178 | }
179 | }
180 | }
181 | }
182 | }
183 |
184 | protected void fireChange() {
185 | Object listener = this.listener;
186 | int invalidationSize = this.invalidationSize;
187 | int size = this.size;
188 | try {
189 | sentinel = true;
190 |
191 | if (size == 1) {
192 | if (invalidationSize == 1) {
193 | try {
194 | ((InvalidationListener) listener).invalidated(this);
195 | } catch (Exception e) {
196 | Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
197 | }
198 | } else {
199 | T oldValue = this.value;
200 | this.value = getValue();
201 | try {
202 | ((ChangeListener super T>) listener).changed(this, oldValue, this.value);
203 | } catch (Exception e) {
204 | Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
205 | }
206 | }
207 | } else if (size > 0) {
208 | Object[] l = (Object[]) listener;
209 |
210 | for (int i = 0; i < invalidationSize; i++) {
211 | try {
212 | ((InvalidationListener) l[i]).invalidated(this);
213 | } catch (Exception e) {
214 | Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
215 | }
216 | }
217 |
218 | if (invalidationSize < size) {
219 | T oldValue = this.value;
220 | this.value = getValue();
221 | for (int i = invalidationSize; i < size; i++) {
222 | try {
223 | ((ChangeListener super T>) l[i]).changed(this, oldValue, this.value);
224 | } catch (Exception e) {
225 | Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
226 | }
227 | }
228 | }
229 | }
230 | } finally {
231 | sentinel = false;
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/schedulers/JavaFxScheduler.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.schedulers;
17 |
18 | import java.util.concurrent.TimeUnit;
19 | import java.util.concurrent.atomic.AtomicReference;
20 |
21 | import io.reactivex.rxjava3.core.Scheduler;
22 | import io.reactivex.rxjava3.disposables.Disposable;
23 | import javafx.animation.KeyFrame;
24 | import javafx.animation.Timeline;
25 | import javafx.application.Platform;
26 | import javafx.util.Duration;
27 |
28 | /**
29 | * Executes work on the JavaFx UI thread.
30 | * This scheduler should only be used with actions that execute quickly.
31 | */
32 | public final class JavaFxScheduler extends Scheduler {
33 | private static final JavaFxScheduler INSTANCE = new JavaFxScheduler();
34 |
35 | /* package for unit test */JavaFxScheduler() {
36 | }
37 |
38 | public static JavaFxScheduler platform() {
39 | return INSTANCE;
40 | }
41 |
42 | private static void assertThatTheDelayIsValidForTheJavaFxTimer(long delay) {
43 | if (delay < 0 || delay > Integer.MAX_VALUE) {
44 | throw new IllegalArgumentException(String.format("The JavaFx timer only accepts non-negative delays up to %d milliseconds.", Integer.MAX_VALUE));
45 | }
46 | }
47 |
48 | @Override
49 | public Worker createWorker() {
50 | return new JavaFxWorker();
51 | }
52 |
53 | /**
54 | * A Worker implementation which manages a queue of QueuedRunnable for execution on the Java FX Application thread
55 | * For a simpler implementation the queue always contains at least one element.
56 | * {@link #head} is the element, which is in execution or was last executed
57 | * {@link #tail} is an atomic reference to the last element in the queue, or null when the worker was disposed
58 | * Recursive actions are not preferred and inserted at the tail of the queue as any other action would be
59 | * The Worker will only schedule a single job with {@link Platform#runLater(Runnable)} for when the queue was previously empty
60 | */
61 | private static class JavaFxWorker extends Worker implements Runnable {
62 | private volatile QueuedRunnable head = new QueuedRunnable(null); /// only advanced in run(), initialised with a starter element
63 | private final AtomicReference tail = new AtomicReference<>(head); /// points to the last element, null when disposed
64 |
65 | private static class QueuedRunnable extends AtomicReference implements Disposable, Runnable {
66 | private volatile Runnable action;
67 |
68 | private QueuedRunnable(Runnable action) {
69 | this.action = action;
70 | }
71 |
72 | @Override
73 | public void dispose() {
74 | action = null;
75 | }
76 |
77 | @Override
78 | public boolean isDisposed() {
79 | return action == null;
80 | }
81 |
82 | @Override
83 | public void run() {
84 | Runnable action = this.action;
85 | if (action != null) {
86 | action.run();
87 | }
88 | this.action = null;
89 | }
90 | }
91 |
92 | @Override
93 | public void dispose() {
94 | tail.set(null);
95 | QueuedRunnable qr = this.head;
96 | while (qr != null) {
97 | qr.dispose();
98 | qr = qr.getAndSet(null);
99 | }
100 | }
101 |
102 | @Override
103 | public boolean isDisposed() {
104 | return tail.get() == null;
105 | }
106 |
107 | @Override
108 | public Disposable schedule(final Runnable action, long delayTime, TimeUnit unit) {
109 | long delay = Math.max(0, unit.toMillis(delayTime));
110 | assertThatTheDelayIsValidForTheJavaFxTimer(delay);
111 |
112 | final QueuedRunnable queuedRunnable = new QueuedRunnable(action);
113 | if (delay == 0) { // delay is too small for the java fx timer, schedule it without delay
114 | return schedule(queuedRunnable);
115 | }
116 |
117 | final Timeline timer = new Timeline(new KeyFrame(Duration.millis(delay), event -> schedule(queuedRunnable)));
118 | timer.play();
119 |
120 | return Disposable.fromRunnable(() -> {
121 | queuedRunnable.dispose();
122 | timer.stop();
123 | });
124 | }
125 |
126 | @Override
127 | public Disposable schedule(final Runnable action) {
128 | if (isDisposed()) {
129 | return Disposable.disposed();
130 | }
131 |
132 | final QueuedRunnable queuedRunnable = action instanceof QueuedRunnable ? (QueuedRunnable) action : new QueuedRunnable(action);
133 |
134 | QueuedRunnable tailPivot;
135 | do {
136 | tailPivot = tail.get();
137 | } while (tailPivot != null && !tailPivot.compareAndSet(null, queuedRunnable));
138 |
139 | if (tailPivot == null) {
140 | queuedRunnable.dispose();
141 | } else {
142 | tail.compareAndSet(tailPivot, queuedRunnable); // can only fail with a concurrent dispose and we don't want to override the disposed value
143 | if (tailPivot == head) {
144 | if (Platform.isFxApplicationThread()) {
145 | run();
146 | } else {
147 | Platform.runLater(this);
148 | }
149 | }
150 | }
151 | return queuedRunnable;
152 | }
153 |
154 | @Override
155 | public void run() {
156 | for (QueuedRunnable qr = head.get(); qr != null; qr = qr.get()) {
157 | qr.run();
158 | head = qr;
159 | }
160 | }
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/ActionEventSource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | import org.pdfsam.rxjavafx.schedulers.JavaFxScheduler;
19 | import org.pdfsam.rxjavafx.subscriptions.JavaFxSubscriptions;
20 |
21 | import io.reactivex.rxjava3.core.Observable;
22 | import io.reactivex.rxjava3.core.ObservableEmitter;
23 | import javafx.event.ActionEvent;
24 | import javafx.event.EventHandler;
25 | import javafx.scene.Node;
26 | import javafx.scene.control.ContextMenu;
27 | import javafx.scene.control.MenuItem;
28 |
29 | public final class ActionEventSource {
30 | private ActionEventSource() {}
31 |
32 | public static Observable fromActionEvents(final Node node) {
33 | return NodeEventSource.fromNodeEvents(node, ActionEvent.ACTION);
34 | }
35 | public static Observable fromActionEvents(final ContextMenu source) {
36 |
37 | return Observable.create((ObservableEmitter subscriber) -> {
38 | final EventHandler handler = subscriber::onNext;
39 |
40 | source.addEventHandler(ActionEvent.ANY, handler);
41 |
42 | subscriber.setDisposable(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeEventHandler(ActionEvent.ANY, handler)));
43 | }).subscribeOn(JavaFxScheduler.platform());
44 | }
45 | public static Observable fromActionEvents(final MenuItem source) {
46 | return Observable.create((ObservableEmitter subscriber) -> {
47 | final EventHandler handler = subscriber::onNext;
48 |
49 | source.addEventHandler(ActionEvent.ANY, handler);
50 |
51 | subscriber.setDisposable(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeEventHandler(ActionEvent.ANY, handler)));
52 | }).subscribeOn(JavaFxScheduler.platform());
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/Change.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | import java.util.Objects;
19 |
20 | public final class Change {
21 | private final T oldVal;
22 | private final T newVal;
23 |
24 | public Change(T oldVal, T newVal) {
25 | this.oldVal = oldVal;
26 | this.newVal = newVal;
27 | }
28 | public T getOldVal() {
29 | return oldVal;
30 | }
31 | public T getNewVal() {
32 | return newVal;
33 | }
34 |
35 | @Override
36 | public boolean equals(Object o) {
37 | if (this == o) return true;
38 | if (o == null || getClass() != o.getClass()) return false;
39 | Change> change = (Change>) o;
40 | return Objects.equals(oldVal, change.oldVal) &&
41 | Objects.equals(newVal, change.newVal);
42 | }
43 |
44 | @Override
45 | public int hashCode() {
46 | return Objects.hash(oldVal, newVal);
47 | }
48 |
49 | @Override
50 | public String toString() {
51 | return "Change{" +
52 | "oldVal=" + oldVal +
53 | ", newVal=" + newVal +
54 | '}';
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/DialogSource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | import java.util.Optional;
19 |
20 | import org.pdfsam.rxjavafx.schedulers.JavaFxScheduler;
21 |
22 | import io.reactivex.rxjava3.core.Maybe;
23 | import io.reactivex.rxjava3.core.Single;
24 | import javafx.scene.control.Dialog;
25 |
26 | public final class DialogSource {
27 | private DialogSource() {}
28 |
29 | public static Maybe fromDialogSource(final Dialog dialog) {
30 | return Single.fromCallable(dialog::showAndWait)
31 | .subscribeOn(JavaFxScheduler.platform())
32 | .filter(Optional::isPresent)
33 | .map(Optional::get);
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/Flag.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | public enum Flag {
19 | ADDED,
20 | REMOVED,
21 | UPDATED;
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/ListChange.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | /**
19 | * Holds an ADDED, REMOVED, or UPDATED flag with the associated value
20 | * @param
21 | */
22 | public final class ListChange {
23 | private final T value;
24 | private final Flag flag;
25 |
26 | private ListChange(T value, Flag flag) {
27 | this.value = value;
28 | this.flag = flag;
29 | }
30 | public static ListChange of(T value, Flag flag) {
31 | return new ListChange<>(value, flag);
32 | }
33 | public T getValue() {
34 | return value;
35 | }
36 | public Flag getFlag() {
37 | return flag;
38 | }
39 | @Override
40 | public String toString() {
41 | return flag + " " + value;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/MapChange.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | public final class MapChange {
19 | private final K key;
20 | private final T value;
21 | private final Flag flag;
22 |
23 | MapChange(K key, T value, Flag flag) {
24 | this.key = key;
25 | this.value = value;
26 | this.flag = flag;
27 | }
28 |
29 | public K getKey() {
30 | return key;
31 | }
32 |
33 | public T getValue() {
34 | return value;
35 | }
36 |
37 | public Flag getFlag() {
38 | return flag;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/NodeEventSource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | import org.pdfsam.rxjavafx.schedulers.JavaFxScheduler;
19 | import org.pdfsam.rxjavafx.subscriptions.JavaFxSubscriptions;
20 |
21 | import io.reactivex.rxjava3.core.Observable;
22 | import io.reactivex.rxjava3.core.ObservableEmitter;
23 | import javafx.event.Event;
24 | import javafx.event.EventHandler;
25 | import javafx.event.EventType;
26 | import javafx.scene.Node;
27 |
28 | public class NodeEventSource {
29 | public static Observable fromNodeEvents(final Node source, final EventType eventType) {
30 |
31 | return Observable.create((ObservableEmitter emitter) -> {
32 | final EventHandler handler = emitter::onNext;
33 |
34 | source.addEventHandler(eventType, handler);
35 |
36 | emitter.setDisposable(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeEventHandler(eventType, handler)));
37 | }).subscribeOn(JavaFxScheduler.platform());
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/org/pdfsam/rxjavafx/sources/ObservableListSource.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2017 Netflix, Inc.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package org.pdfsam.rxjavafx.sources;
17 |
18 | import java.util.HashMap;
19 | import java.util.function.Function;
20 |
21 | import org.pdfsam.rxjavafx.observables.JavaFxObservable;
22 | import org.pdfsam.rxjavafx.schedulers.JavaFxScheduler;
23 | import org.pdfsam.rxjavafx.subscriptions.JavaFxSubscriptions;
24 |
25 | import io.reactivex.rxjava3.core.Observable;
26 | import io.reactivex.rxjava3.core.ObservableOnSubscribe;
27 | import javafx.beans.property.ListProperty;
28 | import javafx.collections.ListChangeListener;
29 | import javafx.collections.ObservableList;
30 |
31 | public final class ObservableListSource {
32 | private ObservableListSource() {}
33 |
34 | public static Observable> fromObservableList(final ObservableList source) {
35 |
36 | Observable> mutations = Observable.create((ObservableOnSubscribe>) subscriber -> {
37 | ListChangeListener listener = c -> subscriber.onNext(source);
38 | source.addListener(listener);
39 | subscriber.setDisposable(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
40 | });
41 |
42 |
43 | if (source instanceof ListProperty>) {
44 | return JavaFxObservable.valuesOf((ListProperty) source);
45 | } else {
46 | return mutations.startWithArray(source);
47 | }
48 | }
49 |
50 | public static Observable fromObservableListAdds(final ObservableList