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