Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/beans/Introspector.java
38829 views
/*1* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.beans;2627import com.sun.beans.TypeResolver;28import com.sun.beans.WeakCache;29import com.sun.beans.finder.ClassFinder;30import com.sun.beans.finder.MethodFinder;3132import java.awt.Component;3334import java.lang.ref.Reference;35import java.lang.ref.SoftReference;36import java.lang.reflect.Method;37import java.lang.reflect.Modifier;38import java.lang.reflect.Type;3940import java.util.Map;41import java.util.ArrayList;42import java.util.HashMap;43import java.util.Iterator;44import java.util.EventListener;45import java.util.EventObject;46import java.util.List;47import java.util.TreeMap;4849import sun.reflect.misc.ReflectUtil;5051/**52* The Introspector class provides a standard way for tools to learn about53* the properties, events, and methods supported by a target Java Bean.54* <p>55* For each of those three kinds of information, the Introspector will56* separately analyze the bean's class and superclasses looking for57* either explicit or implicit information and use that information to58* build a BeanInfo object that comprehensively describes the target bean.59* <p>60* For each class "Foo", explicit information may be available if there exists61* a corresponding "FooBeanInfo" class that provides a non-null value when62* queried for the information. We first look for the BeanInfo class by63* taking the full package-qualified name of the target bean class and64* appending "BeanInfo" to form a new class name. If this fails, then65* we take the final classname component of this name, and look for that66* class in each of the packages specified in the BeanInfo package search67* path.68* <p>69* Thus for a class such as "sun.xyz.OurButton" we would first look for a70* BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd71* look in each package in the BeanInfo search path for an OurButtonBeanInfo72* class. With the default search path, this would mean looking for73* "sun.beans.infos.OurButtonBeanInfo".74* <p>75* If a class provides explicit BeanInfo about itself then we add that to76* the BeanInfo information we obtained from analyzing any derived classes,77* but we regard the explicit information as being definitive for the current78* class and its base classes, and do not proceed any further up the superclass79* chain.80* <p>81* If we don't find explicit BeanInfo on a class, we use low-level82* reflection to study the methods of the class and apply standard design83* patterns to identify property accessors, event sources, or public84* methods. We then proceed to analyze the class's superclass and add85* in the information from it (and possibly on up the superclass chain).86* <p>87* For more information about introspection and design patterns, please88* consult the89* <a href="http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html">JavaBeans™ specification</a>.90*/9192public class Introspector {9394// Flags that can be used to control getBeanInfo:95/**96* Flag to indicate to use of all beaninfo.97*/98public final static int USE_ALL_BEANINFO = 1;99/**100* Flag to indicate to ignore immediate beaninfo.101*/102public final static int IGNORE_IMMEDIATE_BEANINFO = 2;103/**104* Flag to indicate to ignore all beaninfo.105*/106public final static int IGNORE_ALL_BEANINFO = 3;107108// Static Caches to speed up introspection.109private static final WeakCache<Class<?>, Method[]> declaredMethodCache = new WeakCache<>();110111private Class<?> beanClass;112private BeanInfo explicitBeanInfo;113private BeanInfo superBeanInfo;114private BeanInfo additionalBeanInfo[];115116private boolean propertyChangeSource = false;117private static Class<EventListener> eventListenerType = EventListener.class;118119// These should be removed.120private String defaultEventName;121private String defaultPropertyName;122private int defaultEventIndex = -1;123private int defaultPropertyIndex = -1;124125// Methods maps from Method names to MethodDescriptors126private Map<String, MethodDescriptor> methods;127128// properties maps from String names to PropertyDescriptors129private Map<String, PropertyDescriptor> properties;130131// events maps from String names to EventSetDescriptors132private Map<String, EventSetDescriptor> events;133134private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];135136static final String ADD_PREFIX = "add";137static final String REMOVE_PREFIX = "remove";138static final String GET_PREFIX = "get";139static final String SET_PREFIX = "set";140static final String IS_PREFIX = "is";141142//======================================================================143// Public methods144//======================================================================145146/**147* Introspect on a Java Bean and learn about all its properties, exposed148* methods, and events.149* <p>150* If the BeanInfo class for a Java Bean has been previously Introspected151* then the BeanInfo class is retrieved from the BeanInfo cache.152*153* @param beanClass The bean class to be analyzed.154* @return A BeanInfo object describing the target bean.155* @exception IntrospectionException if an exception occurs during156* introspection.157* @see #flushCaches158* @see #flushFromCaches159*/160public static BeanInfo getBeanInfo(Class<?> beanClass)161throws IntrospectionException162{163if (!ReflectUtil.isPackageAccessible(beanClass)) {164return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();165}166ThreadGroupContext context = ThreadGroupContext.getContext();167BeanInfo beanInfo;168synchronized (declaredMethodCache) {169beanInfo = context.getBeanInfo(beanClass);170}171if (beanInfo == null) {172beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();173synchronized (declaredMethodCache) {174context.putBeanInfo(beanClass, beanInfo);175}176}177return beanInfo;178}179180/**181* Introspect on a Java bean and learn about all its properties, exposed182* methods, and events, subject to some control flags.183* <p>184* If the BeanInfo class for a Java Bean has been previously Introspected185* based on the same arguments then the BeanInfo class is retrieved186* from the BeanInfo cache.187*188* @param beanClass The bean class to be analyzed.189* @param flags Flags to control the introspection.190* If flags == USE_ALL_BEANINFO then we use all of the BeanInfo191* classes we can discover.192* If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any193* BeanInfo associated with the specified beanClass.194* If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo195* associated with the specified beanClass or any of its196* parent classes.197* @return A BeanInfo object describing the target bean.198* @exception IntrospectionException if an exception occurs during199* introspection.200*/201public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)202throws IntrospectionException {203return getBeanInfo(beanClass, null, flags);204}205206/**207* Introspect on a Java bean and learn all about its properties, exposed208* methods, below a given "stop" point.209* <p>210* If the BeanInfo class for a Java Bean has been previously Introspected211* based on the same arguments, then the BeanInfo class is retrieved212* from the BeanInfo cache.213* @return the BeanInfo for the bean214* @param beanClass The bean class to be analyzed.215* @param stopClass The baseclass at which to stop the analysis. Any216* methods/properties/events in the stopClass or in its baseclasses217* will be ignored in the analysis.218* @exception IntrospectionException if an exception occurs during219* introspection.220*/221public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)222throws IntrospectionException {223return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);224}225226/**227* Introspect on a Java Bean and learn about all its properties,228* exposed methods and events, below a given {@code stopClass} point229* subject to some control {@code flags}.230* <dl>231* <dt>USE_ALL_BEANINFO</dt>232* <dd>Any BeanInfo that can be discovered will be used.</dd>233* <dt>IGNORE_IMMEDIATE_BEANINFO</dt>234* <dd>Any BeanInfo associated with the specified {@code beanClass} will be ignored.</dd>235* <dt>IGNORE_ALL_BEANINFO</dt>236* <dd>Any BeanInfo associated with the specified {@code beanClass}237* or any of its parent classes will be ignored.</dd>238* </dl>239* Any methods/properties/events in the {@code stopClass}240* or in its parent classes will be ignored in the analysis.241* <p>242* If the BeanInfo class for a Java Bean has been243* previously introspected based on the same arguments then244* the BeanInfo class is retrieved from the BeanInfo cache.245*246* @param beanClass the bean class to be analyzed247* @param stopClass the parent class at which to stop the analysis248* @param flags flags to control the introspection249* @return a BeanInfo object describing the target bean250* @exception IntrospectionException if an exception occurs during introspection251*252* @since 1.7253*/254public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass,255int flags) throws IntrospectionException {256BeanInfo bi;257if (stopClass == null && flags == USE_ALL_BEANINFO) {258// Same parameters to take advantage of caching.259bi = getBeanInfo(beanClass);260} else {261bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();262}263return bi;264265// Old behaviour: Make an independent copy of the BeanInfo.266//return new GenericBeanInfo(bi);267}268269270/**271* Utility method to take a string and convert it to normal Java variable272* name capitalization. This normally means converting the first273* character from upper case to lower case, but in the (unusual) special274* case when there is more than one character and both the first and275* second characters are upper case, we leave it alone.276* <p>277* Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays278* as "URL".279*280* @param name The string to be decapitalized.281* @return The decapitalized version of the string.282*/283public static String decapitalize(String name) {284if (name == null || name.length() == 0) {285return name;286}287if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&288Character.isUpperCase(name.charAt(0))){289return name;290}291char chars[] = name.toCharArray();292chars[0] = Character.toLowerCase(chars[0]);293return new String(chars);294}295296/**297* Gets the list of package names that will be used for298* finding BeanInfo classes.299*300* @return The array of package names that will be searched in301* order to find BeanInfo classes. The default value302* for this array is implementation-dependent; e.g.303* Sun implementation initially sets to {"sun.beans.infos"}.304*/305306public static String[] getBeanInfoSearchPath() {307return ThreadGroupContext.getContext().getBeanInfoFinder().getPackages();308}309310/**311* Change the list of package names that will be used for312* finding BeanInfo classes. The behaviour of313* this method is undefined if parameter path314* is null.315*316* <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>317* method is called. This could result in a SecurityException.318*319* @param path Array of package names.320* @exception SecurityException if a security manager exists and its321* <code>checkPropertiesAccess</code> method doesn't allow setting322* of system properties.323* @see SecurityManager#checkPropertiesAccess324*/325326public static void setBeanInfoSearchPath(String[] path) {327SecurityManager sm = System.getSecurityManager();328if (sm != null) {329sm.checkPropertiesAccess();330}331ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path);332}333334335/**336* Flush all of the Introspector's internal caches. This method is337* not normally required. It is normally only needed by advanced338* tools that update existing "Class" objects in-place and need339* to make the Introspector re-analyze existing Class objects.340*/341342public static void flushCaches() {343synchronized (declaredMethodCache) {344ThreadGroupContext.getContext().clearBeanInfoCache();345declaredMethodCache.clear();346}347}348349/**350* Flush the Introspector's internal cached information for a given class.351* This method is not normally required. It is normally only needed352* by advanced tools that update existing "Class" objects in-place353* and need to make the Introspector re-analyze an existing Class object.354*355* Note that only the direct state associated with the target Class356* object is flushed. We do not flush state for other Class objects357* with the same name, nor do we flush state for any related Class358* objects (such as subclasses), even though their state may include359* information indirectly obtained from the target Class object.360*361* @param clz Class object to be flushed.362* @throws NullPointerException If the Class object is null.363*/364public static void flushFromCaches(Class<?> clz) {365if (clz == null) {366throw new NullPointerException();367}368synchronized (declaredMethodCache) {369ThreadGroupContext.getContext().removeBeanInfo(clz);370declaredMethodCache.put(clz, null);371}372}373374//======================================================================375// Private implementation methods376//======================================================================377378private Introspector(Class<?> beanClass, Class<?> stopClass, int flags)379throws IntrospectionException {380this.beanClass = beanClass;381382// Check stopClass is a superClass of startClass.383if (stopClass != null) {384boolean isSuper = false;385for (Class<?> c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {386if (c == stopClass) {387isSuper = true;388}389}390if (!isSuper) {391throw new IntrospectionException(stopClass.getName() + " not superclass of " +392beanClass.getName());393}394}395396if (flags == USE_ALL_BEANINFO) {397explicitBeanInfo = findExplicitBeanInfo(beanClass);398}399400Class<?> superClass = beanClass.getSuperclass();401if (superClass != stopClass) {402int newFlags = flags;403if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {404newFlags = USE_ALL_BEANINFO;405}406superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);407}408if (explicitBeanInfo != null) {409additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();410}411if (additionalBeanInfo == null) {412additionalBeanInfo = new BeanInfo[0];413}414}415416/**417* Constructs a GenericBeanInfo class from the state of the Introspector418*/419private BeanInfo getBeanInfo() throws IntrospectionException {420421// the evaluation order here is import, as we evaluate the422// event sets and locate PropertyChangeListeners before we423// look for properties.424BeanDescriptor bd = getTargetBeanDescriptor();425MethodDescriptor mds[] = getTargetMethodInfo();426EventSetDescriptor esds[] = getTargetEventInfo();427PropertyDescriptor pds[] = getTargetPropertyInfo();428429int defaultEvent = getTargetDefaultEventIndex();430int defaultProperty = getTargetDefaultPropertyIndex();431432return new GenericBeanInfo(bd, esds, defaultEvent, pds,433defaultProperty, mds, explicitBeanInfo);434435}436437/**438* Looks for an explicit BeanInfo class that corresponds to the Class.439* First it looks in the existing package that the Class is defined in,440* then it checks to see if the class is its own BeanInfo. Finally,441* the BeanInfo search path is prepended to the class and searched.442*443* @param beanClass the class type of the bean444* @return Instance of an explicit BeanInfo class or null if one isn't found.445*/446private static BeanInfo findExplicitBeanInfo(Class<?> beanClass) {447return ThreadGroupContext.getContext().getBeanInfoFinder().find(beanClass);448}449450/**451* @return An array of PropertyDescriptors describing the editable452* properties supported by the target bean.453*/454455private PropertyDescriptor[] getTargetPropertyInfo() {456457// Check if the bean has its own BeanInfo that will provide458// explicit information.459PropertyDescriptor[] explicitProperties = null;460if (explicitBeanInfo != null) {461explicitProperties = getPropertyDescriptors(this.explicitBeanInfo);462}463464if (explicitProperties == null && superBeanInfo != null) {465// We have no explicit BeanInfo properties. Check with our parent.466addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo));467}468469for (int i = 0; i < additionalBeanInfo.length; i++) {470addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors());471}472473if (explicitProperties != null) {474// Add the explicit BeanInfo data to our results.475addPropertyDescriptors(explicitProperties);476477} else {478479// Apply some reflection to the current class.480481// First get an array of all the public methods at this level482Method methodList[] = getPublicDeclaredMethods(beanClass);483484// Now analyze each method.485for (int i = 0; i < methodList.length; i++) {486Method method = methodList[i];487if (method == null) {488continue;489}490// skip static methods.491int mods = method.getModifiers();492if (Modifier.isStatic(mods)) {493continue;494}495String name = method.getName();496Class<?>[] argTypes = method.getParameterTypes();497Class<?> resultType = method.getReturnType();498int argCount = argTypes.length;499PropertyDescriptor pd = null;500501if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {502// Optimization. Don't bother with invalid propertyNames.503continue;504}505506try {507508if (argCount == 0) {509if (name.startsWith(GET_PREFIX)) {510// Simple getter511pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);512} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {513// Boolean getter514pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);515}516} else if (argCount == 1) {517if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {518pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);519} else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {520// Simple setter521pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);522if (throwsException(method, PropertyVetoException.class)) {523pd.setConstrained(true);524}525}526} else if (argCount == 2) {527if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {528pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);529if (throwsException(method, PropertyVetoException.class)) {530pd.setConstrained(true);531}532}533}534} catch (IntrospectionException ex) {535// This happens if a PropertyDescriptor or IndexedPropertyDescriptor536// constructor fins that the method violates details of the deisgn537// pattern, e.g. by having an empty name, or a getter returning538// void , or whatever.539pd = null;540}541542if (pd != null) {543// If this class or one of its base classes is a PropertyChange544// source, then we assume that any properties we discover are "bound".545if (propertyChangeSource) {546pd.setBound(true);547}548addPropertyDescriptor(pd);549}550}551}552processPropertyDescriptors();553554// Allocate and populate the result array.555PropertyDescriptor result[] =556properties.values().toArray(new PropertyDescriptor[properties.size()]);557558// Set the default index.559if (defaultPropertyName != null) {560for (int i = 0; i < result.length; i++) {561if (defaultPropertyName.equals(result[i].getName())) {562defaultPropertyIndex = i;563}564}565}566567return result;568}569570private HashMap<String, List<PropertyDescriptor>> pdStore = new HashMap<>();571572/**573* Adds the property descriptor to the list store.574*/575private void addPropertyDescriptor(PropertyDescriptor pd) {576String propName = pd.getName();577List<PropertyDescriptor> list = pdStore.get(propName);578if (list == null) {579list = new ArrayList<>();580pdStore.put(propName, list);581}582if (this.beanClass != pd.getClass0()) {583// replace existing property descriptor584// only if we have types to resolve585// in the context of this.beanClass586Method read = pd.getReadMethod();587Method write = pd.getWriteMethod();588boolean cls = true;589if (read != null) cls = cls && read.getGenericReturnType() instanceof Class;590if (write != null) cls = cls && write.getGenericParameterTypes()[0] instanceof Class;591if (pd instanceof IndexedPropertyDescriptor) {592IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;593Method readI = ipd.getIndexedReadMethod();594Method writeI = ipd.getIndexedWriteMethod();595if (readI != null) cls = cls && readI.getGenericReturnType() instanceof Class;596if (writeI != null) cls = cls && writeI.getGenericParameterTypes()[1] instanceof Class;597if (!cls) {598pd = new IndexedPropertyDescriptor(ipd);599pd.updateGenericsFor(this.beanClass);600}601}602else if (!cls) {603pd = new PropertyDescriptor(pd);604pd.updateGenericsFor(this.beanClass);605}606}607list.add(pd);608}609610private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {611if (descriptors != null) {612for (PropertyDescriptor descriptor : descriptors) {613addPropertyDescriptor(descriptor);614}615}616}617618private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {619PropertyDescriptor[] descriptors = info.getPropertyDescriptors();620int index = info.getDefaultPropertyIndex();621if ((0 <= index) && (index < descriptors.length)) {622this.defaultPropertyName = descriptors[index].getName();623}624return descriptors;625}626627/**628* Populates the property descriptor table by merging the629* lists of Property descriptors.630*/631private void processPropertyDescriptors() {632if (properties == null) {633properties = new TreeMap<>();634}635636List<PropertyDescriptor> list;637638PropertyDescriptor pd, gpd, spd;639IndexedPropertyDescriptor ipd, igpd, ispd;640641Iterator<List<PropertyDescriptor>> it = pdStore.values().iterator();642while (it.hasNext()) {643pd = null; gpd = null; spd = null;644ipd = null; igpd = null; ispd = null;645646list = it.next();647648// First pass. Find the latest getter method. Merge properties649// of previous getter methods.650for (int i = 0; i < list.size(); i++) {651pd = list.get(i);652if (pd instanceof IndexedPropertyDescriptor) {653ipd = (IndexedPropertyDescriptor)pd;654if (ipd.getIndexedReadMethod() != null) {655if (igpd != null) {656igpd = new IndexedPropertyDescriptor(igpd, ipd);657} else {658igpd = ipd;659}660}661} else {662if (pd.getReadMethod() != null) {663String pdName = pd.getReadMethod().getName();664if (gpd != null) {665// Don't replace the existing read666// method if it starts with "is"667String gpdName = gpd.getReadMethod().getName();668if (gpdName.equals(pdName) || !gpdName.startsWith(IS_PREFIX)) {669gpd = new PropertyDescriptor(gpd, pd);670}671} else {672gpd = pd;673}674}675}676}677678// Second pass. Find the latest setter method which679// has the same type as the getter method.680for (int i = 0; i < list.size(); i++) {681pd = list.get(i);682if (pd instanceof IndexedPropertyDescriptor) {683ipd = (IndexedPropertyDescriptor)pd;684if (ipd.getIndexedWriteMethod() != null) {685if (igpd != null) {686if (isAssignable(igpd.getIndexedPropertyType(), ipd.getIndexedPropertyType())) {687if (ispd != null) {688ispd = new IndexedPropertyDescriptor(ispd, ipd);689} else {690ispd = ipd;691}692}693} else {694if (ispd != null) {695ispd = new IndexedPropertyDescriptor(ispd, ipd);696} else {697ispd = ipd;698}699}700}701} else {702if (pd.getWriteMethod() != null) {703if (gpd != null) {704if (isAssignable(gpd.getPropertyType(), pd.getPropertyType())) {705if (spd != null) {706spd = new PropertyDescriptor(spd, pd);707} else {708spd = pd;709}710}711} else {712if (spd != null) {713spd = new PropertyDescriptor(spd, pd);714} else {715spd = pd;716}717}718}719}720}721722// At this stage we should have either PDs or IPDs for the723// representative getters and setters. The order at which the724// property descriptors are determined represent the725// precedence of the property ordering.726pd = null; ipd = null;727728if (igpd != null && ispd != null) {729// Complete indexed properties set730// Merge any classic property descriptors731if ((gpd == spd) || (gpd == null)) {732pd = spd;733} else if (spd == null) {734pd = gpd;735} else if (spd instanceof IndexedPropertyDescriptor) {736pd = mergePropertyWithIndexedProperty(gpd, (IndexedPropertyDescriptor) spd);737} else if (gpd instanceof IndexedPropertyDescriptor) {738pd = mergePropertyWithIndexedProperty(spd, (IndexedPropertyDescriptor) gpd);739} else {740pd = mergePropertyDescriptor(gpd, spd);741}742if (igpd == ispd) {743ipd = igpd;744} else {745ipd = mergePropertyDescriptor(igpd, ispd);746}747if (pd == null) {748pd = ipd;749} else {750Class<?> propType = pd.getPropertyType();751Class<?> ipropType = ipd.getIndexedPropertyType();752if (propType.isArray() && propType.getComponentType() == ipropType) {753pd = pd.getClass0().isAssignableFrom(ipd.getClass0())754? new IndexedPropertyDescriptor(pd, ipd)755: new IndexedPropertyDescriptor(ipd, pd);756} else if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {757pd = pd.getClass0().isAssignableFrom(ipd.getClass0())758? new PropertyDescriptor(pd, ipd)759: new PropertyDescriptor(ipd, pd);760} else {761pd = ipd;762}763}764} else if (gpd != null && spd != null) {765if (igpd != null) {766gpd = mergePropertyWithIndexedProperty(gpd, igpd);767}768if (ispd != null) {769spd = mergePropertyWithIndexedProperty(spd, ispd);770}771// Complete simple properties set772if (gpd == spd) {773pd = gpd;774} else if (spd instanceof IndexedPropertyDescriptor) {775pd = mergePropertyWithIndexedProperty(gpd, (IndexedPropertyDescriptor) spd);776} else if (gpd instanceof IndexedPropertyDescriptor) {777pd = mergePropertyWithIndexedProperty(spd, (IndexedPropertyDescriptor) gpd);778} else {779pd = mergePropertyDescriptor(gpd, spd);780}781} else if (ispd != null) {782// indexed setter783pd = ispd;784// Merge any classic property descriptors785if (spd != null) {786pd = mergePropertyDescriptor(ispd, spd);787}788if (gpd != null) {789pd = mergePropertyDescriptor(ispd, gpd);790}791} else if (igpd != null) {792// indexed getter793pd = igpd;794// Merge any classic property descriptors795if (gpd != null) {796pd = mergePropertyDescriptor(igpd, gpd);797}798if (spd != null) {799pd = mergePropertyDescriptor(igpd, spd);800}801} else if (spd != null) {802// simple setter803pd = spd;804} else if (gpd != null) {805// simple getter806pd = gpd;807}808809// Very special case to ensure that an IndexedPropertyDescriptor810// doesn't contain less information than the enclosed811// PropertyDescriptor. If it does, then recreate as a812// PropertyDescriptor. See 4168833813if (pd instanceof IndexedPropertyDescriptor) {814ipd = (IndexedPropertyDescriptor)pd;815if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {816pd = new PropertyDescriptor(ipd);817}818}819820// Find the first property descriptor821// which does not have getter and setter methods.822// See regression bug 4984912.823if ( (pd == null) && (list.size() > 0) ) {824pd = list.get(0);825}826827if (pd != null) {828properties.put(pd.getName(), pd);829}830}831}832833private static boolean isAssignable(Class<?> current, Class<?> candidate) {834return ((current == null) || (candidate == null)) ? current == candidate : current.isAssignableFrom(candidate);835}836837private PropertyDescriptor mergePropertyWithIndexedProperty(PropertyDescriptor pd, IndexedPropertyDescriptor ipd) {838Class<?> type = pd.getPropertyType();839if (type.isArray() && (type.getComponentType() == ipd.getIndexedPropertyType())) {840return pd.getClass0().isAssignableFrom(ipd.getClass0())841? new IndexedPropertyDescriptor(pd, ipd)842: new IndexedPropertyDescriptor(ipd, pd);843}844return pd;845}846847/**848* Adds the property descriptor to the indexedproperty descriptor only if the849* types are the same.850*851* The most specific property descriptor will take precedence.852*/853private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd,854PropertyDescriptor pd) {855PropertyDescriptor result = null;856857Class<?> propType = pd.getPropertyType();858Class<?> ipropType = ipd.getIndexedPropertyType();859860if (propType.isArray() && propType.getComponentType() == ipropType) {861if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {862result = new IndexedPropertyDescriptor(pd, ipd);863} else {864result = new IndexedPropertyDescriptor(ipd, pd);865}866} else if ((ipd.getReadMethod() == null) && (ipd.getWriteMethod() == null)) {867if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {868result = new PropertyDescriptor(pd, ipd);869} else {870result = new PropertyDescriptor(ipd, pd);871}872} else {873// Cannot merge the pd because of type mismatch874// Return the most specific pd875if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {876result = ipd;877} else {878result = pd;879// Try to add methods which may have been lost in the type change880// See 4168833881Method write = result.getWriteMethod();882Method read = result.getReadMethod();883884if (read == null && write != null) {885read = findMethod(result.getClass0(),886GET_PREFIX + NameGenerator.capitalize(result.getName()), 0);887if (read != null) {888try {889result.setReadMethod(read);890} catch (IntrospectionException ex) {891// no consequences for failure.892}893}894}895if (write == null && read != null) {896write = findMethod(result.getClass0(),897SET_PREFIX + NameGenerator.capitalize(result.getName()), 1,898new Class<?>[] { FeatureDescriptor.getReturnType(result.getClass0(), read) });899if (write != null) {900try {901result.setWriteMethod(write);902} catch (IntrospectionException ex) {903// no consequences for failure.904}905}906}907}908}909return result;910}911912// Handle regular pd merge913private PropertyDescriptor mergePropertyDescriptor(PropertyDescriptor pd1,914PropertyDescriptor pd2) {915if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {916return new PropertyDescriptor(pd1, pd2);917} else {918return new PropertyDescriptor(pd2, pd1);919}920}921922// Handle regular ipd merge923private IndexedPropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd1,924IndexedPropertyDescriptor ipd2) {925if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {926return new IndexedPropertyDescriptor(ipd1, ipd2);927} else {928return new IndexedPropertyDescriptor(ipd2, ipd1);929}930}931932/**933* @return An array of EventSetDescriptors describing the kinds of934* events fired by the target bean.935*/936private EventSetDescriptor[] getTargetEventInfo() throws IntrospectionException {937if (events == null) {938events = new HashMap<>();939}940941// Check if the bean has its own BeanInfo that will provide942// explicit information.943EventSetDescriptor[] explicitEvents = null;944if (explicitBeanInfo != null) {945explicitEvents = explicitBeanInfo.getEventSetDescriptors();946int ix = explicitBeanInfo.getDefaultEventIndex();947if (ix >= 0 && ix < explicitEvents.length) {948defaultEventName = explicitEvents[ix].getName();949}950}951952if (explicitEvents == null && superBeanInfo != null) {953// We have no explicit BeanInfo events. Check with our parent.954EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors();955for (int i = 0 ; i < supers.length; i++) {956addEvent(supers[i]);957}958int ix = superBeanInfo.getDefaultEventIndex();959if (ix >= 0 && ix < supers.length) {960defaultEventName = supers[ix].getName();961}962}963964for (int i = 0; i < additionalBeanInfo.length; i++) {965EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors();966if (additional != null) {967for (int j = 0 ; j < additional.length; j++) {968addEvent(additional[j]);969}970}971}972973if (explicitEvents != null) {974// Add the explicit explicitBeanInfo data to our results.975for (int i = 0 ; i < explicitEvents.length; i++) {976addEvent(explicitEvents[i]);977}978979} else {980981// Apply some reflection to the current class.982983// Get an array of all the public beans methods at this level984Method methodList[] = getPublicDeclaredMethods(beanClass);985986// Find all suitable "add", "remove" and "get" Listener methods987// The name of the listener type is the key for these hashtables988// i.e, ActionListener989Map<String, Method> adds = null;990Map<String, Method> removes = null;991Map<String, Method> gets = null;992993for (int i = 0; i < methodList.length; i++) {994Method method = methodList[i];995if (method == null) {996continue;997}998// skip static methods.999int mods = method.getModifiers();1000if (Modifier.isStatic(mods)) {1001continue;1002}1003String name = method.getName();1004// Optimization avoid getParameterTypes1005if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)1006&& !name.startsWith(GET_PREFIX)) {1007continue;1008}10091010if (name.startsWith(ADD_PREFIX)) {1011Class<?> returnType = method.getReturnType();1012if (returnType == void.class) {1013Type[] parameterTypes = method.getGenericParameterTypes();1014if (parameterTypes.length == 1) {1015Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));1016if (Introspector.isSubclass(type, eventListenerType)) {1017String listenerName = name.substring(3);1018if (listenerName.length() > 0 &&1019type.getName().endsWith(listenerName)) {1020if (adds == null) {1021adds = new HashMap<>();1022}1023adds.put(listenerName, method);1024}1025}1026}1027}1028}1029else if (name.startsWith(REMOVE_PREFIX)) {1030Class<?> returnType = method.getReturnType();1031if (returnType == void.class) {1032Type[] parameterTypes = method.getGenericParameterTypes();1033if (parameterTypes.length == 1) {1034Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));1035if (Introspector.isSubclass(type, eventListenerType)) {1036String listenerName = name.substring(6);1037if (listenerName.length() > 0 &&1038type.getName().endsWith(listenerName)) {1039if (removes == null) {1040removes = new HashMap<>();1041}1042removes.put(listenerName, method);1043}1044}1045}1046}1047}1048else if (name.startsWith(GET_PREFIX)) {1049Class<?>[] parameterTypes = method.getParameterTypes();1050if (parameterTypes.length == 0) {1051Class<?> returnType = FeatureDescriptor.getReturnType(beanClass, method);1052if (returnType.isArray()) {1053Class<?> type = returnType.getComponentType();1054if (Introspector.isSubclass(type, eventListenerType)) {1055String listenerName = name.substring(3, name.length() - 1);1056if (listenerName.length() > 0 &&1057type.getName().endsWith(listenerName)) {1058if (gets == null) {1059gets = new HashMap<>();1060}1061gets.put(listenerName, method);1062}1063}1064}1065}1066}1067}10681069if (adds != null && removes != null) {1070// Now look for matching addFooListener+removeFooListener pairs.1071// Bonus if there is a matching getFooListeners method as well.1072Iterator<String> keys = adds.keySet().iterator();1073while (keys.hasNext()) {1074String listenerName = keys.next();1075// Skip any "add" which doesn't have a matching "remove" or1076// a listener name that doesn't end with Listener1077if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {1078continue;1079}1080String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));1081Method addMethod = adds.get(listenerName);1082Method removeMethod = removes.get(listenerName);1083Method getMethod = null;1084if (gets != null) {1085getMethod = gets.get(listenerName);1086}1087Class<?> argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0];10881089// generate a list of Method objects for each of the target methods:1090Method allMethods[] = getPublicDeclaredMethods(argType);1091List<Method> validMethods = new ArrayList<>(allMethods.length);1092for (int i = 0; i < allMethods.length; i++) {1093if (allMethods[i] == null) {1094continue;1095}10961097if (isEventHandler(allMethods[i])) {1098validMethods.add(allMethods[i]);1099}1100}1101Method[] methods = validMethods.toArray(new Method[validMethods.size()]);11021103EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,1104methods, addMethod,1105removeMethod,1106getMethod);11071108// If the adder method throws the TooManyListenersException then it1109// is a Unicast event source.1110if (throwsException(addMethod,1111java.util.TooManyListenersException.class)) {1112esd.setUnicast(true);1113}1114addEvent(esd);1115}1116} // if (adds != null ...1117}1118EventSetDescriptor[] result;1119if (events.size() == 0) {1120result = EMPTY_EVENTSETDESCRIPTORS;1121} else {1122// Allocate and populate the result array.1123result = new EventSetDescriptor[events.size()];1124result = events.values().toArray(result);11251126// Set the default index.1127if (defaultEventName != null) {1128for (int i = 0; i < result.length; i++) {1129if (defaultEventName.equals(result[i].getName())) {1130defaultEventIndex = i;1131}1132}1133}1134}1135return result;1136}11371138private void addEvent(EventSetDescriptor esd) {1139String key = esd.getName();1140if (esd.getName().equals("propertyChange")) {1141propertyChangeSource = true;1142}1143EventSetDescriptor old = events.get(key);1144if (old == null) {1145events.put(key, esd);1146return;1147}1148EventSetDescriptor composite = new EventSetDescriptor(old, esd);1149events.put(key, composite);1150}11511152/**1153* @return An array of MethodDescriptors describing the private1154* methods supported by the target bean.1155*/1156private MethodDescriptor[] getTargetMethodInfo() {1157if (methods == null) {1158methods = new HashMap<>(100);1159}11601161// Check if the bean has its own BeanInfo that will provide1162// explicit information.1163MethodDescriptor[] explicitMethods = null;1164if (explicitBeanInfo != null) {1165explicitMethods = explicitBeanInfo.getMethodDescriptors();1166}11671168if (explicitMethods == null && superBeanInfo != null) {1169// We have no explicit BeanInfo methods. Check with our parent.1170MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors();1171for (int i = 0 ; i < supers.length; i++) {1172addMethod(supers[i]);1173}1174}11751176for (int i = 0; i < additionalBeanInfo.length; i++) {1177MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors();1178if (additional != null) {1179for (int j = 0 ; j < additional.length; j++) {1180addMethod(additional[j]);1181}1182}1183}11841185if (explicitMethods != null) {1186// Add the explicit explicitBeanInfo data to our results.1187for (int i = 0 ; i < explicitMethods.length; i++) {1188addMethod(explicitMethods[i]);1189}11901191} else {11921193// Apply some reflection to the current class.11941195// First get an array of all the beans methods at this level1196Method methodList[] = getPublicDeclaredMethods(beanClass);11971198// Now analyze each method.1199for (int i = 0; i < methodList.length; i++) {1200Method method = methodList[i];1201if (method == null) {1202continue;1203}1204MethodDescriptor md = new MethodDescriptor(method);1205addMethod(md);1206}1207}12081209// Allocate and populate the result array.1210MethodDescriptor result[] = new MethodDescriptor[methods.size()];1211result = methods.values().toArray(result);12121213return result;1214}12151216private void addMethod(MethodDescriptor md) {1217// We have to be careful here to distinguish method by both name1218// and argument lists.1219// This method gets called a *lot, so we try to be efficient.1220String name = md.getName();12211222MethodDescriptor old = methods.get(name);1223if (old == null) {1224// This is the common case.1225methods.put(name, md);1226return;1227}12281229// We have a collision on method names. This is rare.12301231// Check if old and md have the same type.1232String[] p1 = md.getParamNames();1233String[] p2 = old.getParamNames();12341235boolean match = false;1236if (p1.length == p2.length) {1237match = true;1238for (int i = 0; i < p1.length; i++) {1239if (p1[i] != p2[i]) {1240match = false;1241break;1242}1243}1244}1245if (match) {1246MethodDescriptor composite = new MethodDescriptor(old, md);1247methods.put(name, composite);1248return;1249}12501251// We have a collision on method names with different type signatures.1252// This is very rare.12531254String longKey = makeQualifiedMethodName(name, p1);1255old = methods.get(longKey);1256if (old == null) {1257methods.put(longKey, md);1258return;1259}1260MethodDescriptor composite = new MethodDescriptor(old, md);1261methods.put(longKey, composite);1262}12631264/**1265* Creates a key for a method in a method cache.1266*/1267private static String makeQualifiedMethodName(String name, String[] params) {1268StringBuffer sb = new StringBuffer(name);1269sb.append('=');1270for (int i = 0; i < params.length; i++) {1271sb.append(':');1272sb.append(params[i]);1273}1274return sb.toString();1275}12761277private int getTargetDefaultEventIndex() {1278return defaultEventIndex;1279}12801281private int getTargetDefaultPropertyIndex() {1282return defaultPropertyIndex;1283}12841285private BeanDescriptor getTargetBeanDescriptor() {1286// Use explicit info, if available,1287if (explicitBeanInfo != null) {1288BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();1289if (bd != null) {1290return (bd);1291}1292}1293// OK, fabricate a default BeanDescriptor.1294return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass));1295}12961297private static Class<?> findCustomizerClass(Class<?> type) {1298String name = type.getName() + "Customizer";1299try {1300type = ClassFinder.findClass(name, type.getClassLoader());1301// Each customizer should inherit java.awt.Component and implement java.beans.Customizer1302// according to the section 9.3 of JavaBeans™ specification1303if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) {1304return type;1305}1306}1307catch (Exception exception) {1308// ignore any exceptions1309}1310return null;1311}13121313private boolean isEventHandler(Method m) {1314// We assume that a method is an event handler if it has a single1315// argument, whose type inherit from java.util.Event.1316Type argTypes[] = m.getGenericParameterTypes();1317if (argTypes.length != 1) {1318return false;1319}1320return isSubclass(TypeResolver.erase(TypeResolver.resolveInClass(beanClass, argTypes[0])), EventObject.class);1321}13221323/*1324* Internal method to return *public* methods within a class.1325*/1326private static Method[] getPublicDeclaredMethods(Class<?> clz) {1327// Looking up Class.getDeclaredMethods is relatively expensive,1328// so we cache the results.1329if (!ReflectUtil.isPackageAccessible(clz)) {1330return new Method[0];1331}1332synchronized (declaredMethodCache) {1333Method[] result = declaredMethodCache.get(clz);1334if (result == null) {1335result = clz.getMethods();1336for (int i = 0; i < result.length; i++) {1337Method method = result[i];1338if (!method.getDeclaringClass().equals(clz)) {1339result[i] = null; // ignore methods declared elsewhere1340}1341else {1342try {1343method = MethodFinder.findAccessibleMethod(method);1344Class<?> type = method.getDeclaringClass();1345result[i] = type.equals(clz) || type.isInterface()1346? method1347: null; // ignore methods from superclasses1348}1349catch (NoSuchMethodException exception) {1350// commented out because of 69765771351// result[i] = null; // ignore inaccessible methods1352}1353}1354}1355declaredMethodCache.put(clz, result);1356}1357return result;1358}1359}13601361//======================================================================1362// Package private support methods.1363//======================================================================13641365/**1366* Internal support for finding a target methodName with a given1367* parameter list on a given class.1368*/1369private static Method internalFindMethod(Class<?> start, String methodName,1370int argCount, Class args[]) {1371// For overriden methods we need to find the most derived version.1372// So we start with the given class and walk up the superclass chain.13731374Method method = null;13751376for (Class<?> cl = start; cl != null; cl = cl.getSuperclass()) {1377Method methods[] = getPublicDeclaredMethods(cl);1378for (int i = 0; i < methods.length; i++) {1379method = methods[i];1380if (method == null) {1381continue;1382}13831384// make sure method signature matches.1385if (method.getName().equals(methodName)) {1386Type[] params = method.getGenericParameterTypes();1387if (params.length == argCount) {1388if (args != null) {1389boolean different = false;1390if (argCount > 0) {1391for (int j = 0; j < argCount; j++) {1392if (TypeResolver.erase(TypeResolver.resolveInClass(start, params[j])) != args[j]) {1393different = true;1394continue;1395}1396}1397if (different) {1398continue;1399}1400}1401}1402return method;1403}1404}1405}1406}1407method = null;14081409// Now check any inherited interfaces. This is necessary both when1410// the argument class is itself an interface, and when the argument1411// class is an abstract class.1412Class ifcs[] = start.getInterfaces();1413for (int i = 0 ; i < ifcs.length; i++) {1414// Note: The original implementation had both methods calling1415// the 3 arg method. This is preserved but perhaps it should1416// pass the args array instead of null.1417method = internalFindMethod(ifcs[i], methodName, argCount, null);1418if (method != null) {1419break;1420}1421}1422return method;1423}14241425/**1426* Find a target methodName on a given class.1427*/1428static Method findMethod(Class<?> cls, String methodName, int argCount) {1429return findMethod(cls, methodName, argCount, null);1430}14311432/**1433* Find a target methodName with specific parameter list on a given class.1434* <p>1435* Used in the contructors of the EventSetDescriptor,1436* PropertyDescriptor and the IndexedPropertyDescriptor.1437* <p>1438* @param cls The Class object on which to retrieve the method.1439* @param methodName Name of the method.1440* @param argCount Number of arguments for the desired method.1441* @param args Array of argument types for the method.1442* @return the method or null if not found1443*/1444static Method findMethod(Class<?> cls, String methodName, int argCount,1445Class args[]) {1446if (methodName == null) {1447return null;1448}1449return internalFindMethod(cls, methodName, argCount, args);1450}14511452/**1453* Return true if class a is either equivalent to class b, or1454* if class a is a subclass of class b, i.e. if a either "extends"1455* or "implements" b.1456* Note tht either or both "Class" objects may represent interfaces.1457*/1458static boolean isSubclass(Class<?> a, Class<?> b) {1459// We rely on the fact that for any given java class or1460// primtitive type there is a unqiue Class object, so1461// we can use object equivalence in the comparisons.1462if (a == b) {1463return true;1464}1465if (a == null || b == null) {1466return false;1467}1468for (Class<?> x = a; x != null; x = x.getSuperclass()) {1469if (x == b) {1470return true;1471}1472if (b.isInterface()) {1473Class<?>[] interfaces = x.getInterfaces();1474for (int i = 0; i < interfaces.length; i++) {1475if (isSubclass(interfaces[i], b)) {1476return true;1477}1478}1479}1480}1481return false;1482}14831484/**1485* Return true iff the given method throws the given exception.1486*/1487private boolean throwsException(Method method, Class<?> exception) {1488Class exs[] = method.getExceptionTypes();1489for (int i = 0; i < exs.length; i++) {1490if (exs[i] == exception) {1491return true;1492}1493}1494return false;1495}14961497/**1498* Try to create an instance of a named class.1499* First try the classloader of "sibling", then try the system1500* classloader then the class loader of the current Thread.1501*/1502static Object instantiate(Class<?> sibling, String className)1503throws InstantiationException, IllegalAccessException,1504ClassNotFoundException {1505// First check with sibling's classloader (if any).1506ClassLoader cl = sibling.getClassLoader();1507Class<?> cls = ClassFinder.findClass(className, cl);1508return cls.newInstance();1509}15101511} // end class Introspector15121513//===========================================================================15141515/**1516* Package private implementation support class for Introspector's1517* internal use.1518* <p>1519* Mostly this is used as a placeholder for the descriptors.1520*/15211522class GenericBeanInfo extends SimpleBeanInfo {15231524private BeanDescriptor beanDescriptor;1525private EventSetDescriptor[] events;1526private int defaultEvent;1527private PropertyDescriptor[] properties;1528private int defaultProperty;1529private MethodDescriptor[] methods;1530private Reference<BeanInfo> targetBeanInfoRef;15311532public GenericBeanInfo(BeanDescriptor beanDescriptor,1533EventSetDescriptor[] events, int defaultEvent,1534PropertyDescriptor[] properties, int defaultProperty,1535MethodDescriptor[] methods, BeanInfo targetBeanInfo) {1536this.beanDescriptor = beanDescriptor;1537this.events = events;1538this.defaultEvent = defaultEvent;1539this.properties = properties;1540this.defaultProperty = defaultProperty;1541this.methods = methods;1542this.targetBeanInfoRef = (targetBeanInfo != null)1543? new SoftReference<>(targetBeanInfo)1544: null;1545}15461547/**1548* Package-private dup constructor1549* This must isolate the new object from any changes to the old object.1550*/1551GenericBeanInfo(GenericBeanInfo old) {15521553beanDescriptor = new BeanDescriptor(old.beanDescriptor);1554if (old.events != null) {1555int len = old.events.length;1556events = new EventSetDescriptor[len];1557for (int i = 0; i < len; i++) {1558events[i] = new EventSetDescriptor(old.events[i]);1559}1560}1561defaultEvent = old.defaultEvent;1562if (old.properties != null) {1563int len = old.properties.length;1564properties = new PropertyDescriptor[len];1565for (int i = 0; i < len; i++) {1566PropertyDescriptor oldp = old.properties[i];1567if (oldp instanceof IndexedPropertyDescriptor) {1568properties[i] = new IndexedPropertyDescriptor(1569(IndexedPropertyDescriptor) oldp);1570} else {1571properties[i] = new PropertyDescriptor(oldp);1572}1573}1574}1575defaultProperty = old.defaultProperty;1576if (old.methods != null) {1577int len = old.methods.length;1578methods = new MethodDescriptor[len];1579for (int i = 0; i < len; i++) {1580methods[i] = new MethodDescriptor(old.methods[i]);1581}1582}1583this.targetBeanInfoRef = old.targetBeanInfoRef;1584}15851586public PropertyDescriptor[] getPropertyDescriptors() {1587return properties;1588}15891590public int getDefaultPropertyIndex() {1591return defaultProperty;1592}15931594public EventSetDescriptor[] getEventSetDescriptors() {1595return events;1596}15971598public int getDefaultEventIndex() {1599return defaultEvent;1600}16011602public MethodDescriptor[] getMethodDescriptors() {1603return methods;1604}16051606public BeanDescriptor getBeanDescriptor() {1607return beanDescriptor;1608}16091610public java.awt.Image getIcon(int iconKind) {1611BeanInfo targetBeanInfo = getTargetBeanInfo();1612if (targetBeanInfo != null) {1613return targetBeanInfo.getIcon(iconKind);1614}1615return super.getIcon(iconKind);1616}16171618private BeanInfo getTargetBeanInfo() {1619if (this.targetBeanInfoRef == null) {1620return null;1621}1622BeanInfo targetBeanInfo = this.targetBeanInfoRef.get();1623if (targetBeanInfo == null) {1624targetBeanInfo = ThreadGroupContext.getContext().getBeanInfoFinder()1625.find(this.beanDescriptor.getBeanClass());1626if (targetBeanInfo != null) {1627this.targetBeanInfoRef = new SoftReference<>(targetBeanInfo);1628}1629}1630return targetBeanInfo;1631}1632}163316341635