KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > java > beans > Introspector


1 /*
2  * @(#)Introspector.java 1.143 05/08/09
3  *
4  * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package java.beans;
9
10 import java.lang.ref.Reference JavaDoc;
11 import java.lang.ref.SoftReference JavaDoc;
12
13 import java.lang.reflect.Method JavaDoc;
14 import java.lang.reflect.Modifier JavaDoc;
15
16 import java.security.AccessController JavaDoc;
17 import java.security.PrivilegedAction JavaDoc;
18
19 import java.util.Collections JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.EventListener JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.WeakHashMap JavaDoc;
27 import java.util.TreeMap JavaDoc;
28 import sun.reflect.misc.ReflectUtil;
29
30 /**
31  * The Introspector class provides a standard way for tools to learn about
32  * the properties, events, and methods supported by a target Java Bean.
33  * <p>
34  * For each of those three kinds of information, the Introspector will
35  * separately analyze the bean's class and superclasses looking for
36  * either explicit or implicit information and use that information to
37  * build a BeanInfo object that comprehensively describes the target bean.
38  * <p>
39  * For each class "Foo", explicit information may be available if there exists
40  * a corresponding "FooBeanInfo" class that provides a non-null value when
41  * queried for the information. We first look for the BeanInfo class by
42  * taking the full package-qualified name of the target bean class and
43  * appending "BeanInfo" to form a new class name. If this fails, then
44  * we take the final classname component of this name, and look for that
45  * class in each of the packages specified in the BeanInfo package search
46  * path.
47  * <p>
48  * Thus for a class such as "sun.xyz.OurButton" we would first look for a
49  * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd
50  * look in each package in the BeanInfo search path for an OurButtonBeanInfo
51  * class. With the default search path, this would mean looking for
52  * "sun.beans.infos.OurButtonBeanInfo".
53  * <p>
54  * If a class provides explicit BeanInfo about itself then we add that to
55  * the BeanInfo information we obtained from analyzing any derived classes,
56  * but we regard the explicit information as being definitive for the current
57  * class and its base classes, and do not proceed any further up the superclass
58  * chain.
59  * <p>
60  * If we don't find explicit BeanInfo on a class, we use low-level
61  * reflection to study the methods of the class and apply standard design
62  * patterns to identify property accessors, event sources, or public
63  * methods. We then proceed to analyze the class's superclass and add
64  * in the information from it (and possibly on up the superclass chain).
65  *
66  * <p>
67  * Because the Introspector caches BeanInfo classes for better performance,
68  * take care if you use it in an application that uses
69  * multiple class loaders.
70  * In general, when you destroy a <code>ClassLoader</code>
71  * that has been used to introspect classes,
72  * you should use the
73  * {@link #flushCaches <code>Introspector.flushCaches</code>}
74  * or
75  * {@link #flushFromCaches <code>Introspector.flushFromCaches</code>} method
76  * to flush all of the introspected classes out of the cache.
77  *
78  * <P>
79  * For more information about introspection and design patterns, please
80  * consult the
81  * <a HREF="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans specification</a>.
82  */

