深入学习java源码之Introspector.getBeanInfo()与Introspector.getPropertyDescriptors()

内省
1.什么是内省
通过反射的方式操作JavaBean的属性,jdk提供了PropertyDescription类来操作访问JavaBean的属性,Beantils工具基于此来实现。
2.内省怎么用
1).操作一个属性
Object obj = new Object();
PropertyDescriptor pd = new PropertyDescriptor(propertyName,Class); //声明属性描述对象,一次只可描述一个属性
Method m = pd.getWriterMethod();//获取setter方法
m.invoke(obj,value);
Method m = pd.getReaderMethod();//获取getter方法
Object value = m.invoke(obj);
2).操作多个属性
BeanInfo bi = Instospector.getBeanInfo(beanClass);//获取Bean描述对象
PropertyDescriptor[] pds = bi.getPropertyDescriptors();//获取属性描述对象数组
拿到属性描述对象数组之后再循环数组,剩余的操作就跟"操作一个属性"相同了。

内省

内省是Java语言对Bean类属性、事件的一种缺省处理方法。例如类A中有属性name,那我们可以通过getName,setName来得到其 值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。Java中提供了一套API用来访问某个属性的 getter/setter方法,通过这些API可以使你不需要了解这个规则,这些API存放于包java.beans中。

一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器 (PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来 调用这些方法。下面我们来看一个例子,这个例子把某个对象的所有属性名称和值都打印出来:

import java.beans.BeanInfo;  
import java.beans.Introspector;
import java.beans.PropertyDescriptor;

public class IntrospectorDemo{
String name;
public static void main(String[]args)throwsException{
Introspector Demodemo=new IntrospectorDemo();
demo.setName("WinterLau");

//如果不想把父类的属性也列出来的话,
//那getBeanInfo的第二个参数填写父类的信息
BeanInfo bi=Introspector.getBeanInfo(demo.getClass(),Object.class);
PropertyDescriptor[] props=bi.getPropertyDescriptors();
for(inti=0;i<props.length;i++){
System.out.println(props[i].getName()+"="+
props[i].getReadMethod().invoke(demo,null));
}

}

publicStringgetName(){
return name;
}

publicvoidsetName(Stringname){
this.name=name;
}
}

Web开发框架Struts中的FormBean就是通过内省机制来将表单中的数据映射到类的属性上,因此要求FormBean的每个属性要有 getter/setter方法。但也并不总是这样,什么意思呢?就是说对一个Bean类来讲,我可以没有属性,但是只要有getter/setter方 法中的其中一个,那么Java的内省机制就会认为存在一个属性,比如类中有方法setMobile,那么就认为存在一个mobile的属性,这样可以方便 我们把Bean类通过一个接口来定义而不用去关心具体实现,不用去关心Bean中数据的存储。比如我们可以把所有的getter/setter方法放到接 口里定义,但是真正数据的存取则是在具体类中去实现,这样可提高系统的扩展性。

总结 

将Java的反射以及内省应用到程序设计中去可以大大的提供程序的智能化和可扩展性。有很多项目都是采取这两种技术来实现其核心功能,例如我们前面 提到的Struts,还有用于处理XML文件的Digester项目,其实应该说几乎所有的项目都或多或少的采用这两种技术。在实际应用过程中二者要相互 结合方能发挥真正的智能化以及高度可扩展性。

 

在web.xml中注册IntrospectorCleanupListener​​监听器​​以解决struts等框架可能产生的内存泄露问题

<listener>  
<listener-class>
org.springframework.web.util.IntrospectorCleanupListener
</listener-class>
</listener>

 在Web应用程序关闭时IntrospectorCleanupListener将会刷新JDK的JavaBeans的Introspector缓存。在你的web.xml中注册这个listener来确保Web应用程序的类加载器以及其加载的类正确的释放资源。

 如果JavaBeans的Introspector已被用来分析应用程序类,系统级的Introspector缓存将持有这些类的一个硬引用。因此,这些类和Web应用程序的类加载器在Web应用程序关闭时将不会被垃圾收集器回收!而IntrospectorCleanupListener则会对其进行适当的清理,已使其能够被垃圾收集器回收。

 不幸的是,唯一能够清理Introspector的方法是刷新整个Introspector缓存,没有其他办法来确切指定应用程序所引用的类。这将删除所有其他应用程序在​​服务器​​的缓存的Introspector结果。

请注意,在使用Spring内部的bean机制时,不需要使用此监听器,因为Spring自己的introspection results cache将会立即刷新被分析过的JavaBeans Introspector cache,而仅仅会在应用程序自己的ClassLoader里面持有一个cache。虽然Spring本身不产生泄漏,注意,即使在Spring框架的类本身驻留在一个“共同”类加载器(如系统的ClassLoader)的情况下,也仍然应该使用使用IntrospectorCleanupListener。在这种情况下,这个IntrospectorCleanupListener将会妥善清理Spring的introspection cache。

应用程序类,几乎不需要直接使用JavaBeans Introspector,所以,通常都不是Introspector resource造成内存泄露。相反,许多库和框架,不清理Introspector,例如: Struts和Quartz。

需要注意的是一个简单Introspector泄漏将会导致整个Web应用程序的类加载器不会被回收!这样做的结果,将会是在web应用程序关闭时,该应用程序所有的静态类资源(比如:单实例对象)都没有得到释放。而导致内存泄露的根本原因其实并不是这些未被回收的类!

  IntrospectorCleanupListener应该注册为web.xml中的第一个Listener,在任何其他Listener之前注册,比如在Spring's ContextLoaderListener注册之前,才能确保IntrospectorCleanupListener在Web应用的生命周期适当时机生效。

 

java源码

Modifier and Type

Method and Description

​BeanInfo[]​

​getAdditionalBeanInfo()​

此方法使当前的 ​​BeanInfo​​​对象能够返回任何其他 ​​BeanInfo​​​对象的 ​​BeanInfo​​集合,该对象提供有关当前bean的附加信息。

​BeanDescriptor​

​getBeanDescriptor()​

返回提供关于该bean的整体信息的bean描述符,例如其显示名称或其定制器。

​int​

​getDefaultEventIndex()​

一个bean可能有一个默认的事件通常应用在这个bean使用。

​int​

​getDefaultPropertyIndex()​

当bean定制时,bean可能具有通常更新的默认属性。

​EventSetDescriptor[]​

​getEventSetDescriptors()​

返回bean定义此bean触发的事件类型的事件描述符。

​Image​

​getIcon(int iconKind)​

返回可用于在工具箱或工具栏中表示bean的图像。

​MethodDescriptor[]​

​getMethodDescriptors()​

返回定义此bean支持的外部可见方法的bean的方法描述符。

​PropertyDescriptor[]​

​getPropertyDescriptors()​

返回bean的所有属性的描述符。

package java.beans;

import java.awt.Image;

public interface BeanInfo {

BeanDescriptor getBeanDescriptor();

EventSetDescriptor[] getEventSetDescriptors();

int getDefaultEventIndex();

PropertyDescriptor[] getPropertyDescriptors();

int getDefaultPropertyIndex();

MethodDescriptor[] getMethodDescriptors();

BeanInfo[] getAdditionalBeanInfo();

Image getIcon(int iconKind);

/**
* Constant to indicate a 16 x 16 color icon.
*/
final static int ICON_COLOR_16x16 = 1;

/**
* Constant to indicate a 32 x 32 color icon.
*/
final static int ICON_COLOR_32x32 = 2;

/**
* Constant to indicate a 16 x 16 monochrome icon.
*/
final static int ICON_MONO_16x16 = 3;

/**
* Constant to indicate a 32 x 32 monochrome icon.
*/
final static int ICON_MONO_32x32 = 4;
}

Introspector类提供了一种标准的工具来了解目标Java Bean支持的属性,事件和方法。

对于这三种信息中的每一种,Introspector将分别分析bean的类和超类,寻找显式或隐式信息,并使用该信息构建一个全面描述目标bean的BeanInfo对象。

对于每个类“Foo”,如果存在相应的“FooBeanInfo”类,在查询信息时提供非空值,则显式信息可能可用。 我们首先通过获取目标bean类的完整的包限定名称并附加“BeanInfo”来形成一个新的类名称来查找BeanInfo类。 如果失败,那么我们将使用该名称的最终类名组件,并在BeanInfo包搜索路径中指定的每个包中查找该类。

因此,对于诸如“sun.xyz.OurButton”之类的类,我们将首先查找一个名为“sun.xyz.OurButtonBeanInfo”的BeanInfo类,如果失败,我们将在BeanInfo搜索路径中查找一个OurButtonBeanInfo类的每个包。 使用默认搜索路径,这意味着要查找“sun.beans.infos.OurButtonBeanInfo”。

如果一个类提供了关于自身的显式BeanInfo,那么我们将它们添加到从分析任何派生类中获得的BeanInfo信息中,但是我们认为显式信息对于当前类及其基类是确定的,并且不再进一步超级链。

如果我们没有在类上找到明确的BeanInfo,我们使用低级反射来研究类的方法,并应用标准设计模式来识别属性访问器,事件源或公共方法。 然后,我们继续分析类的超类,并添加其中的信息(也可能在超类链上)。

Modifier and Type

Method and Description

​static String​

​decapitalize(String​

实用方法来取一个字符串并将其转换为正常的Java变量名称大小写。

​static void​

​flushCaches()​

冲洗所有Introspector的内部缓存。

​static void​

​flushFromCaches(类<?> clz)​

刷新内部缓存信息给一个给定的类。

​static BeanInfo​

​getBeanInfo(类<?> beanClass)​

内省Java Bean并了解其所有属性,暴露的方法和事件。

​static BeanInfo​

​getBeanInfo(类<?> beanClass, 类<?> stopClass)​

内省Java bean并了解其属性,暴露的方法,低于给定的“停止”点。

​static BeanInfo​

​getBeanInfo(类<?> beanClass, 类<?> stopClass, int flags)​

对Java Bean进行内省,并了解其所有属性,暴露的方法和事件,低于给定的 ​​stopClass​​​点,受到一些控制 ​​flags​​ 。

​static BeanInfo​

​getBeanInfo(类<?> beanClass, int flags)​

对Java bean进行内省,并了解其所有属性,公开方法和事件,并遵守一些控制标志。

​static String[]​

​getBeanInfoSearchPath()​

获取将用于查找BeanInfo类的包名称列表。

​static void​

​setBeanInfoSearchPath(String[] path)​

更改将用于查找BeanInfo类的包名称列表。

package java.beans;

import com.sun.beans.TypeResolver;
import com.sun.beans.WeakCache;
import com.sun.beans.finder.ClassFinder;
import com.sun.beans.finder.MethodFinder;

import java.awt.Component;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;

import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;
import java.util.TreeMap;

import sun.reflect.misc.ReflectUtil;

public class Introspector {

public class Introspector {

// Flags that can be used to control getBeanInfo:
/**
* Flag to indicate to use of all beaninfo.
*/
public final static int USE_ALL_BEANINFO = 1;
/**
* Flag to indicate to ignore immediate beaninfo.
*/
public final static int IGNORE_IMMEDIATE_BEANINFO = 2;
/**
* Flag to indicate to ignore all beaninfo.
*/
public final static int IGNORE_ALL_BEANINFO = 3;

// Static Caches to speed up introspection.
private static final WeakCache<Class<?>, Method[]> declaredMethodCache = new WeakCache<>();

private Class<?> beanClass;
private BeanInfo explicitBeanInfo;
private BeanInfo superBeanInfo;
private BeanInfo additionalBeanInfo[];

private boolean propertyChangeSource = false;
private static Class<EventListener> eventListenerType = EventListener.class;

// These should be removed.
private String defaultEventName;
private String defaultPropertyName;
private int defaultEventIndex = -1;
private int defaultPropertyIndex = -1;

// Methods maps from Method names to MethodDescriptors
private Map<String, MethodDescriptor> methods;

// properties maps from String names to PropertyDescriptors
private Map<String, PropertyDescriptor> properties;

// events maps from String names to EventSetDescriptors
private Map<String, EventSetDescriptor> events;

private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];

static final String ADD_PREFIX = "add";
static final String REMOVE_PREFIX = "remove";
static final String GET_PREFIX = "get";
static final String SET_PREFIX = "set";
static final String IS_PREFIX = "is";

public static BeanInfo getBeanInfo(Class<?> beanClass)
throws IntrospectionException
{
if (!ReflectUtil.isPackageAccessible(beanClass)) {
return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
}
ThreadGroupContext context = ThreadGroupContext.getContext();
BeanInfo beanInfo;
synchronized (declaredMethodCache) {
beanInfo = context.getBeanInfo(beanClass);
}
if (beanInfo == null) {
beanInfo = new Introspector(beanClass, null, USE_ALL_BEANINFO).getBeanInfo();
synchronized (declaredMethodCache) {
context.putBeanInfo(beanClass, beanInfo);
}
}
return beanInfo;
}

public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
throws IntrospectionException {
return getBeanInfo(beanClass, null, flags);
}

public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
throws IntrospectionException {
return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
}

public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass,
int flags) throws IntrospectionException {
BeanInfo bi;
if (stopClass == null && flags == USE_ALL_BEANINFO) {
// Same parameters to take advantage of caching.
bi = getBeanInfo(beanClass);
} else {
bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();
}
return bi;

// Old behaviour: Make an independent copy of the BeanInfo.
//return new GenericBeanInfo(bi);
}

public static String decapitalize(String name) {
if (name == null || name.length() == 0) {
return name;
}
if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
Character.isUpperCase(name.charAt(0))){
return name;
}
char chars[] = name.toCharArray();
chars[0] = Character.toLowerCase(chars[0]);
return new String(chars);
}


public static String[] getBeanInfoSearchPath() {
return ThreadGroupContext.getContext().getBeanInfoFinder().getPackages();
}


public static void setBeanInfoSearchPath(String[] path) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPropertiesAccess();
}
ThreadGroupContext.getContext().getBeanInfoFinder().setPackages(path);
}

