Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

BeanFieldGroup.java 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  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.fieldgroup;
  17. import java.beans.IntrospectionException;
  18. import java.lang.reflect.Method;
  19. import java.util.HashMap;
  20. import java.util.Map;
  21. import com.vaadin.data.Item;
  22. import com.vaadin.data.util.BeanItem;
  23. import com.vaadin.data.util.BeanUtil;
  24. import com.vaadin.v7.data.validator.LegacyBeanValidator;
  25. import com.vaadin.v7.ui.LegacyField;
  26. public class BeanFieldGroup<T> extends FieldGroup {
  27. private final Class<T> beanType;
  28. private static Boolean beanValidationImplementationAvailable = null;
  29. private final Map<LegacyField<?>, LegacyBeanValidator> defaultValidators;
  30. public BeanFieldGroup(Class<T> beanType) {
  31. this.beanType = beanType;
  32. this.defaultValidators = new HashMap<LegacyField<?>, LegacyBeanValidator>();
  33. }
  34. @Override
  35. protected Class<?> getPropertyType(Object propertyId) {
  36. if (getItemDataSource() != null) {
  37. return super.getPropertyType(propertyId);
  38. } else {
  39. // Data source not set so we need to figure out the type manually
  40. /*
  41. * toString should never really be needed as propertyId should be of
  42. * form "fieldName" or "fieldName.subField[.subField2]" but the
  43. * method declaration comes from parent.
  44. */
  45. try {
  46. Class<?> type = BeanUtil.getPropertyType(beanType,
  47. propertyId.toString());
  48. if (type == null) {
  49. throw new BindException(
  50. "Cannot determine type of propertyId '" + propertyId
  51. + "'. The propertyId was not found in "
  52. + beanType.getName());
  53. }
  54. return type;
  55. } catch (IntrospectionException e) {
  56. throw new BindException("Cannot determine type of propertyId '"
  57. + propertyId + "'. Unable to introspect " + beanType,
  58. e);
  59. }
  60. }
  61. }
  62. @Override
  63. protected Object findPropertyId(java.lang.reflect.Field memberField) {
  64. String fieldName = memberField.getName();
  65. Item dataSource = getItemDataSource();
  66. if (dataSource != null
  67. && dataSource.getItemProperty(fieldName) != null) {
  68. return fieldName;
  69. } else {
  70. String minifiedFieldName = minifyFieldName(fieldName);
  71. try {
  72. return getFieldName(beanType, minifiedFieldName);
  73. } catch (SecurityException e) {
  74. } catch (NoSuchFieldException e) {
  75. }
  76. }
  77. return null;
  78. }
  79. private static String getFieldName(Class<?> cls, String propertyId)
  80. throws SecurityException, NoSuchFieldException {
  81. for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) {
  82. if (propertyId.equals(minifyFieldName(field1.getName()))) {
  83. return field1.getName();
  84. }
  85. }
  86. // Try super classes until we reach Object
  87. Class<?> superClass = cls.getSuperclass();
  88. if (superClass != null && superClass != Object.class) {
  89. return getFieldName(superClass, propertyId);
  90. } else {
  91. throw new NoSuchFieldException();
  92. }
  93. }
  94. /**
  95. * Helper method for setting the data source directly using a bean. This
  96. * method wraps the bean in a {@link BeanItem} and calls
  97. * {@link #setItemDataSource(Item)}.
  98. * <p>
  99. * For null values, a null item is passed to
  100. * {@link #setItemDataSource(Item)} to be properly clear fields.
  101. *
  102. * @param bean
  103. * The bean to use as data source.
  104. */
  105. public void setItemDataSource(T bean) {
  106. if (bean == null) {
  107. setItemDataSource((Item) null);
  108. } else {
  109. setItemDataSource(new BeanItem<T>(bean, beanType));
  110. }
  111. }
  112. @Override
  113. public void setItemDataSource(Item item) {
  114. if (item == null || (item instanceof BeanItem)) {
  115. super.setItemDataSource(item);
  116. } else {
  117. throw new RuntimeException(getClass().getSimpleName()
  118. + " only supports BeanItems as item data source");
  119. }
  120. }
  121. @Override
  122. public BeanItem<T> getItemDataSource() {
  123. return (BeanItem<T>) super.getItemDataSource();
  124. }
  125. private void ensureNestedPropertyAdded(Object propertyId) {
  126. if (getItemDataSource() != null) {
  127. // The data source is set so the property must be found in the item.
  128. // If it is not we try to add it.
  129. try {
  130. getItemProperty(propertyId);
  131. } catch (BindException e) {
  132. // Not found, try to add a nested property;
  133. // BeanItem property ids are always strings so this is safe
  134. getItemDataSource().addNestedProperty((String) propertyId);
  135. }
  136. }
  137. }
  138. @Override
  139. public void bind(LegacyField field, Object propertyId) {
  140. ensureNestedPropertyAdded(propertyId);
  141. super.bind(field, propertyId);
  142. }
  143. @Override
  144. public <T extends LegacyField> T buildAndBind(String caption,
  145. Object propertyId, Class<T> fieldType) throws BindException {
  146. ensureNestedPropertyAdded(propertyId);
  147. return super.buildAndBind(caption, propertyId, fieldType);
  148. }
  149. @Override
  150. public void unbind(LegacyField<?> field) throws BindException {
  151. super.unbind(field);
  152. LegacyBeanValidator removed = defaultValidators.remove(field);
  153. if (removed != null) {
  154. field.removeValidator(removed);
  155. }
  156. }
  157. @Override
  158. protected void configureField(LegacyField<?> field) {
  159. super.configureField(field);
  160. // Add Bean validators if there are annotations
  161. if (isBeanValidationImplementationAvailable()
  162. && !defaultValidators.containsKey(field)) {
  163. LegacyBeanValidator validator = new LegacyBeanValidator(beanType,
  164. getPropertyId(field).toString());
  165. field.addValidator(validator);
  166. if (field.getLocale() != null) {
  167. validator.setLocale(field.getLocale());
  168. }
  169. defaultValidators.put(field, validator);
  170. }
  171. }
  172. /**
  173. * Checks whether a bean validation implementation (e.g. Hibernate Validator
  174. * or Apache Bean Validation) is available.
  175. *
  176. * TODO move this method to some more generic location
  177. *
  178. * @return true if a JSR-303 bean validation implementation is available
  179. */
  180. protected static boolean isBeanValidationImplementationAvailable() {
  181. if (beanValidationImplementationAvailable != null) {
  182. return beanValidationImplementationAvailable;
  183. }
  184. try {
  185. Class<?> validationClass = Class
  186. .forName("javax.validation.Validation");
  187. Method buildFactoryMethod = validationClass
  188. .getMethod("buildDefaultValidatorFactory");
  189. Object factory = buildFactoryMethod.invoke(null);
  190. beanValidationImplementationAvailable = (factory != null);
  191. } catch (Exception e) {
  192. // no bean validation implementation available
  193. beanValidationImplementationAvailable = false;
  194. }
  195. return beanValidationImplementationAvailable;
  196. }
  197. /**
  198. * Convenience method to bind Fields from a given "field container" to a
  199. * given bean with buffering disabled.
  200. * <p>
  201. * The returned {@link BeanFieldGroup} can be used for further
  202. * configuration.
  203. *
  204. * @see #bindFieldsBuffered(Object, Object)
  205. * @see #bindMemberFields(Object)
  206. * @since 7.2
  207. * @param bean
  208. * the bean to be bound
  209. * @param objectWithMemberFields
  210. * the class that contains {@link LegacyField}s for bean
  211. * properties
  212. * @return the bean field group used to make binding
  213. */
  214. public static <T> BeanFieldGroup<T> bindFieldsUnbuffered(T bean,
  215. Object objectWithMemberFields) {
  216. return createAndBindFields(bean, objectWithMemberFields, false);
  217. }
  218. /**
  219. * Convenience method to bind Fields from a given "field container" to a
  220. * given bean with buffering enabled.
  221. * <p>
  222. * The returned {@link BeanFieldGroup} can be used for further
  223. * configuration.
  224. *
  225. * @see #bindFieldsUnbuffered(Object, Object)
  226. * @see #bindMemberFields(Object)
  227. * @since 7.2
  228. * @param bean
  229. * the bean to be bound
  230. * @param objectWithMemberFields
  231. * the class that contains {@link LegacyField}s for bean
  232. * properties
  233. * @return the bean field group used to make binding
  234. */
  235. public static <T> BeanFieldGroup<T> bindFieldsBuffered(T bean,
  236. Object objectWithMemberFields) {
  237. return createAndBindFields(bean, objectWithMemberFields, true);
  238. }
  239. private static <T> BeanFieldGroup<T> createAndBindFields(T bean,
  240. Object objectWithMemberFields, boolean buffered) {
  241. @SuppressWarnings("unchecked")
  242. BeanFieldGroup<T> beanFieldGroup = new BeanFieldGroup<T>(
  243. (Class<T>) bean.getClass());
  244. beanFieldGroup.setItemDataSource(bean);
  245. beanFieldGroup.setBuffered(buffered);
  246. beanFieldGroup.bindMemberFields(objectWithMemberFields);
  247. return beanFieldGroup;
  248. }
  249. }