| /******************************************************************************* |
| * Copyright (c) 2006, 2009 Cerner Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Brad Reynolds - initial API and implementation |
| * Matt Carter - bug 212518 (constantObservableValue) |
| * Matthew Hall - bugs 208332, 212518, 219909, 184830, 237718, 245647, |
| * 226289 |
| * Marko Topolnik - bug 184830 |
| ******************************************************************************/ |
| |
| package org.eclipse.core.databinding.observable; |
| |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.eclipse.core.databinding.observable.list.DecoratingObservableList; |
| import org.eclipse.core.databinding.observable.list.IListChangeListener; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.ObservableList; |
| import org.eclipse.core.databinding.observable.map.DecoratingObservableMap; |
| import org.eclipse.core.databinding.observable.map.IObservableMap; |
| import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; |
| import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; |
| import org.eclipse.core.databinding.observable.set.DecoratingObservableSet; |
| import org.eclipse.core.databinding.observable.set.IObservableSet; |
| import org.eclipse.core.databinding.observable.set.ISetChangeListener; |
| import org.eclipse.core.databinding.observable.set.ObservableSet; |
| import org.eclipse.core.databinding.observable.value.DecoratingObservableValue; |
| import org.eclipse.core.databinding.observable.value.IObservableValue; |
| import org.eclipse.core.databinding.observable.value.IValueChangeListener; |
| import org.eclipse.core.databinding.observable.value.IVetoableValue; |
| import org.eclipse.core.databinding.observable.value.ValueChangeEvent; |
| import org.eclipse.core.databinding.observable.value.ValueChangingEvent; |
| import org.eclipse.core.internal.databinding.observable.ConstantObservableValue; |
| import org.eclipse.core.internal.databinding.observable.DelayedObservableValue; |
| import org.eclipse.core.internal.databinding.observable.EmptyObservableList; |
| import org.eclipse.core.internal.databinding.observable.EmptyObservableSet; |
| import org.eclipse.core.internal.databinding.observable.MapEntryObservableValue; |
| import org.eclipse.core.internal.databinding.observable.StalenessObservableValue; |
| import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList; |
| import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableMap; |
| import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableSet; |
| import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableValue; |
| import org.eclipse.core.runtime.Assert; |
| |
| /** |
| * Contains static methods to operate on or return {@link IObservable |
| * Observables}. |
| * |
| * @since 1.0 |
| */ |
| public class Observables { |
| /** |
| * Returns an observable which delays notification of value change events |
| * from <code>observable</code> until <code>delay</code> milliseconds have |
| * elapsed since the last change event. This observable helps to boost |
| * performance in situations where an observable has computationally |
| * expensive listeners or many dependencies. A common use of this observable |
| * is to delay validation of user input until the user stops typing in a UI |
| * field. |
| * <p> |
| * To notify about pending changes, the returned observable fires a stale |
| * event when the wrapped observable value fires a change event, and remains |
| * stale until the delay has elapsed and the value change is fired. A call |
| * to {@link IObservableValue#getValue() getValue()} while a value change is |
| * pending will fire the value change immediately, short-circuiting the |
| * delay. |
| * <p> |
| * <b>Note:</b> |
| * <ul> |
| * <li>Use SWTObservables.observeDelayedValue() instead when the target |
| * observable is observing a SWT Control, or |
| * ViewersObservables.observeDelayedValue() when the target observable is |
| * observing a JFace Viewer. These observables ensure that pending value |
| * changes are fired when the underlying control loses focus. (Otherwise, it |
| * is possible for pending changes to be lost if a window is closed before |
| * the delay has elapsed.) |
| * <li>This observable does not forward {@link ValueChangingEvent} events |
| * from a wrapped {@link IVetoableValue}. |
| * </ul> |
| * |
| * @param <T> |
| * |
| * @param delay |
| * the delay in milliseconds |
| * @param observable |
| * the observable being delayed |
| * @return an observable which delays notification of value change events |
| * from <code>observable</code> until <code>delay</code> |
| * milliseconds have elapsed since the last change event. |
| * |
| * @since 1.2 |
| */ |
| public static <T> IObservableValue<T> observeDelayedValue(int delay, |
| IObservableValue<T> observable) { |
| return new DelayedObservableValue<T>(delay, observable); |
| } |
| |
| /** |
| * Returns an unmodifiable observable value backed by the given observable |
| * value. |
| * |
| * @param <T> |
| * |
| * @param value |
| * the value to wrap in an unmodifiable value |
| * @return an unmodifiable observable value backed by the given observable |
| * value |
| * @since 1.1 |
| */ |
| public static <T> IObservableValue<T> unmodifiableObservableValue( |
| IObservableValue<T> value) { |
| Assert.isNotNull(value, "Argument 'value' cannot be null"); //$NON-NLS-1$ |
| return new UnmodifiableObservableValue<T>(value); |
| } |
| |
| /** |
| * Returns an observable value with the given constant value. |
| * |
| * @param <T> |
| * |
| * @param realm |
| * the observable's realm |
| * @param value |
| * the observable's constant value |
| * @param valueType |
| * the observable's value type |
| * @return an immutable observable value with the given constant value |
| * @since 1.1 |
| * @deprecated use constantObservableValue(Realm, T, Class<T>) instead |
| */ |
| public static <T> IObservableValue<T> constantObservableValue(Realm realm, |
| T value, Object valueType) { |
| return new ConstantObservableValue<T>(realm, value, valueType); |
| } |
| |
| /** |
| * Returns an observable value with the given constant value. |
| * |
| * @param <T> |
| * |
| * @param realm |
| * the observable's realm |
| * @param value |
| * the observable's constant value |
| * @param valueType |
| * the observable's value type |
| * @return an immutable observable value with the given constant value |
| * @since 1.5 |
| */ |
| public static <T> IObservableValue<T> constantObservableValue(Realm realm, |
| T value, Class<T> valueType) { |
| return new ConstantObservableValue<T>(realm, value, valueType); |
| } |
| |
| /** |
| * Returns an observable value with the given constant value. |
| * |
| * @param <T> |
| * |
| * @param realm |
| * the observable's realm |
| * @param value |
| * the observable's constant value |
| * @return an immutable observable value with the given constant value |
| * @since 1.1 |
| */ |
| public static <T> IObservableValue<T> constantObservableValue(Realm realm, |
| T value) { |
| return constantObservableValue(realm, value, null); |
| } |
| |
| /** |
| * Returns an observable value with the given constant value. |
| * |
| * @param <T> |
| * |
| * @param value |
| * the observable's constant value |
| * @param valueType |
| * the observable's value type |
| * @return an immutable observable value with the given constant value |
| * @since 1.1 |
| */ |
| public static <T> IObservableValue<T> constantObservableValue(T value, |
| T valueType) { |
| return constantObservableValue(Realm.getDefault(), value, valueType); |
| } |
| |
| /** |
| * Returns an observable value with the given constant value. |
| * |
| * @param <T> |
| * |
| * @param value |
| * the observable's constant value |
| * @return an immutable observable value with the given constant value |
| * @since 1.1 |
| */ |
| public static <T> IObservableValue<T> constantObservableValue(T value) { |
| return constantObservableValue(Realm.getDefault(), value, null); |
| } |
| |
| /** |
| * Returns an unmodifiable observable list backed by the given observable |
| * list. |
| * |
| * @param <E> |
| * |
| * @param list |
| * the list to wrap in an unmodifiable list |
| * @return an unmodifiable observable list backed by the given observable |
| * list |
| */ |
| public static <E> IObservableList<E> unmodifiableObservableList( |
| IObservableList<E> list) { |
| if (list == null) { |
| throw new IllegalArgumentException("List parameter cannot be null."); //$NON-NLS-1$ |
| } |
| |
| return new UnmodifiableObservableList<E>(list); |
| } |
| |
| /** |
| * Returns an unmodifiable observable set backed by the given observable |
| * set. |
| * |
| * @param <E> |
| * |
| * @param set |
| * the set to wrap in an unmodifiable set |
| * @return an unmodifiable observable set backed by the given observable set |
| * @since 1.1 |
| */ |
| public static <E> IObservableSet<E> unmodifiableObservableSet( |
| IObservableSet<E> set) { |
| if (set == null) { |
| throw new IllegalArgumentException("Set parameter cannot be null"); //$NON-NLS-1$ |
| } |
| |
| return new UnmodifiableObservableSet<E>(set); |
| } |
| |
| /** |
| * Returns an unmodifiable observable map backed by the given observable |
| * map. |
| * |
| * @param <K> |
| * @param <V> |
| * |
| * @param map |
| * the map to wrap in an unmodifiable map |
| * @return an unmodifiable observable map backed by the given observable |
| * map. |
| * @since 1.2 |
| */ |
| public static <K, V> IObservableMap<K, V> unmodifiableObservableMap( |
| IObservableMap<K, V> map) { |
| if (map == null) { |
| throw new IllegalArgumentException("Map parameter cannot be null"); //$NON-NLS-1$ |
| } |
| |
| return new UnmodifiableObservableMap<K, V>(map); |
| } |
| |
| /** |
| * Returns an empty observable list. The returned list continues to work |
| * after it has been disposed of and can be disposed of multiple times. |
| * |
| * @return an empty observable list. |
| */ |
| public static IObservableList<Object> emptyObservableList() { |
| return emptyObservableList(Realm.getDefault(), null); |
| } |
| |
| /** |
| * Returns an empty observable list of the given element type. The returned |
| * list continues to work after it has been disposed of and can be disposed |
| * of multiple times. |
| * |
| * @param elementType |
| * the element type of the returned list |
| * @return an empty observable list |
| * @since 1.1 |
| */ |
| public static IObservableList<Object> emptyObservableList(Object elementType) { |
| return emptyObservableList(Realm.getDefault(), elementType); |
| } |
| |
| /** |
| * Returns an empty observable list belonging to the given realm. The |
| * returned list continues to work after it has been disposed of and can be |
| * disposed of multiple times. |
| * |
| * @param realm |
| * the realm of the returned list |
| * @return an empty observable list. |
| */ |
| public static <E> IObservableList<E> emptyObservableList(Realm realm) { |
| return emptyObservableList(realm, null); |
| } |
| |
| /** |
| * Returns an empty observable list of the given element type and belonging |
| * to the given realm. The returned list continues to work after it has been |
| * disposed of and can be disposed of multiple times. |
| * |
| * @param realm |
| * the realm of the returned list |
| * @param elementType |
| * the element type of the returned list |
| * @return an empty observable list |
| * @since 1.1 |
| */ |
| public static <E> IObservableList<E> emptyObservableList(Realm realm, |
| Object elementType) { |
| return new EmptyObservableList<E>(realm, elementType); |
| } |
| |
| /** |
| * Returns an empty observable set. The returned set continues to work after |
| * it has been disposed of and can be disposed of multiple times. |
| * |
| * @return an empty observable set. |
| */ |
| public static IObservableSet<Object> emptyObservableSet() { |
| return emptyObservableSet(Realm.getDefault(), null); |
| } |
| |
| /** |
| * Returns an empty observable set of the given element type. The returned |
| * set continues to work after it has been disposed of and can be disposed |
| * of multiple times. |
| * |
| * @param elementType |
| * the element type of the returned set |
| * @return an empty observable set |
| * @since 1.1 |
| */ |
| public static IObservableSet<Object> emptyObservableSet(Object elementType) { |
| return emptyObservableSet(Realm.getDefault(), elementType); |
| } |
| |
| /** |
| * Returns an empty observable set belonging to the given realm. The |
| * returned set continues to work after it has been disposed of and can be |
| * disposed of multiple times. |
| * |
| * @param realm |
| * the realm of the returned set |
| * @return an empty observable set. |
| */ |
| public static IObservableSet<Object> emptyObservableSet(Realm realm) { |
| return emptyObservableSet(realm, null); |
| } |
| |
| /** |
| * Returns an empty observable set of the given element type and belonging |
| * to the given realm. The returned set continues to work after it has been |
| * disposed of and can be disposed of multiple times. |
| * |
| * @param realm |
| * the realm of the returned set |
| * @param elementType |
| * the element type of the returned set |
| * @return an empty observable set |
| * @since 1.1 |
| */ |
| public static IObservableSet<Object> emptyObservableSet(Realm realm, |
| Object elementType) { |
| return new EmptyObservableSet<Object>(realm, elementType); |
| } |
| |
| /** |
| * Returns an observable set backed by the given set. |
| * |
| * @param <E> |
| * |
| * @param set |
| * the set to wrap in an IObservableSet |
| * @return an observable set backed by the given set |
| */ |
| public static <E> IObservableSet<E> staticObservableSet(Set<E> set) { |
| return staticObservableSet(Realm.getDefault(), set, Object.class); |
| } |
| |
| /** |
| * Returns an observable set of the given element type, backed by the given |
| * set. |
| * |
| * @param <E> |
| * |
| * @param set |
| * the set to wrap in an IObservableSet |
| * @param elementType |
| * the element type of the returned set |
| * @return Returns an observable set backed by the given unchanging set |
| * @since 1.1 |
| */ |
| public static <E> IObservableSet<E> staticObservableSet(Set<E> set, |
| Object elementType) { |
| return staticObservableSet(Realm.getDefault(), set, elementType); |
| } |
| |
| /** |
| * Returns an observable set belonging to the given realm, backed by the |
| * given set. |
| * |
| * @param <E> |
| * |
| * @param realm |
| * the realm of the returned set |
| * @param set |
| * the set to wrap in an IObservableSet |
| * @return an observable set backed by the given unchanging set |
| */ |
| public static <E> IObservableSet<E> staticObservableSet(Realm realm, |
| Set<E> set) { |
| return staticObservableSet(realm, set, Object.class); |
| } |
| |
| /** |
| * Returns an observable set of the given element type and belonging to the |
| * given realm, backed by the given set. |
| * |
| * @param <E> |
| * |
| * @param realm |
| * the realm of the returned set |
| * @param set |
| * the set to wrap in an IObservableSet |
| * @param elementType |
| * the element type of the returned set |
| * @return an observable set backed by the given set |
| * @since 1.1 |
| */ |
| public static <E> IObservableSet<E> staticObservableSet(Realm realm, |
| Set<E> set, Object elementType) { |
| return new ObservableSet<E>(realm, set, elementType) { |
| public synchronized void addChangeListener(IChangeListener listener) { |
| } |
| |
| public synchronized void addStaleListener(IStaleListener listener) { |
| } |
| |
| public synchronized void addSetChangeListener( |
| ISetChangeListener<? super E> listener) { |
| } |
| }; |
| } |
| |
| /** |
| * Returns an observable value that contains the same value as the given |
| * observable, and fires the same events as the given observable, but can be |
| * disposed of without disposing of the wrapped observable. |
| * |
| * @param <T> |
| * |
| * @param target |
| * the observable value to wrap |
| * @return a disposable proxy for the given observable value. |
| * @since 1.2 |
| */ |
| public static <T> IObservableValue<T> proxyObservableValue( |
| IObservableValue<T> target) { |
| return new DecoratingObservableValue<T>(target, false); |
| } |
| |
| /** |
| * Returns an observable set that contains the same elements as the given |
| * set, and fires the same events as the given set, but can be disposed of |
| * without disposing of the wrapped set. |
| * |
| * @param <E> |
| * |
| * @param target |
| * the set to wrap |
| * @return a disposable proxy for the given observable set |
| */ |
| public static <E> IObservableSet<E> proxyObservableSet( |
| IObservableSet<E> target) { |
| return new DecoratingObservableSet<E>(target, false); |
| } |
| |
| /** |
| * Returns an observable list that contains the same elements as the given |
| * list, and fires the same events as the given list, but can be disposed of |
| * without disposing of the wrapped list. |
| * |
| * @param <E> |
| * |
| * @param target |
| * the list to wrap |
| * @return a disposable proxy for the given observable list |
| * @since 1.1 |
| */ |
| public static <E> IObservableList<E> proxyObservableList( |
| IObservableList<E> target) { |
| return new DecoratingObservableList<E>(target, false); |
| } |
| |
| /** |
| * Returns an observable map that contains the same entries as the given |
| * map, and fires the same events as the given map, but can be disposed of |
| * without disposing of the wrapped map. |
| * |
| * @param <K> |
| * @param <V> |
| * |
| * @param target |
| * the map to wrap |
| * @return a disposable proxy for the given observable map |
| * @since 1.2 |
| */ |
| public static <K, V> IObservableMap<K, V> proxyObservableMap( |
| IObservableMap<K, V> target) { |
| return new DecoratingObservableMap<K, V>(target, false); |
| } |
| |
| /** |
| * Returns an observable list backed by the given list. |
| * |
| * @param <E> |
| * |
| * @param list |
| * the list to wrap in an IObservableList |
| * @return an observable list backed by the given unchanging list |
| */ |
| public static <E> IObservableList<E> staticObservableList(List<E> list) { |
| return staticObservableList(Realm.getDefault(), list, Object.class); |
| } |
| |
| /** |
| * Returns an observable list of the given element type, backed by the given |
| * list. |
| * |
| * @param <E> |
| * |
| * @param list |
| * the list to wrap in an IObservableList |
| * @param elementType |
| * the element type of the returned list |
| * @return an observable list backed by the given unchanging list |
| * @since 1.1 |
| */ |
| public static <E> IObservableList<E> staticObservableList(List<E> list, |
| Object elementType) { |
| return staticObservableList(Realm.getDefault(), list, elementType); |
| } |
| |
| /** |
| * Returns an observable list belonging to the given realm, backed by the |
| * given list. |
| * |
| * @param <E> |
| * |
| * @param realm |
| * the realm of the returned list |
| * @param list |
| * the list to wrap in an IObservableList |
| * @return an observable list backed by the given unchanging list |
| */ |
| public static <E> IObservableList<E> staticObservableList(Realm realm, |
| List<E> list) { |
| return staticObservableList(realm, list, Object.class); |
| } |
| |
| /** |
| * Returns an observable list of the given element type and belonging to the |
| * given realm, backed by the given list. |
| * |
| * @param <E> |
| * |
| * @param realm |
| * the realm of the returned list |
| * @param list |
| * the list to wrap in an IObservableList |
| * @param elementType |
| * the element type of the returned list |
| * @return an observable list backed by the given unchanging list |
| * @since 1.1 |
| */ |
| public static <E> IObservableList<E> staticObservableList(Realm realm, |
| List<E> list, Object elementType) { |
| return new ObservableList<E>(realm, list, elementType) { |
| public synchronized void addChangeListener(IChangeListener listener) { |
| } |
| |
| public synchronized void addStaleListener(IStaleListener listener) { |
| } |
| |
| public synchronized void addListChangeListener( |
| IListChangeListener<? super E> listener) { |
| } |
| }; |
| } |
| |
| /** |
| * Returns an observable value of type <code>Boolean.TYPE</code> which |
| * tracks whether the given observable is stale. |
| * |
| * @param observable |
| * the observable to track |
| * @return an observable value which tracks whether the given observable is |
| * stale |
| * |
| * @since 1.1 |
| */ |
| public static IObservableValue<Boolean> observeStale(IObservable observable) { |
| return new StalenessObservableValue(observable); |
| } |
| |
| /** |
| * Returns an observable value that tracks changes to the value of an |
| * observable map's entry specified by its key. |
| * <p> |
| * The state where the key does not exist in the map is equivalent to the |
| * state where the key exists and its value is <code>null</code>. The |
| * transition between these two states is not considered a value change and |
| * no event is fired. |
| * |
| * @param <K> |
| * |
| * @param <V> |
| * |
| * @param map |
| * the observable map whose entry will be tracked. |
| * @param key |
| * the key identifying the map entry to track. |
| * @return an observable value that tracks the value associated with the |
| * specified key in the given map |
| * @since 1.2 |
| */ |
| public static <K, V> IObservableValue<V> observeMapEntry( |
| IObservableMap<K, V> map, K key) { |
| return observeMapEntry(map, key, map.getValueType()); |
| } |
| |
| /** |
| * Returns an observable value that tracks changes to the value of an |
| * observable map's entry specified by its key. |
| * <p> |
| * The state where the key does not exist in the map is equivalent to the |
| * state where the key exists and its value is <code>null</code>. The |
| * transition between these two states is not considered a value change and |
| * no event is fired. |
| * |
| * @param <K> |
| * |
| * @param <V> |
| * |
| * @param map |
| * the observable map whose entry will be tracked. |
| * @param key |
| * the key identifying the map entry to track. |
| * @param valueType |
| * the type of the value. May be <code>null</code>, meaning the |
| * value is untyped. |
| * @return an observable value that tracks the value associated with the |
| * specified key in the given map |
| * @since 1.1 |
| */ |
| public static <K, V> IObservableValue<V> observeMapEntry( |
| IObservableMap<K, V> map, K key, Object valueType) { |
| if (valueType == null) |
| valueType = map.getValueType(); |
| return new MapEntryObservableValue<K, V>(map, key, valueType); |
| } |
| |
| /** |
| * Returns a factory for creating observable values tracking the value of |
| * the {@link IObservableMap observable map} entry identified by a |
| * particular key. |
| * |
| * @param <K> |
| * @param <V> |
| * |
| * @param map |
| * the observable map whose entry will be tracked. |
| * @param valueType |
| * the type of the value. May be <code>null</code>, meaning the |
| * value is untyped. |
| * @return a factory for creating observable values tracking the value of |
| * the observable map entry identified by a particular key object. |
| * @since 1.1 |
| */ |
| public static <K, V> IObservableFactory<K, IObservableValue<V>> mapEntryValueFactory( |
| final IObservableMap<K, V> map, final Object valueType) { |
| return new IObservableFactory<K, IObservableValue<V>>() { |
| public IObservableValue<V> createObservable(K key) { |
| return observeMapEntry(map, key, valueType); |
| } |
| }; |
| } |
| |
| /** |
| * Helper method for <code>MasterDetailObservables.detailValue(master, |
| * mapEntryValueFactory(map, valueType), valueType)</code>. |
| * |
| * @param <K> |
| * @param <V> |
| * |
| * @param map |
| * the observable map whose entry will be tracked. |
| * @param master |
| * the observable value that identifies which map entry to track. |
| * @param valueType |
| * the type of the value. May be <code>null</code>, meaning the |
| * value is untyped. |
| * @return an observable value tracking the current value of the specified |
| * key in the given map an observable value that tracks the current |
| * value of the named property for the current value of the master |
| * observable value |
| * @since 1.1 |
| */ |
| public static <K, V> IObservableValue<V> observeDetailMapEntry( |
| IObservableMap<K, V> map, IObservableValue<K> master, |
| Object valueType) { |
| return MasterDetailObservables.detailValue(master, |
| mapEntryValueFactory(map, valueType), valueType); |
| } |
| |
| /** |
| * Copies the current value of the source observable to the destination |
| * observable, and upon value change events fired by the source observable, |
| * updates the destination observable accordingly, until the source |
| * observable is disposed. This method assumes that both observables are on |
| * the same realm. |
| * |
| * @param <T> |
| * |
| * @param source |
| * the source observable |
| * @param destination |
| * the destination observable |
| * @since 1.2 |
| */ |
| public static <T> void pipe(IObservableValue<T> source, |
| final IObservableValue<? super T> destination) { |
| destination.setValue(source.getValue()); |
| source.addValueChangeListener(new IValueChangeListener<T>() { |
| public void handleValueChange(ValueChangeEvent<T> event) { |
| destination.setValue(event.diff.getNewValue()); |
| } |
| }); |
| } |
| } |