org.reactfx.ObservableBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reactfx Show documentation
Show all versions of reactfx Show documentation
Reactive event streams for JavaFX
The newest version!
package org.reactfx;
import java.util.function.Consumer;
import org.reactfx.util.ListHelper;
import org.reactfx.util.NotificationAccumulator;
/**
* Base class for observable objects. This abstract class implements:
*
* - Observer management: adding and removing observers.
* - Lazy binding to inputs. An observable has 0 or more inputs,
* most commonly, but not necessarily, other observables. Lazy binding to
* inputs means that the observable observes its inputs only when it is
* itself being observed.
* - Observer notification.
*
*
* @param type of the observer
* @param type of produced values
*/
public abstract class ObservableBase implements ProperObservable {
private ListHelper observers = null;
private Subscription inputSubscription = null;
private final NotificationAccumulator pendingNotifications;
protected ObservableBase(NotificationAccumulator pendingNotificationsImpl) {
this.pendingNotifications = pendingNotificationsImpl;
}
protected ObservableBase() {
this.pendingNotifications = defaultNotificationAccumulator();
}
/**
* Starts observing this observable's input(s), if any.
* This method is called when the number of observers goes from 0 to 1.
* This method is called before {@link #newObserver(Object)}
* is called for the first observer.
* @return subscription used to stop observing inputs. The subscription
* is unsubscribed (i.e. input observation stops) when the number of
* observers goes down to 0.
*/
protected abstract Subscription observeInputs();
public final boolean isObservingInputs() {
return inputSubscription != null;
}
protected final int getObserverCount() {
return ListHelper.size(observers);
}
@Override
public final void notifyObservers(T event) {
enqueueNotifications(event);
notifyObservers();
}
protected final void enqueueNotifications(T event) {
// may throw if pendingNotifications not empty and recursion not allowed
pendingNotifications.addAll(ListHelper.iterator(observers), event);
}
protected final void notifyObservers() {
try {
while(!pendingNotifications.isEmpty()) {
pendingNotifications.takeOne().run(); // run() may throw
}
} finally {
pendingNotifications.clear();
}
}
/**
* Executes action for each observer, regardless of recursion state.
* If {@code action} throws an exception for one observer, it will not
* be called for any subsequent observers and the exception will be
* propagated by this method.
* @param action action to execute for each observer.
*/
protected final void forEachObserver(Consumer action) {
ListHelper.forEach(observers, o -> action.accept(o));
}
/**
* Called for each new observer.
* Overriding this method is a convenient way for subclasses
* to handle this event, for example to publish some initial events.
*
* This method is called after the
* {@link #observeInputs()} method.
*/
protected void newObserver(O observer) {
// default implementation is empty
}
@Override
public final Subscription observe(O observer) {
addObserver(observer);
return () -> removeObserver(observer);
}
@Override
public final void addObserver(O observer) {
observers = ListHelper.add(observers, observer);
if(ListHelper.size(observers) == 1) {
inputSubscription = observeInputs();
}
newObserver(observer);
}
@Override
public final void removeObserver(O observer) {
observers = ListHelper.remove(observers, observer);
if(ListHelper.isEmpty(observers) && inputSubscription != null) {
inputSubscription.unsubscribe();
inputSubscription = null;
}
}
@Override
public int hashCode() {
return defaultHashCode();
}
@Override
public boolean equals(Object o) {
return defaultEquals(o);
}
@Override
public String toString() {
return defaultToString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy