Skip to content

Commit 632904a

Browse files
authored
Merge pull request #57 from protogenes/feature/nullable-observer
add nullable Bindings
2 parents 8217e75 + dda401c commit 632904a

File tree

5 files changed

+375
-81
lines changed

5 files changed

+375
-81
lines changed

Diff for: src/main/java/io/reactivex/rxjavafx/observers/BindingObserver.java

100644100755
+27-17
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Copyright 2017 Netflix, Inc.
3-
*
3+
* <p>
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
* <p>
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
* <p>
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -19,28 +19,32 @@
1919
import io.reactivex.Observer;
2020
import io.reactivex.disposables.Disposable;
2121
import io.reactivex.functions.Consumer;
22+
import io.reactivex.functions.Function;
2223
import io.reactivex.observables.ConnectableObservable;
2324
import javafx.beans.InvalidationListener;
2425
import javafx.beans.binding.Binding;
2526
import javafx.beans.value.ChangeListener;
2627
import javafx.beans.value.ObservableValue;
2728
import javafx.collections.ObservableList;
2829

30+
final class BindingObserver<T, S> implements Observer<T>, ObservableValue<S>, Binding<S> {
2931

30-
final class BindingObserver<T> implements Observer<T>, ObservableValue<T>, Binding<T> {
31-
32-
private final Consumer<Throwable> onError;
32+
private final Function<T, S> unmaskingFunction;
33+
private final Consumer<Throwable> onError;
3334
private final ConnectableObservable<T> obs;
3435
private boolean connected = false;
35-
private Disposable disposable;
36-
private ExpressionHelper<T> helper;
37-
private T value;
36+
private Disposable disposable;
37+
private ExpressionHelper<S> helper;
38+
private S value;
3839

39-
BindingObserver(Consumer<Throwable> onError) {
40+
BindingObserver(Function<T, S> unmaskingFunction, Consumer<Throwable> onError) {
41+
this.unmaskingFunction = unmaskingFunction;
4042
this.onError = onError;
4143
this.obs = null;
4244
}
43-
BindingObserver(ConnectableObservable<T> obs, Consumer<Throwable> onError) {
45+
46+
BindingObserver(Function<T, S> unmaskingFunction, ConnectableObservable<T> obs, Consumer<Throwable> onError) {
47+
this.unmaskingFunction = unmaskingFunction;
4448
this.onError = onError;
4549
this.obs = obs;
4650
}
@@ -66,17 +70,23 @@ public void onError(Throwable e) {
6670

6771
@Override
6872
public void onNext(T t) {
69-
value = t;
70-
fireValueChangedEvent();
73+
try {
74+
value = unmaskingFunction.apply(t);
75+
fireValueChangedEvent();
76+
} catch (Exception e) {
77+
onError(e);
78+
}
7179
}
80+
7281
@Override
73-
public T getValue() {
82+
public S getValue() {
7483
if (!connected && obs != null) {
7584
obs.connect();
7685
connected = true;
7786
}
7887
return value;
7988
}
89+
8090
@Override
8191
public boolean isValid() {
8292
return true;
@@ -111,7 +121,7 @@ public void addListener(InvalidationListener listener) {
111121
* {@inheritDoc}
112122
*/
113123
@Override
114-
public void addListener(ChangeListener<? super T> listener) {
124+
public void addListener(ChangeListener<? super S> listener) {
115125
helper = ExpressionHelper.addListener(helper, this, listener);
116126
}
117127

@@ -127,13 +137,13 @@ public void removeListener(InvalidationListener listener) {
127137
* {@inheritDoc}
128138
*/
129139
@Override
130-
public void removeListener(ChangeListener<? super T> listener) {
140+
public void removeListener(ChangeListener<? super S> listener) {
131141
helper = ExpressionHelper.removeListener(helper, listener);
132142
}
133143

134144
/**
135145
* Notify the currently registered observers of a value change.
136-
*
146+
* <p>
137147
* This implementation will ignore all adds and removes of observers that
138148
* are done while a notification is processed. The changes take effect in
139149
* the following call to fireValueChangedEvent.

Diff for: src/main/java/io/reactivex/rxjavafx/observers/BindingSubscriber.java

100644100755
+38-28
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Copyright 2017 Netflix, Inc.
3-
*
3+
* <p>
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
* <p>
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
* <p>
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,6 +18,7 @@
1818
import com.sun.javafx.binding.ExpressionHelper;
1919
import io.reactivex.flowables.ConnectableFlowable;
2020
import io.reactivex.functions.Consumer;
21+
import io.reactivex.functions.Function;
2122
import javafx.beans.InvalidationListener;
2223
import javafx.beans.binding.Binding;
2324
import javafx.beans.value.ChangeListener;
@@ -26,23 +27,32 @@
2627
import org.reactivestreams.Subscriber;
2728
import org.reactivestreams.Subscription;
2829

30+
final class BindingSubscriber<T, S> implements Subscriber<T>, ObservableValue<S>, Binding<S> {
2931

30-
final class BindingSubscriber<T> implements Subscriber<T>, ObservableValue<T>, Binding<T> {
31-
32-
private final Consumer<Throwable> onError;
33-
private final ConnectableFlowable<T> flowable;
32+
private final Function<T, S> unmaskingFunction;
33+
private final Consumer<Throwable> onError;
34+
private final ConnectableFlowable<T> obs;
3435
private boolean connected = false;
35-
private Subscription subscription;
36-
private ExpressionHelper<T> helper;
37-
private T value;
36+
private Subscription subscription;
37+
private ExpressionHelper<S> helper;
38+
private S value;
3839

39-
BindingSubscriber(Consumer<Throwable> onError) {
40-
this.flowable = null;
40+
BindingSubscriber(Function<T, S> unmaskingFunction, Consumer<Throwable> onError) {
41+
this.unmaskingFunction = unmaskingFunction;
4142
this.onError = onError;
43+
this.obs = null;
4244
}
43-
BindingSubscriber(ConnectableFlowable<T> flowable, Consumer<Throwable> onError) {
44-
this.flowable = flowable;
45+
46+
BindingSubscriber(Function<T, S> unmaskingFunction, ConnectableFlowable<T> obs, Consumer<Throwable> onError) {
47+
this.unmaskingFunction = unmaskingFunction;
4548
this.onError = onError;
49+
this.obs = obs;
50+
}
51+
52+
@Override
53+
public void onSubscribe(Subscription s) {
54+
this.subscription = s;
55+
this.subscription.request(Long.MAX_VALUE);
4656
}
4757

4858
@Override
@@ -59,25 +69,25 @@ public void onError(Throwable e) {
5969
}
6070
}
6171

62-
@Override
63-
public void onSubscribe(Subscription s) {
64-
subscription = s;
65-
subscription.request(Long.MAX_VALUE);
66-
}
67-
6872
@Override
6973
public void onNext(T t) {
70-
value = t;
71-
fireValueChangedEvent();
74+
try {
75+
value = unmaskingFunction.apply(t);
76+
fireValueChangedEvent();
77+
} catch (Exception e) {
78+
onError(e);
79+
}
7280
}
81+
7382
@Override
74-
public T getValue() {
75-
if (!connected && flowable != null) {
76-
flowable.connect();
83+
public S getValue() {
84+
if (!connected && obs != null) {
85+
obs.connect();
7786
connected = true;
7887
}
7988
return value;
8089
}
90+
8191
@Override
8292
public boolean isValid() {
8393
return true;
@@ -112,7 +122,7 @@ public void addListener(InvalidationListener listener) {
112122
* {@inheritDoc}
113123
*/
114124
@Override
115-
public void addListener(ChangeListener<? super T> listener) {
125+
public void addListener(ChangeListener<? super S> listener) {
116126
helper = ExpressionHelper.addListener(helper, this, listener);
117127
}
118128

@@ -128,13 +138,13 @@ public void removeListener(InvalidationListener listener) {
128138
* {@inheritDoc}
129139
*/
130140
@Override
131-
public void removeListener(ChangeListener<? super T> listener) {
141+
public void removeListener(ChangeListener<? super S> listener) {
132142
helper = ExpressionHelper.removeListener(helper, listener);
133143
}
134144

135145
/**
136146
* Notify the currently registered observers of a value change.
137-
*
147+
* <p>
138148
* This implementation will ignore all adds and removes of observers that
139149
* are done while a notification is processed. The changes take effect in
140150
* the following call to fireValueChangedEvent.

Diff for: src/main/java/io/reactivex/rxjavafx/observers/JavaFxObserver.java

100644100755
+96-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* Copyright 2017 Netflix, Inc.
3-
*
3+
* <p>
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
7-
*
7+
* <p>
88
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
9+
* <p>
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
1212
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -18,42 +18,129 @@
1818
import io.reactivex.Observable;
1919
import io.reactivex.functions.Consumer;
2020
import io.reactivex.observables.ConnectableObservable;
21+
import io.reactivex.rxjavafx.observables.JavaFxObservable;
2122
import javafx.beans.binding.Binding;
23+
import javafx.beans.value.ObservableValue;
24+
25+
import java.util.Optional;
2226

2327
public enum JavaFxObserver {
2428
;//no instances
29+
2530
/**
2631
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
2732
*/
2833
public static <T> Binding<T> toBinding(Observable<T> obs) {
29-
BindingObserver<T> bindingObserver = new BindingObserver<>(e -> {});
34+
return toBinding(obs, JavaFxObserver::onError);
35+
}
36+
37+
/**
38+
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
39+
*/
40+
public static <T> Binding<T> toBinding(Observable<T> obs, Consumer<Throwable> onErrorAction) {
41+
BindingObserver<T, T> bindingObserver = new BindingObserver<>(t -> t, onErrorAction);
42+
obs.subscribe(bindingObserver);
43+
return bindingObserver;
44+
}
45+
46+
/**
47+
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
48+
* This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
49+
*/
50+
public static <T> Binding<T> toNullBinding(Observable<T> obs, T nullSentinel) {
51+
return toNullBinding(obs, nullSentinel, JavaFxObserver::onError);
52+
}
53+
54+
/**
55+
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
56+
* This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
57+
*/
58+
public static <T> Binding<T> toNullBinding(Observable<T> obs, T nullSentinel, Consumer<Throwable> onErrorAction) {
59+
if (nullSentinel == null) {
60+
throw new NullPointerException("The null value sentinel must not be null.");
61+
}
62+
BindingObserver<T, T> bindingObserver = new BindingObserver<>(t -> t == nullSentinel ? null : t, onErrorAction);
3063
obs.subscribe(bindingObserver);
3164
return bindingObserver;
3265
}
66+
3367
/**
3468
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
69+
* This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
3570
*/
36-
public static <T> Binding<T> toBinding(Observable<T> obs, Consumer<Throwable> onErrorAction ) {
37-
BindingObserver<T> bindingObserver = new BindingObserver<>(onErrorAction);
71+
public static <T> Binding<T> toNullableBinding(Observable<Optional<T>> obs) {
72+
return toNullableBinding(obs, JavaFxObserver::onError);
73+
}
74+
75+
/**
76+
* Turns an Observable into an eager JavaFX Binding that subscribes immediately to the Observable. Calling the Binding's dispose() method will handle the unsubscription.
77+
* This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
78+
*/
79+
public static <T> Binding<T> toNullableBinding(Observable<Optional<T>> obs, Consumer<Throwable> onErrorAction) {
80+
BindingObserver<Optional<T>, T> bindingObserver = new BindingObserver<>(o -> o.orElse(null), onErrorAction);
3881
obs.subscribe(bindingObserver);
3982
return bindingObserver;
4083
}
84+
4185
/**
4286
* 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.
4387
*/
4488
public static <T> Binding<T> toLazyBinding(Observable<T> obs) {
89+
return toLazyBinding(obs, JavaFxObserver::onError);
90+
}
91+
92+
/**
93+
* 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.
94+
*/
95+
public static <T> Binding<T> toLazyBinding(Observable<T> obs, Consumer<Throwable> onErrorAction) {
4596
ConnectableObservable<T> published = obs.publish();
46-
BindingObserver<T> bindingObserver = new BindingObserver<>(published, e -> {});
97+
BindingObserver<T, T> bindingObserver = new BindingObserver<>(t -> t, published, onErrorAction);
4798
published.subscribe(bindingObserver);
4899
return bindingObserver;
49100
}
101+
50102
/**
51103
* 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.
104+
* This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
52105
*/
53-
public static <T> Binding<T> toLazyBinding(Observable<T> obs, Consumer<Throwable> onErrorAction ) {
106+
public static <T> Binding<T> toLazyNullBinding(Observable<T> obs, T nullSentinel) {
107+
return toLazyNullBinding(obs, nullSentinel, JavaFxObserver::onError);
108+
}
109+
110+
/**
111+
* 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.
112+
* This variant unmasks a nullable value as in {@link JavaFxObservable#valuesOf(ObservableValue, Object)} and emits null when the sentinel is encountered.
113+
*/
114+
public static <T> Binding<T> toLazyNullBinding(Observable<T> obs, T nullSentinel, Consumer<Throwable> onErrorAction) {
115+
if (nullSentinel == null) {
116+
throw new NullPointerException("The null value sentinel must not be null.");
117+
}
54118
ConnectableObservable<T> published = obs.publish();
55-
BindingObserver<T> bindingObserver = new BindingObserver<>(published,onErrorAction);
119+
BindingObserver<T, T> bindingObserver = new BindingObserver<>(t -> t == nullSentinel ? null : t, published, onErrorAction);
56120
published.subscribe(bindingObserver);
57121
return bindingObserver;
58122
}
123+
124+
/**
125+
* 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.
126+
* This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
127+
*/
128+
public static <T> Binding<T> toLazyNullableBinding(Observable<Optional<T>> obs) {
129+
return toLazyNullableBinding(obs, JavaFxObserver::onError);
130+
}
131+
132+
/**
133+
* 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.
134+
* This variant unmasks a nullable value as in {@link JavaFxObservable#nullableValuesOf(ObservableValue)} and emits null when the value is not present.
135+
*/
136+
public static <T> Binding<T> toLazyNullableBinding(Observable<Optional<T>> obs, Consumer<Throwable> onErrorAction) {
137+
ConnectableObservable<Optional<T>> published = obs.publish();
138+
BindingObserver<Optional<T>, T> bindingObserver = new BindingObserver<>(o -> o.orElse(null), published, onErrorAction);
139+
published.subscribe(bindingObserver);
140+
return bindingObserver;
141+
}
142+
143+
private static void onError(Throwable t) {
144+
// nothing
145+
}
59146
}

0 commit comments

Comments
 (0)