All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.reactfx.ObservableBase Maven / Gradle / Ivy

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:
 * 
    *
  1. Observer management: adding and removing observers.
  2. *
  3. 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.
  4. *
  5. Observer notification.
  6. *
* * @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