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 62KB

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