public static void flushCaches() {
synchronized (declaredMethodCache) {
ThreadGroupContext.getContext().clearBeanInfoCache();
declaredMethodCache.clear();
}
}

public static void flushFromCaches(Class<?> clz) {
if (clz == null) {
throw new NullPointerException();
}
synchronized (declaredMethodCache) {
ThreadGroupContext.getContext().removeBeanInfo(clz);
declaredMethodCache.put(clz, null);
}
}

private Introspector(Class<?> beanClass, Class<?> stopClass, int flags)
throws IntrospectionException {
this.beanClass = beanClass;

// Check stopClass is a superClass of startClass.
if (stopClass != null) {
boolean isSuper = false;
for (Class<?> c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {
if (c == stopClass) {
isSuper = true;
}
}
if (!isSuper) {
throw new IntrospectionException(stopClass.getName() + " not superclass of " +
beanClass.getName());
}
}

if (flags == USE_ALL_BEANINFO) {
explicitBeanInfo = findExplicitBeanInfo(beanClass);
}

Class<?> superClass = beanClass.getSuperclass();
if (superClass != stopClass) {
int newFlags = flags;
if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
newFlags = USE_ALL_BEANINFO;
}
superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
}
if (explicitBeanInfo != null) {
additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
}
if (additionalBeanInfo == null) {
additionalBeanInfo = new BeanInfo[0];
}
}

