]> gcc.gnu.org Git - gcc.git/blame - libjava/java/beans/PropertyDescriptor.java
Complete 1.4 support
[gcc.git] / libjava / java / beans / PropertyDescriptor.java
CommitLineData
6c80c45e 1/* java.beans.PropertyDescriptor
fbddd18f 2 Copyright (C) 1998, 2001, 2004 Free Software Foundation, Inc.
6c80c45e
TT
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
92aaa246
MW
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
6c80c45e 37
6c80c45e
TT
38package java.beans;
39
cb611e3e 40import java.lang.reflect.Method;
6c80c45e
TT
41
42/**
43 ** PropertyDescriptor describes information about a JavaBean property,
44 ** by which we mean a property that has been exposed via a pair of
45 ** get and set methods. (There may be no get method, which means
46 ** the property is write-only, or no set method, which means the
47 ** the property is read-only.)<P>
48 **
49 ** The constraints put on get and set methods are:<P>
50 ** <OL>
51 ** <LI>A get method must have signature
52 ** <CODE>&lt;propertyType&gt; &lt;getMethodName&gt;()</CODE></LI>
53 ** <LI>A set method must have signature
54 ** <CODE>void &lt;setMethodName&gt;(&lt;propertyType&gt;)</CODE></LI>
55 ** <LI>Either method type may throw any exception.</LI>
56 ** <LI>Both methods must be public.</LI>
57 ** </OL>
58 **
59 ** @author John Keiser
fbddd18f
RS
60 ** @author Robert Schuster <thebohemian@gmx.net>
61 ** @since 1.1
62 ** @status updated to 1.4
6c80c45e
TT
63 **/
64
fbddd18f
RS
65public class PropertyDescriptor extends FeatureDescriptor
66{
67 Class propertyType;
68 Method getMethod;
69 Method setMethod;
70
71 Class propertyEditorClass;
72 boolean bound;
73 boolean constrained;
74
75 PropertyDescriptor(String name)
76 {
77 setName(name);
b20fcd47 78 }
fbddd18f
RS
79
80 /** Create a new PropertyDescriptor by introspection.
81 ** This form of constructor creates the PropertyDescriptor by
82 ** looking for a getter method named <CODE>get&lt;name&gt;()</CODE>
83 ** (or, optionally, if the property is boolean,
84 ** <CODE>is&lt;name&gt;()</CODE>) and
85 ** <CODE>set&lt;name&gt;()</CODE> in class
86 ** <CODE>&lt;beanClass&gt;</CODE>, where &lt;name&gt; has its
87 ** first letter capitalized by the constructor.<P>
88 **
89 ** Note that using this constructor the given property must be read- <strong>and</strong>
90 ** writeable. If the implementation does not both, a read and a write method, an
91 ** <code>IntrospectionException</code> is thrown.
92 **
93 ** <B>Implementation note:</B> If there is both are both isXXX and
94 ** getXXX methods, the former is used in preference to the latter.
95 ** We do not check that an isXXX method returns a boolean. In both
96 ** cases, this matches the behaviour of JDK 1.4<P>
97 **
98 ** @param name the programmatic name of the property, usually
99 ** starting with a lowercase letter (e.g. fooManChu
100 ** instead of FooManChu).
101 ** @param beanClass the class the get and set methods live in.
102 ** @exception IntrospectionException if the methods are not found
103 ** or invalid.
104 **/
105 public PropertyDescriptor(String name, Class beanClass)
106 throws IntrospectionException
107 {
108 setName(name);
109 if (name.length() == 0)
110 {
111 throw new IntrospectionException("empty property name");
112 }
113 String caps = Character.toUpperCase(name.charAt(0)) + name.substring(1);
114 findMethods(beanClass, "is" + caps, "get" + caps, "set" + caps);
115
116 if (getMethod == null)
117 {
118 throw new IntrospectionException(
119 "Cannot find a is" + caps + " or get" + caps + " method");
120 }
121
122 if (setMethod == null)
123 {
124 throw new IntrospectionException(
125 "Cannot find a " + caps + " method");
126 }
127
128 // finally check the methods compatibility
129 checkMethods(getMethod, setMethod);
b20fcd47 130 }
fbddd18f
RS
131
132 /** Create a new PropertyDescriptor by introspection.
133 ** This form of constructor allows you to specify the
134 ** names of the get and set methods to search for.<P>
135 **
136 ** <B>Implementation note:</B> If there is a get method (or
137 ** boolean isXXX() method), then the return type of that method
138 ** is used to find the set method. If there is no get method,
139 ** then the set method is searched for exhaustively.<P>
140 **
141 ** <B>Spec note:</B>
142 ** If there is no get method and multiple set methods with
143 ** the same name and a single parameter (different type of course),
144 ** then an IntrospectionException is thrown. While Sun's spec
145 ** does not state this, it can make Bean behavior different on
146 ** different systems (since method order is not guaranteed) and as
147 ** such, can be treated as a bug in the spec. I am not aware of
148 ** whether Sun's implementation catches this.
149 **
150 ** @param name the programmatic name of the property, usually
151 ** starting with a lowercase letter (e.g. fooManChu
152 ** instead of FooManChu).
153 ** @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 ** @param setMethodName the name of the set method or <code>null</code> if the property is read-only.
156 ** @exception IntrospectionException if the methods are not found
157 ** or invalid.
158 **/
159 public PropertyDescriptor(
160 String name,
161 Class beanClass,
162 String getMethodName,
163 String setMethodName)
164 throws IntrospectionException
165 {
166 setName(name);
167 findMethods(beanClass, getMethodName, null, setMethodName);
168
169 if (getMethod == null && getMethodName != null)
170 {
171 throw new IntrospectionException(
172 "Cannot find a getter method called " + getMethodName);
173 }
174
175 if (setMethod == null && setMethodName != null)
176 {
177 throw new IntrospectionException(
178 "Cannot find a setter method called " + setMethodName);
179 }
180
181 checkMethods(getMethod, setMethod);
b20fcd47 182 }
fbddd18f
RS
183
184 /** Create a new PropertyDescriptor using explicit Methods.
185 ** Note that the methods will be checked for conformance to standard
186 ** 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 ** being <code>null</code>. In such a case the property type is <code>null</code>.
190 **
191 ** @param name the programmatic name of the property, usually
192 ** starting with a lowercase letter (e.g. fooManChu
193 ** instead of FooManChu).
194 ** @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 ** or invalid.
198 **/
199 public PropertyDescriptor(
200 String name,
201 Method readMethod,
202 Method writeMethod)
203 throws IntrospectionException
204 {
205 setName(name);
206 getMethod = readMethod;
207 setMethod = writeMethod;
208
209 if (getMethod != null)
210 {
211 this.propertyType = getMethod.getReturnType();
212 }
213 else if (setMethod != null)
214 {
215 this.propertyType = setMethod.getParameterTypes()[0];
216 }
217
218 checkMethods(getMethod, setMethod);
b20fcd47 219 }
fbddd18f
RS
220
221 /** Get the property type.
222 ** This is the type the get method returns and the set method
223 ** takes in.
224 **/
225 public Class getPropertyType()
226 {
227 return propertyType;
b20fcd47 228 }
fbddd18f
RS
229
230 /** Get the get method. Why they call it readMethod here and
231 ** get everywhere else is beyond me.
232 **/
233 public Method getReadMethod()
234 {
235 return getMethod;
236 }
237
238 /** Sets the read method.<br/>
239 * The read method is used to retrieve the value of a property. A legal
240 * read method must have no arguments. Its return type must not be
241 * <code>void</code>. If this methods succeeds the property type
242 * is adjusted to the return type of the read method.<br/>
243 * <br/>
244 * It is legal to set the read and the write method to <code>null</code>
245 * or provide method which have been declared in distinct classes.
246 *
247 * @param readMethod The new method to be used or <code>null</code>.
248 * @throws IntrospectionException If the given method is invalid.
249 * @since 1.2
250 */
251 public void setReadMethod(Method readMethod) throws IntrospectionException
252 {
253 checkMethods(readMethod, setMethod);
254
255 getMethod = readMethod;
256 }
257
258 /** Get the set method. Why they call it writeMethod here and
259 ** set everywhere else is beyond me.
260 **/
261 public Method getWriteMethod()
262 {
263 return setMethod;
264 }
265
266 /** Sets the write method.<br/>
267 * The write method is used to set the value of a property. A legal write method
268 * must have a single argument which can be assigned to the property. If no
269 * read method exists the property type changes to the argument type of the
270 * write method.<br/>
271 * <br/>
272 * It is legal to set the read and the write method to <code>null</code>
273 * 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 * @throws IntrospectionException If the given method is invalid.
277 * @since 1.2
278 */
279 public void setWriteMethod(Method writeMethod)
280 throws IntrospectionException
281 {
282 propertyType = checkMethods(getMethod, writeMethod);
283
284 setMethod = writeMethod;
b20fcd47 285 }
fbddd18f
RS
286
287 /** Get whether the property is bound. Defaults to false. **/
288 public boolean isBound()
289 {
290 return bound;
b20fcd47 291 }
fbddd18f
RS
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;
b20fcd47 307 }
fbddd18f
RS
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;
b20fcd47 339 }
fbddd18f
RS
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
6c80c45e 593}
This page took 0.431422 seconds and 5 git commands to generate.