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

Binder.java 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020
  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.Collections;
  20. import java.util.HashMap;
  21. import java.util.LinkedHashSet;
  22. import java.util.List;
  23. import java.util.Locale;
  24. import java.util.Map;
  25. import java.util.Objects;
  26. import java.util.Optional;
  27. import java.util.Set;
  28. import java.util.function.BiConsumer;
  29. import java.util.function.Function;
  30. import java.util.function.Predicate;
  31. import java.util.stream.Collectors;
  32. import com.vaadin.data.util.converter.Converter;
  33. import com.vaadin.data.util.converter.StringToIntegerConverter;
  34. import com.vaadin.server.ErrorMessage;
  35. import com.vaadin.server.UserError;
  36. import com.vaadin.shared.Registration;
  37. import com.vaadin.ui.AbstractComponent;
  38. import com.vaadin.ui.Label;
  39. /**
  40. * Connects one or more {@code Field} components to properties of a backing data
  41. * type such as a bean type. With a binder, input components can be grouped
  42. * together into forms to easily create and update business objects with little
  43. * explicit logic needed to move data between the UI and the data layers of the
  44. * application.
  45. * <p>
  46. * A binder is a collection of <i>bindings</i>, each representing the mapping of
  47. * a single field, through converters and validators, to a backing property.
  48. * <p>
  49. * A binder instance can be bound to a single bean instance at a time, but can
  50. * be rebound as needed. This allows usage patterns like a <i>master-details</i>
  51. * view, where a select component is used to pick the bean to edit.
  52. * <p>
  53. * Bean level validators can be added using the
  54. * {@link #withValidator(Validator)} method and will be run on the bound bean
  55. * once it has been updated from the values of the bound fields. Bean level
  56. * validators are also run as part of {@link #save(Object)} and
  57. * {@link #saveIfValid(Object)} if all field level validators pass.
  58. * <p>
  59. * Note: For bean level validators, the item must be updated before the
  60. * validators are run. If a bean level validator fails in {@link #save(Object)}
  61. * or {@link #saveIfValid(Object)}, the item will be reverted to the previous
  62. * state before returning from the method. You should ensure that the
  63. * getters/setters in the item do not have side effects.
  64. * <p>
  65. * Unless otherwise specified, {@code Binder} method arguments cannot be null.
  66. *
  67. * @author Vaadin Ltd.
  68. *
  69. * @param <BEAN>
  70. * the bean type
  71. *
  72. * @see Binding
  73. * @see HasValue
  74. *
  75. * @since 8.0
  76. */
  77. public class Binder<BEAN> implements Serializable {
  78. /**
  79. * Represents the binding between a field and a data property.
  80. *
  81. * @param <BEAN>
  82. * the bean type
  83. * @param <FIELDVALUE>
  84. * the value type of the field
  85. * @param <TARGET>
  86. * the target data type of the binding, matches the field type
  87. * until a converter has been set
  88. *
  89. * @see Binder#forField(HasValue)
  90. */
  91. public interface Binding<BEAN, FIELDVALUE, TARGET> extends Serializable {
  92. /**
  93. * Completes this binding using the given getter and setter functions
  94. * representing a backing bean property. The functions are used to
  95. * update the field value from the property and to store the field value
  96. * to the property, respectively.
  97. * <p>
  98. * When a bean is bound with {@link Binder#bind(BEAN)}, the field value
  99. * is set to the return value of the given getter. The property value is
  100. * then updated via the given setter whenever the field value changes.
  101. * The setter may be null; in that case the property value is never
  102. * updated and the binding is said to be <i>read-only</i>.
  103. * <p>
  104. * If the Binder is already bound to some item, the newly bound field is
  105. * associated with the corresponding bean property as described above.
  106. * <p>
  107. * The getter and setter can be arbitrary functions, for instance
  108. * implementing user-defined conversion or validation. However, in the
  109. * most basic use case you can simply pass a pair of method references
  110. * to this method as follows:
  111. *
  112. * <pre>
  113. * class Person {
  114. * public String getName() { ... }
  115. * public void setName(String name) { ... }
  116. * }
  117. *
  118. * TextField nameField = new TextField();
  119. * binder.forField(nameField).bind(Person::getName, Person::setName);
  120. * </pre>
  121. *
  122. * @param getter
  123. * the function to get the value of the property to the
  124. * field, not null
  125. * @param setter
  126. * the function to save the field value to the property or
  127. * null if read-only
  128. * @throws IllegalStateException
  129. * if {@code bind} has already been called on this binding
  130. */
  131. public void bind(Function<BEAN, TARGET> getter,
  132. BiConsumer<BEAN, TARGET> setter);
  133. /**
  134. * Adds a validator to this binding. Validators are applied, in
  135. * registration order, when the field value is saved to the backing
  136. * property. If any validator returns a failure, the property value is
  137. * not updated.
  138. *
  139. * @param validator
  140. * the validator to add, not null
  141. * @return this binding, for chaining
  142. * @throws IllegalStateException
  143. * if {@code bind} has already been called
  144. */
  145. public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
  146. Validator<? super TARGET> validator);
  147. /**
  148. * A convenience method to add a validator to this binding using the
  149. * {@link Validator#from(Predicate, String)} factory method.
  150. * <p>
  151. * Validators are applied, in registration order, when the field value
  152. * is saved to the backing property. If any validator returns a failure,
  153. * the property value is not updated.
  154. *
  155. * @see #withValidator(Validator)
  156. * @see Validator#from(Predicate, String)
  157. *
  158. * @param predicate
  159. * the predicate performing validation, not null
  160. * @param message
  161. * the error message to report in case validation failure
  162. * @return this binding, for chaining
  163. * @throws IllegalStateException
  164. * if {@code bind} has already been called
  165. */
  166. public default Binding<BEAN, FIELDVALUE, TARGET> withValidator(
  167. Predicate<? super TARGET> predicate, String message) {
  168. return withValidator(Validator.from(predicate, message));
  169. }
  170. /**
  171. * Maps the binding to another data type using the given
  172. * {@link Converter}.
  173. * <p>
  174. * A converter is capable of converting between a presentation type,
  175. * which must match the current target data type of the binding, and a
  176. * model type, which can be any data type and becomes the new target
  177. * type of the binding. When invoking
  178. * {@link #bind(Function, BiConsumer)}, the target type of the binding
  179. * must match the getter/setter types.
  180. * <p>
  181. * For instance, a {@code TextField} can be bound to an integer-typed
  182. * property using an appropriate converter such as a
  183. * {@link StringToIntegerConverter}.
  184. *
  185. * @param <NEWTARGET>
  186. * the type to convert to
  187. * @param converter
  188. * the converter to use, not null
  189. * @return a new binding with the appropriate type
  190. * @throws IllegalStateException
  191. * if {@code bind} has already been called
  192. */
  193. public <NEWTARGET> Binding<BEAN, FIELDVALUE, NEWTARGET> withConverter(
  194. Converter<TARGET, NEWTARGET> converter);
  195. /**
  196. * Maps the binding to another data type using the mapping functions and
  197. * a possible exception as the error message.
  198. * <p>
  199. * The mapping functions are used to convert between a presentation
  200. * type, which must match the current target data type of the binding,
  201. * and a model type, which can be any data type and becomes the new
  202. * target type of the binding. When invoking
  203. * {@link #bind(Function, BiConsumer)}, the target type of the binding
  204. * must match the getter/setter types.
  205. * <p>
  206. * For instance, a {@code TextField} can be bound to an integer-typed
  207. * property using appropriate functions such as:
  208. * <code>withConverter(Integer::valueOf, String::valueOf);</code>
  209. *
  210. * @param <NEWTARGET>
  211. * the type to convert to
  212. * @param toModel
  213. * the function which can convert from the old target type to
  214. * the new target type
  215. * @param toPresentation
  216. * the function which can convert from the new target type to
  217. * the old target type
  218. * @return a new binding with the appropriate type
  219. * @throws IllegalStateException
  220. * if {@code bind} has already been called
  221. */
  222. public default <NEWTARGET> Binding<BEAN, FIELDVALUE, NEWTARGET> withConverter(
  223. Function<TARGET, NEWTARGET> toModel,
  224. Function<NEWTARGET, TARGET> toPresentation) {
  225. return withConverter(Converter.from(toModel, toPresentation,
  226. exception -> exception.getMessage()));
  227. }
  228. /**
  229. * Maps the binding to another data type using the mapping functions and
  230. * the given error error message if a value cannot be converted to the
  231. * new target type.
  232. * <p>
  233. * The mapping functions are used to convert between a presentation
  234. * type, which must match the current target data type of the binding,
  235. * and a model type, which can be any data type and becomes the new
  236. * target type of the binding. When invoking
  237. * {@link #bind(Function, BiConsumer)}, the target type of the binding
  238. * must match the getter/setter types.
  239. * <p>
  240. * For instance, a {@code TextField} can be bound to an integer-typed
  241. * property using appropriate functions such as:
  242. * <code>withConverter(Integer::valueOf, String::valueOf);</code>
  243. *
  244. * @param <NEWTARGET>
  245. * the type to convert to
  246. * @param toModel
  247. * the function which can convert from the old target type to
  248. * the new target type
  249. * @param toPresentation
  250. * the function which can convert from the new target type to
  251. * the old target type
  252. * @param errorMessage
  253. * the error message to use if conversion using
  254. * <code>toModel</code> fails
  255. * @return a new binding with the appropriate type
  256. * @throws IllegalStateException
  257. * if {@code bind} has already been called
  258. */
  259. public default <NEWTARGET> Binding<BEAN, FIELDVALUE, NEWTARGET> withConverter(
  260. Function<TARGET, NEWTARGET> toModel,
  261. Function<NEWTARGET, TARGET> toPresentation,
  262. String errorMessage) {
  263. return withConverter(Converter.from(toModel, toPresentation,
  264. exception -> errorMessage));
  265. }
  266. /**
  267. * Gets the field the binding uses.
  268. *
  269. * @return the field for the binding
  270. */
  271. public HasValue<FIELDVALUE> getField();
  272. /**
  273. * Sets the given {@code label} to show an error message if validation
  274. * fails.
  275. * <p>
  276. * The validation state of each field is updated whenever the user
  277. * modifies the value of that field. The validation state is by default
  278. * shown using {@link AbstractComponent#setComponentError} which is used
  279. * by the layout that the field is shown in. Most built-in layouts will
  280. * show this as a red exclamation mark icon next to the component, so
  281. * that hovering or tapping the icon shows a tooltip with the message
  282. * text.
  283. * <p>
  284. * This method allows to customize the way a binder displays error
  285. * messages to get more flexibility than what
  286. * {@link AbstractComponent#setComponentError} provides (it replaces the
  287. * default behavior).
  288. * <p>
  289. * This is just a shorthand for
  290. * {@link #withStatusChangeHandler(StatusChangeHandler)} method where
  291. * the handler instance hides the {@code label} if there is no error and
  292. * shows it with validation error message if validation fails. It means
  293. * that it cannot be called after
  294. * {@link #withStatusChangeHandler(StatusChangeHandler)} method call or
  295. * {@link #withStatusChangeHandler(StatusChangeHandler)} after this
  296. * method call.
  297. *
  298. * @see #withStatusChangeHandler(StatusChangeHandler)
  299. * @see AbstractComponent#setComponentError(ErrorMessage)
  300. * @param label
  301. * label to show validation status for the field
  302. * @return this binding, for chaining
  303. */
  304. public default Binding<BEAN, FIELDVALUE, TARGET> withStatusLabel(
  305. Label label) {
  306. return withStatusChangeHandler(event -> {
  307. label.setValue(event.getMessage().orElse(""));
  308. // Only show the label when validation has failed
  309. label.setVisible(
  310. ValidationStatus.ERROR.equals(event.getStatus()));
  311. });
  312. }
  313. /**
  314. * Sets a {@link StatusChangeHandler} to track validation status
  315. * changes.
  316. * <p>
  317. * The validation state of each field is updated whenever the user
  318. * modifies the value of that field. The validation state is by default
  319. * shown using {@link AbstractComponent#setComponentError} which is used
  320. * by the layout that the field is shown in. Most built-in layouts will
  321. * show this as a red exclamation mark icon next to the component, so
  322. * that hovering or tapping the icon shows a tooltip with the message
  323. * text.
  324. * <p>
  325. * This method allows to customize the way a binder displays error
  326. * messages to get more flexibility than what
  327. * {@link AbstractComponent#setComponentError} provides (it replaces the
  328. * default behavior).
  329. * <p>
  330. * The method may be called only once. It means there is no chain unlike
  331. * {@link #withValidator(Validator)} or
  332. * {@link #withConverter(Converter)}. Also it means that the shorthand
  333. * method {@link #withStatusLabel(Label)} also may not be called after
  334. * this method.
  335. *
  336. * @see #withStatusLabel(Label)
  337. * @see AbstractComponent#setComponentError(ErrorMessage)
  338. * @param handler
  339. * status change handler
  340. * @return this binding, for chaining
  341. */
  342. public Binding<BEAN, FIELDVALUE, TARGET> withStatusChangeHandler(
  343. StatusChangeHandler handler);
  344. /**
  345. * Validates the field value and returns a {@code Result} instance
  346. * representing the outcome of the validation.
  347. *
  348. * @see Binder#validate()
  349. * @see Validator#apply(Object)
  350. *
  351. * @return the validation result.
  352. */
  353. public Result<TARGET> validate();
  354. }
  355. /**
  356. * An internal implementation of {@code Binding}.
  357. *
  358. * @param <BEAN>
  359. * the bean type, must match the Binder bean type
  360. * @param <FIELDVALUE>
  361. * the value type of the field
  362. * @param <TARGET>
  363. * the target data type of the binding, matches the field type
  364. * until a converter has been set
  365. */
  366. protected static class BindingImpl<BEAN, FIELDVALUE, TARGET>
  367. implements Binding<BEAN, FIELDVALUE, TARGET> {
  368. private final Binder<BEAN> binder;
  369. private final HasValue<FIELDVALUE> field;
  370. private Registration onValueChange;
  371. private StatusChangeHandler statusChangeHandler;
  372. private boolean isStatusHandlerChanged;
  373. private Function<BEAN, TARGET> getter;
  374. private BiConsumer<BEAN, TARGET> setter;
  375. /**
  376. * Contains all converters and validators chained together in the
  377. * correct order.
  378. */
  379. private Converter<FIELDVALUE, TARGET> converterValidatorChain;
  380. /**
  381. * Creates a new binding associated with the given field. Initializes
  382. * the binding with the given converter chain and status change handler.
  383. *
  384. * @param binder
  385. * the binder this instance is connected to, not null
  386. * @param field
  387. * the field to bind, not null
  388. * @param converterValidatorChain
  389. * the converter/validator chain to use, not null
  390. * @param statusChangeHandler
  391. * the handler to track validation status, not null
  392. */
  393. protected BindingImpl(Binder<BEAN> binder, HasValue<FIELDVALUE> field,
  394. Converter<FIELDVALUE, TARGET> converterValidatorChain,
  395. StatusChangeHandler statusChangeHandler) {
  396. this.field = field;
  397. this.binder = binder;
  398. this.converterValidatorChain = converterValidatorChain;
  399. this.statusChangeHandler = statusChangeHandler;
  400. }
  401. @Override
  402. public void bind(Function<BEAN, TARGET> getter,
  403. BiConsumer<BEAN, TARGET> setter) {
  404. checkUnbound();
  405. Objects.requireNonNull(getter, "getter cannot be null");
  406. this.getter = getter;
  407. this.setter = setter;
  408. getBinder().bindings.add(this);
  409. getBinder().getBean().ifPresent(this::bind);
  410. }
  411. @Override
  412. public Binding<BEAN, FIELDVALUE, TARGET> withValidator(
  413. Validator<? super TARGET> validator) {
  414. checkUnbound();
  415. Objects.requireNonNull(validator, "validator cannot be null");
  416. converterValidatorChain = converterValidatorChain
  417. .chain(new ValidatorAsConverter<>(validator));
  418. return this;
  419. }
  420. @Override
  421. public <NEWTARGET> Binding<BEAN, FIELDVALUE, NEWTARGET> withConverter(
  422. Converter<TARGET, NEWTARGET> converter) {
  423. checkUnbound();
  424. Objects.requireNonNull(converter, "converter cannot be null");
  425. return getBinder().createBinding(getField(),
  426. converterValidatorChain.chain(converter),
  427. statusChangeHandler);
  428. }
  429. @Override
  430. public Binding<BEAN, FIELDVALUE, TARGET> withStatusChangeHandler(
  431. StatusChangeHandler handler) {
  432. checkUnbound();
  433. Objects.requireNonNull(handler, "handler cannot be null");
  434. if (isStatusHandlerChanged) {
  435. throw new IllegalStateException(
  436. "A StatusChangeHandler has already been set");
  437. }
  438. isStatusHandlerChanged = true;
  439. statusChangeHandler = handler;
  440. return this;
  441. }
  442. @Override
  443. public HasValue<FIELDVALUE> getField() {
  444. return field;
  445. }
  446. /**
  447. * Returns the {@code Binder} connected to this {@code Binding}
  448. * instance.
  449. *
  450. * @return the binder
  451. */
  452. protected Binder<BEAN> getBinder() {
  453. return binder;
  454. }
  455. /**
  456. * Throws if this binding is already completed and cannot be modified
  457. * anymore.
  458. *
  459. * @throws IllegalStateException
  460. * if this binding is already bound
  461. */
  462. protected void checkUnbound() {
  463. if (getter != null) {
  464. throw new IllegalStateException(
  465. "cannot modify binding: already bound to a property");
  466. }
  467. }
  468. private void bind(BEAN bean) {
  469. setFieldValue(bean);
  470. onValueChange = getField()
  471. .addValueChangeListener(e -> storeFieldValue(bean, true));
  472. }
  473. @Override
  474. public Result<TARGET> validate() {
  475. Result<TARGET> dataValue = getTargetValue();
  476. fireStatusChangeEvent(dataValue);
  477. return dataValue;
  478. }
  479. /**
  480. * Returns the field value run through all converters and validators.
  481. *
  482. * @return a result containing the validated and converted value or
  483. * describing an error
  484. */
  485. private Result<TARGET> getTargetValue() {
  486. FIELDVALUE fieldValue = field.getValue();
  487. Result<TARGET> dataValue = converterValidatorChain.convertToModel(
  488. fieldValue, ((AbstractComponent) field).getLocale());
  489. return dataValue;
  490. }
  491. private void unbind() {
  492. onValueChange.remove();
  493. }
  494. /**
  495. * Sets the field value by invoking the getter function on the given
  496. * bean.
  497. *
  498. * @param bean
  499. * the bean to fetch the property value from
  500. */
  501. private void setFieldValue(BEAN bean) {
  502. assert bean != null;
  503. getField().setValue(convertDataToFieldType(bean));
  504. }
  505. private FIELDVALUE convertDataToFieldType(BEAN bean) {
  506. return converterValidatorChain.convertToPresentation(
  507. getter.apply(bean),
  508. ((AbstractComponent) getField()).getLocale());
  509. }
  510. /**
  511. * Saves the field value by invoking the setter function on the given
  512. * bean, if the value passes all registered validators. Optionally runs
  513. * item level validators if all field validators pass.
  514. *
  515. * @param bean
  516. * the bean to set the property value to
  517. * @param runBeanLevelValidation
  518. * <code>true</code> to run item level validators if all
  519. * field validators pass, <code>false</code> to always skip
  520. * item level validators
  521. */
  522. private void storeFieldValue(BEAN bean,
  523. boolean runBeanLevelValidation) {
  524. assert bean != null;
  525. if (setter != null) {
  526. getTargetValue().ifOk(value -> setBeanValue(bean, value));
  527. }
  528. if (runBeanLevelValidation && !getBinder().bindings.stream()
  529. .map(BindingImpl::getTargetValue)
  530. .anyMatch(Result::isError)) {
  531. List<ValidationError<?>> errors = binder.validateItem(bean);
  532. // TODO: Pass errors to Binder statusChangeHandler once that is
  533. // available
  534. }
  535. }
  536. private void setBeanValue(BEAN bean, TARGET value) {
  537. setter.accept(bean, value);
  538. }
  539. private void fireStatusChangeEvent(Result<TARGET> result) {
  540. ValidationStatusChangeEvent event = new ValidationStatusChangeEvent(
  541. getField(),
  542. result.isError() ? ValidationStatus.ERROR
  543. : ValidationStatus.OK,
  544. result.getMessage().orElse(null));
  545. statusChangeHandler.accept(event);
  546. }
  547. }
  548. /**
  549. * Wraps a validator as a converter.
  550. * <p>
  551. * The type of the validator must be of the same type as this converter or a
  552. * super type of it.
  553. *
  554. * @param <T>
  555. * the type of the converter
  556. */
  557. private static class ValidatorAsConverter<T> implements Converter<T, T> {
  558. private Validator<? super T> validator;
  559. /**
  560. * Creates a new converter wrapping the given validator.
  561. *
  562. * @param validator
  563. * the validator to wrap
  564. */
  565. public ValidatorAsConverter(Validator<? super T> validator) {
  566. this.validator = validator;
  567. }
  568. @Override
  569. public Result<T> convertToModel(T value, Locale locale) {
  570. Result<? super T> validationResult = validator.apply(value);
  571. if (validationResult.isError()) {
  572. return Result.error(validationResult.getMessage().get());
  573. } else {
  574. return Result.ok(value);
  575. }
  576. }
  577. @Override
  578. public T convertToPresentation(T value, Locale locale) {
  579. return value;
  580. }
  581. }
  582. private BEAN bean;
  583. private final Set<BindingImpl<BEAN, ?, ?>> bindings = new LinkedHashSet<>();
  584. private final List<Validator<? super BEAN>> validators = new ArrayList<>();
  585. /**
  586. * Returns an {@code Optional} of the bean that has been bound with
  587. * {@link #bind}, or an empty optional if a bean is not currently bound.
  588. *
  589. * @return the currently bound bean if any
  590. */
  591. public Optional<BEAN> getBean() {
  592. return Optional.ofNullable(bean);
  593. }
  594. /**
  595. * Creates a new binding for the given field. The returned binding may be
  596. * further configured before invoking
  597. * {@link Binding#bind(Function, BiConsumer) Binding.bind} which completes
  598. * the binding. Until {@code Binding.bind} is called, the binding has no
  599. * effect.
  600. *
  601. * @param <FIELDVALUE>
  602. * the value type of the field
  603. * @param field
  604. * the field to be bound, not null
  605. * @return the new binding
  606. */
  607. public <FIELDVALUE> Binding<BEAN, FIELDVALUE, FIELDVALUE> forField(
  608. HasValue<FIELDVALUE> field) {
  609. Objects.requireNonNull(field, "field cannot be null");
  610. return createBinding(field, Converter.identity(),
  611. this::handleValidationStatusChange);
  612. }
  613. /**
  614. * Binds a field to a bean property represented by the given getter and
  615. * setter pair. The functions are used to update the field value from the
  616. * property and to store the field value to the property, respectively.
  617. * <p>
  618. * Use the {@link #forField(HasValue)} overload instead if you want to
  619. * further configure the new binding.
  620. * <p>
  621. * When a bean is bound with {@link Binder#bind(BEAN)}, the field value is
  622. * set to the return value of the given getter. The property value is then
  623. * updated via the given setter whenever the field value changes. The setter
  624. * may be null; in that case the property value is never updated and the
  625. * binding is said to be <i>read-only</i>.
  626. * <p>
  627. * If the Binder is already bound to some item, the newly bound field is
  628. * associated with the corresponding bean property as described above.
  629. * <p>
  630. * The getter and setter can be arbitrary functions, for instance
  631. * implementing user-defined conversion or validation. However, in the most
  632. * basic use case you can simply pass a pair of method references to this
  633. * method as follows:
  634. *
  635. * <pre>
  636. * class Person {
  637. * public String getName() { ... }
  638. * public void setName(String name) { ... }
  639. * }
  640. *
  641. * TextField nameField = new TextField();
  642. * binder.bind(nameField, Person::getName, Person::setName);
  643. * </pre>
  644. *
  645. * @param <FIELDVALUE>
  646. * the value type of the field
  647. * @param field
  648. * the field to bind, not null
  649. * @param getter
  650. * the function to get the value of the property to the field,
  651. * not null
  652. * @param setter
  653. * the function to save the field value to the property or null
  654. * if read-only
  655. */
  656. public <FIELDVALUE> void bind(HasValue<FIELDVALUE> field,
  657. Function<BEAN, FIELDVALUE> getter,
  658. BiConsumer<BEAN, FIELDVALUE> setter) {
  659. forField(field).bind(getter, setter);
  660. }
  661. /**
  662. * Binds the given bean to all the fields added to this Binder. To remove
  663. * the binding, call {@link #unbind()}.
  664. * <p>
  665. * When a bean is bound, the field values are updated by invoking their
  666. * corresponding getter functions. Any changes to field values are reflected
  667. * back to their corresponding property values of the bean as long as the
  668. * bean is bound.
  669. * <p>
  670. * Any change made in the fields also runs validation for the field
  671. * {@link Binding} and bean level validation for this binder (bean level
  672. * validators are added using {@link Binder#withValidator(Validator)}.
  673. *
  674. * @see #load(Object)
  675. * @see #save(Object)
  676. * @see #saveIfValid(Object)
  677. *
  678. * @param bean
  679. * the bean to edit, not null
  680. */
  681. public void bind(BEAN bean) {
  682. Objects.requireNonNull(bean, "bean cannot be null");
  683. unbind();
  684. this.bean = bean;
  685. bindings.forEach(b -> b.bind(bean));
  686. }
  687. /**
  688. * Unbinds the currently bound bean if any. If there is no bound bean, does
  689. * nothing.
  690. */
  691. public void unbind() {
  692. if (bean != null) {
  693. bean = null;
  694. bindings.forEach(BindingImpl::unbind);
  695. }
  696. }
  697. /**
  698. * Reads the bound property values from the given bean to the corresponding
  699. * fields.
  700. * <p>
  701. * The bean is not otherwise associated with this binder; in particular its
  702. * property values are not bound to the field value changes. To achieve
  703. * that, use {@link #bind(BEAN)}.
  704. *
  705. * @see #bind(Object)
  706. * @see #saveIfValid(Object)
  707. * @see #save(Object)
  708. *
  709. * @param bean
  710. * the bean whose property values to read, not null
  711. */
  712. public void load(BEAN bean) {
  713. Objects.requireNonNull(bean, "bean cannot be null");
  714. bindings.forEach(binding -> binding.setFieldValue(bean));
  715. }
  716. /**
  717. * Saves changes from the bound fields to the given bean if all validators
  718. * (binding and bean level) pass.
  719. * <p>
  720. * If any field binding validator fails, no values are saved and a
  721. * {@code ValidationException} is thrown.
  722. * <p>
  723. * If all field level validators pass, the given bean is updated and bean
  724. * level validators are run on the updated item. If any bean level validator
  725. * fails, the bean updates are reverted and a {@code ValidationException} is
  726. * thrown.
  727. *
  728. * @see #saveIfValid(Object)
  729. * @see #load(Object)
  730. * @see #bind(Object)
  731. *
  732. * @param bean
  733. * the object to which to save the field values, not null
  734. * @throws ValidationException
  735. * if some of the bound field values fail to validate
  736. */
  737. public void save(BEAN bean) throws ValidationException {
  738. List<ValidationError<?>> errors = doSaveIfValid(bean);
  739. if (!errors.isEmpty()) {
  740. throw new ValidationException(errors);
  741. }
  742. }
  743. /**
  744. * Saves changes from the bound fields to the given bean if all validators
  745. * (binding and bean level) pass.
  746. * <p>
  747. * If any field binding validator fails, no values are saved and
  748. * <code>false</code> is returned.
  749. * <p>
  750. * If all field level validators pass, the given bean is updated and bean
  751. * level validators are run on the updated item. If any bean level validator
  752. * fails, the bean updates are reverted and <code>false</code> is returned.
  753. *
  754. * @see #save(Object)
  755. * @see #load(Object)
  756. * @see #bind(Object)
  757. *
  758. * @param bean
  759. * the object to which to save the field values, not null
  760. * @return {@code true} if there was no validation errors and the bean was
  761. * updated, {@code false} otherwise
  762. */
  763. public boolean saveIfValid(BEAN bean) {
  764. return doSaveIfValid(bean).isEmpty();
  765. }
  766. /**
  767. * Saves the field values into the given bean if all field level validators
  768. * pass. Runs bean level validators on the bean after saving.
  769. *
  770. * @param bean
  771. * the bean to save field values into
  772. * @return a list of field validation errors if such occur, otherwise a list
  773. * of bean validation errors.
  774. */
  775. private List<ValidationError<?>> doSaveIfValid(BEAN bean) {
  776. Objects.requireNonNull(bean, "bean cannot be null");
  777. // First run fields level validation
  778. List<ValidationError<?>> errors = validateBindings();
  779. // If no validation errors then update bean
  780. if (!errors.isEmpty()) {
  781. return errors;
  782. }
  783. // Save old bean values so we can restore them if validators fail
  784. Map<Binding<BEAN, ?, ?>, Object> oldValues = new HashMap<>();
  785. bindings.forEach(binding -> oldValues.put(binding,
  786. binding.convertDataToFieldType(bean)));
  787. bindings.forEach(binding -> binding.storeFieldValue(bean, false));
  788. // Now run bean level validation against the updated bean
  789. List<ValidationError<?>> itemValidatorErrors = validateItem(bean);
  790. if (!itemValidatorErrors.isEmpty()) {
  791. // Item validator failed, revert values
  792. bindings.forEach((BindingImpl binding) -> binding.setBeanValue(bean,
  793. oldValues.get(binding)));
  794. }
  795. return itemValidatorErrors;
  796. }
  797. /**
  798. * Adds an item level validator.
  799. * <p>
  800. * Item level validators are applied on the item instance after the item is
  801. * updated. If the validators fail, the item instance is reverted to its
  802. * previous state.
  803. *
  804. * @see #save(Object)
  805. * @see #saveIfValid(Object)
  806. *
  807. * @param validator
  808. * the validator to add, not null
  809. * @return this binder, for chaining
  810. */
  811. public Binder<BEAN> withValidator(Validator<? super BEAN> validator) {
  812. Objects.requireNonNull(validator, "validator cannot be null");
  813. validators.add(validator);
  814. return this;
  815. }
  816. /**
  817. * Validates the values of all bound fields and returns the result of the
  818. * validation as a list of validation errors.
  819. * <p>
  820. * If all field level validators pass, and {@link #bind(Object)} has been
  821. * used to bind to an item, item level validators are run for that bean.
  822. * Item level validators are ignored if there is no bound item or if any
  823. * field level validator fails.
  824. * <p>
  825. * Validation is successful if the returned list is empty.
  826. *
  827. * @return a list of validation errors or an empty list if validation
  828. * succeeded
  829. */
  830. public List<ValidationError<?>> validate() {
  831. List<ValidationError<?>> errors = validateBindings();
  832. if (!errors.isEmpty()) {
  833. return errors;
  834. }
  835. if (bean != null) {
  836. return validateItem(bean);
  837. }
  838. return Collections.emptyList();
  839. }
  840. /**
  841. * Validates the bindings and returns the result of the validation as a list
  842. * of validation errors.
  843. * <p>
  844. * If all validators pass, the resulting list is empty.
  845. * <p>
  846. * Does not run bean validators.
  847. *
  848. * @see #validateItem(Object)
  849. *
  850. * @return a list of validation errors or an empty list if validation
  851. * succeeded
  852. */
  853. private List<ValidationError<?>> validateBindings() {
  854. List<ValidationError<?>> resultErrors = new ArrayList<>();
  855. for (BindingImpl<?, ?, ?> binding : bindings) {
  856. binding.validate().ifError(errorMessage -> resultErrors
  857. .add(new ValidationError<>(binding,
  858. binding.getField().getValue(), errorMessage)));
  859. }
  860. return resultErrors;
  861. }
  862. /**
  863. * Validates the {@code item} using item validators added using
  864. * {@link #withValidator(Validator)} and returns the result of the
  865. * validation as a list of validation errors.
  866. * <p>
  867. * If all validators pass, the resulting list is empty.
  868. *
  869. * @see #withValidator(Validator)
  870. *
  871. * @param bean
  872. * the bean to validate
  873. * @return a list of validation errors or an empty list if validation
  874. * succeeded
  875. */
  876. private List<ValidationError<?>> validateItem(BEAN bean) {
  877. Objects.requireNonNull(bean, "bean cannot be null");
  878. return validators.stream().map(validator -> validator.apply(bean))
  879. .filter(Result::isError).map(res -> new ValidationError<>(this,
  880. bean, res.getMessage().get()))
  881. .collect(Collectors.toList());
  882. }
  883. /**
  884. * Creates a new binding with the given field.
  885. *
  886. * @param <FIELDVALUE>
  887. * the value type of the field
  888. * @param <TARGET>
  889. * the target data type
  890. * @param field
  891. * the field to bind, not null
  892. * @param converter
  893. * the converter for converting between FIELDVALUE and TARGET
  894. * types, not null
  895. * @param handler
  896. * the handler to notify of status changes, not null
  897. * @return the new incomplete binding
  898. */
  899. protected <FIELDVALUE, TARGET> BindingImpl<BEAN, FIELDVALUE, TARGET> createBinding(
  900. HasValue<FIELDVALUE> field, Converter<FIELDVALUE, TARGET> converter,
  901. StatusChangeHandler handler) {
  902. return new BindingImpl<>(this, field, converter, handler);
  903. }
  904. /**
  905. * Clears the error condition of the given field, if any. The default
  906. * implementation clears the
  907. * {@link AbstractComponent#setComponentError(ErrorMessage) component error}
  908. * of the field if it is a Component, otherwise does nothing.
  909. *
  910. * @param field
  911. * the field with an invalid value
  912. */
  913. protected void clearError(HasValue<?> field) {
  914. if (field instanceof AbstractComponent) {
  915. ((AbstractComponent) field).setComponentError(null);
  916. }
  917. }
  918. /**
  919. * Handles a validation error emitted when trying to save the value of the
  920. * given field. The default implementation sets the
  921. * {@link AbstractComponent#setComponentError(ErrorMessage) component error}
  922. * of the field if it is a Component, otherwise does nothing.
  923. *
  924. * @param field
  925. * the field with the invalid value
  926. * @param error
  927. * the error message to set
  928. */
  929. protected void handleError(HasValue<?> field, String error) {
  930. if (field instanceof AbstractComponent) {
  931. ((AbstractComponent) field).setComponentError(new UserError(error));
  932. }
  933. }
  934. /**
  935. * Default {@link StatusChangeHandler} functional method implementation.
  936. *
  937. * @param event
  938. * the validation event
  939. */
  940. protected void handleValidationStatusChange(
  941. ValidationStatusChangeEvent event) {
  942. HasValue<?> source = event.getSource();
  943. clearError(source);
  944. if (Objects.equals(ValidationStatus.ERROR, event.getStatus())) {
  945. handleError(source, event.getMessage().get());
  946. }
  947. }
  948. }