/**
* Constructs a GenericBeanInfo class from the state of the Introspector
*/
private BeanInfo getBeanInfo() throws IntrospectionException {

// the evaluation order here is import, as we evaluate the
// event sets and locate PropertyChangeListeners before we
// look for properties.
BeanDescriptor bd = getTargetBeanDescriptor();
MethodDescriptor mds[] = getTargetMethodInfo();
EventSetDescriptor esds[] = getTargetEventInfo();
PropertyDescriptor pds[] = getTargetPropertyInfo();

int defaultEvent = getTargetDefaultEventIndex();
int defaultProperty = getTargetDefaultPropertyIndex();

return new GenericBeanInfo(bd, esds, defaultEvent, pds,
defaultProperty, mds, explicitBeanInfo);

}

private static BeanInfo findExplicitBeanInfo(Class<?> beanClass) {
return ThreadGroupContext.getContext().getBeanInfoFinder().find(beanClass);
}

/**
* @return An array of PropertyDescriptors describing the editable
* properties supported by the target bean.
*/

private PropertyDescriptor[] getTargetPropertyInfo() {

// Check if the bean has its own BeanInfo that will provide
// explicit information.
PropertyDescriptor[] explicitProperties = null;
if (explicitBeanInfo != null) {
explicitProperties = getPropertyDescriptors(this.explicitBeanInfo);
}

if (explicitProperties == null && superBeanInfo != null) {
// We have no explicit BeanInfo properties. Check with our parent.
addPropertyDescriptors(getPropertyDescriptors(this.superBeanInfo));
}

for (int i = 0; i < additionalBeanInfo.length; i++) {
addPropertyDescriptors(additionalBeanInfo[i].getPropertyDescriptors());
}

if (explicitProperties != null) {
// Add the explicit BeanInfo data to our results.
addPropertyDescriptors(explicitProperties);

} else {

// Apply some reflection to the current class.

// First get an array of all the public methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);

// Now analyze each method.
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
// skip static methods.
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
continue;
}
String name = method.getName();
Class<?>[] argTypes = method.getParameterTypes();
Class<?> resultType = method.getReturnType();
int argCount = argTypes.length;
PropertyDescriptor pd = null;

if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
// Optimization. Don't bother with invalid propertyNames.
continue;
}

