You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BinderTest.java 47KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375
  1. package com.vaadin.data;
  2. import static org.junit.Assert.assertArrayEquals;
  3. import static org.junit.Assert.assertEquals;
  4. import static org.junit.Assert.assertFalse;
  5. import static org.junit.Assert.assertNotEquals;
  6. import static org.junit.Assert.assertNotNull;
  7. import static org.junit.Assert.assertNull;
  8. import static org.junit.Assert.assertSame;
  9. import static org.junit.Assert.assertTrue;
  10. import static org.junit.Assert.fail;
  11. import java.util.Locale;
  12. import java.util.Objects;
  13. import java.util.concurrent.atomic.AtomicBoolean;
  14. import java.util.concurrent.atomic.AtomicInteger;
  15. import java.util.concurrent.atomic.AtomicReference;
  16. import java.util.stream.Stream;
  17. import org.junit.Assert;
  18. import org.junit.Before;
  19. import org.junit.Test;
  20. import org.junit.Rule;
  21. import org.junit.rules.ExpectedException;
  22. import com.vaadin.data.Binder.Binding;
  23. import com.vaadin.data.Binder.BindingBuilder;
  24. import com.vaadin.data.converter.StringToIntegerConverter;
  25. import com.vaadin.data.validator.IntegerRangeValidator;
  26. import com.vaadin.data.validator.NotEmptyValidator;
  27. import com.vaadin.data.validator.StringLengthValidator;
  28. import com.vaadin.server.ErrorMessage;
  29. import com.vaadin.shared.ui.ErrorLevel;
  30. import com.vaadin.tests.data.bean.Person;
  31. import com.vaadin.tests.data.bean.Sex;
  32. import com.vaadin.ui.TextField;
  33. import org.apache.commons.lang.StringUtils;
  34. import org.hamcrest.CoreMatchers;
  35. public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
  36. @Rule
  37. /*
  38. * transient to avoid interfering with serialization tests that capture a
  39. * test instance in a closure
  40. */
  41. public transient ExpectedException exceptionRule = ExpectedException.none();
  42. @Before
  43. public void setUp() {
  44. binder = new Binder<>();
  45. item = new Person();
  46. item.setFirstName("Johannes");
  47. item.setAge(32);
  48. }
  49. @Test
  50. public void bindNullBean_noBeanPresent() {
  51. binder.setBean(item);
  52. assertNotNull(binder.getBean());
  53. binder.setBean(null);
  54. assertNull(binder.getBean());
  55. }
  56. @Test
  57. public void bindNullBean_FieldsAreCleared() {
  58. binder.forField(nameField).bind(Person::getFirstName,
  59. Person::setFirstName);
  60. binder.forField(ageField)
  61. .withConverter(new StringToIntegerConverter(""))
  62. .bind(Person::getAge, Person::setAge);
  63. binder.setBean(item);
  64. assertEquals("No name field value", "Johannes", nameField.getValue());
  65. assertEquals("No age field value", "32", ageField.getValue());
  66. binder.setBean(null);
  67. assertEquals("Name field not empty", "", nameField.getValue());
  68. assertEquals("Age field not empty", "", ageField.getValue());
  69. }
  70. @Test
  71. public void clearForReadBean_boundFieldsAreCleared() {
  72. binder.forField(nameField).bind(Person::getFirstName,
  73. Person::setFirstName);
  74. binder.forField(ageField)
  75. .withConverter(new StringToIntegerConverter(""))
  76. .bind(Person::getAge, Person::setAge);
  77. binder.readBean(item);
  78. assertEquals("No name field value", "Johannes", nameField.getValue());
  79. assertEquals("No age field value", "32", ageField.getValue());
  80. binder.readBean(null);
  81. assertEquals("Name field not empty", "", nameField.getValue());
  82. assertEquals("Age field not empty", "", ageField.getValue());
  83. }
  84. @Test
  85. public void clearReadOnlyField_shouldClearField() {
  86. binder.forField(nameField).bind(Person::getFirstName,
  87. Person::setFirstName);
  88. // Make name field read only
  89. nameField.setReadOnly(true);
  90. binder.setBean(item);
  91. assertEquals("No name field value", "Johannes", nameField.getValue());
  92. binder.setBean(null);
  93. assertEquals("ReadOnly field not empty", "", nameField.getValue());
  94. }
  95. @Test
  96. public void clearBean_setsHasChangesToFalse() {
  97. binder.forField(nameField).bind(Person::getFirstName,
  98. Person::setFirstName);
  99. // Make name field read only
  100. nameField.setReadOnly(true);
  101. binder.readBean(item);
  102. assertEquals("No name field value", "Johannes", nameField.getValue());
  103. nameField.setValue("James");
  104. assertTrue("Binder did not have value changes", binder.hasChanges());
  105. binder.readBean(null);
  106. assertFalse("Binder has changes after clearing all fields",
  107. binder.hasChanges());
  108. }
  109. @Test
  110. public void clearReadOnlyBinder_shouldClearFields() {
  111. binder.forField(nameField).bind(Person::getFirstName,
  112. Person::setFirstName);
  113. binder.forField(ageField)
  114. .withConverter(new StringToIntegerConverter(""))
  115. .bind(Person::getAge, Person::setAge);
  116. binder.setReadOnly(true);
  117. binder.setBean(item);
  118. binder.setBean(null);
  119. assertEquals("ReadOnly name field not empty", "", nameField.getValue());
  120. assertEquals("ReadOnly age field not empty", "", ageField.getValue());
  121. }
  122. @Test(expected = NullPointerException.class)
  123. public void bindNullField_throws() {
  124. binder.forField(null);
  125. }
  126. @Test(expected = NullPointerException.class)
  127. public void bindNullGetter_throws() {
  128. binder.bind(nameField, null, Person::setFirstName);
  129. }
  130. @Test
  131. public void fieldBound_bindItem_fieldValueUpdated() {
  132. binder.forField(nameField).bind(Person::getFirstName,
  133. Person::setFirstName);
  134. binder.setBean(item);
  135. assertEquals("Johannes", nameField.getValue());
  136. }
  137. @Test
  138. public void fieldBoundWithShortcut_bindBean_fieldValueUpdated() {
  139. bindName();
  140. assertEquals("Johannes", nameField.getValue());
  141. }
  142. @Test
  143. public void beanBound_updateFieldValue_beanValueUpdated() {
  144. binder.setBean(item);
  145. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  146. assertEquals("Johannes", nameField.getValue());
  147. nameField.setValue("Artur");
  148. assertEquals("Artur", item.getFirstName());
  149. }
  150. @Test
  151. public void bound_getBean_returnsBoundBean() {
  152. assertNull(binder.getBean());
  153. binder.setBean(item);
  154. assertSame(item, binder.getBean());
  155. }
  156. @Test
  157. public void unbound_getBean_returnsNothing() {
  158. binder.setBean(item);
  159. binder.removeBean();
  160. assertNull(binder.getBean());
  161. }
  162. @Test
  163. public void bound_changeFieldValue_beanValueUpdated() {
  164. bindName();
  165. nameField.setValue("Henri");
  166. assertEquals("Henri", item.getFirstName());
  167. }
  168. @Test
  169. public void unbound_changeFieldValue_beanValueNotUpdated() {
  170. bindName();
  171. nameField.setValue("Henri");
  172. binder.removeBean();
  173. nameField.setValue("Aleksi");
  174. assertEquals("Henri", item.getFirstName());
  175. }
  176. @Test
  177. public void bindNullSetter_valueChangesIgnored() {
  178. binder.bind(nameField, Person::getFirstName, null);
  179. binder.setBean(item);
  180. nameField.setValue("Artur");
  181. assertEquals(item.getFirstName(), "Johannes");
  182. }
  183. @Test
  184. public void bound_bindToAnotherBean_stopsUpdatingOriginal() {
  185. bindName();
  186. nameField.setValue("Leif");
  187. Person p2 = new Person();
  188. p2.setFirstName("Marlon");
  189. binder.setBean(p2);
  190. assertEquals("Marlon", nameField.getValue());
  191. assertEquals("Leif", item.getFirstName());
  192. assertSame(p2, binder.getBean());
  193. nameField.setValue("Ilia");
  194. assertEquals("Ilia", p2.getFirstName());
  195. assertEquals("Leif", item.getFirstName());
  196. }
  197. @Test
  198. public void save_unbound_noChanges() throws ValidationException {
  199. Binder<Person> binder = new Binder<>();
  200. Person person = new Person();
  201. int age = 10;
  202. person.setAge(age);
  203. binder.writeBean(person);
  204. assertEquals(age, person.getAge());
  205. }
  206. @Test
  207. public void save_bound_beanIsUpdated() throws ValidationException {
  208. Binder<Person> binder = new Binder<>();
  209. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  210. Person person = new Person();
  211. String fieldValue = "bar";
  212. nameField.setValue(fieldValue);
  213. person.setFirstName("foo");
  214. binder.writeBean(person);
  215. assertEquals(fieldValue, person.getFirstName());
  216. }
  217. @Test
  218. public void load_bound_fieldValueIsUpdated() {
  219. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  220. Person person = new Person();
  221. String name = "bar";
  222. person.setFirstName(name);
  223. binder.readBean(person);
  224. assertEquals(name, nameField.getValue());
  225. }
  226. @Test
  227. public void load_unbound_noChanges() {
  228. nameField.setValue("");
  229. Person person = new Person();
  230. String name = "bar";
  231. person.setFirstName(name);
  232. binder.readBean(person);
  233. assertEquals("", nameField.getValue());
  234. }
  235. protected void bindName() {
  236. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  237. binder.setBean(item);
  238. }
  239. @Test
  240. public void binding_with_null_representation() {
  241. String nullRepresentation = "Some arbitrary text";
  242. String realName = "John";
  243. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  244. null);
  245. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  246. .bind(Person::getFirstName, Person::setFirstName);
  247. // Bind a person with null value and check that null representation is
  248. // used
  249. binder.setBean(namelessPerson);
  250. assertEquals(
  251. "Null value from bean was not converted to explicit null representation",
  252. nullRepresentation, nameField.getValue());
  253. // Verify that changes are applied to bean
  254. nameField.setValue(realName);
  255. assertEquals(
  256. "Bean was not correctly updated from a change in the field",
  257. realName, namelessPerson.getFirstName());
  258. // Verify conversion back to null
  259. nameField.setValue(nullRepresentation);
  260. assertEquals(
  261. "Two-way null representation did not change value back to null",
  262. null, namelessPerson.getFirstName());
  263. }
  264. @Test
  265. public void binding_with_default_null_representation() {
  266. TextField nullTextField = new TextField() {
  267. @Override
  268. public String getEmptyValue() {
  269. return "null";
  270. }
  271. };
  272. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  273. null);
  274. binder.bind(nullTextField, Person::getFirstName, Person::setFirstName);
  275. binder.setBean(namelessPerson);
  276. assertTrue(nullTextField.isEmpty());
  277. assertEquals("null", namelessPerson.getFirstName());
  278. // Change value, see that textfield is not empty and bean is updated.
  279. nullTextField.setValue("");
  280. assertFalse(nullTextField.isEmpty());
  281. assertEquals("First name of person was not properly updated", "",
  282. namelessPerson.getFirstName());
  283. // Verify that default null representation does not map back to null
  284. nullTextField.setValue("null");
  285. assertTrue(nullTextField.isEmpty());
  286. assertEquals("Default one-way null representation failed.", "null",
  287. namelessPerson.getFirstName());
  288. }
  289. @Test
  290. public void binding_with_null_representation_value_not_null() {
  291. String nullRepresentation = "Some arbitrary text";
  292. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  293. .bind(Person::getFirstName, Person::setFirstName);
  294. assertFalse("First name in item should not be null",
  295. Objects.isNull(item.getFirstName()));
  296. binder.setBean(item);
  297. assertEquals("Field value was not set correctly", item.getFirstName(),
  298. nameField.getValue());
  299. }
  300. @Test
  301. public void withConverter_disablesDefaulNullRepresentation() {
  302. Integer customNullConverter = 0;
  303. binder.forField(ageField).withNullRepresentation("foo")
  304. .withConverter(new StringToIntegerConverter(""))
  305. .withConverter(age -> age,
  306. age -> age == null ? customNullConverter : age)
  307. .bind(Person::getSalary, Person::setSalary);
  308. binder.setBean(item);
  309. assertEquals(customNullConverter.toString(), ageField.getValue());
  310. Integer salary = 11;
  311. ageField.setValue(salary.toString());
  312. assertEquals(11, salary.intValue());
  313. }
  314. @Test
  315. public void beanBinder_nullRepresentationIsNotDisabled() {
  316. Binder<Person> binder = new Binder<>(Person.class);
  317. binder.forField(nameField).bind("firstName");
  318. Person person = new Person();
  319. binder.setBean(person);
  320. assertEquals("", nameField.getValue());
  321. }
  322. @Test
  323. public void beanBinder_withConverter_nullRepresentationIsNotDisabled() {
  324. String customNullPointerRepresentation = "foo";
  325. Binder<Person> binder = new Binder<>(Person.class);
  326. binder.forField(nameField)
  327. .withConverter(value -> value,
  328. value -> value == null ? customNullPointerRepresentation
  329. : value)
  330. .bind("firstName");
  331. Person person = new Person();
  332. binder.setBean(person);
  333. assertEquals(customNullPointerRepresentation, nameField.getValue());
  334. }
  335. @Test
  336. public void withValidator_doesNotDisablesDefaulNullRepresentation() {
  337. String nullRepresentation = "foo";
  338. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  339. .withValidator(new NotEmptyValidator<>(""))
  340. .bind(Person::getFirstName, Person::setFirstName);
  341. item.setFirstName(null);
  342. binder.setBean(item);
  343. assertEquals(nullRepresentation, nameField.getValue());
  344. String newValue = "bar";
  345. nameField.setValue(newValue);
  346. assertEquals(newValue, item.getFirstName());
  347. }
  348. @Test
  349. public void setRequired_withErrorMessage_fieldGetsRequiredIndicatorAndValidator() {
  350. TextField textField = new TextField();
  351. assertFalse(textField.isRequiredIndicatorVisible());
  352. BindingBuilder<Person, String> binding = binder.forField(textField);
  353. assertFalse(textField.isRequiredIndicatorVisible());
  354. binding.asRequired("foobar");
  355. assertTrue(textField.isRequiredIndicatorVisible());
  356. binding.bind(Person::getFirstName, Person::setFirstName);
  357. binder.setBean(item);
  358. assertNull(textField.getErrorMessage());
  359. textField.setValue(textField.getEmptyValue());
  360. ErrorMessage errorMessage = textField.getErrorMessage();
  361. assertNotNull(errorMessage);
  362. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  363. textField.setValue("value");
  364. assertNull(textField.getErrorMessage());
  365. assertTrue(textField.isRequiredIndicatorVisible());
  366. }
  367. @Test
  368. public void readNullBeanRemovesError() {
  369. TextField textField = new TextField();
  370. binder.forField(textField).asRequired("foobar")
  371. .bind(Person::getFirstName, Person::setFirstName);
  372. assertTrue(textField.isRequiredIndicatorVisible());
  373. assertNull(textField.getErrorMessage());
  374. binder.readBean(item);
  375. assertNull(textField.getErrorMessage());
  376. textField.setValue(textField.getEmptyValue());
  377. assertTrue(textField.isRequiredIndicatorVisible());
  378. assertNotNull(textField.getErrorMessage());
  379. binder.readBean(null);
  380. assertTrue(textField.isRequiredIndicatorVisible());
  381. assertNull(textField.getErrorMessage());
  382. }
  383. @Test
  384. public void setRequired_withErrorMessageProvider_fieldGetsRequiredIndicatorAndValidator() {
  385. TextField textField = new TextField();
  386. textField.setLocale(Locale.CANADA);
  387. assertFalse(textField.isRequiredIndicatorVisible());
  388. BindingBuilder<Person, String> binding = binder.forField(textField);
  389. assertFalse(textField.isRequiredIndicatorVisible());
  390. AtomicInteger invokes = new AtomicInteger();
  391. binding.asRequired(context -> {
  392. invokes.incrementAndGet();
  393. assertSame(Locale.CANADA, context.getLocale().get());
  394. return "foobar";
  395. });
  396. assertTrue(textField.isRequiredIndicatorVisible());
  397. binding.bind(Person::getFirstName, Person::setFirstName);
  398. binder.setBean(item);
  399. assertNull(textField.getErrorMessage());
  400. assertEquals(0, invokes.get());
  401. textField.setValue(textField.getEmptyValue());
  402. ErrorMessage errorMessage = textField.getErrorMessage();
  403. assertNotNull(errorMessage);
  404. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  405. // validation is done for all changed bindings once.
  406. assertEquals(1, invokes.get());
  407. textField.setValue("value");
  408. assertNull(textField.getErrorMessage());
  409. assertTrue(textField.isRequiredIndicatorVisible());
  410. }
  411. @Test
  412. public void setRequired_withCustomValidator_fieldGetsRequiredIndicatorAndValidator() {
  413. TextField textField = new TextField();
  414. textField.setLocale(Locale.CANADA);
  415. assertFalse(textField.isRequiredIndicatorVisible());
  416. BindingBuilder<Person, String> binding = binder.forField(textField);
  417. assertFalse(textField.isRequiredIndicatorVisible());
  418. AtomicInteger invokes = new AtomicInteger();
  419. Validator<String> customRequiredValidator = (value, context) -> {
  420. invokes.incrementAndGet();
  421. if (StringUtils.isBlank(value)) {
  422. return ValidationResult.error("Input is required.");
  423. }
  424. return ValidationResult.ok();
  425. };
  426. binding.asRequired(customRequiredValidator);
  427. assertTrue(textField.isRequiredIndicatorVisible());
  428. binding.bind(Person::getFirstName, Person::setFirstName);
  429. binder.setBean(item);
  430. assertNull(textField.getErrorMessage());
  431. assertEquals(1, invokes.get());
  432. textField.setValue(" ");
  433. ErrorMessage errorMessage = textField.getErrorMessage();
  434. assertNotNull(errorMessage);
  435. assertEquals("Input&#32;is&#32;required&#46;",
  436. errorMessage.getFormattedHtmlMessage());
  437. // validation is done for all changed bindings once.
  438. assertEquals(2, invokes.get());
  439. textField.setValue("value");
  440. assertNull(textField.getErrorMessage());
  441. assertTrue(textField.isRequiredIndicatorVisible());
  442. }
  443. @Test
  444. public void setRequired_withCustomValidator_modelConverterBeforeValidator() {
  445. TextField textField = new TextField();
  446. textField.setLocale(Locale.CANADA);
  447. assertFalse(textField.isRequiredIndicatorVisible());
  448. Converter<String, String> stringBasicPreProcessingConverter = new Converter<String, String>() {
  449. @Override
  450. public Result<String> convertToModel(String value,
  451. ValueContext context) {
  452. if (StringUtils.isBlank(value)) {
  453. return Result.ok(null);
  454. }
  455. return Result.ok(StringUtils.trim(value));
  456. }
  457. @Override
  458. public String convertToPresentation(String value,
  459. ValueContext context) {
  460. if (value == null) {
  461. return "";
  462. }
  463. return value;
  464. }
  465. };
  466. AtomicInteger invokes = new AtomicInteger();
  467. Validator<String> customRequiredValidator = (value, context) -> {
  468. invokes.incrementAndGet();
  469. if (value == null) {
  470. return ValidationResult.error("Input required.");
  471. }
  472. return ValidationResult.ok();
  473. };
  474. binder.forField(textField)
  475. .withConverter(stringBasicPreProcessingConverter)
  476. .asRequired(customRequiredValidator)
  477. .bind(Person::getFirstName, Person::setFirstName);
  478. binder.setBean(item);
  479. assertNull(textField.getErrorMessage());
  480. assertEquals(1, invokes.get());
  481. textField.setValue(" ");
  482. ErrorMessage errorMessage = textField.getErrorMessage();
  483. assertNotNull(errorMessage);
  484. assertEquals("Input&#32;required&#46;",
  485. errorMessage.getFormattedHtmlMessage());
  486. // validation is done for all changed bindings once.
  487. assertEquals(2, invokes.get());
  488. textField.setValue("value");
  489. assertNull(textField.getErrorMessage());
  490. assertTrue(textField.isRequiredIndicatorVisible());
  491. }
  492. @Test
  493. public void validationStatusHandler_onlyRunForChangedField() {
  494. TextField firstNameField = new TextField();
  495. TextField lastNameField = new TextField();
  496. AtomicInteger invokes = new AtomicInteger();
  497. binder.forField(firstNameField)
  498. .withValidator(new NotEmptyValidator<>(""))
  499. .withValidationStatusHandler(
  500. validationStatus -> invokes.addAndGet(1))
  501. .bind(Person::getFirstName, Person::setFirstName);
  502. binder.forField(lastNameField)
  503. .withValidator(new NotEmptyValidator<>(""))
  504. .bind(Person::getLastName, Person::setLastName);
  505. binder.setBean(item);
  506. // setting the bean causes 2:
  507. assertEquals(2, invokes.get());
  508. lastNameField.setValue("");
  509. assertEquals(2, invokes.get());
  510. firstNameField.setValue("");
  511. assertEquals(3, invokes.get());
  512. binder.removeBean();
  513. Person person = new Person();
  514. person.setFirstName("a");
  515. person.setLastName("a");
  516. binder.readBean(person);
  517. // reading from a bean causes 2:
  518. assertEquals(5, invokes.get());
  519. lastNameField.setValue("");
  520. assertEquals(5, invokes.get());
  521. firstNameField.setValue("");
  522. assertEquals(6, invokes.get());
  523. }
  524. @Test(expected = IllegalStateException.class)
  525. public void noArgsConstructor_stringBind_throws() {
  526. binder.bind(new TextField(), "firstName");
  527. }
  528. @Test
  529. public void setReadOnly_unboundBinder() {
  530. binder.forField(nameField).bind(Person::getFirstName,
  531. Person::setFirstName);
  532. binder.forField(ageField);
  533. binder.setReadOnly(true);
  534. assertTrue(nameField.isReadOnly());
  535. assertFalse(ageField.isReadOnly());
  536. binder.setReadOnly(false);
  537. assertFalse(nameField.isReadOnly());
  538. assertFalse(ageField.isReadOnly());
  539. }
  540. @Test
  541. public void setReadOnly_boundBinder() {
  542. binder.forField(nameField).bind(Person::getFirstName,
  543. Person::setFirstName);
  544. binder.forField(ageField)
  545. .withConverter(new StringToIntegerConverter(""))
  546. .bind(Person::getAge, Person::setAge);
  547. binder.setBean(new Person());
  548. binder.setReadOnly(true);
  549. assertTrue(nameField.isReadOnly());
  550. assertTrue(ageField.isReadOnly());
  551. binder.setReadOnly(false);
  552. assertFalse(nameField.isReadOnly());
  553. assertFalse(ageField.isReadOnly());
  554. }
  555. @Test
  556. public void setReadOnly_binderLoadedByReadBean() {
  557. binder.forField(nameField).bind(Person::getFirstName,
  558. Person::setFirstName);
  559. binder.forField(ageField)
  560. .withConverter(new StringToIntegerConverter(""))
  561. .bind(Person::getAge, Person::setAge);
  562. binder.readBean(new Person());
  563. binder.setReadOnly(true);
  564. assertTrue(nameField.isReadOnly());
  565. assertTrue(ageField.isReadOnly());
  566. binder.setReadOnly(false);
  567. assertFalse(nameField.isReadOnly());
  568. assertFalse(ageField.isReadOnly());
  569. }
  570. @Test
  571. public void setReadonlyShouldIgnoreBindingsWithNullSetter() {
  572. binder.bind(nameField, Person::getFirstName, null);
  573. binder.forField(ageField)
  574. .withConverter(new StringToIntegerConverter(""))
  575. .bind(Person::getAge, Person::setAge);
  576. binder.setReadOnly(true);
  577. assertTrue("Name field should be ignored but should be readonly",
  578. nameField.isReadOnly());
  579. assertTrue("Age field should be readonly", ageField.isReadOnly());
  580. binder.setReadOnly(false);
  581. assertTrue("Name field should be ignored and should remain readonly",
  582. nameField.isReadOnly());
  583. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  584. nameField.setReadOnly(false);
  585. binder.setReadOnly(false);
  586. assertFalse("Name field should be ignored and remain not readonly",
  587. nameField.isReadOnly());
  588. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  589. binder.setReadOnly(true);
  590. assertFalse("Name field should be ignored and remain not readonly",
  591. nameField.isReadOnly());
  592. assertTrue("Age field should be readonly", ageField.isReadOnly());
  593. }
  594. @Test
  595. public void isValidTest_bound_binder() {
  596. binder.forField(nameField)
  597. .withValidator(Validator.from(
  598. name -> !name.equals("fail field validation"), ""))
  599. .bind(Person::getFirstName, Person::setFirstName);
  600. binder.withValidator(Validator.from(
  601. person -> !person.getFirstName().equals("fail bean validation"),
  602. ""));
  603. binder.setBean(item);
  604. assertTrue(binder.isValid());
  605. nameField.setValue("fail field validation");
  606. assertFalse(binder.isValid());
  607. nameField.setValue("");
  608. assertTrue(binder.isValid());
  609. nameField.setValue("fail bean validation");
  610. assertFalse(binder.isValid());
  611. }
  612. @Test
  613. public void isValidTest_unbound_binder() {
  614. binder.forField(nameField)
  615. .withValidator(Validator.from(
  616. name -> !name.equals("fail field validation"), ""))
  617. .bind(Person::getFirstName, Person::setFirstName);
  618. assertTrue(binder.isValid());
  619. nameField.setValue("fail field validation");
  620. assertFalse(binder.isValid());
  621. nameField.setValue("");
  622. assertTrue(binder.isValid());
  623. }
  624. @Test(expected = IllegalStateException.class)
  625. public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
  626. binder.forField(nameField).bind(Person::getFirstName,
  627. Person::setFirstName);
  628. binder.withValidator(Validator.from(
  629. person -> !person.getFirstName().equals("fail bean validation"),
  630. ""));
  631. binder.isValid();
  632. }
  633. @Test
  634. public void getFields_returnsFields() {
  635. assertEquals(0, binder.getFields().count());
  636. binder.forField(nameField).bind(Person::getFirstName,
  637. Person::setFirstName);
  638. assertStreamEquals(Stream.of(nameField), binder.getFields());
  639. binder.forField(ageField)
  640. .withConverter(new StringToIntegerConverter(""))
  641. .bind(Person::getAge, Person::setAge);
  642. assertStreamEquals(Stream.of(nameField, ageField), binder.getFields());
  643. }
  644. private void assertStreamEquals(Stream<?> s1, Stream<?> s2) {
  645. assertArrayEquals(s1.toArray(), s2.toArray());
  646. }
  647. @Test
  648. public void multiple_calls_to_same_binding_builder() {
  649. String stringLength = "String length failure";
  650. String conversion = "Conversion failed";
  651. String ageLimit = "Age not in valid range";
  652. BindingValidationStatus validation;
  653. binder = new Binder<>(Person.class);
  654. BindingBuilder builder = binder.forField(ageField);
  655. builder.withValidator(new StringLengthValidator(stringLength, 0, 3));
  656. builder.withConverter(new StringToIntegerConverter(conversion));
  657. builder.withValidator(new IntegerRangeValidator(ageLimit, 3, 150));
  658. Binding<Person, ?> bind = builder.bind("age");
  659. binder.setBean(item);
  660. ageField.setValue("123123");
  661. validation = bind.validate();
  662. assertTrue(validation.isError());
  663. assertEquals(stringLength, validation.getMessage().get());
  664. ageField.setValue("age");
  665. validation = bind.validate();
  666. assertTrue(validation.isError());
  667. assertEquals(conversion, validation.getMessage().get());
  668. ageField.setValue("256");
  669. validation = bind.validate();
  670. assertTrue(validation.isError());
  671. assertEquals(ageLimit, validation.getMessage().get());
  672. ageField.setValue("30");
  673. validation = bind.validate();
  674. assertFalse(validation.isError());
  675. assertEquals(30, item.getAge());
  676. }
  677. @Test
  678. public void remove_field_binding() {
  679. binder.forField(ageField)
  680. .withConverter(new StringToIntegerConverter("Can't convert"))
  681. .bind(Person::getAge, Person::setAge);
  682. // Test that the binding does work
  683. assertTrue("Field not initially empty", ageField.isEmpty());
  684. binder.setBean(item);
  685. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  686. ageField.getValue());
  687. binder.setBean(null);
  688. assertTrue("Field not cleared", ageField.isEmpty());
  689. // Remove the binding
  690. binder.removeBinding(ageField);
  691. // Test that it does not work anymore
  692. binder.setBean(item);
  693. assertNotEquals("Binding was not removed",
  694. String.valueOf(item.getAge()), ageField.getValue());
  695. }
  696. @Test
  697. public void remove_propertyname_binding() {
  698. // Use a bean aware binder
  699. Binder<Person> binder = new Binder<>(Person.class);
  700. binder.bind(nameField, "firstName");
  701. // Test that the binding does work
  702. assertTrue("Field not initially empty", nameField.isEmpty());
  703. binder.setBean(item);
  704. assertEquals("Binding did not work", item.getFirstName(),
  705. nameField.getValue());
  706. binder.setBean(null);
  707. assertTrue("Field not cleared", nameField.isEmpty());
  708. // Remove the binding
  709. binder.removeBinding("firstName");
  710. // Test that it does not work anymore
  711. binder.setBean(item);
  712. assertNotEquals("Binding was not removed", item.getFirstName(),
  713. nameField.getValue());
  714. }
  715. @Test
  716. public void remove_binding() {
  717. Binding<Person, Integer> binding = binder.forField(ageField)
  718. .withConverter(new StringToIntegerConverter("Can't convert"))
  719. .bind(Person::getAge, Person::setAge);
  720. // Test that the binding does work
  721. assertTrue("Field not initially empty", ageField.isEmpty());
  722. binder.setBean(item);
  723. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  724. ageField.getValue());
  725. binder.setBean(null);
  726. assertTrue("Field not cleared", ageField.isEmpty());
  727. // Remove the binding
  728. binder.removeBinding(binding);
  729. // Test that it does not work anymore
  730. binder.setBean(item);
  731. assertNotEquals("Binding was not removed",
  732. String.valueOf(item.getAge()), ageField.getValue());
  733. }
  734. @Test
  735. public void remove_binding_fromFieldValueChangeListener() {
  736. // Add listener before bind to make sure it will be executed first.
  737. nameField.addValueChangeListener(e -> {
  738. if (e.getValue() == "REMOVE") {
  739. binder.removeBinding(nameField);
  740. }
  741. });
  742. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  743. binder.setBean(item);
  744. nameField.setValue("REMOVE");
  745. // Removed binding should not update bean.
  746. assertNotEquals("REMOVE", item.getFirstName());
  747. }
  748. @Test
  749. public void beanvalidation_two_fields_not_equal() {
  750. TextField lastNameField = new TextField();
  751. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  752. item.setLastName("Valid");
  753. binder.setBean(item);
  754. assertFalse("Should not have changes initially", binder.hasChanges());
  755. assertTrue("Should be ok initially", binder.validate().isOk());
  756. assertNotEquals("First name and last name are not same initially",
  757. item.getFirstName(), item.getLastName());
  758. nameField.setValue("Invalid");
  759. assertFalse("First name change not handled", binder.hasChanges());
  760. assertTrue(
  761. "Changing first name to something else than last name should be ok",
  762. binder.validate().isOk());
  763. lastNameField.setValue("Invalid");
  764. assertTrue("Last name should not be saved yet", binder.hasChanges());
  765. assertFalse("Binder validation should fail with pending illegal value",
  766. binder.validate().isOk());
  767. assertNotEquals("Illegal last name should not be stored to bean",
  768. item.getFirstName(), item.getLastName());
  769. nameField.setValue("Valid");
  770. assertFalse("With new first name both changes should be saved",
  771. binder.hasChanges());
  772. assertTrue("Everything should be ok for 'Valid Invalid'",
  773. binder.validate().isOk());
  774. assertNotEquals("First name and last name should never match.",
  775. item.getFirstName(), item.getLastName());
  776. }
  777. @Test
  778. public void beanvalidation_initially_broken_bean() {
  779. TextField lastNameField = new TextField();
  780. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  781. item.setLastName(item.getFirstName());
  782. binder.setBean(item);
  783. assertFalse(binder.isValid());
  784. assertFalse(binder.validate().isOk());
  785. }
  786. @Test(expected = IllegalStateException.class)
  787. public void beanvalidation_isValid_throws_with_readBean() {
  788. TextField lastNameField = new TextField();
  789. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  790. binder.readBean(item);
  791. assertTrue(binder.isValid());
  792. }
  793. @Test(expected = IllegalStateException.class)
  794. public void beanvalidation_validate_throws_with_readBean() {
  795. TextField lastNameField = new TextField();
  796. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  797. binder.readBean(item);
  798. assertTrue(binder.validate().isOk());
  799. }
  800. protected void setBeanValidationFirstNameNotEqualsLastName(
  801. TextField firstNameField, TextField lastNameField) {
  802. binder.bind(firstNameField, Person::getFirstName, Person::setFirstName);
  803. binder.forField(lastNameField)
  804. .withValidator(t -> !"foo".equals(t),
  805. "Last name cannot be 'foo'")
  806. .bind(Person::getLastName, Person::setLastName);
  807. binder.withValidator(p -> !p.getFirstName().equals(p.getLastName()),
  808. "First name and last name can't be the same");
  809. }
  810. static class MyBindingHandler implements BindingValidationStatusHandler {
  811. boolean expectingError = false;
  812. int callCount = 0;
  813. @Override
  814. public void statusChange(BindingValidationStatus<?> statusChange) {
  815. ++callCount;
  816. if (expectingError) {
  817. assertTrue("Expecting error", statusChange.isError());
  818. } else {
  819. assertFalse("Unexpected error", statusChange.isError());
  820. }
  821. }
  822. }
  823. @Test
  824. public void execute_binding_status_handler_from_binder_status_handler() {
  825. MyBindingHandler bindingHandler = new MyBindingHandler();
  826. binder.forField(nameField)
  827. .withValidator(t -> !t.isEmpty(), "No empty values.")
  828. .withValidationStatusHandler(bindingHandler)
  829. .bind(Person::getFirstName, Person::setFirstName);
  830. String ageError = "CONVERSIONERROR";
  831. binder.forField(ageField)
  832. .withConverter(new StringToIntegerConverter(ageError))
  833. .bind(Person::getAge, Person::setAge);
  834. binder.setValidationStatusHandler(
  835. status -> status.notifyBindingValidationStatusHandlers());
  836. String initialName = item.getFirstName();
  837. int initialAge = item.getAge();
  838. binder.setBean(item);
  839. // Test specific error handling.
  840. bindingHandler.expectingError = true;
  841. nameField.setValue("");
  842. // Test default error handling.
  843. ageField.setValue("foo");
  844. assertTrue("Component error does not contain error message",
  845. ageField.getComponentError().getFormattedHtmlMessage()
  846. .contains(ageError));
  847. // Restore values and test no errors.
  848. ageField.setValue(String.valueOf(initialAge));
  849. assertNull("There should be no component error",
  850. ageField.getComponentError());
  851. bindingHandler.expectingError = false;
  852. nameField.setValue(initialName);
  853. // Assert that the handler was called.
  854. assertEquals(
  855. "Unexpected callCount to binding validation status handler", 6,
  856. bindingHandler.callCount);
  857. }
  858. @Test
  859. public void removed_binding_not_updates_value() {
  860. Binding<Person, Integer> binding = binder.forField(ageField)
  861. .withConverter(new StringToIntegerConverter("Can't convert"))
  862. .bind(Person::getAge, Person::setAge);
  863. binder.setBean(item);
  864. String modifiedAge = String.valueOf(item.getAge() + 10);
  865. String ageBeforeUnbind = String.valueOf(item.getAge());
  866. binder.removeBinding(binding);
  867. ageField.setValue(modifiedAge);
  868. assertEquals("Binding still affects bean even after unbind",
  869. ageBeforeUnbind, String.valueOf(item.getAge()));
  870. }
  871. @Test
  872. public void info_validator_not_considered_error() {
  873. String infoMessage = "Young";
  874. binder.forField(ageField)
  875. .withConverter(new StringToIntegerConverter("Can't convert"))
  876. .withValidator(i -> i > 5, infoMessage, ErrorLevel.INFO)
  877. .bind(Person::getAge, Person::setAge);
  878. binder.setBean(item);
  879. ageField.setValue("3");
  880. assertEquals(infoMessage,
  881. ageField.getComponentError().getFormattedHtmlMessage());
  882. assertEquals(ErrorLevel.INFO,
  883. ageField.getComponentError().getErrorLevel());
  884. assertEquals(3, item.getAge());
  885. }
  886. @Test
  887. public void two_asRequired_fields_without_initial_values() {
  888. binder.forField(nameField).asRequired("Empty name").bind(p -> "",
  889. (p, s) -> {
  890. });
  891. binder.forField(ageField).asRequired("Empty age").bind(p -> "",
  892. (p, s) -> {
  893. });
  894. binder.setBean(item);
  895. assertNull("Initially there should be no errors",
  896. nameField.getComponentError());
  897. assertNull("Initially there should be no errors",
  898. ageField.getComponentError());
  899. nameField.setValue("Foo");
  900. assertNull("Name with a value should not be an error",
  901. nameField.getComponentError());
  902. assertNull(
  903. "Age field should not be in error, since it has not been modified.",
  904. ageField.getComponentError());
  905. nameField.setValue("");
  906. assertNotNull("Empty name should now be in error.",
  907. nameField.getComponentError());
  908. assertNull("Age field should still be ok.",
  909. ageField.getComponentError());
  910. }
  911. @Test
  912. public void refreshValueFromBean() {
  913. Binding<Person, String> binding = binder.bind(nameField,
  914. Person::getFirstName, Person::setFirstName);
  915. binder.readBean(item);
  916. assertEquals("Name should be read from the item", item.getFirstName(),
  917. nameField.getValue());
  918. nameField.setValue("foo");
  919. assertNotEquals("Name should be different from the item",
  920. item.getFirstName(), nameField.getValue());
  921. binding.read(item);
  922. assertEquals("Name should be read again from the item",
  923. item.getFirstName(), nameField.getValue());
  924. }
  925. @Test(expected = IllegalArgumentException.class)
  926. public void remove_binding_from_different_binder() {
  927. Binder<Person> anotherBinder = new Binder<>();
  928. Binding<Person, String> binding = anotherBinder.bind(nameField,
  929. Person::getFirstName, Person::setFirstName);
  930. binder.removeBinding(binding);
  931. }
  932. @Test(expected = IllegalStateException.class)
  933. public void bindWithNullSetterSetReadWrite() {
  934. Binding<Person, String> binding = binder.bind(nameField,
  935. Person::getFirstName, null);
  936. binding.setReadOnly(false);
  937. }
  938. @Test
  939. public void bindWithNullSetterShouldMarkFieldAsReadonly() {
  940. Binding<Person, String> nameBinding = binder.bind(nameField,
  941. Person::getFirstName, null);
  942. binder.forField(ageField)
  943. .withConverter(new StringToIntegerConverter(""))
  944. .bind(Person::getAge, Person::setAge);
  945. assertTrue("Name field should be readonly", nameField.isReadOnly());
  946. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  947. assertTrue("Binding should be marked readonly",
  948. nameBinding.isReadOnly());
  949. }
  950. @Test
  951. public void setReadOnly_binding() {
  952. Binding<Person, String> binding = binder.bind(nameField,
  953. Person::getFirstName, Person::setFirstName);
  954. assertFalse("Binding should not be readonly", binding.isReadOnly());
  955. assertFalse("Name field should not be readonly",
  956. nameField.isReadOnly());
  957. binding.setReadOnly(true);
  958. assertTrue("Binding should be readonly", binding.isReadOnly());
  959. assertTrue("Name field should be readonly", nameField.isReadOnly());
  960. }
  961. @Test
  962. public void conversionWithLocaleBasedErrorMessage() {
  963. String fiError = "VIRHE";
  964. String otherError = "ERROR";
  965. binder.forField(ageField).withConverter(new StringToIntegerConverter(
  966. context -> context.getLocale().map(Locale::getLanguage)
  967. .orElse("en").equals("fi") ? fiError : otherError))
  968. .bind(Person::getAge, Person::setAge);
  969. binder.setBean(item);
  970. ageField.setValue("not a number");
  971. assertEquals(otherError,
  972. ageField.getErrorMessage().getFormattedHtmlMessage());
  973. ageField.setLocale(new Locale("fi"));
  974. // Re-validate to get the error message with correct locale
  975. binder.validate();
  976. assertEquals(fiError,
  977. ageField.getErrorMessage().getFormattedHtmlMessage());
  978. }
  979. @Test
  980. public void valueChangeListenerOrder() {
  981. AtomicBoolean beanSet = new AtomicBoolean();
  982. nameField.addValueChangeListener(e -> {
  983. if (!beanSet.get()) {
  984. assertEquals("Value in bean updated earlier than expected",
  985. e.getOldValue(), item.getFirstName());
  986. }
  987. });
  988. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  989. nameField.addValueChangeListener(e -> {
  990. if (!beanSet.get()) {
  991. assertEquals("Value in bean not updated when expected",
  992. e.getValue(), item.getFirstName());
  993. }
  994. });
  995. beanSet.set(true);
  996. binder.setBean(item);
  997. beanSet.set(false);
  998. nameField.setValue("Foo");
  999. }
  1000. @Test
  1001. public void nonSymetricValue_setBean_writtenToBean() {
  1002. binder.bind(nameField, Person::getLastName, Person::setLastName);
  1003. assertNull(item.getLastName());
  1004. binder.setBean(item);
  1005. assertEquals("", item.getLastName());
  1006. }
  1007. @Test
  1008. public void nonSymmetricValue_readBean_beanNotTouched() {
  1009. binder.bind(nameField, Person::getLastName, Person::setLastName);
  1010. binder.addValueChangeListener(
  1011. event -> fail("No value change event should be fired"));
  1012. assertNull(item.getLastName());
  1013. binder.readBean(item);
  1014. assertNull(item.getLastName());
  1015. }
  1016. @Test
  1017. public void symetricValue_setBean_beanNotUpdated() {
  1018. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  1019. binder.setBean(new Person() {
  1020. @Override
  1021. public String getFirstName() {
  1022. return "First";
  1023. }
  1024. @Override
  1025. public void setFirstName(String firstName) {
  1026. fail("Setter should not be called");
  1027. }
  1028. });
  1029. }
  1030. @Test
  1031. public void nullRejetingField_nullValue_wrappedExceptionMentionsNullRepresentation() {
  1032. TextField field = createNullAnd42RejectingFieldWithEmptyValue("");
  1033. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1034. field);
  1035. exceptionRule.expect(IllegalStateException.class);
  1036. exceptionRule.expectMessage("null representation");
  1037. exceptionRule.expectCause(CoreMatchers.isA(NullPointerException.class));
  1038. binder.readBean(new AtomicReference<>());
  1039. }
  1040. @Test
  1041. public void nullRejetingField_otherRejectedValue_originalExceptionIsThrown() {
  1042. TextField field = createNullAnd42RejectingFieldWithEmptyValue("");
  1043. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1044. field);
  1045. exceptionRule.expect(IllegalArgumentException.class);
  1046. exceptionRule.expectMessage("42");
  1047. binder.readBean(new AtomicReference<>(Integer.valueOf(42)));
  1048. }
  1049. @Test(expected = NullPointerException.class)
  1050. public void nullAcceptingField_nullValue_originalExceptionIsThrown() {
  1051. /*
  1052. * Edge case with a field that throws for null but has null as the empty
  1053. * value. This is most likely the case if the field doesn't explicitly
  1054. * reject null values but is instead somehow broken so that any value is
  1055. * rejected.
  1056. */
  1057. TextField field = createNullAnd42RejectingFieldWithEmptyValue(null);
  1058. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1059. field);
  1060. binder.readBean(new AtomicReference<>(null));
  1061. }
  1062. private TextField createNullAnd42RejectingFieldWithEmptyValue(
  1063. String emptyValue) {
  1064. return new TextField() {
  1065. @Override
  1066. public void setValue(String value) {
  1067. if (value == null) {
  1068. throw new NullPointerException("Null value");
  1069. } else if ("42".equals(value)) {
  1070. throw new IllegalArgumentException("42 is not allowed");
  1071. }
  1072. super.setValue(value);
  1073. }
  1074. @Override
  1075. public String getEmptyValue() {
  1076. return emptyValue;
  1077. }
  1078. };
  1079. }
  1080. private Binder<AtomicReference<Integer>> createIntegerConverterBinder(
  1081. TextField field) {
  1082. Binder<AtomicReference<Integer>> binder = new Binder<>();
  1083. binder.forField(field)
  1084. .withConverter(new StringToIntegerConverter("Must have number"))
  1085. .bind(AtomicReference::get, AtomicReference::set);
  1086. return binder;
  1087. }
  1088. }