65 |
**/ |
**/ |
66 |
|
|
67 |
public class PropertyDescriptor extends FeatureDescriptor { |
public class PropertyDescriptor extends FeatureDescriptor { |
68 |
Class propertyType; |
Class propertyType; |
69 |
Method getMethod; |
Method getMethod; |
70 |
Method setMethod; |
Method setMethod; |
71 |
|
|
72 |
Class propertyEditorClass; |
Class propertyEditorClass; |
73 |
boolean bound; |
boolean bound; |
74 |
boolean constrained; |
boolean constrained; |
75 |
|
|
76 |
PropertyDescriptor(String name) { |
PropertyDescriptor(String name) { |
77 |
setName(name); |
setName(name); |
78 |
} |
} |
79 |
|
|
80 |
/** Create a new PropertyDescriptor by introspection. |
/** Create a new PropertyDescriptor by introspection. |
81 |
** This form of constructor creates the PropertyDescriptor by |
** This form of constructor creates the PropertyDescriptor by |
82 |
** looking for a getter method named <CODE>get<name>()</CODE> |
** looking for a getter method named <CODE>get<name>()</CODE> |
83 |
** (or, optionally, if the property is boolean, |
** (or, optionally, if the property is boolean, |
84 |
** <CODE>is<name>()</CODE>) and |
** <CODE>is<name>()</CODE>) and |
85 |
** <CODE>set<name>()</CODE> in class |
** <CODE>set<name>()</CODE> in class |
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 a get method (or |
90 |
** boolean isXXX() method), then the return type of that method |
** boolean isXXX() method), then the return type of that method |
91 |
** is used to find the set method. If there is no get method, |
** is used to find the set method. If there is no get method, |
92 |
** then the set method is searched for exhaustively.<P> |
** then the set method is searched for exhaustively.<P> |
93 |
** |
** |
94 |
** <B>Spec note:</B> |
** <B>Spec note:</B> |
95 |
** If there is no get method and multiple set methods with |
** If there is no get method and multiple set methods with |
96 |
** the same name and a single parameter (different type of course), |
** the same name and a single parameter (different type of course), |
97 |
** then an IntrospectionException is thrown. While Sun's spec |
** then an IntrospectionException is thrown. While Sun's spec |
98 |
** does not state this, it can make Bean behavior different on |
** does not state this, it can make Bean behavior different on |
99 |
** different systems (since method order is not guaranteed) and as |
** different systems (since method order is not guaranteed) and as |
100 |
** such, can be treated as a bug in the spec. I am not aware of |
** such, can be treated as a bug in the spec. I am not aware of |
101 |
** whether Sun's implementation catches this. |
** whether Sun's implementation catches this. |
102 |
** |
** |
103 |
** @param name the programmatic name of the property, usually |
** @param name the programmatic name of the property, usually |
104 |
** starting with a lowercase letter (e.g. fooManChu |
** starting with a lowercase letter (e.g. fooManChu |
105 |
** instead of FooManChu). |
** instead of FooManChu). |
106 |
** @param beanClass the class the get and set methods live in. |
** @param beanClass the class the get and set methods live in. |
107 |
** @exception IntrospectionException if the methods are not found or invalid. |
** @exception IntrospectionException if the methods are not found or invalid. |
108 |
**/ |
**/ |
109 |
public PropertyDescriptor(String name, Class beanClass) throws IntrospectionException { |
public PropertyDescriptor(String name, Class beanClass) |
110 |
setName(name); |
throws IntrospectionException |
111 |
String capitalized; |
{ |
112 |
try { |
setName(name); |
113 |
capitalized = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
String capitalized; |
114 |
} catch(StringIndexOutOfBoundsException e) { |
try { |
115 |
capitalized = ""; |
capitalized = Character.toUpperCase(name.charAt(0)) + name.substring(1); |
116 |
} |
} |
117 |
findMethods(beanClass, "is" + capitalized, "get" + capitalized, "set" + capitalized); |
catch (StringIndexOutOfBoundsException e) { |
118 |
} |
capitalized = ""; |
119 |
|
} |
120 |
/** Create a new PropertyDescriptor by introspection. |
findMethods(beanClass, "is" + capitalized, "get" + capitalized, |
121 |
** This form of constructor allows you to specify the |
"set" + capitalized); |
122 |
** names of the get and set methods to search for.<P> |
} |
123 |
** |
|
124 |
** <B>Implementation note:</B> If there is a get method (or |
/** Create a new PropertyDescriptor by introspection. |
125 |
** boolean isXXX() method), then the return type of that method |
** This form of constructor allows you to specify the |
126 |
** is used to find the set method. If there is no get method, |
** names of the get and set methods to search for.<P> |
127 |
** then the set method is searched for exhaustively.<P> |
** |
128 |
** |
** <B>Implementation note:</B> If there is a get method (or |
129 |
** <B>Spec note:</B> |
** boolean isXXX() method), then the return type of that method |
130 |
** If there is no get method and multiple set methods with |
** is used to find the set method. If there is no get method, |
131 |
** the same name and a single parameter (different type of course), |
** then the set method is searched for exhaustively.<P> |
132 |
** then an IntrospectionException is thrown. While Sun's spec |
** |
133 |
** does not state this, it can make Bean behavior different on |
** <B>Spec note:</B> |
134 |
** different systems (since method order is not guaranteed) and as |
** If there is no get method and multiple set methods with |
135 |
** such, can be treated as a bug in the spec. I am not aware of |
** the same name and a single parameter (different type of course), |
136 |
** whether Sun's implementation catches this. |
** then an IntrospectionException is thrown. While Sun's spec |
137 |
** |
** does not state this, it can make Bean behavior different on |
138 |
** @param name the programmatic name of the property, usually |
** different systems (since method order is not guaranteed) and as |
139 |
** starting with a lowercase letter (e.g. fooManChu |
** such, can be treated as a bug in the spec. I am not aware of |
140 |
** instead of FooManChu). |
** whether Sun's implementation catches this. |
141 |
** @param beanClass the class the get and set methods live in. |
** |
142 |
** @param getMethodName the name of the get method. |
** @param name the programmatic name of the property, usually |
143 |
** @param setMethodName the name of the set method. |
** starting with a lowercase letter (e.g. fooManChu |
144 |
** @exception IntrospectionException if the methods are not found or invalid. |
** instead of FooManChu). |
145 |
**/ |
** @param beanClass the class the get and set methods live in. |
146 |
public PropertyDescriptor(String name, Class beanClass, String getMethodName, String setMethodName) throws IntrospectionException { |
** @param getMethodName the name of the get method. |
147 |
setName(name); |
** @param setMethodName the name of the set method. |
148 |
findMethods(beanClass, getMethodName, null, setMethodName); |
** @exception IntrospectionException if the methods are not found |
149 |
} |
** or invalid. |
150 |
|
**/ |
151 |
/** Create a new PropertyDescriptor using explicit Methods. |
public PropertyDescriptor(String name, Class beanClass, |
152 |
** Note that the methods will be checked for conformance to standard |
String getMethodName, String setMethodName) |
153 |
** Property method rules, as described above at the top of this class. |
throws IntrospectionException |
154 |
** |
{ |
155 |
** @param name the programmatic name of the property, usually |
setName(name); |
156 |
** starting with a lowercase letter (e.g. fooManChu |
findMethods(beanClass, getMethodName, null, setMethodName); |
157 |
** instead of FooManChu). |
} |
158 |
** @param getMethod the get method. |
|
159 |
** @param setMethod the set method. |
/** Create a new PropertyDescriptor using explicit Methods. |
160 |
** @exception IntrospectionException if the methods are not found or invalid. |
** Note that the methods will be checked for conformance to standard |
161 |
**/ |
** Property method rules, as described above at the top of this class. |
162 |
public PropertyDescriptor(String name, Method getMethod, Method setMethod) throws IntrospectionException { |
** |
163 |
setName(name); |
** @param name the programmatic name of the property, usually |
164 |
if(getMethod != null && getMethod.getParameterTypes().length > 0) { |
** starting with a lowercase letter (e.g. fooManChu |
165 |
throw new IntrospectionException("get method has parameters"); |
** instead of FooManChu). |
166 |
} |
** @param getMethod the get method. |
167 |
if(setMethod != null && setMethod.getParameterTypes().length != 1) { |
** @param setMethod the set method. |
168 |
throw new IntrospectionException("set method does not have exactly one parameter"); |
** @exception IntrospectionException if the methods are not found |
169 |
} |
** or invalid. |
170 |
if(getMethod != null && setMethod != null) { |
**/ |
171 |
if(!getMethod.getReturnType().equals(setMethod.getParameterTypes()[0])) { |
public PropertyDescriptor(String name, Method getMethod, Method setMethod) |
172 |
throw new IntrospectionException("set and get methods do not share the same type"); |
throws IntrospectionException |
173 |
} |
{ |
174 |
if(!getMethod.getDeclaringClass().isAssignableFrom(setMethod.getDeclaringClass()) |
setName(name); |
175 |
&& !setMethod.getDeclaringClass().isAssignableFrom(getMethod.getDeclaringClass())) { |
if (getMethod != null && getMethod.getParameterTypes().length > 0) { |
176 |
throw new IntrospectionException("set and get methods are not in the same class."); |
throw new IntrospectionException("get method has parameters"); |
177 |
} |
} |
178 |
} |
if (setMethod != null && setMethod.getParameterTypes().length != 1) { |
179 |
this.getMethod = getMethod; |
String msg = "set method does not have exactly one parameter"; |
180 |
this.setMethod = setMethod; |
throw new IntrospectionException(msg); |
181 |
if(getMethod != null) { |
} |
182 |
this.propertyType = getMethod.getReturnType(); |
if (getMethod != null && setMethod != null) { |
183 |
} else { |
if (!getMethod.getReturnType(). |
184 |
this.propertyType = setMethod.getParameterTypes()[0]; |
equals(setMethod.getParameterTypes()[0])) { |
185 |
} |
String msg = "set and get methods do not share the same type"; |
186 |
} |
throw new IntrospectionException(msg); |
187 |
|
} |
188 |
/** Get the property type. |
if ((!getMethod.getDeclaringClass(). |
189 |
** This is the type the get method returns and the set method |
isAssignableFrom(setMethod.getDeclaringClass())) && |
190 |
** takes in. |
(!setMethod.getDeclaringClass(). |
191 |
**/ |
isAssignableFrom(getMethod.getDeclaringClass()))) { |
192 |
public Class getPropertyType() { |
String msg = "set and get methods are not in the same class."; |
193 |
return propertyType; |
throw new IntrospectionException(msg); |
194 |
} |
} |
195 |
|
} |
196 |
/** Get the get method. Why they call it readMethod here and |
this.getMethod = getMethod; |
197 |
** get everywhere else is beyond me. |
this.setMethod = setMethod; |
198 |
**/ |
if (getMethod != null) { |
199 |
public Method getReadMethod() { |
this.propertyType = getMethod.getReturnType(); |
200 |
return getMethod; |
} |
201 |
} |
else { |
202 |
|
this.propertyType = setMethod.getParameterTypes()[0]; |
203 |
/** Get the set method. Why they call it writeMethod here and |
} |
204 |
** set everywhere else is beyond me. |
} |
205 |
**/ |
|
206 |
public Method getWriteMethod() { |
/** Get the property type. |
207 |
return setMethod; |
** This is the type the get method returns and the set method |
208 |
} |
** takes in. |
209 |
|
**/ |
210 |
/** Get whether the property is bound. Defaults to false. **/ |
public Class getPropertyType() { |
211 |
public boolean isBound() { |
return propertyType; |
212 |
return bound; |
} |
213 |
} |
|
214 |
|
/** Get the get method. Why they call it readMethod here and |
215 |
/** Set whether the property is bound. |
** get everywhere else is beyond me. |
216 |
** As long as the the bean implements addPropertyChangeListener() and |
**/ |
217 |
** removePropertyChangeListener(), setBound(true) may safely be called.<P> |
public Method getReadMethod() { |
218 |
** If these things are not true, then the behavior of the system |
return getMethod; |
219 |
** will be undefined.<P> |
} |
220 |
** |
|
221 |
** When a property is bound, its set method is required to fire the |
/** Get the set method. Why they call it writeMethod here and |
222 |
** <CODE>PropertyChangeListener.propertyChange())</CODE> event |
** set everywhere else is beyond me. |
223 |
** after the value has changed. |
**/ |
224 |
** @param bound whether the property is bound or not. |
public Method getWriteMethod() { |
225 |
**/ |
return setMethod; |
226 |
public void setBound(boolean bound) { |
} |
227 |
this.bound = bound; |
|
228 |
} |
/** Get whether the property is bound. Defaults to false. **/ |
229 |
|
public boolean isBound() { |
230 |
/** Get whether the property is constrained. Defaults to false. **/ |
return bound; |
231 |
public boolean isConstrained() { |
} |
232 |
return constrained; |
|
233 |
} |
/** Set whether the property is bound. |
234 |
|
** As long as the the bean implements addPropertyChangeListener() and |
235 |
/** Set whether the property is constrained. |
** removePropertyChangeListener(), setBound(true) may safely be called.<P> |
236 |
** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> |
** If these things are not true, then the behavior of the system |
237 |
** (or subclass thereof) and the bean implements addVetoableChangeListener() |
** will be undefined.<P> |
238 |
** and removeVetoableChangeListener(), then setConstrained(true) may safely |
** |
239 |
** be called. Otherwise, the system behavior is undefined. |
** When a property is bound, its set method is required to fire the |
240 |
** <B>Spec note:</B> given those strict parameters, it would be nice if it |
** <CODE>PropertyChangeListener.propertyChange())</CODE> event |
241 |
** got set automatically by detection, but oh well.<P> |
** after the value has changed. |
242 |
** When a property is constrained, its set method is required to:<P> |
** @param bound whether the property is bound or not. |
243 |
** <OL> |
**/ |
244 |
** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> |
public void setBound(boolean bound) { |
245 |
** event notifying others of the change and allowing them a chance to |
this.bound = bound; |
246 |
** say it is a bad thing.</LI> |
} |
247 |
** <LI>If any of the listeners throws a PropertyVetoException, then |
|
248 |
** it must fire another vetoableChange() event notifying the others |
/** Get whether the property is constrained. Defaults to false. **/ |
249 |
** of a reversion to the old value (though, of course, the change |
public boolean isConstrained() { |
250 |
** was never made). Then it rethrows the PropertyVetoException and |
return constrained; |
251 |
** exits.</LI> |
} |
252 |
** <LI>If all has gone well to this point, the value may be changed.</LI> |
|
253 |
** </OL> |
/** Set whether the property is constrained. |
254 |
** @param constrained whether the property is constrained or not. |
** If the set method throws <CODE>java.beans.PropertyVetoException</CODE> |
255 |
**/ |
** (or subclass thereof) and the bean implements addVetoableChangeListener() |
256 |
public void setConstrained(boolean constrained) { |
** and removeVetoableChangeListener(), then setConstrained(true) may safely |
257 |
this.constrained = constrained; |
** be called. Otherwise, the system behavior is undefined. |
258 |
} |
** <B>Spec note:</B> given those strict parameters, it would be nice if it |
259 |
|
** got set automatically by detection, but oh well.<P> |
260 |
/** Get the PropertyEditor class. Defaults to null. **/ |
** When a property is constrained, its set method is required to:<P> |
261 |
public Class getPropertyEditorClass() { |
** <OL> |
262 |
return propertyEditorClass; |
** <LI>Fire the <CODE>VetoableChangeListener.vetoableChange()</CODE> |
263 |
} |
** event notifying others of the change and allowing them a chance to |
264 |
|
** say it is a bad thing.</LI> |
265 |
/** Set the PropertyEditor class. If the class does not implement |
** <LI>If any of the listeners throws a PropertyVetoException, then |
266 |
** the PropertyEditor interface, you will likely get an exception |
** it must fire another vetoableChange() event notifying the others |
267 |
** late in the game. |
** of a reversion to the old value (though, of course, the change |
268 |
** @param propertyEditorClass the PropertyEditor class for this class to use. |
** was never made). Then it rethrows the PropertyVetoException and |
269 |
**/ |
** exits.</LI> |
270 |
public void setPropertyEditorClass(Class propertyEditorClass) { |
** <LI>If all has gone well to this point, the value may be changed.</LI> |
271 |
this.propertyEditorClass = propertyEditorClass; |
** </OL> |
272 |
} |
** @param constrained whether the property is constrained or not. |
273 |
|
**/ |
274 |
private void findMethods(Class beanClass, String getMethodName1, String getMethodName2, String setMethodName) throws IntrospectionException { |
public void setConstrained(boolean constrained) { |
275 |
try { |
this.constrained = constrained; |
276 |
if(getMethodName1 != null) { |
} |
277 |
try { |
|
278 |
getMethod = beanClass.getMethod(getMethodName1, new Class[0]); |
/** Get the PropertyEditor class. Defaults to null. **/ |
279 |
} catch(NoSuchMethodException E) { |
public Class getPropertyEditorClass() { |
280 |
} |
return propertyEditorClass; |
281 |
if(getMethodName2 != null) { |
} |
282 |
if(getMethod != null && !getMethod.getReturnType().equals(java.lang.Boolean.TYPE)) { |
|
283 |
// If the is() method exists but isn't boolean, we'll just go on and look for |
/** Set the PropertyEditor class. If the class does not implement |
284 |
// an ordinary get() method. |
** the PropertyEditor interface, you will likely get an exception |
285 |
getMethod = null; |
** late in the game. |
286 |
} |
** @param propertyEditorClass the PropertyEditor class for this |
287 |
|
** class to use. |
288 |
Method getMethod2; |
**/ |
289 |
try { |
public void setPropertyEditorClass(Class propertyEditorClass) { |
290 |
getMethod2 = beanClass.getMethod(getMethodName2, new Class[0]); |
this.propertyEditorClass = propertyEditorClass; |
291 |
} catch(NoSuchMethodException E) { |
} |
292 |
getMethod2 = null; |
|
293 |
} |
private void findMethods(Class beanClass, String getMethodName1, |
294 |
if(getMethod2 != null) { |
String getMethodName2, String setMethodName) |
295 |
if(getMethod != null) { |
throws IntrospectionException |
296 |
if(!getMethod.getReturnType().equals(getMethod2.getReturnType())) { |
{ |
297 |
throw new IntrospectionException("Both " + getMethodName1 + " and " + getMethodName2 + " exist, and have contradictory return types."); |
try { |
298 |
} |
if (getMethodName1 != null) { |
299 |
} else { |
try { |
300 |
getMethod = getMethod2; |
getMethod = beanClass.getMethod(getMethodName1, new Class[0]); |
301 |
} |
} |
302 |
} |
catch (NoSuchMethodException E) { |
303 |
} |
} |
304 |
} |
if (getMethodName2 != null) { |
305 |
|
if (getMethod != null && |
306 |
if(getMethod != null) { |
!getMethod.getReturnType().equals(java.lang.Boolean.TYPE)) { |
307 |
propertyType = getMethod.getReturnType(); |
// If the is() method exists but isn't boolean, we'll just |
308 |
if(setMethodName != null) { |
// go on and look for an ordinary get() method. |
309 |
Class[] setArgs = new Class[1]; |
getMethod = null; |
310 |
setArgs[0] = propertyType; |
} |
311 |
try { |
|
312 |
setMethod = beanClass.getMethod(setMethodName, setArgs); |
Method getMethod2; |
313 |
if(!setMethod.getReturnType().equals(java.lang.Void.TYPE)) { |
try { |
314 |
throw new IntrospectionException(setMethodName + " has non-void return type"); |
getMethod2 = beanClass.getMethod(getMethodName2, new Class[0]); |
315 |
} |
} |
316 |
} catch(NoSuchMethodException E) { |
catch (NoSuchMethodException E) { |
317 |
} |
getMethod2 = null; |
318 |
} |
} |
319 |
} else if(setMethodName != null) { |
if (getMethod2 != null) { |
320 |
Method[] m = beanClass.getMethods(); |
if (getMethod != null) { |
321 |
for(int i=0;i<m.length;i++) { |
if (!getMethod.getReturnType(). |
322 |
Method current = m[i]; |
equals(getMethod2.getReturnType())) { |
323 |
if(current.getName().equals(setMethodName) |
String msg = "Both " + getMethodName1 + |
324 |
&& current.getParameterTypes().length == 1 |
" and " + getMethodName2 + |
325 |
&& current.getReturnType().equals(java.lang.Void.TYPE)) { |
" exist, and have contradictory return types."; |
326 |
if(setMethod != null) { |
throw new IntrospectionException(msg); |
327 |
throw new IntrospectionException("Multiple, different set methods found that fit the bill!"); |
} |
328 |
} else { |
} |
329 |
setMethod = current; |
else { |
330 |
propertyType = current.getParameterTypes()[0]; |
getMethod = getMethod2; |
331 |
} |
} |
332 |
} |
} |
333 |
} |
} |
334 |
if(setMethod == null) { |
} |
335 |
throw new IntrospectionException("Cannot find get or set methods."); |
|
336 |
} |
if (getMethod != null) { |
337 |
} else { |
propertyType = getMethod.getReturnType(); |
338 |
throw new IntrospectionException("Cannot find get or set methods."); |
if (setMethodName != null) { |
339 |
} |
Class[] setArgs = new Class[1]; |
340 |
} catch(SecurityException E) { |
setArgs[0] = propertyType; |
341 |
throw new IntrospectionException("SecurityException thrown on attempt to access methods."); |
try { |
342 |
} |
setMethod = beanClass.getMethod(setMethodName, setArgs); |
343 |
} |
if (!setMethod.getReturnType().equals(java.lang.Void.TYPE)) { |
344 |
|
throw new IntrospectionException(setMethodName + |
345 |
|
" has non-void return type"); |
346 |
|
} |
347 |
|
} |
348 |
|
catch (NoSuchMethodException E) { |
349 |
|
} |
350 |
|
} |
351 |
|
} |
352 |
|
else if (setMethodName != null) { |
353 |
|
Method[] m = beanClass.getMethods(); |
354 |
|
for(int i = 0; i < m.length; i++) { |
355 |
|
Method current = m[i]; |
356 |
|
if (current.getName().equals(setMethodName) && |
357 |
|
current.getParameterTypes().length == 1 && |
358 |
|
current.getReturnType().equals(java.lang.Void.TYPE)) { |
359 |
|
if (setMethod != null) { |
360 |
|
String msg = |
361 |
|
"Multiple, different set methods found that fit the bill!"; |
362 |
|
throw new IntrospectionException(msg); |
363 |
|
} |
364 |
|
else { |
365 |
|
setMethod = current; |
366 |
|
propertyType = current.getParameterTypes()[0]; |
367 |
|
} |
368 |
|
} |
369 |
|
} |
370 |
|
if (setMethod == null) { |
371 |
|
throw new IntrospectionException("Cannot find get or set methods."); |
372 |
|
} |
373 |
|
} |
374 |
|
else { |
375 |
|
throw new IntrospectionException("Cannot find get or set methods."); |
376 |
|
} |
377 |
|
} |
378 |
|
catch (SecurityException E) { |
379 |
|
String msg = "SecurityException thrown on attempt to access methods."; |
380 |
|
throw new IntrospectionException(msg); |
381 |
|
} |
382 |
|
} |
383 |
} |
} |