try {

if (argCount == 0) {
if (name.startsWith(GET_PREFIX)) {
// Simple getter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), method, null);
} else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
// Boolean getter
pd = new PropertyDescriptor(this.beanClass, name.substring(2), method, null);
}
} else if (argCount == 1) {
if (int.class.equals(argTypes[0]) && name.startsWith(GET_PREFIX)) {
pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, method, null);
} else if (void.class.equals(resultType) && name.startsWith(SET_PREFIX)) {
// Simple setter
pd = new PropertyDescriptor(this.beanClass, name.substring(3), null, method);
if (throwsException(method, PropertyVetoException.class)) {
pd.setConstrained(true);
}
}
} else if (argCount == 2) {
if (void.class.equals(resultType) && int.class.equals(argTypes[0]) && name.startsWith(SET_PREFIX)) {
pd = new IndexedPropertyDescriptor(this.beanClass, name.substring(3), null, null, null, method);
if (throwsException(method, PropertyVetoException.class)) {
pd.setConstrained(true);
}
}
}
} catch (IntrospectionException ex) {
// This happens if a PropertyDescriptor or IndexedPropertyDescriptor
// constructor fins that the method violates details of the deisgn
// pattern, e.g. by having an empty name, or a getter returning
// void , or whatever.
pd = null;
}

if (pd != null) {
// If this class or one of its base classes is a PropertyChange
// source, then we assume that any properties we discover are "bound".
if (propertyChangeSource) {
pd.setBound(true);
}
addPropertyDescriptor(pd);
}
}
}
processPropertyDescriptors();

// Allocate and populate the result array.
PropertyDescriptor result[] =
properties.values().toArray(new PropertyDescriptor[properties.size()]);

// Set the default index.
if (defaultPropertyName != null) {
for (int i = 0; i < result.length; i++) {
if (defaultPropertyName.equals(result[i].getName())) {
defaultPropertyIndex = i;
}
}
}

return result;
}

private HashMap<String, List<PropertyDescriptor>> pdStore = new HashMap<>();

/**
* Adds the property descriptor to the list store.
*/
private void addPropertyDescriptor(PropertyDescriptor pd) {
String propName = pd.getName();
List<PropertyDescriptor> list = pdStore.get(propName);
if (list == null) {
list = new ArrayList<>();
pdStore.put(propName, list);
}
if (this.beanClass != pd.getClass0()) {
// replace existing property descriptor
// only if we have types to resolve
// in the context of this.beanClass
Method read = pd.getReadMethod();
Method write = pd.getWriteMethod();
boolean cls = true;
if (read != null) cls = cls && read.getGenericReturnType() instanceof Class;
if (write != null) cls = cls && write.getGenericParameterTypes()[0] instanceof Class;
if (pd instanceof IndexedPropertyDescriptor) {
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
Method readI = ipd.getIndexedReadMethod();
Method writeI = ipd.getIndexedWriteMethod();
if (readI != null) cls = cls && readI.getGenericReturnType() instanceof Class;
if (writeI != null) cls = cls && writeI.getGenericParameterTypes()[1] instanceof Class;
if (!cls) {
pd = new IndexedPropertyDescriptor(ipd);
pd.updateGenericsFor(this.beanClass);
}
}
else if (!cls) {
pd = new PropertyDescriptor(pd);
pd.updateGenericsFor(this.beanClass);
}
}
list.add(pd);
}

private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
if (descriptors != null) {
for (PropertyDescriptor descriptor : descriptors) {
addPropertyDescriptor(descriptor);
}
}
}

private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {
PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
int index = info.getDefaultPropertyIndex();
if ((0 <= index) && (index < descriptors.length)) {
this.defaultPropertyName = descriptors[index].getName();
}
return descriptors;
}