83
84 public class Introspector {
85
86     // Flags that can be used to control getBeanInfo:
87
public final static int USE_ALL_BEANINFO = 1;
88     public final static int IGNORE_IMMEDIATE_BEANINFO = 2;
89     public final static int IGNORE_ALL_BEANINFO = 3;
90
91     // Static Caches to speed up introspection.
92
private static Map JavaDoc declaredMethodCache =
93     Collections.synchronizedMap(new WeakHashMap JavaDoc());
94     private static Map JavaDoc beanInfoCache =
95     Collections.synchronizedMap(new WeakHashMap JavaDoc());
96
97     private Class JavaDoc beanClass;
98     private BeanInfo JavaDoc explicitBeanInfo;
99     private BeanInfo JavaDoc superBeanInfo;
100     private BeanInfo JavaDoc additionalBeanInfo[];
101
102     private boolean propertyChangeSource = false;
103     private static Class JavaDoc eventListenerType = EventListener JavaDoc.class;
104
105     // These should be removed.
106
private String JavaDoc defaultEventName;
107     private String JavaDoc defaultPropertyName;
108     private int defaultEventIndex = -1;
109     private int defaultPropertyIndex = -1;
110
111     // Methods maps from Method objects to MethodDescriptors
112
private Map JavaDoc methods;
113
114     // properties maps from String names to PropertyDescriptors
115
private Map JavaDoc properties;
116
117     // events maps from String names to EventSetDescriptors
118
private Map JavaDoc events;
119
120     private final static String JavaDoc DEFAULT_INFO_PATH = "sun.beans.infos";
121
122     private static String JavaDoc[] searchPath = { DEFAULT_INFO_PATH };
123
124     private final static EventSetDescriptor JavaDoc[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor JavaDoc[0];
125
126     private static final String JavaDoc ADD_PREFIX = "add";
127     private static final String JavaDoc REMOVE_PREFIX = "remove";
128     private static final String JavaDoc GET_PREFIX = "get";
129     private static final String JavaDoc SET_PREFIX = "set";
130     private static final String JavaDoc IS_PREFIX = "is";
131     private static final String JavaDoc BEANINFO_SUFFIX = "BeanInfo";
132
133     //======================================================================
134
// Public methods
135
//======================================================================
136

137     /**
138      * Introspect on a Java Bean and learn about all its properties, exposed
139      * methods, and events.
140      * <p>
141      * If the BeanInfo class for a Java Bean has been previously Introspected
142      * then the BeanInfo class is retrieved from the BeanInfo cache.
143      *
144      * @param beanClass The bean class to be analyzed.
145      * @return A BeanInfo object describing the target bean.
146      * @exception IntrospectionException if an exception occurs during
147      * introspection.
148      * @see #flushCaches
149      * @see #flushFromCaches
150      */

151     public static BeanInfo JavaDoc getBeanInfo(Class JavaDoc<?> beanClass)
152     throws IntrospectionException JavaDoc
153     {
154     if (!ReflectUtil.isPackageAccessible(beanClass)) {
155         return (new Introspector JavaDoc(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
156     }
157     BeanInfo JavaDoc bi = (BeanInfo JavaDoc)beanInfoCache.get(beanClass);
158     if (bi == null) {
159         bi = (new Introspector JavaDoc(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
160         beanInfoCache.put(beanClass, bi);
161     }
162     return bi;
163     }
164
165     /**
166      * Introspect on a Java bean and learn about all its properties, exposed
167      * methods, and events, subject to some control flags.
168      * <p>
169      * If the BeanInfo class for a Java Bean has been previously Introspected
170      * based on the same arguments then the BeanInfo class is retrieved
171      * from the BeanInfo cache.
172      *
173      * @param beanClass The bean class to be analyzed.
174      * @param flags Flags to control the introspection.
175      * If flags == USE_ALL_BEANINFO then we use all of the BeanInfo
176      * classes we can discover.
177      * If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any
178      * BeanInfo associated with the specified beanClass.
179      * If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo
180      * associated with the specified beanClass or any of its
181      * parent classes.
182      * @return A BeanInfo object describing the target bean.
183      * @exception IntrospectionException if an exception occurs during
184      * introspection.
185      */

186     public static BeanInfo JavaDoc getBeanInfo(Class JavaDoc<?> beanClass, int flags)
187                         throws IntrospectionException JavaDoc {
188     return getBeanInfo(beanClass, null, flags);
189     }
190
191     /**
192      * Introspect on a Java bean and learn all about its properties, exposed
193      * methods, below a given "stop" point.
194      * <p>
195      * If the BeanInfo class for a Java Bean has been previously Introspected
196      * based on the same arguments, then the BeanInfo class is retrieved
197      * from the BeanInfo cache.
198      *
199      * @param beanClass The bean class to be analyzed.
200      * @param stopClass The baseclass at which to stop the analysis. Any
201      * methods/properties/events in the stopClass or in its baseclasses
202      * will be ignored in the analysis.
203      * @exception IntrospectionException if an exception occurs during
204      * introspection.
205      */

206     public static BeanInfo JavaDoc getBeanInfo(Class JavaDoc<?> beanClass, Class JavaDoc<?> stopClass)
207                         throws IntrospectionException JavaDoc {
208     return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
209     }
210
211     /**
212      * Only called from the public getBeanInfo methods. This method caches
213      * the Introspected BeanInfo based on the arguments.
214      */

215     private static BeanInfo JavaDoc getBeanInfo(Class JavaDoc beanClass, Class JavaDoc stopClass,
216                     int flags) throws IntrospectionException JavaDoc {
217     BeanInfo JavaDoc bi;
218     if (stopClass == null && flags == USE_ALL_BEANINFO) {
219         // Same parameters to take advantage of caching.
220
bi = getBeanInfo(beanClass);
221     } else {
222         bi = (new Introspector JavaDoc(beanClass, stopClass, flags)).getBeanInfo();
223     }
224     return bi;
225
226     // Old behaviour: Make an independent copy of the BeanInfo.
227
//return new GenericBeanInfo(bi);
228
}
229
230
231     /**
232      * Utility method to take a string and convert it to normal Java variable
233      * name capitalization. This normally means converting the first
234      * character from upper case to lower case, but in the (unusual) special
235      * case when there is more than one character and both the first and
236      * second characters are upper case, we leave it alone.
237      * <p>
238      * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
239      * as "URL".
240      *
241      * @param name The string to be decapitalized.
242      * @return The decapitalized version of the string.
243      */

244     public static String JavaDoc decapitalize(String JavaDoc name) {
245     if (name == null || name.length() == 0) {
246         return name;
247     }
248     if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
249             Character.isUpperCase(name.charAt(0))){
250         return name;
251     }
252     char chars[] = name.toCharArray();
253     chars[0] = Character.toLowerCase(chars[0]);
254     return new String JavaDoc(chars);
255     }
256
257     /**
258      * Gets the list of package names that will be used for
259      * finding BeanInfo classes.
260      *
261      * @return The array of package names that will be searched in
262      * order to find BeanInfo classes. The default value
263      * for this array is implementation-dependent; e.g.
264      * Sun implementation initially sets to {"sun.beans.infos"}.
265      */

266
267     public static synchronized String JavaDoc[] getBeanInfoSearchPath() {
268     // Return a copy of the searchPath.
269
String JavaDoc result[] = new String JavaDoc[searchPath.length];
270     for (int i = 0; i < searchPath.length; i++) {
271         result[i] = searchPath[i];
272     }
273     return result;
274     }
275
276     /**
277      * Change the list of package names that will be used for
278      * finding BeanInfo classes. The behaviour of
279      * this method is undefined if parameter path
280      * is null.
281      *
282      * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>
283      * method is called. This could result in a SecurityException.
284      *
285      * @param path Array of package names.
286      * @exception SecurityException if a security manager exists and its
287      * <code>checkPropertiesAccess</code> method doesn't allow setting
288      * of system properties.
289      * @see SecurityManager#checkPropertiesAccess
290      */

291
292     public static synchronized void setBeanInfoSearchPath(String JavaDoc path[]) {
293     SecurityManager JavaDoc sm = System.getSecurityManager();
294     if (sm != null) {
295         sm.checkPropertiesAccess();
296     }
297     searchPath = path;
298     }
299
300
301     /**
302      * Flush all of the Introspector's internal caches. This method is
303      * not normally required. It is normally only needed by advanced
304      * tools that update existing "Class" objects in-place and need
305      * to make the Introspector re-analyze existing Class objects.
306      */

307
308     public static void flushCaches() {
309     beanInfoCache.clear();
310     declaredMethodCache.clear();
311     }
312
313     /**
314      * Flush the Introspector's internal cached information for a given class.
315      * This method is not normally required. It is normally only needed
316      * by advanced tools that update existing "Class" objects in-place
317      * and need to make the Introspector re-analyze an existing Class object.
318      *
319      * Note that only the direct state associated with the target Class
320      * object is flushed. We do not flush state for other Class objects
321      * with the same name, nor do we flush state for any related Class
322      * objects (such as subclasses), even though their state may include
323      * information indirectly obtained from the target Class object.
324      *
325      * @param clz Class object to be flushed.
326      * @throws NullPointerException If the Class object is null.
327      */

328     public static void flushFromCaches(Class JavaDoc<?> clz) {
329     if (clz == null) {
330         throw new NullPointerException JavaDoc();
331     }
332     beanInfoCache.remove(clz);
333     declaredMethodCache.remove(clz);
334     }
335
336     //======================================================================
337
// Private implementation methods
338
//======================================================================
339

340     private Introspector(Class JavaDoc beanClass, Class JavaDoc stopClass, int flags)
341                         throws IntrospectionException JavaDoc {
342     this.beanClass = beanClass;
343
344     // Check stopClass is a superClass of startClass.
345
if (stopClass != null) {
346         boolean isSuper = false;
347         for (Class JavaDoc c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {
348             if (c == stopClass) {
349             isSuper = true;
350             }
351         }
352         if (!isSuper) {
353             throw new IntrospectionException JavaDoc(stopClass.getName() + " not superclass of " +
354                     beanClass.getName());
355         }
356     }
357
358         if (flags == USE_ALL_BEANINFO) {
359         explicitBeanInfo = findExplicitBeanInfo(beanClass);
360         }
361
362     Class JavaDoc superClass = beanClass.getSuperclass();
363     if (superClass != stopClass) {
364         int newFlags = flags;
365         if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
366         newFlags = USE_ALL_BEANINFO;
367         }
368         superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
369     }
370     if (explicitBeanInfo != null) {
371         additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
372     }
373     if (additionalBeanInfo == null) {
374         additionalBeanInfo = new BeanInfo JavaDoc[0];
375     }
376     }
377
378     /**
379      * Constructs a GenericBeanInfo class from the state of the Introspector
380      */

381     private BeanInfo JavaDoc getBeanInfo() throws IntrospectionException JavaDoc {
382
383     // the evaluation order here is import, as we evaluate the
384
// event sets and locate PropertyChangeListeners before we
385
// look for properties.
386
BeanDescriptor JavaDoc bd = getTargetBeanDescriptor();
387     MethodDescriptor JavaDoc mds[] = getTargetMethodInfo();
388     EventSetDescriptor JavaDoc esds[] = getTargetEventInfo();
389     PropertyDescriptor JavaDoc pds[] = getTargetPropertyInfo();
390
391     int defaultEvent = getTargetDefaultEventIndex();
392     int defaultProperty = getTargetDefaultPropertyIndex();
393
394         return new GenericBeanInfo(bd, esds, defaultEvent, pds,
395             defaultProperty, mds, explicitBeanInfo);
396     
397     }
398
399     /**
400      * Looks for an explicit BeanInfo class that corresponds to the Class.
401      * First it looks in the existing package that the Class is defined in,
402      * then it checks to see if the class is its own BeanInfo. Finally,
403      * the BeanInfo search path is prepended to the class and searched.
404      *
405      * @return Instance of an explicit BeanInfo class or null if one isn't found.
406      */

407     private static synchronized BeanInfo JavaDoc findExplicitBeanInfo(Class JavaDoc beanClass) {
408     String JavaDoc name = beanClass.getName() + BEANINFO_SUFFIX;
409         try {
410         return (java.beans.BeanInfo JavaDoc)instantiate(beanClass, name);
411     } catch (Exception JavaDoc ex) {
412         // Just drop through
413

414         }
415     // Now try checking if the bean is its own BeanInfo.
416
try {
417         if (isSubclass(beanClass, java.beans.BeanInfo JavaDoc.class)) {
418             return (java.beans.BeanInfo JavaDoc)beanClass.newInstance();
419         }
420     } catch (Exception JavaDoc ex) {
421         // Just drop through
422
}
423     // Now try looking for <searchPath>.fooBeanInfo
424
name = name.substring(name.lastIndexOf('.')+1);
425
426     for (int i = 0; i < searchPath.length; i++) {
427         // This optimization will only use the BeanInfo search path if is has changed
428
// from the original or trying to get the ComponentBeanInfo.
429
if (!DEFAULT_INFO_PATH.equals(searchPath[i]) ||
430         DEFAULT_INFO_PATH.equals(searchPath[i]) && "ComponentBeanInfo".equals(name)) {
431         try {
432             String JavaDoc fullName = searchPath[i] + "." + name;
433             java.beans.BeanInfo JavaDoc bi = (java.beans.BeanInfo JavaDoc)instantiate(beanClass, fullName);
434
435             // Make sure that the returned BeanInfo matches the class.
436
if (bi.getBeanDescriptor() != null) {
437             if (bi.getBeanDescriptor().getBeanClass() == beanClass) {
438                 return bi;
439             }
440             } else if (bi.getPropertyDescriptors() != null) {
441             PropertyDescriptor JavaDoc[] pds = bi.getPropertyDescriptors();
442             for (int j = 0; j < pds.length; j++) {
443                 Method JavaDoc method = pds[j].getReadMethod();
444                 if (method == null) {
445                 method = pds[j].getWriteMethod();
446                 }
447                 if (method != null && method.getDeclaringClass() == beanClass) {
448                 return bi;
449                 }
450             }
451             } else if (bi.getMethodDescriptors() != null) {
452             MethodDescriptor JavaDoc[] mds = bi.getMethodDescriptors();
453             for (int j = 0; j < mds.length; j++) {
454                 Method JavaDoc method = mds[j].getMethod();
455                 if (method != null && method.getDeclaringClass() == beanClass) {
456                 return bi;
457                 }
458             }
459             }
460         } catch (Exception JavaDoc ex) {
461             // Silently ignore any errors.
462
}
463         }
464     }
465     return null;
466     }
467
468     /**
469      * @return An array of PropertyDescriptors describing the editable
470      * properties supported by the target bean.
471      */

472
473     private PropertyDescriptor JavaDoc[] getTargetPropertyInfo() {
474
475     // Check if the bean has its own BeanInfo that will provide
476
// explicit information.
477
PropertyDescriptor JavaDoc[] explicitProperties = null;
478     if (explicitBeanInfo != null) {
479         explicitProperties = explicitBeanInfo.getPropertyDescriptors();
480         int ix = explicitBeanInfo.getDefaultPropertyIndex();
481         if (ix >= 0 && ix < explicitProperties.length) {
482         defaultPropertyName = explicitProperties[ix].getName();
483         }
484         }
485
486     if (explicitProperties == null && superBeanInfo != null) {
487         // We have no explicit BeanInfo properties. Check with our parent.
488
PropertyDescriptor JavaDoc supers[] = superBeanInfo.getPropertyDescriptors();
489         for (int i = 0 ; i < supers.length; i++) {
490         addPropertyDescriptor(supers[i]);
491         }
492         int ix = superBeanInfo.getDefaultPropertyIndex();
493         if (ix >= 0 && ix < supers.length) {
494         defaultPropertyName = supers[ix].getName();
495         }
496     }
497
498     for (int i = 0; i < additionalBeanInfo.length; i++) {
499         PropertyDescriptor JavaDoc additional[] = additionalBeanInfo[i].getPropertyDescriptors();
500         if (additional != null) {
501             for (int j = 0 ; j < additional.length; j++) {
502             addPropertyDescriptor(additional[j]);
503             }
504         }
505     }
506
507     if (explicitProperties != null) {
508         // Add the explicit BeanInfo data to our results.
509
for (int i = 0 ; i < explicitProperties.length; i++) {
510         addPropertyDescriptor(explicitProperties[i]);
511         }
512
513     } else {
514
515         // Apply some reflection to the current class.
516

517         // First get an array of all the public methods at this level
518
Method JavaDoc methodList[] = getPublicDeclaredMethods(beanClass);
519
520         // Now analyze each method.
521
for (int i = 0; i < methodList.length; i++) {
522             Method JavaDoc method = methodList[i];
523         if (method == null) {
524             continue;
525         }
526             // skip static methods.
527
int mods = method.getModifiers();
528         if (Modifier.isStatic(mods)) {
529             continue;
530         }
531             String JavaDoc name = method.getName();
532             Class JavaDoc argTypes[] = method.getParameterTypes();
533             Class JavaDoc resultType = method.getReturnType();
534         int argCount = argTypes.length;
535         PropertyDescriptor JavaDoc pd = null;
536
537         if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
538             // Optimization. Don't bother with invalid propertyNames.
539
continue;
540         }
541
542         try {
543
544                 if (argCount == 0) {
545                 if (name.startsWith(GET_PREFIX)) {
546                     // Simple getter
547
pd = new PropertyDescriptor JavaDoc(decapitalize(name.substring(3)),
548                         method, null);
549                     } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
550                     // Boolean getter
551
pd = new PropertyDescriptor JavaDoc(decapitalize(name.substring(2)),
552                         method, null);
553                 }
554                 } else if (argCount == 1) {
555                 if (argTypes[0] == int.class && name.startsWith(GET_PREFIX)) {
556                     pd = new IndexedPropertyDescriptor JavaDoc(
557                         decapitalize(name.substring(3)),
558                         null, null,
559                         method, null);
560                 } else if (resultType == void.class && name.startsWith(SET_PREFIX)) {
561                     // Simple setter
562
pd = new PropertyDescriptor JavaDoc(decapitalize(name.substring(3)),
563                         null, method);
564                     if (throwsException(method, PropertyVetoException JavaDoc.class)) {
565                     pd.setConstrained(true);
566                 }
567                 }
568                 } else if (argCount == 2) {
569                 if (argTypes[0] == int.class && name.startsWith(SET_PREFIX)) {
570                         pd = new IndexedPropertyDescriptor JavaDoc(
571                         decapitalize(name.substring(3)),
572                         null, null,
573                         null, method);
574                     if (throwsException(method, PropertyVetoException JavaDoc.class)) {
575                     pd.setConstrained(true);
576                 }
577             }
578             }
579         } catch (IntrospectionException JavaDoc ex) {
580             // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
581
// constructor fins that the method violates details of the deisgn
582
// pattern, e.g. by having an empty name, or a getter returning
583
// void , or whatever.
584
pd = null;
585         }
586
587         if (pd != null) {
588             // If this class or one of its base classes is a PropertyChange
589
// source, then we assume that any properties we discover are "bound".
590
if (propertyChangeSource) {
591             pd.setBound(true);
592             }
593             addPropertyDescriptor(pd);
594         }
595         }
596     }
597     processPropertyDescriptors();
598
599     // Allocate and populate the result array.
600
PropertyDescriptor JavaDoc result[] = new PropertyDescriptor JavaDoc[properties.size()];
601     result = (PropertyDescriptor JavaDoc[])properties.values().toArray(result);
602
603     // Set the default index.
604
if (defaultPropertyName != null) {
605         for (int i = 0; i < result.length; i++) {
606         if (defaultPropertyName.equals(result[i].getName())) {
607             defaultPropertyIndex = i;
608         }
609         }
610     }
611
612     return result;
613     }
614
615     private HashMap JavaDoc pdStore = new HashMap JavaDoc();
616
617     /**
618      * Adds the property descriptor to the list store.
619      */

620     private void addPropertyDescriptor(PropertyDescriptor JavaDoc pd) {
621     String JavaDoc propName = pd.getName();
622     List JavaDoc list = (List JavaDoc)pdStore.get(propName);
623     if (list == null) {
624         list = new ArrayList JavaDoc();
625         pdStore.put(propName, list);
626     }
627     list.add(pd);
628     }
629
630     /**
631      * Populates the property descriptor table by merging the
632      * lists of Property descriptors.
633      */

634     private void processPropertyDescriptors() {
635     if (properties == null) {
636         properties = new TreeMap JavaDoc();
637     }
638
639     List JavaDoc list;
640
641     PropertyDescriptor JavaDoc pd, gpd, spd;
642     IndexedPropertyDescriptor JavaDoc ipd, igpd, ispd;
643
644     Iterator JavaDoc it = pdStore.values().iterator();
645     while (it.hasNext()) {
646         pd = null; gpd = null; spd = null;
647         ipd = null; igpd = null; ispd = null;
648
649         list = (List JavaDoc)it.next();
650
651         // First pass. Find the latest getter method. Merge properties
652
// of previous getter methods.
653
for (int i = 0; i < list.size(); i++) {
654         pd = (PropertyDescriptor JavaDoc)list.get(i);
655         if (pd instanceof IndexedPropertyDescriptor JavaDoc) {
656             ipd = (IndexedPropertyDescriptor JavaDoc)pd;
657             if (ipd.getIndexedReadMethod() != null) {
658             if (igpd != null) {
659                 igpd = new IndexedPropertyDescriptor JavaDoc(igpd, ipd);
660             } else {
661                 igpd = ipd;
662             }
663             }
664         } else {
665             if (pd.getReadMethod() != null) {
666             if (gpd != null) {
667                 // Don't replace the existing read
668
// method if it starts with "is"
669
Method JavaDoc method = gpd.getReadMethod();
670                 if (!method.getName().startsWith(IS_PREFIX)) {
671                 gpd = new PropertyDescriptor JavaDoc(gpd, pd);
672                 }
673             } else {
674                 gpd = pd;
675             }
676             }
677         }
678         }
679
680         // Second pass. Find the latest setter method which
681
// has the same type as the getter method.
682
for (int i = 0; i < list.size(); i++) {
683         pd = (PropertyDescriptor JavaDoc)list.get(i);
684         if (pd instanceof IndexedPropertyDescriptor JavaDoc) {
685             ipd = (IndexedPropertyDescriptor JavaDoc)pd;
686             if (ipd.getIndexedWriteMethod() != null) {
687             if (igpd != null) {
688                 if (igpd.getIndexedPropertyType()
689                 == ipd.getIndexedPropertyType()) {
690                 if (ispd != null) {
691                     ispd = new IndexedPropertyDescriptor JavaDoc(ispd, ipd);
692                 } else {
693                     ispd = ipd;
694                 }
695                 }
696             } else {
697                 if (ispd != null) {
698                 ispd = new IndexedPropertyDescriptor JavaDoc(ispd, ipd);
699                 } else {
700                 ispd = ipd;
701                 }
702             }
703             }
704         } else {
705             if (pd.getWriteMethod() != null) {
706             if (gpd != null) {
707                 if (gpd.getPropertyType() == pd.getPropertyType()) {
708                 if (spd != null) {
709                     spd = new PropertyDescriptor JavaDoc(spd, pd);
710                 } else {
711                     spd = pd;
712                 }
713                 }
714             } else {
715                 if (spd != null) {
716                 spd = new PropertyDescriptor JavaDoc(spd, pd);
717                 } else {
718                 spd = pd;
719                 }
720             }
721             }
722         }
723         }
724
725         // At this stage we should have either PDs or IPDs for the
726
// representative getters and setters. The order at which the
727
// property descriptors are determined represent the
728
// precedence of the property ordering.
729
pd = null; ipd = null;
730
731         if (igpd != null && ispd != null) {
732         // Complete indexed properties set
733
// Merge any classic property descriptors
734
if (gpd != null) {
735             PropertyDescriptor JavaDoc tpd = mergePropertyDescriptor(igpd, gpd);
736             if (tpd instanceof IndexedPropertyDescriptor JavaDoc) {
737             igpd = (IndexedPropertyDescriptor JavaDoc)tpd;
738             }
739         }
740         if (spd != null) {
741             PropertyDescriptor JavaDoc tpd = mergePropertyDescriptor(ispd, spd);
742             if (tpd instanceof IndexedPropertyDescriptor JavaDoc) {
743             ispd = (IndexedPropertyDescriptor JavaDoc)tpd;
744             }
745         }
746         if (igpd == ispd) {
747             pd = igpd;
748         } else {
749             pd = mergePropertyDescriptor(igpd, ispd);
750         }
751         } else if (gpd != null && spd != null) {
752         // Complete simple properties set
753
if (gpd == spd) {
754             pd = gpd;
755         } else {
756             pd = mergePropertyDescriptor(gpd, spd);
757         }
758         } else if (ispd != null) {
759         // indexed setter
760
pd = ispd;
761         // Merge any classic property descriptors
762
if (spd != null) {
763             pd = mergePropertyDescriptor(ispd, spd);
764         }
765         if (gpd != null) {
766             pd = mergePropertyDescriptor(ispd, gpd);
767         }
768         } else if (igpd != null) {
769         // indexed getter
770
pd = igpd;
771         // Merge any classic property descriptors
772
if (gpd != null) {
773             pd = mergePropertyDescriptor(igpd, gpd);
774         }
775         if (spd != null) {
776             pd = mergePropertyDescriptor(igpd, spd);
777         }
778         } else if (spd != null) {
779         // simple setter
780
pd = spd;
781         } else if (gpd != null) {
782         // simple getter
783
pd = gpd;
784         }
785
786         // Very special case to ensure that an IndexedPropertyDescriptor
787
// doesn't contain less information than the enclosed
788
// PropertyDescriptor. If it does, then recreate as a
789
// PropertyDescriptor. See 4168833
790
if (pd instanceof IndexedPropertyDescriptor JavaDoc) {
791         ipd = (IndexedPropertyDescriptor JavaDoc)pd;
792         if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {
793             pd = new PropertyDescriptor JavaDoc(ipd);
794         }
795         }
796
797         if (pd != null) {
798         properties.put(pd.getName(), pd);
799         }
800     }
801     }
802     
803     /**
804      * Adds the property descriptor to the indexedproperty descriptor only if the
805      * types are the same.
806      *
807      * The most specific property descriptor will take precedence.
808      */

809     private PropertyDescriptor JavaDoc mergePropertyDescriptor(IndexedPropertyDescriptor JavaDoc ipd,
810                                PropertyDescriptor JavaDoc pd) {
811     PropertyDescriptor JavaDoc result = null;
812
813     Class JavaDoc propType = pd.getPropertyType();
814     Class JavaDoc ipropType = ipd.getIndexedPropertyType();
815
816     if (propType.isArray() && propType.getComponentType() == ipropType) {
817         if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
818         result = new IndexedPropertyDescriptor JavaDoc(pd, ipd);
819         } else {
820         result = new IndexedPropertyDescriptor JavaDoc(ipd, pd);
821         }
822     } else {
823         // Cannot merge the pd because of type mismatch
824
// Return the most specific pd
825
if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
826         result = ipd;
827         } else {
828         result = pd;
829         // Try to add methods which may have been lost in the type change
830
// See 4168833
831
Method JavaDoc write = result.getWriteMethod();
832         Method JavaDoc read = result.getReadMethod();
833                         
834         if (read == null && write != null) {
835             read = findMethod(result.getClass0(),
836                       "get" + result.capitalize(result.getName()), 0);
837             if (read != null) {
838             try {
839                 result.setReadMethod(read);
840             } catch (IntrospectionException JavaDoc ex) {
841                 // no consequences for failure.
842
}
843             }
844         }
845         if (write == null && read != null) {
846             write = findMethod(result.getClass0(),
847                        "set" + result.capitalize(result.getName()), 1,
848                        new Class JavaDoc[] { read.getReturnType() });
849             if (write != null) {
850             try {
851                 result.setWriteMethod(write);
852             } catch (IntrospectionException JavaDoc ex) {
853                 // no consequences for failure.
854
}
855             }
856         }
857         }
858     }
859     return result;
860     }
861
862     // Handle regular pd merge
863
private PropertyDescriptor JavaDoc mergePropertyDescriptor(PropertyDescriptor JavaDoc pd1,
864                                PropertyDescriptor JavaDoc pd2) {
865     if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
866         return new PropertyDescriptor JavaDoc(pd1, pd2);
867     } else {
868         return new PropertyDescriptor JavaDoc(pd2, pd1);
869     }
870     }
871
872     // Handle regular ipd merge
873
private PropertyDescriptor JavaDoc mergePropertyDescriptor(IndexedPropertyDescriptor JavaDoc ipd1,
874                                IndexedPropertyDescriptor JavaDoc ipd2) {
875     if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
876         return new IndexedPropertyDescriptor JavaDoc(ipd1, ipd2);
877     } else {
878         return new IndexedPropertyDescriptor JavaDoc(ipd2, ipd1);
879     }
880     }
881
882     /**
883      * @return An array of EventSetDescriptors describing the kinds of
884      * events fired by the target bean.
885      */

