Source Code Cross Referenced for Introspector.java in  » 6.0-JDK-Core » beans » java » beans » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » beans » java.beans 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025
0026        package java.beans;
0027
0028        import com.sun.beans.finder.ClassFinder;
0029
0030        import java.lang.ref.Reference;
0031        import java.lang.ref.SoftReference;
0032
0033        import java.lang.reflect.Method;
0034        import java.lang.reflect.Modifier;
0035
0036        import java.security.AccessController;
0037        import java.security.PrivilegedAction;
0038
0039        import java.util.Collections;
0040        import java.util.Map;
0041        import java.util.ArrayList;
0042        import java.util.HashMap;
0043        import java.util.Iterator;
0044        import java.util.EventListener;
0045        import java.util.List;
0046        import java.util.WeakHashMap;
0047        import java.util.TreeMap;
0048        import sun.reflect.misc.ReflectUtil;
0049
0050        /**
0051         * The Introspector class provides a standard way for tools to learn about
0052         * the properties, events, and methods supported by a target Java Bean.
0053         * <p>
0054         * For each of those three kinds of information, the Introspector will
0055         * separately analyze the bean's class and superclasses looking for
0056         * either explicit or implicit information and use that information to
0057         * build a BeanInfo object that comprehensively describes the target bean.
0058         * <p>
0059         * For each class "Foo", explicit information may be available if there exists
0060         * a corresponding "FooBeanInfo" class that provides a non-null value when
0061         * queried for the information.   We first look for the BeanInfo class by
0062         * taking the full package-qualified name of the target bean class and
0063         * appending "BeanInfo" to form a new class name.  If this fails, then
0064         * we take the final classname component of this name, and look for that
0065         * class in each of the packages specified in the BeanInfo package search
0066         * path.
0067         * <p>
0068         * Thus for a class such as "sun.xyz.OurButton" we would first look for a
0069         * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd
0070         * look in each package in the BeanInfo search path for an OurButtonBeanInfo
0071         * class.  With the default search path, this would mean looking for
0072         * "sun.beans.infos.OurButtonBeanInfo".
0073         * <p>
0074         * If a class provides explicit BeanInfo about itself then we add that to
0075         * the BeanInfo information we obtained from analyzing any derived classes,
0076         * but we regard the explicit information as being definitive for the current
0077         * class and its base classes, and do not proceed any further up the superclass
0078         * chain.
0079         * <p>
0080         * If we don't find explicit BeanInfo on a class, we use low-level
0081         * reflection to study the methods of the class and apply standard design
0082         * patterns to identify property accessors, event sources, or public
0083         * methods.  We then proceed to analyze the class's superclass and add
0084         * in the information from it (and possibly on up the superclass chain).
0085         *
0086         * <p>
0087         * Because the Introspector caches BeanInfo classes for better performance, 
0088         * take care if you use it in an application that uses
0089         * multiple class loaders.
0090         * In general, when you destroy a <code>ClassLoader</code>
0091         * that has been used to introspect classes,
0092         * you should use the
0093         * {@link #flushCaches <code>Introspector.flushCaches</code>} 
0094         * or 
0095         * {@link #flushFromCaches <code>Introspector.flushFromCaches</code>} method
0096         * to flush all of the introspected classes out of the cache.
0097         *
0098         * <P>
0099         * For more information about introspection and design patterns, please 
0100         * consult the 
0101         *  <a href="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans specification</a>.
0102         */
0103
0104        public class Introspector {
0105
0106            // Flags that can be used to control getBeanInfo:
0107            public final static int USE_ALL_BEANINFO = 1;
0108            public final static int IGNORE_IMMEDIATE_BEANINFO = 2;
0109            public final static int IGNORE_ALL_BEANINFO = 3;
0110
0111            // Static Caches to speed up introspection.
0112            private static Map declaredMethodCache = Collections
0113                    .synchronizedMap(new WeakHashMap());
0114            private static Map beanInfoCache = Collections
0115                    .synchronizedMap(new WeakHashMap());
0116
0117            private Class beanClass;
0118            private BeanInfo explicitBeanInfo;
0119            private BeanInfo super BeanInfo;
0120            private BeanInfo additionalBeanInfo[];
0121
0122            private boolean propertyChangeSource = false;
0123            private static Class eventListenerType = EventListener.class;
0124
0125            // These should be removed.
0126            private String defaultEventName;
0127            private String defaultPropertyName;
0128            private int defaultEventIndex = -1;
0129            private int defaultPropertyIndex = -1;
0130
0131            // Methods maps from Method objects to MethodDescriptors
0132            private Map methods;
0133
0134            // properties maps from String names to PropertyDescriptors
0135            private Map properties;
0136
0137            // events maps from String names to EventSetDescriptors
0138            private Map events;
0139
0140            private final static String DEFAULT_INFO_PATH = "sun.beans.infos";
0141
0142            private static String[] searchPath = { DEFAULT_INFO_PATH };
0143
0144            private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];
0145
0146            static final String ADD_PREFIX = "add";
0147            static final String REMOVE_PREFIX = "remove";
0148            static final String GET_PREFIX = "get";
0149            static final String SET_PREFIX = "set";
0150            static final String IS_PREFIX = "is";
0151
0152            private static final String BEANINFO_SUFFIX = "BeanInfo";
0153
0154            //======================================================================
0155            // 				Public methods
0156            //======================================================================
0157
0158            /**
0159             * Introspect on a Java Bean and learn about all its properties, exposed
0160             * methods, and events.
0161             * <p>
0162             * If the BeanInfo class for a Java Bean has been previously Introspected
0163             * then the BeanInfo class is retrieved from the BeanInfo cache.
0164             *
0165             * @param beanClass  The bean class to be analyzed.
0166             * @return  A BeanInfo object describing the target bean.
0167             * @exception IntrospectionException if an exception occurs during
0168             *              introspection.
0169             * @see #flushCaches
0170             * @see #flushFromCaches
0171             */
0172            public static BeanInfo getBeanInfo(Class<?> beanClass)
0173                    throws IntrospectionException {
0174                if (!ReflectUtil.isPackageAccessible(beanClass)) {
0175                    return (new Introspector(beanClass, null, USE_ALL_BEANINFO))
0176                            .getBeanInfo();
0177                }
0178                BeanInfo bi = (BeanInfo) beanInfoCache.get(beanClass);
0179                if (bi == null) {
0180                    bi = (new Introspector(beanClass, null, USE_ALL_BEANINFO))
0181                            .getBeanInfo();
0182                    beanInfoCache.put(beanClass, bi);
0183                }
0184                return bi;
0185            }
0186
0187            /**
0188             * Introspect on a Java bean and learn about all its properties, exposed
0189             * methods, and events, subject to some control flags.
0190             * <p>
0191             * If the BeanInfo class for a Java Bean has been previously Introspected
0192             * based on the same arguments then the BeanInfo class is retrieved 
0193             * from the BeanInfo cache.
0194             *
0195             * @param beanClass  The bean class to be analyzed.
0196             * @param flags  Flags to control the introspection.
0197             *     If flags == USE_ALL_BEANINFO then we use all of the BeanInfo
0198             *	 	classes we can discover.
0199             *     If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any
0200             *           BeanInfo associated with the specified beanClass.
0201             *     If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo
0202             *           associated with the specified beanClass or any of its
0203             *		 parent classes.
0204             * @return  A BeanInfo object describing the target bean.
0205             * @exception IntrospectionException if an exception occurs during
0206             *              introspection.
0207             */
0208            public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
0209                    throws IntrospectionException {
0210                return getBeanInfo(beanClass, null, flags);
0211            }
0212
0213            /**
0214             * Introspect on a Java bean and learn all about its properties, exposed
0215             * methods, below a given "stop" point.
0216             * <p>
0217             * If the BeanInfo class for a Java Bean has been previously Introspected
0218             * based on the same arguments, then the BeanInfo class is retrieved
0219             * from the BeanInfo cache.
0220             *
0221             * @param beanClass The bean class to be analyzed.
0222             * @param stopClass The baseclass at which to stop the analysis.  Any
0223             *    methods/properties/events in the stopClass or in its baseclasses
0224             *    will be ignored in the analysis.
0225             * @exception IntrospectionException if an exception occurs during
0226             *              introspection.
0227             */
0228            public static BeanInfo getBeanInfo(Class<?> beanClass,
0229                    Class<?> stopClass) throws IntrospectionException {
0230                return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
0231            }
0232
0233            /**
0234             * Only called from the public getBeanInfo methods. This method caches
0235             * the Introspected BeanInfo based on the arguments.
0236             */
0237            private static BeanInfo getBeanInfo(Class beanClass,
0238                    Class stopClass, int flags) throws IntrospectionException {
0239                BeanInfo bi;
0240                if (stopClass == null && flags == USE_ALL_BEANINFO) {
0241                    // Same parameters to take advantage of caching.
0242                    bi = getBeanInfo(beanClass);
0243                } else {
0244                    bi = (new Introspector(beanClass, stopClass, flags))
0245                            .getBeanInfo();
0246                }
0247                return bi;
0248
0249                // Old behaviour: Make an independent copy of the BeanInfo.
0250                //return new GenericBeanInfo(bi);
0251            }
0252
0253            /**
0254             * Utility method to take a string and convert it to normal Java variable
0255             * name capitalization.  This normally means converting the first
0256             * character from upper case to lower case, but in the (unusual) special
0257             * case when there is more than one character and both the first and
0258             * second characters are upper case, we leave it alone.
0259             * <p>
0260             * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
0261             * as "URL".
0262             *
0263             * @param  name The string to be decapitalized.
0264             * @return  The decapitalized version of the string.
0265             */
0266            public static String decapitalize(String name) {
0267                if (name == null || name.length() == 0) {
0268                    return name;
0269                }
0270                if (name.length() > 1 && Character.isUpperCase(name.charAt(1))
0271                        && Character.isUpperCase(name.charAt(0))) {
0272                    return name;
0273                }
0274                char chars[] = name.toCharArray();
0275                chars[0] = Character.toLowerCase(chars[0]);
0276                return new String(chars);
0277            }
0278
0279            /**
0280             * Gets the list of package names that will be used for
0281             *		finding BeanInfo classes.
0282             *
0283             * @return  The array of package names that will be searched in
0284             *		order to find BeanInfo classes. The default value
0285             *          for this array is implementation-dependent; e.g. 
0286             *          Sun implementation initially sets to {"sun.beans.infos"}.
0287             */
0288
0289            public static synchronized String[] getBeanInfoSearchPath() {
0290                // Return a copy of the searchPath.
0291                String result[] = new String[searchPath.length];
0292                for (int i = 0; i < searchPath.length; i++) {
0293                    result[i] = searchPath[i];
0294                }
0295                return result;
0296            }
0297
0298            /**
0299             * Change the list of package names that will be used for
0300             *		finding BeanInfo classes.  The behaviour of 
0301             *          this method is undefined if parameter path
0302             *          is null.
0303             * 
0304             * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code> 
0305             * method is called. This could result in a SecurityException.
0306             * 
0307             * @param path  Array of package names.
0308             * @exception  SecurityException  if a security manager exists and its  
0309             *             <code>checkPropertiesAccess</code> method doesn't allow setting
0310             *              of system properties.
0311             * @see SecurityManager#checkPropertiesAccess
0312             */
0313
0314            public static synchronized void setBeanInfoSearchPath(String path[]) {
0315                SecurityManager sm = System.getSecurityManager();
0316                if (sm != null) {
0317                    sm.checkPropertiesAccess();
0318                }
0319                searchPath = path;
0320            }
0321
0322            /**
0323             * Flush all of the Introspector's internal caches.  This method is
0324             * not normally required.  It is normally only needed by advanced
0325             * tools that update existing "Class" objects in-place and need
0326             * to make the Introspector re-analyze existing Class objects.
0327             */
0328
0329            public static void flushCaches() {
0330                beanInfoCache.clear();
0331                declaredMethodCache.clear();
0332            }
0333
0334            /**
0335             * Flush the Introspector's internal cached information for a given class.
0336             * This method is not normally required.  It is normally only needed
0337             * by advanced tools that update existing "Class" objects in-place
0338             * and need to make the Introspector re-analyze an existing Class object.
0339             *
0340             * Note that only the direct state associated with the target Class
0341             * object is flushed.  We do not flush state for other Class objects
0342             * with the same name, nor do we flush state for any related Class
0343             * objects (such as subclasses), even though their state may include
0344             * information indirectly obtained from the target Class object.
0345             *
0346             * @param clz  Class object to be flushed.
0347             * @throws NullPointerException If the Class object is null.
0348             */
0349            public static void flushFromCaches(Class<?> clz) {
0350                if (clz == null) {
0351                    throw new NullPointerException();
0352                }
0353                beanInfoCache.remove(clz);
0354                declaredMethodCache.remove(clz);
0355            }
0356
0357            //======================================================================
0358            // 			Private implementation methods
0359            //======================================================================
0360
0361            private Introspector(Class beanClass, Class stopClass, int flags)
0362                    throws IntrospectionException {
0363                this .beanClass = beanClass;
0364
0365                // Check stopClass is a superClass of startClass.
0366                if (stopClass != null) {
0367                    boolean isSuper = false;
0368                    for (Class c = beanClass.getSuperclass(); c != null; c = c
0369                            .getSuperclass()) {
0370                        if (c == stopClass) {
0371                            isSuper = true;
0372                        }
0373                    }
0374                    if (!isSuper) {
0375                        throw new IntrospectionException(stopClass.getName()
0376                                + " not superclass of " + beanClass.getName());
0377                    }
0378                }
0379
0380                if (flags == USE_ALL_BEANINFO) {
0381                    explicitBeanInfo = findExplicitBeanInfo(beanClass);
0382                }
0383
0384                Class super Class = beanClass.getSuperclass();
0385                if (super Class != stopClass) {
0386                    int newFlags = flags;
0387                    if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
0388                        newFlags = USE_ALL_BEANINFO;
0389                    }
0390                    super BeanInfo = getBeanInfo(super Class, stopClass, newFlags);
0391                }
0392                if (explicitBeanInfo != null) {
0393                    additionalBeanInfo = explicitBeanInfo
0394                            .getAdditionalBeanInfo();
0395                }
0396                if (additionalBeanInfo == null) {
0397                    additionalBeanInfo = new BeanInfo[0];
0398                }
0399            }
0400
0401            /**
0402             * Constructs a GenericBeanInfo class from the state of the Introspector
0403             */
0404            private BeanInfo getBeanInfo() throws IntrospectionException {
0405
0406                // the evaluation order here is import, as we evaluate the
0407                // event sets and locate PropertyChangeListeners before we
0408                // look for properties.
0409                BeanDescriptor bd = getTargetBeanDescriptor();
0410                MethodDescriptor mds[] = getTargetMethodInfo();
0411                EventSetDescriptor esds[] = getTargetEventInfo();
0412                PropertyDescriptor pds[] = getTargetPropertyInfo();
0413
0414                int defaultEvent = getTargetDefaultEventIndex();
0415                int defaultProperty = getTargetDefaultPropertyIndex();
0416
0417                return new GenericBeanInfo(bd, esds, defaultEvent, pds,
0418                        defaultProperty, mds, explicitBeanInfo);
0419
0420            }
0421
0422            /**
0423             * Looks for an explicit BeanInfo class that corresponds to the Class.
0424             * First it looks in the existing package that the Class is defined in,
0425             * then it checks to see if the class is its own BeanInfo. Finally,
0426             * the BeanInfo search path is prepended to the class and searched.
0427             *
0428             * @return Instance of an explicit BeanInfo class or null if one isn't found.
0429             */
0430            private static synchronized BeanInfo findExplicitBeanInfo(
0431                    Class beanClass) {
0432                String name = beanClass.getName() + BEANINFO_SUFFIX;
0433                try {
0434                    return (java.beans.BeanInfo) instantiate(beanClass, name);
0435                } catch (Exception ex) {
0436                    // Just drop through
0437
0438                }
0439                // Now try checking if the bean is its own BeanInfo.
0440                try {
0441                    if (isSubclass(beanClass, java.beans.BeanInfo.class)) {
0442                        return (java.beans.BeanInfo) beanClass.newInstance();
0443                    }
0444                } catch (Exception ex) {
0445                    // Just drop through
0446                }
0447                // Now try looking for <searchPath>.fooBeanInfo
0448                name = name.substring(name.lastIndexOf('.') + 1);
0449
0450                for (int i = 0; i < searchPath.length; i++) {
0451                    // This optimization will only use the BeanInfo search path if is has changed
0452                    // from the original or trying to get the ComponentBeanInfo. 
0453                    if (!DEFAULT_INFO_PATH.equals(searchPath[i])
0454                            || DEFAULT_INFO_PATH.equals(searchPath[i])
0455                            && "ComponentBeanInfo".equals(name)) {
0456                        try {
0457                            String fullName = searchPath[i] + "." + name;
0458                            java.beans.BeanInfo bi = (java.beans.BeanInfo) instantiate(
0459                                    beanClass, fullName);
0460
0461                            // Make sure that the returned BeanInfo matches the class.
0462                            if (bi.getBeanDescriptor() != null) {
0463                                if (bi.getBeanDescriptor().getBeanClass() == beanClass) {
0464                                    return bi;
0465                                }
0466                            } else if (bi.getPropertyDescriptors() != null) {
0467                                PropertyDescriptor[] pds = bi
0468                                        .getPropertyDescriptors();
0469                                for (int j = 0; j < pds.length; j++) {
0470                                    Method method = pds[j].getReadMethod();
0471                                    if (method == null) {
0472                                        method = pds[j].getWriteMethod();
0473                                    }
0474                                    if (method != null
0475                                            && method.getDeclaringClass() == beanClass) {
0476                                        return bi;
0477                                    }
0478                                }
0479                            } else if (bi.getMethodDescriptors() != null) {
0480                                MethodDescriptor[] mds = bi
0481                                        .getMethodDescriptors();
0482                                for (int j = 0; j < mds.length; j++) {
0483                                    Method method = mds[j].getMethod();
0484                                    if (method != null
0485                                            && method.getDeclaringClass() == beanClass) {
0486                                        return bi;
0487                                    }
0488                                }
0489                            }
0490                        } catch (Exception ex) {
0491                            // Silently ignore any errors.
0492                        }
0493                    }
0494                }
0495                return null;
0496            }
0497
0498            /**
0499             * @return An array of PropertyDescriptors describing the editable
0500             * properties supported by the target bean.
0501             */
0502
0503            private PropertyDescriptor[] getTargetPropertyInfo() {
0504
0505                // Check if the bean has its own BeanInfo that will provide
0506                // explicit information.
0507                PropertyDescriptor[] explicitProperties = null;
0508                if (explicitBeanInfo != null) {
0509                    explicitProperties = getPropertyDescriptors(this .explicitBeanInfo);
0510                }
0511
0512                if (explicitProperties == null && super BeanInfo != null) {
0513                    // We have no explicit BeanInfo properties.  Check with our parent.
0514                    addPropertyDescriptors(getPropertyDescriptors(this .super BeanInfo));
0515                }
0516
0517                for (int i = 0; i < additionalBeanInfo.length; i++) {
0518                    addPropertyDescriptors(additionalBeanInfo[i]
0519                            .getPropertyDescriptors());
0520                }
0521
0522                if (explicitProperties != null) {
0523                    // Add the explicit BeanInfo data to our results.
0524                    addPropertyDescriptors(explicitProperties);
0525
0526                } else {
0527
0528                    // Apply some reflection to the current class.
0529
0530                    // First get an array of all the public methods at this level
0531                    Method methodList[] = getPublicDeclaredMethods(beanClass);
0532
0533                    // Now analyze each method.
0534                    for (int i = 0; i < methodList.length; i++) {
0535                        Method method = methodList[i];
0536                        if (method == null || method.isSynthetic()) {
0537                            continue;
0538                        }
0539                        // skip static methods.
0540                        int mods = method.getModifiers();
0541                        if (Modifier.isStatic(mods)) {
0542                            continue;
0543                        }
0544                        String name = method.getName();
0545                        Class argTypes[] = method.getParameterTypes();
0546                        Class resultType = method.getReturnType();
0547                        int argCount = argTypes.length;
0548                        PropertyDescriptor pd = null;
0549
0550                        if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
0551                            // Optimization. Don't bother with invalid propertyNames.
0552                            continue;
0553                        }
0554
0555                        try {
0556
0557                            if (argCount == 0) {
0558                                if (name.startsWith(GET_PREFIX)) {
0559                                    // Simple getter
0560                                    pd = new PropertyDescriptor(this .beanClass,
0561                                            name.substring(3), method, null);
0562                                } else if (resultType == boolean.class
0563                                        && name.startsWith(IS_PREFIX)) {
0564                                    // Boolean getter
0565                                    pd = new PropertyDescriptor(this .beanClass,
0566                                            name.substring(2), method, null);
0567                                }
0568                            } else if (argCount == 1) {
0569                                if (argTypes[0] == int.class
0570                                        && name.startsWith(GET_PREFIX)) {
0571                                    pd = new IndexedPropertyDescriptor(
0572                                            this .beanClass, name.substring(3),
0573                                            null, null, method, null);
0574                                } else if (resultType == void.class
0575                                        && name.startsWith(SET_PREFIX)) {
0576                                    // Simple setter
0577                                    pd = new PropertyDescriptor(this .beanClass,
0578                                            name.substring(3), null, method);
0579                                    if (throwsException(method,
0580                                            PropertyVetoException.class)) {
0581                                        pd.setConstrained(true);
0582                                    }
0583                                }
0584                            } else if (argCount == 2) {
0585                                if (argTypes[0] == int.class
0586                                        && name.startsWith(SET_PREFIX)) {
0587                                    pd = new IndexedPropertyDescriptor(
0588                                            this .beanClass, name.substring(3),
0589                                            null, null, null, method);
0590                                    if (throwsException(method,
0591                                            PropertyVetoException.class)) {
0592                                        pd.setConstrained(true);
0593                                    }
0594                                }
0595                            }
0596                        } catch (IntrospectionException ex) {
0597                            // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
0598                            // constructor fins that the method violates details of the deisgn
0599                            // pattern, e.g. by having an empty name, or a getter returning
0600                            // void , or whatever.
0601                            pd = null;
0602                        }
0603
0604                        if (pd != null) {
0605                            // If this class or one of its base classes is a PropertyChange
0606                            // source, then we assume that any properties we discover are "bound".
0607                            if (propertyChangeSource) {
0608                                pd.setBound(true);
0609                            }
0610                            addPropertyDescriptor(pd);
0611                        }
0612                    }
0613                }
0614                processPropertyDescriptors();
0615
0616                // Allocate and populate the result array.
0617                PropertyDescriptor result[] = new PropertyDescriptor[properties
0618                        .size()];
0619                result = (PropertyDescriptor[]) properties.values().toArray(
0620                        result);
0621
0622                // Set the default index. 
0623                if (defaultPropertyName != null) {
0624                    for (int i = 0; i < result.length; i++) {
0625                        if (defaultPropertyName.equals(result[i].getName())) {
0626                            defaultPropertyIndex = i;
0627                        }
0628                    }
0629                }
0630
0631                return result;
0632            }
0633
0634            private HashMap pdStore = new HashMap();
0635
0636            /**
0637             * Adds the property descriptor to the list store.
0638             */
0639            private void addPropertyDescriptor(PropertyDescriptor pd) {
0640                String propName = pd.getName();
0641                List list = (List) pdStore.get(propName);
0642                if (list == null) {
0643                    list = new ArrayList();
0644                    pdStore.put(propName, list);
0645                }
0646                if (this .beanClass != pd.getClass0()) {
0647                    // replace existing property descriptor
0648                    // only if we have types to resolve
0649                    // in the context of this.beanClass
0650                    try {
0651                        String name = pd.getName();
0652                        Method read = pd.getReadMethod();
0653                        Method write = pd.getWriteMethod();
0654                        boolean cls = true;
0655                        if (read != null)
0656                            cls = cls
0657                                    && read.getGenericReturnType() instanceof  Class;
0658                        if (write != null)
0659                            cls = cls
0660                                    && write.getGenericParameterTypes()[0] instanceof  Class;
0661                        if (pd instanceof  IndexedPropertyDescriptor) {
0662                            IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
0663                            Method readI = ipd.getIndexedReadMethod();
0664                            Method writeI = ipd.getIndexedWriteMethod();
0665                            if (readI != null)
0666                                cls = cls
0667                                        && readI.getGenericReturnType() instanceof  Class;
0668                            if (writeI != null)
0669                                cls = cls
0670                                        && writeI.getGenericParameterTypes()[1] instanceof  Class;
0671                            if (!cls) {
0672                                pd = new IndexedPropertyDescriptor(
0673                                        this .beanClass, name, read, write,
0674                                        readI, writeI);
0675                            }
0676                        } else if (!cls) {
0677                            pd = new PropertyDescriptor(this .beanClass, name,
0678                                    read, write);
0679                        }
0680                    } catch (IntrospectionException e) {
0681                    }
0682                }
0683                list.add(pd);
0684            }
0685
0686            private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
0687                if (descriptors != null) {
0688                    for (PropertyDescriptor descriptor : descriptors) {
0689                        addPropertyDescriptor(descriptor);
0690                    }
0691                }
0692            }
0693
0694            private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {
0695                PropertyDescriptor[] descriptors = info
0696                        .getPropertyDescriptors();
0697                int index = info.getDefaultPropertyIndex();
0698                if ((0 <= index) && (index < descriptors.length)) {
0699                    this .defaultPropertyName = descriptors[index].getName();
0700                }
0701                return descriptors;
0702            }
0703
0704            /**
0705             * Populates the property descriptor table by merging the 
0706             * lists of Property descriptors.
0707             */
0708            private void processPropertyDescriptors() {
0709                if (properties == null) {
0710                    properties = new TreeMap();
0711                }
0712
0713                List list;
0714
0715                PropertyDescriptor pd, gpd, spd;
0716                IndexedPropertyDescriptor ipd, igpd, ispd;
0717
0718                Iterator it = pdStore.values().iterator();
0719                while (it.hasNext()) {
0720                    pd = null;
0721                    gpd = null;
0722                    spd = null;
0723                    ipd = null;
0724                    igpd = null;
0725                    ispd = null;
0726
0727                    list = (List) it.next();
0728
0729                    // First pass. Find the latest getter method. Merge properties
0730                    // of previous getter methods.
0731                    for (int i = 0; i < list.size(); i++) {
0732                        pd = (PropertyDescriptor) list.get(i);
0733                        if (pd instanceof  IndexedPropertyDescriptor) {
0734                            ipd = (IndexedPropertyDescriptor) pd;
0735                            if (ipd.getIndexedReadMethod() != null) {
0736                                if (igpd != null) {
0737                                    igpd = new IndexedPropertyDescriptor(igpd,
0738                                            ipd);
0739                                } else {
0740                                    igpd = ipd;
0741                                }
0742                            }
0743                        } else {
0744                            if (pd.getReadMethod() != null) {
0745                                if (gpd != null) {
0746                                    // Don't replace the existing read
0747                                    // method if it starts with "is"
0748                                    Method method = gpd.getReadMethod();
0749                                    if (!method.getName().startsWith(IS_PREFIX)) {
0750                                        gpd = new PropertyDescriptor(gpd, pd);
0751                                    }
0752                                } else {
0753                                    gpd = pd;
0754                                }
0755                            }
0756                        }
0757                    }
0758
0759                    // Second pass. Find the latest setter method which
0760                    // has the same type as the getter method.
0761                    for (int i = 0; i < list.size(); i++) {
0762                        pd = (PropertyDescriptor) list.get(i);
0763                        if (pd instanceof  IndexedPropertyDescriptor) {
0764                            ipd = (IndexedPropertyDescriptor) pd;
0765                            if (ipd.getIndexedWriteMethod() != null) {
0766                                if (igpd != null) {
0767                                    if (igpd.getIndexedPropertyType() == ipd
0768                                            .getIndexedPropertyType()) {
0769                                        if (ispd != null) {
0770                                            ispd = new IndexedPropertyDescriptor(
0771                                                    ispd, ipd);
0772                                        } else {
0773                                            ispd = ipd;
0774                                        }
0775                                    }
0776                                } else {
0777                                    if (ispd != null) {
0778                                        ispd = new IndexedPropertyDescriptor(
0779                                                ispd, ipd);
0780                                    } else {
0781                                        ispd = ipd;
0782                                    }
0783                                }
0784                            }
0785                        } else {
0786                            if (pd.getWriteMethod() != null) {
0787                                if (gpd != null) {
0788                                    if (gpd.getPropertyType() == pd
0789                                            .getPropertyType()) {
0790                                        if (spd != null) {
0791                                            spd = new PropertyDescriptor(spd,
0792                                                    pd);
0793                                        } else {
0794                                            spd = pd;
0795                                        }
0796                                    }
0797                                } else {
0798                                    if (spd != null) {
0799                                        spd = new PropertyDescriptor(spd, pd);
0800                                    } else {
0801                                        spd = pd;
0802                                    }
0803                                }
0804                            }
0805                        }
0806                    }
0807
0808                    // At this stage we should have either PDs or IPDs for the
0809                    // representative getters and setters. The order at which the 
0810                    // property descriptors are determined represent the 
0811                    // precedence of the property ordering.
0812                    pd = null;
0813                    ipd = null;
0814
0815                    if (igpd != null && ispd != null) {
0816                        // Complete indexed properties set
0817                        // Merge any classic property descriptors
0818                        if (gpd != null) {
0819                            PropertyDescriptor tpd = mergePropertyDescriptor(
0820                                    igpd, gpd);
0821                            if (tpd instanceof  IndexedPropertyDescriptor) {
0822                                igpd = (IndexedPropertyDescriptor) tpd;
0823                            }
0824                        }
0825                        if (spd != null) {
0826                            PropertyDescriptor tpd = mergePropertyDescriptor(
0827                                    ispd, spd);
0828                            if (tpd instanceof  IndexedPropertyDescriptor) {
0829                                ispd = (IndexedPropertyDescriptor) tpd;
0830                            }
0831                        }
0832                        if (igpd == ispd) {
0833                            pd = igpd;
0834                        } else {
0835                            pd = mergePropertyDescriptor(igpd, ispd);
0836                        }
0837                    } else if (gpd != null && spd != null) {
0838                        // Complete simple properties set
0839                        if (gpd == spd) {
0840                            pd = gpd;
0841                        } else {
0842                            pd = mergePropertyDescriptor(gpd, spd);
0843                        }
0844                    } else if (ispd != null) {
0845                        // indexed setter
0846                        pd = ispd;
0847                        // Merge any classic property descriptors
0848                        if (spd != null) {
0849                            pd = mergePropertyDescriptor(ispd, spd);
0850                        }
0851                        if (gpd != null) {
0852                            pd = mergePropertyDescriptor(ispd, gpd);
0853                        }
0854                    } else if (igpd != null) {
0855                        // indexed getter
0856                        pd = igpd;
0857                        // Merge any classic property descriptors
0858                        if (gpd != null) {
0859                            pd = mergePropertyDescriptor(igpd, gpd);
0860                        }
0861                        if (spd != null) {
0862                            pd = mergePropertyDescriptor(igpd, spd);
0863                        }
0864                    } else if (spd != null) {
0865                        // simple setter
0866                        pd = spd;
0867                    } else if (gpd != null) {
0868                        // simple getter
0869                        pd = gpd;
0870                    }
0871
0872                    // Very special case to ensure that an IndexedPropertyDescriptor
0873                    // doesn't contain less information than the enclosed 
0874                    // PropertyDescriptor. If it does, then recreate as a 
0875                    // PropertyDescriptor. See 4168833
0876                    if (pd instanceof  IndexedPropertyDescriptor) {
0877                        ipd = (IndexedPropertyDescriptor) pd;
0878                        if (ipd.getIndexedReadMethod() == null
0879                                && ipd.getIndexedWriteMethod() == null) {
0880                            pd = new PropertyDescriptor(ipd);
0881                        }
0882                    }
0883
0884                    // Find the first property descriptor
0885                    // which does not have getter and setter methods.
0886                    // See regression bug 4984912.
0887                    if ((pd == null) && (list.size() > 0)) {
0888                        pd = (PropertyDescriptor) list.get(0);
0889                    }
0890
0891                    if (pd != null) {
0892                        properties.put(pd.getName(), pd);
0893                    }
0894                }
0895            }
0896
0897            /**
0898             * Adds the property descriptor to the indexedproperty descriptor only if the 
0899             * types are the same.
0900             *
0901             * The most specific property descriptor will take precedence.
0902             */
0903            private PropertyDescriptor mergePropertyDescriptor(
0904                    IndexedPropertyDescriptor ipd, PropertyDescriptor pd) {
0905                PropertyDescriptor result = null;
0906
0907                Class propType = pd.getPropertyType();
0908                Class ipropType = ipd.getIndexedPropertyType();
0909
0910                if (propType.isArray()
0911                        && propType.getComponentType() == ipropType) {
0912                    if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0913                        result = new IndexedPropertyDescriptor(pd, ipd);
0914                    } else {
0915                        result = new IndexedPropertyDescriptor(ipd, pd);
0916                    }
0917                } else {
0918                    // Cannot merge the pd because of type mismatch
0919                    // Return the most specific pd
0920                    if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0921                        result = ipd;
0922                    } else {
0923                        result = pd;
0924                        // Try to add methods which may have been lost in the type change
0925                        // See 4168833
0926                        Method write = result.getWriteMethod();
0927                        Method read = result.getReadMethod();
0928
0929                        if (read == null && write != null) {
0930                            read = findMethod(result.getClass0(), GET_PREFIX
0931                                    + NameGenerator
0932                                            .capitalize(result.getName()), 0);
0933                            if (read != null) {
0934                                try {
0935                                    result.setReadMethod(read);
0936                                } catch (IntrospectionException ex) {
0937                                    // no consequences for failure.
0938                                }
0939                            }
0940                        }
0941                        if (write == null && read != null) {
0942                            write = findMethod(result.getClass0(), SET_PREFIX
0943                                    + NameGenerator
0944                                            .capitalize(result.getName()), 1,
0945                                    new Class[] { FeatureDescriptor
0946                                            .getReturnType(result.getClass0(),
0947                                                    read) });
0948                            if (write != null) {
0949                                try {
0950                                    result.setWriteMethod(write);
0951                                } catch (IntrospectionException ex) {
0952                                    // no consequences for failure.
0953                                }
0954                            }
0955                        }
0956                    }
0957                }
0958                return result;
0959            }
0960
0961            // Handle regular pd merge
0962            private PropertyDescriptor mergePropertyDescriptor(
0963                    PropertyDescriptor pd1, PropertyDescriptor pd2) {
0964                if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
0965                    return new PropertyDescriptor(pd1, pd2);
0966                } else {
0967                    return new PropertyDescriptor(pd2, pd1);
0968                }
0969            }
0970
0971            // Handle regular ipd merge
0972            private PropertyDescriptor mergePropertyDescriptor(
0973                    IndexedPropertyDescriptor ipd1,
0974                    IndexedPropertyDescriptor ipd2) {
0975                if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
0976                    return new IndexedPropertyDescriptor(ipd1, ipd2);
0977                } else {
0978                    return new IndexedPropertyDescriptor(ipd2, ipd1);
0979                }
0980            }
0981
0982            /**
0983             * @return An array of EventSetDescriptors describing the kinds of 
0984             * events fired by the target bean.
0985             */
0986            private EventSetDescriptor[] getTargetEventInfo()
0987                    throws IntrospectionException {
0988                if (events == null) {
0989                    events = new HashMap();
0990                }
0991
0992                // Check if the bean has its own BeanInfo that will provide
0993                // explicit information.
0994                EventSetDescriptor[] explicitEvents = null;
0995                if (explicitBeanInfo != null) {
0996                    explicitEvents = explicitBeanInfo.getEventSetDescriptors();
0997                    int ix = explicitBeanInfo.getDefaultEventIndex();
0998                    if (ix >= 0 && ix < explicitEvents.length) {
0999                        defaultEventName = explicitEvents[ix].getName();
1000                    }
1001                }
1002
1003                if (explicitEvents == null && super BeanInfo != null) {
1004                    // We have no explicit BeanInfo events.  Check with our parent.
1005                    EventSetDescriptor super s[] = super BeanInfo
1006                            .getEventSetDescriptors();
1007                    for (int i = 0; i < super s.length; i++) {
1008                        addEvent(super s[i]);
1009                    }
1010                    int ix = super BeanInfo.getDefaultEventIndex();
1011                    if (ix >= 0 && ix < super s.length) {
1012                        defaultEventName = super s[ix].getName();
1013                    }
1014                }
1015
1016                for (int i = 0; i < additionalBeanInfo.length; i++) {
1017                    EventSetDescriptor additional[] = additionalBeanInfo[i]
1018                            .getEventSetDescriptors();
1019                    if (additional != null) {
1020                        for (int j = 0; j < additional.length; j++) {
1021                            addEvent(additional[j]);
1022                        }
1023                    }
1024                }
1025
1026                if (explicitEvents != null) {
1027                    // Add the explicit explicitBeanInfo data to our results.
1028                    for (int i = 0; i < explicitEvents.length; i++) {
1029                        addEvent(explicitEvents[i]);
1030                    }
1031
1032                } else {
1033
1034                    // Apply some reflection to the current class.
1035
1036                    // Get an array of all the public beans methods at this level
1037                    Method methodList[] = getPublicDeclaredMethods(beanClass);
1038
1039                    // Find all suitable "add", "remove" and "get" Listener methods
1040                    // The name of the listener type is the key for these hashtables
1041                    // i.e, ActionListener
1042                    Map adds = null;
1043                    Map removes = null;
1044                    Map gets = null;
1045
1046                    for (int i = 0; i < methodList.length; i++) {
1047                        Method method = methodList[i];
1048                        if (method == null) {
1049                            continue;
1050                        }
1051                        // skip static methods.
1052                        int mods = method.getModifiers();
1053                        if (Modifier.isStatic(mods)) {
1054                            continue;
1055                        }
1056                        String name = method.getName();
1057                        // Optimization avoid getParameterTypes
1058                        if (!name.startsWith(ADD_PREFIX)
1059                                && !name.startsWith(REMOVE_PREFIX)
1060                                && !name.startsWith(GET_PREFIX)) {
1061                            continue;
1062                        }
1063
1064                        Class argTypes[] = FeatureDescriptor.getParameterTypes(
1065                                beanClass, method);
1066                        Class resultType = FeatureDescriptor.getReturnType(
1067                                beanClass, method);
1068
1069                        if (name.startsWith(ADD_PREFIX)
1070                                && argTypes.length == 1
1071                                && resultType == Void.TYPE
1072                                && Introspector.isSubclass(argTypes[0],
1073                                        eventListenerType)) {
1074                            String listenerName = name.substring(3);
1075                            if (listenerName.length() > 0
1076                                    && argTypes[0].getName().endsWith(
1077                                            listenerName)) {
1078                                if (adds == null) {
1079                                    adds = new HashMap();
1080                                }
1081                                adds.put(listenerName, method);
1082                            }
1083                        } else if (name.startsWith(REMOVE_PREFIX)
1084                                && argTypes.length == 1
1085                                && resultType == Void.TYPE
1086                                && Introspector.isSubclass(argTypes[0],
1087                                        eventListenerType)) {
1088                            String listenerName = name.substring(6);
1089                            if (listenerName.length() > 0
1090                                    && argTypes[0].getName().endsWith(
1091                                            listenerName)) {
1092                                if (removes == null) {
1093                                    removes = new HashMap();
1094                                }
1095                                removes.put(listenerName, method);
1096                            }
1097                        } else if (name.startsWith(GET_PREFIX)
1098                                && argTypes.length == 0
1099                                && resultType.isArray()
1100                                && Introspector.isSubclass(resultType
1101                                        .getComponentType(), eventListenerType)) {
1102                            String listenerName = name.substring(3, name
1103                                    .length() - 1);
1104                            if (listenerName.length() > 0
1105                                    && resultType.getComponentType().getName()
1106                                            .endsWith(listenerName)) {
1107                                if (gets == null) {
1108                                    gets = new HashMap();
1109                                }
1110                                gets.put(listenerName, method);
1111                            }
1112                        }
1113                    }
1114
1115                    if (adds != null && removes != null) {
1116                        // Now look for matching addFooListener+removeFooListener pairs.
1117                        // Bonus if there is a matching getFooListeners method as well.
1118                        Iterator keys = adds.keySet().iterator();
1119                        while (keys.hasNext()) {
1120                            String listenerName = (String) keys.next();
1121                            // Skip any "add" which doesn't have a matching "remove" or
1122                            // a listener name that doesn't end with Listener
1123                            if (removes.get(listenerName) == null
1124                                    || !listenerName.endsWith("Listener")) {
1125                                continue;
1126                            }
1127                            String eventName = decapitalize(listenerName
1128                                    .substring(0, listenerName.length() - 8));
1129                            Method addMethod = (Method) adds.get(listenerName);
1130                            Method removeMethod = (Method) removes
1131                                    .get(listenerName);
1132                            Method getMethod = null;
1133                            if (gets != null) {
1134                                getMethod = (Method) gets.get(listenerName);
1135                            }
1136                            Class argType = FeatureDescriptor
1137                                    .getParameterTypes(beanClass, addMethod)[0];
1138
1139                            // generate a list of Method objects for each of the target methods:
1140                            Method allMethods[] = getPublicDeclaredMethods(argType);
1141                            List validMethods = new ArrayList(allMethods.length);
1142                            for (int i = 0; i < allMethods.length; i++) {
1143                                if (allMethods[i] == null) {
1144                                    continue;
1145                                }
1146
1147                                if (isEventHandler(allMethods[i])) {
1148                                    validMethods.add(allMethods[i]);
1149                                }
1150                            }
1151                            Method[] methods = (Method[]) validMethods
1152                                    .toArray(new Method[validMethods.size()]);
1153
1154                            EventSetDescriptor esd = new EventSetDescriptor(
1155                                    eventName, argType, methods, addMethod,
1156                                    removeMethod, getMethod);
1157
1158                            // If the adder method throws the TooManyListenersException then it
1159                            // is a Unicast event source.
1160                            if (throwsException(addMethod,
1161                                    java.util.TooManyListenersException.class)) {
1162                                esd.setUnicast(true);
1163                            }
1164                            addEvent(esd);
1165                        }
1166                    } // if (adds != null ...
1167                }
1168                EventSetDescriptor[] result;
1169                if (events.size() == 0) {
1170                    result = EMPTY_EVENTSETDESCRIPTORS;
1171                } else {
1172                    // Allocate and populate the result array.
1173                    result = new EventSetDescriptor[events.size()];
1174                    result = (EventSetDescriptor[]) events.values().toArray(
1175                            result);
1176
1177                    // Set the default index. 
1178                    if (defaultEventName != null) {
1179                        for (int i = 0; i < result.length; i++) {
1180                            if (defaultEventName.equals(result[i].getName())) {
1181                                defaultEventIndex = i;
1182                            }
1183                        }
1184                    }
1185                }
1186                return result;
1187            }
1188
1189            private void addEvent(EventSetDescriptor esd) {
1190                String key = esd.getName();
1191                if (esd.getName().equals("propertyChange")) {
1192                    propertyChangeSource = true;
1193                }
1194                EventSetDescriptor old = (EventSetDescriptor) events.get(key);
1195                if (old == null) {
1196                    events.put(key, esd);
1197                    return;
1198                }
1199                EventSetDescriptor composite = new EventSetDescriptor(old, esd);
1200                events.put(key, composite);
1201            }
1202
1203            /**
1204             * @return An array of MethodDescriptors describing the private
1205             * methods supported by the target bean.
1206             */
1207            private MethodDescriptor[] getTargetMethodInfo() {
1208                if (methods == null) {
1209                    methods = new HashMap(100);
1210                }
1211
1212                // Check if the bean has its own BeanInfo that will provide
1213                // explicit information.
1214                MethodDescriptor[] explicitMethods = null;
1215                if (explicitBeanInfo != null) {
1216                    explicitMethods = explicitBeanInfo.getMethodDescriptors();
1217                }
1218
1219                if (explicitMethods == null && super BeanInfo != null) {
1220                    // We have no explicit BeanInfo methods.  Check with our parent.
1221                    MethodDescriptor super s[] = super BeanInfo
1222                            .getMethodDescriptors();
1223                    for (int i = 0; i < super s.length; i++) {
1224                        addMethod(super s[i]);
1225                    }
1226                }
1227
1228                for (int i = 0; i < additionalBeanInfo.length; i++) {
1229                    MethodDescriptor additional[] = additionalBeanInfo[i]
1230                            .getMethodDescriptors();
1231                    if (additional != null) {
1232                        for (int j = 0; j < additional.length; j++) {
1233                            addMethod(additional[j]);
1234                        }
1235                    }
1236                }
1237
1238                if (explicitMethods != null) {
1239                    // Add the explicit explicitBeanInfo data to our results.
1240                    for (int i = 0; i < explicitMethods.length; i++) {
1241                        addMethod(explicitMethods[i]);
1242                    }
1243
1244                } else {
1245
1246                    // Apply some reflection to the current class.
1247
1248                    // First get an array of all the beans methods at this level
1249                    Method methodList[] = getPublicDeclaredMethods(beanClass);
1250
1251                    // Now analyze each method.
1252                    for (int i = 0; i < methodList.length; i++) {
1253                        Method method = methodList[i];
1254                        if (method == null) {
1255                            continue;
1256                        }
1257                        MethodDescriptor md = new MethodDescriptor(method);
1258                        addMethod(md);
1259                    }
1260                }
1261
1262                // Allocate and populate the result array.
1263                MethodDescriptor result[] = new MethodDescriptor[methods.size()];
1264                result = (MethodDescriptor[]) methods.values().toArray(result);
1265
1266                return result;
1267            }
1268
1269            private void addMethod(MethodDescriptor md) {
1270                // We have to be careful here to distinguish method by both name
1271                // and argument lists.
1272                // This method gets called a *lot, so we try to be efficient.
1273                String name = md.getName();
1274
1275                MethodDescriptor old = (MethodDescriptor) methods.get(name);
1276                if (old == null) {
1277                    // This is the common case.
1278                    methods.put(name, md);
1279                    return;
1280                }
1281
1282                // We have a collision on method names.  This is rare.
1283
1284                // Check if old and md have the same type.
1285                String[] p1 = md.getParamNames();
1286                String[] p2 = old.getParamNames();
1287
1288                boolean match = false;
1289                if (p1.length == p2.length) {
1290                    match = true;
1291                    for (int i = 0; i < p1.length; i++) {
1292                        if (p1[i] != p2[i]) {
1293                            match = false;
1294                            break;
1295                        }
1296                    }
1297                }
1298                if (match) {
1299                    MethodDescriptor composite = new MethodDescriptor(old, md);
1300                    methods.put(name, composite);
1301                    return;
1302                }
1303
1304                // We have a collision on method names with different type signatures.
1305                // This is very rare.
1306
1307                String longKey = makeQualifiedMethodName(name, p1);
1308                old = (MethodDescriptor) methods.get(longKey);
1309                if (old == null) {
1310                    methods.put(longKey, md);
1311                    return;
1312                }
1313                MethodDescriptor composite = new MethodDescriptor(old, md);
1314                methods.put(longKey, composite);
1315            }
1316
1317            /**
1318             * Creates a key for a method in a method cache.
1319             */
1320            private static String makeQualifiedMethodName(String name,
1321                    String[] params) {
1322                StringBuffer sb = new StringBuffer(name);
1323                sb.append('=');
1324                for (int i = 0; i < params.length; i++) {
1325                    sb.append(':');
1326                    sb.append(params[i]);
1327                }
1328                return sb.toString();
1329            }
1330
1331            private int getTargetDefaultEventIndex() {
1332                return defaultEventIndex;
1333            }
1334
1335            private int getTargetDefaultPropertyIndex() {
1336                return defaultPropertyIndex;
1337            }
1338
1339            private BeanDescriptor getTargetBeanDescriptor() {
1340                // Use explicit info, if available,
1341                if (explicitBeanInfo != null) {
1342                    BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
1343                    if (bd != null) {
1344                        return (bd);
1345                    }
1346                }
1347                // OK, fabricate a default BeanDescriptor.
1348                return (new BeanDescriptor(beanClass));
1349            }
1350
1351            private boolean isEventHandler(Method m) {
1352                // We assume that a method is an event handler if it has a single
1353                // argument, whose type inherit from java.util.Event.
1354                Class argTypes[] = FeatureDescriptor.getParameterTypes(
1355                        beanClass, m);
1356                if (argTypes.length != 1) {
1357                    return false;
1358                }
1359                if (isSubclass(argTypes[0], java.util.EventObject.class)) {
1360                    return true;
1361                }
1362                return false;
1363            }
1364
1365            /*
1366             * Internal method to return *public* methods within a class.
1367             */
1368            private static synchronized Method[] getPublicDeclaredMethods(
1369                    Class clz) {
1370                // Looking up Class.getDeclaredMethods is relatively expensive,
1371                // so we cache the results.
1372                Method[] result = null;
1373                if (!ReflectUtil.isPackageAccessible(clz)) {
1374                    return new Method[0];
1375                }
1376                final Class fclz = clz;
1377                Reference ref = (Reference) declaredMethodCache.get(fclz);
1378                if (ref != null) {
1379                    result = (Method[]) ref.get();
1380                    if (result != null) {
1381                        return result;
1382                    }
1383                }
1384
1385                // We have to raise privilege for getDeclaredMethods
1386                result = (Method[]) AccessController
1387                        .doPrivileged(new PrivilegedAction() {
1388                            public Object run() {
1389                                return fclz.getDeclaredMethods();
1390                            }
1391                        });
1392
1393                // Null out any non-public methods.
1394                for (int i = 0; i < result.length; i++) {
1395                    Method method = result[i];
1396                    int mods = method.getModifiers();
1397                    if (!Modifier.isPublic(mods)) {
1398                        result[i] = null;
1399                    }
1400                }
1401                // Add it to the cache.
1402                declaredMethodCache.put(fclz, new SoftReference(result));
1403                return result;
1404            }
1405
1406            //======================================================================
1407            // Package private support methods.
1408            //======================================================================
1409
1410            /**
1411             * Internal support for finding a target methodName with a given
1412             * parameter list on a given class.
1413             */
1414            private static Method internalFindMethod(Class start,
1415                    String methodName, int argCount, Class args[]) {
1416                // For overriden methods we need to find the most derived version.
1417                // So we start with the given class and walk up the superclass chain.
1418
1419                Method method = null;
1420
1421                for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
1422                    Method methods[] = getPublicDeclaredMethods(cl);
1423                    for (int i = 0; i < methods.length; i++) {
1424                        method = methods[i];
1425                        if (method == null) {
1426                            continue;
1427                        }
1428
1429                        // make sure method signature matches.
1430                        Class params[] = FeatureDescriptor.getParameterTypes(
1431                                start, method);
1432                        if (method.getName().equals(methodName)
1433                                && params.length == argCount) {
1434                            if (args != null) {
1435                                boolean different = false;
1436                                if (argCount > 0) {
1437                                    for (int j = 0; j < argCount; j++) {
1438                                        if (params[j] != args[j]) {
1439                                            different = true;
1440                                            continue;
1441                                        }
1442                                    }
1443                                    if (different) {
1444                                        continue;
1445                                    }
1446                                }
1447                            }
1448                            return method;
1449                        }
1450                    }
1451                }
1452                method = null;
1453
1454                // Now check any inherited interfaces.  This is necessary both when
1455                // the argument class is itself an interface, and when the argument
1456                // class is an abstract class.
1457                Class ifcs[] = start.getInterfaces();
1458                for (int i = 0; i < ifcs.length; i++) {
1459                    // Note: The original implementation had both methods calling
1460                    // the 3 arg method. This is preserved but perhaps it should
1461                    // pass the args array instead of null.
1462                    method = internalFindMethod(ifcs[i], methodName, argCount,
1463                            null);
1464                    if (method != null) {
1465                        break;
1466                    }
1467                }
1468                return method;
1469            }
1470
1471            /**
1472             * Find a target methodName on a given class.
1473             */
1474            static Method findMethod(Class cls, String methodName, int argCount) {
1475                return findMethod(cls, methodName, argCount, null);
1476            }
1477
1478            /**
1479             * Find a target methodName with specific parameter list on a given class.
1480             * <p>
1481             * Used in the contructors of the EventSetDescriptor, 
1482             * PropertyDescriptor and the IndexedPropertyDescriptor.
1483             * <p>
1484             * @param cls The Class object on which to retrieve the method.
1485             * @param methodName Name of the method.
1486             * @param argCount Number of arguments for the desired method.
1487             * @param args Array of argument types for the method.
1488             * @return the method or null if not found
1489             */
1490            static Method findMethod(Class cls, String methodName,
1491                    int argCount, Class args[]) {
1492                if (methodName == null) {
1493                    return null;
1494                }
1495                return internalFindMethod(cls, methodName, argCount, args);
1496            }
1497
1498            /**
1499             * Return true if class a is either equivalent to class b, or
1500             * if class a is a subclass of class b, i.e. if a either "extends"
1501             * or "implements" b.
1502             * Note tht either or both "Class" objects may represent interfaces.
1503             */
1504            static boolean isSubclass(Class a, Class b) {
1505                // We rely on the fact that for any given java class or
1506                // primtitive type there is a unqiue Class object, so
1507                // we can use object equivalence in the comparisons.
1508                if (a == b) {
1509                    return true;
1510                }
1511                if (a == null || b == null) {
1512                    return false;
1513                }
1514                for (Class x = a; x != null; x = x.getSuperclass()) {
1515                    if (x == b) {
1516                        return true;
1517                    }
1518                    if (b.isInterface()) {
1519                        Class interfaces[] = x.getInterfaces();
1520                        for (int i = 0; i < interfaces.length; i++) {
1521                            if (isSubclass(interfaces[i], b)) {
1522                                return true;
1523                            }
1524                        }
1525                    }
1526                }
1527                return false;
1528            }
1529
1530            /**
1531             * Return true iff the given method throws the given exception.
1532             */
1533            private boolean throwsException(Method method, Class exception) {
1534                Class exs[] = method.getExceptionTypes();
1535                for (int i = 0; i < exs.length; i++) {
1536                    if (exs[i] == exception) {
1537                        return true;
1538                    }
1539                }
1540                return false;
1541            }
1542
1543            /**
1544             * Try to create an instance of a named class.
1545             * First try the classloader of "sibling", then try the system
1546             * classloader then the class loader of the current Thread.
1547             */
1548            static Object instantiate(Class sibling, String className)
1549                    throws InstantiationException, IllegalAccessException,
1550                    ClassNotFoundException {
1551                // First check with sibling's classloader (if any). 
1552                ClassLoader cl = sibling.getClassLoader();
1553                Class cls = ClassFinder.findClass(className, cl);
1554                return cls.newInstance();
1555            }
1556
1557        } // end class Introspector
1558
1559        //===========================================================================
1560
1561        /**
1562         * Package private implementation support class for Introspector's
1563         * internal use.
1564         * <p>
1565         * Mostly this is used as a placeholder for the descriptors.
1566         */
1567
1568        class GenericBeanInfo extends SimpleBeanInfo {
1569
1570            private BeanDescriptor beanDescriptor;
1571            private EventSetDescriptor[] events;
1572            private int defaultEvent;
1573            private PropertyDescriptor[] properties;
1574            private int defaultProperty;
1575            private MethodDescriptor[] methods;
1576            private BeanInfo targetBeanInfo;
1577
1578            public GenericBeanInfo(BeanDescriptor beanDescriptor,
1579                    EventSetDescriptor[] events, int defaultEvent,
1580                    PropertyDescriptor[] properties, int defaultProperty,
1581                    MethodDescriptor[] methods, BeanInfo targetBeanInfo) {
1582                this .beanDescriptor = beanDescriptor;
1583                this .events = events;
1584                this .defaultEvent = defaultEvent;
1585                this .properties = properties;
1586                this .defaultProperty = defaultProperty;
1587                this .methods = methods;
1588                this .targetBeanInfo = targetBeanInfo;
1589            }
1590
1591            /**
1592             * Package-private dup constructor
1593             * This must isolate the new object from any changes to the old object.
1594             */
1595            GenericBeanInfo(GenericBeanInfo old) {
1596
1597                beanDescriptor = new BeanDescriptor(old.beanDescriptor);
1598                if (old.events != null) {
1599                    int len = old.events.length;
1600                    events = new EventSetDescriptor[len];
1601                    for (int i = 0; i < len; i++) {
1602                        events[i] = new EventSetDescriptor(old.events[i]);
1603                    }
1604                }
1605                defaultEvent = old.defaultEvent;
1606                if (old.properties != null) {
1607                    int len = old.properties.length;
1608                    properties = new PropertyDescriptor[len];
1609                    for (int i = 0; i < len; i++) {
1610                        PropertyDescriptor oldp = old.properties[i];
1611                        if (oldp instanceof  IndexedPropertyDescriptor) {
1612                            properties[i] = new IndexedPropertyDescriptor(
1613                                    (IndexedPropertyDescriptor) oldp);
1614                        } else {
1615                            properties[i] = new PropertyDescriptor(oldp);
1616                        }
1617                    }
1618                }
1619                defaultProperty = old.defaultProperty;
1620                if (old.methods != null) {
1621                    int len = old.methods.length;
1622                    methods = new MethodDescriptor[len];
1623                    for (int i = 0; i < len; i++) {
1624                        methods[i] = new MethodDescriptor(old.methods[i]);
1625                    }
1626                }
1627                targetBeanInfo = old.targetBeanInfo;
1628            }
1629
1630            public PropertyDescriptor[] getPropertyDescriptors() {
1631                return properties;
1632            }
1633
1634            public int getDefaultPropertyIndex() {
1635                return defaultProperty;
1636            }
1637
1638            public EventSetDescriptor[] getEventSetDescriptors() {
1639                return events;
1640            }
1641
1642            public int getDefaultEventIndex() {
1643                return defaultEvent;
1644            }
1645
1646            public MethodDescriptor[] getMethodDescriptors() {
1647                return methods;
1648            }
1649
1650            public BeanDescriptor getBeanDescriptor() {
1651                return beanDescriptor;
1652            }
1653
1654            public java.awt.Image getIcon(int iconKind) {
1655                if (targetBeanInfo != null) {
1656                    return targetBeanInfo.getIcon(iconKind);
1657                }
1658                return super.getIcon(iconKind);
1659            }
1660        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.