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

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