Bug 412499 - inital commit of new Bind methods

Change-Id: I2ac95fa8333e0b7c80ff9125537c9ac8b4a4c89a
diff --git a/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
index f2fdfc8..94416b0 100644
--- a/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.core.databinding/META-INF/MANIFEST.MF
@@ -11,6 +11,7 @@
  org.eclipse.core.databinding.validation;x-internal:=false,
  org.eclipse.core.internal.databinding;x-friends:="org.eclipse.core.databinding.beans",
  org.eclipse.core.internal.databinding.conversion;x-friends:="org.eclipse.jface.tests.databinding",
+ org.eclipse.core.internal.databinding.provisional.bind;x-internal:=true,
  org.eclipse.core.internal.databinding.validation;x-friends:="org.eclipse.jface.tests.databinding"
 Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.2.0,4.0.0)",
  org.eclipse.core.databinding.observable;bundle-version="[1.3.0,2.0.0)";visibility:=reexport,
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/Bind.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/Bind.java
new file mode 100644
index 0000000..0c5ed90
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/Bind.java
@@ -0,0 +1,256 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * @since 1.5
+ * @provisional This class has been added as part of a work in progress. It is
+ *              not guaranteed to work or remain the same in future releases.
+ *              For more information contact e4-dev@eclipse.org.
+ */
+public class Bind {
+
+	/**
+	 * This is the ITwoWayBinding that sits immediately on top on the model
+	 * observable.
+	 * 
+	 * @param <V>
+	 */
+	static class TwoWayModelBinding<V> extends TwoWayBinding<V> {
+		private final IObservableValue<V> modelObservable;
+
+		private final boolean takeOwnership;
+
+		private boolean isModelChanging = false;
+
+		IValueChangeListener<V> modelListener = new IValueChangeListener<V>() {
+			public void handleValueChange(ValueChangeEvent<V> event) {
+				if (!isModelChanging) {
+					targetBinding.setTargetValue(event.diff.getNewValue());
+				}
+			}
+		};
+
+		/**
+		 * 
+		 * @param modelObservable
+		 * @param takeOwnership
+		 *            <code>true</code> if this class is to take ownership of
+		 *            the given model observable, which means it has the
+		 *            responsibility of disposing of it when this binding is
+		 *            disposed, <code>false</code> if the given model observable
+		 *            must not be disposed of by this class
+		 */
+		public TwoWayModelBinding(IObservableValue<V> modelObservable,
+				boolean takeOwnership) {
+			super(true);
+			this.modelObservable = modelObservable;
+			this.takeOwnership = takeOwnership;
+
+			modelObservable.addValueChangeListener(modelListener);
+		}
+
+		public V getModelValue() {
+			return modelObservable.getValue();
+		}
+
+		public void setModelValue(V newValue) {
+			isModelChanging = true;
+			try {
+				modelObservable.setValue(newValue);
+			} finally {
+				isModelChanging = false;
+			}
+		}
+
+		public void removeModelListener() {
+			modelObservable.removeValueChangeListener(modelListener);
+
+			/*
+			 * If we 'own' the observable then we must dispose of it, if we
+			 * don't 'own' the observable then we must not dispose of it.
+			 */
+			if (takeOwnership) {
+				modelObservable.dispose();
+			}
+		}
+	}
+
+	/**
+	 * This is the IOneWayBinding that sits immediately on top on the model
+	 * observable.
+	 * 
+	 * @param <V>
+	 */
+	static class OneWayModelBinding<V> extends OneWayBinding<V> {
+		private final IObservableValue<V> modelObservable;
+
+		private final boolean takeOwnership;
+
+		IValueChangeListener<V> modelListener = new IValueChangeListener<V>() {
+			public void handleValueChange(ValueChangeEvent<V> event) {
+				targetBinding.setTargetValue(event.diff.getNewValue());
+			}
+		};
+
+		/**
+		 * 
+		 * @param modelObservable
+		 * @param takeOwnership
+		 *            <code>true</code> if this class is to take ownership of
+		 *            the given model observable, which means it has the
+		 *            responsibility of disposing of it when this binding is
+		 *            disposed, <code>false</code> if the given model observable
+		 *            must not be disposed of by this class
+		 */
+		public OneWayModelBinding(IObservableValue<V> modelObservable,
+				boolean takeOwnership) {
+			this.modelObservable = modelObservable;
+			this.takeOwnership = takeOwnership;
+
+			modelObservable.addValueChangeListener(modelListener);
+		}
+
+		public V getModelValue() {
+			return modelObservable.getValue();
+		}
+
+		public void removeModelListener() {
+			modelObservable.removeValueChangeListener(modelListener);
+
+			/*
+			 * If we 'own' the observable then we must dispose of it, if we
+			 * don't 'own' the observable then we must not dispose of it.
+			 */
+			if (takeOwnership) {
+				modelObservable.dispose();
+			}
+		}
+	}
+
+	/**
+	 * Initiates two-way binding.
+	 * <P>
+	 * The model observable is not disposed when this binding is disposed.
+	 * 
+	 * @param modelObservable
+	 * @return an object that can chain one-way bindings
+	 */
+	public static <V> IOneWayBinding<V> oneWay(
+			IObservableValue<V> modelObservable) {
+		return new OneWayModelBinding<V>(modelObservable, false);
+	}
+
+	/**
+	 * This is a convenience method that creates an observable for the model
+	 * from the given property and source. The observable will be disposed when
+	 * the binding is disposed.
+	 * 
+	 * @param modelProperty
+	 * @param source
+	 * @return an object that can chain one-way bindings
+	 */
+	public static <S, V> IOneWayBinding<V> oneWay(
+			IValueProperty<S, V> modelProperty, S source) {
+		IObservableValue<V> modelObservable = modelProperty.observe(source);
+		return new OneWayModelBinding<V>(modelObservable, true);
+	}
+
+	/**
+	 * Initiates two-way binding.
+	 * <P>
+	 * The model observable is not disposed when this binding is disposed.
+	 * 
+	 * @param modelObservable
+	 * @return an object that can chain two-way bindings
+	 */
+	public static <V> ITwoWayBinding<V> twoWay(
+			IObservableValue<V> modelObservable) {
+		return new TwoWayModelBinding<V>(modelObservable, false);
+	}
+
+	/**
+	 * This is a convenience method that creates an observable for the model
+	 * from the given property and source. The observable will be disposed when
+	 * the binding is disposed.
+	 * 
+	 * @param modelProperty
+	 * @param source
+	 * @return an object that can chain two-way bindings
+	 */
+	public static <S, V> ITwoWayBinding<V> twoWay(
+			IValueProperty<S, V> modelProperty, S source) {
+		IObservableValue<V> modelObservable = modelProperty.observe(source);
+		return new TwoWayModelBinding<V>(modelObservable, true);
+	}
+
+	/**
+	 * This method is used to 'bounce back' a value from the target.
+	 * Specifically this means whenever the target value changes, the value is
+	 * converted using the given converter (targetToModel). The resulting value
+	 * is the converted back using the same converter (modelToTarget).
+	 * <P>
+	 * A use case for this method is as follows. You have a number that is
+	 * stored in the model as an Integer. You want to display the number in a
+	 * text box with separators, so 1234567 would be displayed as 1,234,567 or
+	 * 1.234.567 depending on your regional settings. You want to allow more
+	 * flexibility on what the user can enter. For example you may want to allow
+	 * the user to miss out the separators. As the user types, the value is
+	 * updated in the model. When the control loses focus you want the
+	 * separators to be inserted in the Text control in the proper positions.
+	 * <P>
+	 * To do this you create two bindings. One is a two-way binding that
+	 * observes the Text control with SWT.Modify. The other is a 'bounce back'
+	 * that observes the control with SWT.FocusOut. It might be coded as
+	 * follows:
+	 * <P>
+	 * <code>
+	 * 		Bind.bounceBack(myIntegerToTextConverter)
+		.to(SWTObservables.observeText(textControl, SWT.FocusOut));
+	 * </code>
+	 * 
+	 * @param converter
+	 * @return an object that can chain two-way bindings
+	 */
+	public static <T1, T2> ITwoWayBinding<T2> bounceBack(
+			final IBidiConverter<T1, T2> converter) {
+		return new TwoWayBinding<T2>(false) {
+
+			public T2 getModelValue() {
+				/*
+				 * This method should never be called because pullInitialValue
+				 * is set to false.
+				 */
+				throw new UnsupportedOperationException();
+			}
+
+			public void setModelValue(T2 valueFromTarget) {
+				try {
+					T1 modelSideValue = converter
+							.targetToModel(valueFromTarget);
+					T2 valueBackToTarget = converter
+							.modelToTarget(modelSideValue);
+					this.targetBinding.setTargetValue(valueBackToTarget);
+				} catch (CoreException e) {
+					/*
+					 * No bounce-back occurs if the value from the target side
+					 * cannot be converted. We do nothing because the user will
+					 * typically have an error indicator anyway.
+					 */
+				}
+			}
+
+			public void removeModelListener() {
+				/*
+				 * Nothing to do here because nothing originates from the model
+				 * side.
+				 */
+			}
+		};
+	}
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/DefaultValueBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/DefaultValueBinding.java
new file mode 100644
index 0000000..d770daa
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/DefaultValueBinding.java
@@ -0,0 +1,76 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T1>
+ */
+public class DefaultValueBinding<T1> extends TwoWayBinding<T1> implements
+		ITargetBinding<T1> {
+
+	private final IOneWayModelBinding<T1> modelBinding;
+
+	private boolean stopped = false;
+
+	/**
+	 * @param modelBinding
+	 */
+	public DefaultValueBinding(IOneWayModelBinding<T1> modelBinding) {
+		super(true);
+		this.modelBinding = modelBinding;
+	}
+
+	public T1 getModelValue() {
+		return modelBinding.getModelValue();
+	}
+
+	public void setTargetValue(T1 valueOnModelSide) {
+		targetBinding.setTargetValue(valueOnModelSide);
+	}
+
+	public void setStatus(IStatus status) {
+		/*
+		 * Generally there are no status values sent from the model to the
+		 * target because the model is generally valid. However there may be
+		 * cases when error or warning statuses come from the model. For example
+		 * when using JSR-303 validations the validation is done on the value in
+		 * the model object. In any case we just pass it on.
+		 */
+		targetBinding.setStatus(status);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see
+	 * org.eclipse.core.databinding.bind.IModelBinding#setModelValue(java.lang
+	 * .Object)
+	 */
+	public void setModelValue(T1 newValue) {
+		/*
+		 * The target has changed so stop this binding. The target will continue
+		 * to notify us of changes for as long as it exists so we need to set a
+		 * flag to indicate that this binding is in a stopped state.
+		 */
+		if (!stopped) {
+			stopped = true;
+			modelBinding.removeModelListener();
+		}
+	}
+
+	public void removeModelListener() {
+		/*
+		 * Pass the request back to the next link in the binding chain so
+		 * eventually the request gets back to the model observable.
+		 * 
+		 * Note that if we are in a 'stopped' state then the listener has
+		 * already been removed from the model and we should not attempt to
+		 * remove it again.
+		 */
+		if (!stopped) {
+			modelBinding.removeModelListener();
+		}
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IBidiConverter.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IBidiConverter.java
new file mode 100644
index 0000000..3024ac3
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IBidiConverter.java
@@ -0,0 +1,55 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Interface for bi-directional conversions.
+ * <P>
+ * Bi-directional conversions encapsulate both the target-to-model and the
+ * model-to-target conversions in a single converter. This simplifies the API
+ * for two-way binding as only one converter need be specified. It also helps to
+ * ensure consistency in the conversions.
+ * <P>
+ * The two conversions must be consistent with each other. More specifically,
+ * for any value of type T1, call modelToTarget to get a type T2, pass that
+ * value to targetToModel to get a type T1, pass that value to modelToTarget to
+ * get a type T2. The value returned by the second call to modelToTarget must be
+ * 'equal' to the value returned by the first call to modelToTarget ('equal'
+ * meaning the 'equal' method returns 'true'). Likewise, for any value of type
+ * T2, call targetToModel to get a type T1, pass that value to modelToTarget to
+ * get a type T2, pass that value to targetToModel to get a type T1. The value
+ * returned by the second call to targetToModel must be 'equal' to the value
+ * returned by the first call to targetToModel.
+ * <P>
+ * Note that the above rules allow the conversion to start with non-canonical
+ * forms but the first conversion must always result in a canonical form
+ * (meaning if the value is conceptually the same then it must be 'equal'. For
+ * example, "12,345.300" may be allowed as the target, this being converted to a
+ * BigDecimal with a value of 12345.30 in the model, being converted back to
+ * "12345.30" in the target. This does not 'equal' the original target value but
+ * converting this back again to the model data type must result in a BigDecimal
+ * that 'equals' the BigDecimal obtained from the first conversion.
+ * 
+ * @since 1.5
+ * @param <T1>
+ *            the data type on the model side
+ * @param <T2>
+ *            the data type on the target side
+ */
+public interface IBidiConverter<T1, T2> {
+
+	/**
+	 * @param fromObject
+	 * @return the value converted for use on the target side
+	 */
+	T2 modelToTarget(T1 fromObject);
+
+	/**
+	 * @param fromObject
+	 * @return the value converted for use on the model side
+	 * @throws CoreException
+	 *             if the value cannot be converted
+	 */
+	T1 targetToModel(T2 fromObject) throws CoreException;
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IModelBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IModelBinding.java
new file mode 100644
index 0000000..77e3da4
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IModelBinding.java
@@ -0,0 +1,28 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+/**
+ * This interface is used internally by this package to manage two-way binding
+ * to the model observable.
+ * 
+ * @since 1.5
+ * @noimplement
+ * @param <T>
+ */
+interface IModelBinding<T> {
+
+	/**
+	 * @return the model from the model side
+	 */
+	T getModelValue();
+
+	/**
+	 * @param newValue
+	 */
+	void setModelValue(T newValue);
+
+	/**
+	 * Removes the listener from the model. This method is called when the
+	 * target is disposed.
+	 */
+	void removeModelListener();
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayBinding.java
new file mode 100644
index 0000000..0018604
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayBinding.java
@@ -0,0 +1,92 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * This interface is used to chain together one-way binding.
+ * <P>
+ * To create a one-way binding, first create an implementation of this interface
+ * from an observable on the model value, e.g.: <code>
+ * Bind.oneWay(modelObservable)
+ * </code> then call methods on this interface to perform conversions or other
+ * supported operations. Each of these methods returns an implementation of this
+ * interface so these operations can be chained together. Finally call the
+ * <code>to</code> method to bind to the target observable.
+ * 
+ * @since 1.5
+ * @param <T1>
+ */
+public interface IOneWayBinding<T1> {
+
+	/**
+	 * @param converter
+	 * @return the value converted to the type expected by the next part of the
+	 *         binding chain
+	 */
+	<T2> IOneWayBinding<T2> convert(IConverter<T1, T2> converter);
+
+	/**
+	 * This method is similar to <code>convert</code>. However if any
+	 * observables are read during the conversion then listeners are added to
+	 * these observables and the conversion is done again.
+	 * <P>
+	 * The conversion is always repeated keeping the same value of the model. It
+	 * is assumed that the tracked observables affect the target. For example
+	 * suppose a time widget contains a time which is bound to a Date property
+	 * in the model. The time zone to use is a preference and an observable
+	 * exists for the time zone (which would implement
+	 * IObservableValue<TimeZone>). If the user changes the time zone in the
+	 * preferences then the text in the time widget will change to show the same
+	 * time but in a different time zone. The time in the model will not change
+	 * when the time zone is changed. If the user edits the time in the time
+	 * widget then that time will be interpreted using the new time zone and
+	 * converted to a Date object for the model.
+	 * 
+	 * @param converter
+	 * @return an object that can chain one-way bindings
+	 */
+	<T2> IOneWayBinding<T2> convertWithTracking(IConverter<T1, T2> converter);
+
+	/**
+	 * When chaining together one-way binding operations, this method must be
+	 * last. It binds to the target.
+	 * <P>
+	 * The target observable is not disposed when this binding is disposed.
+	 * 
+	 * @param targetObservable
+	 */
+	void to(IObservableValue<T1> targetObservable);
+
+	/**
+	 * When chaining together one-way binding operations, this method must be
+	 * last. It binds to the target.
+	 * 
+	 * This is a convenience method that creates an observable for the target
+	 * from the given property and source. The observable will be disposed when
+	 * the binding is disposed.
+	 * 
+	 * @param targetProperty
+	 * @param source
+	 */
+	<S> void to(IValueProperty<S, T1> targetProperty, S source);
+
+	/**
+	 * This method is used to create a one-way binding from the model to the
+	 * target but the binding stops if something else changes the target.
+	 * <P>
+	 * A use case is when a default value is provided in a UI control. For
+	 * example suppose you have two fields, 'amount' and 'sales tax'. When the
+	 * user enters an amount, the requirement is that the 'sales tax' field is
+	 * completed based on the amount using a given tax rate. If the user edits
+	 * the amount, the sales tax amount changes accordingly. However once the
+	 * user edits the sales tax field then changes to the amount field no longer
+	 * affect the sales tax field.
+	 * 
+	 * @return an object that can chain two-way bindings (although this is a
+	 *         one-way binding, the binding onwards to the target must be
+	 *         two-way so that we know when the user changes the target)
+	 */
+	ITwoWayBinding<T1> untilTargetChanges();
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayModelBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayModelBinding.java
new file mode 100644
index 0000000..31fc800
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/IOneWayModelBinding.java
@@ -0,0 +1,23 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+/**
+ * This interface is used internally by this package to manage one-way binding
+ * from the model observable.
+ * 
+ * @since 1.5
+ * @noimplement
+ * @param <T>
+ */
+interface IOneWayModelBinding<T> {
+
+	/**
+	 * @return the value from the model side
+	 */
+	T getModelValue();
+
+	/**
+	 * Removes the listener from the model. This method is called when the
+	 * target is disposed.
+	 */
+	void removeModelListener();
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITargetBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITargetBinding.java
new file mode 100644
index 0000000..b8c4bf9
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITargetBinding.java
@@ -0,0 +1,22 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.5
+ * 
+ * @param <F>
+ */
+public interface ITargetBinding<F> {
+	/**
+	 * @param targetValue
+	 */
+	void setTargetValue(F targetValue);
+
+	/**
+	 * Push the error status back to the target
+	 * 
+	 * @param status
+	 */
+	void setStatus(IStatus status);
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITwoWayBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITwoWayBinding.java
new file mode 100644
index 0000000..a6e1245
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/ITwoWayBinding.java
@@ -0,0 +1,73 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+
+/**
+ * This interface is used to chain together two-way binding.
+ * <P>
+ * To create a two-way binding, first create an implementation of this interface
+ * from an observable on the model value, e.g.: <code>
+ * Bind.twoWay(modelObservable)
+ * </code> then call methods on this interface to perform conversions or other
+ * supported operations. Each of these methods returns an implementation of this
+ * interface so these operations can be chained together. Finally call the
+ * <code>to</code> method to bind to the target observable.
+ * 
+ * @since 1.5
+ * @param <T1>
+ */
+public interface ITwoWayBinding<T1> {
+
+	/**
+	 * @param converter
+	 * @return an object that can chain two-way bindings
+	 */
+	<T2> ITwoWayBinding<T2> convert(final IBidiConverter<T1, T2> converter);
+
+	/**
+	 * This method is similar to <code>convert</code>. However if any
+	 * observables are read during the conversion then listeners are added to
+	 * these observables and the conversion is done again.
+	 * <P>
+	 * The conversion is always repeated keeping the same value of the model. It
+	 * is assumed that the tracked observables affect the target. For example
+	 * suppose a time widget contains a time which is bound to a Date property
+	 * in the model. The time zone to use is a preference and an observable
+	 * exists for the time zone (which would implement
+	 * IObservableValue<TimeZone>). If the user changes the time zone in the
+	 * preferences then the text in the time widget will change to show the same
+	 * time but in a different time zone. The time in the model will not change
+	 * when the time zone is changed. If the user edits the time in the time
+	 * widget then that time will be interpreted using the new time zone and
+	 * converted to a Date object for the model.
+	 * 
+	 * @param converter
+	 * @return an object that can chain two-way bindings
+	 */
+	<T2> ITwoWayBinding<T2> convertWithTracking(IBidiConverter<T1, T2> converter);
+
+	/**
+	 * When chaining together two-way binding operations, this method must be
+	 * last. It binds to the target observable.
+	 * <P>
+	 * The target observable is not disposed when this binding is disposed.
+	 * 
+	 * @param targetObservable
+	 */
+	void to(IObservableValue<T1> targetObservable);
+
+	/**
+	 * When chaining together two-way binding operations, this method must be
+	 * last. It binds to the target.
+	 * 
+	 * This is a convenience method that creates an observable for the target
+	 * from the given property and source. The observable will be disposed when
+	 * the binding is disposed.
+	 * 
+	 * @param targetProperty
+	 * @param source
+	 */
+	<S> void to(IValueProperty<S, T1> targetProperty, S source);
+
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayBinding.java
new file mode 100644
index 0000000..c8538dd
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayBinding.java
@@ -0,0 +1,157 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T2>
+ */
+public abstract class OneWayBinding<T2> implements IOneWayBinding<T2>,
+		IOneWayModelBinding<T2> {
+
+	protected ITargetBinding<T2> targetBinding;
+
+	public <T3> IOneWayBinding<T3> convert(final IConverter<T2, T3> converter) {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		OneWayConversionBinding<T3, T2> nextBinding = new OneWayConversionBinding<T3, T2>(
+				this, converter);
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	/**
+	 * This method is similar to <code>convert</code>. However if any
+	 * observables are read during the conversion then listeners are added to
+	 * these observables and the conversion is done again.
+	 * <P>
+	 * The conversion is always repeated keeping the same value of the model. It
+	 * is assumed that the tracked observables affect the target. For example
+	 * suppose a time widget contains a time which is bound to a Date property
+	 * in the model. The time zone to use is a preference and an observable
+	 * exists for the time zone (which would implement
+	 * IObservableValue<TimeZone>). If the user changes the time zone in the
+	 * preferences then the text in the time widget will change to show the same
+	 * time but in a different time zone. The time in the model will not change
+	 * when the time zone is changed. If the user edits the time in the time
+	 * widget then that time will be interpreted using the new time zone and
+	 * converted to a Date object for the model.
+	 * 
+	 * @param converter
+	 * @return an object that can chain one-way bindings
+	 */
+	public <T3> IOneWayBinding<T3> convertWithTracking(
+			final IConverter<T2, T3> converter) {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		OneWayConversionBinding<T3, T2> nextBinding = new OneWayConversionBinding<T3, T2>(
+				this, converter);
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	/**
+	 * This method creates a one-way binding from model to target that stops if
+	 * someone else edits the target.
+	 * <P>
+	 * This method is used to provide a default value. The default value is a
+	 * one-way binding, typically a one-way binding from a ComputedValue. This
+	 * default value is the receiver of this method. The target may be either an
+	 * observable on the model or an observable on a UI control. (If you have
+	 * two-way binding between the model and the UI control then you can bind a
+	 * default value to either but you will typically have fewer conversions to
+	 * do if you bind to the model).
+	 * <P>
+	 * The binding on to the target is a two-way binding. The default value is
+	 * passed on to the target until such time that the target is changed by
+	 * someone other than ourselves. At that point the binding stops and changes
+	 * in the default value are no longer set into the target.
+	 * <P>
+	 * An example use is as follows: There is a field in which the user can
+	 * enter the price of an item. There is another field in which the user can
+	 * enter the sales tax for the sale of the item. We want the sales tax field
+	 * to automatically calculate to be 5% of the price. If the user edits the
+	 * price then the sales tax field should change too. The user may edit the
+	 * sales tax field. If the user does this then the sales tax field will no
+	 * longer be updated as the price changes.
+	 */
+	public ITwoWayBinding<T2> untilTargetChanges() {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		DefaultValueBinding<T2> nextBinding = new DefaultValueBinding<T2>(this);
+
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	public void to(final IObservableValue<T2> targetObservable) {
+		/*
+		 * We have finally made it to the target observable.
+		 * 
+		 * Initially set the target observable to the current value from the
+		 * model.
+		 */
+		targetObservable.setValue(getModelValue());
+
+		/*
+		 * The target binding contains a method that is called whenever a new
+		 * value comes from the model side. We simply set a target binding that
+		 * sets that value into the target observable.
+		 */
+		targetBinding = new ITargetBinding<T2>() {
+			public void setTargetValue(T2 targetValue) {
+				targetObservable.setValue(targetValue);
+			}
+
+			public void setStatus(IStatus status) {
+				/*
+				 * Generally there are no status values sent from the model to
+				 * the target because the model is generally valid. However
+				 * there may be cases when error or warning statuses come from
+				 * the model. For example when using JSR-303 validations the
+				 * validation is done on the value in the model object. In any
+				 * case, there is no status observable provided by the user so
+				 * we drop it.
+				 */
+			}
+		};
+
+		/*
+		 * If the target is disposed, be sure to remove the listener from the
+		 * model.
+		 */
+		targetObservable.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent event) {
+				removeModelListener();
+			}
+		});
+	}
+
+	public <S> void to(IValueProperty<S, T2> targetProperty, S source) {
+		IObservableValue<T2> targetObservable = targetProperty.observe(source);
+		to(targetObservable);
+		// TODO dispose observable if binding is disposed
+	}
+
+	/**
+	 * @param status
+	 */
+	public void setStatus(IStatus status) {
+		targetBinding.setStatus(status);
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayConversionBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayConversionBinding.java
new file mode 100644
index 0000000..dbb8511
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/OneWayConversionBinding.java
@@ -0,0 +1,43 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T2>
+ * @param <T1>
+ */
+public class OneWayConversionBinding<T2, T1> extends OneWayBinding<T2>
+		implements ITargetBinding<T1> {
+	private final IOneWayModelBinding<T1> modelBinding;
+	private final IConverter<T1, T2> converter;
+
+	/**
+	 * @param modelBinding
+	 * @param converter
+	 */
+	public OneWayConversionBinding(IOneWayModelBinding<T1> modelBinding,
+			IConverter<T1, T2> converter) {
+		this.modelBinding = modelBinding;
+		this.converter = converter;
+	}
+
+	public T2 getModelValue() {
+		T1 modelValue = modelBinding.getModelValue();
+		return converter.convert(modelValue);
+	}
+
+	public void setTargetValue(T1 valueOnModelSide) {
+		T2 valueOnTargetSide = converter.convert(valueOnModelSide);
+		targetBinding.setTargetValue(valueOnTargetSide);
+	}
+
+	public void removeModelListener() {
+		/*
+		 * Pass the request back to the next link in the binding chain so
+		 * eventually the request gets back to the model observable.
+		 */
+		modelBinding.removeModelListener();
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayBinding.java
new file mode 100644
index 0000000..4577664
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayBinding.java
@@ -0,0 +1,177 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.observable.DisposeEvent;
+import org.eclipse.core.databinding.observable.IDisposeListener;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.property.value.IValueProperty;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T2>
+ */
+public abstract class TwoWayBinding<T2> implements ITwoWayBinding<T2>,
+		IModelBinding<T2> {
+
+	/**
+	 * <code>true</code> if the target observable bound by the <code>to</code>
+	 * method is to be initially set to the value from the model side,
+	 * <code>false</code> if its value is to be set only when changes are pushed
+	 * from the target side
+	 */
+	protected boolean pullInitialValue;
+
+	protected ITargetBinding<T2> targetBinding;
+
+	/**
+	 * @param pullInitialValue
+	 *            <code>true</code> if the initial value from the model is to be
+	 *            set into the target, <code>false</code> if the target must not
+	 *            be set until the model value changes
+	 */
+	public TwoWayBinding(boolean pullInitialValue) {
+		this.pullInitialValue = pullInitialValue;
+	}
+
+	public <T3> ITwoWayBinding<T3> convert(
+			final IBidiConverter<T2, T3> converter) {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		TwoWayConversionBinding<T3, T2> nextBinding = new TwoWayConversionBinding<T3, T2>(
+				this, converter, pullInitialValue);
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	/**
+	 * This method is similar to <code>convert</code>. However if any
+	 * observables are read during the conversion then listeners are added to
+	 * these observables and the conversion is done again.
+	 * <P>
+	 * The conversion is always repeated keeping the same value of the model. It
+	 * is assumed that the tracked observables affect the target. For example
+	 * suppose a time widget contains a time which is bound to a Date property
+	 * in the model. The time zone to use is a preference and an observable
+	 * exists for the time zone (which would implement
+	 * IObservableValue<TimeZone>). If the user changes the time zone in the
+	 * preferences then the text in the time widget will change to show the same
+	 * time but in a different time zone. The time in the model will not change
+	 * when the time zone is changed. If the user edits the time in the time
+	 * widget then that time will be interpreted using the new time zone and
+	 * converted to a Date object for the model.
+	 * 
+	 * @param converter
+	 * @return an object that can chain two-way bindings
+	 */
+	public <T3> ITwoWayBinding<T3> convertWithTracking(
+			final IBidiConverter<T2, T3> converter) {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		TwoWayConversionBinding<T3, T2> nextBinding = new TwoWayConversionBinding<T3, T2>(
+				this, converter, pullInitialValue);
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	/**
+	 * @param validator
+	 * @return an object that can chain two-way bindings
+	 */
+	public ITwoWayBinding<T2> validate(final IValidator<T2> validator) {
+		if (targetBinding != null) {
+			throw new RuntimeException(
+					"When chaining together a binding, you cannot chain more than one target."); //$NON-NLS-1$
+		}
+
+		TwoWayValidationBinding<T2> nextBinding = new TwoWayValidationBinding<T2>(
+				this, validator, pullInitialValue);
+		targetBinding = nextBinding;
+		return nextBinding;
+	}
+
+	public void to(final IObservableValue<T2> targetObservable) {
+		to(targetObservable, null);
+	}
+
+	public <S> void to(IValueProperty<S, T2> targetProperty, S source) {
+		IObservableValue<T2> targetObservable = targetProperty.observe(source);
+		to(targetObservable);
+		// TODO dispose observable if binding is disposed
+	}
+
+	/**
+	 * We have finally made it to the target observable.
+	 * 
+	 * Initially set the target observable to the current value from the model
+	 * (if the pullInitialValue flag is set which it will be in most cases).
+	 * 
+	 * @param targetObservable
+	 * @param statusObservable
+	 */
+	public void to(final IObservableValue<T2> targetObservable,
+			final IObservableValue<IStatus> statusObservable) {
+		if (pullInitialValue) {
+			targetObservable.setValue(getModelValue());
+		}
+
+		final boolean[] isChanging = new boolean[] { false };
+
+		/*
+		 * The target binding contains a method that is called whenever a new
+		 * value comes from the model side. We simply set a target binding that
+		 * sets that value into the target observable.
+		 */
+		targetBinding = new ITargetBinding<T2>() {
+			public void setTargetValue(T2 targetValue) {
+				try {
+					isChanging[0] = true;
+					targetObservable.setValue(targetValue);
+				} finally {
+					isChanging[0] = false;
+				}
+			}
+
+			public void setStatus(IStatus status) {
+				/*
+				 * If there is a target for the status, set it. Otherwise drop
+				 * it.
+				 */
+				if (statusObservable != null) {
+					statusObservable.setValue(status);
+				}
+			}
+		};
+
+		/*
+		 * Listen for changes originating from the target observable, and send
+		 * those back through to the model side.
+		 */
+		targetObservable.addValueChangeListener(new IValueChangeListener<T2>() {
+			public void handleValueChange(ValueChangeEvent<T2> event) {
+				if (!isChanging[0]) {
+					setModelValue(event.diff.getNewValue());
+				}
+			}
+		});
+
+		/*
+		 * If the target is disposed, be sure to remove the listener from the
+		 * model.
+		 */
+		targetObservable.addDisposeListener(new IDisposeListener() {
+			public void handleDispose(DisposeEvent event) {
+				removeModelListener();
+			}
+		});
+	}
+}
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayConversionBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayConversionBinding.java
new file mode 100644
index 0000000..2ac9ffe
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayConversionBinding.java
@@ -0,0 +1,71 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T2>
+ * @param <T1>
+ */
+public class TwoWayConversionBinding<T2, T1> extends TwoWayBinding<T2>
+		implements ITargetBinding<T1> {
+	private final IModelBinding<T1> modelBinding;
+	private final IBidiConverter<T1, T2> converter;
+
+	/**
+	 * @param modelBinding
+	 * @param converter
+	 * @param pullInitialValue
+	 */
+	public TwoWayConversionBinding(IModelBinding<T1> modelBinding,
+			IBidiConverter<T1, T2> converter, boolean pullInitialValue) {
+		super(pullInitialValue);
+		this.modelBinding = modelBinding;
+		this.converter = converter;
+	}
+
+	/**
+	 * @return the value from the model, converted for use on the target side
+	 */
+	public T2 getModelValue() {
+		T1 modelValue = modelBinding.getModelValue();
+		return converter.modelToTarget(modelValue);
+	}
+
+	/**
+	 * @param valueOnTargetSide
+	 */
+	public void setModelValue(T2 valueOnTargetSide) {
+		try {
+			T1 valueOnModelSide = converter.targetToModel(valueOnTargetSide);
+			modelBinding.setModelValue(valueOnModelSide);
+			targetBinding.setStatus(Status.OK_STATUS);
+		} catch (CoreException e) {
+			targetBinding.setStatus(e.getStatus());
+		}
+	}
+
+	public void setTargetValue(T1 valueOnModelSide) {
+		T2 valueOnTargetSide = converter.modelToTarget(valueOnModelSide);
+		targetBinding.setTargetValue(valueOnTargetSide);
+	}
+
+	public void setStatus(IStatus status) {
+		/*
+		 * The error occurred somewhere on the model side. We push this error
+		 * back to the target.
+		 */
+		targetBinding.setStatus(status);
+	}
+
+	public void removeModelListener() {
+		/*
+		 * Pass the request back to the next link in the binding chain so
+		 * eventually the request gets back to the model observable.
+		 */
+		modelBinding.removeModelListener();
+	}
+}
\ No newline at end of file
diff --git a/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayValidationBinding.java b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayValidationBinding.java
new file mode 100644
index 0000000..fef7589
--- /dev/null
+++ b/bundles/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/provisional/bind/TwoWayValidationBinding.java
@@ -0,0 +1,80 @@
+package org.eclipse.core.internal.databinding.provisional.bind;
+
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * @since 1.5
+ * 
+ * @param <T>
+ */
+public class TwoWayValidationBinding<T> extends TwoWayBinding<T> implements
+		ITargetBinding<T> {
+	private final IModelBinding<T> modelBinding;
+	private final IValidator<T> validator;
+
+	/**
+	 * @param modelBinding
+	 * @param validator
+	 * @param pullInitialValue
+	 */
+	public TwoWayValidationBinding(IModelBinding<T> modelBinding,
+			IValidator<T> validator, boolean pullInitialValue) {
+		super(pullInitialValue);
+		this.modelBinding = modelBinding;
+		this.validator = validator;
+	}
+
+	/**
+	 * The default behavior is to validate only when going from target to model.
+	 * The error status observable is assumed to be there to show user errors.
+	 * Therefore no validation is done in this method.
+	 * 
+	 * @return the value from the model
+	 */
+	public T getModelValue() {
+		return modelBinding.getModelValue();
+	}
+
+	/**
+	 * @param valueOnTargetSide
+	 */
+	public void setModelValue(T valueOnTargetSide) {
+		IStatus status = validator.validate(valueOnTargetSide);
+		targetBinding.setStatus(status);
+
+		/*
+		 * We pass on the value towards the model only if a warning or better.
+		 * We block if an error or worse. This may or may not be the behavior
+		 * expected by the users.
+		 */
+		if (status.getSeverity() >= IStatus.WARNING) {
+			modelBinding.setModelValue(valueOnTargetSide);
+		}
+	}
+
+	/**
+	 * The default behavior is to validate only when going from target to model.
+	 * The error status observable is assumed to be there to show user errors.
+	 * Therefore no validation is done in this method.
+	 */
+	public void setTargetValue(T valueOnModelSide) {
+		targetBinding.setTargetValue(valueOnModelSide);
+	}
+
+	public void setStatus(IStatus status) {
+		/*
+		 * The error occurred somewhere on the model side. We push this error
+		 * back to the target.
+		 */
+		targetBinding.setStatus(status);
+	}
+
+	public void removeModelListener() {
+		/*
+		 * Pass the request back to the next link in the binding chain so
+		 * eventually the request gets back to the model observable.
+		 */
+		modelBinding.removeModelListener();
+	}
+}
\ No newline at end of file