001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang;
012
013import java.util.LinkedList;
014
015/**
016 * Models a thread-safe observable variable.
017 */
018public final class Observable {
019
020  private volatile Object value;
021
022  private final Object lock = new Object();
023  private final LinkedList<Observer> observers = new LinkedList<>();
024
025  /**
026   * Creates a new observable from an initial value.
027   *
028   * @param initialValue the initial value.
029   */
030  public Observable(Object initialValue) {
031    this.value = initialValue;
032  }
033
034  /**
035   * Gets the current value.
036   *
037   * @return the current value.
038   */
039  public Object get() {
040    return value;
041  }
042
043  /**
044   * Changes the current value and notifies all observers.
045   *
046   * @param newValue the new value.
047   */
048  public void set(Object newValue) {
049    synchronized (lock) {
050      this.value = newValue;
051      for (Observer observer : observers) {
052        observer.apply(newValue);
053      }
054    }
055  }
056
057  /**
058   * Registers an observer.
059   *
060   * @param observer an observer.
061   * @return this observable object.
062   */
063  public Observable onChange(Observer observer) {
064    synchronized (lock) {
065      observers.add(observer);
066    }
067    return this;
068  }
069
070  /**
071   * Creates an observer that filters the values of this observable.
072   *
073   * @param predicate a predicate function.
074   * @return an observer whose values filter this observable.
075   */
076  public Observable filter(final Predicate predicate) {
077    final Observable observable = new Observable(null);
078    this.onChange(newValue -> {
079      if (predicate.apply(newValue)) {
080        observable.set(newValue);
081      }
082    });
083    return observable;
084  }
085
086  /**
087   * Creates an observer that maps the values of this observable.
088   *
089   * @param function a mapping function.
090   * @return an observer whose values map this observable.
091   */
092  public Observable map(final Function function) {
093    final Observable observable = new Observable(null);
094    this.onChange(newValue -> observable.set(function.apply(newValue)));
095    return observable;
096  }
097
098  @Override
099  public String toString() {
100    return String.format("Observable{value=%s}", value);
101  }
102
103  @FunctionalInterface
104  public interface Function {
105    Object apply(Object value);
106  }
107
108  @FunctionalInterface
109  public interface Predicate {
110    boolean apply(Object value);
111  }
112
113  @FunctionalInterface
114  public interface Observer {
115    void apply(Object newValue);
116  }
117}