886     private EventSetDescriptor JavaDoc[] getTargetEventInfo() throws IntrospectionException JavaDoc {
887     if (events == null) {
888         events = new HashMap JavaDoc();
889     }
890
891     // Check if the bean has its own BeanInfo that will provide
892
// explicit information.
893
EventSetDescriptor JavaDoc[] explicitEvents = null;
894     if (explicitBeanInfo != null) {
895         explicitEvents = explicitBeanInfo.getEventSetDescriptors();
896         int ix = explicitBeanInfo.getDefaultEventIndex();
897         if (ix >= 0 && ix < explicitEvents.length) {
898         defaultEventName = explicitEvents[ix].getName();
899         }
900     }
901
902     if (explicitEvents == null && superBeanInfo != null) {
903         // We have no explicit BeanInfo events. Check with our parent.
904
EventSetDescriptor JavaDoc supers[] = superBeanInfo.getEventSetDescriptors();
905         for (int i = 0 ; i < supers.length; i++) {
906         addEvent(supers[i]);
907         }
908         int ix = superBeanInfo.getDefaultEventIndex();
909         if (ix >= 0 && ix < supers.length) {
910         defaultEventName = supers[ix].getName();
911         }
912     }
913
914     for (int i = 0; i < additionalBeanInfo.length; i++) {
915         EventSetDescriptor JavaDoc additional[] = additionalBeanInfo[i].getEventSetDescriptors();
916         if (additional != null) {
917             for (int j = 0 ; j < additional.length; j++) {
918             addEvent(additional[j]);
919             }
920         }
921     }
922
923     if (explicitEvents != null) {
924         // Add the explicit explicitBeanInfo data to our results.
925
for (int i = 0 ; i < explicitEvents.length; i++) {
926         addEvent(explicitEvents[i]);
927         }
928
929     } else {
930
931         // Apply some reflection to the current class.
932

933         // Get an array of all the public beans methods at this level
934
Method JavaDoc methodList[] = getPublicDeclaredMethods(beanClass);
935
936         // Find all suitable "add", "remove" and "get" Listener methods
937
// The name of the listener type is the key for these hashtables
938
// i.e, ActionListener
939
Map JavaDoc adds = null;
940         Map JavaDoc removes = null;
941         Map JavaDoc gets = null;
942
943         for (int i = 0; i < methodList.length; i++) {
944             Method JavaDoc method = methodList[i];
945         if (method == null) {
946             continue;
947         }
948             // skip static methods.
949
int mods = method.getModifiers();
950         if (Modifier.isStatic(mods)) {
951             continue;
952         }
953             String JavaDoc name = method.getName();
954         // Optimization avoid getParameterTypes
955
if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)
956             && !name.startsWith(GET_PREFIX)) {
957             continue;
958         }
959
960             Class JavaDoc argTypes[] = method.getParameterTypes();
961             Class JavaDoc resultType = method.getReturnType();
962
963             if (name.startsWith(ADD_PREFIX) && argTypes.length == 1 &&
964             resultType == Void.TYPE &&
965             Introspector.isSubclass(argTypes[0], eventListenerType)) {
966             String JavaDoc listenerName = name.substring(3);
967             if (listenerName.length() > 0 &&
968             argTypes[0].getName().endsWith(listenerName)) {
969             if (adds == null) {
970                 adds = new HashMap JavaDoc();
971             }
972             adds.put(listenerName, method);
973             }
974         }
975         else if (name.startsWith(REMOVE_PREFIX) && argTypes.length == 1 &&
976              resultType == Void.TYPE &&
977              Introspector.isSubclass(argTypes[0], eventListenerType)) {
978             String JavaDoc listenerName = name.substring(6);
979             if (listenerName.length() > 0 &&
980             argTypes[0].getName().endsWith(listenerName)) {
981             if (removes == null) {
982                 removes = new HashMap JavaDoc();
983             }
984             removes.put(listenerName, method);
985             }
986             }
987         else if (name.startsWith(GET_PREFIX) && argTypes.length == 0 &&
988              resultType.isArray() &&
989              Introspector.isSubclass(resultType.getComponentType(),
990                          eventListenerType)) {
991             String JavaDoc listenerName = name.substring(3, name.length() - 1);
992             if (listenerName.length() > 0 &&
993             resultType.getComponentType().getName().endsWith(listenerName)) {
994             if (gets == null) {
995                 gets = new HashMap JavaDoc();
996             }
997             gets.put(listenerName, method);
998             }
999         }
1000        }
1001
1002        if (adds != null && removes != null) {
1003        // Now look for matching addFooListener+removeFooListener pairs.
1004
// Bonus if there is a matching getFooListeners method as well.
1005
Iterator JavaDoc keys = adds.keySet().iterator();
1006        while (keys.hasNext()) {
1007            String JavaDoc listenerName = (String JavaDoc) keys.next();
1008            // Skip any "add" which doesn't have a matching "remove" or
1009
// a listener name that doesn't end with Listener
1010
if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
1011            continue;
1012            }
1013            String JavaDoc eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
1014            Method JavaDoc addMethod = (Method JavaDoc)adds.get(listenerName);
1015            Method JavaDoc removeMethod = (Method JavaDoc)removes.get(listenerName);
1016            Method JavaDoc getMethod = null;
1017            if (gets != null) {
1018            getMethod = (Method JavaDoc)gets.get(listenerName);
1019            }
1020            Class JavaDoc argType = addMethod.getParameterTypes()[0];
1021            
1022            // generate a list of Method objects for each of the target methods:
1023
Method JavaDoc allMethods[] = getPublicDeclaredMethods(argType);
1024            List JavaDoc validMethods = new ArrayList JavaDoc(allMethods.length);
1025            for (int i = 0; i < allMethods.length; i++) {
1026            if (allMethods[i] == null) {
1027                continue;
1028            }
1029            
1030            if (isEventHandler(allMethods[i])) {
1031                validMethods.add(allMethods[i]);
1032            }
1033            }
1034            Method JavaDoc[] methods = (Method JavaDoc[])validMethods.toArray(new Method JavaDoc[validMethods.size()]);
1035
1036            EventSetDescriptor JavaDoc esd = new EventSetDescriptor JavaDoc(eventName, argType,
1037                                    methods, addMethod,
1038                                    removeMethod,
1039                                    getMethod);
1040
1041            // If the adder method throws the TooManyListenersException then it
1042
// is a Unicast event source.
1043
if (throwsException(addMethod,
1044                    java.util.TooManyListenersException JavaDoc.class)) {
1045            esd.setUnicast(true);
1046            }
1047            addEvent(esd);
1048        }
1049        } // if (adds != null ...
1050
}
1051    EventSetDescriptor JavaDoc[] result;
1052    if (events.size() == 0) {
1053        result = EMPTY_EVENTSETDESCRIPTORS;
1054    } else {
1055        // Allocate and populate the result array.
1056
result = new EventSetDescriptor JavaDoc[events.size()];
1057        result = (EventSetDescriptor JavaDoc[])events.values().toArray(result);
1058
1059        // Set the default index.
1060
if (defaultEventName != null) {
1061        for (int i = 0; i < result.length; i++) {
1062            if (defaultEventName.equals(result[i].getName())) {
1063            defaultEventIndex = i;
1064            }
1065        }
1066        }
1067    }
1068    return result;
1069    }
1070
1071    private void addEvent(EventSetDescriptor JavaDoc esd) {
1072    String JavaDoc key = esd.getName();
1073    if (esd.getName().equals("propertyChange")) {
1074        propertyChangeSource = true;
1075    }
1076    EventSetDescriptor JavaDoc old = (EventSetDescriptor JavaDoc)events.get(key);
1077    if (old == null) {
1078        events.put(key, esd);
1079        return;
1080    }
1081    EventSetDescriptor JavaDoc composite = new EventSetDescriptor JavaDoc(old, esd);
1082    events.put(key, composite);
1083    }
1084
1085    /**
1086     * @return An array of MethodDescriptors describing the private
1087     * methods supported by the target bean.
1088     */

