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