/*******************************************************************************
* Copyright (c) 2007, 2014 compeople AG 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:
* compeople AG - initial API and implementation
*******************************************************************************/
package org.eclipse.riena.ui.ridgets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.riena.beans.common.Address;
import org.eclipse.riena.beans.common.Person;
import org.eclipse.riena.beans.common.TestBean;
import org.eclipse.riena.core.marker.IMarkable;
import org.eclipse.riena.core.marker.Markable;
import org.eclipse.riena.core.test.RienaTestCase;
import org.eclipse.riena.core.test.collect.NonUITestCase;
import org.eclipse.riena.core.util.ReflectionUtils;
import org.eclipse.riena.ui.core.marker.ErrorMarker;
import org.eclipse.riena.ui.core.marker.ErrorMessageMarker;
import org.eclipse.riena.ui.core.marker.IMessageMarker;
import org.eclipse.riena.ui.core.marker.MessageMarker;
import org.eclipse.riena.ui.core.marker.ValidationTime;
import org.eclipse.riena.ui.ridgets.marker.ValidationMessageMarker;
import org.eclipse.riena.ui.ridgets.swt.DefaultRealm;
import org.eclipse.riena.ui.ridgets.validation.MinLength;
import org.eclipse.riena.ui.ridgets.validation.ValidRange;
import org.eclipse.riena.ui.ridgets.validation.ValidationFailure;
import org.eclipse.riena.ui.ridgets.validation.ValidationRuleStatus;
/**
* Tests for the ValueBindingSupport.
*/
@NonUITestCase
public class ValueBindingSupportTest extends RienaTestCase {
private DefaultRealm realm;
private ValueBindingSupport valueBindingSupport;
private TestBean bean;
private IObservableValue model;
private IObservableValue target;
private IMarkable markable;
@Override
protected void setUp() throws Exception {
super.setUp();
realm = new DefaultRealm();
bean = new TestBean();
model = PojoObservables.observeValue(bean, TestBean.PROPERTY);
target = new WritableValue();
valueBindingSupport = new ValueBindingSupport(target, model);
markable = new Markable();
valueBindingSupport.setMarkable(markable);
}
@Override
protected void tearDown() throws Exception {
realm.dispose();
realm = null;
super.tearDown();
}
public void testAddRemoveAfterGetValidator() throws Exception {
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
final MinLength validator = new MinLength(5);
assertFalse(valueBindingSupport.addValidationRule(validator, ValidationTime.ON_UPDATE_TO_MODEL));
assertEquals(1, valueBindingSupport.getAllValidators().getValidators().size());
assertFalse(valueBindingSupport.removeValidationRule(validator));
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
}
public void testAddRemoveAfterSetValidator() throws Exception {
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
final MinLength validator = new MinLength(5);
assertFalse(valueBindingSupport.addValidationRule(validator, ValidationTime.AFTER_UPDATE_TO_MODEL));
assertEquals(1, valueBindingSupport.getAllValidators().getValidators().size());
assertFalse(valueBindingSupport.removeValidationRule(validator));
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
}
public void testAddRemoveOnEditValidator() throws Exception {
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
final MinLength validator = new MinLength(5);
assertTrue(valueBindingSupport.addValidationRule(validator, ValidationTime.ON_UI_CONTROL_EDIT));
assertEquals(1, valueBindingSupport.getAllValidators().getValidators().size());
assertTrue(valueBindingSupport.removeValidationRule(validator));
assertTrue(valueBindingSupport.getAllValidators().getValidators().isEmpty());
}
public void testUpdateFromModelOnRequest() throws Exception {
assertNull(target.getValue());
bean.setProperty("TestValue"); //$NON-NLS-1$
assertNull(target.getValue());
valueBindingSupport.updateFromModel();
assertEquals("TestValue", target.getValue()); //$NON-NLS-1$
}
public void testUpdateFromTargetImmediately() throws Exception {
assertNull(bean.getProperty());
target.setValue("TestValue"); //$NON-NLS-1$
assertEquals("TestValue", bean.getProperty()); //$NON-NLS-1$
}
public void testValidationMessagesAddAndRemove() throws Exception {
valueBindingSupport.addValidationRule(new EvenNumberOfCharacters(), ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationMessage("TestMessage1"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage("TestMessage2"); //$NON-NLS-1$
final ErrorMessageMarker messageMarker1 = new ErrorMessageMarker("TestMessage3"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(messageMarker1);
final MessageMarker messageMarker2 = new MessageMarker("TestMessage4"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(messageMarker2);
assertEquals(0, markable.getMarkers().size());
target.setValue("odd"); //$NON-NLS-1$
assertEquals(5, markable.getMarkers().size());
assertMessageMarkers("TestMessage1", "TestMessage2", "TestMessage3", "TestMessage4"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
target.setValue("even"); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
valueBindingSupport.removeValidationMessage("TestMessage1"); //$NON-NLS-1$
valueBindingSupport.removeValidationMessage(messageMarker1);
target.setValue("odd"); //$NON-NLS-1$
assertEquals(3, markable.getMarkers().size());
assertMessageMarkers("TestMessage2", "TestMessage4"); //$NON-NLS-1$ //$NON-NLS-2$
target.setValue("even"); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
valueBindingSupport.removeValidationMessage("TestMessage2"); //$NON-NLS-1$
valueBindingSupport.removeValidationMessage(messageMarker2);
target.setValue("odd"); //$NON-NLS-1$
assertEquals(1, markable.getMarkers().size());
assertTrue(markable.getMarkers().iterator().next() instanceof ErrorMarker);
target.setValue("even"); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
}
public void testAddValidationMessageForUnknownRule() {
final AlwaysWrongValidator rule = new AlwaysWrongValidator();
valueBindingSupport.addValidationMessage("foo", rule); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
}
public void testRemoveValidationMessageWhenRemovingRule() {
final IValidator rule = new AlwaysWrongValidator();
valueBindingSupport.addValidationMessage("foo", rule); //$NON-NLS-1$
valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
target.setValue("value"); //$NON-NLS-1$
assertEquals(1, markable.getMarkersOfType(ValidationMessageMarker.class).size());
valueBindingSupport.removeValidationRule(rule);
assertEquals(0, markable.getMarkersOfType(ValidationMessageMarker.class).size());
}
/**
* Tests that adding the same validation several times has no effect
*/
public void testAddSameValidationMessage() {
final EvenNumberOfCharacters rule = new EvenNumberOfCharacters();
valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationMessage("TestMessage1"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage("TestMessage1"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage("TestMessage2", rule); //$NON-NLS-1$
valueBindingSupport.addValidationMessage("TestMessage2", rule); //$NON-NLS-1$
final MessageMarker messageMarker = new MessageMarker("TestMessage3"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(messageMarker);
valueBindingSupport.addValidationMessage(messageMarker);
final MessageMarker messageMarker2 = new MessageMarker("TestMessage4"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(messageMarker2, rule);
valueBindingSupport.addValidationMessage(messageMarker2, rule);
assertEquals(0, markable.getMarkers().size());
target.setValue("odd"); //$NON-NLS-1$
assertEquals(4, markable.getMarkersOfType(ValidationMessageMarker.class).size());
target.setValue("even"); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
}
public void testValidationMessagesAddAndRemoveWhileActive() throws Exception {
valueBindingSupport.addValidationRule(new EvenNumberOfCharacters(), ValidationTime.ON_UPDATE_TO_MODEL);
target.setValue("odd"); //$NON-NLS-1$
assertEquals(1, markable.getMarkers().size());
assertTrue(markable.getMarkers().iterator().next() instanceof ErrorMarker);
valueBindingSupport.addValidationMessage("TestMessage1"); //$NON-NLS-1$
assertEquals(2, markable.getMarkers().size());
assertMessageMarkers("TestMessage1"); //$NON-NLS-1$
final MessageMarker messageMarker = new MessageMarker("TestMessage2"); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(messageMarker);
assertEquals(3, markable.getMarkers().size());
assertMessageMarkers("TestMessage1", "TestMessage2"); //$NON-NLS-1$ //$NON-NLS-2$
valueBindingSupport.removeValidationMessage("TestMessage1"); //$NON-NLS-1$
assertEquals(2, markable.getMarkers().size());
assertMessageMarkers("TestMessage2"); //$NON-NLS-1$
valueBindingSupport.removeValidationMessage(messageMarker);
assertEquals(1, markable.getMarkers().size());
assertTrue(markable.getMarkers().iterator().next() instanceof ErrorMarker);
}
public void testSpecialValidationMessages() throws Exception {
final EvenNumberOfCharacters evenNumberOfCharacters = new EvenNumberOfCharacters();
final NotEndingWithDisaster notEndingWithDisaster = new NotEndingWithDisaster();
valueBindingSupport.addValidationRule(evenNumberOfCharacters, ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationRule(notEndingWithDisaster, ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationMessage("TestNotEvenMessage1", evenNumberOfCharacters); //$NON-NLS-1$
valueBindingSupport.addValidationMessage(new MessageMarker("TestNotEvenMessage2"), evenNumberOfCharacters); //$NON-NLS-1$
valueBindingSupport.addValidationMessage("TestDisasterMessage", notEndingWithDisaster); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
target.setValue("Disaster"); //$NON-NLS-1$
assertEquals(2, markable.getMarkers().size());
assertMessageMarkers("TestDisasterMessage"); //$NON-NLS-1$
target.setValue("Disaster Area"); //$NON-NLS-1$
assertEquals(3, markable.getMarkers().size());
assertMessageMarkers("TestNotEvenMessage1", "TestNotEvenMessage2"); //$NON-NLS-1$ //$NON-NLS-2$
target.setValue("We are teetering on the brink of disaster"); //$NON-NLS-1$
assertEquals(5, markable.getMarkers().size());
assertMessageMarkers("TestNotEvenMessage1", "TestNotEvenMessage2", "TestDisasterMessage"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
target.setValue("Save again"); //$NON-NLS-1$
assertEquals(0, markable.getMarkers().size());
}
public void testValidationRuleAddAndRemove() {
final IValidator rule = new EvenNumberOfCharacters();
final boolean isOnEdit1 = valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UI_CONTROL_EDIT);
assertTrue(isOnEdit1);
assertTrue(valueBindingSupport.getOnEditValidators().contains(rule));
assertFalse(valueBindingSupport.getAfterGetValidators().contains(rule));
final boolean isOnEdit2 = valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
assertFalse(isOnEdit2);
assertTrue(valueBindingSupport.getOnEditValidators().contains(rule));
assertTrue(valueBindingSupport.getAfterGetValidators().contains(rule));
valueBindingSupport.removeValidationRule(rule);
assertFalse(valueBindingSupport.getOnEditValidators().contains(rule));
assertFalse(valueBindingSupport.getAfterGetValidators().contains(rule));
}
public void testValidationRuleAddAndRemoveNull() {
try {
valueBindingSupport.addValidationRule(null, ValidationTime.ON_UPDATE_TO_MODEL);
fail();
} catch (final RuntimeException rex) {
ok();
}
final boolean result = valueBindingSupport.removeValidationRule(null);
assertFalse(result);
}
public void testAddAndRemoveValidationMessageWithRule() {
assertEquals(0, markable.getMarkersOfType(ValidationMessageMarker.class).size());
final MinLength rule = new MinLength(3);
valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationMessage("too short", rule); //$NON-NLS-1$
target.setValue("a"); //$NON-NLS-1$
assertEquals(1, markable.getMarkersOfType(ValidationMessageMarker.class).size());
assertMessageMarkers("too short"); //$NON-NLS-1$
valueBindingSupport.removeValidationMessage("too short", rule); //$NON-NLS-1$
assertEquals(0, markable.getMarkersOfType(ValidationMessageMarker.class).size());
}
public void testAddAndRemoveValidationMessageWithoutRule() {
assertEquals(0, markable.getMarkersOfType(ValidationMessageMarker.class).size());
final MinLength rule = new MinLength(3);
valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
valueBindingSupport.addValidationMessage("too short"); //$NON-NLS-1$
target.setValue("a"); //$NON-NLS-1$
assertEquals(1, markable.getMarkersOfType(ValidationMessageMarker.class).size());
assertMessageMarkers("too short"); //$NON-NLS-1$
valueBindingSupport.removeValidationMessage("too short"); //$NON-NLS-1$
assertEquals(0, markable.getMarkersOfType(ValidationMessageMarker.class).size());
}
/**
* As per Bug 289458
*/
public void testShowErrorAfterValidationOnUpdateWithBlock() {
final ValidRange rule = new ValidRange(18, 80);
valueBindingSupport.addValidationRule(rule, ValidationTime.ON_UPDATE_TO_MODEL);
final IStatus errorStatus = rule.validate("81"); //$NON-NLS-1$
assertEquals(ValidationRuleStatus.ERROR_BLOCK_WITH_FLASH, errorStatus.getCode());
assertEquals(0, markable.getMarkersOfType(ErrorMarker.class).size());
valueBindingSupport.updateValidationStatus(rule, errorStatus);
assertEquals(1, markable.getMarkersOfType(ErrorMarker.class).size());
final IStatus okStatus = rule.validate("80"); //$NON-NLS-1$
valueBindingSupport.updateValidationStatus(rule, okStatus);
assertEquals(0, markable.getMarkersOfType(ErrorMarker.class).size());
}
/**
* As per Bug 313969
*/
public void testUsesBeanObservablesWhenBindingABean() {
final Person person = new Person("Max", "Muster"); //$NON-NLS-1$ //$NON-NLS-2$
final Address pdx = createAddress("pdx", "Portland", "97209", "USA"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
final Address fra = createAddress("fra", "Frankfurt", "60329", "DE"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
person.setAddress(pdx);
valueBindingSupport.bindToModel(person, "address.streetAndNumber"); //$NON-NLS-1$
assertEquals("pdx", person.getAddress().getStreetAndNumber()); //$NON-NLS-1$
assertEquals("pdx", pdx.getStreetAndNumber()); //$NON-NLS-1$
target.setValue("new 1"); //$NON-NLS-1$
assertEquals("new 1", person.getAddress().getStreetAndNumber()); //$NON-NLS-1$
assertEquals("new 1", pdx.getStreetAndNumber()); //$NON-NLS-1$
person.setAddress(fra);
target.setValue("new 2"); //$NON-NLS-1$
assertEquals("new 2", person.getAddress().getStreetAndNumber()); //$NON-NLS-1$
assertEquals("new 2", fra.getStreetAndNumber()); //$NON-NLS-1$
assertEquals("new 1", pdx.getStreetAndNumber()); //$NON-NLS-1$
}
public void testUpdateFromModelOnRequest2() {
final Person person = new Person("muster", "max"); //$NON-NLS-1$ //$NON-NLS-2$
valueBindingSupport.bindToModel(person, "firstname"); //$NON-NLS-1$
valueBindingSupport.updateFromModel();
assertEquals("max", person.getFirstname()); //$NON-NLS-1$
assertEquals("max", target.getValue()); //$NON-NLS-1$
target.setValue("moritz"); //$NON-NLS-1$
assertEquals("moritz", person.getFirstname()); //$NON-NLS-1$
assertEquals("moritz", target.getValue()); //$NON-NLS-1$
person.setFirstname("michel"); //$NON-NLS-1$
assertEquals("michel", person.getFirstname()); //$NON-NLS-1$
assertEquals("moritz", target.getValue()); //$NON-NLS-1$
valueBindingSupport.updateFromModel();
assertEquals("michel", person.getFirstname()); //$NON-NLS-1$
assertEquals("michel", target.getValue()); //$NON-NLS-1$
}
/**
* As per Bug 327684
*/
public void testRebindToModelNoPerfomanceDegratation() {
assertNotNull(target);
assertNotNull(model);
final ValueBindingSupport secondValueBindingSupport = new ValueBindingSupport(target, model);
final long start1 = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
secondValueBindingSupport.rebindToModel();
}
final long time1 = System.currentTimeMillis() - start1;
final long start2 = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
secondValueBindingSupport.rebindToModel();
}
final long time2 = System.currentTimeMillis() - start2;
final String msg = String.format("1st iteration: %d, 2nd iteration: %d", time1, time2); //$NON-NLS-1$
assertTrue(msg, (time2 / 2) <= time1);
}
/**
* Tests the <i>private</i> method {@code isNestedProperty(String)}.
*/
public void testIsNestedProperty() {
boolean ret = ReflectionUtils.invokeHidden(valueBindingSupport, "isNestedProperty", ""); //$NON-NLS-1$ //$NON-NLS-2$
assertFalse(ret);
ret = ReflectionUtils.invokeHidden(valueBindingSupport, "isNestedProperty", "prop"); //$NON-NLS-1$ //$NON-NLS-2$
assertFalse(ret);
ret = ReflectionUtils.invokeHidden(valueBindingSupport, "isNestedProperty", "prop1.prop2"); //$NON-NLS-1$ //$NON-NLS-2$
assertTrue(ret);
ret = ReflectionUtils.invokeHidden(valueBindingSupport, "isNestedProperty", "."); //$NON-NLS-1$ //$NON-NLS-2$
assertTrue(ret);
}
// helping methods
// ////////////////
private void assertMessageMarkers(final String... messages) {
final Collection<String> missingMessages = new ArrayList<String>(Arrays.asList(messages));
for (final IMessageMarker messageMarker : markable.getMarkersOfType(IMessageMarker.class)) {
missingMessages.remove(messageMarker.getMessage());
}
assertTrue("missing MessageMarker for " + missingMessages, missingMessages.isEmpty()); //$NON-NLS-1$
}
public static Address createAddress(final String str, final String city, final String zip, final String cc) {
final Address result = new Address();
result.setStreetAndNumber(str);
result.setTown(city);
result.setPostalCode(Integer.valueOf(zip));
result.setCountry(cc);
return result;
}
// helping clases
// ///////////////
private static class EvenNumberOfCharacters implements IValidator {
public IStatus validate(final Object value) {
if (value == null) {
return ValidationRuleStatus.ok();
}
if (value instanceof String) {
final String string = (String) value;
if (string.length() % 2 == 0) {
return ValidationRuleStatus.ok();
}
return ValidationRuleStatus.error(false, "Odd number of characters."); //$NON-NLS-1$
}
throw new ValidationFailure(getClass().getName() + " can only validate objects of type " //$NON-NLS-1$
+ String.class.getName());
}
}
private static class NotEndingWithDisaster implements IValidator {
public IStatus validate(final Object value) {
if (value == null) {
return ValidationRuleStatus.ok();
}
if (value instanceof String) {
final String string = (String) value;
if (!string.toLowerCase().endsWith("disaster")) { //$NON-NLS-1$
return ValidationRuleStatus.ok();
}
return ValidationRuleStatus.error(false, "It ends with disaster."); //$NON-NLS-1$
}
throw new ValidationFailure(getClass().getName() + " can only validate objects of type " //$NON-NLS-1$
+ String.class.getName());
}
}
private static final class AlwaysWrongValidator implements IValidator {
public IStatus validate(final Object value) {
return ValidationRuleStatus.error(false, "wrong"); //$NON-NLS-1$
}
}
}