1089    private MethodDescriptor JavaDoc[] getTargetMethodInfo() {
1090    if (methods == null) {
1091        methods = new HashMap JavaDoc(100);
1092    }
1093
1094    // Check if the bean has its own BeanInfo that will provide
1095
// explicit information.
1096
MethodDescriptor JavaDoc[] explicitMethods = null;
1097    if (explicitBeanInfo != null) {
1098        explicitMethods = explicitBeanInfo.getMethodDescriptors();
1099    }
1100
1101    if (explicitMethods == null && superBeanInfo != null) {
1102        // We have no explicit BeanInfo methods. Check with our parent.
1103
MethodDescriptor JavaDoc supers[] = superBeanInfo.getMethodDescriptors();
1104        for (int i = 0 ; i < supers.length; i++) {
1105        addMethod(supers[i]);
1106        }
1107    }
1108
1109    for (int i = 0; i < additionalBeanInfo.length; i++) {
1110        MethodDescriptor JavaDoc additional[] = additionalBeanInfo[i].getMethodDescriptors();
1111        if (additional != null) {
1112            for (int j = 0 ; j < additional.length; j++) {
1113            addMethod(additional[j]);
1114            }
1115        }
1116    }
1117
1118    if (explicitMethods != null) {
1119        // Add the explicit explicitBeanInfo data to our results.
1120
for (int i = 0 ; i < explicitMethods.length; i++) {
1121        addMethod(explicitMethods[i]);
1122        }
1123
1124    } else {
1125
1126        // Apply some reflection to the current class.
1127

1128        // First get an array of all the beans methods at this level
1129
Method JavaDoc methodList[] = getPublicDeclaredMethods(beanClass);
1130
1131        // Now analyze each method.
1132
for (int i = 0; i < methodList.length; i++) {
1133            Method JavaDoc method = methodList[i];
1134        if (method == null) {
1135            continue;
1136        }
1137        MethodDescriptor JavaDoc md = new MethodDescriptor JavaDoc(method);
1138        addMethod(md);
1139        }
1140    }
1141
1142    // Allocate and populate the result array.
1143
MethodDescriptor JavaDoc result[] = new MethodDescriptor JavaDoc[methods.size()];
1144    result = (MethodDescriptor JavaDoc[])methods.values().toArray(result);
1145
1146    return result;
1147    }
1148
1149    private void addMethod(MethodDescriptor JavaDoc md) {
1150    // We have to be careful here to distinguish method by both name
1151
// and argument lists.
1152
// This method gets called a *lot, so we try to be efficient.
1153
String JavaDoc name = md.getName();
1154
1155    MethodDescriptor JavaDoc old = (MethodDescriptor JavaDoc)methods.get(name);
1156    if (old == null) {
1157        // This is the common case.
1158
methods.put(name, md);
1159        return;
1160    }
1161
1162    // We have a collision on method names. This is rare.
1163

1164    // Check if old and md have the same type.
1165
String JavaDoc[] p1 = md.getParamNames();
1166    String JavaDoc[] p2 = old.getParamNames();
1167
1168    boolean match = false;
1169    if (p1.length == p2.length) {
1170        match = true;
1171        for (int i = 0; i < p1.length; i++) {
1172        if (p1[i] != p2[i]) {
1173            match = false;
1174            break;
1175        }
1176        }
1177    }
1178    if (match) {
1179        MethodDescriptor JavaDoc composite = new MethodDescriptor JavaDoc(old, md);
1180        methods.put(name, composite);
1181        return;
1182    }
1183
1184    // We have a collision on method names with different type signatures.
1185
// This is very rare.
1186

1187    String JavaDoc longKey = makeQualifiedMethodName(name, p1);
1188    old = (MethodDescriptor JavaDoc)methods.get(longKey);
1189    if (old == null) {
1190        methods.put(longKey, md);
1191        return;
1192    }
1193    MethodDescriptor JavaDoc composite = new MethodDescriptor JavaDoc(old, md);
1194    methods.put(longKey, composite);
1195    }
1196
1197    /**
1198     * Creates a key for a method in a method cache.
1199     */