/**
* Populates the property descriptor table by merging the
* lists of Property descriptors.
*/
private void processPropertyDescriptors() {
if (properties == null) {
properties = new TreeMap<>();
}

List<PropertyDescriptor> list;

PropertyDescriptor pd, gpd, spd;
IndexedPropertyDescriptor ipd, igpd, ispd;

Iterator<List<PropertyDescriptor>> it = pdStore.values().iterator();
while (it.hasNext()) {
pd = null; gpd = null; spd = null;
ipd = null; igpd = null; ispd = null;

list = it.next();

// First pass. Find the latest getter method. Merge properties
// of previous getter methods.
for (int i = 0; i < list.size(); i++) {
pd = list.get(i);
if (pd instanceof IndexedPropertyDescriptor) {
ipd = (IndexedPropertyDescriptor)pd;
if (ipd.getIndexedReadMethod() != null) {
if (igpd != null) {
igpd = new IndexedPropertyDescriptor(igpd, ipd);
} else {
igpd = ipd;
}
}
} else {
if (pd.getReadMethod() != null) {
String pdName = pd.getReadMethod().getName();
if (gpd != null) {
// Don't replace the existing read
// method if it starts with "is"
String gpdName = gpd.getReadMethod().getName();
if (gpdName.equals(pdName) || !gpdName.startsWith(IS_PREFIX)) {
gpd = new PropertyDescriptor(gpd, pd);
}
} else {
gpd = pd;
}
}
}
}

// Second pass. Find the latest setter method which
// has the same type as the getter method.
for (int i = 0; i < list.size(); i++) {
pd = list.get(i);
if (pd instanceof IndexedPropertyDescriptor) {
ipd = (IndexedPropertyDescriptor)pd;
if (ipd.getIndexedWriteMethod() != null) {
if (igpd != null) {
if (isAssignable(igpd.getIndexedPropertyType(), ipd.getIndexedPropertyType())) {
if (ispd != null) {
ispd = new IndexedPropertyDescriptor(ispd, ipd);
} else {
ispd = ipd;
}
}
} else {
if (ispd != null) {
ispd = new IndexedPropertyDescriptor(ispd, ipd);
} else {
ispd = ipd;
}
}
}
} else {
if (pd.getWriteMethod() != null) {
if (gpd != null) {
if (isAssignable(gpd.getPropertyType(), pd.getPropertyType())) {
if (spd != null) {
spd = new PropertyDescriptor(spd, pd);
} else {
spd = pd;
}
}
} else {
if (spd != null) {
spd = new PropertyDescriptor(spd, pd);
} else {
spd = pd;
}
}
}
}
}

// At this stage we should have either PDs or IPDs for the
// representative getters and setters. The order at which the
// property descriptors are determined represent the
// precedence of the property ordering.
pd = null; ipd = null;

if (igpd != null && ispd != null) {
// Complete indexed properties set
// Merge any classic property descriptors
if ((gpd == spd) || (gpd == null)) {
pd = spd;
} else if (spd == null) {
pd = gpd;
} else if (spd instanceof IndexedPropertyDescriptor) {
pd = mergePropertyWithIndexedProperty(gpd, (IndexedPropertyDescriptor) spd);
} else if (gpd instanceof IndexedPropertyDescriptor) {
pd = mergePropertyWithIndexedProperty(spd, (IndexedPropertyDescriptor) gpd);
} else {
pd = mergePropertyDescriptor(gpd, spd);
}
if (igpd == ispd) {
ipd = igpd;
} else {
ipd = mergePropertyDescriptor(igpd, ispd);
}
if (pd == null) {
pd = ipd;
} else {
Class<?> propType = pd.getPropertyType();
Class<?> ipropType = ipd.getIndexedPropertyType();
if (propType.isArray() && propType.getComponentType() == ipropType) {
pd = pd.getClass0().isAssignableFrom(ipd.getClass0())
? new IndexedPropertyDescriptor(pd, ipd)
: new IndexedPropertyDescriptor(ipd, pd);
} else if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
pd = pd.getClass0().isAssignableFrom(ipd.getClass0())
? new PropertyDescriptor(pd, ipd)
: new PropertyDescriptor(ipd, pd);
} else {
pd = ipd;
}
}
} else if (gpd != null && spd != null) {
if (igpd != null) {
gpd = mergePropertyWithIndexedProperty(gpd, igpd);
}
if (ispd != null) {
spd = mergePropertyWithIndexedProperty(spd, ispd);
}
// Complete simple properties set
if (gpd == spd) {
pd = gpd;
} else if (spd instanceof IndexedPropertyDescriptor) {
pd = mergePropertyWithIndexedProperty(gpd, (IndexedPropertyDescriptor) spd);
} else if (gpd instanceof IndexedPropertyDescriptor) {
pd = mergePropertyWithIndexedProperty(spd, (IndexedPropertyDescriptor) gpd);
} else {
pd = mergePropertyDescriptor(gpd, spd);
}
} else if (ispd != null) {
// indexed setter
pd = ispd;
// Merge any classic property descriptors
if (spd != null) {
pd = mergePropertyDescriptor(ispd, spd);
}
if (gpd != null) {
pd = mergePropertyDescriptor(ispd, gpd);
}
} else if (igpd != null) {
// indexed getter
pd = igpd;
// Merge any classic property descriptors
if (gpd != null) {
pd = mergePropertyDescriptor(igpd, gpd);
}
if (spd != null) {
pd = mergePropertyDescriptor(igpd, spd);
}
} else if (spd != null) {
// simple setter
pd = spd;
} else if (gpd != null) {
// simple getter
pd = gpd;
}

// Very special case to ensure that an IndexedPropertyDescriptor
// doesn't contain less information than the enclosed
// PropertyDescriptor. If it does, then recreate as a
// PropertyDescriptor. See 4168833
if (pd instanceof IndexedPropertyDescriptor) {
ipd = (IndexedPropertyDescriptor)pd;
if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {
pd = new PropertyDescriptor(ipd);
}
}

// Find the first property descriptor
// which does not have getter and setter methods.
// See regression bug 4984912.
if ( (pd == null) && (list.size() > 0) ) {
pd = list.get(0);
}

if (pd != null) {
properties.put(pd.getName(), pd);
}
}
}

private static boolean isAssignable(Class<?> current, Class<?> candidate) {
return ((current == null) || (candidate == null)) ? current == candidate : current.isAssignableFrom(candidate);
}

private PropertyDescriptor mergePropertyWithIndexedProperty(PropertyDescriptor pd, IndexedPropertyDescriptor ipd) {
Class<?> type = pd.getPropertyType();
if (type.isArray() && (type.getComponentType() == ipd.getIndexedPropertyType())) {
return pd.getClass0().isAssignableFrom(ipd.getClass0())
? new IndexedPropertyDescriptor(pd, ipd)
: new IndexedPropertyDescriptor(ipd, pd);
}
return pd;
}

/**
* Adds the property descriptor to the indexedproperty descriptor only if the
* types are the same.
*
* The most specific property descriptor will take precedence.
*/
private PropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd,
PropertyDescriptor pd) {
PropertyDescriptor result = null;

Class<?> propType = pd.getPropertyType();
Class<?> ipropType = ipd.getIndexedPropertyType();

if (propType.isArray() && propType.getComponentType() == ipropType) {
if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
result = new IndexedPropertyDescriptor(pd, ipd);
} else {
result = new IndexedPropertyDescriptor(ipd, pd);
}
} else if ((ipd.getReadMethod() == null) && (ipd.getWriteMethod() == null)) {
if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
result = new PropertyDescriptor(pd, ipd);
} else {
result = new PropertyDescriptor(ipd, pd);
}
} else {
// Cannot merge the pd because of type mismatch
// Return the most specific pd
if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
result = ipd;
} else {
result = pd;
// Try to add methods which may have been lost in the type change
// See 4168833
Method write = result.getWriteMethod();
Method read = result.getReadMethod();

if (read == null && write != null) {
read = findMethod(result.getClass0(),
GET_PREFIX + NameGenerator.capitalize(result.getName()), 0);
if (read != null) {
try {
result.setReadMethod(read);
} catch (IntrospectionException ex) {
// no consequences for failure.
}
}
}
if (write == null && read != null) {
write = findMethod(result.getClass0(),
SET_PREFIX + NameGenerator.capitalize(result.getName()), 1,
new Class<?>[] { FeatureDescriptor.getReturnType(result.getClass0(), read) });
if (write != null) {
try {
result.setWriteMethod(write);
} catch (IntrospectionException ex) {
// no consequences for failure.
}
}
}
}
}
return result;
}

