blob: 63f26d3ca571a57bd2f6f534ef69fc36285e3cd3 [file] [log] [blame]
/*******************************************************************************
* 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
*/
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
* @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<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());
}
});
}
}