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.

FieldGroup.java 42KB

Migrate 7.7.5 branch patches to v8. (#7969) * Prevent adding several scrollbar handlers (#19189). Change-Id: Ib0cc6c6835aab6d263f153362a328bcf2be7bc5c * Prevent adding several scrollbar handlers (#19189). * Keep expand ratio for last row/column when reducing grid layout size (#20297) Change-Id: Iff53a803596f4fc1eae8e4bfa307b9c1f4df961a * Fixed drag and drop failure when message dragged from email client (#20451) When dragging message form email client on Windows, item.webkitGetAsEntry() might return null creating NPE on the client side. Added additional checks for this situation. Change-Id: I569f7e6d0d7b137f24be53d1fbce384695ae8c73 * Change expected pre-release version number pattern in publish report Change-Id: Icdacecc490d2490ea9e262f5c5736c1dece2a89d * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java * Fixed touch scrolling issue in Surface and WP devices (#18737) Fixed by using pointerevents instead of touchevents when the browser is IE11, or Edge. Also added touch-action: none; css rules into escalator.css to prevent default touch behaviour on IE11 and Edge. Does not affect IE8 to IE10 browsers, behaviour on those will stay the same as before the fix. No new unit tests since we do not have automatic touch testing possibilities yet. Please test manually with Surface: IE11 and Edge, use for example uitest: com.vaadin.tests.components.grid.basics.GridBasicsomponents.grid.basics.GridBasics Change-Id: Iddbf1852e6ffafc855f749d6f4ebb235ed0f5703 * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 # Conflicts: # client/src/main/java/com/vaadin/client/connectors/GridConnector.java # client/src/main/java/com/vaadin/client/widgets/Grid.java # server/src/main/java/com/vaadin/ui/Grid.java # shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java # themes/src/main/themes/VAADIN/themes/base/grid/grid.scss # uitest/src/main/java/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java Change-Id: Ieca56121875198ed559a41c143b28926e2695433 * Fix NPE in case some items don't contain all properties of Grid. This could occur in when parent is a different entity than its children in hierarchical data. Change-Id: Icd53b5b5e5544a3680d0cd99702ab78224b2dc08 # Conflicts: # server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java # server/src/main/java/com/vaadin/ui/Grid.java * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 * Removed V8 VTextField unused import, forgotten @RunLocally. * Don't rely on selenium "sendKeys" behavior. * Revert "Change expected pre-release version number pattern in publish report" This reverts commit 8df27b952dddb691aead6a633c5b3724c98bf343. * Migrate TextField/TextArea patch from 7.7 to master (modern components) Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50
7 years ago
Migrate 7.7.5 branch patches to v8. (#7969) * Prevent adding several scrollbar handlers (#19189). Change-Id: Ib0cc6c6835aab6d263f153362a328bcf2be7bc5c * Prevent adding several scrollbar handlers (#19189). * Keep expand ratio for last row/column when reducing grid layout size (#20297) Change-Id: Iff53a803596f4fc1eae8e4bfa307b9c1f4df961a * Fixed drag and drop failure when message dragged from email client (#20451) When dragging message form email client on Windows, item.webkitGetAsEntry() might return null creating NPE on the client side. Added additional checks for this situation. Change-Id: I569f7e6d0d7b137f24be53d1fbce384695ae8c73 * Change expected pre-release version number pattern in publish report Change-Id: Icdacecc490d2490ea9e262f5c5736c1dece2a89d * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java * Fixed touch scrolling issue in Surface and WP devices (#18737) Fixed by using pointerevents instead of touchevents when the browser is IE11, or Edge. Also added touch-action: none; css rules into escalator.css to prevent default touch behaviour on IE11 and Edge. Does not affect IE8 to IE10 browsers, behaviour on those will stay the same as before the fix. No new unit tests since we do not have automatic touch testing possibilities yet. Please test manually with Surface: IE11 and Edge, use for example uitest: com.vaadin.tests.components.grid.basics.GridBasicsomponents.grid.basics.GridBasics Change-Id: Iddbf1852e6ffafc855f749d6f4ebb235ed0f5703 * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 # Conflicts: # client/src/main/java/com/vaadin/client/connectors/GridConnector.java # client/src/main/java/com/vaadin/client/widgets/Grid.java # server/src/main/java/com/vaadin/ui/Grid.java # shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java # themes/src/main/themes/VAADIN/themes/base/grid/grid.scss # uitest/src/main/java/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java Change-Id: Ieca56121875198ed559a41c143b28926e2695433 * Fix NPE in case some items don't contain all properties of Grid. This could occur in when parent is a different entity than its children in hierarchical data. Change-Id: Icd53b5b5e5544a3680d0cd99702ab78224b2dc08 # Conflicts: # server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java # server/src/main/java/com/vaadin/ui/Grid.java * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 * Removed V8 VTextField unused import, forgotten @RunLocally. * Don't rely on selenium "sendKeys" behavior. * Revert "Change expected pre-release version number pattern in publish report" This reverts commit 8df27b952dddb691aead6a633c5b3724c98bf343. * Migrate TextField/TextArea patch from 7.7 to master (modern components) Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50
7 years ago
Migrate 7.7.5 branch patches to v8. (#7969) * Prevent adding several scrollbar handlers (#19189). Change-Id: Ib0cc6c6835aab6d263f153362a328bcf2be7bc5c * Prevent adding several scrollbar handlers (#19189). * Keep expand ratio for last row/column when reducing grid layout size (#20297) Change-Id: Iff53a803596f4fc1eae8e4bfa307b9c1f4df961a * Fixed drag and drop failure when message dragged from email client (#20451) When dragging message form email client on Windows, item.webkitGetAsEntry() might return null creating NPE on the client side. Added additional checks for this situation. Change-Id: I569f7e6d0d7b137f24be53d1fbce384695ae8c73 * Change expected pre-release version number pattern in publish report Change-Id: Icdacecc490d2490ea9e262f5c5736c1dece2a89d * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/main/java/com/vaadin/tests/components/textfield/TextChangeEvents.java * Fixed touch scrolling issue in Surface and WP devices (#18737) Fixed by using pointerevents instead of touchevents when the browser is IE11, or Edge. Also added touch-action: none; css rules into escalator.css to prevent default touch behaviour on IE11 and Edge. Does not affect IE8 to IE10 browsers, behaviour on those will stay the same as before the fix. No new unit tests since we do not have automatic touch testing possibilities yet. Please test manually with Surface: IE11 and Edge, use for example uitest: com.vaadin.tests.components.grid.basics.GridBasicsomponents.grid.basics.GridBasics Change-Id: Iddbf1852e6ffafc855f749d6f4ebb235ed0f5703 * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 # Conflicts: # client/src/main/java/com/vaadin/client/connectors/GridConnector.java # client/src/main/java/com/vaadin/client/widgets/Grid.java # server/src/main/java/com/vaadin/ui/Grid.java # shared/src/main/java/com/vaadin/shared/ui/grid/GridState.java # themes/src/main/themes/VAADIN/themes/base/grid/grid.scss # uitest/src/main/java/com/vaadin/tests/components/grid/basicfeatures/GridBasicFeatures.java Change-Id: Ieca56121875198ed559a41c143b28926e2695433 * Fix NPE in case some items don't contain all properties of Grid. This could occur in when parent is a different entity than its children in hierarchical data. Change-Id: Icd53b5b5e5544a3680d0cd99702ab78224b2dc08 # Conflicts: # server/src/main/java/com/vaadin/data/fieldgroup/FieldGroup.java # server/src/main/java/com/vaadin/ui/Grid.java * Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50 # Conflicts: # client/src/main/java/com/vaadin/client/ui/VTextField.java # uitest/src/test/java/com/vaadin/tests/components/textfield/TextChangeEventsTest.java * Add lazy/simple resize mode to Grid (#20108) Change-Id: I47427efc28c350382dba8c1f50fd332a3f4585e4 * Removed V8 VTextField unused import, forgotten @RunLocally. * Don't rely on selenium "sendKeys" behavior. * Revert "Change expected pre-release version number pattern in publish report" This reverts commit 8df27b952dddb691aead6a633c5b3724c98bf343. * Migrate TextField/TextArea patch from 7.7 to master (modern components) Mark TextField/TextArea as busy when a text change event is pending (#20469) Change-Id: I404985ae0be1e7dc65171b610032f8649e700f50
7 years ago
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280
  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.v7.data.fieldgroup;
  17. import java.io.Serializable;
  18. import java.lang.reflect.InvocationTargetException;
  19. import java.util.ArrayList;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.LinkedHashMap;
  24. import java.util.List;
  25. import java.util.Map;
  26. import com.vaadin.annotations.PropertyId;
  27. import com.vaadin.util.ReflectTools;
  28. import com.vaadin.v7.data.Item;
  29. import com.vaadin.v7.data.Property;
  30. import com.vaadin.v7.data.Validator.InvalidValueException;
  31. import com.vaadin.v7.data.util.TransactionalPropertyWrapper;
  32. import com.vaadin.v7.ui.AbstractField;
  33. import com.vaadin.v7.ui.DefaultFieldFactory;
  34. import com.vaadin.v7.ui.Field;
  35. /**
  36. * FieldGroup provides an easy way of binding fields to data and handling
  37. * commits of these fields.
  38. * <p>
  39. * The typical use case is to create a layout outside the FieldGroup and then
  40. * use FieldGroup to bind the fields to a data source.
  41. * </p>
  42. * <p>
  43. * {@link FieldGroup} is not a UI component so it cannot be added to a layout.
  44. * Using the buildAndBind methods {@link FieldGroup} can create fields for you
  45. * using a FieldGroupFieldFactory but you still have to add them to the correct
  46. * position in your layout.
  47. * </p>
  48. *
  49. * @author Vaadin Ltd
  50. * @since 7.0
  51. */
  52. @Deprecated
  53. public class FieldGroup implements Serializable {
  54. private Item itemDataSource;
  55. private boolean buffered = true;
  56. private boolean enabled = true;
  57. private boolean readOnly = false;
  58. private HashMap<Object, Field<?>> propertyIdToField = new HashMap<>();
  59. private LinkedHashMap<Field<?>, Object> fieldToPropertyId = new LinkedHashMap<>();
  60. private List<CommitHandler> commitHandlers = new ArrayList<>();
  61. /**
  62. * The field factory used by builder methods.
  63. */
  64. private FieldGroupFieldFactory fieldFactory = DefaultFieldGroupFieldFactory
  65. .get();
  66. /**
  67. * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a
  68. * data source for the field binder.
  69. *
  70. */
  71. public FieldGroup() {
  72. }
  73. /**
  74. * Constructs a field binder that uses the given data source.
  75. *
  76. * @param itemDataSource
  77. * The data source to bind the fields to
  78. */
  79. public FieldGroup(Item itemDataSource) {
  80. setItemDataSource(itemDataSource);
  81. }
  82. /**
  83. * Updates the item that is used by this FieldBinder. Rebinds all fields to
  84. * the properties in the new item.
  85. *
  86. * @param itemDataSource
  87. * The new item to use
  88. */
  89. public void setItemDataSource(Item itemDataSource) {
  90. this.itemDataSource = itemDataSource;
  91. bindFields();
  92. }
  93. /**
  94. * Binds all fields to the properties in the item in use.
  95. */
  96. protected void bindFields() {
  97. for (Field<?> f : fieldToPropertyId.keySet()) {
  98. bind(f, fieldToPropertyId.get(f));
  99. }
  100. }
  101. /**
  102. * Gets the item used by this FieldBinder. Note that you must call
  103. * {@link #commit()} for the item to be updated unless buffered mode has
  104. * been switched off.
  105. *
  106. * @see #setBuffered(boolean)
  107. * @see #commit()
  108. *
  109. * @return The item used by this FieldBinder
  110. */
  111. public Item getItemDataSource() {
  112. return itemDataSource;
  113. }
  114. /**
  115. * Checks the buffered mode for the bound fields.
  116. * <p>
  117. *
  118. * @see #setBuffered(boolean) for more details on buffered mode
  119. *
  120. * @see Field#isBuffered()
  121. * @return true if buffered mode is on, false otherwise
  122. *
  123. */
  124. public boolean isBuffered() {
  125. return buffered;
  126. }
  127. /**
  128. * Sets the buffered mode for the bound fields.
  129. * <p>
  130. * When buffered mode is on the item will not be updated until
  131. * {@link #commit()} is called. If buffered mode is off the item will be
  132. * updated once the fields are updated.
  133. * </p>
  134. * <p>
  135. * The default is to use buffered mode.
  136. * </p>
  137. *
  138. * @see Field#setBuffered(boolean)
  139. * @param buffered
  140. * true to turn on buffered mode, false otherwise
  141. */
  142. public void setBuffered(boolean buffered) {
  143. if (buffered == this.buffered) {
  144. return;
  145. }
  146. this.buffered = buffered;
  147. for (Field<?> field : getFields()) {
  148. field.setBuffered(buffered);
  149. }
  150. }
  151. /**
  152. * Returns the enabled status for the fields.
  153. * <p>
  154. * Note that this will not accurately represent the enabled status of all
  155. * fields if you change the enabled status of the fields through some other
  156. * method than {@link #setEnabled(boolean)}.
  157. *
  158. * @return true if the fields are enabled, false otherwise
  159. */
  160. public boolean isEnabled() {
  161. return enabled;
  162. }
  163. /**
  164. * Updates the enabled state of all bound fields.
  165. *
  166. * @param fieldsEnabled
  167. * true to enable all bound fields, false to disable them
  168. */
  169. public void setEnabled(boolean fieldsEnabled) {
  170. enabled = fieldsEnabled;
  171. for (Field<?> field : getFields()) {
  172. field.setEnabled(fieldsEnabled);
  173. }
  174. }
  175. /**
  176. * Returns the read only status that is used by default with all fields that
  177. * have a writable data source.
  178. * <p>
  179. * Note that this will not accurately represent the read only status of all
  180. * fields if you change the read only status of the fields through some
  181. * other method than {@link #setReadOnly(boolean)}.
  182. *
  183. * @return true if the fields are set to read only, false otherwise
  184. */
  185. public boolean isReadOnly() {
  186. return readOnly;
  187. }
  188. /**
  189. * Sets the read only state to the given value for all fields with writable
  190. * data source. Fields with read only data source will always be set to read
  191. * only.
  192. *
  193. * @param fieldsReadOnly
  194. * true to set the fields with writable data source to read only,
  195. * false to set them to read write
  196. */
  197. public void setReadOnly(boolean fieldsReadOnly) {
  198. readOnly = fieldsReadOnly;
  199. for (Field<?> field : getFields()) {
  200. if (field.getPropertyDataSource() == null
  201. || !field.getPropertyDataSource().isReadOnly()) {
  202. field.setReadOnly(fieldsReadOnly);
  203. } else {
  204. field.setReadOnly(true);
  205. }
  206. }
  207. }
  208. /**
  209. * Returns a collection of all fields that have been bound.
  210. * <p>
  211. * The fields are not returned in any specific order.
  212. * </p>
  213. *
  214. * @return A collection with all bound Fields
  215. */
  216. public Collection<Field<?>> getFields() {
  217. return fieldToPropertyId.keySet();
  218. }
  219. /**
  220. * Binds the field with the given propertyId from the current item. If an
  221. * item has not been set then the binding is postponed until the item is set
  222. * using {@link #setItemDataSource(Item)}.
  223. * <p>
  224. * This method also adds validators when applicable.
  225. * </p>
  226. *
  227. * @param field
  228. * The field to bind
  229. * @param propertyId
  230. * The propertyId to bind to the field
  231. * @throws BindException
  232. * If the field is null or the property id is already bound to
  233. * another field by this field binder
  234. */
  235. public void bind(Field<?> field, Object propertyId) throws BindException {
  236. throwIfFieldIsNull(field, propertyId);
  237. throwIfPropertyIdAlreadyBound(field, propertyId);
  238. fieldToPropertyId.put(field, propertyId);
  239. propertyIdToField.put(propertyId, field);
  240. if (itemDataSource == null) {
  241. clearField(field);
  242. // Will be bound when data source is set
  243. return;
  244. }
  245. field.setPropertyDataSource(
  246. wrapInTransactionalProperty(getItemProperty(propertyId)));
  247. configureField(field);
  248. }
  249. /**
  250. * Clears field and any possible existing binding.
  251. *
  252. * @param field
  253. * The field to be cleared
  254. */
  255. protected void clearField(Field<?> field) {
  256. // Clear any possible existing binding to clear the field
  257. field.setPropertyDataSource(null);
  258. boolean fieldReadOnly = field.isReadOnly();
  259. if (!fieldReadOnly) {
  260. field.clear();
  261. } else {
  262. // Temporarily make the field read-write so we can clear the
  263. // value. Needed because setPropertyDataSource(null) does not
  264. // currently clear the field
  265. // (https://dev.vaadin.com/ticket/14733)
  266. field.setReadOnly(false);
  267. field.clear();
  268. field.setReadOnly(true);
  269. }
  270. }
  271. /**
  272. * Wrap property to transactional property.
  273. */
  274. protected <T> Property.Transactional<T> wrapInTransactionalProperty(
  275. Property<T> itemProperty) {
  276. return new TransactionalPropertyWrapper<>(itemProperty);
  277. }
  278. private void throwIfFieldIsNull(Field<?> field, Object propertyId) {
  279. if (field == null) {
  280. throw new BindException(String.format(
  281. "Cannot bind property id '%s' to a null field.",
  282. propertyId));
  283. }
  284. }
  285. private void throwIfPropertyIdAlreadyBound(Field<?> field,
  286. Object propertyId) {
  287. if (propertyIdToField.containsKey(propertyId)
  288. && propertyIdToField.get(propertyId) != field) {
  289. throw new BindException("Property id " + propertyId
  290. + " is already bound to another field");
  291. }
  292. }
  293. /**
  294. * Gets the property with the given property id from the item.
  295. *
  296. * @param propertyId
  297. * The id if the property to find
  298. * @return The property with the given id from the item
  299. * @throws BindException
  300. * If the property was not found in the item or no item has been
  301. * set
  302. */
  303. protected Property getItemProperty(Object propertyId) throws BindException {
  304. Item item = getItemDataSource();
  305. if (item == null) {
  306. throw new BindException("Could not lookup property with id "
  307. + propertyId + " as no item has been set");
  308. }
  309. Property<?> p = item.getItemProperty(propertyId);
  310. if (p == null) {
  311. throw new BindException("A property with id " + propertyId
  312. + " was not found in the item");
  313. }
  314. return p;
  315. }
  316. /**
  317. * Detaches the field from its property id and removes it from this
  318. * FieldBinder.
  319. * <p>
  320. * Note that the field is not detached from its property data source if it
  321. * is no longer connected to the same property id it was bound to using this
  322. * FieldBinder.
  323. *
  324. * @param field
  325. * The field to detach
  326. * @throws BindException
  327. * If the field is not bound by this field binder or not bound
  328. * to the correct property id
  329. */
  330. public void unbind(Field<?> field) throws BindException {
  331. Object propertyId = fieldToPropertyId.get(field);
  332. if (propertyId == null) {
  333. throw new BindException(
  334. "The given field is not part of this FieldBinder");
  335. }
  336. TransactionalPropertyWrapper<?> wrapper = null;
  337. Property fieldDataSource = field.getPropertyDataSource();
  338. if (fieldDataSource instanceof TransactionalPropertyWrapper) {
  339. wrapper = (TransactionalPropertyWrapper<?>) fieldDataSource;
  340. fieldDataSource = ((TransactionalPropertyWrapper<?>) fieldDataSource)
  341. .getWrappedProperty();
  342. }
  343. if (getItemDataSource() != null
  344. && fieldDataSource == getItemProperty(propertyId)) {
  345. if (null != wrapper) {
  346. wrapper.detachFromProperty();
  347. }
  348. field.setPropertyDataSource(null);
  349. }
  350. fieldToPropertyId.remove(field);
  351. propertyIdToField.remove(propertyId);
  352. }
  353. /**
  354. * Configures a field with the settings set for this FieldBinder.
  355. * <p>
  356. * By default this updates the buffered, read only and enabled state of the
  357. * field. Also adds validators when applicable. Fields with read only data
  358. * source are always configured as read only.
  359. *
  360. * @param field
  361. * The field to update
  362. */
  363. protected void configureField(Field<?> field) {
  364. field.setBuffered(isBuffered());
  365. field.setEnabled(isEnabled());
  366. if (field.getPropertyDataSource().isReadOnly()) {
  367. field.setReadOnly(true);
  368. } else {
  369. field.setReadOnly(isReadOnly());
  370. }
  371. }
  372. /**
  373. * Gets the type of the property with the given property id.
  374. *
  375. * @param propertyId
  376. * The propertyId. Must be find
  377. * @return The type of the property
  378. */
  379. protected Class<?> getPropertyType(Object propertyId) throws BindException {
  380. if (getItemDataSource() == null) {
  381. throw new BindException("Property type for '" + propertyId
  382. + "' could not be determined. No item data source has been set.");
  383. }
  384. Property<?> p = getItemDataSource().getItemProperty(propertyId);
  385. if (p == null) {
  386. throw new BindException("Property type for '" + propertyId
  387. + "' could not be determined. No property with that id was found.");
  388. }
  389. return p.getType();
  390. }
  391. /**
  392. * Returns a collection of all property ids that have been bound to fields.
  393. * <p>
  394. * Note that this will return property ids even before the item has been
  395. * set. In that case it returns the property ids that will be bound once the
  396. * item is set.
  397. * </p>
  398. * <p>
  399. * No guarantee is given for the order of the property ids
  400. * </p>
  401. *
  402. * @return A collection of bound property ids
  403. */
  404. public Collection<Object> getBoundPropertyIds() {
  405. return Collections.unmodifiableCollection(propertyIdToField.keySet());
  406. }
  407. /**
  408. * Returns a collection of all property ids that exist in the item set using
  409. * {@link #setItemDataSource(Item)} but have not been bound to fields.
  410. * <p>
  411. * Will always return an empty collection before an item has been set using
  412. * {@link #setItemDataSource(Item)}.
  413. * </p>
  414. * <p>
  415. * No guarantee is given for the order of the property ids
  416. * </p>
  417. *
  418. * @return A collection of property ids that have not been bound to fields
  419. */
  420. public Collection<Object> getUnboundPropertyIds() {
  421. if (getItemDataSource() == null) {
  422. return new ArrayList<>();
  423. }
  424. List<Object> unboundPropertyIds = new ArrayList<>();
  425. unboundPropertyIds.addAll(getItemDataSource().getItemPropertyIds());
  426. unboundPropertyIds.removeAll(propertyIdToField.keySet());
  427. return unboundPropertyIds;
  428. }
  429. /**
  430. * Commits all changes done to the bound fields.
  431. * <p>
  432. * Calls all {@link CommitHandler}s before and after committing the field
  433. * changes to the item data source. The whole commit is aborted and state is
  434. * restored to what it was before commit was called if any
  435. * {@link CommitHandler} throws a CommitException or there is a problem
  436. * committing the fields
  437. *
  438. * @throws CommitException
  439. * If the commit was aborted
  440. */
  441. public void commit() throws CommitException {
  442. if (!isBuffered()) {
  443. // Not using buffered mode, nothing to do
  444. return;
  445. }
  446. startTransactions();
  447. try {
  448. firePreCommitEvent();
  449. Map<Field<?>, InvalidValueException> invalidValueExceptions = commitFields();
  450. if (invalidValueExceptions.isEmpty()) {
  451. firePostCommitEvent();
  452. commitTransactions();
  453. } else {
  454. throw new FieldGroupInvalidValueException(
  455. invalidValueExceptions);
  456. }
  457. } catch (Exception e) {
  458. rollbackTransactions();
  459. throw new CommitException("Commit failed", this, e);
  460. }
  461. }
  462. /**
  463. * Tries to commit all bound fields one by one and gathers any validation
  464. * exceptions in a map, which is returned to the caller
  465. *
  466. * @return a propertyId to validation exception map which is empty if all
  467. * commits succeeded
  468. */
  469. private Map<Field<?>, InvalidValueException> commitFields() {
  470. Map<Field<?>, InvalidValueException> invalidValueExceptions = new HashMap<>();
  471. for (Field<?> f : fieldToPropertyId.keySet()) {
  472. try {
  473. f.commit();
  474. } catch (InvalidValueException e) {
  475. invalidValueExceptions.put(f, e);
  476. }
  477. }
  478. return invalidValueExceptions;
  479. }
  480. /**
  481. * Exception which wraps InvalidValueExceptions from all invalid fields in a
  482. * FieldGroup
  483. *
  484. * @since 7.4
  485. */
  486. @Deprecated
  487. public static class FieldGroupInvalidValueException
  488. extends InvalidValueException {
  489. private Map<Field<?>, InvalidValueException> invalidValueExceptions;
  490. /**
  491. * Constructs a new exception with the specified validation exceptions.
  492. *
  493. * @param invalidValueExceptions
  494. * a property id to exception map
  495. */
  496. public FieldGroupInvalidValueException(
  497. Map<Field<?>, InvalidValueException> invalidValueExceptions) {
  498. super(null, invalidValueExceptions.values().toArray(
  499. new InvalidValueException[invalidValueExceptions.size()]));
  500. this.invalidValueExceptions = invalidValueExceptions;
  501. }
  502. /**
  503. * Returns a map containing fields which failed validation and the
  504. * exceptions the corresponding validators threw.
  505. *
  506. * @return a map with all the invalid value exceptions
  507. */
  508. public Map<Field<?>, InvalidValueException> getInvalidFields() {
  509. return invalidValueExceptions;
  510. }
  511. }
  512. private void startTransactions() throws CommitException {
  513. for (Field<?> f : fieldToPropertyId.keySet()) {
  514. Property.Transactional<?> property = (Property.Transactional<?>) f
  515. .getPropertyDataSource();
  516. if (property == null) {
  517. throw new CommitException(
  518. "Property \"" + fieldToPropertyId.get(f)
  519. + "\" not bound to datasource.");
  520. }
  521. property.startTransaction();
  522. }
  523. }
  524. private void commitTransactions() {
  525. for (Field<?> f : fieldToPropertyId.keySet()) {
  526. ((Property.Transactional<?>) f.getPropertyDataSource()).commit();
  527. }
  528. }
  529. private void rollbackTransactions() {
  530. for (Field<?> f : fieldToPropertyId.keySet()) {
  531. try {
  532. ((Property.Transactional<?>) f.getPropertyDataSource())
  533. .rollback();
  534. } catch (Exception rollbackException) {
  535. // FIXME: What to do ?
  536. }
  537. }
  538. }
  539. /**
  540. * Sends a preCommit event to all registered commit handlers
  541. *
  542. * @throws CommitException
  543. * If the commit should be aborted
  544. */
  545. private void firePreCommitEvent() throws CommitException {
  546. CommitHandler[] handlers = commitHandlers
  547. .toArray(new CommitHandler[commitHandlers.size()]);
  548. for (CommitHandler handler : handlers) {
  549. handler.preCommit(new CommitEvent(this));
  550. }
  551. }
  552. /**
  553. * Sends a postCommit event to all registered commit handlers
  554. *
  555. * @throws CommitException
  556. * If the commit should be aborted
  557. */
  558. private void firePostCommitEvent() throws CommitException {
  559. CommitHandler[] handlers = commitHandlers
  560. .toArray(new CommitHandler[commitHandlers.size()]);
  561. for (CommitHandler handler : handlers) {
  562. handler.postCommit(new CommitEvent(this));
  563. }
  564. }
  565. /**
  566. * Discards all changes done to the bound fields.
  567. * <p>
  568. * Only has effect if buffered mode is used.
  569. *
  570. */
  571. public void discard() {
  572. for (Field<?> f : fieldToPropertyId.keySet()) {
  573. try {
  574. f.discard();
  575. } catch (Exception e) {
  576. // TODO: handle exception
  577. // What can we do if discard fails other than try to discard all
  578. // other fields?
  579. }
  580. }
  581. }
  582. /**
  583. * Returns the field that is bound to the given property id
  584. *
  585. * @param propertyId
  586. * The property id to use to lookup the field
  587. * @return The field that is bound to the property id or null if no field is
  588. * bound to that property id
  589. */
  590. public Field<?> getField(Object propertyId) {
  591. return propertyIdToField.get(propertyId);
  592. }
  593. /**
  594. * Returns the property id that is bound to the given field
  595. *
  596. * @param field
  597. * The field to use to lookup the property id
  598. * @return The property id that is bound to the field or null if the field
  599. * is not bound to any property id by this FieldBinder
  600. */
  601. public Object getPropertyId(Field<?> field) {
  602. return fieldToPropertyId.get(field);
  603. }
  604. /**
  605. * Adds a commit handler.
  606. * <p>
  607. * The commit handler is called before the field values are committed to the
  608. * item ( {@link CommitHandler#preCommit(CommitEvent)}) and after the item
  609. * has been updated ({@link CommitHandler#postCommit(CommitEvent)}). If a
  610. * {@link CommitHandler} throws a CommitException the whole commit is
  611. * aborted and the fields retain their old values.
  612. *
  613. * @param commitHandler
  614. * The commit handler to add
  615. */
  616. public void addCommitHandler(CommitHandler commitHandler) {
  617. commitHandlers.add(commitHandler);
  618. }
  619. /**
  620. * Removes the given commit handler.
  621. *
  622. * @see #addCommitHandler(CommitHandler)
  623. *
  624. * @param commitHandler
  625. * The commit handler to remove
  626. */
  627. public void removeCommitHandler(CommitHandler commitHandler) {
  628. commitHandlers.remove(commitHandler);
  629. }
  630. /**
  631. * Returns a list of all commit handlers for this {@link FieldGroup}.
  632. * <p>
  633. * Use {@link #addCommitHandler(CommitHandler)} and
  634. * {@link #removeCommitHandler(CommitHandler)} to register or unregister a
  635. * commit handler.
  636. *
  637. * @return A collection of commit handlers
  638. */
  639. protected Collection<CommitHandler> getCommitHandlers() {
  640. return Collections.unmodifiableCollection(commitHandlers);
  641. }
  642. /**
  643. * CommitHandlers are used by {@link FieldGroup#commit()} as part of the
  644. * commit transactions. CommitHandlers can perform custom operations as part
  645. * of the commit and cause the commit to be aborted by throwing a
  646. * {@link CommitException}.
  647. */
  648. @Deprecated
  649. public interface CommitHandler extends Serializable {
  650. /**
  651. * Called before changes are committed to the field and the item is
  652. * updated.
  653. * <p>
  654. * Throw a {@link CommitException} to abort the commit.
  655. *
  656. * @param commitEvent
  657. * An event containing information regarding the commit
  658. * @throws CommitException
  659. * if the commit should be aborted
  660. */
  661. public void preCommit(CommitEvent commitEvent) throws CommitException;
  662. /**
  663. * Called after changes are committed to the fields and the item is
  664. * updated.
  665. * <p>
  666. * Throw a {@link CommitException} to abort the commit.
  667. *
  668. * @param commitEvent
  669. * An event containing information regarding the commit
  670. * @throws CommitException
  671. * if the commit should be aborted
  672. */
  673. public void postCommit(CommitEvent commitEvent) throws CommitException;
  674. }
  675. /**
  676. * FIXME javadoc
  677. *
  678. */
  679. @Deprecated
  680. public static class CommitEvent implements Serializable {
  681. private FieldGroup fieldBinder;
  682. private CommitEvent(FieldGroup fieldBinder) {
  683. this.fieldBinder = fieldBinder;
  684. }
  685. /**
  686. * Returns the field binder that this commit relates to
  687. *
  688. * @return The FieldBinder that is being committed.
  689. */
  690. public FieldGroup getFieldBinder() {
  691. return fieldBinder;
  692. }
  693. }
  694. /**
  695. * Checks the validity of the bound fields.
  696. * <p>
  697. * Call the {@link Field#validate()} for the fields to get the individual
  698. * error messages.
  699. *
  700. * @return true if all bound fields are valid, false otherwise.
  701. */
  702. public boolean isValid() {
  703. try {
  704. for (Field<?> field : getFields()) {
  705. field.validate();
  706. }
  707. return true;
  708. } catch (InvalidValueException e) {
  709. return false;
  710. }
  711. }
  712. /**
  713. * Checks if any bound field has been modified.
  714. *
  715. * @return true if at least one field has been modified, false otherwise
  716. */
  717. public boolean isModified() {
  718. for (Field<?> field : getFields()) {
  719. if (field.isModified()) {
  720. return true;
  721. }
  722. }
  723. return false;
  724. }
  725. /**
  726. * Gets the field factory for the {@link FieldGroup}. The field factory is
  727. * only used when {@link FieldGroup} creates a new field.
  728. *
  729. * @return The field factory in use
  730. *
  731. */
  732. public FieldGroupFieldFactory getFieldFactory() {
  733. return fieldFactory;
  734. }
  735. /**
  736. * Sets the field factory for the {@link FieldGroup}. The field factory is
  737. * only used when {@link FieldGroup} creates a new field.
  738. *
  739. * @param fieldFactory
  740. * The field factory to use
  741. */
  742. public void setFieldFactory(FieldGroupFieldFactory fieldFactory) {
  743. this.fieldFactory = fieldFactory;
  744. }
  745. /**
  746. * Binds member fields found in the given object.
  747. * <p>
  748. * This method processes all (Java) member fields whose type extends
  749. * {@link Field} and that can be mapped to a property id. Property id
  750. * mapping is done based on the field name or on a @{@link PropertyId}
  751. * annotation on the field. All non-null fields for which a property id can
  752. * be determined are bound to the property id.
  753. * </p>
  754. * <p>
  755. * For example:
  756. *
  757. * <pre>
  758. * public class MyForm extends VerticalLayout {
  759. * private TextField firstName = new TextField("First name");
  760. * &#64;PropertyId("last")
  761. * private TextField lastName = new TextField("Last name");
  762. * private TextField age = new TextField("Age"); ... }
  763. *
  764. * MyForm myForm = new MyForm();
  765. * ...
  766. * fieldGroup.bindMemberFields(myForm);
  767. * </pre>
  768. *
  769. * </p>
  770. * This binds the firstName TextField to a "firstName" property in the item,
  771. * lastName TextField to a "last" property and the age TextField to a "age"
  772. * property.
  773. *
  774. * @param objectWithMemberFields
  775. * The object that contains (Java) member fields to bind
  776. * @throws BindException
  777. * If there is a problem binding a field
  778. */
  779. public void bindMemberFields(Object objectWithMemberFields)
  780. throws BindException {
  781. buildAndBindMemberFields(objectWithMemberFields, false);
  782. }
  783. /**
  784. * Binds member fields found in the given object and builds member fields
  785. * that have not been initialized.
  786. * <p>
  787. * This method processes all (Java) member fields whose type extends
  788. * {@link Field} and that can be mapped to a property id. Property ids are
  789. * searched in the following order: @{@link PropertyId} annotations, exact
  790. * field name matches and the case-insensitive matching that ignores
  791. * underscores. Fields that are not initialized (null) are built using the
  792. * field factory. All non-null fields for which a property id can be
  793. * determined are bound to the property id.
  794. * </p>
  795. * <p>
  796. * For example:
  797. *
  798. * <pre>
  799. * public class MyForm extends VerticalLayout {
  800. * private TextField firstName = new TextField("First name");
  801. * &#64;PropertyId("last")
  802. * private TextField lastName = new TextField("Last name");
  803. * private TextField age;
  804. *
  805. * MyForm myForm = new MyForm();
  806. * ...
  807. * fieldGroup.buildAndBindMemberFields(myForm);
  808. * </pre>
  809. *
  810. * </p>
  811. * <p>
  812. * This binds the firstName TextField to a "firstName" property in the item,
  813. * lastName TextField to a "last" property and builds an age TextField using
  814. * the field factory and then binds it to the "age" property.
  815. * </p>
  816. *
  817. * @param objectWithMemberFields
  818. * The object that contains (Java) member fields to build and
  819. * bind
  820. * @throws BindException
  821. * If there is a problem binding or building a field
  822. */
  823. public void buildAndBindMemberFields(Object objectWithMemberFields)
  824. throws BindException {
  825. buildAndBindMemberFields(objectWithMemberFields, true);
  826. }
  827. /**
  828. * Binds member fields found in the given object and optionally builds
  829. * member fields that have not been initialized.
  830. * <p>
  831. * This method processes all (Java) member fields whose type extends
  832. * {@link Field} and that can be mapped to a property id. Property ids are
  833. * searched in the following order: @{@link PropertyId} annotations, exact
  834. * field name matches and the case-insensitive matching that ignores
  835. * underscores. Fields that are not initialized (null) are built using the
  836. * field factory is buildFields is true. All non-null fields for which a
  837. * property id can be determined are bound to the property id.
  838. * </p>
  839. *
  840. * @param objectWithMemberFields
  841. * The object that contains (Java) member fields to build and
  842. * bind
  843. * @throws BindException
  844. * If there is a problem binding or building a field
  845. */
  846. protected void buildAndBindMemberFields(Object objectWithMemberFields,
  847. boolean buildFields) throws BindException {
  848. Class<?> objectClass = objectWithMemberFields.getClass();
  849. for (java.lang.reflect.Field memberField : getFieldsInDeclareOrder(
  850. objectClass)) {
  851. if (!Field.class.isAssignableFrom(memberField.getType())) {
  852. // Process next field
  853. continue;
  854. }
  855. PropertyId propertyIdAnnotation = memberField
  856. .getAnnotation(PropertyId.class);
  857. Class<? extends Field> fieldType = (Class<? extends Field>) memberField
  858. .getType();
  859. Object propertyId = null;
  860. if (propertyIdAnnotation != null) {
  861. // @PropertyId(propertyId) always overrides property id
  862. propertyId = propertyIdAnnotation.value();
  863. } else {
  864. try {
  865. propertyId = findPropertyId(memberField);
  866. } catch (SearchException e) {
  867. // Property id was not found, skip this field
  868. continue;
  869. }
  870. if (propertyId == null) {
  871. // Property id was not found, skip this field
  872. continue;
  873. }
  874. }
  875. // Ensure that the property id exists
  876. Class<?> propertyType;
  877. try {
  878. propertyType = getPropertyType(propertyId);
  879. } catch (BindException e) {
  880. // Property id was not found, skip this field
  881. continue;
  882. }
  883. Field<?> field;
  884. try {
  885. // Get the field from the object
  886. field = (Field<?>) ReflectTools.getJavaFieldValue(
  887. objectWithMemberFields, memberField, Field.class);
  888. } catch (Exception e) {
  889. // If we cannot determine the value, just skip the field and try
  890. // the next one
  891. continue;
  892. }
  893. if (field == null && buildFields) {
  894. Caption captionAnnotation = memberField
  895. .getAnnotation(Caption.class);
  896. String caption;
  897. if (captionAnnotation != null) {
  898. caption = captionAnnotation.value();
  899. } else {
  900. caption = DefaultFieldFactory
  901. .createCaptionByPropertyId(propertyId);
  902. }
  903. // Create the component (LegacyField)
  904. field = build(caption, propertyType, fieldType);
  905. // Store it in the field
  906. try {
  907. ReflectTools.setJavaFieldValue(objectWithMemberFields,
  908. memberField, field);
  909. } catch (IllegalArgumentException e) {
  910. throw new BindException("Could not assign value to field '"
  911. + memberField.getName() + "'", e);
  912. } catch (IllegalAccessException e) {
  913. throw new BindException("Could not assign value to field '"
  914. + memberField.getName() + "'", e);
  915. } catch (InvocationTargetException e) {
  916. throw new BindException("Could not assign value to field '"
  917. + memberField.getName() + "'", e);
  918. }
  919. }
  920. if (field != null) {
  921. // Bind it to the property id
  922. bind(field, propertyId);
  923. }
  924. }
  925. }
  926. /**
  927. * Searches for a property id from the current itemDataSource that matches
  928. * the given memberField.
  929. * <p>
  930. * If perfect match is not found, uses a case insensitive search that also
  931. * ignores underscores. Returns null if no match is found. Throws a
  932. * SearchException if no item data source has been set.
  933. * </p>
  934. * <p>
  935. * The propertyId search logic used by
  936. * {@link #buildAndBindMemberFields(Object, boolean)
  937. * buildAndBindMemberFields} can easily be customized by overriding this
  938. * method. No other changes are needed.
  939. * </p>
  940. *
  941. * @param memberField
  942. * The field an object id is searched for
  943. * @return
  944. */
  945. protected Object findPropertyId(java.lang.reflect.Field memberField) {
  946. String fieldName = memberField.getName();
  947. if (getItemDataSource() == null) {
  948. throw new SearchException("Property id type for field '" + fieldName
  949. + "' could not be determined. No item data source has been set.");
  950. }
  951. Item dataSource = getItemDataSource();
  952. if (dataSource.getItemProperty(fieldName) != null) {
  953. return fieldName;
  954. } else {
  955. String minifiedFieldName = minifyFieldName(fieldName);
  956. for (Object itemPropertyId : dataSource.getItemPropertyIds()) {
  957. if (itemPropertyId instanceof String) {
  958. String itemPropertyName = (String) itemPropertyId;
  959. if (minifiedFieldName
  960. .equals(minifyFieldName(itemPropertyName))) {
  961. return itemPropertyName;
  962. }
  963. }
  964. }
  965. }
  966. return null;
  967. }
  968. protected static String minifyFieldName(String fieldName) {
  969. return fieldName.toLowerCase().replace("_", "");
  970. }
  971. /**
  972. * Exception thrown by a FieldGroup when the commit operation fails.
  973. *
  974. * Provides information about validation errors through
  975. * {@link #getInvalidFields()} if the cause of the failure is that all bound
  976. * fields did not pass validation
  977. *
  978. */
  979. @Deprecated
  980. public static class CommitException extends Exception {
  981. private FieldGroup fieldGroup;
  982. public CommitException() {
  983. super();
  984. }
  985. public CommitException(String message, FieldGroup fieldGroup,
  986. Throwable cause) {
  987. super(message, cause);
  988. this.fieldGroup = fieldGroup;
  989. }
  990. public CommitException(String message, Throwable cause) {
  991. super(message, cause);
  992. }
  993. public CommitException(String message) {
  994. super(message);
  995. }
  996. public CommitException(Throwable cause) {
  997. super(cause);
  998. }
  999. /**
  1000. * Returns a map containing the fields which failed validation and the
  1001. * exceptions the corresponding validators threw.
  1002. *
  1003. * @since 7.4
  1004. * @return a map with all the invalid value exceptions. Can be empty but
  1005. * not null
  1006. */
  1007. public Map<Field<?>, InvalidValueException> getInvalidFields() {
  1008. if (getCause() instanceof FieldGroupInvalidValueException) {
  1009. return ((FieldGroupInvalidValueException) getCause())
  1010. .getInvalidFields();
  1011. }
  1012. return new HashMap<>();
  1013. }
  1014. /**
  1015. * Returns the field group where the exception occurred
  1016. *
  1017. * @since 7.4
  1018. * @return the field group
  1019. */
  1020. public FieldGroup getFieldGroup() {
  1021. return fieldGroup;
  1022. }
  1023. }
  1024. @Deprecated
  1025. public static class BindException extends RuntimeException {
  1026. public BindException(String message) {
  1027. super(message);
  1028. }
  1029. public BindException(String message, Throwable t) {
  1030. super(message, t);
  1031. }
  1032. }
  1033. @Deprecated
  1034. public static class SearchException extends RuntimeException {
  1035. public SearchException(String message) {
  1036. super(message);
  1037. }
  1038. public SearchException(String message, Throwable t) {
  1039. super(message, t);
  1040. }
  1041. }
  1042. /**
  1043. * Builds a field and binds it to the given property id using the field
  1044. * binder.
  1045. *
  1046. * @param propertyId
  1047. * The property id to bind to. Must be present in the field
  1048. * finder.
  1049. * @throws BindException
  1050. * If there is a problem while building or binding
  1051. * @return The created and bound field
  1052. */
  1053. public Field<?> buildAndBind(Object propertyId) throws BindException {
  1054. String caption = DefaultFieldFactory
  1055. .createCaptionByPropertyId(propertyId);
  1056. return buildAndBind(caption, propertyId);
  1057. }
  1058. /**
  1059. * Builds a field using the given caption and binds it to the given property
  1060. * id using the field binder.
  1061. *
  1062. * @param caption
  1063. * The caption for the field
  1064. * @param propertyId
  1065. * The property id to bind to. Must be present in the field
  1066. * finder.
  1067. * @throws BindException
  1068. * If there is a problem while building or binding
  1069. * @return The created and bound field. Can be any type of {@link Field}.
  1070. */
  1071. public Field<?> buildAndBind(String caption, Object propertyId)
  1072. throws BindException {
  1073. return buildAndBind(caption, propertyId, Field.class);
  1074. }
  1075. /**
  1076. * Builds a field using the given caption and binds it to the given property
  1077. * id using the field binder. Ensures the new field is of the given type.
  1078. *
  1079. * @param caption
  1080. * The caption for the field
  1081. * @param propertyId
  1082. * The property id to bind to. Must be present in the field
  1083. * finder.
  1084. * @throws BindException
  1085. * If the field could not be created
  1086. * @return The created and bound field. Can be any type of {@link Field}.
  1087. */
  1088. public <T extends Field> T buildAndBind(String caption, Object propertyId,
  1089. Class<T> fieldType) throws BindException {
  1090. Class<?> type = getPropertyType(propertyId);
  1091. T field = build(caption, type, fieldType);
  1092. bind(field, propertyId);
  1093. return field;
  1094. }
  1095. /**
  1096. * Creates a field based on the given data type.
  1097. * <p>
  1098. * The data type is the type that we want to edit using the field. The field
  1099. * type is the type of field we want to create, can be {@link Field} if any
  1100. * LegacyField is good.
  1101. * </p>
  1102. *
  1103. * @param caption
  1104. * The caption for the new field
  1105. * @param dataType
  1106. * The data model type that we want to edit using the field
  1107. * @param fieldType
  1108. * The type of field that we want to create
  1109. * @return A LegacyField capable of editing the given type
  1110. * @throws BindException
  1111. * If the field could not be created
  1112. */
  1113. protected <T extends Field> T build(String caption, Class<?> dataType,
  1114. Class<T> fieldType) throws BindException {
  1115. T field = getFieldFactory().createField(dataType, fieldType);
  1116. if (field == null) {
  1117. throw new BindException(
  1118. "Unable to build a field of type " + fieldType.getName()
  1119. + " for editing " + dataType.getName());
  1120. }
  1121. field.setCaption(caption);
  1122. return field;
  1123. }
  1124. /**
  1125. * Returns an array containing LegacyField objects reflecting all the fields
  1126. * of the class or interface represented by this Class object. The elements
  1127. * in the array returned are sorted in declare order from sub class to super
  1128. * class.
  1129. *
  1130. * @param searchClass
  1131. * @return
  1132. */
  1133. protected static List<java.lang.reflect.Field> getFieldsInDeclareOrder(
  1134. Class searchClass) {
  1135. ArrayList<java.lang.reflect.Field> memberFieldInOrder = new ArrayList<>();
  1136. while (searchClass != null) {
  1137. for (java.lang.reflect.Field memberField : searchClass
  1138. .getDeclaredFields()) {
  1139. memberFieldInOrder.add(memberField);
  1140. }
  1141. searchClass = searchClass.getSuperclass();
  1142. }
  1143. return memberFieldInOrder;
  1144. }
  1145. /**
  1146. * Clears the value of all fields.
  1147. *
  1148. * @since 7.4
  1149. */
  1150. public void clear() {
  1151. for (Field<?> f : getFields()) {
  1152. if (f instanceof AbstractField) {
  1153. ((AbstractField) f).clear();
  1154. }
  1155. }
  1156. }
  1157. }