1200    private static String JavaDoc makeQualifiedMethodName(String JavaDoc name, String JavaDoc[] params) {
1201    StringBuffer JavaDoc sb = new StringBuffer JavaDoc(name);
1202    sb.append('=');
1203    for (int i = 0; i < params.length; i++) {
1204        sb.append(':');
1205        sb.append(params[i]);
1206    }
1207    return sb.toString();
1208    }
1209
1210    private int getTargetDefaultEventIndex() {
1211    return defaultEventIndex;
1212    }
1213
1214    private int getTargetDefaultPropertyIndex() {
1215    return defaultPropertyIndex;
1216    }
1217
1218    private BeanDescriptor JavaDoc getTargetBeanDescriptor() {
1219    // Use explicit info, if available,
1220
if (explicitBeanInfo != null) {
1221        BeanDescriptor JavaDoc bd = explicitBeanInfo.getBeanDescriptor();
1222        if (bd != null) {
1223        return (bd);
1224        }
1225    }
1226    // OK, fabricate a default BeanDescriptor.
1227
return (new BeanDescriptor JavaDoc(beanClass));
1228    }
1229
1230    private boolean isEventHandler(Method JavaDoc m) {
1231    // We assume that a method is an event handler if it has a single
1232
// argument, whose type inherit from java.util.Event.
1233
Class JavaDoc argTypes[] = m.getParameterTypes();
1234    if (argTypes.length != 1) {
1235        return false;
1236    }
1237    if (isSubclass(argTypes[0], java.util.EventObject JavaDoc.class)) {
1238        return true;
1239    }
1240    return false;
1241    }
1242
1243    /*
1244     * Internal method to return *public* methods within a class.
1245     */

