選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

BinderTest.java 57KB

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