1 |
/* java.beans.PropertyDescriptor |
/* java.beans.PropertyDescriptor |
2 |
Copyright (C) 1998, 2001 Free Software Foundation, Inc. |
Copyright (C) 1998, 2001, 2004 Free Software Foundation, Inc. |
3 |
|
|
4 |
This file is part of GNU Classpath. |
This file is part of GNU Classpath. |
5 |
|
|
35 |
obligated to do so. If you do not wish to do so, delete this |
obligated to do so. If you do not wish to do so, delete this |
36 |
exception statement from your version. */ |
exception statement from your version. */ |
37 |
|
|
|
|
|
38 |
package java.beans; |
package java.beans; |
39 |
|
|
40 |
import java.lang.reflect.Method; |
import java.lang.reflect.Method; |
57 |
** </OL> |
** </OL> |
58 |
** |
** |
59 |
** @author John Keiser |
** @author John Keiser |
60 |
** @since JDK1.1 |
** @author Robert Schuster <thebohemian@gmx.net> |
61 |
** @version 1.1.0, 26 Jul 1998 |
** @since 1.1 |
62 |
|
** @status updated to 1.4 |
63 |
**/ |
**/ |
64 |
|
|
65 |
public class PropertyDescriptor extends FeatureDescriptor { |
public class PropertyDescriptor extends FeatureDescriptor |
66 |
Class propertyType; |
{ |
67 |
Method getMethod; |
Class propertyType; |
68 |
Method setMethod; |
Method getMethod; |
69 |
|
Method setMethod; |
70 |
Class propertyEditorClass; |
|
71 |
boolean bound; |
Class propertyEditorClass; |
72 |
boolean constrained; |
boolean bound; |
73 |
|
boolean constrained; |
74 |
PropertyDescriptor(String name) { |
|
75 |
setName(name); |
PropertyDescriptor(String name) |
76 |
} |
{ |
77 |
|
setName(name); |
78 |
/** Create a new PropertyDescriptor by introspection. |
} |
79 |
** This form of constructor creates the PropertyDescriptor by |
|
80 |
** looking for a getter method named <CODE>get<name>()</CODE> |
/** Create a new PropertyDescriptor by introspection. |
81 |
** (or, optionally, if the property is boolean, |
** This form of constructor creates the PropertyDescriptor by |
82 |
** <CODE>is<name>()</CODE>) and |
** looking for a getter method named <CODE>get<name>()</CODE> |
83 |
** <CODE>set<name>()</CODE> in class |
** (or, optionally, if the property is boolean, |
84 |
** <CODE><beanClass></CODE>, where <name> has its |
** <CODE>is<name>()</CODE>) and |
85 |
** first letter capitalized by the constructor.<P> |
** <CODE>set<name>()</CODE> in class |
86 |
** |
** <CODE><beanClass></CODE>, where <name> has its |
87 |
** <B>Implementation note:</B> If there is both are both isXXX and |
** first letter capitalized by the constructor.<P> |
88 |
** getXXX methods, the former is used in preference to the latter. |
** |
89 |
** We do not check that an isXXX method returns a boolean. In both |
** Note that using this constructor the given property must be read- <strong>and</strong> |
90 |
** cases, this matches the behaviour of JDK 1.4<P> |
** writeable. If the implementation does not both, a read and a write method, an |
91 |
** |
** <code>IntrospectionException</code> is thrown. |
92 |
** @param name the programmatic name of the property, usually |
** |
93 |
** starting with a lowercase letter (e.g. fooManChu |
** <B>Implementation note:</B> If there is both are both isXXX and |
94 |
** instead of FooManChu). |
** getXXX methods, the former is used in preference to the latter. |
95 |
** @param beanClass the class the get and set methods live in. |
** We do not check that an isXXX method returns a boolean. In both |
96 |
** @exception IntrospectionException if the methods are not found |
** cases, this matches the behaviour of JDK 1.4<P> |
97 |
** or invalid. |
** |
98 |
**/ |
** @param name the programmatic name of the property, usually |
99 |
public PropertyDescriptor(String name, Class beanClass) |
** starting with a lowercase letter (e.g. fooManChu |
100 |
throws IntrospectionException |
** instead of FooManChu). |
101 |
{ |
** @param beanClass the class the get and set methods live in. |
102 |
setName(name); |
** @exception IntrospectionException if the methods are not found |
103 |
if (name.length() == 0) { |
** or invalid. |
104 |
throw new IntrospectionException("empty property name"); |
**/ |
105 |
} |
public PropertyDescriptor(String name, Class beanClass) |
106 |
String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
throws IntrospectionException |
107 |
findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); |
{ |
108 |
if (getMethod == null) { |
setName(name); |
109 |
throw new IntrospectionException("Cannot find an is" + caps + |
if (name.length() == 0) |
110 |
" or get" + caps + " method"); |
{ |
111 |
} |
throw new IntrospectionException("empty property name"); |
112 |
if (setMethod == null) { |
} |
113 |
throw new IntrospectionException("Cannot find a " + caps + " method"); |
String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
114 |
} |
findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps); |
115 |
checkMethods(); |
|
116 |
} |
if (getMethod == null) |
117 |
|
{ |
118 |
/** Create a new PropertyDescriptor by introspection. |
throw new IntrospectionException( |
119 |
** This form of constructor allows you to specify the |
"Cannot find a is" + caps + " or get" + caps + " method"); |
120 |
** names of the get and set methods to search for.<P> |
} |
121 |
** |
|
122 |
** <B>Implementation note:</B> If there is a get method (or |
if (setMethod == null) |
123 |
** boolean isXXX() method), then the return type of that method |
{ |
124 |
** is used to find the set method. If there is no get method, |
throw new IntrospectionException( |
125 |
** then the set method is searched for exhaustively.<P> |
"Cannot find a " + caps + " method"); |
126 |
** |
} |
127 |
** <B>Spec note:</B> |
|
128 |
** If there is no get method and multiple set methods with |
// finally check the methods compatibility |
129 |
** the same name and a single parameter (different type of course), |
checkMethods(getMethod, setMethod); |
130 |
** then an IntrospectionException is thrown. While Sun's spec |
} |
131 |
** does not state this, it can make Bean behavior different on |
|
132 |
** different systems (since method order is not guaranteed) and as |
/** Create a new PropertyDescriptor by introspection. |
133 |
** such, can be treated as a bug in the spec. I am not aware of |
** This form of constructor allows you to specify the |
134 |
** whether Sun's implementation catches this. |
** names of the get and set methods to search for.<P> |
135 |
** |
** |
136 |
** @param name the programmatic name of the property, usually |
** <B>Implementation note:</B> If there is a get method (or |
137 |
** starting with a lowercase letter (e.g. fooManChu |
** boolean isXXX() method), then the return type of that method |
138 |
** instead of FooManChu). |
** is used to find the set method. If there is no get method, |
139 |
** @param beanClass the class the get and set methods live in. |
** then the set method is searched for exhaustively.<P> |
140 |
** @param getMethodName the name of the get method. |
** |
141 |
** @param setMethodName the name of the set method. |
** <B>Spec note:</B> |
142 |
** @exception IntrospectionException if the methods are not found |
** If there is no get method and multiple set methods with |
143 |
** or invalid. |
** the same name and a single parameter (different type of course), |
144 |
**/ |
** then an IntrospectionException is thrown. While Sun's spec |
145 |
public PropertyDescriptor(String name, Class beanClass, |
** does not state this, it can make Bean behavior different on |
146 |
String getMethodName, String setMethodName) |
** different systems (since method order is not guaranteed) and as |
147 |
throws IntrospectionException |
** such, can be treated as a bug in the spec. I am not aware of |
148 |
{ |
** whether Sun's implementation catches this. |
149 |
setName(name); |
** |
150 |
findMethods(beanClass, getMethodName, null, setMethodName); |
** @param name the programmatic name of the property, usually |
151 |
if (getMethod == null && getMethodName != null) { |
** starting with a lowercase letter (e.g. fooManChu |
152 |
throw new IntrospectionException("Cannot find a getter method called " + |
** instead of FooManChu). |
153 |
getMethodName); |
** @param beanClass the class the get and set methods live in. |
154 |
} |
** @param getMethodName the name of the get method or <code>null</code> if the property is write-only. |
155 |
if (setMethod == null && setMethodName != null) { |
** @param setMethodName the name of the set method or <code>null</code> if the property is read-only. |
156 |
throw new IntrospectionException("Cannot find a setter method called " + |
** @exception IntrospectionException if the methods are not found |
157 |
setMethodName); |
** or invalid. |
158 |
} |
**/ |
159 |
checkMethods(); |
public PropertyDescriptor( |
160 |
} |
String name, |
161 |
|
Class beanClass, |
162 |
/** Create a new PropertyDescriptor using explicit Methods. |
String getMethodName, |
163 |
** Note that the methods will be checked for conformance to standard |
String setMethodName) |
164 |
** Property method rules, as described above at the top of this class. |
throws IntrospectionException |
165 |
** |
{ |
166 |
** @param name the programmatic name of the property, usually |
setName(name); |
167 |
** starting with a lowercase letter (e.g. fooManChu |
findMethods(beanClass, getMethodName, null, setMethodName); |
168 |
** instead of FooManChu). |
|
169 |
** @param getMethod the get method. |
if (getMethod == null && getMethodName != null) |
170 |
** @param setMethod the set method. |
{ |
171 |
** @exception IntrospectionException if the methods are not found |
throw new IntrospectionException( |
172 |
** or invalid. |
"Cannot find a getter method called " + getMethodName); |
173 |
**/ |
} |
174 |
public PropertyDescriptor(String name, Method getMethod, Method setMethod) |
|
175 |
throws IntrospectionException |
if (setMethod == null && setMethodName != null) |
176 |
{ |
{ |
177 |
setName(name); |
throw new IntrospectionException( |
178 |
this.getMethod = getMethod; |
"Cannot find a setter method called " + setMethodName); |
179 |
this.setMethod = setMethod; |
} |
180 |
if (getMethod != null) { |
|
181 |
this.propertyType = getMethod.getReturnType(); |
checkMethods(getMethod, setMethod); |
182 |
} |
} |
183 |
else if (setMethod != null) { |
|
184 |
this.propertyType = setMethod.getParameterTypes()[0]; |
/** Create a new PropertyDescriptor using explicit Methods. |
185 |
} |
** Note that the methods will be checked for conformance to standard |
186 |
checkMethods(); |
** Property method rules, as described above at the top of this class. |
187 |
} |
**<br> |
188 |
|
** It is possible to call this method with both <code>Method</code> arguments |
189 |
/** Get the property type. |
** being <code>null</code>. In such a case the property type is <code>null</code>. |
190 |
** This is the type the get method returns and the set method |
** |
191 |
** takes in. |
** @param name the programmatic name of the property, usually |
192 |
**/ |
** starting with a lowercase letter (e.g. fooManChu |
193 |
public Class getPropertyType() { |
** instead of FooManChu). |
194 |
return propertyType; |
** @param readMethod the read method or <code>null</code> if the property is write-only. |
195 |
} |
** @param writeMethod the write method or <code>null</code> if the property is read-only. |
196 |
|
** @exception IntrospectionException if the methods are not found |
197 |
/** Get the get method. Why they call it readMethod here and |
** or invalid. |
198 |
** get everywhere else is beyond me. |
**/ |
199 |
**/ |
public PropertyDescriptor( |
200 |
public Method getReadMethod() { |
String name, |
201 |
return getMethod; |
Method readMethod, |
202 |
} |
Method writeMethod) |
203 |
|
throws IntrospectionException |
204 |
/** Get the set method. Why they call it writeMethod here and |
{ |
205 |
** set everywhere else is beyond me. |
setName(name); |
206 |
**/ |
getMethod = readMethod; |
207 |
public Method getWriteMethod() { |
setMethod = writeMethod; |
208 |
return setMethod; |
|
209 |
} |
if (getMethod != null) |
210 |
|
{ |
211 |
/** Get whether the property is bound. Defaults to false. **/ |
this.propertyType = getMethod.getReturnType(); |
212 |
public boolean isBound() { |
} |
213 |
return bound; |
else if (setMethod != null) |
214 |
} |
{ |
215 |
|
this.propertyType = setMethod.getParameterTypes()[0]; |
216 |
/** Set whether the property is bound. |
} |
217 |
** As long as the the bean implements addPropertyChangeListener() and |
|
218 |
** removePropertyChangeListener(), setBound(true) may safely be called.<P> |
checkMethods(getMethod, setMethod); |
219 |
** If these things are not true, then the behavior of the system |
} |
220 |
** will be undefined.<P> |
|
221 |
** |
/** Get the property type. |
222 |
** When a property is bound, its set method is required to fire the |
** This is the type the get method returns and the set method |
223 |
** <CODE>PropertyChangeListener.propertyChange())</CODE> event |
** takes in. |
224 |
** after the value has changed. |
**/ |
225 |
** @param bound whether the property is bound or not. |
public Class getPropertyType() |
226 |
**/ |
{ |
227 |
public void setBound(boolean bound) { |
return propertyType; |
228 |
this.bound = bound; |
} |
229 |
} |
|
230 |
|
/** Get the get method. Why they call it readMethod here and |
231 |
/** Get whether the property is constrained. Defaults to false. **/ |
** get everywhere else is beyond me. |
232 |
public boolean isConstrained() { |
**/ |
233 |
return constrained; |
public Method getReadMethod() |
234 |
} |
{ |
235 |
|
return getMethod; |
236 |
/** Set whether the property is constrained. |
} |
237 |
** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> |
|
238 |
** (or subclass thereof) and the bean implements addVetoableChangeListener() |
/** Sets the read method.<br/> |
239 |
** and removeVetoableChangeListener(), then setConstrained(true) may safely |
* The read method is used to retrieve the value of a property. A legal |
240 |
** be called. Otherwise, the system behavior is undefined. |
* read method must have no arguments. Its return type must not be |
241 |
** <B>Spec note:</B> given those strict parameters, it would be nice if it |
* <code>void</code>. If this methods succeeds the property type |
242 |
** got set automatically by detection, but oh well.<P> |
* is adjusted to the return type of the read method.<br/> |
243 |
** When a property is constrained, its set method is required to:<P> |
* <br/> |
244 |
** <OL> |
* It is legal to set the read and the write method to <code>null</code> |
245 |
** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> |
* or provide method which have been declared in distinct classes. |
246 |
** event notifying others of the change and allowing them a chance to |
* |
247 |
** say it is a bad thing.</LI> |
* @param readMethod The new method to be used or <code>null</code>. |
248 |
** <LI>If any of the listeners throws a PropertyVetoException, then |
* @throws IntrospectionException If the given method is invalid. |
249 |
** it must fire another vetoableChange() event notifying the others |
* @since 1.2 |
250 |
** of a reversion to the old value (though, of course, the change |
*/ |
251 |
** was never made). Then it rethrows the PropertyVetoException and |
public void setReadMethod(Method readMethod) throws IntrospectionException |
252 |
** exits.</LI> |
{ |
253 |
** <LI>If all has gone well to this point, the value may be changed.</LI> |
checkMethods(readMethod, setMethod); |
254 |
** </OL> |
|
255 |
** @param constrained whether the property is constrained or not. |
getMethod = readMethod; |
256 |
**/ |
} |
257 |
public void setConstrained(boolean constrained) { |
|
258 |
this.constrained = constrained; |
/** Get the set method. Why they call it writeMethod here and |
259 |
} |
** set everywhere else is beyond me. |
260 |
|
**/ |
261 |
/** Get the PropertyEditor class. Defaults to null. **/ |
public Method getWriteMethod() |
262 |
public Class getPropertyEditorClass() { |
{ |
263 |
return propertyEditorClass; |
return setMethod; |
264 |
} |
} |
265 |
|
|
266 |
/** Set the PropertyEditor class. If the class does not implement |
/** Sets the write method.<br/> |
267 |
** the PropertyEditor interface, you will likely get an exception |
* The write method is used to set the value of a property. A legal write method |
268 |
** late in the game. |
* must have a single argument which can be assigned to the property. If no |
269 |
** @param propertyEditorClass the PropertyEditor class for this |
* read method exists the property type changes to the argument type of the |
270 |
** class to use. |
* write method.<br/> |
271 |
**/ |
* <br/> |
272 |
public void setPropertyEditorClass(Class propertyEditorClass) { |
* It is legal to set the read and the write method to <code>null</code> |
273 |
this.propertyEditorClass = propertyEditorClass; |
* or provide method which have been declared in distinct classes. |
274 |
} |
* |
275 |
|
* @param writeMethod The new method to be used or <code>null</code>. |
276 |
private void findMethods(Class beanClass, String getMethodName1, |
* @throws IntrospectionException If the given method is invalid. |
277 |
String getMethodName2, String setMethodName) |
* @since 1.2 |
278 |
throws IntrospectionException |
*/ |
279 |
{ |
public void setWriteMethod(Method writeMethod) |
280 |
try { |
throws IntrospectionException |
281 |
// Try the first get method name |
{ |
282 |
if (getMethodName1 != null) { |
propertyType = checkMethods(getMethod, writeMethod); |
283 |
try { |
|
284 |
getMethod = beanClass.getMethod(getMethodName1, new Class[0]); |
setMethod = writeMethod; |
|
} |
|
|
catch (NoSuchMethodException e) { |
|
|
} |
|
|
} |
|
|
|
|
|
// Fall back to the second get method name |
|
|
if (getMethod == null && getMethodName2 != null) { |
|
|
try { |
|
|
getMethod = beanClass.getMethod(getMethodName2, new Class[0]); |
|
|
} |
|
|
catch (NoSuchMethodException e) { |
|
|
} |
|
|
} |
|
|
|
|
|
// Try the set method name |
|
|
if (setMethodName != null) { |
|
|
if (getMethod != null) { |
|
|
// If there is a get method, use its return type to help |
|
|
// select the corresponding set method. |
|
|
Class propertyType = getMethod.getReturnType(); |
|
|
if (propertyType == Void.TYPE) { |
|
|
String msg = "The property's read method has return type 'void'"; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
|
|
|
Class[] setArgs = new Class[]{propertyType}; |
|
|
try { |
|
|
setMethod = beanClass.getMethod(setMethodName, setArgs); |
|
|
} |
|
|
catch (NoSuchMethodException e) { |
|
|
} |
|
|
} |
|
|
else if (getMethodName1 == null && getMethodName2 == null) { |
|
|
// If this is a write-only property, choose the first set method |
|
|
// with the required name, one parameter and return type 'void' |
|
|
Method[] methods = beanClass.getMethods(); |
|
|
for (int i = 0; i < methods.length; i++) { |
|
|
if (methods[i].getName().equals(setMethodName) && |
|
|
methods[i].getParameterTypes().length == 1 && |
|
|
methods[i].getReturnType() == Void.TYPE) { |
|
|
setMethod = methods[i]; |
|
|
break; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
catch (SecurityException e) { |
|
|
// FIXME -- shouldn't we just allow SecurityException to propagate? |
|
|
String msg = "SecurityException thrown on attempt to access methods."; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
} |
|
|
|
|
|
private void checkMethods() |
|
|
throws IntrospectionException |
|
|
{ |
|
|
if (getMethod != null) { |
|
|
if (getMethod.getParameterTypes().length > 0) { |
|
|
throw new IntrospectionException("get method has parameters"); |
|
|
} |
|
|
this.propertyType = getMethod.getReturnType(); |
|
|
if (propertyType == Void.TYPE) { |
|
|
throw new IntrospectionException("get method has void return type"); |
|
|
} |
|
|
} |
|
|
if (setMethod != null) { |
|
|
if (setMethod.getParameterTypes().length != 1) { |
|
|
String msg = "set method does not have exactly one parameter"; |
|
|
throw new IntrospectionException(msg); |
|
|
} |
|
|
if (getMethod == null) { |
|
|
propertyType = setMethod.getParameterTypes()[0]; |
|
|
} |
|
|
else { |
|
|
if (!propertyType.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); |
|
|
} |
|
|
} |
|
285 |
} |
} |
286 |
} |
|
287 |
|
/** Get whether the property is bound. Defaults to false. **/ |
288 |
|
public boolean isBound() |
289 |
|
{ |
290 |
|
return bound; |
291 |
|
} |
292 |
|
|
293 |
|
/** Set whether the property is bound. |
294 |
|
** As long as the the bean implements addPropertyChangeListener() and |
295 |
|
** removePropertyChangeListener(), setBound(true) may safely be called.<P> |
296 |
|
** If these things are not true, then the behavior of the system |
297 |
|
** will be undefined.<P> |
298 |
|
** |
299 |
|
** When a property is bound, its set method is required to fire the |
300 |
|
** <CODE>PropertyChangeListener.propertyChange())</CODE> event |
301 |
|
** after the value has changed. |
302 |
|
** @param bound whether the property is bound or not. |
303 |
|
**/ |
304 |
|
public void setBound(boolean bound) |
305 |
|
{ |
306 |
|
this.bound = bound; |
307 |
|
} |
308 |
|
|
309 |
|
/** Get whether the property is constrained. Defaults to false. **/ |
310 |
|
public boolean isConstrained() |
311 |
|
{ |
312 |
|
return constrained; |
313 |
|
} |
314 |
|
|
315 |
|
/** Set whether the property is constrained. |
316 |
|
** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> |
317 |
|
** (or subclass thereof) and the bean implements addVetoableChangeListener() |
318 |
|
** and removeVetoableChangeListener(), then setConstrained(true) may safely |
319 |
|
** be called. Otherwise, the system behavior is undefined. |
320 |
|
** <B>Spec note:</B> given those strict parameters, it would be nice if it |
321 |
|
** got set automatically by detection, but oh well.<P> |
322 |
|
** When a property is constrained, its set method is required to:<P> |
323 |
|
** <OL> |
324 |
|
** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> |
325 |
|
** event notifying others of the change and allowing them a chance to |
326 |
|
** say it is a bad thing.</LI> |
327 |
|
** <LI>If any of the listeners throws a PropertyVetoException, then |
328 |
|
** it must fire another vetoableChange() event notifying the others |
329 |
|
** of a reversion to the old value (though, of course, the change |
330 |
|
** was never made). Then it rethrows the PropertyVetoException and |
331 |
|
** exits.</LI> |
332 |
|
** <LI>If all has gone well to this point, the value may be changed.</LI> |
333 |
|
** </OL> |
334 |
|
** @param constrained whether the property is constrained or not. |
335 |
|
**/ |
336 |
|
public void setConstrained(boolean constrained) |
337 |
|
{ |
338 |
|
this.constrained = constrained; |
339 |
|
} |
340 |
|
|
341 |
|
/** Get the PropertyEditor class. Defaults to null. **/ |
342 |
|
public Class getPropertyEditorClass() |
343 |
|
{ |
344 |
|
return propertyEditorClass; |
345 |
|
} |
346 |
|
|
347 |
|
/** Set the PropertyEditor class. If the class does not implement |
348 |
|
** the PropertyEditor interface, you will likely get an exception |
349 |
|
** late in the game. |
350 |
|
** @param propertyEditorClass the PropertyEditor class for this |
351 |
|
** class to use. |
352 |
|
**/ |
353 |
|
public void setPropertyEditorClass(Class propertyEditorClass) |
354 |
|
{ |
355 |
|
this.propertyEditorClass = propertyEditorClass; |
356 |
|
} |
357 |
|
|
358 |
|
private void findMethods( |
359 |
|
Class beanClass, |
360 |
|
String getMethodName1, |
361 |
|
String getMethodName2, |
362 |
|
String setMethodName) |
363 |
|
throws IntrospectionException |
364 |
|
{ |
365 |
|
try |
366 |
|
{ |
367 |
|
// Try the first get method name |
368 |
|
if (getMethodName1 != null) |
369 |
|
{ |
370 |
|
try |
371 |
|
{ |
372 |
|
getMethod = |
373 |
|
beanClass.getMethod(getMethodName1, new Class[0]); |
374 |
|
} |
375 |
|
catch (NoSuchMethodException e) |
376 |
|
{} |
377 |
|
} |
378 |
|
|
379 |
|
// Fall back to the second get method name |
380 |
|
if (getMethod == null && getMethodName2 != null) |
381 |
|
{ |
382 |
|
try |
383 |
|
{ |
384 |
|
getMethod = |
385 |
|
beanClass.getMethod(getMethodName2, new Class[0]); |
386 |
|
} |
387 |
|
catch (NoSuchMethodException e) |
388 |
|
{} |
389 |
|
} |
390 |
|
|
391 |
|
// Try the set method name |
392 |
|
if (setMethodName != null) |
393 |
|
{ |
394 |
|
if (getMethod != null) |
395 |
|
{ |
396 |
|
// If there is a get method, use its return type to help |
397 |
|
// select the corresponding set method. |
398 |
|
Class propertyType = getMethod.getReturnType(); |
399 |
|
if (propertyType == Void.TYPE) |
400 |
|
{ |
401 |
|
String msg = |
402 |
|
"The property's read method has return type 'void'"; |
403 |
|
throw new IntrospectionException(msg); |
404 |
|
} |
405 |
|
|
406 |
|
Class[] setArgs = new Class[] { propertyType }; |
407 |
|
try |
408 |
|
{ |
409 |
|
setMethod = beanClass.getMethod(setMethodName, setArgs); |
410 |
|
} |
411 |
|
catch (NoSuchMethodException e) |
412 |
|
{} |
413 |
|
} |
414 |
|
else if (getMethodName1 == null && getMethodName2 == null) |
415 |
|
{ |
416 |
|
// If this is a write-only property, choose the first set method |
417 |
|
// with the required name, one parameter and return type 'void' |
418 |
|
Method[] methods = beanClass.getMethods(); |
419 |
|
for (int i = 0; i < methods.length; i++) |
420 |
|
{ |
421 |
|
if (methods[i].getName().equals(setMethodName) |
422 |
|
&& methods[i].getParameterTypes().length == 1 |
423 |
|
&& methods[i].getReturnType() == Void.TYPE) |
424 |
|
{ |
425 |
|
setMethod = methods[i]; |
426 |
|
break; |
427 |
|
} |
428 |
|
} |
429 |
|
} |
430 |
|
} |
431 |
|
} |
432 |
|
catch (SecurityException e) |
433 |
|
{ |
434 |
|
// FIXME -- shouldn't we just allow SecurityException to propagate? |
435 |
|
String msg = |
436 |
|
"SecurityException thrown on attempt to access methods."; |
437 |
|
throw new IntrospectionException(msg); |
438 |
|
} |
439 |
|
} |
440 |
|
|
441 |
|
/** Checks whether the given <code>Method</code> instances are legal read and |
442 |
|
* write methods. The following requirements must be met:<br/> |
443 |
|
* <ul> |
444 |
|
* <li>the read method must not have an argument</li> |
445 |
|
* <li>the read method must have a non void return type</li> |
446 |
|
* <li>the read method may not exist</li> |
447 |
|
* <li>the write method must have a single argument</li> |
448 |
|
* <li>the property type and the read method's return type must be assignable from the |
449 |
|
* write method's argument type</li> |
450 |
|
* <li>the write method may not exist</li> |
451 |
|
* <ul> |
452 |
|
* While checking the methods a common new property type is calculated. If the method |
453 |
|
* succeeds this property type is returned.<br/> |
454 |
|
* <br/> |
455 |
|
* For compatibility this has to be noted:<br/> |
456 |
|
* The two methods are allowed to be defined in two distinct classes and may both be null. |
457 |
|
* |
458 |
|
* @param readMethod The new read method to check. |
459 |
|
* @param writeMethod The new write method to check. |
460 |
|
* @return The common property type of the two method. |
461 |
|
* @throws IntrospectionException If any of the above requirements are not met. |
462 |
|
*/ |
463 |
|
private Class checkMethods(Method readMethod, Method writeMethod) |
464 |
|
throws IntrospectionException |
465 |
|
{ |
466 |
|
Class newPropertyType = propertyType; |
467 |
|
|
468 |
|
// a valid read method has zero arguments and a non-void return type. |
469 |
|
if (readMethod != null) |
470 |
|
{ |
471 |
|
if (readMethod.getParameterTypes().length > 0) |
472 |
|
{ |
473 |
|
throw new IntrospectionException("read method has unexpected parameters"); |
474 |
|
} |
475 |
|
|
476 |
|
newPropertyType = readMethod.getReturnType(); |
477 |
|
|
478 |
|
if (newPropertyType == Void.TYPE) |
479 |
|
{ |
480 |
|
throw new IntrospectionException("read method return type is void"); |
481 |
|
} |
482 |
|
} |
483 |
|
|
484 |
|
// a valid write method has one argument which can be assigned to the property |
485 |
|
if (writeMethod != null) |
486 |
|
{ |
487 |
|
if (writeMethod.getParameterTypes().length != 1) |
488 |
|
{ |
489 |
|
String msg = "write method does not have exactly one parameter"; |
490 |
|
throw new IntrospectionException(msg); |
491 |
|
} |
492 |
|
|
493 |
|
if (readMethod == null) |
494 |
|
{ |
495 |
|
// changes the property type if there is no read method |
496 |
|
newPropertyType = writeMethod.getParameterTypes()[0]; |
497 |
|
} |
498 |
|
else |
499 |
|
{ |
500 |
|
// checks whether the write method can be assigned to the return type of the read |
501 |
|
// method (if this is not the case, the methods are not compatible) |
502 |
|
// note: newPropertyType may be null if no methods or method names have been |
503 |
|
// delivered in the constructor. |
504 |
|
if (newPropertyType != null |
505 |
|
&& !newPropertyType.isAssignableFrom( |
506 |
|
writeMethod.getParameterTypes()[0])) |
507 |
|
{ |
508 |
|
// note: newPropertyType is the same as readMethod.getReturnType() at this point |
509 |
|
throw new IntrospectionException("read and write method are not compatible"); |
510 |
|
} |
511 |
|
|
512 |
|
/* note: the check whether both method are defined in related classes makes sense but is not |
513 |
|
* done in the JDK. |
514 |
|
* I leave this code here in case someone at Sun decides to add that functionality in later versions (rschuster) |
515 |
|
if ((!readMethod |
516 |
|
.getDeclaringClass() |
517 |
|
.isAssignableFrom(writeMethod.getDeclaringClass())) |
518 |
|
&& (!writeMethod |
519 |
|
.getDeclaringClass() |
520 |
|
.isAssignableFrom(readMethod.getDeclaringClass()))) |
521 |
|
{ |
522 |
|
String msg = |
523 |
|
"set and get methods are not in the same class."; |
524 |
|
throw new IntrospectionException(msg); |
525 |
|
} |
526 |
|
*/ |
527 |
|
|
528 |
|
} |
529 |
|
} |
530 |
|
|
531 |
|
return newPropertyType; |
532 |
|
} |
533 |
|
|
534 |
|
/** Compares this <code>PropertyDescriptor</code> against the |
535 |
|
* given object. |
536 |
|
* Two PropertyDescriptors are equals if |
537 |
|
* <ul> |
538 |
|
* <li>the read methods are equal</li> |
539 |
|
* <li>the write methods are equal</li> |
540 |
|
* <li>the property types are equals</li> |
541 |
|
* <li>the property editor classes are equal</li> |
542 |
|
* <li>the flags (constrained and bound) are equal</li> |
543 |
|
* </ul> |
544 |
|
* @return Whether both objects are equal according to the rules given above. |
545 |
|
* @since 1.4 |
546 |
|
*/ |
547 |
|
public boolean equals(Object o) |
548 |
|
{ |
549 |
|
if (o instanceof PropertyDescriptor) |
550 |
|
{ |
551 |
|
PropertyDescriptor that = (PropertyDescriptor) o; |
552 |
|
|
553 |
|
// compares the property types and checks the case where both are null |
554 |
|
boolean samePropertyType = |
555 |
|
(propertyType == null) |
556 |
|
? that.propertyType == null |
557 |
|
: propertyType.equals(that.propertyType); |
558 |
|
|
559 |
|
// compares the property editor classes and checks the case where both are null |
560 |
|
boolean samePropertyEditorClass = |
561 |
|
(propertyEditorClass == null) |
562 |
|
? that.propertyEditorClass == null |
563 |
|
: propertyEditorClass.equals(that.propertyEditorClass); |
564 |
|
|
565 |
|
// compares the flags for equality |
566 |
|
boolean sameFlags = |
567 |
|
bound == that.bound && constrained == that.constrained; |
568 |
|
|
569 |
|
// compares the read methods and checks the case where both are null |
570 |
|
boolean sameReadMethod = |
571 |
|
(getMethod == null) |
572 |
|
? that.getMethod == null |
573 |
|
: getMethod.equals(that.getMethod); |
574 |
|
|
575 |
|
boolean sameWriteMethod = |
576 |
|
(setMethod == null) |
577 |
|
? that.setMethod == null |
578 |
|
: setMethod.equals(that.setMethod); |
579 |
|
|
580 |
|
return samePropertyType |
581 |
|
&& sameFlags |
582 |
|
&& sameReadMethod |
583 |
|
&& sameWriteMethod |
584 |
|
&& samePropertyEditorClass; |
585 |
|
} |
586 |
|
else |
587 |
|
{ |
588 |
|
return false; |
589 |
|
} |
590 |
|
|
591 |
|
} |
592 |
|
|
593 |
} |
} |