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

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