rx.javafx.sources.ObservableListSource Maven / Gradle / Ivy
/**
* Copyright 2016 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package rx.javafx.sources;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import rx.Observable;
import rx.functions.Func1;
import rx.schedulers.JavaFxScheduler;
import rx.subscriptions.JavaFxSubscriptions;
import java.util.HashMap;
public final class ObservableListSource {
private ObservableListSource() {}
public static Observable> fromObservableList(final ObservableList source) {
return Observable.create((Observable.OnSubscribe>) subscriber -> {
ListChangeListener listener = c -> subscriber.onNext(source);
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).startWith(source).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable fromObservableListAdds(final ObservableList source) {
return Observable.create((Observable.OnSubscribe) subscriber -> {
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(subscriber::onNext);
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable fromObservableListRemovals(final ObservableList source) {
return Observable.create((Observable.OnSubscribe) subscriber -> {
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasRemoved()) {
c.getRemoved().forEach(subscriber::onNext);
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable fromObservableListUpdates(final ObservableList source) {
return Observable.create((Observable.OnSubscribe) subscriber -> {
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasUpdated()) {
for (int i = c.getFrom(); i < c.getTo(); i++) {
subscriber.onNext(c.getList().get(i));
}
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable> fromObservableListChanges(final ObservableList source) {
return Observable.create((Observable.OnSubscribe>) subscriber -> {
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().forEach(v -> subscriber.onNext(ListChange.of(v,Flag.ADDED)));
}
if (c.wasRemoved()) {
c.getRemoved().forEach(v -> subscriber.onNext(ListChange.of(v,Flag.REMOVED)));
}
if (c.wasUpdated()) {
for (int i = c.getFrom(); i < c.getTo(); i++) {
subscriber.onNext(ListChange.of(c.getList().get(i),Flag.UPDATED));
}
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable> fromObservableListDistinctChanges(final ObservableList source) {
return Observable.create((Observable.OnSubscribe>) subscriber -> {
final DupeCounter dupeCounter = new DupeCounter<>();
source.stream().forEach(dupeCounter::add);
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().stream().filter(v -> dupeCounter.add(v) == 1)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.ADDED)));
}
if (c.wasRemoved()) {
c.getRemoved().stream().filter(v -> dupeCounter.remove(v) == 0)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.REMOVED)));
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable> fromObservableListDistinctChanges(final ObservableList source, Func1 mapper) {
return Observable.create((Observable.OnSubscribe>) subscriber -> {
final DupeCounter dupeCounter = new DupeCounter<>();
source.stream().map(mapper::call).forEach(dupeCounter::add);
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().stream().filter(v -> dupeCounter.add(mapper.call(v)) == 1)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.ADDED)));
}
if (c.wasRemoved()) {
c.getRemoved().stream().filter(v -> dupeCounter.remove(mapper.call(v)) == 0)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.REMOVED)));
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
public static Observable> fromObservableListDistinctMappings(final ObservableList source, Func1 mapper) {
return Observable.create((Observable.OnSubscribe>) subscriber -> {
final DupeCounter dupeCounter = new DupeCounter<>();
source.stream().map(mapper::call).forEach(dupeCounter::add);
ListChangeListener listener = c -> {
while (c.next()) {
if (c.wasAdded()) {
c.getAddedSubList().stream().map(mapper::call)
.filter(v -> dupeCounter.add(v) == 1)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.ADDED)));
}
if (c.wasRemoved()) {
c.getRemoved().stream().map(mapper::call)
.filter(v -> dupeCounter.remove(v) == 0)
.forEach(v -> subscriber.onNext(ListChange.of(v,Flag.REMOVED)));
}
}
};
source.addListener(listener);
subscriber.add(JavaFxSubscriptions.unsubscribeInEventDispatchThread(() -> source.removeListener(listener)));
}).subscribeOn(JavaFxScheduler.getInstance());
}
private static final class DupeCounter {
private final HashMap counts = new HashMap<>();
public int add(T value) {
Integer prev = counts.get(value);
int newVal = 0;
if (prev == null) {
newVal = 1;
counts.put(value, newVal);
} else {
newVal = prev + 1;
counts.put(value, newVal);
}
return newVal;
}
public int remove(T value) {
Integer prev = counts.get(value);
if (prev != null && prev > 0) {
int newVal = prev - 1;
if (newVal == 0) {
counts.remove(value);
} else {
counts.put(value, newVal);
}
return newVal;
}
else {
throw new IllegalStateException();
}
}
}
}