blob: c866a55b99f96462424824b623c433c65abf4627 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005 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.tests.databinding.scenarios;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
import org.eclipse.jface.databinding.BindSpec;
import org.eclipse.jface.databinding.ChangeEvent;
import org.eclipse.jface.databinding.IChangeListener;
import org.eclipse.jface.databinding.IDataBindingContext;
import org.eclipse.jface.databinding.IUpdatableValue;
import org.eclipse.jface.databinding.Property;
import org.eclipse.jface.databinding.converter.Converter;
import org.eclipse.jface.databinding.converter.IConverter;
import org.eclipse.jface.databinding.converters.IdentityConverter;
import org.eclipse.jface.databinding.swt.SWTProperties;
import org.eclipse.jface.databinding.validator.IValidator;
import org.eclipse.jface.examples.databinding.model.Adventure;
import org.eclipse.jface.examples.databinding.model.Cart;
import org.eclipse.jface.examples.databinding.model.SampleData;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.Text;
/**
* To run the tests in this class, right-click and select "Run As JUnit Plug-in
* Test". This will also start an Eclipse instance. To clean up the launch
* configuration, open up its "Main" tab and select "[No Application] - Headless
* Mode" as the application to run.
*/
public class PropertyScenarios extends ScenariosTestCase {
private Adventure adventure;
protected void setUp() throws Exception {
super.setUp();
// do any setup work here
adventure = SampleData.WINTER_HOLIDAY;
}
protected void tearDown() throws Exception {
// do any teardown work here
super.tearDown();
}
public void testEnterText() {
// just to make sure enterText() generates a FocusOut event.
Text text = new Text(getComposite(), SWT.BORDER);
final boolean[] focusLostHolder = { false };
text.addFocusListener(new FocusListener() {
public void focusGained(FocusEvent e) {
// only interested in focus lost events
}
public void focusLost(FocusEvent e) {
focusLostHolder[0] = true;
}
});
enterText(text, "hallo");
assertTrue(focusLostHolder[0]);
}
public void testScenario01() {
Text text = new Text(getComposite(), SWT.BORDER);
getDbc().bind(text, new Property(adventure, "name"), null);
// uncomment the following line to see what's happening
// happening
// spinEventLoop(1);
assertEquals(adventure.getName(), text.getText());
enterText(text, "foobar");
// uncomment the following line to see what's happening
// spinEventLoop(1);
assertEquals("foobar", adventure.getName());
adventure.setName("barfoo");
// uncomment the following line to see what's happening
// spinEventLoop(1);
assertEquals("barfoo", text.getText());
}
public void testScenario02() {
// Binding the name property of an Adventure object to the contents of
// Text controls, no conversion, no validation. The Text widget editable
// is set to false.by the developer (can not change the name)
Text text = new Text(getComposite(), SWT.READ_ONLY);
getDbc().bind(text, new Property(adventure, "name"), null);
assertEquals(adventure.getName(), text.getText());
}
public void testScenario03() {
// Binding of a read-only property of an Adventure object to the
// contents of Text controls, no conversion, no validation. Text control
// is not editable as a side effect of binding to a read-only property..
Cart cart = SampleData.CART;
cart.setAdventureDays(42);
// bind to the lodgingDays feature, which is read-only and always one
// less than the number of adventure days.
Text text = new Text(getComposite(), SWT.BORDER);
getDbc().bind(text, new Property(cart, "lodgingDays"),
new BindSpec(new IConverter() {
public Class getModelType() {
return int.class;
}
public Class getTargetType() {
return String.class;
}
public Object convertTargetToModel(Object object) {
return new Integer((String) object);
}
public Object convertModelToTarget(Object object) {
return object.toString();
}
}, null));
assertEquals(new Integer(cart.getLodgingDays()).toString(), text
.getText());
// TODO API extension needed: getChangeable() and setChangeable() on
// IUpdatableValue or IUpdatable
// assertEquals(
// "Needs API extension: getChangeable() and setChangeable() on
// IUpdatableValue or IUpdatable.",
// false, text.getEditable());
}
public void testScenario04() {
// Binding a nested property of an Adventure object to the content of a
// Text control, no conversion, no validation.
Text text = new Text(getComposite(), SWT.BORDER);
// TODO Scenario needs to be more specific - I'm binding to the default
// lodging's description of an adventure. What do we expect to happen
// when the default lodging changes? If we expect no change, then this
// scenario does not introduce anything new. If we expect the binding to
// be to the new default lodging's description, shouldn't we move this
// scenario to the master/detail section? I'm assuming the latter for
// now.
IUpdatableValue defaultLodging = (IUpdatableValue) getDbc()
.createUpdatable(
new Property(adventure, "defaultLodging"));
getDbc().bind(
text,
new Property(defaultLodging, "description",
String.class, Boolean.FALSE), null);
// test changing the description
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
enterText(text, "foobar");
assertEquals("foobar", adventure.getDefaultLodging().getDescription());
adventure.getDefaultLodging().setDescription("barfoo");
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
// test changing the default lodging
adventure.setDefaultLodging(SampleData.CAMP_GROUND);
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
adventure.getDefaultLodging().setDescription("barfo");
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
adventure.setDefaultLodging(null);
assertEquals("", text.getText());
adventure.setDefaultLodging(SampleData.FIVE_STAR_HOTEL);
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
adventure.getDefaultLodging().setDescription("barf");
assertEquals(adventure.getDefaultLodging().getDescription(), text
.getText());
}
public void testScenario05() {
// Binding the name property of an Adventure object to the contents of
// Text controls where conversion occurs � the model data is held all
// in
// uppercase and displayed in lowercase with the first letter
// capitalized.
Text text = new Text(getComposite(), SWT.BORDER);
adventure.setName("UPPERCASE");
getDbc().bind(text, new Property(adventure, "name"),
new BindSpec(new IConverter() {
public Class getModelType() {
return String.class;
}
public Class getTargetType() {
return String.class;
}
public Object convertTargetToModel(Object fromObject) {
return ((String) fromObject).toUpperCase();
}
public Object convertModelToTarget(Object toObject) {
String modelValue = (String) toObject;
if (modelValue == null || modelValue.equals("")) {
return modelValue;
}
String firstChar = modelValue.substring(0, 1);
String remainingChars = modelValue.substring(1);
return firstChar.toUpperCase()
+ remainingChars.toLowerCase();
}
}, null));
// spinEventLoop(1);
assertEquals("Uppercase", text.getText());
enterText(text, "lowercase");
// spinEventLoop(1);
// TODO If we wanted to "canonicalize" the value in the text field, how
// could we do that?
assertEquals("LOWERCASE", adventure.getName());
}
public void testScenario06() {
// Binding the name property of an Adventure object to the contents of
// Text controls where validation occurs and the name cannot be longer
// than 15 characters and cannot contain spaces
Text text = new Text(getComposite(), SWT.BORDER);
final String noSpacesMessage = "Name must not contain spaces.";
final String max15CharactersMessage = "Maximum length for name is 15 characters.";
adventure.setName("ValidValue");
getDbc().bind(
text,
new Property(adventure, "name"),
new BindSpec(new IdentityConverter(String.class),
new IValidator() {
public String isPartiallyValid(Object value) {
return isValid(value);
}
public String isValid(Object value) {
String stringValue = (String) value;
if (stringValue.length() > 15) {
return max15CharactersMessage;
} else if (stringValue.indexOf(' ') != -1) {
return noSpacesMessage;
} else {
return null;
}
}
}));
// no validation message
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
text.setText("Invalid Value");
assertEquals(noSpacesMessage, getDbc().getCombinedValidationMessage()
.getValue());
assertEquals("ValidValue", text.getText());
text.setText("InvalidValueBecauseTooLong");
assertEquals(max15CharactersMessage, getDbc()
.getCombinedValidationMessage().getValue());
assertEquals("ValidValue", text.getText());
enterText(text, "anothervalid");
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
assertEquals("anothervalid", text.getText());
assertEquals("anothervalid", adventure.getName());
}
public void testScenario07() {
// Binding the price property of an Adventure to a Text control. Price
// is a double and Text accepts String so conversion will have to occur.
// Validation ensure that the value is positive
Text text = new Text(getComposite(), SWT.BORDER);
adventure.setPrice(5.0);
final String cannotBeNegativeMessage = "Price cannot be negative.";
final String mustBeCurrencyMessage = "Price must be a currency.";
getDbc().bind(text, new Property(adventure, "price"),
new BindSpec(new Converter(String.class, double.class) {
public Object convertTargetToModel(Object fromObject) {
return new Double((String) fromObject);
}
public Object convertModelToTarget(Object toObject) {
return ((Double) toObject).toString();
}
}, new IValidator() {
public String isPartiallyValid(Object value) {
return null;
}
public String isValid(Object value) {
String stringValue = (String) value;
try {
double doubleValue = new Double(stringValue)
.doubleValue();
if (doubleValue < 0.0) {
return cannotBeNegativeMessage;
}
return null;
} catch (NumberFormatException ex) {
return mustBeCurrencyMessage;
}
}
}));
assertEquals("5.0", text.getText());
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
enterText(text, "0.65");
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
assertEquals(0.65, adventure.getPrice(), 0.0001);
adventure.setPrice(42.24);
assertEquals("42.24", text.getText());
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
enterText(text, "jygt");
assertEquals(mustBeCurrencyMessage, getDbc()
.getCombinedValidationMessage().getValue());
enterText(text, "-23.9");
assertEquals(cannotBeNegativeMessage, getDbc()
.getCombinedValidationMessage().getValue());
assertEquals(42.24, adventure.getPrice(), 0.0001);
adventure.setPrice(0.0);
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
}
public void testScenario08() {
// Binding the price property of an Adventure to a Text control but with
// custom conversion � the double will be validated to only have two
// decimal places and displayed with a leading currency symbol, and can
// be entered with or without the currency symbol.
Text text = new Text(getComposite(), SWT.BORDER);
adventure.setPrice(5.0);
final String cannotBeNegativeMessage = "Price cannot be negative.";
final String mustBeCurrencyMessage = "Price must be a currency.";
final NumberFormat currencyFormat = NumberFormat
.getCurrencyInstance(Locale.CANADA);
getDbc().bind(text, new Property(adventure, "price"),
new BindSpec(new Converter(String.class, double.class) {
public Object convertTargetToModel(Object fromObject) {
try {
return currencyFormat.parse((String) fromObject);
} catch (ParseException e) {
// TODO throw something like
// IllegalConversionException?
return new Double(0);
}
}
public Object convertModelToTarget(Object toObject) {
return currencyFormat.format(((Double) toObject)
.doubleValue());
}
}, new IValidator() {
public String isPartiallyValid(Object value) {
return null;
}
public String isValid(Object value) {
String stringValue = (String) value;
try {
double doubleValue = currencyFormat.parse(
stringValue).doubleValue();
if (doubleValue < 0.0) {
return cannotBeNegativeMessage;
}
return null;
} catch (ParseException e) {
return mustBeCurrencyMessage;
}
}
}));
assertEquals("$5.00", text.getText());
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
enterText(text, "$0.65");
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
assertEquals(0.65, adventure.getPrice(), 0.0001);
adventure.setPrice(42.24);
assertEquals("$42.24", text.getText());
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
enterText(text, "jygt");
assertEquals(mustBeCurrencyMessage, getDbc()
.getCombinedValidationMessage().getValue());
enterText(text, "-$23.9");
assertEquals(cannotBeNegativeMessage, getDbc()
.getCombinedValidationMessage().getValue());
assertEquals(42.24, adventure.getPrice(), 0.0001);
adventure.setPrice(0.0);
assertEquals("", getDbc().getCombinedValidationMessage().getValue());
}
public void testScenario09() {
// Binding a boolean property to a CheckBox. Adventure will have a
// Boolean property �petsAllowed�
Button checkbox = new Button(getComposite(), SWT.CHECK);
// checkbox.setText("Pets allowed");
// checkbox.setLayoutData(new GridData(SWT.LEFT,SWT.TOP, false,false));
adventure.setPetsAllowed(true);
getDbc().bind(checkbox,
new Property(adventure, "petsAllowed"), null);
assertEquals(true, checkbox.getSelection());
setButtonSelectionWithEvents(checkbox, false);
assertEquals(false, adventure.isPetsAllowed());
adventure.setPetsAllowed(true);
assertEquals(true, checkbox.getSelection());
}
public void testScenario10() {
// Binding a Transportation departure time to a Text control that
// formats and validates the time to and from a String. There are
// property bindings that bind elements of the GUI to elements to GUI
// and also elements of the domain to elements of the domain
// TODO fail("not implemented");
}
public void testScenario11() {
// Binding the max value of a spinner to another spinner.
Spinner spinner1 = new Spinner(getComposite(), SWT.NONE);
spinner1.setSelection(10);
spinner1.setMinimum(1);
spinner1.setMaximum(100);
Spinner spinner2 = new Spinner(getComposite(), SWT.NONE);
spinner2.setMaximum(1);
getDbc().bind(spinner1,
new Property(spinner2, SWTProperties.MAX), null);
assertEquals(1, spinner1.getSelection());
spinner1.setSelection(10);
spinner1.notifyListeners(SWT.Modify, new Event());
assertEquals(10, spinner2.getMaximum());
}
public void testScenario12() {
// Binding the enabled state of several Text controls to a check box.
// There will be two check boxes, so as each is enabled/disabled the
// other one follows as do the states of the Text controls.
Button checkbox1 = new Button(getComposite(), SWT.CHECK);
checkbox1.setSelection(false);
Button checkbox2 = new Button(getComposite(), SWT.CHECK);
checkbox2.setSelection(false);
Text text1 = new Text(getComposite(), SWT.NONE);
Text text2 = new Text(getComposite(), SWT.NONE);
IUpdatableValue checkbox1Selected = (IUpdatableValue) getDbc()
.createUpdatable(checkbox1);
IUpdatableValue checkbox2Selected = (IUpdatableValue) getDbc()
.createUpdatable(checkbox2);
// bind the two checkboxes so that if one is checked, the other is not
// and vice versa.
getDbc().bind(checkbox1Selected, checkbox2Selected,
new BindSpec(new IConverter() {
public Class getModelType() {
return boolean.class;
}
public Class getTargetType() {
return boolean.class;
}
private Boolean negated(Boolean booleanObject) {
return new Boolean(!booleanObject.booleanValue());
}
public Object convertTargetToModel(Object targetObject) {
return negated((Boolean) targetObject);
}
public Object convertModelToTarget(Object modelObject) {
return negated((Boolean) modelObject);
}
}, null));
// bind the enabled state of the two text widgets to one of the
// checkboxes each.
getDbc().bind(new Property(text1, SWTProperties.ENABLED),
checkbox1Selected, null);
getDbc().bind(new Property(text2, SWTProperties.ENABLED),
checkbox2Selected, null);
assertEquals(true, text1.getEnabled());
assertEquals(false, text2.getEnabled());
assertEquals(true, checkbox1.getSelection());
setButtonSelectionWithEvents(checkbox1, false);
assertEquals(false, text1.getEnabled());
assertEquals(true, text2.getEnabled());
assertEquals(true, checkbox2.getSelection());
setButtonSelectionWithEvents(checkbox2, false);
assertEquals(true, text1.getEnabled());
assertEquals(false, text2.getEnabled());
assertEquals(true, checkbox1.getSelection());
}
public void testScenario13() {
// Changing the update policy to be not automatic, but on explicit
// method call (e.g. triggered by a button click).
getSWTUpdatableFactory().setUpdateTime(IDataBindingContext.TIME_LATE);
Text text = new Text(getComposite(), SWT.BORDER);
getDbc().bind(text, new Property(adventure, "name"), null);
// uncomment the following line to see what's happening
// happening
// spinEventLoop(1);
String adventureName = adventure.getName();
assertEquals(adventureName, text.getText());
enterText(text, "foobar");
// uncomment the following line to see what's happening
// spinEventLoop(1);
assertEquals("foobar", adventure.getName());
adventure.setName("barfoo");
// uncomment the following line to see what's happening
// spinEventLoop(1);
assertEquals("barfoo", text.getText());
}
public void testScenario14() {
Text t1 = new Text(getComposite(), SWT.BORDER);
Text t2 = new Text(getComposite(), SWT.BORDER);
getSWTUpdatableFactory().setUpdateTime(IDataBindingContext.TIME_EARLY);
getDbc().bind(t1, new Property(adventure, "name"), null);
getDbc().bind(t2, new Property(adventure, "name"), null);
final int[] counter = { 0 };
IUpdatableValue uv = (IUpdatableValue) getDbc().createUpdatable(
new Property(adventure, "name"));
uv.addChangeListener(new IChangeListener() {
public void handleChange(ChangeEvent changeEvent) {
// Count how many times adventure has changed
counter[0]++;
}
});
String name = adventure.getName() + "Foo";
enterText(t1, name);
assertEquals(name, adventure.getName());
assertEquals(name, t2.getText());
assertTrue(counter[0] == 1);
name = name + "Bar";
uv.setValue(name);
assertEquals(t1.getText(), adventure.getName());
assertEquals(2, counter[0]);
}
}