// Handle regular pd merge
private PropertyDescriptor mergePropertyDescriptor(PropertyDescriptor pd1,
PropertyDescriptor pd2) {
if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
return new PropertyDescriptor(pd1, pd2);
} else {
return new PropertyDescriptor(pd2, pd1);
}
}

// Handle regular ipd merge
private IndexedPropertyDescriptor mergePropertyDescriptor(IndexedPropertyDescriptor ipd1,
IndexedPropertyDescriptor ipd2) {
if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
return new IndexedPropertyDescriptor(ipd1, ipd2);
} else {
return new IndexedPropertyDescriptor(ipd2, ipd1);
}
}

/**
* @return An array of EventSetDescriptors describing the kinds of
* events fired by the target bean.
*/
private EventSetDescriptor[] getTargetEventInfo() throws IntrospectionException {
if (events == null) {
events = new HashMap<>();
}

// Check if the bean has its own BeanInfo that will provide
// explicit information.
EventSetDescriptor[] explicitEvents = null;
if (explicitBeanInfo != null) {
explicitEvents = explicitBeanInfo.getEventSetDescriptors();
int ix = explicitBeanInfo.getDefaultEventIndex();
if (ix >= 0 && ix < explicitEvents.length) {
defaultEventName = explicitEvents[ix].getName();
}
}

if (explicitEvents == null && superBeanInfo != null) {
// We have no explicit BeanInfo events. Check with our parent.
EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors();
for (int i = 0 ; i < supers.length; i++) {
addEvent(supers[i]);
}
int ix = superBeanInfo.getDefaultEventIndex();
if (ix >= 0 && ix < supers.length) {
defaultEventName = supers[ix].getName();
}
}

for (int i = 0; i < additionalBeanInfo.length; i++) {
EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors();
if (additional != null) {
for (int j = 0 ; j < additional.length; j++) {
addEvent(additional[j]);
}
}
}

if (explicitEvents != null) {
// Add the explicit explicitBeanInfo data to our results.
for (int i = 0 ; i < explicitEvents.length; i++) {
addEvent(explicitEvents[i]);
}

} else {

// Apply some reflection to the current class.

// Get an array of all the public beans methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);

// Find all suitable "add", "remove" and "get" Listener methods
// The name of the listener type is the key for these hashtables
// i.e, ActionListener
Map<String, Method> adds = null;
Map<String, Method> removes = null;
Map<String, Method> gets = null;

for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
// skip static methods.
int mods = method.getModifiers();
if (Modifier.isStatic(mods)) {
continue;
}
String name = method.getName();
// Optimization avoid getParameterTypes
if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX)
&& !name.startsWith(GET_PREFIX)) {
continue;
}

if (name.startsWith(ADD_PREFIX)) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
Type[] parameterTypes = method.getGenericParameterTypes();
if (parameterTypes.length == 1) {
Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(3);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (adds == null) {
adds = new HashMap<>();
}
adds.put(listenerName, method);
}
}
}
}
}
else if (name.startsWith(REMOVE_PREFIX)) {
Class<?> returnType = method.getReturnType();
if (returnType == void.class) {
Type[] parameterTypes = method.getGenericParameterTypes();
if (parameterTypes.length == 1) {
Class<?> type = TypeResolver.erase(TypeResolver.resolveInClass(beanClass, parameterTypes[0]));
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(6);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (removes == null) {
removes = new HashMap<>();
}
removes.put(listenerName, method);
}
}
}
}
}
else if (name.startsWith(GET_PREFIX)) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 0) {
Class<?> returnType = FeatureDescriptor.getReturnType(beanClass, method);
if (returnType.isArray()) {
Class<?> type = returnType.getComponentType();
if (Introspector.isSubclass(type, eventListenerType)) {
String listenerName = name.substring(3, name.length() - 1);
if (listenerName.length() > 0 &&
type.getName().endsWith(listenerName)) {
if (gets == null) {
gets = new HashMap<>();
}
gets.put(listenerName, method);
}
}
}
}
}
}

if (adds != null && removes != null) {
// Now look for matching addFooListener+removeFooListener pairs.
// Bonus if there is a matching getFooListeners method as well.
Iterator<String> keys = adds.keySet().iterator();
while (keys.hasNext()) {
String listenerName = keys.next();
// Skip any "add" which doesn't have a matching "remove" or
// a listener name that doesn't end with Listener
if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
continue;
}
String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
Method addMethod = adds.get(listenerName);
Method removeMethod = removes.get(listenerName);
Method getMethod = null;
if (gets != null) {
getMethod = gets.get(listenerName);
}
Class<?> argType = FeatureDescriptor.getParameterTypes(beanClass, addMethod)[0];

// generate a list of Method objects for each of the target methods:
Method allMethods[] = getPublicDeclaredMethods(argType);
List<Method> validMethods = new ArrayList<>(allMethods.length);
for (int i = 0; i < allMethods.length; i++) {
if (allMethods[i] == null) {
continue;
}

if (isEventHandler(allMethods[i])) {
validMethods.add(allMethods[i]);
}
}
Method[] methods = validMethods.toArray(new Method[validMethods.size()]);

EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,
methods, addMethod,
removeMethod,
getMethod);

// If the adder method throws the TooManyListenersException then it
// is a Unicast event source.
if (throwsException(addMethod,
java.util.TooManyListenersException.class)) {
esd.setUnicast(true);
}
addEvent(esd);
}
} // if (adds != null ...
}
EventSetDescriptor[] result;
if (events.size() == 0) {
result = EMPTY_EVENTSETDESCRIPTORS;
} else {
// Allocate and populate the result array.
result = new EventSetDescriptor[events.size()];
result = events.values().toArray(result);

// Set the default index.
if (defaultEventName != null) {
for (int i = 0; i < result.length; i++) {
if (defaultEventName.equals(result[i].getName())) {
defaultEventIndex = i;
}
}
}
}
return result;
}

