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.

Binder.java 65KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.data;
  17. import java.io.Serializable;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.IdentityHashMap;
  23. import java.util.LinkedHashSet;
  24. import java.util.List;
  25. import java.util.Locale;
  26. import java.util.Map;
  27. import java.util.Objects;
  28. import java.util.Optional;
  29. import java.util.Set;
  30. import java.util.stream.Collectors;
  31. import com.vaadin.data.HasValue.ValueChangeEvent;
  32. import com.vaadin.data.util.converter.Converter;
  33. import com.vaadin.data.util.converter.StringToIntegerConverter;
  34. import com.vaadin.data.util.converter.ValueContext;
  35. import com.vaadin.event.EventRouter;
  36. import com.vaadin.server.ErrorMessage;
  37. import com.vaadin.server.SerializableBiConsumer;
  38. import com.vaadin.server.SerializableFunction;
  39. import com.vaadin.server.SerializablePredicate;
  40. import com.vaadin.server.UserError;
  41. import com.vaadin.shared.Registration;
  42. import com.vaadin.ui.AbstractComponent;
  43. import com.vaadin.ui.Component;
  44. import com.vaadin.ui.Label;
  45. import com.vaadin.ui.UI;
  46. /**
  47. * Connects one or more {@code Field} components to properties of a backing data
  48. * type such as a bean type. With a binder, input components can be grouped
  49. * together into forms to easily create and update business objects with little
  50. * explicit logic needed to move data between the UI and the data layers of the
  51. * application.
  52. * <p>
  53. * A binder is a collection of <i>bindings</i>, each representing the mapping of
  54. * a single field, through converters and validators, to a backing property.
  55. * <p>
  56. * A binder instance can be bound to a single bean instance at a time, but can
  57. * be rebound as needed. This allows usage patterns like a <i>master-details</i>
  58. * view, where a select component is used to pick the bean to edit.
  59. * <p>
  60. * Bean level validators can be added using the
  61. * {@link #withValidator(Validator)} method and will be run on the bound bean
  62. * once it has been updated from the values of the bound fields. Bean level
  63. * validators are also run as part of {@link #writeBean(Object)} and
  64. * {@link #writeBeanIfValid(Object)} if all field level validators pass.
  65. * <p>
  66. * Note: For bean level validators, the bean must be updated before the
  67. * validators are run. If a bean level validator fails in
  68. * {@link #writeBean(Object)} or {@link #writeBeanIfValid(Object)}, the bean
  69. * will be reverted to the previous state before returning from the method. You
  70. * should ensure that the getters/setters in the bean do not have side effects.
  71. * <p>
  72. * Unless otherwise specified, {@code Binder} method arguments cannot be null.
  73. *
  74. * @author Vaadin Ltd.
  75. *
  76. * @param <BEAN>
  77. * the bean type
  78. *
  79. * @see BindingBuilder
  80. * @see Binding
  81. * @see HasValue
  82. *
  83. * @since 8.0
  84. */
  85. public class Binder<BEAN> implements Serializable {
  86. /**
  87. * Represents the binding between a field and a data property.
  88. *
  89. * @param <BEAN>
  90. * the bean type
  91. * @param <TARGET>
  92. * the target data type of the binding, matches the field type
  93. * unless a converter has been set
  94. *
  95. * @see Binder#forField(HasValue)
  96. */
  97. public interface Binding<BEAN, TARGET> extends Serializable {
  98. /**
  99. * Gets the field the binding uses.
  100. *
  101. * @return the field for the binding
  102. */
  103. public HasValue<?> getField();
  104. /**
  105. * Validates the field value and returns a {@code ValidationStatus}
  106. * instance representing the outcome of the validation.
  107. *
  108. * @see Binder#validate()
  109. * @see Validator#apply(Object)
  110. *
  111. * @return the validation result.
  112. */
  113. public ValidationStatus<TARGET> validate();
  114. }
  115. /**
  116. * Creates a binding between a field and a data property.
  117. *
  118. * @param <BEAN>
  119. * the bean type
  120. * @param <TARGET>
  121. * the target data type of the binding, matches the field type
  122. * until a converter has been set
  123. *
  124. * @see Binder#forField(HasValue)
  125. */
  126. public interface BindingBuilder<BEAN, TARGET> extends Serializable {
  127. /**
  128. * Completes this binding using the given getter and setter functions
  129. * representing a backing bean property. The functions are used to
  130. * update the field value from the property and to store the field value
  131. * to the property, respectively.
  132. * <p>
  133. * When a bean is bound with {@link Binder#setBean(BEAN)}, the field
  134. * value is set to the return value of the given getter. The property
  135. * value is then updated via the given setter whenever the field value
  136. * changes. The setter may be null; in that case the property value is
  137. * never updated and the binding is said to be <i>read-only</i>.
  138. * <p>
  139. * If the Binder is already bound to some bean, the newly bound field is
  140. * associated with the corresponding bean property as described above.
  141. * <p>
  142. * The getter and setter can be arbitrary functions, for instance
  143. * implementing user-defined conversion or validation. However, in the
  144. * most basic use case you can simply pass a pair of method references
  145. * to this method as follows:
  146. *
  147. * <pre>
  148. * class Person {
  149. * public String getName() { ... }
  150. * public void setName(String name) { ... }
  151. * }
  152. *
  153. * TextField nameField = new TextField();
  154. * binder.forField(nameField).bind(Person::getName, Person::setName);
  155. * </pre>
  156. *
  157. * @param getter
  158. * the function to get the value of the property to the
  159. * field, not null
  160. * @param setter
  161. * the function to write the field value to the property or
  162. * null if read-only
  163. * @return the newly created binding
  164. * @throws IllegalStateException
  165. * if {@code bind} has already been called on this binding
  166. */
  167. public Binding<BEAN, TARGET> bind(
  168. SerializableFunction<BEAN, TARGET> getter,
  169. com.vaadin.server.SerializableBiConsumer<BEAN, TARGET> setter);
  170. /**
  171. * Adds a validator to this binding. Validators are applied, in
  172. * registration order, when the field value is written to the backing
  173. * property. If any validator returns a failure, the property value is
  174. * not updated.
  175. *
  176. * @see #withValidator(SerializablePredicate, String)
  177. * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
  178. *
  179. * @param validator
  180. * the validator to add, not null
  181. * @return this binding, for chaining
  182. * @throws IllegalStateException
  183. * if {@code bind} has already been called
  184. */
  185. public BindingBuilder<BEAN, TARGET> withValidator(
  186. Validator<? super TARGET> validator);
  187. /**
  188. * A convenience method to add a validator to this binding using the
  189. * {@link Validator#from(SerializablePredicate, String)} factory method.
  190. * <p>
  191. * Validators are applied, in registration order, when the field value
  192. * is written to the backing property. If any validator returns a
  193. * failure, the property value is not updated.
  194. *
  195. * @see #withValidator(Validator)
  196. * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
  197. * @see Validator#from(SerializablePredicate, String)
  198. *
  199. * @param predicate
  200. * the predicate performing validation, not null
  201. * @param message
  202. * the error message to report in case validation failure
  203. * @return this binding, for chaining
  204. * @throws IllegalStateException
  205. * if {@code bind} has already been called
  206. */
  207. public default BindingBuilder<BEAN, TARGET> withValidator(
  208. SerializablePredicate<? super TARGET> predicate,
  209. String message) {
  210. return withValidator(Validator.from(predicate, message));
  211. }
  212. /**
  213. * A convenience method to add a validator to this binding using the
  214. * {@link Validator#from(SerializablePredicate, ErrorMessageProvider)}
  215. * factory method.
  216. * <p>
  217. * Validators are applied, in registration order, when the field value
  218. * is written to the backing property. If any validator returns a
  219. * failure, the property value is not updated.
  220. *
  221. * @see #withValidator(Validator)
  222. * @see #withValidator(SerializablePredicate, String)
  223. * @see Validator#from(SerializablePredicate, ErrorMessageProvider)
  224. *
  225. * @param predicate
  226. * the predicate performing validation, not null
  227. * @param errorMessageProvider
  228. * the provider to generate error messages, not null
  229. * @return this binding, for chaining
  230. * @throws IllegalStateException
  231. * if {@code bind} has already been called
  232. */
  233. public default BindingBuilder<BEAN, TARGET> withValidator(
  234. SerializablePredicate<? super TARGET> predicate,
  235. ErrorMessageProvider errorMessageProvider) {
  236. return withValidator(
  237. Validator.from(predicate, errorMessageProvider));
  238. }
  239. /**
  240. * Maps the binding to another data type using the given
  241. * {@link Converter}.
  242. * <p>
  243. * A converter is capable of converting between a presentation type,
  244. * which must match the current target data type of the binding, and a
  245. * model type, which can be any data type and becomes the new target
  246. * type of the binding. When invoking
  247. * {@link #bind(SerializableFunction, SerializableBiConsumer)}, the
  248. * target type of the binding must match the getter/setter types.
  249. * <p>
  250. * For instance, a {@code TextField} can be bound to an integer-typed
  251. * property using an appropriate converter such as a
  252. * {@link StringToIntegerConverter}.
  253. *
  254. * @param <NEWTARGET>
  255. * the type to convert to
  256. * @param converter
  257. * the converter to use, not null
  258. * @return a new binding with the appropriate type
  259. * @throws IllegalStateException
  260. * if {@code bind} has already been called
  261. */
  262. public <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
  263. Converter<TARGET, NEWTARGET> converter);
  264. /**
  265. * Maps the binding to another data type using the mapping functions and
  266. * a possible exception as the error message.
  267. * <p>
  268. * The mapping functions are used to convert between a presentation
  269. * type, which must match the current target data type of the binding,
  270. * and a model type, which can be any data type and becomes the new
  271. * target type of the binding. When invoking
  272. * {@link #bind(SerializableFunction, SerializableBiConsumer)}, the
  273. * target type of the binding must match the getter/setter types.
  274. * <p>
  275. * For instance, a {@code TextField} can be bound to an integer-typed
  276. * property using appropriate functions such as:
  277. * <code>withConverter(Integer::valueOf, String::valueOf);</code>
  278. *
  279. * @param <NEWTARGET>
  280. * the type to convert to
  281. * @param toModel
  282. * the function which can convert from the old target type to
  283. * the new target type
  284. * @param toPresentation
  285. * the function which can convert from the new target type to
  286. * the old target type
  287. * @return a new binding with the appropriate type
  288. * @throws IllegalStateException
  289. * if {@code bind} has already been called
  290. */
  291. public default <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
  292. SerializableFunction<TARGET, NEWTARGET> toModel,
  293. SerializableFunction<NEWTARGET, TARGET> toPresentation) {
  294. return withConverter(Converter.from(toModel, toPresentation,
  295. exception -> exception.getMessage()));
  296. }
  297. /**
  298. * Maps the binding to another data type using the mapping functions and
  299. * the given error error message if a value cannot be converted to the
  300. * new target type.
  301. * <p>
  302. * The mapping functions are used to convert between a presentation
  303. * type, which must match the current target data type of the binding,
  304. * and a model type, which can be any data type and becomes the new
  305. * target type of the binding. When invoking
  306. * {@link #bind(SerializableFunction, SerializableBiConsumer)}, the
  307. * target type of the binding must match the getter/setter types.
  308. * <p>
  309. * For instance, a {@code TextField} can be bound to an integer-typed
  310. * property using appropriate functions such as:
  311. * <code>withConverter(Integer::valueOf, String::valueOf);</code>
  312. *
  313. * @param <NEWTARGET>
  314. * the type to convert to
  315. * @param toModel
  316. * the function which can convert from the old target type to
  317. * the new target type
  318. * @param toPresentation
  319. * the function which can convert from the new target type to
  320. * the old target type
  321. * @param errorMessage
  322. * the error message to use if conversion using
  323. * <code>toModel</code> fails
  324. * @return a new binding with the appropriate type
  325. * @throws IllegalStateException
  326. * if {@code bind} has already been called
  327. */
  328. public default <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
  329. SerializableFunction<TARGET, NEWTARGET> toModel,
  330. SerializableFunction<NEWTARGET, TARGET> toPresentation,
  331. String errorMessage) {
  332. return withConverter(Converter.from(toModel, toPresentation,
  333. exception -> errorMessage));
  334. }
  335. /**
  336. * Maps binding value {@code null} to given null representation and back
  337. * to {@code null} when converting back to model value.
  338. *
  339. * @param nullRepresentation
  340. * the value to use instead of {@code null}
  341. * @return a new binding with null representation handling.
  342. */
  343. public default BindingBuilder<BEAN, TARGET> withNullRepresentation(
  344. TARGET nullRepresentation) {
  345. return withConverter(
  346. fieldValue -> Objects.equals(fieldValue, nullRepresentation)
  347. ? null : fieldValue,
  348. modelValue -> Objects.isNull(modelValue)
  349. ? nullRepresentation : modelValue);
  350. }
  351. /**
  352. * Sets the given {@code label} to show an error message if validation
  353. * fails.
  354. * <p>
  355. * The validation state of each field is updated whenever the user
  356. * modifies the value of that field. The validation state is by default
  357. * shown using {@link AbstractComponent#setComponentError} which is used
  358. * by the layout that the field is shown in. Most built-in layouts will
  359. * show this as a red exclamation mark icon next to the component, so
  360. * that hovering or tapping the icon shows a tooltip with the message
  361. * text.
  362. * <p>
  363. * This method allows to customize the way a binder displays error
  364. * messages to get more flexibility than what
  365. * {@link AbstractComponent#setComponentError} provides (it replaces the
  366. * default behavior).
  367. * <p>
  368. * This is just a shorthand for
  369. * {@link #withValidationStatusHandler(ValidationStatusHandler)} method
  370. * where the handler instance hides the {@code label} if there is no
  371. * error and shows it with validation error message if validation fails.
  372. * It means that it cannot be called after
  373. * {@link #withValidationStatusHandler(ValidationStatusHandler)} method
  374. * call or {@link #withValidationStatusHandler(ValidationStatusHandler)}
  375. * after this method call.
  376. *
  377. * @see #withValidationStatusHandler(ValidationStatusHandler)
  378. * @see AbstractComponent#setComponentError(ErrorMessage)
  379. * @param label
  380. * label to show validation status for the field
  381. * @return this binding, for chaining
  382. */
  383. public default BindingBuilder<BEAN, TARGET> withStatusLabel(
  384. Label label) {
  385. return withValidationStatusHandler(status -> {
  386. label.setValue(status.getMessage().orElse(""));
  387. // Only show the label when validation has failed
  388. label.setVisible(status.isError());
  389. });
  390. }
  391. /**
  392. * Sets a {@link ValidationStatusHandler} to track validation status
  393. * changes.
  394. * <p>
  395. * The validation state of each field is updated whenever the user
  396. * modifies the value of that field. The validation state is by default
  397. * shown using {@link AbstractComponent#setComponentError} which is used
  398. * by the layout that the field is shown in. Most built-in layouts will
  399. * show this as a red exclamation mark icon next to the component, so
  400. * that hovering or tapping the icon shows a tooltip with the message
  401. * text.
  402. * <p>
  403. * This method allows to customize the way a binder displays error
  404. * messages to get more flexibility than what
  405. * {@link AbstractComponent#setComponentError} provides (it replaces the
  406. * default behavior).
  407. * <p>
  408. * The method may be called only once. It means there is no chain unlike
  409. * {@link #withValidator(Validator)} or
  410. * {@link #withConverter(Converter)}. Also it means that the shorthand
  411. * method {@link #withStatusLabel(Label)} also may not be called after
  412. * this method.
  413. *
  414. * @see #withStatusLabel(Label)
  415. * @see AbstractComponent#setComponentError(ErrorMessage)
  416. * @param handler
  417. * status change handler
  418. * @return this binding, for chaining
  419. */
  420. public BindingBuilder<BEAN, TARGET> withValidationStatusHandler(
  421. ValidationStatusHandler handler);
  422. /**
  423. * Sets the field to be required. This means two things:
  424. * <ol>
  425. * <li>the required indicator is visible</li>
  426. * <li>the field value is validated for not being empty*</li>
  427. * </ol>
  428. * For localizing the error message, use
  429. * {@link #setRequired(SerializableFunction)}.
  430. * <p>
  431. * *Value not being the equal to what {@link HasValue#getEmptyValue()}
  432. * returns.
  433. *
  434. * @see #setRequired(SerializableFunction)
  435. * @see HasValue#setRequiredIndicatorVisible(boolean)
  436. * @see HasValue#isEmpty()
  437. * @param errorMessage
  438. * the error message to show for the invalid value
  439. * @return this binding, for chaining
  440. */
  441. public default BindingBuilder<BEAN, TARGET> setRequired(
  442. String errorMessage) {
  443. return setRequired(context -> errorMessage);
  444. }
  445. /**
  446. * Sets the field to be required. This means two things:
  447. * <ol>
  448. * <li>the required indicator is visible</li>
  449. * <li>the field value is validated for not being empty*</li>
  450. * </ol>
  451. * *Value not being the equal to what {@link HasValue#getEmptyValue()}
  452. * returns.
  453. *
  454. * @see HasValue#setRequiredIndicatorVisible(boolean)
  455. * @see HasValue#isEmpty()
  456. * @param errorMessageProvider
  457. * the provider for localized validation error message
  458. * @return this binding, for chaining
  459. */
  460. public BindingBuilder<BEAN, TARGET> setRequired(
  461. ErrorMessageProvider errorMessageProvider);
  462. }
  463. /**
  464. * An internal implementation of {@code BindingBuilder}.
  465. *
  466. * @param <BEAN>
  467. * the bean type, must match the Binder bean type
  468. * @param <FIELDVALUE>
  469. * the value type of the field
  470. * @param <TARGET>
  471. * the target data type of the binding, matches the field type
  472. * until a converter has been set
  473. */
  474. protected static class BindingBuilderImpl<BEAN, FIELDVALUE, TARGET>
  475. implements BindingBuilder<BEAN, TARGET> {
  476. private final Binder<BEAN> binder;
  477. private final HasValue<FIELDVALUE> field;
  478. private ValidationStatusHandler statusHandler;
  479. private boolean isStatusHandlerChanged;
  480. private boolean bound;
  481. /**
  482. * Contains all converters and validators chained together in the
  483. * correct order.
  484. */
  485. private Converter<FIELDVALUE, TARGET> converterValidatorChain;
  486. /**
  487. * Creates a new binding builder associated with the given field.
  488. * Initializes the builder with the given converter chain and status
  489. * change handler.
  490. *
  491. * @param binder
  492. * the binder this instance is connected to, not null
  493. * @param field
  494. * the field to bind, not null
  495. * @param converterValidatorChain
  496. * the converter/validator chain to use, not null
  497. * @param statusHandler
  498. * the handler to track validation status, not null
  499. */
  500. protected BindingBuilderImpl(Binder<BEAN> binder,
  501. HasValue<FIELDVALUE> field,
  502. Converter<FIELDVALUE, TARGET> converterValidatorChain,
  503. ValidationStatusHandler statusHandler) {
  504. this.field = field;
  505. this.binder = binder;
  506. this.converterValidatorChain = converterValidatorChain;
  507. this.statusHandler = statusHandler;
  508. }
  509. @Override
  510. public Binding<BEAN, TARGET> bind(
  511. SerializableFunction<BEAN, TARGET> getter,
  512. SerializableBiConsumer<BEAN, TARGET> setter) {
  513. checkUnbound();
  514. Objects.requireNonNull(getter, "getter cannot be null");
  515. BindingImpl<BEAN, FIELDVALUE, TARGET> binding = new BindingImpl<>(
  516. this, getter, setter);
  517. getBinder().bindings.add(binding);
  518. getBinder().getBean().ifPresent(binding::initFieldValue);
  519. getBinder().fireStatusChangeEvent(false);
  520. bound = true;
  521. return binding;
  522. }
  523. @Override
  524. public BindingBuilder<BEAN, TARGET> withValidator(
  525. Validator<? super TARGET> validator) {
  526. checkUnbound();
  527. Objects.requireNonNull(validator, "validator cannot be null");
  528. converterValidatorChain = converterValidatorChain
  529. .chain(new ValidatorAsConverter<>(validator));
  530. return this;
  531. }
  532. @Override
  533. public <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
  534. Converter<TARGET, NEWTARGET> converter) {
  535. return withConverter(converter, true);
  536. }
  537. @Override
  538. public BindingBuilder<BEAN, TARGET> withValidationStatusHandler(
  539. ValidationStatusHandler handler) {
  540. checkUnbound();
  541. Objects.requireNonNull(handler, "handler cannot be null");
  542. if (isStatusHandlerChanged) {
  543. throw new IllegalStateException(
  544. "A " + ValidationStatusHandler.class.getSimpleName()
  545. + " has already been set");
  546. }
  547. isStatusHandlerChanged = true;
  548. statusHandler = handler;
  549. return this;
  550. }
  551. @Override
  552. public BindingBuilder<BEAN, TARGET> setRequired(
  553. ErrorMessageProvider errorMessageProvider) {
  554. checkUnbound();
  555. field.setRequiredIndicatorVisible(true);
  556. return withValidator(
  557. value -> !Objects.equals(value, field.getEmptyValue()),
  558. errorMessageProvider);
  559. }
  560. /**
  561. * Implements {@link #withConverter(Converter)} method with additional
  562. * possibility to disable (reset) default null representation converter.
  563. * <p>
  564. * The method {@link #withConverter(Converter)} calls this method with
  565. * {@code true} provided as the second argument value.
  566. *
  567. * @see #withConverter(Converter)
  568. *
  569. * @param converter
  570. * the converter to use, not null
  571. * @param resetNullRepresentation
  572. * if {@code true} then default null representation will be
  573. * deactivated (if not yet), otherwise it won't be removed
  574. * @return a new binding with the appropriate type
  575. * @param <NEWTARGET>
  576. * the type to convert to
  577. * @throws IllegalStateException
  578. * if {@code bind} has already been called
  579. */
  580. protected <NEWTARGET> BindingBuilder<BEAN, NEWTARGET> withConverter(
  581. Converter<TARGET, NEWTARGET> converter,
  582. boolean resetNullRepresentation) {
  583. checkUnbound();
  584. Objects.requireNonNull(converter, "converter cannot be null");
  585. if (resetNullRepresentation) {
  586. getBinder().initialConverters.get(field).setIdentity();
  587. }
  588. return getBinder().createBinding(field,
  589. converterValidatorChain.chain(converter), statusHandler);
  590. }
  591. /**
  592. * Returns the {@code Binder} connected to this {@code Binding}
  593. * instance.
  594. *
  595. * @return the binder
  596. */
  597. protected Binder<BEAN> getBinder() {
  598. return binder;
  599. }
  600. /**
  601. * Throws if this binding is already completed and cannot be modified
  602. * anymore.
  603. *
  604. * @throws IllegalStateException
  605. * if this binding is already bound
  606. */
  607. protected void checkUnbound() {
  608. if (bound) {
  609. throw new IllegalStateException(
  610. "cannot modify binding: already bound to a property");
  611. }
  612. }
  613. }
  614. /**
  615. * An internal implementation of {@code Binding}.
  616. *
  617. * @param <BEAN>
  618. * the bean type, must match the Binder bean type
  619. * @param <FIELDVALUE>
  620. * the value type of the field
  621. * @param <TARGET>
  622. * the target data type of the binding, matches the field type
  623. * unless a converter has been set
  624. */
  625. protected static class BindingImpl<BEAN, FIELDVALUE, TARGET>
  626. implements Binding<BEAN, TARGET> {
  627. private final Binder<BEAN> binder;
  628. private final HasValue<FIELDVALUE> field;
  629. private final ValidationStatusHandler statusHandler;
  630. private final SerializableFunction<BEAN, TARGET> getter;
  631. private final SerializableBiConsumer<BEAN, TARGET> setter;
  632. // Not final since we temporarily remove listener while changing values
  633. private Registration onValueChange;
  634. /**
  635. * Contains all converters and validators chained together in the
  636. * correct order.
  637. */
  638. private final Converter<FIELDVALUE, TARGET> converterValidatorChain;
  639. public BindingImpl(BindingBuilderImpl<BEAN, FIELDVALUE, TARGET> builder,
  640. SerializableFunction<BEAN, TARGET> getter,
  641. SerializableBiConsumer<BEAN, TARGET> setter) {
  642. this.binder = builder.getBinder();
  643. this.field = builder.field;
  644. this.statusHandler = builder.statusHandler;
  645. converterValidatorChain = builder.converterValidatorChain;
  646. onValueChange = getField()
  647. .addValueChangeListener(this::handleFieldValueChange);
  648. this.getter = getter;
  649. this.setter = setter;
  650. }
  651. @Override
  652. public HasValue<FIELDVALUE> getField() {
  653. return field;
  654. }
  655. /**
  656. * Finds an appropriate locale to be used in conversion and validation.
  657. *
  658. * @return the found locale, not null
  659. */
  660. protected Locale findLocale() {
  661. Locale l = null;
  662. if (getField() instanceof Component) {
  663. l = ((Component) getField()).getLocale();
  664. }
  665. if (l == null && UI.getCurrent() != null) {
  666. l = UI.getCurrent().getLocale();
  667. }
  668. if (l == null) {
  669. l = Locale.getDefault();
  670. }
  671. return l;
  672. }
  673. @Override
  674. public ValidationStatus<TARGET> validate() {
  675. ValidationStatus<TARGET> status = doValidation();
  676. getBinder().getValidationStatusHandler()
  677. .accept(new BinderValidationStatus<>(getBinder(),
  678. Arrays.asList(status), Collections.emptyList()));
  679. getBinder().fireStatusChangeEvent(status.isError());
  680. return status;
  681. }
  682. /**
  683. * Returns the field value run through all converters and validators,
  684. * but doesn't pass the {@link ValidationStatus} to any status handler.
  685. *
  686. * @return the result of the conversion
  687. */
  688. private Result<TARGET> doConversion() {
  689. FIELDVALUE fieldValue = field.getValue();
  690. return converterValidatorChain.convertToModel(fieldValue,
  691. createValueContext());
  692. }
  693. private ValidationStatus<TARGET> toValidationStatus(
  694. Result<TARGET> result) {
  695. return new ValidationStatus<>(this,
  696. result.isError()
  697. ? ValidationResult.error(result.getMessage().get())
  698. : ValidationResult.ok());
  699. }
  700. /**
  701. * Returns the field value run through all converters and validators,
  702. * but doesn't pass the {@link ValidationStatus} to any status handler.
  703. *
  704. * @return the validation status
  705. */
  706. private ValidationStatus<TARGET> doValidation() {
  707. return toValidationStatus(doConversion());
  708. }
  709. /**
  710. * Creates a value context from the current state of the binding and its
  711. * field.
  712. *
  713. * @return the value context
  714. */
  715. protected ValueContext createValueContext() {
  716. if (field instanceof Component) {
  717. return new ValueContext((Component) field);
  718. }
  719. return new ValueContext(findLocale());
  720. }
  721. /**
  722. * Sets the field value by invoking the getter function on the given
  723. * bean. The default listener attached to the field will be removed for
  724. * the duration of this update.
  725. *
  726. * @param bean
  727. * the bean to fetch the property value from
  728. */
  729. private void initFieldValue(BEAN bean) {
  730. assert bean != null;
  731. assert onValueChange != null;
  732. onValueChange.remove();
  733. try {
  734. getField().setValue(convertDataToFieldType(bean));
  735. } finally {
  736. onValueChange = getField()
  737. .addValueChangeListener(this::handleFieldValueChange);
  738. }
  739. }
  740. private FIELDVALUE convertDataToFieldType(BEAN bean) {
  741. return converterValidatorChain.convertToPresentation(
  742. getter.apply(bean), createValueContext());
  743. }
  744. /**
  745. * Handles the value change triggered by the bound field.
  746. *
  747. * @param bean
  748. * the new value
  749. */
  750. private void handleFieldValueChange(
  751. ValueChangeEvent<FIELDVALUE> event) {
  752. getBinder().setHasChanges(true);
  753. List<ValidationResult> binderValidationResults = Collections
  754. .emptyList();
  755. ValidationStatus<TARGET> fieldValidationStatus;
  756. if (getBinder().getBean().isPresent()) {
  757. BEAN bean = getBinder().getBean().get();
  758. fieldValidationStatus = writeFieldValue(bean);
  759. if (!getBinder().bindings.stream()
  760. .map(BindingImpl::doValidation)
  761. .anyMatch(ValidationStatus::isError)) {
  762. binderValidationResults = getBinder().validateBean(bean);
  763. if (!binderValidationResults.stream()
  764. .anyMatch(ValidationResult::isError)) {
  765. getBinder().setHasChanges(false);
  766. }
  767. }
  768. } else {
  769. fieldValidationStatus = doValidation();
  770. }
  771. BinderValidationStatus<BEAN> status = new BinderValidationStatus<>(
  772. getBinder(), Arrays.asList(fieldValidationStatus),
  773. binderValidationResults);
  774. getBinder().getValidationStatusHandler().accept(status);
  775. getBinder().fireStatusChangeEvent(status.hasErrors());
  776. }
  777. /**
  778. * Write the field value by invoking the setter function on the given
  779. * bean, if the value passes all registered validators.
  780. *
  781. * @param bean
  782. * the bean to set the property value to
  783. */
  784. private ValidationStatus<TARGET> writeFieldValue(BEAN bean) {
  785. assert bean != null;
  786. Result<TARGET> result = doConversion();
  787. if (setter != null) {
  788. result.ifOk(value -> setter.accept(bean, value));
  789. }
  790. return toValidationStatus(result);
  791. }
  792. /**
  793. * Returns the {@code Binder} connected to this {@code Binding}
  794. * instance.
  795. *
  796. * @return the binder
  797. */
  798. protected Binder<BEAN> getBinder() {
  799. return binder;
  800. }
  801. private void notifyStatusHandler(ValidationStatus<?> status) {
  802. statusHandler.accept(status);
  803. }
  804. }
  805. /**
  806. * Wraps a validator as a converter.
  807. * <p>
  808. * The type of the validator must be of the same type as this converter or a
  809. * super type of it.
  810. *
  811. * @param <T>
  812. * the type of the converter
  813. */
  814. private static class ValidatorAsConverter<T> implements Converter<T, T> {
  815. private final Validator<? super T> validator;
  816. /**
  817. * Creates a new converter wrapping the given validator.
  818. *
  819. * @param validator
  820. * the validator to wrap
  821. */
  822. public ValidatorAsConverter(Validator<? super T> validator) {
  823. this.validator = validator;
  824. }
  825. @Override
  826. public Result<T> convertToModel(T value, ValueContext context) {
  827. ValidationResult validationResult = validator.apply(value, context);
  828. if (validationResult.isError()) {
  829. return Result.error(validationResult.getErrorMessage());
  830. } else {
  831. return Result.ok(value);
  832. }
  833. }
  834. @Override
  835. public T convertToPresentation(T value, ValueContext context) {
  836. return value;
  837. }
  838. }
  839. /**
  840. * Converter decorator-strategy pattern to use initially provided "delegate"
  841. * converter to execute its logic until the {@code setIdentity()} method is
  842. * called. Once the method is called the class changes its behavior to the
  843. * same as {@link Converter#identity()} behavior.
  844. */
  845. private static class ConverterDelegate<FIELDVALUE>
  846. implements Converter<FIELDVALUE, FIELDVALUE> {
  847. private Converter<FIELDVALUE, FIELDVALUE> delegate;
  848. private ConverterDelegate(Converter<FIELDVALUE, FIELDVALUE> converter) {
  849. delegate = converter;
  850. }
  851. @Override
  852. public Result<FIELDVALUE> convertToModel(FIELDVALUE value,
  853. ValueContext context) {
  854. if (delegate == null) {
  855. return Result.ok(value);
  856. } else {
  857. return delegate.convertToModel(value, context);
  858. }
  859. }
  860. @Override
  861. public FIELDVALUE convertToPresentation(FIELDVALUE value,
  862. ValueContext context) {
  863. if (delegate == null) {
  864. return value;
  865. } else {
  866. return delegate.convertToPresentation(value, context);
  867. }
  868. }
  869. void setIdentity() {
  870. delegate = null;
  871. }
  872. }
  873. private BEAN bean;
  874. private final Set<BindingImpl<BEAN, ?, ?>> bindings = new LinkedHashSet<>();
  875. private final List<Validator<? super BEAN>> validators = new ArrayList<>();
  876. private final Map<HasValue<?>, ConverterDelegate<?>> initialConverters = new IdentityHashMap<>();
  877. private EventRouter eventRouter;
  878. private Label statusLabel;
  879. private BinderValidationStatusHandler<BEAN> statusHandler;
  880. private boolean hasChanges = false;
  881. /**
  882. * Returns an {@code Optional} of the bean that has been bound with
  883. * {@link #bind}, or an empty optional if a bean is not currently bound.
  884. *
  885. * @return the currently bound bean if any
  886. */
  887. public Optional<BEAN> getBean() {
  888. return Optional.ofNullable(bean);
  889. }
  890. /**
  891. * Creates a new binding for the given field. The returned builder may be
  892. * further configured before invoking
  893. * {@link BindingBuilder#bind(SerializableFunction, SerializableBiConsumer)}
  894. * which completes the binding. Until {@code Binding.bind} is called, the
  895. * binding has no effect.
  896. * <p>
  897. * <strong>Note:</strong> Not all {@link HasValue} implementations support
  898. * passing {@code null} as the value. For these the Binder will
  899. * automatically change {@code null} to a null representation provided by
  900. * {@link HasValue#getEmptyValue()}. This conversion is one-way only, if you
  901. * want to have a two-way mapping back to {@code null}, use
  902. * {@link BindingBuilder#withNullRepresentation(Object)}.
  903. *
  904. * @param <FIELDVALUE>
  905. * the value type of the field
  906. * @param field
  907. * the field to be bound, not null
  908. * @return the new binding
  909. *
  910. * @see #bind(HasValue, SerializableFunction, SerializableBiConsumer)
  911. */
  912. public <FIELDVALUE> BindingBuilder<BEAN, FIELDVALUE> forField(
  913. HasValue<FIELDVALUE> field) {
  914. Objects.requireNonNull(field, "field cannot be null");
  915. // clear previous errors for this field and any bean level validation
  916. clearError(field);
  917. getStatusLabel().ifPresent(label -> label.setValue(""));
  918. return createBinding(field, createNullRepresentationAdapter(field),
  919. this::handleValidationStatus);
  920. }
  921. /**
  922. * Binds a field to a bean property represented by the given getter and
  923. * setter pair. The functions are used to update the field value from the
  924. * property and to store the field value to the property, respectively.
  925. * <p>
  926. * Use the {@link #forField(HasValue)} overload instead if you want to
  927. * further configure the new binding.
  928. * <p>
  929. * <strong>Note:</strong> Not all {@link HasValue} implementations support
  930. * passing {@code null} as the value. For these the Binder will
  931. * automatically change {@code null} to a null representation provided by
  932. * {@link HasValue#getEmptyValue()}. This conversion is one-way only, if you
  933. * want to have a two-way mapping back to {@code null}, use
  934. * {@link #forField(HasValue)} and
  935. * {@link Binding#withNullRepresentation(Object))}.
  936. * <p>
  937. * When a bean is bound with {@link Binder#setBean(BEAN)}, the field value
  938. * is set to the return value of the given getter. The property value is
  939. * then updated via the given setter whenever the field value changes. The
  940. * setter may be null; in that case the property value is never updated and
  941. * the binding is said to be <i>read-only</i>.
  942. * <p>
  943. * If the Binder is already bound to some bean, the newly bound field is
  944. * associated with the corresponding bean property as described above.
  945. * <p>
  946. * The getter and setter can be arbitrary functions, for instance
  947. * implementing user-defined conversion or validation. However, in the most
  948. * basic use case you can simply pass a pair of method references to this
  949. * method as follows:
  950. *
  951. * <pre>
  952. * class Person {
  953. * public String getName() { ... }
  954. * public void setName(String name) { ... }
  955. * }
  956. *
  957. * TextField nameField = new TextField();
  958. * binder.bind(nameField, Person::getName, Person::setName);
  959. * </pre>
  960. *
  961. * @param <FIELDVALUE>
  962. * the value type of the field
  963. * @param field
  964. * the field to bind, not null
  965. * @param getter
  966. * the function to get the value of the property to the field,
  967. * not null
  968. * @param setter
  969. * the function to write the field value to the property or null
  970. * if read-only
  971. * @return the newly created binding
  972. */
  973. public <FIELDVALUE> Binding<BEAN, FIELDVALUE> bind(
  974. HasValue<FIELDVALUE> field,
  975. SerializableFunction<BEAN, FIELDVALUE> getter,
  976. SerializableBiConsumer<BEAN, FIELDVALUE> setter) {
  977. return forField(field).bind(getter, setter);
  978. }
  979. /**
  980. * Binds the given bean to all the fields added to this Binder. A
  981. * {@code null} value removes a currently bound bean.
  982. * <p>
  983. * When a bean is bound, the field values are updated by invoking their
  984. * corresponding getter functions. Any changes to field values are reflected
  985. * back to their corresponding property values of the bean as long as the
  986. * bean is bound.
  987. * <p>
  988. * Any change made in the fields also runs validation for the field
  989. * {@link Binding} and bean level validation for this binder (bean level
  990. * validators are added using {@link Binder#withValidator(Validator)}.
  991. *
  992. * @see #readBean(Object)
  993. * @see #writeBean(Object)
  994. * @see #writeBeanIfValid(Object)
  995. *
  996. * @param bean
  997. * the bean to edit, or {@code null} to remove a currently bound
  998. * bean
  999. */
  1000. public void setBean(BEAN bean) {
  1001. if (bean == null) {
  1002. if (this.bean != null) {
  1003. doRemoveBean(true);
  1004. }
  1005. } else {
  1006. doRemoveBean(false);
  1007. this.bean = bean;
  1008. bindings.forEach(b -> b.initFieldValue(bean));
  1009. // if there has been field value change listeners that trigger
  1010. // validation, need to make sure the validation errors are cleared
  1011. getValidationStatusHandler().accept(
  1012. BinderValidationStatus.createUnresolvedStatus(this));
  1013. fireStatusChangeEvent(false);
  1014. }
  1015. }
  1016. /**
  1017. * Removes the currently set bean, if any. If there is no bound bean, does
  1018. * nothing.
  1019. * <p>
  1020. * This is a shorthand for {@link #setBean(Object)} with {@code null} bean.
  1021. */
  1022. public void removeBean() {
  1023. setBean(null);
  1024. }
  1025. /**
  1026. * Reads the bound property values from the given bean to the corresponding
  1027. * fields.
  1028. * <p>
  1029. * The bean is not otherwise associated with this binder; in particular its
  1030. * property values are not bound to the field value changes. To achieve
  1031. * that, use {@link #setBean(BEAN)}.
  1032. *
  1033. * @see #setBean(Object)
  1034. * @see #writeBeanIfValid(Object)
  1035. * @see #writeBean(Object)
  1036. *
  1037. * @param bean
  1038. * the bean whose property values to read, not null
  1039. */
  1040. public void readBean(BEAN bean) {
  1041. Objects.requireNonNull(bean, "bean cannot be null");
  1042. setHasChanges(false);
  1043. bindings.forEach(binding -> binding.initFieldValue(bean));
  1044. getValidationStatusHandler()
  1045. .accept(BinderValidationStatus.createUnresolvedStatus(this));
  1046. fireStatusChangeEvent(false);
  1047. }
  1048. /**
  1049. * Writes changes from the bound fields to the given bean if all validators
  1050. * (binding and bean level) pass.
  1051. * <p>
  1052. * If any field binding validator fails, no values are written and a
  1053. * {@code ValidationException} is thrown.
  1054. * <p>
  1055. * If all field level validators pass, the given bean is updated and bean
  1056. * level validators are run on the updated bean. If any bean level validator
  1057. * fails, the bean updates are reverted and a {@code ValidationException} is
  1058. * thrown.
  1059. *
  1060. * @see #writeBeanIfValid(Object)
  1061. * @see #readBean(Object)
  1062. * @see #setBean(Object)
  1063. *
  1064. * @param bean
  1065. * the object to which to write the field values, not
  1066. * {@code null}
  1067. * @throws ValidationException
  1068. * if some of the bound field values fail to validate
  1069. */
  1070. public void writeBean(BEAN bean) throws ValidationException {
  1071. BinderValidationStatus<BEAN> status = doWriteIfValid(bean);
  1072. if (status.hasErrors()) {
  1073. throw new ValidationException(status.getFieldValidationErrors(),
  1074. status.getBeanValidationErrors());
  1075. }
  1076. }
  1077. /**
  1078. * Writes changes from the bound fields to the given bean if all validators
  1079. * (binding and bean level) pass.
  1080. * <p>
  1081. * If any field binding validator fails, no values are written and
  1082. * <code>false</code> is returned.
  1083. * <p>
  1084. * If all field level validators pass, the given bean is updated and bean
  1085. * level validators are run on the updated bean. If any bean level validator
  1086. * fails, the bean updates are reverted and <code>false</code> is returned.
  1087. *
  1088. * @see #writeBean(Object)
  1089. * @see #readBean(Object)
  1090. * @see #setBean(Object)
  1091. *
  1092. * @param bean
  1093. * the object to which to write the field values, not
  1094. * {@code null}
  1095. * @return {@code true} if there was no validation errors and the bean was
  1096. * updated, {@code false} otherwise
  1097. */
  1098. public boolean writeBeanIfValid(BEAN bean) {
  1099. return doWriteIfValid(bean).isOk();
  1100. }
  1101. /**
  1102. * Writes the field values into the given bean if all field level validators
  1103. * pass. Runs bean level validators on the bean after writing.
  1104. *
  1105. * @param bean
  1106. * the bean to write field values into
  1107. * @return a list of field validation errors if such occur, otherwise a list
  1108. * of bean validation errors.
  1109. */
  1110. @SuppressWarnings({ "rawtypes", "unchecked" })
  1111. private BinderValidationStatus<BEAN> doWriteIfValid(BEAN bean) {
  1112. Objects.requireNonNull(bean, "bean cannot be null");
  1113. // First run fields level validation
  1114. List<ValidationStatus<?>> bindingStatuses = validateBindings();
  1115. // If no validation errors then update bean
  1116. if (bindingStatuses.stream().filter(ValidationStatus::isError).findAny()
  1117. .isPresent()) {
  1118. fireStatusChangeEvent(true);
  1119. return new BinderValidationStatus<>(this, bindingStatuses,
  1120. Collections.emptyList());
  1121. }
  1122. // Store old bean values so we can restore them if validators fail
  1123. Map<Binding<BEAN, ?>, Object> oldValues = new HashMap<>();
  1124. bindings.forEach(
  1125. binding -> oldValues.put(binding, binding.getter.apply(bean)));
  1126. bindings.forEach(binding -> binding.writeFieldValue(bean));
  1127. // Now run bean level validation against the updated bean
  1128. List<ValidationResult> binderResults = validateBean(bean);
  1129. boolean hasErrors = binderResults.stream()
  1130. .filter(ValidationResult::isError).findAny().isPresent();
  1131. if (hasErrors) {
  1132. // Bean validator failed, revert values
  1133. bindings.forEach((BindingImpl binding) -> binding.setter
  1134. .accept(bean, oldValues.get(binding)));
  1135. } else {
  1136. // Write successful, reset hasChanges to false
  1137. setHasChanges(false);
  1138. }
  1139. fireStatusChangeEvent(hasErrors);
  1140. return new BinderValidationStatus<>(this, bindingStatuses,
  1141. binderResults);
  1142. }
  1143. /**
  1144. * Adds an bean level validator.
  1145. * <p>
  1146. * Bean level validators are applied on the bean instance after the bean is
  1147. * updated. If the validators fail, the bean instance is reverted to its
  1148. * previous state.
  1149. *
  1150. * @see #writeBean(Object)
  1151. * @see #writeBeanIfValid(Object)
  1152. * @see #withValidator(SerializablePredicate, String)
  1153. * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
  1154. *
  1155. * @param validator
  1156. * the validator to add, not null
  1157. * @return this binder, for chaining
  1158. */
  1159. public Binder<BEAN> withValidator(Validator<? super BEAN> validator) {
  1160. Objects.requireNonNull(validator, "validator cannot be null");
  1161. validators.add(validator);
  1162. return this;
  1163. }
  1164. /**
  1165. * A convenience method to add a validator to this binder using the
  1166. * {@link Validator#from(SerializablePredicate, String)} factory method.
  1167. * <p>
  1168. * Bean level validators are applied on the bean instance after the bean is
  1169. * updated. If the validators fail, the bean instance is reverted to its
  1170. * previous state.
  1171. *
  1172. * @see #writeBean(Object)
  1173. * @see #writeBeanIfValid(Object)
  1174. * @see #withValidator(Validator)
  1175. * @see #withValidator(SerializablePredicate, ErrorMessageProvider)
  1176. *
  1177. * @param predicate
  1178. * the predicate performing validation, not null
  1179. * @param message
  1180. * the error message to report in case validation failure
  1181. * @return this binder, for chaining
  1182. */
  1183. public Binder<BEAN> withValidator(SerializablePredicate<BEAN> predicate,
  1184. String message) {
  1185. return withValidator(Validator.from(predicate, message));
  1186. }
  1187. /**
  1188. * A convenience method to add a validator to this binder using the
  1189. * {@link Validator#from(SerializablePredicate, ErrorMessageProvider)}
  1190. * factory method.
  1191. * <p>
  1192. * Bean level validators are applied on the bean instance after the bean is
  1193. * updated. If the validators fail, the bean instance is reverted to its
  1194. * previous state.
  1195. *
  1196. * @see #writeBean(Object)
  1197. * @see #writeBeanIfValid(Object)
  1198. * @see #withValidator(Validator)
  1199. * @see #withValidator(SerializablePredicate, String)
  1200. *
  1201. * @param predicate
  1202. * the predicate performing validation, not null
  1203. * @param errorMessageProvider
  1204. * the provider to generate error messages, not null
  1205. * @return this binder, for chaining
  1206. */
  1207. public Binder<BEAN> withValidator(SerializablePredicate<BEAN> predicate,
  1208. ErrorMessageProvider errorMessageProvider) {
  1209. return withValidator(Validator.from(predicate, errorMessageProvider));
  1210. }
  1211. /**
  1212. * Validates the values of all bound fields and returns the validation
  1213. * status.
  1214. * <p>
  1215. * If all field level validators pass, and {@link #setBean(Object)} has been
  1216. * used to bind to a bean, bean level validators are run for that bean. Bean
  1217. * level validators are ignored if there is no bound bean or if any field
  1218. * level validator fails.
  1219. * <p>
  1220. *
  1221. * @return validation status for the binder
  1222. */
  1223. public BinderValidationStatus<BEAN> validate() {
  1224. List<ValidationStatus<?>> bindingStatuses = validateBindings();
  1225. BinderValidationStatus<BEAN> validationStatus;
  1226. if (bindingStatuses.stream().filter(ValidationStatus::isError).findAny()
  1227. .isPresent() || bean == null) {
  1228. validationStatus = new BinderValidationStatus<>(this,
  1229. bindingStatuses, Collections.emptyList());
  1230. } else {
  1231. validationStatus = new BinderValidationStatus<>(this,
  1232. bindingStatuses, validateBean(bean));
  1233. }
  1234. getValidationStatusHandler().accept(validationStatus);
  1235. fireStatusChangeEvent(validationStatus.hasErrors());
  1236. return validationStatus;
  1237. }
  1238. /**
  1239. * Validates the bindings and returns the result of the validation as a list
  1240. * of validation statuses.
  1241. * <p>
  1242. * Does not run bean validators.
  1243. *
  1244. * @see #validateBean(Object)
  1245. *
  1246. * @return an immutable list of validation results for bindings
  1247. */
  1248. private List<ValidationStatus<?>> validateBindings() {
  1249. List<ValidationStatus<?>> results = new ArrayList<>();
  1250. for (BindingImpl<?, ?, ?> binding : bindings) {
  1251. results.add(binding.doValidation());
  1252. }
  1253. return results;
  1254. }
  1255. /**
  1256. * Validates the {@code bean} using validators added using
  1257. * {@link #withValidator(Validator)} and returns the result of the
  1258. * validation as a list of validation results.
  1259. * <p>
  1260. *
  1261. * @see #withValidator(Validator)
  1262. *
  1263. * @param bean
  1264. * the bean to validate
  1265. * @return a list of validation errors or an empty list if validation
  1266. * succeeded
  1267. */
  1268. private List<ValidationResult> validateBean(BEAN bean) {
  1269. Objects.requireNonNull(bean, "bean cannot be null");
  1270. List<ValidationResult> results = Collections.unmodifiableList(validators
  1271. .stream()
  1272. .map(validator -> validator.apply(bean, new ValueContext()))
  1273. .collect(Collectors.toList()));
  1274. return results;
  1275. }
  1276. /**
  1277. * Sets the label to show the binder level validation errors not related to
  1278. * any specific field.
  1279. * <p>
  1280. * Only the one validation error message is shown in this label at a time.
  1281. * <p>
  1282. * This is a convenience method for
  1283. * {@link #setValidationStatusHandler(BinderStatusHandler)}, which means
  1284. * that this method cannot be used after the handler has been set. Also the
  1285. * handler cannot be set after this label has been set.
  1286. *
  1287. * @param statusLabel
  1288. * the status label to set
  1289. * @see #setValidationStatusHandler(BinderStatusHandler)
  1290. * @see BindingBuilder#withStatusLabel(Label)
  1291. */
  1292. public void setStatusLabel(Label statusLabel) {
  1293. if (statusHandler != null) {
  1294. throw new IllegalStateException("Cannot set status label if a "
  1295. + BinderValidationStatusHandler.class.getSimpleName()
  1296. + " has already been set.");
  1297. }
  1298. this.statusLabel = statusLabel;
  1299. }
  1300. /**
  1301. * Gets the status label or an empty optional if none has been set.
  1302. *
  1303. * @return the optional status label
  1304. * @see #setStatusLabel(Label)
  1305. */
  1306. public Optional<Label> getStatusLabel() {
  1307. return Optional.ofNullable(statusLabel);
  1308. }
  1309. /**
  1310. * Sets the status handler to track form status changes.
  1311. * <p>
  1312. * Setting this handler will override the default behavior, which is to let
  1313. * fields show their validation status messages and show binder level
  1314. * validation errors or OK status in the label set with
  1315. * {@link #setStatusLabel(Label)}.
  1316. * <p>
  1317. * This handler cannot be set after the status label has been set with
  1318. * {@link #setStatusLabel(Label)}, or {@link #setStatusLabel(Label)} cannot
  1319. * be used after this handler has been set.
  1320. *
  1321. * @param statusHandler
  1322. * the status handler to set, not <code>null</code>
  1323. * @throws NullPointerException
  1324. * for <code>null</code> status handler
  1325. * @see #setStatusLabel(Label)
  1326. * @see BindingBuilder#withValidationStatusHandler(ValidationStatusHandler)
  1327. */
  1328. public void setValidationStatusHandler(
  1329. BinderValidationStatusHandler<BEAN> statusHandler) {
  1330. Objects.requireNonNull(statusHandler, "Cannot set a null "
  1331. + BinderValidationStatusHandler.class.getSimpleName());
  1332. if (statusLabel != null) {
  1333. throw new IllegalStateException("Cannot set "
  1334. + BinderValidationStatusHandler.class.getSimpleName()
  1335. + " if a status label has already been set.");
  1336. }
  1337. this.statusHandler = statusHandler;
  1338. }
  1339. /**
  1340. * Gets the status handler of this form.
  1341. * <p>
  1342. * If none has been set with
  1343. * {@link #setValidationStatusHandler(BinderStatusHandler)}, the default
  1344. * implementation is returned.
  1345. *
  1346. * @return the status handler used, never <code>null</code>
  1347. * @see #setValidationStatusHandler(BinderStatusHandler)
  1348. */
  1349. public BinderValidationStatusHandler<BEAN> getValidationStatusHandler() {
  1350. return Optional.ofNullable(statusHandler)
  1351. .orElse(this::handleBinderValidationStatus);
  1352. }
  1353. /**
  1354. * Adds status change listener to the binder.
  1355. * <p>
  1356. * The {@link Binder} status is changed whenever any of the following
  1357. * happens:
  1358. * <ul>
  1359. * <li>if it's bound and any of its bound field or select has been changed
  1360. * <li>{@link #writeBean(Object)} or {@link #writeBeanIfValid(Object)} is
  1361. * called
  1362. * <li>{@link #readBean(Object)} is called
  1363. * <li>{@link #setBean(Object)} is called
  1364. * <li>{@link #removeBean()} is called
  1365. * <li>{@link BindingBuilder#bind(SerializableFunction, SerializableBiConsumer)}
  1366. * is called
  1367. * <li>{@link Binder#validate()} or {@link Binding#validate()} is called
  1368. * </ul>
  1369. *
  1370. * @see #readBean(Object)
  1371. * @see #writeBean(Object)
  1372. * @see #writeBeanIfValid(Object)
  1373. * @see #setBean(Object)
  1374. * @see #removeBean()
  1375. * @see #forField(HasValue)
  1376. * @see #validate()
  1377. * @see Binding#validate()
  1378. *
  1379. * @param listener
  1380. * status change listener to add, not null
  1381. * @return a registration for the listener
  1382. */
  1383. public Registration addStatusChangeListener(StatusChangeListener listener) {
  1384. return getEventRouter().addListener(StatusChangeEvent.class, listener,
  1385. StatusChangeListener.class.getDeclaredMethods()[0]);
  1386. }
  1387. /**
  1388. * Creates a new binding with the given field.
  1389. *
  1390. * @param <FIELDVALUE>
  1391. * the value type of the field
  1392. * @param <TARGET>
  1393. * the target data type
  1394. * @param field
  1395. * the field to bind, not null
  1396. * @param converter
  1397. * the converter for converting between FIELDVALUE and TARGET
  1398. * types, not null
  1399. * @param handler
  1400. * the handler to notify of status changes, not null
  1401. * @return the new incomplete binding
  1402. */
  1403. protected <FIELDVALUE, TARGET> BindingBuilder<BEAN, TARGET> createBinding(
  1404. HasValue<FIELDVALUE> field, Converter<FIELDVALUE, TARGET> converter,
  1405. ValidationStatusHandler handler) {
  1406. return new BindingBuilderImpl<>(this, field, converter, handler);
  1407. }
  1408. /**
  1409. * Clears the error condition of the given field, if any. The default
  1410. * implementation clears the
  1411. * {@link AbstractComponent#setComponentError(ErrorMessage) component error}
  1412. * of the field if it is a Component, otherwise does nothing.
  1413. *
  1414. * @param field
  1415. * the field with an invalid value
  1416. */
  1417. protected void clearError(HasValue<?> field) {
  1418. if (field instanceof AbstractComponent) {
  1419. ((AbstractComponent) field).setComponentError(null);
  1420. }
  1421. }
  1422. /**
  1423. * Handles a validation error emitted when trying to write the value of the
  1424. * given field. The default implementation sets the
  1425. * {@link AbstractComponent#setComponentError(ErrorMessage) component error}
  1426. * of the field if it is a Component, otherwise does nothing.
  1427. *
  1428. * @param field
  1429. * the field with the invalid value
  1430. * @param error
  1431. * the error message to set
  1432. */
  1433. protected void handleError(HasValue<?> field, String error) {
  1434. if (field instanceof AbstractComponent) {
  1435. ((AbstractComponent) field).setComponentError(new UserError(error));
  1436. }
  1437. }
  1438. /**
  1439. * Default {@link ValidationStatusHandler} functional method implementation.
  1440. *
  1441. * @param status
  1442. * the validation status
  1443. */
  1444. protected void handleValidationStatus(ValidationStatus<?> status) {
  1445. HasValue<?> source = status.getField();
  1446. clearError(source);
  1447. if (status.isError()) {
  1448. handleError(source, status.getMessage().get());
  1449. }
  1450. }
  1451. /**
  1452. * Returns the bindings for this binder.
  1453. *
  1454. * @return a set of the bindings
  1455. */
  1456. protected Set<BindingImpl<BEAN, ?, ?>> getBindings() {
  1457. return bindings;
  1458. }
  1459. /**
  1460. * The default binder level status handler.
  1461. * <p>
  1462. * Passes all field related results to the Binding status handlers. All
  1463. * other status changes are displayed in the status label, if one has been
  1464. * set with {@link #setStatusLabel(Label)}.
  1465. *
  1466. * @param binderStatus
  1467. * status of validation results from binding and/or bean level
  1468. * validators
  1469. */
  1470. protected void handleBinderValidationStatus(
  1471. BinderValidationStatus<BEAN> binderStatus) {
  1472. // let field events go to binding status handlers
  1473. binderStatus.getFieldValidationStatuses()
  1474. .forEach(status -> ((BindingImpl<?, ?, ?>) status.getBinding())
  1475. .notifyStatusHandler(status));
  1476. // show first possible error or OK status in the label if set
  1477. if (getStatusLabel().isPresent()) {
  1478. String statusMessage = binderStatus.getBeanValidationErrors()
  1479. .stream().findFirst().map(ValidationResult::getErrorMessage)
  1480. .orElse("");
  1481. getStatusLabel().get().setValue(statusMessage);
  1482. }
  1483. }
  1484. /**
  1485. * Sets whether the values of the fields this binder is bound to have
  1486. * changed since the last explicit call to either bind, write or read.
  1487. *
  1488. * @param hasChanges
  1489. * whether this binder should be marked to have changes
  1490. */
  1491. private void setHasChanges(boolean hasChanges) {
  1492. this.hasChanges = hasChanges;
  1493. }
  1494. /**
  1495. * Check whether any of the bound fields' values have changed since last
  1496. * explicit call to {@link #setBean(Object)}, {@link #readBean(Object)},
  1497. * {@link #removeBean()}, {@link #writeBean(Object)} or
  1498. * {@link #writeBeanIfValid(Object)}. Unsuccessful write operations will not
  1499. * affect this value. Return values for each case are compiled into the
  1500. * following table:
  1501. *
  1502. * <p>
  1503. *
  1504. * <table>
  1505. * <tr>
  1506. * <td></td>
  1507. * <td>After readBean, setBean or removeBean</td>
  1508. * <td>After valid user changes</td>
  1509. * <td>After invalid user changes</td>
  1510. * <td>After successful writeBean or writeBeanIfValid</td>
  1511. * <td>After unsuccessful writeBean or writeBeanIfValid</td>
  1512. * </tr>
  1513. * <tr>
  1514. * <td>A bean is currently bound</td>
  1515. * <td>{@code false}</td>
  1516. * <td>{@code false}</td>
  1517. * <td>{@code true}</td>
  1518. * <td>{@code false}</td>
  1519. * <td>no change</td>
  1520. * </tr>
  1521. * <tr>
  1522. * <td>No bean is currently bound</td>
  1523. * <td>{@code false}</td>
  1524. * <td>{@code true}</td>
  1525. * <td>{@code true}</td>
  1526. * <td>{@code false}</td>
  1527. * <td>no change</td>
  1528. * </tr>
  1529. * </table>
  1530. *
  1531. * @return whether any bound field's value has changed since last call to
  1532. * setBean, readBean, writeBean or writeBeanIfValid
  1533. */
  1534. public boolean hasChanges() {
  1535. return hasChanges;
  1536. }
  1537. /**
  1538. * Returns the event router for this binder.
  1539. *
  1540. * @return the event router, not null
  1541. */
  1542. protected EventRouter getEventRouter() {
  1543. if (eventRouter == null) {
  1544. eventRouter = new EventRouter();
  1545. }
  1546. return eventRouter;
  1547. }
  1548. private void doRemoveBean(boolean fireStatusEvent) {
  1549. setHasChanges(false);
  1550. if (bean != null) {
  1551. bean = null;
  1552. }
  1553. getValidationStatusHandler()
  1554. .accept(BinderValidationStatus.createUnresolvedStatus(this));
  1555. if (fireStatusEvent) {
  1556. fireStatusChangeEvent(false);
  1557. }
  1558. }
  1559. private void fireStatusChangeEvent(boolean hasValidationErrors) {
  1560. getEventRouter()
  1561. .fireEvent(new StatusChangeEvent(this, hasValidationErrors));
  1562. }
  1563. private <FIELDVALUE> Converter<FIELDVALUE, FIELDVALUE> createNullRepresentationAdapter(
  1564. HasValue<FIELDVALUE> field) {
  1565. Converter<FIELDVALUE, FIELDVALUE> nullRepresentationConverter = Converter
  1566. .from(fieldValue -> fieldValue,
  1567. modelValue -> Objects.isNull(modelValue)
  1568. ? field.getEmptyValue() : modelValue,
  1569. exception -> exception.getMessage());
  1570. ConverterDelegate<FIELDVALUE> converter = new ConverterDelegate<>(
  1571. nullRepresentationConverter);
  1572. initialConverters.put(field, converter);
  1573. return converter;
  1574. }
  1575. }