1. /*
  2. * @(#)IndexedPropertyDescriptor.java 1.49 04/05/05
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.beans;
  8. import java.lang.ref.Reference;
  9. import java.lang.reflect.Method;
  10. /**
  11. * An IndexedPropertyDescriptor describes a property that acts like an
  12. * array and has an indexed read and/or indexed write method to access
  13. * specific elements of the array.
  14. * <p>
  15. * An indexed property may also provide simple non-indexed read and write
  16. * methods. If these are present, they read and write arrays of the type
  17. * returned by the indexed read method.
  18. */
  19. public class IndexedPropertyDescriptor extends PropertyDescriptor {
  20. private Reference indexedPropertyTypeRef;
  21. private Reference indexedReadMethodRef;
  22. private Reference indexedWriteMethodRef;
  23. private String indexedReadMethodName;
  24. private String indexedWriteMethodName;
  25. /**
  26. * This constructor constructs an IndexedPropertyDescriptor for a property
  27. * that follows the standard Java conventions by having getFoo and setFoo
  28. * accessor methods, for both indexed access and array access.
  29. * <p>
  30. * Thus if the argument name is "fred", it will assume that there
  31. * is an indexed reader method "getFred", a non-indexed (array) reader
  32. * method also called "getFred", an indexed writer method "setFred",
  33. * and finally a non-indexed writer method "setFred".
  34. *
  35. * @param propertyName The programmatic name of the property.
  36. * @param beanClass The Class object for the target bean.
  37. * @exception IntrospectionException if an exception occurs during
  38. * introspection.
  39. */
  40. public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass)
  41. throws IntrospectionException {
  42. this(propertyName, beanClass,
  43. "get" + capitalize(propertyName),
  44. "set" + capitalize(propertyName),
  45. "get" + capitalize(propertyName),
  46. "set" + capitalize(propertyName));
  47. }
  48. /**
  49. * This constructor takes the name of a simple property, and method
  50. * names for reading and writing the property, both indexed
  51. * and non-indexed.
  52. *
  53. * @param propertyName The programmatic name of the property.
  54. * @param beanClass The Class object for the target bean.
  55. * @param readMethodName The name of the method used for reading the property
  56. * values as an array. May be null if the property is write-only
  57. * or must be indexed.
  58. * @param writeMethodName The name of the method used for writing the property
  59. * values as an array. May be null if the property is read-only
  60. * or must be indexed.
  61. * @param indexedReadMethodName The name of the method used for reading
  62. * an indexed property value.
  63. * May be null if the property is write-only.
  64. * @param indexedWriteMethodName The name of the method used for writing
  65. * an indexed property value.
  66. * May be null if the property is read-only.
  67. * @exception IntrospectionException if an exception occurs during
  68. * introspection.
  69. */
  70. public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass,
  71. String readMethodName, String writeMethodName,
  72. String indexedReadMethodName, String indexedWriteMethodName)
  73. throws IntrospectionException {
  74. super(propertyName, beanClass, readMethodName, writeMethodName);
  75. this.indexedReadMethodName = indexedReadMethodName;
  76. if (indexedReadMethodName != null && getIndexedReadMethod() == null) {
  77. throw new IntrospectionException("Method not found: " + indexedReadMethodName);
  78. }
  79. this.indexedWriteMethodName = indexedWriteMethodName;
  80. if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) {
  81. throw new IntrospectionException("Method not found: " + indexedWriteMethodName);
  82. }
  83. // Implemented only for type checking.
  84. findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod());
  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 pro
  91. perty.
  92. * @param readMethod The method used for reading the property values as an array.
  93. * May be null if the property is write-only or must be indexed.
  94. * @param writeMethod The method used for writing the property values as an array.
  95. * May be null if the property is read-only or must be indexed.
  96. * @param indexedReadMethod The method used for reading an indexed property value.
  97. * May be null if the property is write-only.
  98. * @param indexedWriteMethod The method used for writing an indexed property value.
  99. * May be null if the property is read-only.
  100. * @exception IntrospectionException if an exception occurs during
  101. * introspection.
  102. */
  103. public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod,
  104. Method indexedReadMethod, Method indexedWriteMethod)
  105. throws IntrospectionException {
  106. super(propertyName, readMethod, writeMethod);
  107. setIndexedReadMethod0(indexedReadMethod);
  108. setIndexedWriteMethod0(indexedWriteMethod);
  109. // Type checking
  110. setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod));
  111. }
  112. /**
  113. * Gets the method that should be used to read an indexed
  114. * property value.
  115. *
  116. * @return The method that should be used to read an indexed
  117. * property value.
  118. * May return null if the property isn't indexed or is write-only.
  119. */
  120. public synchronized Method getIndexedReadMethod() {
  121. Method indexedReadMethod = getIndexedReadMethod0();
  122. if (indexedReadMethod == null) {
  123. Class cls = getClass0();
  124. if (cls == null ||
  125. (indexedReadMethodName == null && indexedReadMethodRef == null)) {
  126. // the Indexed readMethod was explicitly set to null.
  127. return null;
  128. }
  129. if (indexedReadMethodName == null) {
  130. Class type = getIndexedPropertyType0();
  131. if (type == boolean.class || type == null) {
  132. indexedReadMethodName = "is" + getBaseName();
  133. } else {
  134. indexedReadMethodName = "get" + getBaseName();
  135. }
  136. }
  137. Class[] args = { int.class };
  138. indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName,
  139. 1, args);
  140. if (indexedReadMethod == null) {
  141. // no "is" method, so look for a "get" method.
  142. indexedReadMethodName = "get" + getBaseName();
  143. indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName,
  144. 1, args);
  145. }
  146. setIndexedReadMethod0(indexedReadMethod);
  147. }
  148. return indexedReadMethod;
  149. }
  150. /**
  151. * Sets the method that should be used to read an indexed property value.
  152. *
  153. * @param readMethod The new indexed read method.
  154. */
  155. public synchronized void setIndexedReadMethod(Method readMethod)
  156. throws IntrospectionException {
  157. // the indexed property type is set by the reader.
  158. setIndexedPropertyType(findIndexedPropertyType(readMethod,
  159. getIndexedWriteMethod0()));
  160. setIndexedReadMethod0(readMethod);
  161. }
  162. private void setIndexedReadMethod0(Method readMethod) {
  163. if (readMethod == null) {
  164. indexedReadMethodName = null;
  165. indexedReadMethodRef = null;
  166. return;
  167. }
  168. setClass0(readMethod.getDeclaringClass());
  169. indexedReadMethodName = readMethod.getName();
  170. indexedReadMethodRef = createReference(readMethod);
  171. }
  172. /**
  173. * Gets the method that should be used to write an indexed property value.
  174. *
  175. * @return The method that should be used to write an indexed
  176. * property value.
  177. * May return null if the property isn't indexed or is read-only.
  178. */
  179. public synchronized Method getIndexedWriteMethod() {
  180. Method indexedWriteMethod = getIndexedWriteMethod0();
  181. if (indexedWriteMethod == null) {
  182. Class cls = getClass0();
  183. if (cls == null ||
  184. (indexedWriteMethodName == null && indexedWriteMethodRef == null)) {
  185. // the Indexed writeMethod was explicitly set to null.
  186. return null;
  187. }
  188. // We need the indexed type to ensure that we get the correct method.
  189. // Cannot use the getIndexedPropertyType method since that could
  190. // result in an infinite loop.
  191. Class type = getIndexedPropertyType0();
  192. if (type == null) {
  193. try {
  194. type = findIndexedPropertyType(getIndexedReadMethod(), null);
  195. setIndexedPropertyType(type);
  196. } catch (IntrospectionException ex) {
  197. // Set iprop type to be the classic type
  198. Class propType = getPropertyType();
  199. if (propType.isArray()) {
  200. type = propType.getComponentType();
  201. }
  202. }
  203. }
  204. if (indexedWriteMethodName == null) {
  205. indexedWriteMethodName = "set" + getBaseName();
  206. }
  207. indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName,
  208. 2, (type == null) ? null : new Class[] { int.class, type });
  209. setIndexedWriteMethod0(indexedWriteMethod);
  210. }
  211. return indexedWriteMethod;
  212. }
  213. /**
  214. * Sets the method that should be used to write an indexed property value.
  215. *
  216. * @param writeMethod The new indexed write method.
  217. */
  218. public synchronized void setIndexedWriteMethod(Method writeMethod)
  219. throws IntrospectionException {
  220. // If the indexed property type has not been set, then set it.
  221. Class type = findIndexedPropertyType(getIndexedReadMethod(),
  222. writeMethod);
  223. setIndexedPropertyType(type);
  224. setIndexedWriteMethod0(writeMethod);
  225. }
  226. private void setIndexedWriteMethod0(Method writeMethod) {
  227. if (writeMethod == null) {
  228. indexedWriteMethodName = null;
  229. indexedWriteMethodRef = null;
  230. return;
  231. }
  232. setClass0(writeMethod.getDeclaringClass());
  233. indexedWriteMethodName = writeMethod.getName();
  234. indexedWriteMethodRef = createReference(writeMethod);
  235. }
  236. /**
  237. * Gets the <code>Class</code> object of the indexed properties' type.
  238. * The returned <code>Class</code> may describe a primitive type such as <code>int</code>.
  239. *
  240. * @return The <code>Class</code> for the indexed properties' type; may return <code>null</code>
  241. * if the type cannot be determined.
  242. */
  243. public synchronized Class<?> getIndexedPropertyType() {
  244. Class type = getIndexedPropertyType0();
  245. if (type == null) {
  246. try {
  247. type = findIndexedPropertyType(getIndexedReadMethod(),
  248. getIndexedWriteMethod());
  249. setIndexedPropertyType(type);
  250. } catch (IntrospectionException ex) {
  251. // fall
  252. }
  253. }
  254. return type;
  255. }
  256. // Private methods which set get/set the Reference objects
  257. private void setIndexedPropertyType(Class type) {
  258. indexedPropertyTypeRef = createReference(type);
  259. }
  260. private Class getIndexedPropertyType0() {
  261. return (Class)getObject(indexedPropertyTypeRef);
  262. }
  263. private Method getIndexedReadMethod0() {
  264. return (Method)getObject(indexedReadMethodRef);
  265. }
  266. private Method getIndexedWriteMethod0() {
  267. return (Method)getObject(indexedWriteMethodRef);
  268. }
  269. private Class findIndexedPropertyType(Method indexedReadMethod,
  270. Method indexedWriteMethod)
  271. throws IntrospectionException {
  272. Class indexedPropertyType = null;
  273. if (indexedReadMethod != null) {
  274. Class params[] = indexedReadMethod.getParameterTypes();
  275. if (params.length != 1) {
  276. throw new IntrospectionException("bad indexed read method arg count");
  277. }
  278. if (params[0] != Integer.TYPE) {
  279. throw new IntrospectionException("non int index to indexed read method");
  280. }
  281. indexedPropertyType = indexedReadMethod.getReturnType();
  282. if (indexedPropertyType == Void.TYPE) {
  283. throw new IntrospectionException("indexed read method returns void");
  284. }
  285. }
  286. if (indexedWriteMethod != null) {
  287. Class params[] = indexedWriteMethod.getParameterTypes();
  288. if (params.length != 2) {
  289. throw new IntrospectionException("bad indexed write method arg count");
  290. }
  291. if (params[0] != Integer.TYPE) {
  292. throw new IntrospectionException("non int index to indexed write method");
  293. }
  294. if (indexedPropertyType != null && indexedPropertyType != params[1]) {
  295. throw new IntrospectionException(
  296. "type mismatch between indexed read and indexed write methods: "
  297. + getName());
  298. }
  299. indexedPropertyType = params[1];
  300. }
  301. Class propertyType = getPropertyType();
  302. if (propertyType != null && (!propertyType.isArray() ||
  303. propertyType.getComponentType() != indexedPropertyType)) {
  304. throw new IntrospectionException("type mismatch between indexed and non-indexed methods: "
  305. + getName());
  306. }
  307. return indexedPropertyType;
  308. }
  309. /**
  310. * Compares this <code>PropertyDescriptor</code> against the specified object.
  311. * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s
  312. * are the same if the read, write, property types, property editor and
  313. * flags are equivalent.
  314. *
  315. * @since 1.4
  316. */
  317. public boolean equals(Object obj) {
  318. // Note: This would be identical to PropertyDescriptor but they don't
  319. // share the same fields.
  320. if (this == obj) {
  321. return true;
  322. }
  323. if (obj != null && obj instanceof IndexedPropertyDescriptor) {
  324. IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj;
  325. Method otherIndexedReadMethod = other.getIndexedReadMethod();
  326. Method otherIndexedWriteMethod = other.getIndexedWriteMethod();
  327. if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) {
  328. return false;
  329. }
  330. if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) {
  331. return false;
  332. }
  333. if (getIndexedPropertyType() != other.getIndexedPropertyType()) {
  334. return false;
  335. }
  336. return super.equals(obj);
  337. }
  338. return false;
  339. }
  340. /**
  341. * Package-private constructor.
  342. * Merge two property descriptors. Where they conflict, give the
  343. * second argument (y) priority over the first argumnnt (x).
  344. *
  345. * @param x The first (lower priority) PropertyDescriptor
  346. * @param y The second (higher priority) PropertyDescriptor
  347. */
  348. IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) {
  349. super(x,y);
  350. if (x instanceof IndexedPropertyDescriptor) {
  351. IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor)x;
  352. try {
  353. Method xr = ix.getIndexedReadMethod();
  354. if (xr != null) {
  355. setIndexedReadMethod(xr);
  356. }
  357. Method xw = ix.getIndexedWriteMethod();
  358. if (xw != null) {
  359. setIndexedWriteMethod(xw);
  360. }
  361. } catch (IntrospectionException ex) {
  362. // Should not happen
  363. throw new AssertionError(ex);
  364. }
  365. }
  366. if (y instanceof IndexedPropertyDescriptor) {
  367. IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor)y;
  368. try {
  369. Method yr = iy.getIndexedReadMethod();
  370. if (yr != null && yr.getDeclaringClass() == getClass0()) {
  371. setIndexedReadMethod(yr);
  372. }
  373. Method yw = iy.getIndexedWriteMethod();
  374. if (yw != null && yw.getDeclaringClass() == getClass0()) {
  375. setIndexedWriteMethod(yw);
  376. }
  377. } catch (IntrospectionException ex) {
  378. // Should not happen
  379. throw new AssertionError(ex);
  380. }
  381. }
  382. }
  383. /*
  384. * Package-private dup constructor
  385. * This must isolate the new object from any changes to the old object.
  386. */
  387. IndexedPropertyDescriptor(IndexedPropertyDescriptor old) {
  388. super(old);
  389. indexedReadMethodRef = old.indexedReadMethodRef;
  390. indexedWriteMethodRef = old.indexedWriteMethodRef;
  391. indexedPropertyTypeRef = old.indexedPropertyTypeRef;
  392. indexedWriteMethodName = old.indexedWriteMethodName;
  393. indexedReadMethodName = old.indexedReadMethodName;
  394. }
  395. /**
  396. * Returns a hash code value for the object.
  397. * See {@link java.lang.Object#hashCode} for a complete description.
  398. *
  399. * @return a hash code value for this object.
  400. * @since 1.5
  401. */
  402. public int hashCode() {
  403. int result = super.hashCode();
  404. result = 37 * result + ((indexedWriteMethodName == null) ? 0 :
  405. indexedWriteMethodName.hashCode());
  406. result = 37 * result + ((indexedReadMethodName == null) ? 0 :
  407. indexedReadMethodName.hashCode());
  408. result = 37 * result + ((getIndexedPropertyType() == null) ? 0 :
  409. getIndexedPropertyType().hashCode());
  410. return result;
  411. }
  412. /*
  413. public String toString() {
  414. String message = super.toString();
  415. message += ", indexedType=";
  416. message += getIndexedPropertyType();
  417. message += ", indexedWriteMethod=";
  418. message += indexedWriteMethodName;
  419. message += ", indexedReadMethod=";
  420. message += indexedReadMethodName;
  421. return message;
  422. }
  423. */
  424. }