1246    private static synchronized Method JavaDoc[] getPublicDeclaredMethods(Class JavaDoc clz) {
1247    // Looking up Class.getDeclaredMethods is relatively expensive,
1248
// so we cache the results.
1249
Method JavaDoc[] result = null;
1250    if (!ReflectUtil.isPackageAccessible(clz)) {
1251        return new Method JavaDoc[0];
1252    }
1253    final Class JavaDoc fclz = clz;
1254    Reference JavaDoc ref = (Reference JavaDoc)declaredMethodCache.get(fclz);
1255    if (ref != null) {
1256        result = (Method JavaDoc[])ref.get();
1257        if (result != null) {
1258        return result;
1259        }
1260    }
1261
1262    // We have to raise privilege for getDeclaredMethods
1263
result = (Method JavaDoc[]) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
1264        public Object JavaDoc run() {
1265            return fclz.getDeclaredMethods();
1266        }
1267        });
1268
1269
1270    // Null out any non-public methods.
1271
for (int i = 0; i < result.length; i++) {
1272        Method JavaDoc method = result[i];
1273        int mods = method.getModifiers();
1274        if (!Modifier.isPublic(mods)) {
1275        result[i] = null;
1276        }
1277        }
1278    // Add it to the cache.
1279
declaredMethodCache.put(fclz, new SoftReference JavaDoc(result));
1280    return result;
1281    }
1282
1283    //======================================================================
1284
// Package private support methods.
1285
//======================================================================
1286

