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}