private void addEvent(EventSetDescriptor esd) {
String key = esd.getName();
if (esd.getName().equals("propertyChange")) {
propertyChangeSource = true;
}
EventSetDescriptor old = events.get(key);
if (old == null) {
events.put(key, esd);
return;
}
EventSetDescriptor composite = new EventSetDescriptor(old, esd);
events.put(key, composite);
}

private MethodDescriptor[] getTargetMethodInfo() {
if (methods == null) {
methods = new HashMap<>(100);
}

// Check if the bean has its own BeanInfo that will provide
// explicit information.
MethodDescriptor[] explicitMethods = null;
if (explicitBeanInfo != null) {
explicitMethods = explicitBeanInfo.getMethodDescriptors();
}

if (explicitMethods == null && superBeanInfo != null) {
// We have no explicit BeanInfo methods. Check with our parent.
MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors();
for (int i = 0 ; i < supers.length; i++) {
addMethod(supers[i]);
}
}

for (int i = 0; i < additionalBeanInfo.length; i++) {
MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors();
if (additional != null) {
for (int j = 0 ; j < additional.length; j++) {
addMethod(additional[j]);
}
}
}

if (explicitMethods != null) {
// Add the explicit explicitBeanInfo data to our results.
for (int i = 0 ; i < explicitMethods.length; i++) {
addMethod(explicitMethods[i]);
}

} else {

// Apply some reflection to the current class.

// First get an array of all the beans methods at this level
Method methodList[] = getPublicDeclaredMethods(beanClass);

// Now analyze each method.
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
if (method == null) {
continue;
}
MethodDescriptor md = new MethodDescriptor(method);
addMethod(md);
}
}

// Allocate and populate the result array.
MethodDescriptor result[] = new MethodDescriptor[methods.size()];
result = methods.values().toArray(result);

return result;
}

private void addMethod(MethodDescriptor md) {
// We have to be careful here to distinguish method by both name
// and argument lists.
// This method gets called a *lot, so we try to be efficient.
String name = md.getName();

MethodDescriptor old = methods.get(name);
if (old == null) {
// This is the common case.
methods.put(name, md);
return;
}

// We have a collision on method names. This is rare.

// Check if old and md have the same type.
String[] p1 = md.getParamNames();
String[] p2 = old.getParamNames();

boolean match = false;
if (p1.length == p2.length) {
match = true;
for (int i = 0; i < p1.length; i++) {
if (p1[i] != p2[i]) {
match = false;
break;
}
}
}
if (match) {
MethodDescriptor composite = new MethodDescriptor(old, md);
methods.put(name, composite);
return;
}

// We have a collision on method names with different type signatures.
// This is very rare.

String longKey = makeQualifiedMethodName(name, p1);
old = methods.get(longKey);
if (old == null) {
methods.put(longKey, md);
return;
}
MethodDescriptor composite = new MethodDescriptor(old, md);
methods.put(longKey, composite);
}

/**
* Creates a key for a method in a method cache.
*/
private static String makeQualifiedMethodName(String name, String[] params) {
StringBuffer sb = new StringBuffer(name);
sb.append('=');
for (int i = 0; i < params.length; i++) {
sb.append(':');
sb.append(params[i]);
}
return sb.toString();
}

private int getTargetDefaultEventIndex() {
return defaultEventIndex;
}

private int getTargetDefaultPropertyIndex() {
return defaultPropertyIndex;
}

private BeanDescriptor getTargetBeanDescriptor() {
// Use explicit info, if available,
if (explicitBeanInfo != null) {
BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
if (bd != null) {
return (bd);
}
}
// OK, fabricate a default BeanDescriptor.
return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass));
}

private static Class<?> findCustomizerClass(Class<?> type) {
String name = type.getName() + "Customizer";
try {
type = ClassFinder.findClass(name, type.getClassLoader());
// Each customizer should inherit java.awt.Component and implement java.beans.Customizer
// according to the section 9.3 of JavaBeans™ specification
if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) {
return type;
}
}
catch (Exception exception) {
// ignore any exceptions
}
return null;
}

private boolean isEventHandler(Method m) {
// We assume that a method is an event handler if it has a single
// argument, whose type inherit from java.util.Event.
Type argTypes[] = m.getGenericParameterTypes();
if (argTypes.length != 1) {
return false;
}
return isSubclass(TypeResolver.erase(TypeResolver.resolveInClass(beanClass, argTypes[0])), EventObject.class);
}

private static Method[] getPublicDeclaredMethods(Class<?> clz) {
// Looking up Class.getDeclaredMethods is relatively expensive,
// so we cache the results.
if (!ReflectUtil.isPackageAccessible(clz)) {
return new Method[0];
}
synchronized (declaredMethodCache) {
Method[] result = declaredMethodCache.get(clz);
if (result == null) {
result = clz.getMethods();
for (int i = 0; i < result.length; i++) {
Method method = result[i];
if (!method.getDeclaringClass().equals(clz)) {
result[i] = null; // ignore methods declared elsewhere
}
else {
try {
method = MethodFinder.findAccessibleMethod(method);
Class<?> type = method.getDeclaringClass();
result[i] = type.equals(clz) || type.isInterface()
? method
: null; // ignore methods from superclasses
}
catch (NoSuchMethodException exception) {
// commented out because of 6976577
// result[i] = null; // ignore inaccessible methods
}
}
}
declaredMethodCache.put(clz, result);
}
return result;
}
}