1287    /**
1288     * Internal support for finding a target methodName with a given
1289     * parameter list on a given class.
1290     */

1291    private static Method JavaDoc internalFindMethod(Class JavaDoc start, String JavaDoc methodName,
1292                                                 int argCount, Class JavaDoc args[]) {
1293        // For overriden methods we need to find the most derived version.
1294
// So we start with the given class and walk up the superclass chain.
1295

1296    Method JavaDoc method = null;
1297
1298        for (Class JavaDoc cl = start; cl != null; cl = cl.getSuperclass()) {
1299            Method JavaDoc methods[] = getPublicDeclaredMethods(cl);
1300            for (int i = 0; i < methods.length; i++) {
1301                method = methods[i];
1302                if (method == null) {
1303                    continue;
1304                }
1305
1306                // make sure method signature matches.
1307
Class JavaDoc params[] = method.getParameterTypes();
1308                if (method.getName().equals(methodName) &&
1309                    params.length == argCount) {
1310            if (args != null) {
1311            boolean different = false;
1312            if (argCount > 0) {
1313                for (int j = 0; j < argCount; j++) {
1314                if (params[j] != args[j]) {
1315                    different = true;
1316                    continue;
1317                }
1318                }
1319                if (different) {
1320                continue;
1321                }
1322            }
1323            }
1324                    return method;
1325                }
1326            }
1327        }
1328    method = null;
1329
1330        // Now check any inherited interfaces. This is necessary both when
1331
// the argument class is itself an interface, and when the argument
1332
// class is an abstract class.
1333
Class JavaDoc ifcs[] = start.getInterfaces();
1334        for (int i = 0 ; i < ifcs.length; i++) {
1335        // Note: The original implementation had both methods calling
1336
// the 3 arg method. This is preserved but perhaps it should
1337
// pass the args array instead of null.
1338
method = internalFindMethod(ifcs[i], methodName, argCount, null);
1339        if (method != null) {
1340        break;
1341        }
1342        }
1343        return method;
1344    }
1345
1346    /**
1347     * Find a target methodName on a given class.
1348     */

1349    static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount) {
1350    return findMethod(cls, methodName, argCount, null);
1351    }
1352
1353    /**
1354     * Find a target methodName with specific parameter list on a given class.
1355     * <p>
1356     * Used in the contructors of the EventSetDescriptor,
1357     * PropertyDescriptor and the IndexedPropertyDescriptor.
1358     * <p>
1359     * @param cls The Class object on which to retrieve the method.
1360     * @param methodName Name of the method.
1361     * @param argCount Number of arguments for the desired method.
1362     * @param args Array of argument types for the method.
1363     * @return the method or null if not found
1364     */

