Home » openjdk-7 » java » beans » [javadoc | source]

    1   /*
    2    * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package java.beans;
   27   
   28   import com.sun.beans.WeakCache;
   29   import com.sun.beans.finder.BeanInfoFinder;
   30   import com.sun.beans.finder.ClassFinder;
   31   
   32   import java.awt.Component;
   33   
   34   import java.lang.ref.Reference;
   35   import java.lang.ref.SoftReference;
   36   import java.lang.reflect.Method;
   37   import java.lang.reflect.Modifier;
   38   
   39   import java.util.Map;
   40   import java.util.ArrayList;
   41   import java.util.HashMap;
   42   import java.util.Iterator;
   43   import java.util.EventListener;
   44   import java.util.EventObject;
   45   import java.util.List;
   46   import java.util.TreeMap;
   47   import java.util.WeakHashMap;
   48   
   49   import sun.awt.AppContext;
   50   import sun.reflect.misc.ReflectUtil;
   51   
   52   /**
   53    * The Introspector class provides a standard way for tools to learn about
   54    * the properties, events, and methods supported by a target Java Bean.
   55    * <p>
   56    * For each of those three kinds of information, the Introspector will
   57    * separately analyze the bean's class and superclasses looking for
   58    * either explicit or implicit information and use that information to
   59    * build a BeanInfo object that comprehensively describes the target bean.
   60    * <p>
   61    * For each class "Foo", explicit information may be available if there exists
   62    * a corresponding "FooBeanInfo" class that provides a non-null value when
   63    * queried for the information.   We first look for the BeanInfo class by
   64    * taking the full package-qualified name of the target bean class and
   65    * appending "BeanInfo" to form a new class name.  If this fails, then
   66    * we take the final classname component of this name, and look for that
   67    * class in each of the packages specified in the BeanInfo package search
   68    * path.
   69    * <p>
   70    * Thus for a class such as "sun.xyz.OurButton" we would first look for a
   71    * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd
   72    * look in each package in the BeanInfo search path for an OurButtonBeanInfo
   73    * class.  With the default search path, this would mean looking for
   74    * "sun.beans.infos.OurButtonBeanInfo".
   75    * <p>
   76    * If a class provides explicit BeanInfo about itself then we add that to
   77    * the BeanInfo information we obtained from analyzing any derived classes,
   78    * but we regard the explicit information as being definitive for the current
   79    * class and its base classes, and do not proceed any further up the superclass
   80    * chain.
   81    * <p>
   82    * If we don't find explicit BeanInfo on a class, we use low-level
   83    * reflection to study the methods of the class and apply standard design
   84    * patterns to identify property accessors, event sources, or public
   85    * methods.  We then proceed to analyze the class's superclass and add
   86    * in the information from it (and possibly on up the superclass chain).
   87    * <p>
   88    * For more information about introspection and design patterns, please
   89    * consult the
   90    *  <a href="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans&trade; specification</a>.
   91    */
   92   
   93   public class Introspector {
   94   
   95       // Flags that can be used to control getBeanInfo:
   96       public final static int USE_ALL_BEANINFO           = 1;
   97       public final static int IGNORE_IMMEDIATE_BEANINFO  = 2;
   98       public final static int IGNORE_ALL_BEANINFO        = 3;
   99   
  100       // Static Caches to speed up introspection.
  101       private static WeakCache<Class<?>, Method[]> declaredMethodCache =
  102               new WeakCache<Class<?>, Method[]>();
  103   
  104       private static final Object BEANINFO_CACHE = new Object();
  105   
  106       private Class beanClass;
  107       private BeanInfo explicitBeanInfo;
  108       private BeanInfo superBeanInfo;
  109       private BeanInfo additionalBeanInfo[];
  110   
  111       private boolean propertyChangeSource = false;
  112       private static Class eventListenerType = EventListener.class;
  113   
  114       // These should be removed.
  115       private String defaultEventName;
  116       private String defaultPropertyName;
  117       private int defaultEventIndex = -1;
  118       private int defaultPropertyIndex = -1;
  119   
  120       // Methods maps from Method objects to MethodDescriptors
  121       private Map methods;
  122   
  123       // properties maps from String names to PropertyDescriptors
  124       private Map properties;
  125   
  126       // events maps from String names to EventSetDescriptors
  127       private Map events;
  128   
  129       private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];
  130   
  131       static final String ADD_PREFIX = "add";
  132       static final String REMOVE_PREFIX = "remove";
  133       static final String GET_PREFIX = "get";
  134       static final String SET_PREFIX = "set";
  135       static final String IS_PREFIX = "is";
  136   
  137       private static final Object FINDER_KEY = new Object();
  138   
  139       //======================================================================
  140       //                          Public methods
  141       //======================================================================
  142   
  143       /**
  144        * Introspect on a Java Bean and learn about all its properties, exposed
  145        * methods, and events.
  146        * <p>
  147        * If the BeanInfo class for a Java Bean has been previously Introspected
  148        * then the BeanInfo class is retrieved from the BeanInfo cache.
  149        *
  150        * @param beanClass  The bean class to be analyzed.
  151        * @return  A BeanInfo object describing the target bean.
  152        * @exception IntrospectionException if an exception occurs during
  153        *              introspection.
  154        * @see #flushCaches
  155        * @see #flushFromCaches
  156        */
  157       public static BeanInfo getBeanInfo(Class<?> beanClass)
  158           throws IntrospectionException
  159       {
  160           if (!ReflectUtil.isPackageAccessible(beanClass)) {
  161               return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
  162           }
  163           Map<Class<?>, BeanInfo> beanInfoCache;
  164           BeanInfo beanInfo;
  165           synchronized (BEANINFO_CACHE) {
  166               beanInfoCache = (Map<Class<?>, BeanInfo>) AppContext.getAppContext().get(BEANINFO_CACHE);
  167               if (beanInfoCache == null) {
  168                   beanInfoCache = new WeakHashMap<Class<?>, BeanInfo>();
  169                   AppContext.getAppContext().put(BEANINFO_CACHE, beanInfoCache);
  170               }
  171               beanInfo = beanInfoCache.get(beanClass);
  172           }
  173           if (beanInfo == null) {
  174               beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
  175               synchronized (BEANINFO_CACHE) {
  176                   beanInfoCache.put(beanClass, beanInfo);
  177               }
  178           }
  179           return beanInfo;
  180       }
  181   
  182       /**
  183        * Introspect on a Java bean and learn about all its properties, exposed
  184        * methods, and events, subject to some control flags.
  185        * <p>
  186        * If the BeanInfo class for a Java Bean has been previously Introspected
  187        * based on the same arguments then the BeanInfo class is retrieved
  188        * from the BeanInfo cache.
  189        *
  190        * @param beanClass  The bean class to be analyzed.
  191        * @param flags  Flags to control the introspection.
  192        *     If flags == USE_ALL_BEANINFO then we use all of the BeanInfo
  193        *          classes we can discover.
  194        *     If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any
  195        *           BeanInfo associated with the specified beanClass.
  196        *     If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo
  197        *           associated with the specified beanClass or any of its
  198        *           parent classes.
  199        * @return  A BeanInfo object describing the target bean.
  200        * @exception IntrospectionException if an exception occurs during
  201        *              introspection.
  202        */
  203       public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
  204                                                   throws IntrospectionException {
  205           return getBeanInfo(beanClass, null, flags);
  206       }
  207   
  208       /**
  209        * Introspect on a Java bean and learn all about its properties, exposed
  210        * methods, below a given "stop" point.
  211        * <p>
  212        * If the BeanInfo class for a Java Bean has been previously Introspected
  213        * based on the same arguments, then the BeanInfo class is retrieved
  214        * from the BeanInfo cache.
  215        *
  216        * @param beanClass The bean class to be analyzed.
  217        * @param stopClass The baseclass at which to stop the analysis.  Any
  218        *    methods/properties/events in the stopClass or in its baseclasses
  219        *    will be ignored in the analysis.
  220        * @exception IntrospectionException if an exception occurs during
  221        *              introspection.
  222        */
  223       public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
  224                                                   throws IntrospectionException {
  225           return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
  226       }
  227   
  228       /**
  229        * Introspect on a Java Bean and learn about all its properties,
  230        * exposed methods and events, below a given {@code stopClass} point
  231        * subject to some control {@code flags}.
  232        * <dl>
  233        *  <dt>USE_ALL_BEANINFO</dt>
  234        *  <dd>Any BeanInfo that can be discovered will be used.</dd>
  235        *  <dt>IGNORE_IMMEDIATE_BEANINFO</dt>
  236        *  <dd>Any BeanInfo associated with the specified {@code beanClass} will be ignored.</dd>
  237        *  <dt>IGNORE_ALL_BEANINFO</dt>
  238        *  <dd>Any BeanInfo associated with the specified {@code beanClass}
  239        *      or any of its parent classes will be ignored.</dd>
  240        * </dl>
  241        * Any methods/properties/events in the {@code stopClass}
  242        * or in its parent classes will be ignored in the analysis.
  243        * <p>
  244        * If the BeanInfo class for a Java Bean has been
  245        * previously introspected based on the same arguments then
  246        * the BeanInfo class is retrieved from the BeanInfo cache.
  247        *
  248        * @param beanClass  the bean class to be analyzed
  249        * @param stopClass  the parent class at which to stop the analysis
  250        * @param flags      flags to control the introspection
  251        * @return a BeanInfo object describing the target bean
  252        * @exception IntrospectionException if an exception occurs during introspection
  253        *
  254        * @since 1.7
  255        */
  256       public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass,
  257                                           int flags) throws IntrospectionException {
  258           BeanInfo bi;
  259           if (stopClass == null && flags == USE_ALL_BEANINFO) {
  260               // Same parameters to take advantage of caching.
  261               bi = getBeanInfo(beanClass);
  262           } else {
  263               bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();
  264           }
  265           return bi;
  266   
  267           // Old behaviour: Make an independent copy of the BeanInfo.
  268           //return new GenericBeanInfo(bi);
  269       }
  270   
  271   
  272       /**
  273        * Utility method to take a string and convert it to normal Java variable
  274        * name capitalization.  This normally means converting the first
  275        * character from upper case to lower case, but in the (unusual) special
  276        * case when there is more than one character and both the first and
  277        * second characters are upper case, we leave it alone.
  278        * <p>
  279        * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
  280        * as "URL".
  281        *
  282        * @param  name The string to be decapitalized.
  283        * @return  The decapitalized version of the string.
  284        */
  285       public static String decapitalize(String name) {
  286           if (name == null || name.length() == 0) {
  287               return name;
  288           }
  289           if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
  290                           Character.isUpperCase(name.charAt(0))){
  291               return name;
  292           }
  293           char chars[] = name.toCharArray();
  294           chars[0] = Character.toLowerCase(chars[0]);
  295           return new String(chars);
  296       }
  297   
  298       /**
  299        * Gets the list of package names that will be used for
  300        *          finding BeanInfo classes.
  301        *
  302        * @return  The array of package names that will be searched in
  303        *          order to find BeanInfo classes. The default value
  304        *          for this array is implementation-dependent; e.g.
  305        *          Sun implementation initially sets to {"sun.beans.infos"}.
  306        */
  307   
  308       public static String[] getBeanInfoSearchPath() {
  309           return getFinder().getPackages();
  310       }
  311   
  312       /**
  313        * Change the list of package names that will be used for
  314        *          finding BeanInfo classes.  The behaviour of
  315        *          this method is undefined if parameter path
  316        *          is null.
  317        *
  318        * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>
  319        * method is called. This could result in a SecurityException.
  320        *
  321        * @param path  Array of package names.
  322        * @exception  SecurityException  if a security manager exists and its
  323        *             <code>checkPropertiesAccess</code> method doesn't allow setting
  324        *              of system properties.
  325        * @see SecurityManager#checkPropertiesAccess
  326        */
  327   
  328       public static void setBeanInfoSearchPath(String[] path) {
  329           SecurityManager sm = System.getSecurityManager();
  330           if (sm != null) {
  331               sm.checkPropertiesAccess();
  332           }
  333           getFinder().setPackages(path);
  334       }
  335   
  336   
  337       /**
  338        * Flush all of the Introspector's internal caches.  This method is
  339        * not normally required.  It is normally only needed by advanced
  340        * tools that update existing "Class" objects in-place and need
  341        * to make the Introspector re-analyze existing Class objects.
  342        */
  343   
  344       public static void flushCaches() {
  345           synchronized (BEANINFO_CACHE) {
  346               Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE);
  347               if (beanInfoCache != null) {
  348                   beanInfoCache.clear();
  349               }
  350               declaredMethodCache.clear();
  351           }
  352       }
  353   
  354       /**
  355        * Flush the Introspector's internal cached information for a given class.
  356        * This method is not normally required.  It is normally only needed
  357        * by advanced tools that update existing "Class" objects in-place
  358        * and need to make the Introspector re-analyze an existing Class object.
  359        *
  360        * Note that only the direct state associated with the target Class
  361        * object is flushed.  We do not flush state for other Class objects
  362        * with the same name, nor do we flush state for any related Class
  363        * objects (such as subclasses), even though their state may include
  364        * information indirectly obtained from the target Class object.
  365        *
  366        * @param clz  Class object to be flushed.
  367        * @throws NullPointerException If the Class object is null.
  368        */
  369       public static void flushFromCaches(Class<?> clz) {
  370           if (clz == null) {
  371               throw new NullPointerException();
  372           }
  373           synchronized (BEANINFO_CACHE) {
  374               Map beanInfoCache = (Map) AppContext.getAppContext().get(BEANINFO_CACHE);
  375               if (beanInfoCache != null) {
  376                   beanInfoCache.put(clz, null);
  377               }
  378               declaredMethodCache.put(clz, null);
  379           }
  380       }
  381   
  382       //======================================================================
  383       //                  Private implementation methods
  384       //======================================================================
  385   
  386       private Introspector(Class beanClass, Class stopClass, int flags)
  387                                               throws IntrospectionException {
  388           this.beanClass = beanClass;
  389   
  390           // Check stopClass is a superClass of startClass.
  391           if (stopClass != null) {
  392               boolean isSuper = false;
  393               for (Class c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {
  394                   if (c == stopClass) {
  395                       isSuper = true;
  396                   }
  397               }
  398               if (!isSuper) {
  399                   throw new IntrospectionException(stopClass.getName() + " not superclass of " +
  400                                           beanClass.getName());
  401               }
  402           }
  403   
  404           if (flags == USE_ALL_BEANINFO) {
  405               explicitBeanInfo = findExplicitBeanInfo(beanClass);
  406           }
  407   
  408           Class superClass = beanClass.getSuperclass();
  409           if (superClass != stopClass) {
  410               int newFlags = flags;
  411               if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
  412                   newFlags = USE_ALL_BEANINFO;
  413               }
  414               superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
  415           }
  416           if (explicitBeanInfo != null) {
  417               additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
  418           }
  419           if (additionalBeanInfo == null) {
  420               additionalBeanInfo = new BeanInfo[0];
  421           }
  422       }
  423   
  424       /**
  425        * Constructs a GenericBeanInfo class from the state of the Introspector
  426        */
  427       private BeanInfo getBeanInfo() throws IntrospectionException {
  428   
  429           // the evaluation order here is import, as we evaluate the
  430           // event sets and locate PropertyChangeListeners before we
  431           // look for properties.
  432           BeanDescriptor bd = getTargetBeanDescriptor();
  433           MethodDescriptor mds[] = getTargetMethodInfo();
  434           EventSetDescriptor esds[] = getTargetEventInfo();
  435           PropertyDescriptor pds[] = getTargetPropertyInfo();
  436   
  437           int defaultEvent = getTargetDefaultEventIndex();
  438           int defaultProperty = getTargetDefaultPropertyIndex();
  439   
  440           return new GenericBeanInfo(bd, esds, defaultEvent, pds,
  441                           defaultProperty, mds, explicitBeanInfo);
  442   
  443       }
  444   
  445       /**
  446        * Looks for an explicit BeanInfo class that corresponds to the Class.
  447        * First it looks in the existing package that the Class is defined in,
  448        * then it checks to see if the class is its own BeanInfo. Finally,
  449        * the BeanInfo search path is prepended to the class and searched.
  450        *
  451        * @param beanClass  the class type of the bean
  452        * @return Instance of an explicit BeanInfo class or null if one isn't found.
  453        */
  454       private static BeanInfo findExplicitBeanInfo(Class beanClass) {
  455           return getFinder().find(beanClass);
  456       }
  457   
  458       /**
  459        * @return An array of PropertyDescriptors describing the editable
  460        * properties supported by the target bean.
  461        */
  462   
  463       private PropertyDescriptor[] getTargetPropertyInfo() {
  464   
  465           // Check if the bean has its own BeanInfo that will provide
  466           // explicit information.
  467           PropertyDescriptor[] explicitProperties = null;
  468           if (explicitBeanInfo != null) {
  469               explicitProperties = getPropertyDescriptors(this.explicitBeanInfo);
  470           }
  471   
  472           if (explicitProperties == null && superBeanInfo != null) {
  473               // We have no explicit BeanInfo properties.  Check with our parent.
  474               addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo));
  475           }
  476   
  477           for (int i = 0; i < additionalBeanInfo.length; i++) {
  478               addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors());
  479           }
  480   
  481           if (explicitProperties != null) {
  482               // Add the explicit BeanInfo data to our results.
  483               addPropertyDescriptors(explicitProperties);
  484   
  485           } else {
  486   
  487               // Apply some reflection to the current class.
  488   
  489               // First get an array of all the public methods at this level
  490               Method methodList[] = getPublicDeclaredMethods(beanClass);
  491   
  492               // Now analyze each method.
  493               for (int i = 0; i < methodList.length; i++) {
  494                   Method method = methodList[i];
  495                   if (method == null || method.isSynthetic()) {
  496                       continue;
  497                   }
  498                   // skip static methods.
  499                   int mods = method.getModifiers();
  500                   if (Modifier.isStatic(mods)) {
  501                       continue;
  502                   }
  503                   String name = method.getName();
  504                   Class argTypes[] = method.getParameterTypes();
  505                   Class resultType = method.getReturnType();
  506                   int argCount = argTypes.length;
  507                   PropertyDescriptor pd = null;
  508   
  509                   if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
  510                       // Optimization. Don't bother with invalid propertyNames.
  511                       continue;
  512                   }
  513   
  514                   try {
  515   
  516                       if (argCount == 0) {
  517                           if (name.startsWith(GET_PREFIX)) {
  518                               // Simple getter
  519                               pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
  520                           } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
  521                               // Boolean getter
  522                               pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
  523                           }
  524                       } else if (argCount == 1) {
  525                           if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
  526                               pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
  527                           } else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
  528                               // Simple setter
  529                               pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
  530                               if (throwsException(method, PropertyVetoException.class)) {
  531                                   pd.setConstrained(true);
  532                               }
  533                           }
  534                       } else if (argCount == 2) {
  535                               if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {
  536                               pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);
  537                               if (throwsException(method, PropertyVetoException.class)) {
  538                                   pd.setConstrained(true);
  539                               }
  540                           }
  541                       }
  542                   } catch (IntrospectionException ex) {
  543                       // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
  544                       // constructor fins that the method violates details of the deisgn
  545                       // pattern, e.g. by having an empty name, or a getter returning
  546                       // void , or whatever.
  547                       pd = null;
  548                   }
  549   
  550                   if (pd != null) {
  551                       // If this class or one of its base classes is a PropertyChange
  552                       // source, then we assume that any properties we discover are "bound".
  553                       if (propertyChangeSource) {
  554                           pd.setBound(true);
  555                       }
  556                       addPropertyDescriptor(pd);
  557                   }
  558               }
  559           }
  560           processPropertyDescriptors();
  561   
  562           // Allocate and populate the result array.
  563           PropertyDescriptor result[] = new PropertyDescriptor[properties.size()];
  564           result = (PropertyDescriptor[])properties.values().toArray(result);
  565   
  566           // Set the default index.
  567           if (defaultPropertyName != null) {
  568               for (int i = 0; i < result.length; i++) {
  569                   if (defaultPropertyName.equals(result[i].getName())) {
  570                       defaultPropertyIndex = i;
  571                   }
  572               }
  573           }
  574   
  575           return result;
  576       }
  577   
  578       private HashMap pdStore = new HashMap();
  579   
  580       /**
  581        * Adds the property descriptor to the list store.
  582        */
  583       private void addPropertyDescriptor(PropertyDescriptor pd) {
  584           String propName = pd.getName();
  585           List list = (List)pdStore.get(propName);
  586           if (list == null) {
  587               list = new ArrayList();
  588               pdStore.put(propName, list);
  589           }
  590           if (this.beanClass != pd.getClass0()) {
  591               // replace existing property descriptor
  592               // only if we have types to resolve
  593               // in the context of this.beanClass
  594               try {
  595                   String name = pd.getName();
  596                   Method read = pd.getReadMethod();
  597                   Method write = pd.getWriteMethod();
  598                   boolean cls = true;
  599                   if (read != null) cls = cls && read.getGenericReturnType() instanceof Class;
  600                   if (write != null) cls = cls && write.getGenericParameterTypes()[0] instanceof Class;
  601                   if (pd instanceof IndexedPropertyDescriptor) {
  602                       IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor)pd;
  603                       Method readI = ipd.getIndexedReadMethod();
  604                       Method writeI = ipd.getIndexedWriteMethod();
  605                       if (readI != null) cls = cls && readI.getGenericReturnType() instanceof Class;
  606                       if (writeI != null) cls = cls && writeI.getGenericParameterTypes()[1] instanceof Class;
  607                       if (!cls) {
  608                           pd = new IndexedPropertyDescriptor(this.beanClass, name, read, write, readI, writeI);
  609                       }
  610                   } else if (!cls) {
  611                       pd = new PropertyDescriptor(this.beanClass, name, read, write);
  612                   }
  613               } catch ( IntrospectionException e ) {
  614               }
  615           }
  616           list.add(pd);
  617       }
  618   
  619       private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
  620           if (descriptors != null) {
  621               for (PropertyDescriptor descriptor : descriptors) {
  622                   addPropertyDescriptor(descriptor);
  623               }
  624           }
  625       }
  626   
  627       private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {
  628           PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
  629           int index = info.getDefaultPropertyIndex();
  630           if ((0 <= index) && (index < descriptors.length)) {
  631               this.defaultPropertyName = descriptors[index].getName();
  632           }
  633           return descriptors;
  634       }
  635   
  636       /**
  637        * Populates the property descriptor table by merging the
  638        * lists of Property descriptors.
  639        */
  640       private void processPropertyDescriptors() {
  641           if (properties == null) {
  642               properties = new TreeMap();
  643           }
  644   
  645           List list;
  646   
  647           PropertyDescriptor pd, gpd, spd;
  648           IndexedPropertyDescriptor ipd, igpd, ispd;
  649   
  650           Iterator it = pdStore.values().iterator();
  651           while (it.hasNext()) {
  652               pd = null; gpd = null; spd = null;
  653               ipd = null; igpd = null; ispd = null;
  654   
  655               list = (List)it.next();
  656   
  657               // First pass. Find the latest getter method. Merge properties
  658               // of previous getter methods.
  659               for (int i = 0; i < list.size(); i++) {
  660                   pd = (PropertyDescriptor)list.get(i);
  661                   if (pd instanceof IndexedPropertyDescriptor) {
  662                       ipd = (IndexedPropertyDescriptor)pd;
  663                       if (ipd.getIndexedReadMethod() != null) {
  664                           if (igpd != null) {
  665                               igpd = new IndexedPropertyDescriptor(igpd, ipd);
  666                           } else {
  667                               igpd = ipd;
  668                           }
  669                       }
  670                   } else {
  671                       if (pd.getReadMethod() != null) {
  672                           if (gpd != null) {
  673                               // Don't replace the existing read
  674                               // method if it starts with "is"
  675                               Method method = gpd.getReadMethod();
  676                               if (!method.getName().startsWith(IS_PREFIX)) {
  677                                   gpd = new PropertyDescriptor(gpd, pd);
  678                               }
  679                           } else {
  680                               gpd = pd;
  681                           }
  682                       }
  683                   }
  684               }
  685   
  686               // Second pass. Find the latest setter method which
  687               // has the same type as the getter method.
  688               for (int i = 0; i < list.size(); i++) {
  689                   pd = (PropertyDescriptor)list.get(i);
  690                   if (pd instanceof IndexedPropertyDescriptor) {
  691                       ipd = (IndexedPropertyDescriptor)pd;
  692                       if (ipd.getIndexedWriteMethod() != null) {
  693                           if (igpd != null) {
  694                               if (igpd.getIndexedPropertyType()
  695                                   == ipd.getIndexedPropertyType()) {
  696                                   if (ispd != null) {
  697                                       ispd = new IndexedPropertyDescriptor(ispd, ipd);
  698                                   } else {
  699                                       ispd = ipd;
  700                                   }
  701                               }
  702                           } else {
  703                               if (ispd != null) {
  704                                   ispd = new IndexedPropertyDescriptor(ispd, ipd);
  705                               } else {
  706                                   ispd = ipd;
  707                               }
  708                           }
  709                       }
  710                   } else {
  711                       if (pd.getWriteMethod() != null) {
  712                           if (gpd != null) {
  713                               if (gpd.getPropertyType() == pd.getPropertyType()) {
  714                                   if (spd != null) {
  715                                       spd = new PropertyDescriptor(spd, pd);
  716                                   } else {
  717                                       spd = pd;
  718                                   }
  719                               }
  720                           } else {
  721                               if (spd != null) {
  722                                   spd = new PropertyDescriptor(spd, pd);
  723                               } else {
  724                                   spd = pd;
  725                               }
  726                           }
  727                       }
  728                   }
  729               }
  730   
  731               // At this stage we should have either PDs or IPDs for the
  732               // representative getters and setters. The order at which the
  733               // property descriptors are determined represent the
  734               // precedence of the property ordering.
  735               pd = null; ipd = null;
  736   
  737               if (igpd != null && ispd != null) {
  738                   // Complete indexed properties set
  739                   // Merge any classic property descriptors
  740                   if (gpd != null) {
  741                       PropertyDescriptor tpd = mergePropertyDescriptor(igpd, gpd);
  742                       if (tpd instanceof IndexedPropertyDescriptor) {
  743                           igpd = (IndexedPropertyDescriptor)tpd;
  744                       }
  745                   }
  746                   if (spd != null) {
  747                       PropertyDescriptor tpd = mergePropertyDescriptor(ispd, spd);
  748                       if (tpd instanceof IndexedPropertyDescriptor) {
  749                           ispd = (IndexedPropertyDescriptor)tpd;
  750                       }
  751                   }
  752                   if (igpd == ispd) {
  753                       pd = igpd;
  754                   } else {
  755                       pd = mergePropertyDescriptor(igpd, ispd);
  756                   }
  757               } else if (gpd != null && spd != null) {
  758                   // Complete simple properties set
  759                   if (gpd == spd) {
  760                       pd = gpd;
  761                   } else {
  762                       pd = mergePropertyDescriptor(gpd, spd);
  763                   }
  764               } else if (ispd != null) {
  765                   // indexed setter
  766                   pd = ispd;
  767                   // Merge any classic property descriptors
  768                   if (spd != null) {
  769                       pd = mergePropertyDescriptor(ispd, spd);
  770                   }
  771                   if (gpd != null) {
  772                       pd = mergePropertyDescriptor(ispd, gpd);
  773                   }
  774               } else if (igpd != null) {
  775                   // indexed getter
  776                   pd = igpd;
  777                   // Merge any classic property descriptors
  778                   if (gpd != null) {
  779                       pd = mergePropertyDescriptor(igpd, gpd);
  780                   }
  781                   if (spd != null) {
  782                       pd = mergePropertyDescriptor(igpd, spd);
  783                   }
  784               } else if (spd != null) {
  785                   // simple setter
  786                   pd = spd;
  787               } else if (gpd != null) {
  788                   // simple getter
  789                   pd = gpd;
  790               }
  791   
  792               // Very special case to ensure that an IndexedPropertyDescriptor
  793               // doesn't contain less information than the enclosed
  794               // PropertyDescriptor. If it does, then recreate as a
  795               // PropertyDescriptor. See 4168833
  796               if (pd instanceof IndexedPropertyDescriptor) {
  797                   ipd = (IndexedPropertyDescriptor)pd;
  798                   if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {
  799                       pd = new PropertyDescriptor(ipd);
  800                   }
  801               }
  802   
  803               // Find the first property descriptor
  804               // which does not have getter and setter methods.
  805               // See regression bug 4984912.
  806               if ( (pd == null) && (list.size() > 0) ) {
  807                   pd = (PropertyDescriptor) list.get(0);
  808               }
  809   
  810               if (pd != null) {
  811                   properties.put(pd.getName(), pd);
  812               }
  813           }
  814       }
  815   
  816       /**
  817        * Adds the property descriptor to the indexedproperty descriptor only if the
  818        * types are the same.
  819        *
  820        * The most specific property descriptor will take precedence.
  821        */
  822       private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd,
  823                                                          PropertyDescriptor pd) {
  824           PropertyDescriptor result = null;
  825   
  826           Class propType = pd.getPropertyType();
  827           Class ipropType = ipd.getIndexedPropertyType();
  828   
  829           if (propType.isArray() && propType.getComponentType() == ipropType) {
  830               if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
  831                   result = new IndexedPropertyDescriptor(pd, ipd);
  832               } else {
  833                   result = new IndexedPropertyDescriptor(ipd, pd);
  834               }
  835           } else {
  836               // Cannot merge the pd because of type mismatch
  837               // Return the most specific pd
  838               if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
  839                   result = ipd;
  840               } else {
  841                   result = pd;
  842                   // Try to add methods which may have been lost in the type change
  843                   // See 4168833
  844                   Method write = result.getWriteMethod();
  845                   Method read = result.getReadMethod();
  846   
  847                   if (read == null && write != null) {
  848                       read = findMethod(result.getClass0(),
  849                                         GET_PREFIX + NameGenerator.capitalize(result.getName()), 0);
  850                       if (read != null) {
  851                           try {
  852                               result.setReadMethod(read);
  853                           } catch (IntrospectionException ex) {
  854                               // no consequences for failure.
  855                           }
  856                       }
  857                   }
  858                   if (write == null && read != null) {
  859                       write = findMethod(result.getClass0(),
  860                                          SET_PREFIX + NameGenerator.capitalize(result.getName()), 1,
  861                                          new Class[] { FeatureDescriptor.getReturnType(result.getClass0(), read) });
  862                       if (write != null) {
  863                           try {
  864                               result.setWriteMethod(write);
  865                           } catch (IntrospectionException ex) {
  866                               // no consequences for failure.
  867                           }
  868                       }
  869                   }
  870               }
  871           }
  872           return result;
  873       }
  874   
  875       // Handle regular pd merge
  876       private PropertyDescriptor mergePropertyDescriptor(PropertyDescriptor pd1,
  877                                                          PropertyDescriptor pd2) {
  878           if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
  879               return new PropertyDescriptor(pd1, pd2);
  880           } else {
  881               return new PropertyDescriptor(pd2, pd1);
  882           }
  883       }
  884   
  885       // Handle regular ipd merge
  886       private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd1,
  887                                                          IndexedPropertyDescriptor ipd2) {
  888           if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
  889               return new IndexedPropertyDescriptor(ipd1, ipd2);
  890           } else {
  891               return new IndexedPropertyDescriptor(ipd2, ipd1);
  892           }
  893       }
  894   
  895       /**
  896        * @return An array of EventSetDescriptors describing the kinds of
  897        * events fired by the target bean.
  898        */
  899       private EventSetDescriptor[] getTargetEventInfo() throws IntrospectionException {
  900           if (events == null) {
  901               events = new HashMap();
  902           }
  903   
  904           // Check if the bean has its own BeanInfo that will provide
  905           // explicit information.
  906           EventSetDescriptor[] explicitEvents = null;
  907           if (explicitBeanInfo != null) {
  908               explicitEvents = explicitBeanInfo.getEventSetDescriptors();
  909               int ix = explicitBeanInfo.getDefaultEventIndex();
  910               if (ix >= 0 && ix < explicitEvents.length) {
  911                   defaultEventName = explicitEvents[ix].getName();
  912               }
  913           }
  914   
  915           if (explicitEvents == null && superBeanInfo != null) {
  916               // We have no explicit BeanInfo events.  Check with our parent.
  917               EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors();
  918               for (int i = 0 ; i < supers.length; i++) {
  919                   addEvent(supers[i]);
  920               }
  921               int ix = superBeanInfo.getDefaultEventIndex();
  922               if (ix >= 0 && ix < supers.length) {
  923                   defaultEventName = supers[ix].getName();
  924               }
  925           }
  926   
  927           for (int i = 0; i < additionalBeanInfo.length; i++) {
  928               EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors();
  929               if (additional != null) {
  930                   for (int j = 0 ; j < additional.length; j++) {
  931                       addEvent(additional[j]);
  932                   }
  933               }
  934           }
  935   
  936           if (explicitEvents != null) {
  937               // Add the explicit explicitBeanInfo data to our results.
  938               for (int i = 0 ; i < explicitEvents.length; i++) {
  939                   addEvent(explicitEvents[i]);
  940               }
  941   
  942           } else {
  943   
  944               // Apply some reflection to the current class.
  945   
  946               // Get an array of all the public beans methods at this level
  947               Method methodList[] = getPublicDeclaredMethods(beanClass);
  948   
  949               // Find all suitable "add", "remove" and "get" Listener methods
  950               // The name of the listener type is the key for these hashtables
  951               // i.e, ActionListener
  952               Map adds = null;
  953               Map removes = null;
  954               Map gets = null;
  955   
  956               for (int i = 0; i < methodList.length; i++) {
  957                   Method method = methodList[i];
  958                   if (method == null) {
  959                       continue;
  960                   }
  961                   // skip static methods.
  962                   int mods = method.getModifiers();
  963                   if (Modifier.isStatic(mods)) {
  964                       continue;
  965                   }
  966                   String name = method.getName();
  967                   // Optimization avoid getParameterTypes
  968                   if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)
  969                       && !name.startsWith(GET_PREFIX)) {
  970                       continue;
  971                   }
  972   
  973                   Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, method);
  974                   Class resultType = FeatureDescriptor.getReturnType(beanClass, method);
  975   
  976                   if (name.startsWith(ADD_PREFIX) && argTypes.length == 1 &&
  977                       resultType == Void.TYPE &&
  978                       Introspector.isSubclass(argTypes[0], eventListenerType)) {
  979                       String listenerName = name.substring(3);
  980                       if (listenerName.length() > 0 &&
  981                           argTypes[0].getName().endsWith(listenerName)) {
  982                           if (adds == null) {
  983                               adds = new HashMap();
  984                           }
  985                           adds.put(listenerName, method);
  986                       }
  987                   }
  988                   else if (name.startsWith(REMOVE_PREFIX) && argTypes.length == 1 &&
  989                            resultType == Void.TYPE &&
  990                            Introspector.isSubclass(argTypes[0], eventListenerType)) {
  991                       String listenerName = name.substring(6);
  992                       if (listenerName.length() > 0 &&
  993                           argTypes[0].getName().endsWith(listenerName)) {
  994                           if (removes == null) {
  995                               removes = new HashMap();
  996                           }
  997                           removes.put(listenerName, method);
  998                       }
  999                   }
 1000                   else if (name.startsWith(GET_PREFIX) && argTypes.length == 0 &&
 1001                            resultType.isArray() &&
 1002                            Introspector.isSubclass(resultType.getComponentType(),
 1003                                                    eventListenerType)) {
 1004                       String listenerName  = name.substring(3, name.length() - 1);
 1005                       if (listenerName.length() > 0 &&
 1006                           resultType.getComponentType().getName().endsWith(listenerName)) {
 1007                           if (gets == null) {
 1008                               gets = new HashMap();
 1009                           }
 1010                           gets.put(listenerName, method);
 1011                       }
 1012                   }
 1013               }
 1014   
 1015               if (adds != null && removes != null) {
 1016                   // Now look for matching addFooListener+removeFooListener pairs.
 1017                   // Bonus if there is a matching getFooListeners method as well.
 1018                   Iterator keys = adds.keySet().iterator();
 1019                   while (keys.hasNext()) {
 1020                       String listenerName = (String) keys.next();
 1021                       // Skip any "add" which doesn't have a matching "remove" or
 1022                       // a listener name that doesn't end with Listener
 1023                       if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
 1024                           continue;
 1025                       }
 1026                       String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
 1027                       Method addMethod = (Method)adds.get(listenerName);
 1028                       Method removeMethod = (Method)removes.get(listenerName);
 1029                       Method getMethod = null;
 1030                       if (gets != null) {
 1031                           getMethod = (Method)gets.get(listenerName);
 1032                       }
 1033                       Class argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0];
 1034   
 1035                       // generate a list of Method objects for each of the target methods:
 1036                       Method allMethods[] = getPublicDeclaredMethods(argType);
 1037                       List validMethods = new ArrayList(allMethods.length);
 1038                       for (int i = 0; i < allMethods.length; i++) {
 1039                           if (allMethods[i] == null) {
 1040                               continue;
 1041                           }
 1042   
 1043                           if (isEventHandler(allMethods[i])) {
 1044                               validMethods.add(allMethods[i]);
 1045                           }
 1046                       }
 1047                       Method[] methods = (Method[])validMethods.toArray(new Method[validMethods.size()]);
 1048   
 1049                       EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,
 1050                                                                       methods, addMethod,
 1051                                                                       removeMethod,
 1052                                                                       getMethod);
 1053   
 1054                       // If the adder method throws the TooManyListenersException then it
 1055                       // is a Unicast event source.
 1056                       if (throwsException(addMethod,
 1057                                           java.util.TooManyListenersException.class)) {
 1058                           esd.setUnicast(true);
 1059                       }
 1060                       addEvent(esd);
 1061                   }
 1062               } // if (adds != null ...
 1063           }
 1064           EventSetDescriptor[] result;
 1065           if (events.size() == 0) {
 1066               result = EMPTY_EVENTSETDESCRIPTORS;
 1067           } else {
 1068               // Allocate and populate the result array.
 1069               result = new EventSetDescriptor[events.size()];
 1070               result = (EventSetDescriptor[])events.values().toArray(result);
 1071   
 1072               // Set the default index.
 1073               if (defaultEventName != null) {
 1074                   for (int i = 0; i < result.length; i++) {
 1075                       if (defaultEventName.equals(result[i].getName())) {
 1076                           defaultEventIndex = i;
 1077                       }
 1078                   }
 1079               }
 1080           }
 1081           return result;
 1082       }
 1083   
 1084       private void addEvent(EventSetDescriptor esd) {
 1085           String key = esd.getName();
 1086           if (esd.getName().equals("propertyChange")) {
 1087               propertyChangeSource = true;
 1088           }
 1089           EventSetDescriptor old = (EventSetDescriptor)events.get(key);
 1090           if (old == null) {
 1091               events.put(key, esd);
 1092               return;
 1093           }
 1094           EventSetDescriptor composite = new EventSetDescriptor(old, esd);
 1095           events.put(key, composite);
 1096       }
 1097   
 1098       /**
 1099        * @return An array of MethodDescriptors describing the private
 1100        * methods supported by the target bean.
 1101        */
 1102       private MethodDescriptor[] getTargetMethodInfo() {
 1103           if (methods == null) {
 1104               methods = new HashMap(100);
 1105           }
 1106   
 1107           // Check if the bean has its own BeanInfo that will provide
 1108           // explicit information.
 1109           MethodDescriptor[] explicitMethods = null;
 1110           if (explicitBeanInfo != null) {
 1111               explicitMethods = explicitBeanInfo.getMethodDescriptors();
 1112           }
 1113   
 1114           if (explicitMethods == null && superBeanInfo != null) {
 1115               // We have no explicit BeanInfo methods.  Check with our parent.
 1116               MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors();
 1117               for (int i = 0 ; i < supers.length; i++) {
 1118                   addMethod(supers[i]);
 1119               }
 1120           }
 1121   
 1122           for (int i = 0; i < additionalBeanInfo.length; i++) {
 1123               MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors();
 1124               if (additional != null) {
 1125                   for (int j = 0 ; j < additional.length; j++) {
 1126                       addMethod(additional[j]);
 1127                   }
 1128               }
 1129           }
 1130   
 1131           if (explicitMethods != null) {
 1132               // Add the explicit explicitBeanInfo data to our results.
 1133               for (int i = 0 ; i < explicitMethods.length; i++) {
 1134                   addMethod(explicitMethods[i]);
 1135               }
 1136   
 1137           } else {
 1138   
 1139               // Apply some reflection to the current class.
 1140   
 1141               // First get an array of all the beans methods at this level
 1142               Method methodList[] = getPublicDeclaredMethods(beanClass);
 1143   
 1144               // Now analyze each method.
 1145               for (int i = 0; i < methodList.length; i++) {
 1146                   Method method = methodList[i];
 1147                   if (method == null) {
 1148                       continue;
 1149                   }
 1150                   MethodDescriptor md = new MethodDescriptor(method);
 1151                   addMethod(md);
 1152               }
 1153           }
 1154   
 1155           // Allocate and populate the result array.
 1156           MethodDescriptor result[] = new MethodDescriptor[methods.size()];
 1157           result = (MethodDescriptor[])methods.values().toArray(result);
 1158   
 1159           return result;
 1160       }
 1161   
 1162       private void addMethod(MethodDescriptor md) {
 1163           // We have to be careful here to distinguish method by both name
 1164           // and argument lists.
 1165           // This method gets called a *lot, so we try to be efficient.
 1166           String name = md.getName();
 1167   
 1168           MethodDescriptor old = (MethodDescriptor)methods.get(name);
 1169           if (old == null) {
 1170               // This is the common case.
 1171               methods.put(name, md);
 1172               return;
 1173           }
 1174   
 1175           // We have a collision on method names.  This is rare.
 1176   
 1177           // Check if old and md have the same type.
 1178           String[] p1 = md.getParamNames();
 1179           String[] p2 = old.getParamNames();
 1180   
 1181           boolean match = false;
 1182           if (p1.length == p2.length) {
 1183               match = true;
 1184               for (int i = 0; i < p1.length; i++) {
 1185                   if (p1[i] != p2[i]) {
 1186                       match = false;
 1187                       break;
 1188                   }
 1189               }
 1190           }
 1191           if (match) {
 1192               MethodDescriptor composite = new MethodDescriptor(old, md);
 1193               methods.put(name, composite);
 1194               return;
 1195           }
 1196   
 1197           // We have a collision on method names with different type signatures.
 1198           // This is very rare.
 1199   
 1200           String longKey = makeQualifiedMethodName(name, p1);
 1201           old = (MethodDescriptor)methods.get(longKey);
 1202           if (old == null) {
 1203               methods.put(longKey, md);
 1204               return;
 1205           }
 1206           MethodDescriptor composite = new MethodDescriptor(old, md);
 1207           methods.put(longKey, composite);
 1208       }
 1209   
 1210       /**
 1211        * Creates a key for a method in a method cache.
 1212        */
 1213       private static String makeQualifiedMethodName(String name, String[] params) {
 1214           StringBuffer sb = new StringBuffer(name);
 1215           sb.append('=');
 1216           for (int i = 0; i < params.length; i++) {
 1217               sb.append(':');
 1218               sb.append(params[i]);
 1219           }
 1220           return sb.toString();
 1221       }
 1222   
 1223       private int getTargetDefaultEventIndex() {
 1224           return defaultEventIndex;
 1225       }
 1226   
 1227       private int getTargetDefaultPropertyIndex() {
 1228           return defaultPropertyIndex;
 1229       }
 1230   
 1231       private BeanDescriptor getTargetBeanDescriptor() {
 1232           // Use explicit info, if available,
 1233           if (explicitBeanInfo != null) {
 1234               BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
 1235               if (bd != null) {
 1236                   return (bd);
 1237               }
 1238           }
 1239           // OK, fabricate a default BeanDescriptor.
 1240           return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass));
 1241       }
 1242   
 1243       private static Class<?> findCustomizerClass(Class<?> type) {
 1244           String name = type.getName() + "Customizer";
 1245           try {
 1246               type = ClassFinder.findClass(name, type.getClassLoader());
 1247               // Each customizer should inherit java.awt.Component and implement java.beans.Customizer
 1248               // according to the section 9.3 of JavaBeans&trade; specification
 1249               if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) {
 1250                   return type;
 1251               }
 1252           }
 1253           catch (Exception exception) {
 1254               // ignore any exceptions
 1255           }
 1256           return null;
 1257       }
 1258   
 1259       private boolean isEventHandler(Method m) {
 1260           // We assume that a method is an event handler if it has a single
 1261           // argument, whose type inherit from java.util.Event.
 1262           Class argTypes[] = FeatureDescriptor.getParameterTypes(beanClass, m);
 1263           if (argTypes.length != 1) {
 1264               return false;
 1265           }
 1266           return isSubclass(argTypes[0], EventObject.class);
 1267       }
 1268   
 1269       /*
 1270        * Internal method to return *public* methods within a class.
 1271        */
 1272       private static Method[] getPublicDeclaredMethods(Class clz) {
 1273           // Looking up Class.getDeclaredMethods is relatively expensive,
 1274           // so we cache the results.
 1275           if (!ReflectUtil.isPackageAccessible(clz)) {
 1276               return new Method[0];
 1277           }
 1278           synchronized (BEANINFO_CACHE) {
 1279               Method[] result = declaredMethodCache.get(clz);
 1280               if (result == null) {
 1281                   result = clz.getMethods();
 1282                   for (int i = 0; i < result.length; i++) {
 1283                       Method method = result[i];
 1284                       if (!method.getDeclaringClass().equals(clz)) {
 1285                           result[i] = null;
 1286                       }
 1287                   }
 1288                   declaredMethodCache.put(clz, result);
 1289               }
 1290               return result;
 1291           }
 1292       }
 1293   
 1294       //======================================================================
 1295       // Package private support methods.
 1296       //======================================================================
 1297   
 1298       /**
 1299        * Internal support for finding a target methodName with a given
 1300        * parameter list on a given class.
 1301        */
 1302       private static Method internalFindMethod(Class start, String methodName,
 1303                                                    int argCount, Class args[]) {
 1304           // For overriden methods we need to find the most derived version.
 1305           // So we start with the given class and walk up the superclass chain.
 1306   
 1307           Method method = null;
 1308   
 1309           for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
 1310               Method methods[] = getPublicDeclaredMethods(cl);
 1311               for (int i = 0; i < methods.length; i++) {
 1312                   method = methods[i];
 1313                   if (method == null) {
 1314                       continue;
 1315                   }
 1316   
 1317                   // make sure method signature matches.
 1318                   Class params[] = FeatureDescriptor.getParameterTypes(start, method);
 1319                   if (method.getName().equals(methodName) &&
 1320                       params.length == argCount) {
 1321                       if (args != null) {
 1322                           boolean different = false;
 1323                           if (argCount > 0) {
 1324                               for (int j = 0; j < argCount; j++) {
 1325                                   if (params[j] != args[j]) {
 1326                                       different = true;
 1327                                       continue;
 1328                                   }
 1329                               }
 1330                               if (different) {
 1331                                   continue;
 1332                               }
 1333                           }
 1334                       }
 1335                       return method;
 1336                   }
 1337               }
 1338           }
 1339           method = null;
 1340   
 1341           // Now check any inherited interfaces.  This is necessary both when
 1342           // the argument class is itself an interface, and when the argument
 1343           // class is an abstract class.
 1344           Class ifcs[] = start.getInterfaces();
 1345           for (int i = 0 ; i < ifcs.length; i++) {
 1346               // Note: The original implementation had both methods calling
 1347               // the 3 arg method. This is preserved but perhaps it should
 1348               // pass the args array instead of null.
 1349               method = internalFindMethod(ifcs[i], methodName, argCount, null);
 1350               if (method != null) {
 1351                   break;
 1352               }
 1353           }
 1354           return method;
 1355       }
 1356   
 1357       /**
 1358        * Find a target methodName on a given class.
 1359        */
 1360       static Method findMethod(Class cls, String methodName, int argCount) {
 1361           return findMethod(cls, methodName, argCount, null);
 1362       }
 1363   
 1364       /**
 1365        * Find a target methodName with specific parameter list on a given class.
 1366        * <p>
 1367        * Used in the contructors of the EventSetDescriptor,
 1368        * PropertyDescriptor and the IndexedPropertyDescriptor.
 1369        * <p>
 1370        * @param cls The Class object on which to retrieve the method.
 1371        * @param methodName Name of the method.
 1372        * @param argCount Number of arguments for the desired method.
 1373        * @param args Array of argument types for the method.
 1374        * @return the method or null if not found
 1375        */
 1376       static Method findMethod(Class cls, String methodName, int argCount,
 1377                                Class args[]) {
 1378           if (methodName == null) {
 1379               return null;
 1380           }
 1381           return internalFindMethod(cls, methodName, argCount, args);
 1382       }
 1383   
 1384       /**
 1385        * Return true if class a is either equivalent to class b, or
 1386        * if class a is a subclass of class b, i.e. if a either "extends"
 1387        * or "implements" b.
 1388        * Note tht either or both "Class" objects may represent interfaces.
 1389        */
 1390       static  boolean isSubclass(Class a, Class b) {
 1391           // We rely on the fact that for any given java class or
 1392           // primtitive type there is a unqiue Class object, so
 1393           // we can use object equivalence in the comparisons.
 1394           if (a == b) {
 1395               return true;
 1396           }
 1397           if (a == null || b == null) {
 1398               return false;
 1399           }
 1400           for (Class x = a; x != null; x = x.getSuperclass()) {
 1401               if (x == b) {
 1402                   return true;
 1403               }
 1404               if (b.isInterface()) {
 1405                   Class interfaces[] = x.getInterfaces();
 1406                   for (int i = 0; i < interfaces.length; i++) {
 1407                       if (isSubclass(interfaces[i], b)) {
 1408                           return true;
 1409                       }
 1410                   }
 1411               }
 1412           }
 1413           return false;
 1414       }
 1415   
 1416       /**
 1417        * Return true iff the given method throws the given exception.
 1418        */
 1419       private boolean throwsException(Method method, Class exception) {
 1420           Class exs[] = method.getExceptionTypes();
 1421           for (int i = 0; i < exs.length; i++) {
 1422               if (exs[i] == exception) {
 1423                   return true;
 1424               }
 1425           }
 1426           return false;
 1427       }
 1428   
 1429       private static BeanInfoFinder getFinder() {
 1430           AppContext context = AppContext.getAppContext();
 1431           Object object = context.get(FINDER_KEY);
 1432           if (object instanceof BeanInfoFinder) {
 1433               return (BeanInfoFinder) object;
 1434           }
 1435           BeanInfoFinder finder = new BeanInfoFinder();
 1436           context.put(FINDER_KEY, finder);
 1437           return finder;
 1438       }
 1439   
 1440       /**
 1441        * Try to create an instance of a named class.
 1442        * First try the classloader of "sibling", then try the system
 1443        * classloader then the class loader of the current Thread.
 1444        */
 1445       static Object instantiate(Class sibling, String className)
 1446                    throws InstantiationException, IllegalAccessException,
 1447                                                   ClassNotFoundException {
 1448           // First check with sibling's classloader (if any).
 1449           ClassLoader cl = sibling.getClassLoader();
 1450           Class cls = ClassFinder.findClass(className, cl);
 1451           return cls.newInstance();
 1452       }
 1453   
 1454   } // end class Introspector
 1455   
 1456   //===========================================================================
 1457   
 1458   /**
 1459    * Package private implementation support class for Introspector's
 1460    * internal use.
 1461    * <p>
 1462    * Mostly this is used as a placeholder for the descriptors.
 1463    */
 1464   
 1465   class GenericBeanInfo extends SimpleBeanInfo {
 1466   
 1467       private BeanDescriptor beanDescriptor;
 1468       private EventSetDescriptor[] events;
 1469       private int defaultEvent;
 1470       private PropertyDescriptor[] properties;
 1471       private int defaultProperty;
 1472       private MethodDescriptor[] methods;
 1473       private final Reference<BeanInfo> targetBeanInfoRef;
 1474   
 1475       public GenericBeanInfo(BeanDescriptor beanDescriptor,
 1476                   EventSetDescriptor[] events, int defaultEvent,
 1477                   PropertyDescriptor[] properties, int defaultProperty,
 1478                   MethodDescriptor[] methods, BeanInfo targetBeanInfo) {
 1479           this.beanDescriptor = beanDescriptor;
 1480           this.events = events;
 1481           this.defaultEvent = defaultEvent;
 1482           this.properties = properties;
 1483           this.defaultProperty = defaultProperty;
 1484           this.methods = methods;
 1485           this.targetBeanInfoRef = new SoftReference<BeanInfo>(targetBeanInfo);
 1486       }
 1487   
 1488       /**
 1489        * Package-private dup constructor
 1490        * This must isolate the new object from any changes to the old object.
 1491        */
 1492       GenericBeanInfo(GenericBeanInfo old) {
 1493   
 1494           beanDescriptor = new BeanDescriptor(old.beanDescriptor);
 1495           if (old.events != null) {
 1496               int len = old.events.length;
 1497               events = new EventSetDescriptor[len];
 1498               for (int i = 0; i < len; i++) {
 1499                   events[i] = new EventSetDescriptor(old.events[i]);
 1500               }
 1501           }
 1502           defaultEvent = old.defaultEvent;
 1503           if (old.properties != null) {
 1504               int len = old.properties.length;
 1505               properties = new PropertyDescriptor[len];
 1506               for (int i = 0; i < len; i++) {
 1507                   PropertyDescriptor oldp = old.properties[i];
 1508                   if (oldp instanceof IndexedPropertyDescriptor) {
 1509                       properties[i] = new IndexedPropertyDescriptor(
 1510                                           (IndexedPropertyDescriptor) oldp);
 1511                   } else {
 1512                       properties[i] = new PropertyDescriptor(oldp);
 1513                   }
 1514               }
 1515           }
 1516           defaultProperty = old.defaultProperty;
 1517           if (old.methods != null) {
 1518               int len = old.methods.length;
 1519               methods = new MethodDescriptor[len];
 1520               for (int i = 0; i < len; i++) {
 1521                   methods[i] = new MethodDescriptor(old.methods[i]);
 1522               }
 1523           }
 1524           this.targetBeanInfoRef = old.targetBeanInfoRef;
 1525       }
 1526   
 1527       public PropertyDescriptor[] getPropertyDescriptors() {
 1528           return properties;
 1529       }
 1530   
 1531       public int getDefaultPropertyIndex() {
 1532           return defaultProperty;
 1533       }
 1534   
 1535       public EventSetDescriptor[] getEventSetDescriptors() {
 1536           return events;
 1537       }
 1538   
 1539       public int getDefaultEventIndex() {
 1540           return defaultEvent;
 1541       }
 1542   
 1543       public MethodDescriptor[] getMethodDescriptors() {
 1544           return methods;
 1545       }
 1546   
 1547       public BeanDescriptor getBeanDescriptor() {
 1548           return beanDescriptor;
 1549       }
 1550   
 1551       public java.awt.Image getIcon(int iconKind) {
 1552           BeanInfo targetBeanInfo = this.targetBeanInfoRef.get();
 1553           if (targetBeanInfo != null) {
 1554               return targetBeanInfo.getIcon(iconKind);
 1555           }
 1556           return super.getIcon(iconKind);
 1557       }
 1558   }

Home » openjdk-7 » java » beans » [javadoc | source]