0N/A/*
3909N/A * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage java.beans;
0N/A
5235N/Aimport com.sun.beans.TypeResolver;
1987N/Aimport com.sun.beans.WeakCache;
0N/Aimport com.sun.beans.finder.ClassFinder;
0N/A
3083N/Aimport java.awt.Component;
3083N/A
2149N/Aimport java.lang.ref.Reference;
2149N/Aimport java.lang.ref.SoftReference;
0N/Aimport java.lang.reflect.Method;
0N/Aimport java.lang.reflect.Modifier;
5235N/Aimport java.lang.reflect.Type;
0N/A
0N/Aimport java.util.Map;
0N/Aimport java.util.ArrayList;
0N/Aimport java.util.HashMap;
0N/Aimport java.util.Iterator;
0N/Aimport java.util.EventListener;
3083N/Aimport java.util.EventObject;
0N/Aimport java.util.List;
0N/Aimport java.util.TreeMap;
1405N/A
0N/Aimport sun.reflect.misc.ReflectUtil;
0N/A
0N/A/**
0N/A * The Introspector class provides a standard way for tools to learn about
0N/A * the properties, events, and methods supported by a target Java Bean.
0N/A * <p>
0N/A * For each of those three kinds of information, the Introspector will
0N/A * separately analyze the bean's class and superclasses looking for
0N/A * either explicit or implicit information and use that information to
0N/A * build a BeanInfo object that comprehensively describes the target bean.
0N/A * <p>
0N/A * For each class "Foo", explicit information may be available if there exists
0N/A * a corresponding "FooBeanInfo" class that provides a non-null value when
0N/A * queried for the information. We first look for the BeanInfo class by
0N/A * taking the full package-qualified name of the target bean class and
0N/A * appending "BeanInfo" to form a new class name. If this fails, then
0N/A * we take the final classname component of this name, and look for that
0N/A * class in each of the packages specified in the BeanInfo package search
0N/A * path.
0N/A * <p>
0N/A * Thus for a class such as "sun.xyz.OurButton" we would first look for a
0N/A * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd
0N/A * look in each package in the BeanInfo search path for an OurButtonBeanInfo
0N/A * class. With the default search path, this would mean looking for
0N/A * "sun.beans.infos.OurButtonBeanInfo".
0N/A * <p>
0N/A * If a class provides explicit BeanInfo about itself then we add that to
0N/A * the BeanInfo information we obtained from analyzing any derived classes,
0N/A * but we regard the explicit information as being definitive for the current
0N/A * class and its base classes, and do not proceed any further up the superclass
0N/A * chain.
0N/A * <p>
0N/A * If we don't find explicit BeanInfo on a class, we use low-level
0N/A * reflection to study the methods of the class and apply standard design
0N/A * patterns to identify property accessors, event sources, or public
0N/A * methods. We then proceed to analyze the class's superclass and add
0N/A * in the information from it (and possibly on up the superclass chain).
0N/A * <p>
0N/A * For more information about introspection and design patterns, please
0N/A * consult the
3731N/A * <a href="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans&trade; specification</a>.
0N/A */
0N/A
0N/Apublic class Introspector {
0N/A
0N/A // Flags that can be used to control getBeanInfo:
0N/A public final static int USE_ALL_BEANINFO = 1;
0N/A public final static int IGNORE_IMMEDIATE_BEANINFO = 2;
0N/A public final static int IGNORE_ALL_BEANINFO = 3;
0N/A
0N/A // Static Caches to speed up introspection.
4373N/A private static final WeakCache<Class<?>, Method[]> declaredMethodCache = new WeakCache<>();
0N/A
0N/A private Class beanClass;
0N/A private BeanInfo explicitBeanInfo;
0N/A private BeanInfo superBeanInfo;
0N/A private BeanInfo additionalBeanInfo[];
0N/A
0N/A private boolean propertyChangeSource = false;
0N/A private static Class eventListenerType = EventListener.class;
0N/A
0N/A // These should be removed.
0N/A private String defaultEventName;
0N/A private String defaultPropertyName;
0N/A private int defaultEventIndex = -1;
0N/A private int defaultPropertyIndex = -1;
0N/A
0N/A // Methods maps from Method objects to MethodDescriptors
0N/A private Map methods;
0N/A
0N/A // properties maps from String names to PropertyDescriptors
0N/A private Map properties;
0N/A
0N/A // events maps from String names to EventSetDescriptors
0N/A private Map events;
0N/A
0N/A private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];
0N/A
0N/A static final String ADD_PREFIX = "add";
0N/A static final String REMOVE_PREFIX = "remove";
0N/A static final String GET_PREFIX = "get";
0N/A static final String SET_PREFIX = "set";
0N/A static final String IS_PREFIX = "is";
0N/A
0N/A //======================================================================
0N/A // Public methods
0N/A //======================================================================
0N/A
0N/A /**
0N/A * Introspect on a Java Bean and learn about all its properties, exposed
0N/A * methods, and events.
0N/A * <p>
0N/A * If the BeanInfo class for a Java Bean has been previously Introspected
0N/A * then the BeanInfo class is retrieved from the BeanInfo cache.
0N/A *
0N/A * @param beanClass The bean class to be analyzed.
0N/A * @return A BeanInfo object describing the target bean.
0N/A * @exception IntrospectionException if an exception occurs during
0N/A * introspection.
0N/A * @see #flushCaches
0N/A * @see #flushFromCaches
0N/A */
0N/A public static BeanInfo getBeanInfo(Class<?> beanClass)
0N/A throws IntrospectionException
0N/A {
0N/A if (!ReflectUtil.isPackageAccessible(beanClass)) {
0N/A return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
0N/A }
4373N/A ThreadGroupContext context = ThreadGroupContext.getContext();
2535N/A BeanInfo beanInfo;
4373N/A synchronized (declaredMethodCache) {
4373N/A beanInfo = context.getBeanInfo(beanClass);
2535N/A }
2535N/A if (beanInfo == null) {
2535N/A beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
4373N/A synchronized (declaredMethodCache) {
4373N/A context.putBeanInfo(beanClass, beanInfo);
1987N/A }
1491N/A }
2535N/A return beanInfo;
0N/A }
0N/A
0N/A /**
0N/A * Introspect on a Java bean and learn about all its properties, exposed
0N/A * methods, and events, subject to some control flags.
0N/A * <p>
0N/A * If the BeanInfo class for a Java Bean has been previously Introspected
0N/A * based on the same arguments then the BeanInfo class is retrieved
0N/A * from the BeanInfo cache.
0N/A *
0N/A * @param beanClass The bean class to be analyzed.
0N/A * @param flags Flags to control the introspection.
0N/A * If flags == USE_ALL_BEANINFO then we use all of the BeanInfo
0N/A * classes we can discover.
0N/A * If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any
0N/A * BeanInfo associated with the specified beanClass.
0N/A * If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo
0N/A * associated with the specified beanClass or any of its
0N/A * parent classes.
0N/A * @return A BeanInfo object describing the target bean.
0N/A * @exception IntrospectionException if an exception occurs during
0N/A * introspection.
0N/A */
0N/A public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
0N/A throws IntrospectionException {
0N/A return getBeanInfo(beanClass, null, flags);
0N/A }
0N/A
0N/A /**
0N/A * Introspect on a Java bean and learn all about its properties, exposed
0N/A * methods, below a given "stop" point.
0N/A * <p>
0N/A * If the BeanInfo class for a Java Bean has been previously Introspected
0N/A * based on the same arguments, then the BeanInfo class is retrieved
0N/A * from the BeanInfo cache.
0N/A *
0N/A * @param beanClass The bean class to be analyzed.
0N/A * @param stopClass The baseclass at which to stop the analysis. Any
0N/A * methods/properties/events in the stopClass or in its baseclasses
0N/A * will be ignored in the analysis.
0N/A * @exception IntrospectionException if an exception occurs during
0N/A * introspection.
0N/A */
0N/A public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
0N/A throws IntrospectionException {
0N/A return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
0N/A }
0N/A
0N/A /**
0N/A * Introspect on a Java Bean and learn about all its properties,
0N/A * exposed methods and events, below a given {@code stopClass} point
0N/A * subject to some control {@code flags}.
0N/A * <dl>
0N/A * <dt>USE_ALL_BEANINFO</dt>
0N/A * <dd>Any BeanInfo that can be discovered will be used.</dd>
0N/A * <dt>IGNORE_IMMEDIATE_BEANINFO</dt>
0N/A * <dd>Any BeanInfo associated with the specified {@code beanClass} will be ignored.</dd>
0N/A * <dt>IGNORE_ALL_BEANINFO</dt>
0N/A * <dd>Any BeanInfo associated with the specified {@code beanClass}
0N/A * or any of its parent classes will be ignored.</dd>
0N/A * </dl>
0N/A * Any methods/properties/events in the {@code stopClass}
0N/A * or in its parent classes will be ignored in the analysis.
0N/A * <p>
0N/A * If the BeanInfo class for a Java Bean has been
0N/A * previously introspected based on the same arguments then
0N/A * the BeanInfo class is retrieved from the BeanInfo cache.
0N/A *
0N/A * @param beanClass the bean class to be analyzed
0N/A * @param stopClass the parent class at which to stop the analysis
0N/A * @param flags flags to control the introspection
0N/A * @return a BeanInfo object describing the target bean
0N/A * @exception IntrospectionException if an exception occurs during introspection
0N/A *
0N/A * @since 1.7
0N/A */
0N/A public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass,
0N/A int flags) throws IntrospectionException {
0N/A BeanInfo bi;
0N/A if (stopClass == null && flags == USE_ALL_BEANINFO) {
0N/A // Same parameters to take advantage of caching.
0N/A bi = getBeanInfo(beanClass);
0N/A } else {
0N/A bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();
0N/A }
0N/A return bi;
0N/A
0N/A // Old behaviour: Make an independent copy of the BeanInfo.
0N/A //return new GenericBeanInfo(bi);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Utility method to take a string and convert it to normal Java variable
0N/A * name capitalization. This normally means converting the first
0N/A * character from upper case to lower case, but in the (unusual) special
0N/A * case when there is more than one character and both the first and
0N/A * second characters are upper case, we leave it alone.
0N/A * <p>
0N/A * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
0N/A * as "URL".
0N/A *
0N/A * @param name The string to be decapitalized.
0N/A * @return The decapitalized version of the string.
0N/A */
0N/A public static String decapitalize(String name) {
0N/A if (name == null || name.length() == 0) {
0N/A return name;
0N/A }
0N/A if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
0N/A Character.isUpperCase(name.charAt(0))){
0N/A return name;
0N/A }
0N/A char chars[] = name.toCharArray();
0N/A chars[0] = Character.toLowerCase(chars[0]);
0N/A return new String(chars);
0N/A }
0N/A
0N/A /**
0N/A * Gets the list of package names that will be used for
0N/A * finding BeanInfo classes.
0N/A *
0N/A * @return The array of package names that will be searched in
0N/A * order to find BeanInfo classes. The default value
0N/A * for this array is implementation-dependent; e.g.
0N/A * Sun implementation initially sets to {"sun.beans.infos"}.
0N/A */
0N/A
1405N/A public static String[] getBeanInfoSearchPath() {
4373N/A return ThreadGroupContext.getContext().getBeanInfoFinder().getPackages();
0N/A }
0N/A
0N/A /**
0N/A * Change the list of package names that will be used for
0N/A * finding BeanInfo classes. The behaviour of
0N/A * this method is undefined if parameter path
0N/A * is null.
0N/A *
0N/A * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>
0N/A * method is called. This could result in a SecurityException.
0N/A *
0N/A * @param path Array of package names.
0N/A * @exception SecurityException if a security manager exists and its
0N/A * <code>checkPropertiesAccess</code> method doesn't allow setting
0N/A * of system properties.
0N/A * @see SecurityManager#checkPropertiesAccess
0N/A */
0N/A
1405N/A public static void setBeanInfoSearchPath(String[] path) {
0N/A SecurityManager sm = System.getSecurityManager();
0N/A if (sm != null) {
0N/A sm.checkPropertiesAccess();
0N/A }
4373N/A ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path);
0N/A }
0N/A
0N/A
0N/A /**
0N/A * Flush all of the Introspector's internal caches. This method is
0N/A * not normally required. It is normally only needed by advanced
0N/A * tools that update existing "Class" objects in-place and need
0N/A * to make the Introspector re-analyze existing Class objects.
0N/A */
0N/A
0N/A public static void flushCaches() {
4373N/A synchronized (declaredMethodCache) {
4373N/A ThreadGroupContext.getContext().clearBeanInfoCache();
1987N/A declaredMethodCache.clear();
1491N/A }
0N/A }
0N/A
0N/A /**
0N/A * Flush the Introspector's internal cached information for a given class.
0N/A * This method is not normally required. It is normally only needed
0N/A * by advanced tools that update existing "Class" objects in-place
0N/A * and need to make the Introspector re-analyze an existing Class object.
0N/A *
0N/A * Note that only the direct state associated with the target Class
0N/A * object is flushed. We do not flush state for other Class objects
0N/A * with the same name, nor do we flush state for any related Class
0N/A * objects (such as subclasses), even though their state may include
0N/A * information indirectly obtained from the target Class object.
0N/A *
0N/A * @param clz Class object to be flushed.
0N/A * @throws NullPointerException If the Class object is null.
0N/A */
0N/A public static void flushFromCaches(Class<?> clz) {
0N/A if (clz == null) {
0N/A throw new NullPointerException();
0N/A }
4373N/A synchronized (declaredMethodCache) {
4373N/A ThreadGroupContext.getContext().removeBeanInfo(clz);
1987N/A declaredMethodCache.put(clz, null);
1491N/A }
0N/A }
0N/A
0N/A //======================================================================
0N/A // Private implementation methods
0N/A //======================================================================
0N/A
0N/A private Introspector(Class beanClass, Class stopClass, int flags)
0N/A throws IntrospectionException {
0N/A this.beanClass = beanClass;
0N/A
0N/A // Check stopClass is a superClass of startClass.
0N/A if (stopClass != null) {
0N/A boolean isSuper = false;
0N/A for (Class c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {
0N/A if (c == stopClass) {
0N/A isSuper = true;
0N/A }
0N/A }
0N/A if (!isSuper) {
0N/A throw new IntrospectionException(stopClass.getName() + " not superclass of " +
0N/A beanClass.getName());
0N/A }
0N/A }
0N/A
0N/A if (flags == USE_ALL_BEANINFO) {
0N/A explicitBeanInfo = findExplicitBeanInfo(beanClass);
0N/A }
0N/A
0N/A Class superClass = beanClass.getSuperclass();
0N/A if (superClass != stopClass) {
0N/A int newFlags = flags;
0N/A if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
0N/A newFlags = USE_ALL_BEANINFO;
0N/A }
0N/A superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
0N/A }
0N/A if (explicitBeanInfo != null) {
0N/A additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
0N/A }
0N/A if (additionalBeanInfo == null) {
0N/A additionalBeanInfo = new BeanInfo[0];
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Constructs a GenericBeanInfo class from the state of the Introspector
0N/A */
0N/A private BeanInfo getBeanInfo() throws IntrospectionException {
0N/A
0N/A // the evaluation order here is import, as we evaluate the
0N/A // event sets and locate PropertyChangeListeners before we
0N/A // look for properties.
0N/A BeanDescriptor bd = getTargetBeanDescriptor();
0N/A MethodDescriptor mds[] = getTargetMethodInfo();
0N/A EventSetDescriptor esds[] = getTargetEventInfo();
0N/A PropertyDescriptor pds[] = getTargetPropertyInfo();
0N/A
0N/A int defaultEvent = getTargetDefaultEventIndex();
0N/A int defaultProperty = getTargetDefaultPropertyIndex();
0N/A
0N/A return new GenericBeanInfo(bd, esds, defaultEvent, pds,
0N/A defaultProperty, mds, explicitBeanInfo);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Looks for an explicit BeanInfo class that corresponds to the Class.
0N/A * First it looks in the existing package that the Class is defined in,
0N/A * then it checks to see if the class is its own BeanInfo. Finally,
0N/A * the BeanInfo search path is prepended to the class and searched.
0N/A *
1405N/A * @param beanClass the class type of the bean
0N/A * @return Instance of an explicit BeanInfo class or null if one isn't found.
0N/A */
1405N/A private static BeanInfo findExplicitBeanInfo(Class beanClass) {
4373N/A return ThreadGroupContext.getContext().getBeanInfoFinder().find(beanClass);
0N/A }
0N/A
0N/A /**
0N/A * @return An array of PropertyDescriptors describing the editable
0N/A * properties supported by the target bean.
0N/A */
0N/A
0N/A private PropertyDescriptor[] getTargetPropertyInfo() {
0N/A
0N/A // Check if the bean has its own BeanInfo that will provide
0N/A // explicit information.
0N/A PropertyDescriptor[] explicitProperties = null;
0N/A if (explicitBeanInfo != null) {
0N/A explicitProperties = getPropertyDescriptors(this.explicitBeanInfo);
0N/A }
0N/A
0N/A if (explicitProperties == null && superBeanInfo != null) {
0N/A // We have no explicit BeanInfo properties. Check with our parent.
0N/A addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo));
0N/A }
0N/A
0N/A for (int i = 0; i < additionalBeanInfo.length; i++) {
0N/A addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors());
0N/A }
0N/A
0N/A if (explicitProperties != null) {
0N/A // Add the explicit BeanInfo data to our results.
0N/A addPropertyDescriptors(explicitProperties);
0N/A
0N/A } else {
0N/A
0N/A // Apply some reflection to the current class.
0N/A
0N/A // First get an array of all the public methods at this level
0N/A Method methodList[] = getPublicDeclaredMethods(beanClass);
0N/A
0N/A // Now analyze each method.
0N/A for (int i = 0; i < methodList.length; i++) {
0N/A Method method = methodList[i];
4626N/A if (method == null) {
0N/A continue;
0N/A }
0N/A // skip static methods.
0N/A int mods = method.getModifiers();
0N/A if (Modifier.isStatic(mods)) {
0N/A continue;
0N/A }
0N/A String name = method.getName();
0N/A Class argTypes[] = method.getParameterTypes();
0N/A Class resultType = method.getReturnType();
0N/A int argCount = argTypes.length;
0N/A PropertyDescriptor pd = null;
0N/A
0N/A if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
0N/A // Optimization. Don't bother with invalid propertyNames.
0N/A continue;
0N/A }
0N/A
0N/A try {
0N/A
0N/A if (argCount == 0) {
0N/A if (name.startsWith(GET_PREFIX)) {
0N/A // Simple getter
0N/A pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
0N/A } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
0N/A // Boolean getter
0N/A pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
0N/A }
0N/A } else if (argCount == 1) {
1407N/A if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
0N/A pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
1407N/A } else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
0N/A // Simple setter
0N/A pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
0N/A if (throwsException(method, PropertyVetoException.class)) {
0N/A pd.setConstrained(true);
0N/A }
0N/A }
0N/A } else if (argCount == 2) {
1407N/A if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {
0N/A pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);
0N/A if (throwsException(method, PropertyVetoException.class)) {
0N/A pd.setConstrained(true);
0N/A }
0N/A }
0N/A }
0N/A } catch (IntrospectionException ex) {
0N/A // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
0N/A // constructor fins that the method violates details of the deisgn
0N/A // pattern, e.g. by having an empty name, or a getter returning
0N/A // void , or whatever.
0N/A pd = null;
0N/A }
0N/A
0N/A if (pd != null) {
0N/A // If this class or one of its base classes is a PropertyChange
0N/A // source, then we assume that any properties we discover are "bound".
0N/A if (propertyChangeSource) {
0N/A pd.setBound(true);
0N/A }
0N/A addPropertyDescriptor(pd);
0N/A }
0N/A }
0N/A }
0N/A processPropertyDescriptors();
0N/A
0N/A // Allocate and populate the result array.
0N/A PropertyDescriptor result[] = new PropertyDescriptor[properties.size()];
0N/A result = (PropertyDescriptor[])properties.values().toArray(result);
0N/A
0N/A // Set the default index.
0N/A if (defaultPropertyName != null) {
0N/A for (int i = 0; i < result.length; i++) {
0N/A if (defaultPropertyName.equals(result[i].getName())) {
0N/A defaultPropertyIndex = i;
0N/A }
0N/A }
0N/A }
0N/A
0N/A return result;
0N/A }
0N/A
0N/A private HashMap pdStore = new HashMap();
0N/A
0N/A /**
0N/A * Adds the property descriptor to the list store.
0N/A */
0N/A private void addPropertyDescriptor(PropertyDescriptor pd) {
0N/A String propName = pd.getName();
0N/A List list = (List)pdStore.get(propName);
0N/A if (list == null) {
0N/A list = new ArrayList();
0N/A pdStore.put(propName, list);
0N/A }
0N/A if (this.beanClass != pd.getClass0()) {
0N/A // replace existing property descriptor
0N/A // only if we have types to resolve
0N/A // in the context of this.beanClass
5587N/A Method read = pd.getReadMethod();
5587N/A Method write = pd.getWriteMethod();
5587N/A boolean cls = true;
5587N/A if (read != null) cls = cls && read.getGenericReturnType() instanceof Class;
5587N/A if (write != null) cls = cls && write.getGenericParameterTypes()[0] instanceof Class;
5587N/A if (pd instanceof IndexedPropertyDescriptor) {
5587N/A IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
5587N/A Method readI = ipd.getIndexedReadMethod();
5587N/A Method writeI = ipd.getIndexedWriteMethod();
5587N/A if (readI != null) cls = cls && readI.getGenericReturnType() instanceof Class;
5587N/A if (writeI != null) cls = cls && writeI.getGenericParameterTypes()[1] instanceof Class;
5587N/A if (!cls) {
5327N/A pd = new IndexedPropertyDescriptor(ipd);
5587N/A pd.updateGenericsFor(this.beanClass);
0N/A }
5587N/A }
5587N/A else if (!cls) {
5587N/A pd = new PropertyDescriptor(pd);
5587N/A pd.updateGenericsFor(this.beanClass);
0N/A }
0N/A }
0N/A list.add(pd);
0N/A }
0N/A
0N/A private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
0N/A if (descriptors != null) {
0N/A for (PropertyDescriptor descriptor : descriptors) {
0N/A addPropertyDescriptor(descriptor);
0N/A }
0N/A }
0N/A }
0N/A
0N/A private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {
0N/A PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
0N/A int index = info.getDefaultPropertyIndex();
0N/A if ((0 <= index) && (index < descriptors.length)) {
0N/A this.defaultPropertyName = descriptors[index].getName();
0N/A }
0N/A return descriptors;
0N/A }
0N/A
0N/A /**
0N/A * Populates the property descriptor table by merging the
0N/A * lists of Property descriptors.
0N/A */
0N/A private void processPropertyDescriptors() {
0N/A if (properties == null) {
0N/A properties = new TreeMap();
0N/A }
0N/A
0N/A List list;
0N/A
0N/A PropertyDescriptor pd, gpd, spd;
0N/A IndexedPropertyDescriptor ipd, igpd, ispd;
0N/A
0N/A Iterator it = pdStore.values().iterator();
0N/A while (it.hasNext()) {
0N/A pd = null; gpd = null; spd = null;
0N/A ipd = null; igpd = null; ispd = null;
0N/A
0N/A list = (List)it.next();
0N/A
0N/A // First pass. Find the latest getter method. Merge properties
0N/A // of previous getter methods.
0N/A for (int i = 0; i < list.size(); i++) {
0N/A pd = (PropertyDescriptor)list.get(i);
0N/A if (pd instanceof IndexedPropertyDescriptor) {
0N/A ipd = (IndexedPropertyDescriptor)pd;
0N/A if (ipd.getIndexedReadMethod() != null) {
0N/A if (igpd != null) {
0N/A igpd = new IndexedPropertyDescriptor(igpd, ipd);
0N/A } else {
0N/A igpd = ipd;
0N/A }
0N/A }
0N/A } else {
0N/A if (pd.getReadMethod() != null) {
0N/A if (gpd != null) {
0N/A // Don't replace the existing read
0N/A // method if it starts with "is"
0N/A Method method = gpd.getReadMethod();
0N/A if (!method.getName().startsWith(IS_PREFIX)) {
0N/A gpd = new PropertyDescriptor(gpd, pd);
0N/A }
0N/A } else {
0N/A gpd = pd;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // Second pass. Find the latest setter method which
0N/A // has the same type as the getter method.
0N/A for (int i = 0; i < list.size(); i++) {
0N/A pd = (PropertyDescriptor)list.get(i);
0N/A if (pd instanceof IndexedPropertyDescriptor) {
0N/A ipd = (IndexedPropertyDescriptor)pd;
0N/A if (ipd.getIndexedWriteMethod() != null) {
0N/A if (igpd != null) {
0N/A if (igpd.getIndexedPropertyType()
0N/A == ipd.getIndexedPropertyType()) {
0N/A if (ispd != null) {
0N/A ispd = new IndexedPropertyDescriptor(ispd, ipd);
0N/A } else {
0N/A ispd = ipd;
0N/A }
0N/A }
0N/A } else {
0N/A if (ispd != null) {
0N/A ispd = new IndexedPropertyDescriptor(ispd, ipd);
0N/A } else {
0N/A ispd = ipd;
0N/A }
0N/A }
0N/A }
0N/A } else {
0N/A if (pd.getWriteMethod() != null) {
0N/A if (gpd != null) {
0N/A if (gpd.getPropertyType() == pd.getPropertyType()) {
0N/A if (spd != null) {
0N/A spd = new PropertyDescriptor(spd, pd);
0N/A } else {
0N/A spd = pd;
0N/A }
0N/A }
0N/A } else {
0N/A if (spd != null) {
0N/A spd = new PropertyDescriptor(spd, pd);
0N/A } else {
0N/A spd = pd;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A // At this stage we should have either PDs or IPDs for the
0N/A // representative getters and setters. The order at which the
0N/A // property descriptors are determined represent the
0N/A // precedence of the property ordering.
0N/A pd = null; ipd = null;
0N/A
0N/A if (igpd != null && ispd != null) {
0N/A // Complete indexed properties set
0N/A // Merge any classic property descriptors
0N/A if (gpd != null) {
0N/A PropertyDescriptor tpd = mergePropertyDescriptor(igpd, gpd);
0N/A if (tpd instanceof IndexedPropertyDescriptor) {
0N/A igpd = (IndexedPropertyDescriptor)tpd;
0N/A }
0N/A }
0N/A if (spd != null) {
0N/A PropertyDescriptor tpd = mergePropertyDescriptor(ispd, spd);
0N/A if (tpd instanceof IndexedPropertyDescriptor) {
0N/A ispd = (IndexedPropertyDescriptor)tpd;
0N/A }
0N/A }
0N/A if (igpd == ispd) {
0N/A pd = igpd;
0N/A } else {
0N/A pd = mergePropertyDescriptor(igpd, ispd);
0N/A }
0N/A } else if (gpd != null && spd != null) {
0N/A // Complete simple properties set
0N/A if (gpd == spd) {
0N/A pd = gpd;
0N/A } else {
0N/A pd = mergePropertyDescriptor(gpd, spd);
0N/A }
0N/A } else if (ispd != null) {
0N/A // indexed setter
0N/A pd = ispd;
0N/A // Merge any classic property descriptors
0N/A if (spd != null) {
0N/A pd = mergePropertyDescriptor(ispd, spd);
0N/A }
0N/A if (gpd != null) {
0N/A pd = mergePropertyDescriptor(ispd, gpd);
0N/A }
0N/A } else if (igpd != null) {
0N/A // indexed getter
0N/A pd = igpd;
0N/A // Merge any classic property descriptors
0N/A if (gpd != null) {
0N/A pd = mergePropertyDescriptor(igpd, gpd);
0N/A }
0N/A if (spd != null) {
0N/A pd = mergePropertyDescriptor(igpd, spd);
0N/A }
0N/A } else if (spd != null) {
0N/A // simple setter
0N/A pd = spd;
0N/A } else if (gpd != null) {
0N/A // simple getter
0N/A pd = gpd;
0N/A }
0N/A
0N/A // Very special case to ensure that an IndexedPropertyDescriptor
0N/A // doesn't contain less information than the enclosed
0N/A // PropertyDescriptor. If it does, then recreate as a
0N/A // PropertyDescriptor. See 4168833
0N/A if (pd instanceof IndexedPropertyDescriptor) {
0N/A ipd = (IndexedPropertyDescriptor)pd;
0N/A if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {
0N/A pd = new PropertyDescriptor(ipd);
0N/A }
0N/A }
0N/A
0N/A // Find the first property descriptor
0N/A // which does not have getter and setter methods.
0N/A // See regression bug 4984912.
0N/A if ( (pd == null) && (list.size() > 0) ) {
0N/A pd = (PropertyDescriptor) list.get(0);
0N/A }
0N/A
0N/A if (pd != null) {
0N/A properties.put(pd.getName(), pd);
0N/A }
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Adds the property descriptor to the indexedproperty descriptor only if the
0N/A * types are the same.
0N/A *
0N/A * The most specific property descriptor will take precedence.
0N/A */
0N/A private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd,
0N/A PropertyDescriptor pd) {
0N/A PropertyDescriptor result = null;
0N/A
0N/A Class propType = pd.getPropertyType();
0N/A Class ipropType = ipd.getIndexedPropertyType();
0N/A
0N/A if (propType.isArray() && propType.getComponentType() == ipropType) {
0N/A if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0N/A result = new IndexedPropertyDescriptor(pd, ipd);
0N/A } else {
0N/A result = new IndexedPropertyDescriptor(ipd, pd);
0N/A }
0N/A } else {
0N/A // Cannot merge the pd because of type mismatch
0N/A // Return the most specific pd
0N/A if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0N/A result = ipd;
0N/A } else {
0N/A result = pd;
0N/A // Try to add methods which may have been lost in the type change
0N/A // See 4168833
0N/A Method write = result.getWriteMethod();
0N/A Method read = result.getReadMethod();
0N/A
0N/A if (read == null && write != null) {
2788N/A read = findMethod(result.getClass0(),
2788N/A GET_PREFIX + NameGenerator.capitalize(result.getName()), 0);
0N/A if (read != null) {
0N/A try {
0N/A result.setReadMethod(read);
0N/A } catch (IntrospectionException ex) {
0N/A // no consequences for failure.
0N/A }
0N/A }
0N/A }
0N/A if (write == null && read != null) {
2788N/A write = findMethod(result.getClass0(),
2788N/A SET_PREFIX + NameGenerator.capitalize(result.getName()), 1,
2788N/A new Class[] { FeatureDescriptor.getReturnType(result.getClass0(), read) });
0N/A if (write != null) {
0N/A try {
0N/A result.setWriteMethod(write);
0N/A } catch (IntrospectionException ex) {
0N/A // no consequences for failure.
0N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A // Handle regular pd merge
0N/A private PropertyDescriptor mergePropertyDescriptor(PropertyDescriptor pd1,
0N/A PropertyDescriptor pd2) {
0N/A if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
0N/A return new PropertyDescriptor(pd1, pd2);
0N/A } else {
0N/A return new PropertyDescriptor(pd2, pd1);
0N/A }
0N/A }
0N/A
0N/A // Handle regular ipd merge
0N/A private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd1,
0N/A IndexedPropertyDescriptor ipd2) {
0N/A if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
0N/A return new IndexedPropertyDescriptor(ipd1, ipd2);
0N/A } else {
0N/A return new IndexedPropertyDescriptor(ipd2, ipd1);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * @return An array of EventSetDescriptors describing the kinds of
0N/A * events fired by the target bean.
0N/A */
0N/A private EventSetDescriptor[] getTargetEventInfo() throws IntrospectionException {
0N/A if (events == null) {
0N/A events = new HashMap();
0N/A }
0N/A
0N/A // Check if the bean has its own BeanInfo that will provide
0N/A // explicit information.
0N/A EventSetDescriptor[] explicitEvents = null;
0N/A if (explicitBeanInfo != null) {
0N/A explicitEvents = explicitBeanInfo.getEventSetDescriptors();
0N/A int ix = explicitBeanInfo.getDefaultEventIndex();
0N/A if (ix >= 0 && ix < explicitEvents.length) {
0N/A defaultEventName = explicitEvents[ix].getName();
0N/A }
0N/A }
0N/A
0N/A if (explicitEvents == null && superBeanInfo != null) {
0N/A // We have no explicit BeanInfo events. Check with our parent.
0N/A EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors();
0N/A for (int i = 0 ; i < supers.length; i++) {
0N/A addEvent(supers[i]);
0N/A }
0N/A int ix = superBeanInfo.getDefaultEventIndex();
0N/A if (ix >= 0 && ix < supers.length) {
0N/A defaultEventName = supers[ix].getName();
0N/A }
0N/A }
0N/A
0N/A for (int i = 0; i < additionalBeanInfo.length; i++) {
0N/A EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors();
0N/A if (additional != null) {
0N/A for (int j = 0 ; j < additional.length; j++) {
0N/A addEvent(additional[j]);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (explicitEvents != null) {
0N/A // Add the explicit explicitBeanInfo data to our results.
0N/A for (int i = 0 ; i < explicitEvents.length; i++) {
0N/A addEvent(explicitEvents[i]);
0N/A }
0N/A
0N/A } else {
0N/A
0N/A // Apply some reflection to the current class.
0N/A
0N/A // Get an array of all the public beans methods at this level
0N/A Method methodList[] = getPublicDeclaredMethods(beanClass);
0N/A
0N/A // Find all suitable "add", "remove" and "get" Listener methods
0N/A // The name of the listener type is the key for these hashtables
0N/A // i.e, ActionListener
0N/A Map adds = null;
0N/A Map removes = null;
0N/A Map gets = null;
0N/A
0N/A for (int i = 0; i < methodList.length; i++) {
0N/A Method method = methodList[i];
0N/A if (method == null) {
0N/A continue;
0N/A }
0N/A // skip static methods.
0N/A int mods = method.getModifiers();
0N/A if (Modifier.isStatic(mods)) {
0N/A continue;
0N/A }
0N/A String name = method.getName();
0N/A // Optimization avoid getParameterTypes
0N/A if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)
0N/A && !name.startsWith(GET_PREFIX)) {
0N/A continue;
0N/A }
0N/A
5235N/A if (name.startsWith(ADD_PREFIX)) {
5235N/A Class<?> returnType = method.getReturnType();
5235N/A if (returnType == void.class) {
5235N/A Type[] parameterTypes = method.getGenericParameterTypes();
5235N/A if (parameterTypes.length == 1) {
5235N/A Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
5235N/A if (Introspector.isSubclass(type, eventListenerType)) {
5235N/A String listenerName = name.substring(3);
5235N/A if (listenerName.length() > 0 &&
5235N/A type.getName().endsWith(listenerName)) {
5235N/A if (adds == null) {
5235N/A adds = new HashMap();
5235N/A }
5235N/A adds.put(listenerName, method);
5235N/A }
5235N/A }
0N/A }
0N/A }
0N/A }
5235N/A else if (name.startsWith(REMOVE_PREFIX)) {
5235N/A Class<?> returnType = method.getReturnType();
5235N/A if (returnType == void.class) {
5235N/A Type[] parameterTypes = method.getGenericParameterTypes();
5235N/A if (parameterTypes.length == 1) {
5235N/A Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
5235N/A if (Introspector.isSubclass(type, eventListenerType)) {
5235N/A String listenerName = name.substring(6);
5235N/A if (listenerName.length() > 0 &&
5235N/A type.getName().endsWith(listenerName)) {
5235N/A if (removes == null) {
5235N/A removes = new HashMap();
5235N/A }
5235N/A removes.put(listenerName, method);
5235N/A }
5235N/A }
0N/A }
0N/A }
0N/A }
5235N/A else if (name.startsWith(GET_PREFIX)) {
5235N/A Class<?>[] parameterTypes = method.getParameterTypes();
5235N/A if (parameterTypes.length == 0) {
5235N/A Class<?> returnType = FeatureDescriptor.getReturnType(beanClass, method);
5235N/A if (returnType.isArray()) {
5235N/A Class<?> type = returnType.getComponentType();
5235N/A if (Introspector.isSubclass(type, eventListenerType)) {
5235N/A String listenerName = name.substring(3, name.length() - 1);
5235N/A if (listenerName.length() > 0 &&
5235N/A type.getName().endsWith(listenerName)) {
5235N/A if (gets == null) {
5235N/A gets = new HashMap();
5235N/A }
5235N/A gets.put(listenerName, method);
5235N/A }
5235N/A }
0N/A }
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (adds != null && removes != null) {
0N/A // Now look for matching addFooListener+removeFooListener pairs.
0N/A // Bonus if there is a matching getFooListeners method as well.
0N/A Iterator keys = adds.keySet().iterator();
0N/A while (keys.hasNext()) {
0N/A String listenerName = (String) keys.next();
0N/A // Skip any "add" which doesn't have a matching "remove" or
0N/A // a listener name that doesn't end with Listener
0N/A if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
0N/A continue;
0N/A }
0N/A String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
0N/A Method addMethod = (Method)adds.get(listenerName);
0N/A Method removeMethod = (Method)removes.get(listenerName);
0N/A Method getMethod = null;
0N/A if (gets != null) {
0N/A getMethod = (Method)gets.get(listenerName);
0N/A }
0N/A Class argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0];
0N/A
0N/A // generate a list of Method objects for each of the target methods:
0N/A Method allMethods[] = getPublicDeclaredMethods(argType);
0N/A List validMethods = new ArrayList(allMethods.length);
0N/A for (int i = 0; i < allMethods.length; i++) {
0N/A if (allMethods[i] == null) {
0N/A continue;
0N/A }
0N/A
0N/A if (isEventHandler(allMethods[i])) {
0N/A validMethods.add(allMethods[i]);
0N/A }
0N/A }
0N/A Method[] methods = (Method[])validMethods.toArray(new Method[validMethods.size()]);
0N/A
0N/A EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,
0N/A methods, addMethod,
0N/A removeMethod,
0N/A getMethod);
0N/A
0N/A // If the adder method throws the TooManyListenersException then it
0N/A // is a Unicast event source.
0N/A if (throwsException(addMethod,
0N/A java.util.TooManyListenersException.class)) {
0N/A esd.setUnicast(true);
0N/A }
0N/A addEvent(esd);
0N/A }
0N/A } // if (adds != null ...
0N/A }
0N/A EventSetDescriptor[] result;
0N/A if (events.size() == 0) {
0N/A result = EMPTY_EVENTSETDESCRIPTORS;
0N/A } else {
0N/A // Allocate and populate the result array.
0N/A result = new EventSetDescriptor[events.size()];
0N/A result = (EventSetDescriptor[])events.values().toArray(result);
0N/A
0N/A // Set the default index.
0N/A if (defaultEventName != null) {
0N/A for (int i = 0; i < result.length; i++) {
0N/A if (defaultEventName.equals(result[i].getName())) {
0N/A defaultEventIndex = i;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A private void addEvent(EventSetDescriptor esd) {
0N/A String key = esd.getName();
0N/A if (esd.getName().equals("propertyChange")) {
0N/A propertyChangeSource = true;
0N/A }
0N/A EventSetDescriptor old = (EventSetDescriptor)events.get(key);
0N/A if (old == null) {
0N/A events.put(key, esd);
0N/A return;
0N/A }
0N/A EventSetDescriptor composite = new EventSetDescriptor(old, esd);
0N/A events.put(key, composite);
0N/A }
0N/A
0N/A /**
0N/A * @return An array of MethodDescriptors describing the private
0N/A * methods supported by the target bean.
0N/A */
0N/A private MethodDescriptor[] getTargetMethodInfo() {
0N/A if (methods == null) {
0N/A methods = new HashMap(100);
0N/A }
0N/A
0N/A // Check if the bean has its own BeanInfo that will provide
0N/A // explicit information.
0N/A MethodDescriptor[] explicitMethods = null;
0N/A if (explicitBeanInfo != null) {
0N/A explicitMethods = explicitBeanInfo.getMethodDescriptors();
0N/A }
0N/A
0N/A if (explicitMethods == null && superBeanInfo != null) {
0N/A // We have no explicit BeanInfo methods. Check with our parent.
0N/A MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors();
0N/A for (int i = 0 ; i < supers.length; i++) {
0N/A addMethod(supers[i]);
0N/A }
0N/A }
0N/A
0N/A for (int i = 0; i < additionalBeanInfo.length; i++) {
0N/A MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors();
0N/A if (additional != null) {
0N/A for (int j = 0 ; j < additional.length; j++) {
0N/A addMethod(additional[j]);
0N/A }
0N/A }
0N/A }
0N/A
0N/A if (explicitMethods != null) {
0N/A // Add the explicit explicitBeanInfo data to our results.
0N/A for (int i = 0 ; i < explicitMethods.length; i++) {
0N/A addMethod(explicitMethods[i]);
0N/A }
0N/A
0N/A } else {
0N/A
0N/A // Apply some reflection to the current class.
0N/A
0N/A // First get an array of all the beans methods at this level
0N/A Method methodList[] = getPublicDeclaredMethods(beanClass);
0N/A
0N/A // Now analyze each method.
0N/A for (int i = 0; i < methodList.length; i++) {
0N/A Method method = methodList[i];
0N/A if (method == null) {
0N/A continue;
0N/A }
0N/A MethodDescriptor md = new MethodDescriptor(method);
0N/A addMethod(md);
0N/A }
0N/A }
0N/A
0N/A // Allocate and populate the result array.
0N/A MethodDescriptor result[] = new MethodDescriptor[methods.size()];
0N/A result = (MethodDescriptor[])methods.values().toArray(result);
0N/A
0N/A return result;
0N/A }
0N/A
0N/A private void addMethod(MethodDescriptor md) {
0N/A // We have to be careful here to distinguish method by both name
0N/A // and argument lists.
0N/A // This method gets called a *lot, so we try to be efficient.
0N/A String name = md.getName();
0N/A
0N/A MethodDescriptor old = (MethodDescriptor)methods.get(name);
0N/A if (old == null) {
0N/A // This is the common case.
0N/A methods.put(name, md);
0N/A return;
0N/A }
0N/A
0N/A // We have a collision on method names. This is rare.
0N/A
0N/A // Check if old and md have the same type.
0N/A String[] p1 = md.getParamNames();
0N/A String[] p2 = old.getParamNames();
0N/A
0N/A boolean match = false;
0N/A if (p1.length == p2.length) {
0N/A match = true;
0N/A for (int i = 0; i < p1.length; i++) {
0N/A if (p1[i] != p2[i]) {
0N/A match = false;
0N/A break;
0N/A }
0N/A }
0N/A }
0N/A if (match) {
0N/A MethodDescriptor composite = new MethodDescriptor(old, md);
0N/A methods.put(name, composite);
0N/A return;
0N/A }
0N/A
0N/A // We have a collision on method names with different type signatures.
0N/A // This is very rare.
0N/A
0N/A String longKey = makeQualifiedMethodName(name, p1);
0N/A old = (MethodDescriptor)methods.get(longKey);
0N/A if (old == null) {
0N/A methods.put(longKey, md);
0N/A return;
0N/A }
0N/A MethodDescriptor composite = new MethodDescriptor(old, md);
0N/A methods.put(longKey, composite);
0N/A }
0N/A
0N/A /**
0N/A * Creates a key for a method in a method cache.
0N/A */
0N/A private static String makeQualifiedMethodName(String name, String[] params) {
0N/A StringBuffer sb = new StringBuffer(name);
0N/A sb.append('=');
0N/A for (int i = 0; i < params.length; i++) {
0N/A sb.append(':');
0N/A sb.append(params[i]);
0N/A }
0N/A return sb.toString();
0N/A }
0N/A
0N/A private int getTargetDefaultEventIndex() {
0N/A return defaultEventIndex;
0N/A }
0N/A
0N/A private int getTargetDefaultPropertyIndex() {
0N/A return defaultPropertyIndex;
0N/A }
0N/A
0N/A private BeanDescriptor getTargetBeanDescriptor() {
0N/A // Use explicit info, if available,
0N/A if (explicitBeanInfo != null) {
0N/A BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
0N/A if (bd != null) {
0N/A return (bd);
0N/A }
0N/A }
0N/A // OK, fabricate a default BeanDescriptor.
3083N/A return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass));
3083N/A }
3083N/A
3083N/A private static Class<?> findCustomizerClass(Class<?> type) {
3083N/A String name = type.getName() + "Customizer";
3083N/A try {
3083N/A type = ClassFinder.findClass(name, type.getClassLoader());
3083N/A // Each customizer should inherit java.awt.Component and implement java.beans.Customizer
3731N/A // according to the section 9.3 of JavaBeans&trade; specification
3083N/A if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) {
3083N/A return type;
3083N/A }
3083N/A }
3083N/A catch (Exception exception) {
3083N/A // ignore any exceptions
3083N/A }
3083N/A return null;
0N/A }
0N/A
0N/A private boolean isEventHandler(Method m) {
0N/A // We assume that a method is an event handler if it has a single
0N/A // argument, whose type inherit from java.util.Event.
5235N/A Type argTypes[] = m.getGenericParameterTypes();
0N/A if (argTypes.length != 1) {
0N/A return false;
0N/A }
5235N/A return isSubclass(TypeResolver.erase(TypeResolver.resolveInClass(beanClass, argTypes[0])), EventObject.class);
0N/A }
0N/A
0N/A /*
0N/A * Internal method to return *public* methods within a class.
0N/A */
1987N/A private static Method[] getPublicDeclaredMethods(Class clz) {
0N/A // Looking up Class.getDeclaredMethods is relatively expensive,
0N/A // so we cache the results.
0N/A if (!ReflectUtil.isPackageAccessible(clz)) {
0N/A return new Method[0];
0N/A }
4373N/A synchronized (declaredMethodCache) {
1987N/A Method[] result = declaredMethodCache.get(clz);
1987N/A if (result == null) {
1987N/A result = clz.getMethods();
1987N/A for (int i = 0; i < result.length; i++) {
1987N/A Method method = result[i];
1987N/A if (!method.getDeclaringClass().equals(clz)) {
1987N/A result[i] = null;
1987N/A }
0N/A }
1987N/A declaredMethodCache.put(clz, result);
0N/A }
1987N/A return result;
0N/A }
0N/A }
0N/A
0N/A //======================================================================
0N/A // Package private support methods.
0N/A //======================================================================
0N/A
2788N/A /**
2788N/A * Internal support for finding a target methodName with a given
2788N/A * parameter list on a given class.
2788N/A */
2788N/A private static Method internalFindMethod(Class start, String methodName,
2788N/A int argCount, Class args[]) {
2788N/A // For overriden methods we need to find the most derived version.
2788N/A // So we start with the given class and walk up the superclass chain.
2788N/A
2788N/A Method method = null;
2788N/A
2788N/A for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
2788N/A Method methods[] = getPublicDeclaredMethods(cl);
2788N/A for (int i = 0; i < methods.length; i++) {
2788N/A method = methods[i];
2788N/A if (method == null) {
2788N/A continue;
0N/A }
2788N/A
2788N/A // make sure method signature matches.
5235N/A if (method.getName().equals(methodName)) {
5235N/A Type[] params = method.getGenericParameterTypes();
5235N/A if (params.length == argCount) {
5235N/A if (args != null) {
5235N/A boolean different = false;
5235N/A if (argCount > 0) {
5235N/A for (int j = 0; j < argCount; j++) {
5235N/A if (TypeResolver.erase(TypeResolver.resolveInClass(start, params[j])) != args[j]) {
5235N/A different = true;
5235N/A continue;
5235N/A }
5235N/A }
5235N/A if (different) {
2788N/A continue;
2788N/A }
2788N/A }
2788N/A }
5235N/A return method;
2788N/A }
0N/A }
0N/A }
0N/A }
2788N/A method = null;
2788N/A
2788N/A // Now check any inherited interfaces. This is necessary both when
2788N/A // the argument class is itself an interface, and when the argument
2788N/A // class is an abstract class.
2788N/A Class ifcs[] = start.getInterfaces();
2788N/A for (int i = 0 ; i < ifcs.length; i++) {
2788N/A // Note: The original implementation had both methods calling
2788N/A // the 3 arg method. This is preserved but perhaps it should
2788N/A // pass the args array instead of null.
2788N/A method = internalFindMethod(ifcs[i], methodName, argCount, null);
2788N/A if (method != null) {
2788N/A break;
2788N/A }
2788N/A }
2788N/A return method;
0N/A }
0N/A
2788N/A /**
2788N/A * Find a target methodName on a given class.
2788N/A */
2788N/A static Method findMethod(Class cls, String methodName, int argCount) {
2788N/A return findMethod(cls, methodName, argCount, null);
2788N/A }
2788N/A
2788N/A /**
2788N/A * Find a target methodName with specific parameter list on a given class.
2788N/A * <p>
2788N/A * Used in the contructors of the EventSetDescriptor,
2788N/A * PropertyDescriptor and the IndexedPropertyDescriptor.
2788N/A * <p>
2788N/A * @param cls The Class object on which to retrieve the method.
2788N/A * @param methodName Name of the method.
2788N/A * @param argCount Number of arguments for the desired method.
2788N/A * @param args Array of argument types for the method.
2788N/A * @return the method or null if not found
2788N/A */
2788N/A static Method findMethod(Class cls, String methodName, int argCount,
2788N/A Class args[]) {
2788N/A if (methodName == null) {
0N/A return null;
0N/A }
2788N/A return internalFindMethod(cls, methodName, argCount, args);
0N/A }
0N/A
0N/A /**
0N/A * Return true if class a is either equivalent to class b, or
0N/A * if class a is a subclass of class b, i.e. if a either "extends"
0N/A * or "implements" b.
0N/A * Note tht either or both "Class" objects may represent interfaces.
0N/A */
0N/A static boolean isSubclass(Class a, Class b) {
0N/A // We rely on the fact that for any given java class or
0N/A // primtitive type there is a unqiue Class object, so
0N/A // we can use object equivalence in the comparisons.
0N/A if (a == b) {
0N/A return true;
0N/A }
0N/A if (a == null || b == null) {
0N/A return false;
0N/A }
0N/A for (Class x = a; x != null; x = x.getSuperclass()) {
0N/A if (x == b) {
0N/A return true;
0N/A }
0N/A if (b.isInterface()) {
0N/A Class interfaces[] = x.getInterfaces();
0N/A for (int i = 0; i < interfaces.length; i++) {
0N/A if (isSubclass(interfaces[i], b)) {
0N/A return true;
0N/A }
0N/A }
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Return true iff the given method throws the given exception.
0N/A */
0N/A private boolean throwsException(Method method, Class exception) {
0N/A Class exs[] = method.getExceptionTypes();
0N/A for (int i = 0; i < exs.length; i++) {
0N/A if (exs[i] == exception) {
0N/A return true;
0N/A }
0N/A }
0N/A return false;
0N/A }
0N/A
0N/A /**
0N/A * Try to create an instance of a named class.
0N/A * First try the classloader of "sibling", then try the system
0N/A * classloader then the class loader of the current Thread.
0N/A */
0N/A static Object instantiate(Class sibling, String className)
0N/A throws InstantiationException, IllegalAccessException,
0N/A ClassNotFoundException {
0N/A // First check with sibling's classloader (if any).
0N/A ClassLoader cl = sibling.getClassLoader();
0N/A Class cls = ClassFinder.findClass(className, cl);
0N/A return cls.newInstance();
0N/A }
0N/A
0N/A} // end class Introspector
0N/A
0N/A//===========================================================================
0N/A
0N/A/**
0N/A * Package private implementation support class for Introspector's
0N/A * internal use.
0N/A * <p>
0N/A * Mostly this is used as a placeholder for the descriptors.
0N/A */
0N/A
0N/Aclass GenericBeanInfo extends SimpleBeanInfo {
0N/A
0N/A private BeanDescriptor beanDescriptor;
0N/A private EventSetDescriptor[] events;
0N/A private int defaultEvent;
0N/A private PropertyDescriptor[] properties;
0N/A private int defaultProperty;
0N/A private MethodDescriptor[] methods;
5573N/A private Reference<BeanInfo> targetBeanInfoRef;
0N/A
0N/A public GenericBeanInfo(BeanDescriptor beanDescriptor,
0N/A EventSetDescriptor[] events, int defaultEvent,
0N/A PropertyDescriptor[] properties, int defaultProperty,
0N/A MethodDescriptor[] methods, BeanInfo targetBeanInfo) {
0N/A this.beanDescriptor = beanDescriptor;
0N/A this.events = events;
0N/A this.defaultEvent = defaultEvent;
0N/A this.properties = properties;
0N/A this.defaultProperty = defaultProperty;
0N/A this.methods = methods;
5573N/A this.targetBeanInfoRef = (targetBeanInfo != null)
5573N/A ? new SoftReference<>(targetBeanInfo)
5573N/A : null;
0N/A }
0N/A
0N/A /**
0N/A * Package-private dup constructor
0N/A * This must isolate the new object from any changes to the old object.
0N/A */
0N/A GenericBeanInfo(GenericBeanInfo old) {
0N/A
0N/A beanDescriptor = new BeanDescriptor(old.beanDescriptor);
0N/A if (old.events != null) {
0N/A int len = old.events.length;
0N/A events = new EventSetDescriptor[len];
0N/A for (int i = 0; i < len; i++) {
0N/A events[i] = new EventSetDescriptor(old.events[i]);
0N/A }
0N/A }
0N/A defaultEvent = old.defaultEvent;
0N/A if (old.properties != null) {
0N/A int len = old.properties.length;
0N/A properties = new PropertyDescriptor[len];
0N/A for (int i = 0; i < len; i++) {
0N/A PropertyDescriptor oldp = old.properties[i];
0N/A if (oldp instanceof IndexedPropertyDescriptor) {
0N/A properties[i] = new IndexedPropertyDescriptor(
0N/A (IndexedPropertyDescriptor) oldp);
0N/A } else {
0N/A properties[i] = new PropertyDescriptor(oldp);
0N/A }
0N/A }
0N/A }
0N/A defaultProperty = old.defaultProperty;
0N/A if (old.methods != null) {
0N/A int len = old.methods.length;
0N/A methods = new MethodDescriptor[len];
0N/A for (int i = 0; i < len; i++) {
0N/A methods[i] = new MethodDescriptor(old.methods[i]);
0N/A }
0N/A }
2149N/A this.targetBeanInfoRef = old.targetBeanInfoRef;
0N/A }
0N/A
0N/A public PropertyDescriptor[] getPropertyDescriptors() {
0N/A return properties;
0N/A }
0N/A
0N/A public int getDefaultPropertyIndex() {
0N/A return defaultProperty;
0N/A }
0N/A
0N/A public EventSetDescriptor[] getEventSetDescriptors() {
0N/A return events;
0N/A }
0N/A
0N/A public int getDefaultEventIndex() {
0N/A return defaultEvent;
0N/A }
0N/A
0N/A public MethodDescriptor[] getMethodDescriptors() {
0N/A return methods;
0N/A }
0N/A
0N/A public BeanDescriptor getBeanDescriptor() {
0N/A return beanDescriptor;
0N/A }
0N/A
0N/A public java.awt.Image getIcon(int iconKind) {
5573N/A BeanInfo targetBeanInfo = getTargetBeanInfo();
0N/A if (targetBeanInfo != null) {
0N/A return targetBeanInfo.getIcon(iconKind);
0N/A }
0N/A return super.getIcon(iconKind);
0N/A }
5573N/A
5573N/A private BeanInfo getTargetBeanInfo() {
5573N/A if (this.targetBeanInfoRef == null) {
5573N/A return null;
5573N/A }
5573N/A BeanInfo targetBeanInfo = this.targetBeanInfoRef.get();
5573N/A if (targetBeanInfo == null) {
5573N/A targetBeanInfo = ThreadGroupContext.getContext().getBeanInfoFinder()
5573N/A .find(this.beanDescriptor.getBeanClass());
5573N/A if (targetBeanInfo != null) {
5573N/A this.targetBeanInfoRef = new SoftReference<>(targetBeanInfo);
5573N/A }
5573N/A }
5573N/A return targetBeanInfo;
5573N/A }
0N/A}