|
1 /* |
|
2 * Copyright 1996-2004 Sun Microsystems, Inc. All Rights Reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun in the LICENSE file that accompanied this code. |
|
10 * |
|
11 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
14 * version 2 for more details (a copy is included in the LICENSE file that |
|
15 * accompanied this code). |
|
16 * |
|
17 * You should have received a copy of the GNU General Public License version |
|
18 * 2 along with this work; if not, write to the Free Software Foundation, |
|
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
20 * |
|
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package java.beans; |
|
27 |
|
28 import java.lang.ref.Reference; |
|
29 |
|
30 import java.lang.reflect.Method; |
|
31 |
|
32 /** |
|
33 * An IndexedPropertyDescriptor describes a property that acts like an |
|
34 * array and has an indexed read and/or indexed write method to access |
|
35 * specific elements of the array. |
|
36 * <p> |
|
37 * An indexed property may also provide simple non-indexed read and write |
|
38 * methods. If these are present, they read and write arrays of the type |
|
39 * returned by the indexed read method. |
|
40 */ |
|
41 |
|
42 public class IndexedPropertyDescriptor extends PropertyDescriptor { |
|
43 |
|
44 private Reference<Class> indexedPropertyTypeRef; |
|
45 private Reference<Method> indexedReadMethodRef; |
|
46 private Reference<Method> indexedWriteMethodRef; |
|
47 |
|
48 private String indexedReadMethodName; |
|
49 private String indexedWriteMethodName; |
|
50 |
|
51 /** |
|
52 * This constructor constructs an IndexedPropertyDescriptor for a property |
|
53 * that follows the standard Java conventions by having getFoo and setFoo |
|
54 * accessor methods, for both indexed access and array access. |
|
55 * <p> |
|
56 * Thus if the argument name is "fred", it will assume that there |
|
57 * is an indexed reader method "getFred", a non-indexed (array) reader |
|
58 * method also called "getFred", an indexed writer method "setFred", |
|
59 * and finally a non-indexed writer method "setFred". |
|
60 * |
|
61 * @param propertyName The programmatic name of the property. |
|
62 * @param beanClass The Class object for the target bean. |
|
63 * @exception IntrospectionException if an exception occurs during |
|
64 * introspection. |
|
65 */ |
|
66 public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass) |
|
67 throws IntrospectionException { |
|
68 this(propertyName, beanClass, |
|
69 Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), |
|
70 Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName), |
|
71 Introspector.GET_PREFIX + NameGenerator.capitalize(propertyName), |
|
72 Introspector.SET_PREFIX + NameGenerator.capitalize(propertyName)); |
|
73 } |
|
74 |
|
75 /** |
|
76 * This constructor takes the name of a simple property, and method |
|
77 * names for reading and writing the property, both indexed |
|
78 * and non-indexed. |
|
79 * |
|
80 * @param propertyName The programmatic name of the property. |
|
81 * @param beanClass The Class object for the target bean. |
|
82 * @param readMethodName The name of the method used for reading the property |
|
83 * values as an array. May be null if the property is write-only |
|
84 * or must be indexed. |
|
85 * @param writeMethodName The name of the method used for writing the property |
|
86 * values as an array. May be null if the property is read-only |
|
87 * or must be indexed. |
|
88 * @param indexedReadMethodName The name of the method used for reading |
|
89 * an indexed property value. |
|
90 * May be null if the property is write-only. |
|
91 * @param indexedWriteMethodName The name of the method used for writing |
|
92 * an indexed property value. |
|
93 * May be null if the property is read-only. |
|
94 * @exception IntrospectionException if an exception occurs during |
|
95 * introspection. |
|
96 */ |
|
97 public IndexedPropertyDescriptor(String propertyName, Class<?> beanClass, |
|
98 String readMethodName, String writeMethodName, |
|
99 String indexedReadMethodName, String indexedWriteMethodName) |
|
100 throws IntrospectionException { |
|
101 super(propertyName, beanClass, readMethodName, writeMethodName); |
|
102 |
|
103 this.indexedReadMethodName = indexedReadMethodName; |
|
104 if (indexedReadMethodName != null && getIndexedReadMethod() == null) { |
|
105 throw new IntrospectionException("Method not found: " + indexedReadMethodName); |
|
106 } |
|
107 |
|
108 this.indexedWriteMethodName = indexedWriteMethodName; |
|
109 if (indexedWriteMethodName != null && getIndexedWriteMethod() == null) { |
|
110 throw new IntrospectionException("Method not found: " + indexedWriteMethodName); |
|
111 } |
|
112 // Implemented only for type checking. |
|
113 findIndexedPropertyType(getIndexedReadMethod(), getIndexedWriteMethod()); |
|
114 } |
|
115 |
|
116 /** |
|
117 * This constructor takes the name of a simple property, and Method |
|
118 * objects for reading and writing the property. |
|
119 * |
|
120 * @param propertyName The programmatic name of the pro |
|
121 perty. |
|
122 * @param readMethod The method used for reading the property values as an array. |
|
123 * May be null if the property is write-only or must be indexed. |
|
124 * @param writeMethod The method used for writing the property values as an array. |
|
125 * May be null if the property is read-only or must be indexed. |
|
126 * @param indexedReadMethod The method used for reading an indexed property value. |
|
127 * May be null if the property is write-only. |
|
128 * @param indexedWriteMethod The method used for writing an indexed property value. |
|
129 * May be null if the property is read-only. |
|
130 * @exception IntrospectionException if an exception occurs during |
|
131 * introspection. |
|
132 */ |
|
133 public IndexedPropertyDescriptor(String propertyName, Method readMethod, Method writeMethod, |
|
134 Method indexedReadMethod, Method indexedWriteMethod) |
|
135 throws IntrospectionException { |
|
136 super(propertyName, readMethod, writeMethod); |
|
137 |
|
138 setIndexedReadMethod0(indexedReadMethod); |
|
139 setIndexedWriteMethod0(indexedWriteMethod); |
|
140 |
|
141 // Type checking |
|
142 setIndexedPropertyType(findIndexedPropertyType(indexedReadMethod, indexedWriteMethod)); |
|
143 } |
|
144 |
|
145 /** |
|
146 * Creates <code>PropertyDescriptor</code> for the specified bean |
|
147 * with the specified name and methods to read/write the property value. |
|
148 * |
|
149 * @param bean the type of the target bean |
|
150 * @param base the base name of the property (the rest of the method name) |
|
151 * @param read the method used for reading the property value |
|
152 * @param write the method used for writing the property value |
|
153 * @param readIndexed the method used for reading an indexed property value |
|
154 * @param writeIndexed the method used for writing an indexed property value |
|
155 * @exception IntrospectionException if an exception occurs during introspection |
|
156 * |
|
157 * @since 1.7 |
|
158 */ |
|
159 IndexedPropertyDescriptor(Class<?> bean, String base, Method read, Method write, Method readIndexed, Method writeIndexed) throws IntrospectionException { |
|
160 super(bean, base, read, write); |
|
161 |
|
162 setIndexedReadMethod0(readIndexed); |
|
163 setIndexedWriteMethod0(writeIndexed); |
|
164 |
|
165 // Type checking |
|
166 setIndexedPropertyType(findIndexedPropertyType(readIndexed, writeIndexed)); |
|
167 } |
|
168 |
|
169 /** |
|
170 * Gets the method that should be used to read an indexed |
|
171 * property value. |
|
172 * |
|
173 * @return The method that should be used to read an indexed |
|
174 * property value. |
|
175 * May return null if the property isn't indexed or is write-only. |
|
176 */ |
|
177 public synchronized Method getIndexedReadMethod() { |
|
178 Method indexedReadMethod = getIndexedReadMethod0(); |
|
179 if (indexedReadMethod == null) { |
|
180 Class cls = getClass0(); |
|
181 if (cls == null || |
|
182 (indexedReadMethodName == null && indexedReadMethodRef == null)) { |
|
183 // the Indexed readMethod was explicitly set to null. |
|
184 return null; |
|
185 } |
|
186 if (indexedReadMethodName == null) { |
|
187 Class type = getIndexedPropertyType0(); |
|
188 if (type == boolean.class || type == null) { |
|
189 indexedReadMethodName = Introspector.IS_PREFIX + getBaseName(); |
|
190 } else { |
|
191 indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); |
|
192 } |
|
193 } |
|
194 |
|
195 Class[] args = { int.class }; |
|
196 |
|
197 indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, |
|
198 1, args); |
|
199 if (indexedReadMethod == null) { |
|
200 // no "is" method, so look for a "get" method. |
|
201 indexedReadMethodName = Introspector.GET_PREFIX + getBaseName(); |
|
202 indexedReadMethod = Introspector.findMethod(cls, indexedReadMethodName, |
|
203 1, args); |
|
204 } |
|
205 setIndexedReadMethod0(indexedReadMethod); |
|
206 } |
|
207 return indexedReadMethod; |
|
208 } |
|
209 |
|
210 /** |
|
211 * Sets the method that should be used to read an indexed property value. |
|
212 * |
|
213 * @param readMethod The new indexed read method. |
|
214 */ |
|
215 public synchronized void setIndexedReadMethod(Method readMethod) |
|
216 throws IntrospectionException { |
|
217 |
|
218 // the indexed property type is set by the reader. |
|
219 setIndexedPropertyType(findIndexedPropertyType(readMethod, |
|
220 getIndexedWriteMethod0())); |
|
221 setIndexedReadMethod0(readMethod); |
|
222 } |
|
223 |
|
224 private void setIndexedReadMethod0(Method readMethod) { |
|
225 if (readMethod == null) { |
|
226 indexedReadMethodName = null; |
|
227 indexedReadMethodRef = null; |
|
228 return; |
|
229 } |
|
230 setClass0(readMethod.getDeclaringClass()); |
|
231 |
|
232 indexedReadMethodName = readMethod.getName(); |
|
233 this.indexedReadMethodRef = getSoftReference(readMethod); |
|
234 } |
|
235 |
|
236 |
|
237 /** |
|
238 * Gets the method that should be used to write an indexed property value. |
|
239 * |
|
240 * @return The method that should be used to write an indexed |
|
241 * property value. |
|
242 * May return null if the property isn't indexed or is read-only. |
|
243 */ |
|
244 public synchronized Method getIndexedWriteMethod() { |
|
245 Method indexedWriteMethod = getIndexedWriteMethod0(); |
|
246 if (indexedWriteMethod == null) { |
|
247 Class cls = getClass0(); |
|
248 if (cls == null || |
|
249 (indexedWriteMethodName == null && indexedWriteMethodRef == null)) { |
|
250 // the Indexed writeMethod was explicitly set to null. |
|
251 return null; |
|
252 } |
|
253 |
|
254 // We need the indexed type to ensure that we get the correct method. |
|
255 // Cannot use the getIndexedPropertyType method since that could |
|
256 // result in an infinite loop. |
|
257 Class type = getIndexedPropertyType0(); |
|
258 if (type == null) { |
|
259 try { |
|
260 type = findIndexedPropertyType(getIndexedReadMethod(), null); |
|
261 setIndexedPropertyType(type); |
|
262 } catch (IntrospectionException ex) { |
|
263 // Set iprop type to be the classic type |
|
264 Class propType = getPropertyType(); |
|
265 if (propType.isArray()) { |
|
266 type = propType.getComponentType(); |
|
267 } |
|
268 } |
|
269 } |
|
270 |
|
271 if (indexedWriteMethodName == null) { |
|
272 indexedWriteMethodName = Introspector.SET_PREFIX + getBaseName(); |
|
273 } |
|
274 indexedWriteMethod = Introspector.findMethod(cls, indexedWriteMethodName, |
|
275 2, (type == null) ? null : new Class[] { int.class, type }); |
|
276 setIndexedWriteMethod0(indexedWriteMethod); |
|
277 } |
|
278 return indexedWriteMethod; |
|
279 } |
|
280 |
|
281 /** |
|
282 * Sets the method that should be used to write an indexed property value. |
|
283 * |
|
284 * @param writeMethod The new indexed write method. |
|
285 */ |
|
286 public synchronized void setIndexedWriteMethod(Method writeMethod) |
|
287 throws IntrospectionException { |
|
288 |
|
289 // If the indexed property type has not been set, then set it. |
|
290 Class type = findIndexedPropertyType(getIndexedReadMethod(), |
|
291 writeMethod); |
|
292 setIndexedPropertyType(type); |
|
293 setIndexedWriteMethod0(writeMethod); |
|
294 } |
|
295 |
|
296 private void setIndexedWriteMethod0(Method writeMethod) { |
|
297 if (writeMethod == null) { |
|
298 indexedWriteMethodName = null; |
|
299 indexedWriteMethodRef = null; |
|
300 return; |
|
301 } |
|
302 setClass0(writeMethod.getDeclaringClass()); |
|
303 |
|
304 indexedWriteMethodName = writeMethod.getName(); |
|
305 this.indexedWriteMethodRef = getSoftReference(writeMethod); |
|
306 } |
|
307 |
|
308 /** |
|
309 * Gets the <code>Class</code> object of the indexed properties' type. |
|
310 * The returned <code>Class</code> may describe a primitive type such as <code>int</code>. |
|
311 * |
|
312 * @return The <code>Class</code> for the indexed properties' type; may return <code>null</code> |
|
313 * if the type cannot be determined. |
|
314 */ |
|
315 public synchronized Class<?> getIndexedPropertyType() { |
|
316 Class type = getIndexedPropertyType0(); |
|
317 if (type == null) { |
|
318 try { |
|
319 type = findIndexedPropertyType(getIndexedReadMethod(), |
|
320 getIndexedWriteMethod()); |
|
321 setIndexedPropertyType(type); |
|
322 } catch (IntrospectionException ex) { |
|
323 // fall |
|
324 } |
|
325 } |
|
326 return type; |
|
327 } |
|
328 |
|
329 // Private methods which set get/set the Reference objects |
|
330 |
|
331 private void setIndexedPropertyType(Class type) { |
|
332 this.indexedPropertyTypeRef = getWeakReference(type); |
|
333 } |
|
334 |
|
335 private Class getIndexedPropertyType0() { |
|
336 return (this.indexedPropertyTypeRef != null) |
|
337 ? this.indexedPropertyTypeRef.get() |
|
338 : null; |
|
339 } |
|
340 |
|
341 private Method getIndexedReadMethod0() { |
|
342 return (this.indexedReadMethodRef != null) |
|
343 ? this.indexedReadMethodRef.get() |
|
344 : null; |
|
345 } |
|
346 |
|
347 private Method getIndexedWriteMethod0() { |
|
348 return (this.indexedWriteMethodRef != null) |
|
349 ? this.indexedWriteMethodRef.get() |
|
350 : null; |
|
351 } |
|
352 |
|
353 private Class findIndexedPropertyType(Method indexedReadMethod, |
|
354 Method indexedWriteMethod) |
|
355 throws IntrospectionException { |
|
356 Class indexedPropertyType = null; |
|
357 |
|
358 if (indexedReadMethod != null) { |
|
359 Class params[] = getParameterTypes(getClass0(), indexedReadMethod); |
|
360 if (params.length != 1) { |
|
361 throw new IntrospectionException("bad indexed read method arg count"); |
|
362 } |
|
363 if (params[0] != Integer.TYPE) { |
|
364 throw new IntrospectionException("non int index to indexed read method"); |
|
365 } |
|
366 indexedPropertyType = getReturnType(getClass0(), indexedReadMethod); |
|
367 if (indexedPropertyType == Void.TYPE) { |
|
368 throw new IntrospectionException("indexed read method returns void"); |
|
369 } |
|
370 } |
|
371 if (indexedWriteMethod != null) { |
|
372 Class params[] = getParameterTypes(getClass0(), indexedWriteMethod); |
|
373 if (params.length != 2) { |
|
374 throw new IntrospectionException("bad indexed write method arg count"); |
|
375 } |
|
376 if (params[0] != Integer.TYPE) { |
|
377 throw new IntrospectionException("non int index to indexed write method"); |
|
378 } |
|
379 if (indexedPropertyType != null && indexedPropertyType != params[1]) { |
|
380 throw new IntrospectionException( |
|
381 "type mismatch between indexed read and indexed write methods: " |
|
382 + getName()); |
|
383 } |
|
384 indexedPropertyType = params[1]; |
|
385 } |
|
386 Class propertyType = getPropertyType(); |
|
387 if (propertyType != null && (!propertyType.isArray() || |
|
388 propertyType.getComponentType() != indexedPropertyType)) { |
|
389 throw new IntrospectionException("type mismatch between indexed and non-indexed methods: " |
|
390 + getName()); |
|
391 } |
|
392 return indexedPropertyType; |
|
393 } |
|
394 |
|
395 /** |
|
396 * Compares this <code>PropertyDescriptor</code> against the specified object. |
|
397 * Returns true if the objects are the same. Two <code>PropertyDescriptor</code>s |
|
398 * are the same if the read, write, property types, property editor and |
|
399 * flags are equivalent. |
|
400 * |
|
401 * @since 1.4 |
|
402 */ |
|
403 public boolean equals(Object obj) { |
|
404 // Note: This would be identical to PropertyDescriptor but they don't |
|
405 // share the same fields. |
|
406 if (this == obj) { |
|
407 return true; |
|
408 } |
|
409 |
|
410 if (obj != null && obj instanceof IndexedPropertyDescriptor) { |
|
411 IndexedPropertyDescriptor other = (IndexedPropertyDescriptor)obj; |
|
412 Method otherIndexedReadMethod = other.getIndexedReadMethod(); |
|
413 Method otherIndexedWriteMethod = other.getIndexedWriteMethod(); |
|
414 |
|
415 if (!compareMethods(getIndexedReadMethod(), otherIndexedReadMethod)) { |
|
416 return false; |
|
417 } |
|
418 |
|
419 if (!compareMethods(getIndexedWriteMethod(), otherIndexedWriteMethod)) { |
|
420 return false; |
|
421 } |
|
422 |
|
423 if (getIndexedPropertyType() != other.getIndexedPropertyType()) { |
|
424 return false; |
|
425 } |
|
426 return super.equals(obj); |
|
427 } |
|
428 return false; |
|
429 } |
|
430 |
|
431 /** |
|
432 * Package-private constructor. |
|
433 * Merge two property descriptors. Where they conflict, give the |
|
434 * second argument (y) priority over the first argumnnt (x). |
|
435 * |
|
436 * @param x The first (lower priority) PropertyDescriptor |
|
437 * @param y The second (higher priority) PropertyDescriptor |
|
438 */ |
|
439 |
|
440 IndexedPropertyDescriptor(PropertyDescriptor x, PropertyDescriptor y) { |
|
441 super(x,y); |
|
442 if (x instanceof IndexedPropertyDescriptor) { |
|
443 IndexedPropertyDescriptor ix = (IndexedPropertyDescriptor)x; |
|
444 try { |
|
445 Method xr = ix.getIndexedReadMethod(); |
|
446 if (xr != null) { |
|
447 setIndexedReadMethod(xr); |
|
448 } |
|
449 |
|
450 Method xw = ix.getIndexedWriteMethod(); |
|
451 if (xw != null) { |
|
452 setIndexedWriteMethod(xw); |
|
453 } |
|
454 } catch (IntrospectionException ex) { |
|
455 // Should not happen |
|
456 throw new AssertionError(ex); |
|
457 } |
|
458 } |
|
459 if (y instanceof IndexedPropertyDescriptor) { |
|
460 IndexedPropertyDescriptor iy = (IndexedPropertyDescriptor)y; |
|
461 try { |
|
462 Method yr = iy.getIndexedReadMethod(); |
|
463 if (yr != null && yr.getDeclaringClass() == getClass0()) { |
|
464 setIndexedReadMethod(yr); |
|
465 } |
|
466 |
|
467 Method yw = iy.getIndexedWriteMethod(); |
|
468 if (yw != null && yw.getDeclaringClass() == getClass0()) { |
|
469 setIndexedWriteMethod(yw); |
|
470 } |
|
471 } catch (IntrospectionException ex) { |
|
472 // Should not happen |
|
473 throw new AssertionError(ex); |
|
474 } |
|
475 } |
|
476 } |
|
477 |
|
478 /* |
|
479 * Package-private dup constructor |
|
480 * This must isolate the new object from any changes to the old object. |
|
481 */ |
|
482 IndexedPropertyDescriptor(IndexedPropertyDescriptor old) { |
|
483 super(old); |
|
484 indexedReadMethodRef = old.indexedReadMethodRef; |
|
485 indexedWriteMethodRef = old.indexedWriteMethodRef; |
|
486 indexedPropertyTypeRef = old.indexedPropertyTypeRef; |
|
487 indexedWriteMethodName = old.indexedWriteMethodName; |
|
488 indexedReadMethodName = old.indexedReadMethodName; |
|
489 } |
|
490 |
|
491 /** |
|
492 * Returns a hash code value for the object. |
|
493 * See {@link java.lang.Object#hashCode} for a complete description. |
|
494 * |
|
495 * @return a hash code value for this object. |
|
496 * @since 1.5 |
|
497 */ |
|
498 public int hashCode() { |
|
499 int result = super.hashCode(); |
|
500 |
|
501 result = 37 * result + ((indexedWriteMethodName == null) ? 0 : |
|
502 indexedWriteMethodName.hashCode()); |
|
503 result = 37 * result + ((indexedReadMethodName == null) ? 0 : |
|
504 indexedReadMethodName.hashCode()); |
|
505 result = 37 * result + ((getIndexedPropertyType() == null) ? 0 : |
|
506 getIndexedPropertyType().hashCode()); |
|
507 |
|
508 return result; |
|
509 } |
|
510 |
|
511 /* |
|
512 public String toString() { |
|
513 String message = super.toString(); |
|
514 |
|
515 message += ", indexedType="; |
|
516 message += getIndexedPropertyType(); |
|
517 |
|
518 message += ", indexedWriteMethod="; |
|
519 message += indexedWriteMethodName; |
|
520 |
|
521 message += ", indexedReadMethod="; |
|
522 message += indexedReadMethodName; |
|
523 |
|
524 return message; |
|
525 } |
|
526 */ |
|
527 } |