diff options
author | Dave Orme | 2006-01-23 23:38:02 +0000 |
---|---|---|
committer | Dave Orme | 2006-01-23 23:38:02 +0000 |
commit | ac37a0ae151ae54b049dfa4705449e1fcb27e078 (patch) | |
tree | 0eb345ea44c3f441e00e4b8711a5055ca698eb40 | |
parent | 51dd9d3d0624eb47328cdd112c8e494d593b4fd6 (diff) | |
download | org.eclipse.e4.databinding-ac37a0ae151ae54b049dfa4705449e1fcb27e078.tar.gz org.eclipse.e4.databinding-ac37a0ae151ae54b049dfa4705449e1fcb27e078.tar.xz org.eclipse.e4.databinding-ac37a0ae151ae54b049dfa4705449e1fcb27e078.zip |
Progress on: Bug 118323: [DataBinding] Need editing life cycle events on the data binding frameworkRoot_dpollock_MenuVisibilityI20060124-0800
https://bugs.eclipse.org/bugs/show_bug.cgi?id=118323
Added life cycle events to ValueBinding
9 files changed, 318 insertions, 38 deletions
diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingAdapter.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingAdapter.java new file mode 100644 index 00000000..20e1a1c2 --- /dev/null +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingAdapter.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.jface.databinding; + +/** + * A default implementation for an IBindingListener event handler. + * + * @since 3.2 + */ +public class BindingAdapter implements IBindingListener { + + /* (non-Javadoc) + * @see org.eclipse.jface.databinding.IBindingListener#bindingEvent(org.eclipse.jface.databinding.BindingEvent) + */ + public String bindingEvent(BindingEvent e) { + return null; + } + +} diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingEvent.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingEvent.java new file mode 100644 index 00000000..2bc76bc0 --- /dev/null +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/BindingEvent.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.jface.databinding; + +/** + * The event that is passed to a #bindingEvent method of an IBindingListener. + * + * @since 3.2 + */ +public class BindingEvent { + /** + * The type of copy that is occuring. One of the COPY_TO_* constants. + */ + public int copyType; + + /** + * The position in the processing pipeline where this event is occuring. + * One of the PIPELINE_* constants. + */ + public int pipelinePosition; + + /** + * Holds the value that was retrieved from the source updatable. Setting + * the value of this field changes the value that will be processed by + * all subsequent steps in the data flow pipeline. + */ + public Object originalValue = null; + + /** + * Holds the value that will be copied into the final updatable. This + * value is null if the original value has not been converted into the + * final updatable's data type yet. Setting the value of this field + * changes the value that will be processed by all subsequent steps in + * the data flow pipeline. + */ + public Object convertedValue = null; + + /** + * A constant indicating that this event is occuring during a copy from + * model to target. + */ + public static final int COPY_TO_TARGET = 0; + /** + * A constant indicating that this event is occuring during a copy from + * target to model. + */ + public static final int COPY_TO_MODEL = 1; + + /** + * A constant indicating that this event is occuring immedately after the + * value to copy has been gotten from its IUpdatable. + */ + public static final int PIPELINE_AFTER_GET = 0; + /** + * A constant indicating that this event is occuring immedately after the + * value has been validated as being possible to convert to the other + * updatable's data type. + */ + public static final int PIPELINE_AFTER_VALIDATE = 1; + /** + * A constant indicating that this event is occuring immedately after the + * original value has been converted to the other updatable's data type. + */ + public static final int PIPELINE_AFTER_CONVERT = 2; + /** + * A constant indicating that this event is occuring immedately after the + * business rule validation has occured. + */ + public static final int PIPELINE_AFTER_BUSINESS_VALIDATE = 3; + /** + * A constant indicating that this event is occuring immedately after the + * converted value has been set on the updatable. + */ + public static final int PIPELINE_AFTER_SET = 4; +} diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBinding.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBinding.java new file mode 100644 index 00000000..2ca834ce --- /dev/null +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBinding.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.jface.databinding; + +/** + * The interface that represents a binding between a model and a target. + * + * @since 3.2 + */ +public interface IBinding { + /** + * Add a listener to the set of listeners that will be notified when + * an event occurs in the data flow pipeline that is managed by this + * IBinding. + * + * @param listener The listener to add. + */ + public void addBindingEventListener(IBindingListener listener); + /** + * Removes a listener from the set of listeners that will be notified when + * an event occurs in the data flow pipeline that is managed by this + * IBinding. + * + * @param listener The listener to remove. + */ + public void removeBindingEventListener(IBindingListener listener); +} diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBindingListener.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBindingListener.java new file mode 100644 index 00000000..6c88192b --- /dev/null +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IBindingListener.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.jface.databinding; + +/** + * An interface for objects that need to listen to events that occur in the + * data flow pipeline + * + * @since 3.2 + */ +public interface IBindingListener { + /** + * Method bindingEvent. The method that is called when something interesting + * occurs in the data flow pipeline. + * + * @param e The IBindingEvent to handle. + * @return null if no error or an error message to abort the operation. The + * error message will be propagated to the data binding context's error + * message updatable. + */ + public String bindingEvent(BindingEvent e); + +} diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IDataBindingContext.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IDataBindingContext.java index 50df6c26..55f54ec9 100644 --- a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IDataBindingContext.java +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/IDataBindingContext.java @@ -88,8 +88,9 @@ public interface IDataBindingContext { * @param modelUpdatable * @param bindSpec * the bind spec, or null + * @return The IBinding that manages this data flow */ - public void bind(IUpdatable targetUpdatable, IUpdatable modelUpdatable, + public IBinding bind(IUpdatable targetUpdatable, IUpdatable modelUpdatable, IBindSpec bindSpec) ; /** @@ -100,8 +101,9 @@ public interface IDataBindingContext { * @param modelDescription * @param bindSpec * the bind spec, or null + * @return The IBinding that manages this data flow */ - public void bind(IUpdatable targetUpdatable, Object modelDescription, + public IBinding bind(IUpdatable targetUpdatable, Object modelDescription, IBindSpec bindSpec); /** @@ -112,8 +114,9 @@ public interface IDataBindingContext { * @param modelUpdatable * @param bindSpec * the bind spec, or null + * @return The IBinding that manages this data flow */ - public void bind(Object targetDescription, IUpdatable modelUpdatable, + public IBinding bind(Object targetDescription, IUpdatable modelUpdatable, IBindSpec bindSpec) ; /** @@ -124,8 +127,9 @@ public interface IDataBindingContext { * @param modelDescription * @param bindSpec * the bind spec, or null + * @return The IBinding that manages this data flow */ - public void bind(Object targetDescription, Object modelDescription, + public IBinding bind(Object targetDescription, Object modelDescription, IBindSpec bindSpec) ; /** @@ -143,7 +147,7 @@ public interface IDataBindingContext { * interpreted by implementors of IUpdatableFactory, the data binding * framework does not impose any semantics on them. * - * @param description + * @param nestedProperty * @return IUpdatable for the given description */ public IUpdatable createNestedUpdatable(NestedProperty nestedProperty); diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/Binding.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/Binding.java index 1d9d45a6..009a9c43 100644 --- a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/Binding.java +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/Binding.java @@ -10,12 +10,19 @@ *******************************************************************************/ package org.eclipse.jface.internal.databinding; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.databinding.BindingEvent; +import org.eclipse.jface.databinding.IBinding; +import org.eclipse.jface.databinding.IBindingListener; /** * @since 3.2 * */ -abstract public class Binding { +abstract public class Binding implements IBinding { protected final DataBindingContext context; @@ -31,4 +38,30 @@ abstract public class Binding { */ abstract public void updateTargetFromModel(); + /* (non-Javadoc) + * @see org.eclipse.jface.databinding.IBinding#addBindingEventListener(org.eclipse.jface.databinding.IBindingListener) + */ + public void addBindingEventListener(IBindingListener listener) { + bindingEventListeners.add(listener); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.databinding.IBinding#removeBindingEventListener(org.eclipse.jface.databinding.IBindingListener) + */ + public void removeBindingEventListener(IBindingListener listener) { + bindingEventListeners.remove(listener); + } + + private List bindingEventListeners = new ArrayList(); + + protected String fireBindingEvent(BindingEvent event) { + String result = null; + for (Iterator bindingEventIter = bindingEventListeners.iterator(); bindingEventIter.hasNext();) { + IBindingListener listener = (IBindingListener) bindingEventIter.next(); + result = listener.bindingEvent(event); + if (result != null) + break; + } + return result; + } } diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/DataBindingContext.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/DataBindingContext.java index a378d13f..982dc534 100644 --- a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/DataBindingContext.java +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/DataBindingContext.java @@ -23,6 +23,7 @@ import org.eclipse.jface.databinding.BindSpec; import org.eclipse.jface.databinding.BindingException; import org.eclipse.jface.databinding.IBindSpec; import org.eclipse.jface.databinding.IBindSupportFactory; +import org.eclipse.jface.databinding.IBinding; import org.eclipse.jface.databinding.IChangeListener; import org.eclipse.jface.databinding.IDataBindingContext; import org.eclipse.jface.databinding.IUpdatable; @@ -327,7 +328,7 @@ public class DataBindingContext implements IDataBindingContext { * org.eclipse.jface.databinding.IUpdatable, * org.eclipse.jface.databinding.IBindSpec) */ - public void bind(IUpdatable targetUpdatable, IUpdatable modelUpdatable, + public IBinding bind(IUpdatable targetUpdatable, IUpdatable modelUpdatable, IBindSpec bindSpec) { Binding binding; if (bindSpec == null) { @@ -375,6 +376,7 @@ public class DataBindingContext implements IDataBindingContext { // targetUpdatable.addChangeListener(binding); // modelUpdatable.addChangeListener(binding); binding.updateTargetFromModel(); + return binding; } /* @@ -384,9 +386,9 @@ public class DataBindingContext implements IDataBindingContext { * org.eclipse.jface.databinding.IUpdatable, * org.eclipse.jface.databinding.IBindSpec) */ - public void bind(Object targetDescription, IUpdatable modelUpdatable, + public IBinding bind(Object targetDescription, IUpdatable modelUpdatable, IBindSpec bindSpec) { - bind(createUpdatable(targetDescription), modelUpdatable, bindSpec); + return bind(createUpdatable(targetDescription), modelUpdatable, bindSpec); } /* @@ -395,7 +397,7 @@ public class DataBindingContext implements IDataBindingContext { * @see org.eclipse.jface.databinding.IDataBindingContext#bind(org.eclipse.jface.databinding.IUpdatable, * java.lang.Object, org.eclipse.jface.databinding.IBindSpec) */ - public void bind(IUpdatable targetUpdatable, Object modelDescription, + public IBinding bind(IUpdatable targetUpdatable, Object modelDescription, IBindSpec bindSpec) { if (bindSpec == null) { bindSpec = new BindSpec(null, null); @@ -408,7 +410,7 @@ public class DataBindingContext implements IDataBindingContext { .getElementType(); } fillBindSpecDefaults(bindSpec, fromType, null, modelDescription); - bind(targetUpdatable, createUpdatable(modelDescription), bindSpec); + return bind(targetUpdatable, createUpdatable(modelDescription), bindSpec); } protected void fillBindSpecDefaults(IBindSpec bindSpec, Class fromType, @@ -463,9 +465,9 @@ public class DataBindingContext implements IDataBindingContext { * @see org.eclipse.jface.databinding.IDataBindingContext#bind(java.lang.Object, * java.lang.Object, org.eclipse.jface.databinding.IBindSpec) */ - public void bind(Object targetDescription, Object modelDescription, + public IBinding bind(Object targetDescription, Object modelDescription, IBindSpec bindSpec) { - bind(createUpdatable(targetDescription), modelDescription, bindSpec); + return bind(createUpdatable(targetDescription), modelDescription, bindSpec); } /* diff --git a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/ValueBinding.java b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/ValueBinding.java index dd335e1c..7044fd55 100644 --- a/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/ValueBinding.java +++ b/bundles/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/ValueBinding.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jface.internal.databinding; +import org.eclipse.jface.databinding.BindingEvent; import org.eclipse.jface.databinding.BindingException; import org.eclipse.jface.databinding.ChangeEvent; import org.eclipse.jface.databinding.IBindSpec; @@ -49,7 +50,6 @@ public class ValueBinding extends Binding { if (converter == null) { throw new BindingException("Missing converter from " + target.getValueType() + " to " + model.getValueType()); //$NON-NLS-1$ //$NON-NLS-2$ } - // FIXME: Waiting for M4 to ship to commit; also need to write a test case if (!converter.getModelType().isAssignableFrom(model.getValueType())) { throw new BindingException( "Converter does not apply to model type. Expected: " + model.getValueType() + ", actual: " + converter.getModelType()); //$NON-NLS-1$ //$NON-NLS-2$ @@ -105,31 +105,43 @@ public class ValueBinding extends Binding { * This also does validation. */ public void updateModelFromTarget() { - Object value = target.getValue(); - String validationError = doValidateTarget(value); - context.updateValidationError(targetChangeListener, validationError); - if (validationError == null) { - try { - updating = true; - model.setValue(converter.convertTargetToModel(value)); - } catch (Exception ex) { - context.updateValidationError(targetChangeListener, BindingMessages - .getString("ValueBinding_ErrorWhileSettingValue")); //$NON-NLS-1$ - } finally { - updating = false; - } + BindingEvent e = new BindingEvent(); + e.copyType = BindingEvent.COPY_TO_MODEL; + e.originalValue = target.getValue(); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_GET; + fireBindingEvent(e); + + String validationError = doValidate(e.originalValue); + if (validationError != null) { + context.updatePartialValidationError(targetChangeListener, validationError); + return; + } + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_VALIDATE; + fireBindingEvent(e); + + try { + updating = true; + + e.convertedValue = converter.convertTargetToModel(e.originalValue); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_CONVERT; + fireBindingEvent(e); + + // FIXME: Need to add business validation + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_BUSINESS_VALIDATE; + fireBindingEvent(e); + + model.setValue(e.convertedValue); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_SET; + fireBindingEvent(e); + } catch (Exception ex) { + context.updateValidationError(targetChangeListener, BindingMessages + .getString("ValueBinding_ErrorWhileSettingValue")); //$NON-NLS-1$ + } finally { + updating = false; } } - /** - * - */ - public void validateTarget() { - Object value = target.getValue(); - doValidateTarget(value); - } - - private String doValidateTarget(Object value) { + private String doValidate(Object value) { String validationError = validator.isValid(value); context.updatePartialValidationError(targetChangeListener, null); context.updateValidationError(targetChangeListener, validationError); @@ -142,8 +154,23 @@ public class ValueBinding extends Binding { public void updateTargetFromModel() { try { updating = true; - target.setValue(converter.convertModelToTarget(model.getValue())); - validateTarget(); + BindingEvent e = new BindingEvent(); + e.copyType = BindingEvent.COPY_TO_TARGET; + e.originalValue = model.getValue(); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_GET; + fireBindingEvent(e); + + e.convertedValue = converter.convertModelToTarget(e.originalValue); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_CONVERT; + fireBindingEvent(e); + + target.setValue(e.convertedValue); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_SET; + fireBindingEvent(e); + + doValidate(target.getValue()); + e.pipelinePosition = BindingEvent.PIPELINE_AFTER_VALIDATE; + fireBindingEvent(e); } finally { updating = false; } diff --git a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/DatabindingContextTest.java b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/DatabindingContextTest.java index 0f4fd634..8ffcc40c 100644 --- a/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/DatabindingContextTest.java +++ b/tests/org.eclipse.jface.tests.databinding/src/org/eclipse/jface/tests/databinding/DatabindingContextTest.java @@ -15,8 +15,11 @@ import java.util.Map; import junit.framework.TestCase; import org.eclipse.jface.databinding.BindSpec; +import org.eclipse.jface.databinding.BindingAdapter; +import org.eclipse.jface.databinding.BindingEvent; import org.eclipse.jface.databinding.BindingException; import org.eclipse.jface.databinding.DataBinding; +import org.eclipse.jface.databinding.IBinding; import org.eclipse.jface.databinding.IDataBindingContext; import org.eclipse.jface.databinding.IUpdatable; import org.eclipse.jface.databinding.IUpdatableFactory; @@ -115,6 +118,37 @@ public class DatabindingContextTest extends TestCase { assertEquals(o2, settableValue1.getValue()); } + public void testBindingListeners() { + final int[] calls = new int[] {0, 0}; + final int[] pipelinePositions = new int[] {0, 1, 2, 3, 4, 0, 2, 4, 1}; + settableValue1.setValue(o1); + settableValue2.setValue(o2); + IBinding binding = dbc.bind(settableValue1, settableValue2, null); + binding.addBindingEventListener(new BindingAdapter() { + public String bindingEvent(BindingEvent e) { + assertEquals("Unexpected pipeline position", pipelinePositions[calls[0]], e.pipelinePosition); + calls[0]++; + return null; + } + }); + binding.addBindingEventListener(new BindingAdapter() { + public String bindingEvent(BindingEvent e) { + calls[1]++; + return null; + } + }); + assertEquals(o2, settableValue1.getValue()); + assertEquals("Both binding events should be called the same number of times", calls[0], calls[1]); + settableValue1.setValue(o1); + assertEquals(o1, settableValue2.getValue()); + assertEquals("Both binding events should be called the same number of times", calls[0], calls[1]); + settableValue2.setValue(o2); + assertEquals(o2, settableValue1.getValue()); + assertEquals("Both binding events should be called the same number of times", calls[0], calls[1]); + assertEquals("binding events should be called at least once", true, calls[0] > 0); + assertEquals("should be 9 binding events", 9, calls[0]); + } + public void testCreateNestedUpdatableWithArrays() { String parentObject = ""; NestedProperty nestedProperty = new NestedProperty(parentObject, new String[] {"nestedChild1", "nestedChild2", "foo"}, new Class[] {Integer.class, String.class, Float.class}); |