1. /*
  2. * @(#)PropertyDescriptor.java 1.52 00/02/02
  3. *
  4. * Copyright 1996-2000 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.beans;
  11. import java.lang.reflect.*;
  12. /**
  13. * A PropertyDescriptor describes one property that a Java Bean
  14. * exports via a pair of accessor methods.
  15. */
  16. public class PropertyDescriptor extends FeatureDescriptor {
  17. /**
  18. * Constructs a PropertyDescriptor for a property that follows
  19. * the standard Java convention by having getFoo and setFoo
  20. * accessor methods. Thus if the argument name is "fred", it will
  21. * assume that the writer method is "setFred" and the reader method
  22. * is "getFred" (or "isFred" for a boolean property). Note that the
  23. * property name should start with a lower case character, which will
  24. * be capitalized in the method names.
  25. *
  26. * @param propertyName The programmatic name of the property.
  27. * @param beanClass The Class object for the target bean. For
  28. * example sun.beans.OurButton.class.
  29. * @exception IntrospectionException if an exception occurs during
  30. * introspection.
  31. */
  32. public PropertyDescriptor(String propertyName, Class beanClass)
  33. throws IntrospectionException {
  34. if (propertyName == null || propertyName.length() == 0) {
  35. throw new IntrospectionException("bad property name");
  36. }
  37. setName(propertyName);
  38. String base = capitalize(propertyName);
  39. // Since there can be multiple setter methods but only one getter
  40. // method, find the getter method first so that you know what the
  41. // property type is. For booleans, there can be "is" and "get"
  42. // methods. If an "is" method exists, this is the official
  43. // reader method so look for this one first.
  44. try {
  45. readMethod = Introspector.findMethod(beanClass, "is" + base, 0);
  46. } catch (Exception getterExc) {
  47. // no "is" method, so look for a "get" method.
  48. readMethod = Introspector.findMethod(beanClass, "get" + base, 0);
  49. }
  50. Class params[] = { readMethod.getReturnType() };
  51. writeMethod = Introspector.findMethod(beanClass, "set" + base, 1,
  52. params);
  53. findPropertyType();
  54. }
  55. /**
  56. * This constructor takes the name of a simple property, and method
  57. * names for reading and writing the property.
  58. *
  59. * @param propertyName The programmatic name of the property.
  60. * @param beanClass The Class object for the target bean. For
  61. * example sun.beans.OurButton.class.
  62. * @param getterName The name of the method used for reading the property
  63. * value. May be null if the property is write-only.
  64. * @param setterName The name of the method used for writing the property
  65. * value. May be null if the property is read-only.
  66. * @exception IntrospectionException if an exception occurs during
  67. * introspection.
  68. */
  69. public PropertyDescriptor(String propertyName, Class beanClass,
  70. String getterName, String setterName)
  71. throws IntrospectionException {
  72. if (propertyName == null || propertyName.length() == 0) {
  73. throw new IntrospectionException("bad property name");
  74. }
  75. setName(propertyName);
  76. readMethod = Introspector.findMethod(beanClass, getterName, 0);
  77. if (readMethod != null) {
  78. Class params[] = { readMethod.getReturnType() };
  79. writeMethod = Introspector.findMethod(beanClass, setterName, 1,
  80. params);
  81. } else {
  82. writeMethod = Introspector.findMethod(beanClass, setterName, 1);
  83. }
  84. findPropertyType();
  85. }
  86. /**
  87. * This constructor takes the name of a simple property, and Method
  88. * objects for reading and writing the property.
  89. *
  90. * @param propertyName The programmatic name of the property.
  91. * @param getter The method used for reading the property value.
  92. * May be null if the property is write-only.
  93. * @param setter The method used for writing the property value.
  94. * May be null if the property is read-only.
  95. * @exception IntrospectionException if an exception occurs during
  96. * introspection.
  97. */
  98. public PropertyDescriptor(String propertyName, Method getter, Method setter)
  99. throws IntrospectionException {
  100. if (propertyName == null || propertyName.length() == 0) {
  101. throw new IntrospectionException("bad property name");
  102. }
  103. setName(propertyName);
  104. readMethod = getter;
  105. writeMethod = setter;
  106. findPropertyType();
  107. }
  108. /**
  109. * Gets the Class object for the property.
  110. *
  111. * @return The Java type info for the property. Note that
  112. * the "Class" object may describe a built-in Java type such as "int".
  113. * The result may be "null" if this is an indexed property that
  114. * does not support non-indexed access.
  115. * <p>
  116. * This is the type that will be returned by the ReadMethod.
  117. */
  118. public Class getPropertyType() {
  119. return propertyType;
  120. }
  121. /**
  122. * Gets the method that should be used to read the property value.
  123. *
  124. * @return The method that should be used to read the property value.
  125. * May return null if the property can't be read.
  126. */
  127. public Method getReadMethod() {
  128. return readMethod;
  129. }
  130. /**
  131. * Sets the method that should be used to read the property value.
  132. *
  133. * @param getter The new getter method.
  134. */
  135. public void setReadMethod(Method getter)
  136. throws IntrospectionException {
  137. readMethod = getter;
  138. findPropertyType();
  139. }
  140. /**
  141. * Gets the method that should be used to write the property value.
  142. *
  143. * @return The method that should be used to write the property value.
  144. * May return null if the property can't be written.
  145. */
  146. public Method getWriteMethod() {
  147. return writeMethod;
  148. }
  149. /**
  150. * Sets the method that should be used to write the property value.
  151. *
  152. * @param setter The new setter method.
  153. */
  154. public void setWriteMethod(Method setter)
  155. throws IntrospectionException {
  156. writeMethod = setter;
  157. findPropertyType();
  158. }
  159. /**
  160. * Updates to "bound" properties will cause a "PropertyChange" event to
  161. * get fired when the property is changed.
  162. *
  163. * @return True if this is a bound property.
  164. */
  165. public boolean isBound() {
  166. return bound;
  167. }
  168. /**
  169. * Updates to "bound" properties will cause a "PropertyChange" event to
  170. * get fired when the property is changed.
  171. *
  172. * @param bound True if this is a bound property.
  173. */
  174. public void setBound(boolean bound) {
  175. this.bound = bound;
  176. }
  177. /**
  178. * Attempted updates to "Constrained" properties will cause a "VetoableChange"
  179. * event to get fired when the property is changed.
  180. *
  181. * @return True if this is a constrained property.
  182. */
  183. public boolean isConstrained() {
  184. return constrained;
  185. }
  186. /**
  187. * Attempted updates to "Constrained" properties will cause a "VetoableChange"
  188. * event to get fired when the property is changed.
  189. *
  190. * @param constrained True if this is a constrained property.
  191. */
  192. public void setConstrained(boolean constrained) {
  193. this.constrained = constrained;
  194. }
  195. /**
  196. * Normally PropertyEditors will be found using the PropertyEditorManager.
  197. * However if for some reason you want to associate a particular
  198. * PropertyEditor with a given property, then you can do it with
  199. * this method.
  200. *
  201. * @param propertyEditorClass The Class for the desired PropertyEditor.
  202. */
  203. public void setPropertyEditorClass(Class propertyEditorClass) {
  204. this.propertyEditorClass = propertyEditorClass;
  205. }
  206. /**
  207. * Gets any explicit PropertyEditor Class that has been registered
  208. * for this property.
  209. *
  210. * @return Any explicit PropertyEditor Class that has been registered
  211. * for this property. Normally this will return "null",
  212. * indicating that no special editor has been registered,
  213. * so the PropertyEditorManager should be used to locate
  214. * a suitable PropertyEditor.
  215. */
  216. public Class getPropertyEditorClass() {
  217. return propertyEditorClass;
  218. }
  219. /*
  220. * Package-private constructor.
  221. * Merge two property descriptors. Where they conflict, give the
  222. * second argument (y) priority over the first argument (x).
  223. *
  224. * @param x The first (lower priority) PropertyDescriptor
  225. * @param y The second (higher priority) PropertyDescriptor
  226. */
  227. PropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
  228. super(x,y);
  229. // Figure out the merged read method.
  230. Method xr = x.readMethod;
  231. Method yr = y.readMethod;
  232. readMethod = xr;
  233. // Normally give priority to y's readMethod.
  234. if (yr != null) {
  235. readMethod = yr;
  236. }
  237. // However, if both x and y reference read methods in the same class,
  238. // give priority to a boolean "is" method over a boolean "get" method.
  239. if (xr != null && yr != null &&
  240. xr.getDeclaringClass() == yr.getDeclaringClass() &&
  241. xr.getReturnType() == boolean.class &&
  242. yr.getReturnType() == boolean.class &&
  243. xr.getName().indexOf("is") == 0 &&
  244. yr.getName().indexOf("get") == 0) {
  245. readMethod = xr;
  246. }
  247. writeMethod = x.writeMethod;
  248. if (y.writeMethod != null) {
  249. writeMethod = y.writeMethod;
  250. }
  251. propertyEditorClass = x.propertyEditorClass;
  252. if (y.propertyEditorClass != null) {
  253. propertyEditorClass = y.propertyEditorClass;
  254. }
  255. bound = x.bound | y.bound;
  256. constrained = x.constrained | y.constrained;
  257. try {
  258. findPropertyType();
  259. } catch (IntrospectionException ex) {
  260. // Given we're merging two valid PDs, this "should never happen".
  261. throw new Error("PropertyDescriptor: internal error while merging PDs");
  262. }
  263. }
  264. /*
  265. * Package-private dup constructor.
  266. * This must isolate the new object from any changes to the old object.
  267. */
  268. PropertyDescriptor(PropertyDescriptor old) {
  269. super(old);
  270. readMethod = old.readMethod;;
  271. writeMethod = old.writeMethod;
  272. propertyEditorClass = old.propertyEditorClass;
  273. bound = old.bound;
  274. constrained = old.constrained;
  275. propertyType = old.propertyType;
  276. }
  277. private void findPropertyType() throws IntrospectionException {
  278. try {
  279. propertyType = null;
  280. if (readMethod != null) {
  281. if (readMethod.getParameterTypes().length != 0) {
  282. throw new IntrospectionException("bad read method arg count");
  283. }
  284. propertyType = readMethod.getReturnType();
  285. if (propertyType == Void.TYPE) {
  286. throw new IntrospectionException("read method " +
  287. readMethod.getName() + " returns void");
  288. }
  289. }
  290. if (writeMethod != null) {
  291. Class params[] = writeMethod.getParameterTypes();
  292. if (params.length != 1) {
  293. throw new IntrospectionException("bad write method arg count");
  294. }
  295. if (propertyType != null && propertyType != params[0]) {
  296. throw new IntrospectionException("type mismatch between read and write methods");
  297. }
  298. propertyType = params[0];
  299. }
  300. } catch (IntrospectionException ex) {
  301. throw ex;
  302. }
  303. }
  304. static String capitalize(String s) {
  305. if (s.length() == 0) {
  306. return s;
  307. }
  308. char chars[] = s.toCharArray();
  309. chars[0] = Character.toUpperCase(chars[0]);
  310. return new String(chars);
  311. }
  312. private Class propertyType;
  313. private Method readMethod;
  314. private Method writeMethod;
  315. private boolean bound;
  316. private boolean constrained;
  317. private Class propertyEditorClass;
  318. }