1365    static Method JavaDoc findMethod(Class JavaDoc cls, String JavaDoc methodName, int argCount,
1366                             Class JavaDoc args[]) {
1367        if (methodName == null) {
1368            return null;
1369        }
1370        return internalFindMethod(cls, methodName, argCount, args);
1371    }
1372
1373    /**
1374     * Return true if class a is either equivalent to class b, or
1375     * if class a is a subclass of class b, i.e. if a either "extends"
1376     * or "implements" b.
1377     * Note tht either or both "Class" objects may represent interfaces.
1378     */

1379    static boolean isSubclass(Class JavaDoc a, Class JavaDoc b) {
1380    // We rely on the fact that for any given java class or
1381
// primtitive type there is a unqiue Class object, so
1382
// we can use object equivalence in the comparisons.
1383
if (a == b) {
1384        return true;
1385    }
1386    if (a == null || b == null) {
1387        return false;
1388    }
1389    for (Class JavaDoc x = a; x != null; x = x.getSuperclass()) {
1390        if (x == b) {
1391        return true;
1392        }
1393        if (b.isInterface()) {
1394        Class JavaDoc interfaces[] = x.getInterfaces();
1395        for (int i = 0; i < interfaces.length; i++) {
1396            if (isSubclass(interfaces[i], b)) {
1397            return true;
1398            }
1399        }
1400        }
1401    }
1402    return false;
1403    }
1404
1405    /**
1406     * Return true iff the given method throws the given exception.
1407     */

1408    private boolean throwsException(Method JavaDoc method, Class JavaDoc exception) {
1409    Class JavaDoc exs[] = method.getExceptionTypes();
1410    for (int i = 0; i < exs.length; i++) {
1411        if (exs[i] == exception) {
1412        return true;
1413        }
1414    }
1415    return false;
1416    }
1417
1418
1419    /**
1420     * Try to create an instance of a named class.
1421     * First try the classloader of "sibling", then try the system
1422     * classloader then the class loader of the current Thread.
1423     */

1424    static Object JavaDoc instantiate(Class JavaDoc sibling, String JavaDoc className)
1425         throws InstantiationException JavaDoc, IllegalAccessException JavaDoc,
1426                        ClassNotFoundException JavaDoc {
1427    // First check with sibling's classloader (if any).
1428
ClassLoader JavaDoc cl = sibling.getClassLoader();
1429    if (cl != null) {
1430        try {
1431            Class JavaDoc cls = cl.loadClass(className);
1432        return cls.newInstance();
1433        } catch (Exception JavaDoc ex) {
1434            // Just drop through and try the system classloader.
1435
}
1436        }
1437
1438    // Now try the system classloader.
1439
try {
1440        cl = ClassLoader.getSystemClassLoader();
1441        if (cl != null) {
1442            Class JavaDoc cls = cl.loadClass(className);
1443        return cls.newInstance();
1444        }
1445        } catch (Exception JavaDoc ex) {
1446        // We're not allowed to access the system class loader or
1447
// the class creation failed.
1448
// Drop through.
1449
}
1450
1451    // Use the classloader from the current Thread.
1452
cl = Thread.currentThread().getContextClassLoader();
1453    Class JavaDoc cls = cl.loadClass(className);
1454    return cls.newInstance();
1455    }
1456
1457} // end class Introspector
1458

1459//===========================================================================
1460

1461/**
1462 * Package private implementation support class for Introspector's
1463 * internal use.
1464 * <p>
1465 * Mostly this is used as a placeholder for the descriptors.
1466 */

1467
1468class GenericBeanInfo extends SimpleBeanInfo JavaDoc {
1469
1470    private BeanDescriptor JavaDoc beanDescriptor;
1471    private EventSetDescriptor JavaDoc[] events;
1472    private int defaultEvent;
1473    private PropertyDescriptor JavaDoc[] properties;
1474    private int defaultProperty;
1475    private MethodDescriptor JavaDoc[] methods;
1476    private BeanInfo JavaDoc targetBeanInfo;
1477
1478    public GenericBeanInfo(BeanDescriptor JavaDoc beanDescriptor,
1479        EventSetDescriptor JavaDoc[] events, int defaultEvent,
1480        PropertyDescriptor JavaDoc[] properties, int defaultProperty,
1481        MethodDescriptor JavaDoc[] methods, BeanInfo JavaDoc targetBeanInfo) {
1482    this.beanDescriptor = beanDescriptor;
1483    this.events = events;
1484    this.defaultEvent = defaultEvent;
1485    this.properties = properties;
1486    this.defaultProperty = defaultProperty;
1487    this.methods = methods;
1488    this.targetBeanInfo = targetBeanInfo;
1489    }
1490
1491    /**
1492     * Package-private dup constructor
1493     * This must isolate the new object from any changes to the old object.
1494     */

1495    GenericBeanInfo(GenericBeanInfo old) {
1496
1497    beanDescriptor = new BeanDescriptor JavaDoc(old.beanDescriptor);
1498    if (old.events != null) {
1499        int len = old.events.length;
1500        events = new EventSetDescriptor JavaDoc[len];
1501        for (int i = 0; i < len; i++) {
1502        events[i] = new EventSetDescriptor JavaDoc(old.events[i]);
1503        }
1504    }
1505    defaultEvent = old.defaultEvent;
1506    if (old.properties != null) {
1507        int len = old.properties.length;
1508        properties = new PropertyDescriptor JavaDoc[len];
1509        for (int i = 0; i < len; i++) {
1510        PropertyDescriptor JavaDoc oldp = old.properties[i];
1511        if (oldp instanceof IndexedPropertyDescriptor JavaDoc) {
1512            properties[i] = new IndexedPropertyDescriptor JavaDoc(
1513                    (IndexedPropertyDescriptor JavaDoc) oldp);
1514        } else {
1515            properties[i] = new PropertyDescriptor JavaDoc(oldp);
1516        }
1517        }
1518    }
1519    defaultProperty = old.defaultProperty;
1520    if (old.methods != null) {
1521        int len = old.methods.length;
1522        methods = new MethodDescriptor JavaDoc[len];
1523        for (int i = 0; i < len; i++) {
1524        methods[i] = new MethodDescriptor JavaDoc(old.methods[i]);
1525        }
1526    }
1527    targetBeanInfo = old.targetBeanInfo;
1528    }
1529
1530    public PropertyDescriptor JavaDoc[] getPropertyDescriptors() {
1531    return properties;
1532    }
1533
1534    public int getDefaultPropertyIndex() {
1535    return defaultProperty;
1536    }
1537
1538    public EventSetDescriptor JavaDoc[] getEventSetDescriptors() {
1539    return events;
1540    }
1541
1542    public int getDefaultEventIndex() {
1543    return defaultEvent;
1544    }
1545
1546    public MethodDescriptor JavaDoc[] getMethodDescriptors() {
1547    return methods;
1548    }
1549
1550    public BeanDescriptor JavaDoc getBeanDescriptor() {
1551    return beanDescriptor;
1552    }
1553
1554    public java.awt.Image JavaDoc getIcon(int iconKind) {
1555    if (targetBeanInfo != null) {
1556        return targetBeanInfo.getIcon(iconKind);
1557    }
1558    return super.getIcon(iconKind);
1559    }
1560}
1561
Popular Tags