Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

NestedMethodProperty.java 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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.util;
  17. import static com.vaadin.util.ReflectTools.convertPrimitiveType;
  18. import java.io.IOException;
  19. import java.lang.reflect.InvocationTargetException;
  20. import java.lang.reflect.Method;
  21. import java.util.ArrayList;
  22. import java.util.Collections;
  23. import java.util.List;
  24. import com.vaadin.data.Binder;
  25. import com.vaadin.data.ValueProvider;
  26. import com.vaadin.server.Setter;
  27. import com.vaadin.shared.util.SharedUtil;
  28. import com.vaadin.v7.data.Property;
  29. import com.vaadin.v7.data.util.MethodProperty.MethodException;
  30. /**
  31. * Nested accessor based property for a bean.
  32. *
  33. * The property is specified in the dotted notation, e.g. "address.street", and
  34. * can contain multiple levels of nesting.
  35. *
  36. * When accessing the property value, all intermediate getters must exist and
  37. * should return non-null values when the property value is accessed. If an
  38. * intermediate getter returns null, a null value will be returned.
  39. *
  40. * @see MethodProperty
  41. *
  42. * @since 6.6
  43. *
  44. * @deprecated As of 8.0, replaced by {@link ValueProvider}, {@link Setter}, see {@link Binder}
  45. */
  46. @Deprecated
  47. public class NestedMethodProperty<T> extends AbstractProperty<T> {
  48. // needed for de-serialization
  49. private String propertyName;
  50. // chain of getter methods
  51. private transient List<Method> getMethods;
  52. /**
  53. * The setter method.
  54. */
  55. private transient Method setMethod;
  56. /**
  57. * Bean instance used as a starting point for accessing the property value.
  58. */
  59. private Object instance;
  60. private Class<? extends T> type;
  61. /* Special serialization to handle method references */
  62. private void writeObject(java.io.ObjectOutputStream out)
  63. throws IOException {
  64. out.defaultWriteObject();
  65. // getMethods and setMethod are reconstructed on read based on
  66. // propertyName
  67. }
  68. /* Special serialization to handle method references */
  69. private void readObject(java.io.ObjectInputStream in)
  70. throws IOException, ClassNotFoundException {
  71. in.defaultReadObject();
  72. initialize(instance.getClass(), propertyName);
  73. }
  74. /**
  75. * Constructs a nested method property for a given object instance. The
  76. * property name is a dot separated string pointing to a nested property,
  77. * e.g. "manager.address.street".
  78. * <p>
  79. * Calling getValue will return null if any intermediate getter returns null
  80. *
  81. * @param instance
  82. * top-level bean to which the property applies
  83. * @param propertyName
  84. * dot separated nested property name
  85. * @throws IllegalArgumentException
  86. * if the property name is invalid
  87. */
  88. public NestedMethodProperty(Object instance, String propertyName) {
  89. this.instance = instance;
  90. initialize(instance.getClass(), propertyName);
  91. }
  92. /**
  93. * For internal use to deduce property type etc. without a bean instance.
  94. * Calling {@link #setValue(Object)} or {@link #getValue()} on properties
  95. * constructed this way is not supported.
  96. *
  97. * @param instanceClass
  98. * class of the top-level bean
  99. * @param propertyName
  100. */
  101. NestedMethodProperty(Class<?> instanceClass, String propertyName) {
  102. instance = null;
  103. initialize(instanceClass, propertyName);
  104. }
  105. /**
  106. * Initializes most of the internal fields based on the top-level bean
  107. * instance and property name (dot-separated string).
  108. *
  109. * @param beanClass
  110. * class of the top-level bean to which the property applies
  111. * @param propertyName
  112. * dot separated nested property name
  113. * @throws IllegalArgumentException
  114. * if the property name is invalid
  115. */
  116. private void initialize(Class<?> beanClass, String propertyName)
  117. throws IllegalArgumentException {
  118. List<Method> getMethods = new ArrayList<Method>();
  119. String lastSimplePropertyName = propertyName;
  120. Class<?> lastClass = beanClass;
  121. // first top-level property, then go deeper in a loop
  122. Class<?> propertyClass = beanClass;
  123. String[] simplePropertyNames = propertyName.split("\\.");
  124. if (propertyName.endsWith(".") || 0 == simplePropertyNames.length) {
  125. throw new IllegalArgumentException(
  126. "Invalid property name '" + propertyName + "'");
  127. }
  128. for (int i = 0; i < simplePropertyNames.length; i++) {
  129. String simplePropertyName = simplePropertyNames[i].trim();
  130. if (simplePropertyName.length() > 0) {
  131. lastSimplePropertyName = simplePropertyName;
  132. lastClass = propertyClass;
  133. try {
  134. Method getter = MethodProperty.initGetterMethod(
  135. simplePropertyName, propertyClass);
  136. propertyClass = getter.getReturnType();
  137. getMethods.add(getter);
  138. } catch (final java.lang.NoSuchMethodException e) {
  139. throw new IllegalArgumentException("Bean property '"
  140. + simplePropertyName + "' not found", e);
  141. }
  142. } else {
  143. throw new IllegalArgumentException(
  144. "Empty or invalid bean property identifier in '"
  145. + propertyName + "'");
  146. }
  147. }
  148. // In case the get method is found, resolve the type
  149. Method lastGetMethod = getMethods.get(getMethods.size() - 1);
  150. Class<?> type = lastGetMethod.getReturnType();
  151. // Finds the set method
  152. Method setMethod = null;
  153. try {
  154. // Assure that the first letter is upper cased (it is a common
  155. // mistake to write firstName, not FirstName).
  156. lastSimplePropertyName = SharedUtil
  157. .capitalize(lastSimplePropertyName);
  158. setMethod = lastClass.getMethod("set" + lastSimplePropertyName,
  159. new Class[] { type });
  160. } catch (final NoSuchMethodException skipped) {
  161. }
  162. this.type = (Class<? extends T>) convertPrimitiveType(type);
  163. this.propertyName = propertyName;
  164. this.getMethods = getMethods;
  165. this.setMethod = setMethod;
  166. }
  167. @Override
  168. public Class<? extends T> getType() {
  169. return type;
  170. }
  171. @Override
  172. public boolean isReadOnly() {
  173. return super.isReadOnly() || (null == setMethod);
  174. }
  175. /**
  176. * Gets the value stored in the Property. The value is resolved by calling
  177. * the specified getter methods on the current instance:
  178. *
  179. * @return the value of the Property
  180. * @see #getInstance()
  181. */
  182. @Override
  183. public T getValue() {
  184. try {
  185. Object object = instance;
  186. for (Method m : getMethods) {
  187. object = m.invoke(object);
  188. if (object == null) {
  189. return null;
  190. }
  191. }
  192. return (T) object;
  193. } catch (final Throwable e) {
  194. throw new MethodException(this, e);
  195. }
  196. }
  197. /**
  198. * Sets the value of the property. The new value must be assignable to the
  199. * type of this property.
  200. *
  201. * @param newValue
  202. * the New value of the property.
  203. * @throws <code>Property.ReadOnlyException</code>
  204. * if the object is in read-only mode.
  205. * @see #invokeSetMethod(Object)
  206. */
  207. @Override
  208. public void setValue(T newValue) throws ReadOnlyException {
  209. // Checks the mode
  210. if (isReadOnly()) {
  211. throw new Property.ReadOnlyException();
  212. }
  213. invokeSetMethod(newValue);
  214. fireValueChange();
  215. }
  216. /**
  217. * Internal method to actually call the setter method of the wrapped
  218. * property.
  219. *
  220. * @param value
  221. */
  222. protected void invokeSetMethod(T value) {
  223. try {
  224. Object object = instance;
  225. for (int i = 0; i < getMethods.size() - 1; i++) {
  226. object = getMethods.get(i).invoke(object);
  227. }
  228. setMethod.invoke(object, new Object[] { value });
  229. } catch (final InvocationTargetException e) {
  230. throw new MethodException(this, e.getTargetException());
  231. } catch (final Exception e) {
  232. throw new MethodException(this, e);
  233. }
  234. }
  235. /**
  236. * Returns an unmodifiable list of getter methods to call in sequence to get
  237. * the property value.
  238. *
  239. * This API may change in future versions.
  240. *
  241. * @return unmodifiable list of getter methods corresponding to each segment
  242. * of the property name
  243. */
  244. protected List<Method> getGetMethods() {
  245. return Collections.unmodifiableList(getMethods);
  246. }
  247. /**
  248. * The instance used by this property
  249. *
  250. * @return the instance used for fetching the property value
  251. * @since 7.7.7
  252. */
  253. public Object getInstance() {
  254. return instance;
  255. }
  256. /**
  257. * Sets the instance used by this property.
  258. * <p>
  259. * The new instance must be of the same type as the old instance
  260. * <p>
  261. * To be consistent with {@link #setValue(Object)}, this method will fire a
  262. * value change event even if the value stays the same
  263. *
  264. * @param instance
  265. * the instance to use
  266. * @since 7.7.7
  267. */
  268. public void setInstance(Object instance) {
  269. if (this.instance.getClass() != instance.getClass()) {
  270. throw new IllegalArgumentException("The new instance is of type "
  271. + instance.getClass().getName()
  272. + " which does not match the old instance type "
  273. + this.instance.getClass().getName());
  274. }
  275. this.instance = instance;
  276. fireValueChange();
  277. }
  278. }