private static Method internalFindMethod(Class<?> start, String methodName,
int argCount, Class args[]) {
// For overriden methods we need to find the most derived version.
// So we start with the given class and walk up the superclass chain.

Method method = null;

for (Class<?> cl = start; cl != null; cl = cl.getSuperclass()) {
Method methods[] = getPublicDeclaredMethods(cl);
for (int i = 0; i < methods.length; i++) {
method = methods[i];
if (method == null) {
continue;
}

// make sure method signature matches.
if (method.getName().equals(methodName)) {
Type[] params = method.getGenericParameterTypes();
if (params.length == argCount) {
if (args != null) {
boolean different = false;
if (argCount > 0) {
for (int j = 0; j < argCount; j++) {
if (TypeResolver.erase(TypeResolver.resolveInClass(start, params[j])) != args[j]) {
different = true;
continue;
}
}
if (different) {
continue;
}
}
}
return method;
}
}
}
}
method = null;

// Now check any inherited interfaces. This is necessary both when
// the argument class is itself an interface, and when the argument
// class is an abstract class.
Class ifcs[] = start.getInterfaces();
for (int i = 0 ; i < ifcs.length; i++) {
// Note: The original implementation had both methods calling
// the 3 arg method. This is preserved but perhaps it should
// pass the args array instead of null.
method = internalFindMethod(ifcs[i], methodName, argCount, null);
if (method != null) {
break;
}
}
return method;
}

static Method findMethod(Class<?> cls, String methodName, int argCount) {
return findMethod(cls, methodName, argCount, null);
}


static Method findMethod(Class<?> cls, String methodName, int argCount,
Class args[]) {
if (methodName == null) {
return null;
}
return internalFindMethod(cls, methodName, argCount, args);
}

static boolean isSubclass(Class<?> a, Class<?> b) {
// We rely on the fact that for any given java class or
// primtitive type there is a unqiue Class object, so
// we can use object equivalence in the comparisons.
if (a == b) {
return true;
}
if (a == null || b == null) {
return false;
}
for (Class<?> x = a; x != null; x = x.getSuperclass()) {
if (x == b) {
return true;
}
if (b.isInterface()) {
Class<?>[] interfaces = x.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
if (isSubclass(interfaces[i], b)) {
return true;
}
}
}
}
return false;
}


private boolean throwsException(Method method, Class<?> exception) {
Class exs[] = method.getExceptionTypes();
for (int i = 0; i < exs.length; i++) {
if (exs[i] == exception) {
return true;
}
}
return false;
}

/**
* Try to create an instance of a named class.
* First try the classloader of "sibling", then try the system
* classloader then the class loader of the current Thread.
*/
static Object instantiate(Class<?> sibling, String className)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
// First check with sibling's classloader (if any).
ClassLoader cl = sibling.getClassLoader();
Class<?> cls = ClassFinder.findClass(className, cl);
return cls.newInstance();
}

} // end class Introspector


class GenericBeanInfo extends SimpleBeanInfo {

private BeanDescriptor beanDescriptor;
private EventSetDescriptor[] events;
private int defaultEvent;
private PropertyDescriptor[] properties;
private int defaultProperty;
private MethodDescriptor[] methods;
private Reference<BeanInfo> targetBeanInfoRef;

public GenericBeanInfo(BeanDescriptor beanDescriptor,
EventSetDescriptor[] events, int defaultEvent,
PropertyDescriptor[] properties, int defaultProperty,
MethodDescriptor[] methods, BeanInfo targetBeanInfo) {
this.beanDescriptor = beanDescriptor;
this.events = events;
this.defaultEvent = defaultEvent;
this.properties = properties;
this.defaultProperty = defaultProperty;
this.methods = methods;
this.targetBeanInfoRef = (targetBeanInfo != null)
? new SoftReference<>(targetBeanInfo)
: null;
}

/**
* Package-private dup constructor
* This must isolate the new object from any changes to the old object.
*/
GenericBeanInfo(GenericBeanInfo old) {

beanDescriptor = new BeanDescriptor(old.beanDescriptor);
if (old.events != null) {
int len = old.events.length;
events = new EventSetDescriptor[len];
for (int i = 0; i < len; i++) {
events[i] = new EventSetDescriptor(old.events[i]);
}
}
defaultEvent = old.defaultEvent;
if (old.properties != null) {
int len = old.properties.length;
properties = new PropertyDescriptor[len];
for (int i = 0; i < len; i++) {
PropertyDescriptor oldp = old.properties[i];
if (oldp instanceof IndexedPropertyDescriptor) {
properties[i] = new IndexedPropertyDescriptor(
(IndexedPropertyDescriptor) oldp);
} else {
properties[i] = new PropertyDescriptor(oldp);
}
}
}
defaultProperty = old.defaultProperty;
if (old.methods != null) {
int len = old.methods.length;
methods = new MethodDescriptor[len];
for (int i = 0; i < len; i++) {
methods[i] = new MethodDescriptor(old.methods[i]);
}
}
this.targetBeanInfoRef = old.targetBeanInfoRef;
}

public PropertyDescriptor[] getPropertyDescriptors() {
return properties;
}

public int getDefaultPropertyIndex() {
return defaultProperty;
}

public EventSetDescriptor[] getEventSetDescriptors() {
return events;
}

public int getDefaultEventIndex() {
return defaultEvent;
}

public MethodDescriptor[] getMethodDescriptors() {
return methods;
}

public BeanDescriptor getBeanDescriptor() {
return beanDescriptor;
}

public java.awt.Image getIcon(int iconKind) {
BeanInfo targetBeanInfo = getTargetBeanInfo();
if (targetBeanInfo != null) {
return targetBeanInfo.getIcon(iconKind);
}
return super.getIcon(iconKind);
}

private BeanInfo getTargetBeanInfo() {
if (this.targetBeanInfoRef == null) {
return null;
}
BeanInfo targetBeanInfo = this.targetBeanInfoRef.get();
if (targetBeanInfo == null) {
targetBeanInfo = ThreadGroupContext.getContext().getBeanInfoFinder()
.find(this.beanDescriptor.getBeanClass());
if (targetBeanInfo != null) {
this.targetBeanInfoRef = new SoftReference<>(targetBeanInfo);
}
}
return targetBeanInfo;
}
}