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}