86 |
** <CODE><beanClass></CODE>, where <name> has its |
** <CODE><beanClass></CODE>, where <name> has its |
87 |
** first letter capitalized by the constructor.<P> |
** first letter capitalized by the constructor.<P> |
88 |
** |
** |
89 |
** <B>Implementation note:</B> If there is a get method (or |
** <B>Implementation note:</B> If there is both are both isXXX and |
90 |
** boolean isXXX() method), then the return type of that method |
** getXXX methods, the former is used in preference to the latter. |
91 |
** is used to find the set method. If there is no get method, |
** We do not check that an isXXX method returns a boolean. In both |
92 |
** then the set method is searched for exhaustively.<P> |
** cases, this matches the behaviour of JDK 1.4<P> |
|
** |
|
|
** <B>Spec note:</B> |
|
|
** If there is no get method and multiple set methods with |
|
|
** the same name and a single parameter (different type of course), |
|
|
** then an IntrospectionException is thrown. While Sun's spec |
|
|
** does not state this, it can make Bean behavior different on |
|
|
** different systems (since method order is not guaranteed) and as |
|
|
** such, can be treated as a bug in the spec. I am not aware of |
|
|
** whether Sun's implementation catches this. |
|
93 |
** |
** |
94 |
** @param name the programmatic name of the property, usually |
** @param name the programmatic name of the property, usually |
95 |
** starting with a lowercase letter (e.g. fooManChu |
** starting with a lowercase letter (e.g. fooManChu |
96 |
** instead of FooManChu). |
** instead of FooManChu). |
97 |
** @param beanClass the class the get and set methods live in. |
** @param beanClass the class the get and set methods live in. |
98 |
** @exception IntrospectionException if the methods are not found or invalid. |
** @exception IntrospectionException if the methods are not found |
99 |
|
** or invalid. |
100 |
**/ |
**/ |
101 |
public PropertyDescriptor(String name, Class beanClass) |
public PropertyDescriptor(String name, Class beanClass) |
102 |
throws IntrospectionException |
throws IntrospectionException |
103 |
{ |
{ |
104 |
setName(name); |
setName(name); |
105 |
String capitalized; |
if (name.length() == 0) { |
106 |
try { |
throw new IntrospectionException("empty property name"); |
107 |
capitalized = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
} |
108 |
} |
String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
109 |
catch (StringIndexOutOfBoundsException e) { |
findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); |
110 |
capitalized = ""; |
if (getMethod == null) { |
111 |
|
throw new IntrospectionException("Cannot find an is" + caps + |
112 |
|
" or get" + caps + " method"); |
113 |
|
} |
114 |
|
if (setMethod == null) { |
115 |
|
throw new IntrospectionException("Cannot find a " + caps + " method"); |
116 |
} |
} |
117 |
findMethods(beanClass, "is" + capitalized, "get" + capitalized, |
checkMethods(); |
|
"set" + capitalized); |
|
118 |
} |
} |
119 |
|
|
120 |
/** Create a new PropertyDescriptor by introspection. |
/** Create a new PropertyDescriptor by introspection. |
150 |
{ |
{ |
151 |
setName(name); |
setName(name); |
152 |
findMethods(beanClass, getMethodName, null, setMethodName); |
findMethods(beanClass, getMethodName, null, setMethodName); |
153 |
|
if (getMethod == null && getMethodName != null) { |
154 |
|
throw new IntrospectionException("Cannot find a getter method called " + |
155 |
|
getMethodName); |
156 |
|
} |
157 |
|
if (setMethod == null && setMethodName != null) { |
158 |
|
throw new IntrospectionException("Cannot find a setter method called " + |
159 |
|
setMethodName); |
160 |
|
} |
161 |
|
checkMethods(); |
162 |
} |
} |
163 |
|
|
164 |
/** Create a new PropertyDescriptor using explicit Methods. |
/** Create a new PropertyDescriptor using explicit Methods. |
177 |
throws IntrospectionException |
throws IntrospectionException |
178 |
{ |
{ |
179 |
setName(name); |
setName(name); |
|
if (getMethod != null && getMethod.getParameterTypes().length > 0) { |
|
|
throw new IntrospectionException("get method has parameters"); |
|
|
} |
|
|
if (setMethod != null && setMethod.getParameterTypes().length != 1) { |
|
|
String msg = "set method does not have exactly one parameter"; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
if (getMethod != null && setMethod != null) { |
|
|
if (!getMethod.getReturnType(). |
|
|
equals(setMethod.getParameterTypes()[0])) { |
|
|
String msg = "set and get methods do not share the same type"; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
if ((!getMethod.getDeclaringClass(). |
|
|
isAssignableFrom(setMethod.getDeclaringClass())) && |
|
|
(!setMethod.getDeclaringClass(). |
|
|
isAssignableFrom(getMethod.getDeclaringClass()))) { |
|
|
String msg = "set and get methods are not in the same class."; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
} |
|
180 |
this.getMethod = getMethod; |
this.getMethod = getMethod; |
181 |
this.setMethod = setMethod; |
this.setMethod = setMethod; |
182 |
if (getMethod != null) { |
if (getMethod != null) { |
183 |
this.propertyType = getMethod.getReturnType(); |
this.propertyType = getMethod.getReturnType(); |
184 |
} |
} |
185 |
else { |
else if (setMethod != null) { |
186 |
this.propertyType = setMethod.getParameterTypes()[0]; |
this.propertyType = setMethod.getParameterTypes()[0]; |
187 |
} |
} |
188 |
|
checkMethods(); |
189 |
} |
} |
190 |
|
|
191 |
/** Get the property type. |
/** Get the property type. |
280 |
throws IntrospectionException |
throws IntrospectionException |
281 |
{ |
{ |
282 |
try { |
try { |
283 |
|
// Try the first get method name |
284 |
if (getMethodName1 != null) { |
if (getMethodName1 != null) { |
285 |
try { |
try { |
286 |
getMethod = beanClass.getMethod(getMethodName1, new Class[0]); |
getMethod = beanClass.getMethod(getMethodName1, new Class[0]); |
287 |
} |
} |
288 |
catch (NoSuchMethodException E) { |
catch (NoSuchMethodException e) { |
289 |
} |
} |
290 |
if (getMethodName2 != null) { |
} |
291 |
if (getMethod != null && |
|
292 |
!getMethod.getReturnType().equals(java.lang.Boolean.TYPE)) { |
// Fall back to the second get method name |
293 |
// If the is() method exists but isn't boolean, we'll just |
if (getMethod == null && getMethodName2 != null) { |
294 |
// go on and look for an ordinary get() method. |
try { |
295 |
getMethod = null; |
getMethod = beanClass.getMethod(getMethodName2, new Class[0]); |
296 |
} |
} |
297 |
|
catch (NoSuchMethodException e) { |
|
Method getMethod2; |
|
|
try { |
|
|
getMethod2 = beanClass.getMethod(getMethodName2, new Class[0]); |
|
|
} |
|
|
catch (NoSuchMethodException E) { |
|
|
getMethod2 = null; |
|
|
} |
|
|
if (getMethod2 != null) { |
|
|
if (getMethod != null) { |
|
|
if (!getMethod.getReturnType(). |
|
|
equals(getMethod2.getReturnType())) { |
|
|
String msg = "Both " + getMethodName1 + |
|
|
" and " + getMethodName2 + |
|
|
" exist, and have contradictory return types."; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
} |
|
|
else { |
|
|
getMethod = getMethod2; |
|
|
} |
|
|
} |
|
298 |
} |
} |
299 |
} |
} |
300 |
|
|
301 |
if (getMethod != null) { |
// Try the set method name |
302 |
propertyType = getMethod.getReturnType(); |
if (setMethodName != null) { |
303 |
if (setMethodName != null) { |
if (getMethod != null) { |
304 |
Class[] setArgs = new Class[1]; |
// If there is a get method, use its return type to help |
305 |
setArgs[0] = propertyType; |
// select the corresponding set method. |
306 |
|
Class propertyType = getMethod.getReturnType(); |
307 |
|
if (propertyType == Void.TYPE) { |
308 |
|
String msg = "The property's read method has return type 'void'"; |
309 |
|
throw new IntrospectionException(msg); |
310 |
|
} |
311 |
|
|
312 |
|
Class[] setArgs = new Class[]{propertyType}; |
313 |
try { |
try { |
314 |
setMethod = beanClass.getMethod(setMethodName, setArgs); |
setMethod = beanClass.getMethod(setMethodName, setArgs); |
|
if (!setMethod.getReturnType().equals(java.lang.Void.TYPE)) { |
|
|
throw new IntrospectionException(setMethodName + |
|
|
" has non-void return type"); |
|
|
} |
|
315 |
} |
} |
316 |
catch (NoSuchMethodException E) { |
catch (NoSuchMethodException e) { |
317 |
} |
} |
318 |
} |
} |
319 |
} |
else if (getMethodName1 == null && getMethodName2 == null) { |
320 |
else if (setMethodName != null) { |
// If this is a write-only property, choose the first set method |
321 |
Method[] m = beanClass.getMethods(); |
// with the required name, one parameter and return type 'void' |
322 |
for(int i = 0; i < m.length; i++) { |
Method[] methods = beanClass.getMethods(); |
323 |
Method current = m[i]; |
for (int i = 0; i < methods.length; i++) { |
324 |
if (current.getName().equals(setMethodName) && |
if (methods[i].getName().equals(setMethodName) && |
325 |
current.getParameterTypes().length == 1 && |
methods[i].getParameterTypes().length == 1 && |
326 |
current.getReturnType().equals(java.lang.Void.TYPE)) { |
methods[i].getReturnType() == Void.TYPE) { |
327 |
if (setMethod != null) { |
setMethod = methods[i]; |
328 |
String msg = |
break; |
|
"Multiple, different set methods found that fit the bill!"; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
else { |
|
|
setMethod = current; |
|
|
propertyType = current.getParameterTypes()[0]; |
|
329 |
} |
} |
330 |
} |
} |
331 |
} |
} |
|
if (setMethod == null) { |
|
|
throw new IntrospectionException("Cannot find get or set methods."); |
|
|
} |
|
|
} |
|
|
else { |
|
|
throw new IntrospectionException("Cannot find get or set methods."); |
|
332 |
} |
} |
333 |
} |
} |
334 |
catch (SecurityException E) { |
catch (SecurityException e) { |
335 |
|
// FIXME -- shouldn't we just allow SecurityException to propagate? |
336 |
String msg = "SecurityException thrown on attempt to access methods."; |
String msg = "SecurityException thrown on attempt to access methods."; |
337 |
throw new IntrospectionException(msg); |
throw new IntrospectionException(msg); |
338 |
} |
} |
339 |
} |
} |
340 |
|
|
341 |
|
private void checkMethods() |
342 |
|
throws IntrospectionException |
343 |
|
{ |
344 |
|
if (getMethod != null) { |
345 |
|
if (getMethod.getParameterTypes().length > 0) { |
346 |
|
throw new IntrospectionException("get method has parameters"); |
347 |
|
} |
348 |
|
this.propertyType = getMethod.getReturnType(); |
349 |
|
if (propertyType == Void.TYPE) { |
350 |
|
throw new IntrospectionException("get method has void return type"); |
351 |
|
} |
352 |
|
} |
353 |
|
if (setMethod != null) { |
354 |
|
if (setMethod.getParameterTypes().length != 1) { |
355 |
|
String msg = "set method does not have exactly one parameter"; |
356 |
|
throw new IntrospectionException(msg); |
357 |
|
} |
358 |
|
if (getMethod == null) { |
359 |
|
propertyType = setMethod.getParameterTypes()[0]; |
360 |
|
} |
361 |
|
else { |
362 |
|
if (!propertyType.equals(setMethod.getParameterTypes()[0])) { |
363 |
|
String msg = "set and get methods do not share the same type"; |
364 |
|
throw new IntrospectionException(msg); |
365 |
|
} |
366 |
|
if ((!getMethod.getDeclaringClass(). |
367 |
|
isAssignableFrom(setMethod.getDeclaringClass())) && |
368 |
|
(!setMethod.getDeclaringClass(). |
369 |
|
isAssignableFrom(getMethod.getDeclaringClass()))) { |
370 |
|
String msg = "set and get methods are not in the same class."; |
371 |
|
throw new IntrospectionException(msg); |
372 |
|
} |
373 |
|
} |
374 |
|
} |
375 |
|
} |
376 |
} |
} |