Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

BeanBinderPropertySet.java 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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.beans.IntrospectionException;
  18. import java.beans.PropertyDescriptor;
  19. import java.io.IOException;
  20. import java.io.ObjectInputStream;
  21. import java.io.ObjectOutputStream;
  22. import java.lang.reflect.InvocationTargetException;
  23. import java.lang.reflect.Method;
  24. import java.util.Map;
  25. import java.util.Objects;
  26. import java.util.Optional;
  27. import java.util.concurrent.ConcurrentHashMap;
  28. import java.util.concurrent.ConcurrentMap;
  29. import java.util.function.Function;
  30. import java.util.stream.Collectors;
  31. import java.util.stream.Stream;
  32. import com.vaadin.data.Binder.BindingBuilder;
  33. import com.vaadin.data.util.BeanUtil;
  34. import com.vaadin.data.validator.BeanValidator;
  35. import com.vaadin.server.Setter;
  36. import com.vaadin.util.ReflectTools;
  37. /**
  38. * A {@link BinderPropertySet} that uses reflection to find bean properties.
  39. *
  40. * @author Vaadin Ltd
  41. *
  42. * @since
  43. *
  44. * @param <T>
  45. * the type of the bean
  46. */
  47. public class BeanBinderPropertySet<T> implements BinderPropertySet<T> {
  48. private static class BeanBinderPropertyDefinition<T, V>
  49. implements BinderPropertyDefinition<T, V> {
  50. private transient PropertyDescriptor descriptor;
  51. private final BeanBinderPropertySet<T> propertySet;
  52. public BeanBinderPropertyDefinition(
  53. BeanBinderPropertySet<T> propertySet,
  54. PropertyDescriptor descriptor) {
  55. this.propertySet = propertySet;
  56. this.descriptor = descriptor;
  57. if (descriptor.getReadMethod() == null) {
  58. throw new IllegalArgumentException(
  59. "Bean property has no accessible getter: "
  60. + propertySet.beanType + "."
  61. + descriptor.getName());
  62. }
  63. }
  64. @Override
  65. public ValueProvider<T, V> getGetter() {
  66. return bean -> {
  67. Method readMethod = descriptor.getReadMethod();
  68. Object value = invokeWrapExceptions(readMethod, bean);
  69. return getType().cast(value);
  70. };
  71. }
  72. @Override
  73. public Optional<Setter<T, V>> getSetter() {
  74. Method setter = descriptor.getWriteMethod();
  75. if (setter == null) {
  76. return Optional.empty();
  77. }
  78. return Optional.of(
  79. (bean, value) -> invokeWrapExceptions(setter, bean, value));
  80. }
  81. @SuppressWarnings("unchecked")
  82. @Override
  83. public Class<V> getType() {
  84. return (Class<V>) ReflectTools
  85. .convertPrimitiveType(descriptor.getPropertyType());
  86. }
  87. @Override
  88. public BindingBuilder<T, V> beforeBind(
  89. BindingBuilder<T, V> originalBuilder) {
  90. if (BeanUtil.checkBeanValidationAvailable()) {
  91. return originalBuilder.withValidator(new BeanValidator(
  92. getPropertySet().beanType, descriptor.getName()));
  93. } else {
  94. return originalBuilder;
  95. }
  96. }
  97. @Override
  98. public String getName() {
  99. return descriptor.getName();
  100. }
  101. @Override
  102. public BeanBinderPropertySet<T> getPropertySet() {
  103. return propertySet;
  104. }
  105. private void writeObject(ObjectOutputStream stream) throws IOException {
  106. stream.defaultWriteObject();
  107. stream.writeObject(descriptor.getName());
  108. stream.writeObject(propertySet.beanType);
  109. }
  110. private void readObject(ObjectInputStream stream)
  111. throws IOException, ClassNotFoundException {
  112. stream.defaultReadObject();
  113. String propertyName = (String) stream.readObject();
  114. @SuppressWarnings("unchecked")
  115. Class<T> beanType = (Class<T>) stream.readObject();
  116. try {
  117. descriptor = BeanUtil.getBeanPropertyDescriptors(beanType)
  118. .stream()
  119. .filter(descriptor -> descriptor.getName()
  120. .equals(propertyName))
  121. .findAny()
  122. .orElseThrow(() -> new IOException(
  123. "Property " + propertyName + " not found for "
  124. + beanType.getName()));
  125. } catch (IntrospectionException e) {
  126. throw new IOException(e);
  127. }
  128. }
  129. }
  130. private static final ConcurrentMap<Class<?>, BeanBinderPropertySet<?>> instances = new ConcurrentHashMap<>();
  131. private final Class<T> beanType;
  132. private final Map<String, BinderPropertyDefinition<T, ?>> definitions;
  133. private BeanBinderPropertySet(Class<T> beanType) {
  134. this.beanType = beanType;
  135. try {
  136. definitions = BeanUtil.getBeanPropertyDescriptors(beanType).stream()
  137. .filter(BeanBinderPropertySet::hasReadMethod)
  138. .map(descriptor -> new BeanBinderPropertyDefinition<>(this,
  139. descriptor))
  140. .collect(Collectors.toMap(BinderPropertyDefinition::getName,
  141. Function.identity()));
  142. } catch (IntrospectionException e) {
  143. throw new IllegalArgumentException(
  144. "Cannot find property descriptors for "
  145. + beanType.getName(),
  146. e);
  147. }
  148. }
  149. /**
  150. * Gets a {@link BeanBinderPropertySet} for the given bean type.
  151. *
  152. * @param beanType
  153. * the bean type to get a property set for, not <code>null</code>
  154. * @return the bean binder property set, not <code>null</code>
  155. */
  156. @SuppressWarnings("unchecked")
  157. public static <T> BinderPropertySet<T> get(Class<? extends T> beanType) {
  158. Objects.requireNonNull(beanType, "Bean type cannot be null");
  159. // Cache the reflection results
  160. return (BinderPropertySet<T>) instances.computeIfAbsent(beanType,
  161. BeanBinderPropertySet::new);
  162. }
  163. @Override
  164. public Stream<BinderPropertyDefinition<T, ?>> getProperties() {
  165. return definitions.values().stream();
  166. }
  167. @Override
  168. public Optional<BinderPropertyDefinition<T, ?>> getProperty(String name) {
  169. return Optional.ofNullable(definitions.get(name));
  170. }
  171. private static boolean hasReadMethod(PropertyDescriptor descriptor) {
  172. return descriptor.getReadMethod() != null;
  173. }
  174. private static Object invokeWrapExceptions(Method method, Object target,
  175. Object... parameters) {
  176. try {
  177. return method.invoke(target, parameters);
  178. } catch (IllegalAccessException | InvocationTargetException e) {
  179. throw new RuntimeException(e);
  180. }
  181. }
  182. @Override
  183. public String toString() {
  184. return "Property set for bean " + beanType.getName();
  185. }
  186. }