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