Introspector.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /* java.beans.Introspector
  2. Copyright (C) 1998, 2002, 2003 Free Software Foundation, Inc.
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package java.beans;
  32. import gnu.java.beans.BeanInfoEmbryo;
  33. import gnu.java.beans.ExplicitBeanInfo;
  34. import gnu.java.beans.IntrospectionIncubator;
  35. import gnu.java.lang.ClassHelper;
  36. import java.util.Hashtable;
  37. import java.util.Vector;
  38. /**
  39. * Introspector is the class that does the bulk of the
  40. * design-time work in Java Beans. Every class must have
  41. * a BeanInfo in order for an RAD tool to use it; but, as
  42. * promised, you don't have to write the BeanInfo class
  43. * yourself if you don't want to. All you have to do is
  44. * call getBeanInfo() in the Introspector and it will use
  45. * standard JavaBeans-defined method signatures to
  46. * determine the information about your class.<P>
  47. *
  48. * Don't worry about it too much, though: you can provide
  49. * JavaBeans with as much customized information as you
  50. * want, or as little as you want, using the BeanInfo
  51. * interface (see BeanInfo for details).<P>
  52. *
  53. * <STRONG>Order of Operations</STRONG><P>
  54. *
  55. * When you call getBeanInfo(class c), the Introspector
  56. * first searches for BeanInfo class to see if you
  57. * provided any explicit information. It searches for a
  58. * class named &lt;bean class name&gt;BeanInfo in different
  59. * packages, first searching the bean class's package
  60. * and then moving on to search the beanInfoSearchPath.<P>
  61. *
  62. * If it does not find a BeanInfo class, it acts as though
  63. * it had found a BeanInfo class returning null from all
  64. * methods (meaning it should discover everything through
  65. * Introspection). If it does, then it takes the
  66. * information it finds in the BeanInfo class to be
  67. * canonical (that is, the information speaks for its
  68. * class as well as all superclasses).<P>
  69. *
  70. * When it has introspected the class, calls
  71. * getBeanInfo(c.getSuperclass) and adds that information
  72. * to the information it has, not adding to any information
  73. * it already has that is canonical.<P>
  74. *
  75. * <STRONG>Introspection Design Patterns</STRONG><P>
  76. *
  77. * When the Introspector goes in to read the class, it
  78. * follows a well-defined order in order to not leave any
  79. * methods unaccounted for. Its job is to step over all
  80. * of the public methods in a class and determine whether
  81. * they are part of a property, an event, or a method (in
  82. * that order).
  83. *
  84. *
  85. * <STRONG>Properties:</STRONG><P>
  86. *
  87. * <OL>
  88. * <LI>If there is a <CODE>public boolean isXXX()</CODE>
  89. * method, then XXX is a read-only boolean property.
  90. * <CODE>boolean getXXX()</CODE> may be supplied in
  91. * addition to this method, although isXXX() is the
  92. * one that will be used in this case and getXXX()
  93. * will be ignored. If there is a
  94. * <CODE>public void setXXX(boolean)</CODE> method,
  95. * it is part of this group and makes it a read-write
  96. * property.</LI>
  97. * <LI>If there is a
  98. * <CODE>public &lt;type&gt; getXXX(int)</CODE>
  99. * method, then XXX is a read-only indexed property of
  100. * type &lt;type&gt;. If there is a
  101. * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
  102. * method, then it is a read-write indexed property of
  103. * type &lt;type&gt;. There may also be a
  104. * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
  105. * <CODE>public void setXXX(&lt;type&gt;)</CODE>
  106. * method as well.</LI>
  107. * <LI>If there is a
  108. * <CODE>public void setXXX(int,&lt;type&gt;)</CODE>
  109. * method, then it is a write-only indexed property of
  110. * type &lt;type&gt;. There may also be a
  111. * <CODE>public &lt;type&gt;[] getXXX()</CODE> and a
  112. * <CODE>public void setXXX(&lt;type&gt;)</CODE>
  113. * method as well.</LI>
  114. * <LI>If there is a
  115. * <CODE>public &lt;type&gt; getXXX()</CODE> method,
  116. * then XXX is a read-only property of type
  117. * &lt;type&gt;. If there is a
  118. * <CODE>public void setXXX(&lt;type&gt;)</CODE>
  119. * method, then it will be used for the property and
  120. * the property will be considered read-write.</LI>
  121. * <LI>If there is a
  122. * <CODE>public void setXXX(&lt;type&gt;)</CODE>
  123. * method, then as long as XXX is not already used as
  124. * the name of a property, XXX is assumed to be a
  125. * write-only property of type &lt;type&gt;.</LI>
  126. * <LI>In all of the above cases, if the setXXX() method
  127. * throws <CODE>PropertyVetoException</CODE>, then the
  128. * property in question is assumed to be constrained.
  129. * No properties are ever assumed to be bound
  130. * (<STRONG>Spec Note:</STRONG> this is not in the
  131. * spec, it just makes sense). See PropertyDescriptor
  132. * for a description of bound and constrained
  133. * properties.</LI>
  134. * </OL>
  135. *
  136. * <STRONG>Events:</STRONG><P>
  137. *
  138. * If there is a pair of methods,
  139. * <CODE>public void addXXX(&lt;type&gt;)</CODE> and
  140. * <CODE>public void removeXXX(&lt;type&gt;)</CODE>, where
  141. * &lt;type&gt; is a descendant of
  142. * <CODE>java.util.EventListener</CODE>, then the pair of
  143. * methods imply that this Bean will fire events to
  144. * listeners of type &lt;type&gt;.<P>
  145. *
  146. * If the addXXX() method throws
  147. * <CODE>java.util.TooManyListenersException</CODE>, then
  148. * the event set is assumed to be <EM>unicast</EM>. See
  149. * EventSetDescriptor for a discussion of unicast event
  150. * sets.<P>
  151. *
  152. * <STRONG>Spec Note:</STRONG> the spec seems to say that
  153. * the listener type's classname must be equal to the XXX
  154. * part of addXXX() and removeXXX(), but that is not the
  155. * case in Sun's implementation, so I am assuming it is
  156. * not the case in general.<P>
  157. *
  158. * <STRONG>Methods:</STRONG><P>
  159. *
  160. * Any public methods (including those which were used
  161. * for Properties or Events) are used as Methods.
  162. *
  163. * @author John Keiser
  164. * @since JDK1.1
  165. * @see java.beans.BeanInfo
  166. */
  167. public class Introspector {
  168. public static final int USE_ALL_BEANINFO = 1;
  169. public static final int IGNORE_IMMEDIATE_BEANINFO = 2;
  170. public static final int IGNORE_ALL_BEANINFO = 3;
  171. static String[] beanInfoSearchPath = {"gnu.java.beans.info"};
  172. static Hashtable<Class<?>,BeanInfo> beanInfoCache =
  173. new Hashtable<Class<?>,BeanInfo>();
  174. private Introspector() {}
  175. /**
  176. * Get the BeanInfo for class <CODE>beanClass</CODE>,
  177. * first by looking for explicit information, next by
  178. * using standard design patterns to determine
  179. * information about the class.
  180. *
  181. * @param beanClass the class to get BeanInfo about.
  182. * @return the BeanInfo object representing the class.
  183. */
  184. public static BeanInfo getBeanInfo(Class<?> beanClass)
  185. throws IntrospectionException
  186. {
  187. BeanInfo cachedInfo;
  188. synchronized(beanClass)
  189. {
  190. cachedInfo = beanInfoCache.get(beanClass);
  191. if(cachedInfo != null)
  192. {
  193. return cachedInfo;
  194. }
  195. cachedInfo = getBeanInfo(beanClass,null);
  196. beanInfoCache.put(beanClass,cachedInfo);
  197. return cachedInfo;
  198. }
  199. }
  200. /**
  201. * Returns a {@BeanInfo} instance for the given Bean class where a flag
  202. * controls the usage of explicit BeanInfo class to retrieve that
  203. * information.
  204. *
  205. * <p>You have three options:</p>
  206. * <p>With {@link #USE_ALL_BEANINFO} the result is the same as
  207. * {@link #getBeanInfo(Class)}.</p>
  208. *
  209. * <p>Calling the method with <code>flag</code> set to
  210. * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all
  211. * explicit BeanInfo classes for the beans superclasses
  212. * but not for the bean class itself. Furthermore eventset,
  213. * property and method information is retrieved by introspection
  214. * if the explicit <code>BeanInfos</code> did not provide such data
  215. * (ie. return <code>null</code> on {@link BeanInfo.getMethodDescriptors},
  216. * {@link BeanInfo.getEventSetDescriptors} and
  217. * {@link BeanInfo.getPropertyDescriptors}.)
  218. * </p>
  219. *
  220. * <p>When the method is called with <code>flag</code< set to
  221. * {@link #IGNORE_ALL_BEANINFO} all the bean data is retrieved
  222. * by inspecting the class.</p>
  223. *
  224. * <p>Note: Any unknown value for <code>flag</code> is interpreted
  225. * as {@link #IGNORE_ALL_BEANINFO}</p>.
  226. *
  227. * @param beanClass The class whose BeanInfo should be returned.
  228. * @param flag Controls the usage of explicit <code>BeanInfo</code> classes.
  229. * @return A BeanInfo object describing the class.
  230. * @throws IntrospectionException If something goes wrong while retrieving
  231. * the bean data.
  232. */
  233. public static BeanInfo getBeanInfo(Class<?> beanClass, int flag)
  234. throws IntrospectionException
  235. {
  236. IntrospectionIncubator ii;
  237. BeanInfoEmbryo infoEmbryo;
  238. switch(flag)
  239. {
  240. case USE_ALL_BEANINFO:
  241. return getBeanInfo(beanClass);
  242. case IGNORE_IMMEDIATE_BEANINFO:
  243. Class superclass = beanClass.getSuperclass();
  244. ExplicitInfo explicit = new ExplicitInfo(superclass, null);
  245. ii = new IntrospectionIncubator();
  246. if (explicit.explicitEventSetDescriptors != null)
  247. ii.setEventStopClass(superclass);
  248. if (explicit.explicitMethodDescriptors != null)
  249. ii.setMethodStopClass(superclass);
  250. if (explicit.explicitPropertyDescriptors != null)
  251. ii.setPropertyStopClass(superclass);
  252. ii.addMethods(beanClass.getMethods());
  253. infoEmbryo = ii.getBeanInfoEmbryo();
  254. merge(infoEmbryo, explicit);
  255. infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
  256. return infoEmbryo.getBeanInfo();
  257. case IGNORE_ALL_BEANINFO:
  258. default:
  259. ii = new IntrospectionIncubator();
  260. ii.addMethods(beanClass.getMethods());
  261. infoEmbryo = ii.getBeanInfoEmbryo();
  262. infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
  263. return infoEmbryo.getBeanInfo();
  264. }
  265. }
  266. /**
  267. * Flush all of the Introspector's internal caches.
  268. *
  269. * @since 1.2
  270. */
  271. public static void flushCaches()
  272. {
  273. beanInfoCache.clear();
  274. // Clears all the intermediate ExplicitInfo instances which
  275. // have been created.
  276. // This makes sure we have to retrieve stuff like BeanDescriptors
  277. // again. (Remember that FeatureDescriptor can be modified by the user.)
  278. ExplicitInfo.flushCaches();
  279. }
  280. /**
  281. * Flush the Introspector's internal cached information for a given
  282. * class.
  283. *
  284. * @param clz the class to be flushed.
  285. * @throws NullPointerException if clz is null.
  286. * @since 1.2
  287. */
  288. public static void flushFromCaches(Class<?> clz)
  289. {
  290. synchronized (clz)
  291. {
  292. beanInfoCache.remove(clz);
  293. }
  294. }
  295. /** Adds all explicity given bean info data to the introspected
  296. * data.
  297. *
  298. * @param infoEmbryo Bean info data retrieved by introspection.
  299. * @param explicit Bean info data retrieved by BeanInfo classes.
  300. */
  301. private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit)
  302. {
  303. PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
  304. if(p!=null)
  305. {
  306. for(int i=0;i<p.length;i++)
  307. {
  308. if(!infoEmbryo.hasProperty(p[i]))
  309. {
  310. infoEmbryo.addProperty(p[i]);
  311. }
  312. }
  313. // -1 should be used to denote a missing default property but
  314. // for robustness reasons any value below zero is discarded.
  315. // Not doing so would let Classpath fail where the JDK succeeds.
  316. if(explicit.defaultProperty > -1)
  317. {
  318. infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
  319. }
  320. }
  321. EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
  322. if(e!=null)
  323. {
  324. for(int i=0;i<e.length;i++)
  325. {
  326. if(!infoEmbryo.hasEvent(e[i]))
  327. {
  328. infoEmbryo.addEvent(e[i]);
  329. }
  330. }
  331. // -1 should be used to denote a missing default event but
  332. // for robustness reasons any value below zero is discarded.
  333. // Not doing so would let Classpath fail where the JDK succeeds.
  334. if(explicit.defaultEvent > -1)
  335. {
  336. infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName());
  337. }
  338. }
  339. MethodDescriptor[] m = explicit.explicitMethodDescriptors;
  340. if(m!=null)
  341. {
  342. for(int i=0;i<m.length;i++)
  343. {
  344. if(!infoEmbryo.hasMethod(m[i]))
  345. {
  346. infoEmbryo.addMethod(m[i]);
  347. }
  348. }
  349. }
  350. infoEmbryo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
  351. infoEmbryo.setIcons(explicit.im);
  352. }
  353. /**
  354. * Get the BeanInfo for class <CODE>beanClass</CODE>,
  355. * first by looking for explicit information, next by
  356. * using standard design patterns to determine
  357. * information about the class. It crawls up the
  358. * inheritance tree until it hits <CODE>topClass</CODE>.
  359. *
  360. * @param beanClass the Bean class.
  361. * @param stopClass the class to stop at.
  362. * @return the BeanInfo object representing the class.
  363. */
  364. public static BeanInfo getBeanInfo(Class<?> beanClass, Class<?> stopClass)
  365. throws IntrospectionException
  366. {
  367. ExplicitInfo explicit = new ExplicitInfo(beanClass, stopClass);
  368. IntrospectionIncubator ii = new IntrospectionIncubator();
  369. ii.setPropertyStopClass(explicit.propertyStopClass);
  370. ii.setEventStopClass(explicit.eventStopClass);
  371. ii.setMethodStopClass(explicit.methodStopClass);
  372. ii.addMethods(beanClass.getMethods());
  373. BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo();
  374. merge(currentInfo, explicit);
  375. // Sets the info's BeanDescriptor to the one we extracted from the
  376. // explicit BeanInfo instance(s) if they contained one. Otherwise we
  377. // create the BeanDescriptor from scratch.
  378. // Note: We do not create a copy the retrieved BeanDescriptor which will allow
  379. // the user to modify the instance while it is cached. However this is how
  380. // the RI does it.
  381. currentInfo.setBeanDescriptor(
  382. (explicit.explicitBeanDescriptor == null ?
  383. new BeanDescriptor(beanClass, null) :
  384. explicit.explicitBeanDescriptor));
  385. return currentInfo.getBeanInfo();
  386. }
  387. /**
  388. * Get the search path for BeanInfo classes.
  389. *
  390. * @return the BeanInfo search path.
  391. */
  392. public static String[] getBeanInfoSearchPath()
  393. {
  394. return beanInfoSearchPath;
  395. }
  396. /**
  397. * Set the search path for BeanInfo classes.
  398. * @param beanInfoSearchPath the new BeanInfo search
  399. * path.
  400. */
  401. public static void setBeanInfoSearchPath(String[] beanInfoSearchPath)
  402. {
  403. Introspector.beanInfoSearchPath = beanInfoSearchPath;
  404. }
  405. /**
  406. * A helper method to convert a name to standard Java
  407. * naming conventions: anything with two capitals as the
  408. * first two letters remains the same, otherwise the
  409. * first letter is decapitalized. URL = URL, I = i,
  410. * MyMethod = myMethod.
  411. *
  412. * @param name the name to decapitalize.
  413. * @return the decapitalized name.
  414. */
  415. public static String decapitalize(String name)
  416. {
  417. try
  418. {
  419. if(!Character.isUpperCase(name.charAt(0)))
  420. {
  421. return name;
  422. }
  423. else
  424. {
  425. try
  426. {
  427. if(Character.isUpperCase(name.charAt(1)))
  428. {
  429. return name;
  430. }
  431. else
  432. {
  433. char[] c = name.toCharArray();
  434. c[0] = Character.toLowerCase(c[0]);
  435. return new String(c);
  436. }
  437. }
  438. catch(StringIndexOutOfBoundsException E)
  439. {
  440. char[] c = new char[1];
  441. c[0] = Character.toLowerCase(name.charAt(0));
  442. return new String(c);
  443. }
  444. }
  445. }
  446. catch(StringIndexOutOfBoundsException E)
  447. {
  448. return name;
  449. }
  450. catch(NullPointerException E)
  451. {
  452. return null;
  453. }
  454. }
  455. static BeanInfo copyBeanInfo(BeanInfo b)
  456. {
  457. java.awt.Image[] icons = new java.awt.Image[4];
  458. for(int i=1;i<=4;i++)
  459. {
  460. icons[i-1] = b.getIcon(i);
  461. }
  462. return new ExplicitBeanInfo(b.getBeanDescriptor(),
  463. b.getAdditionalBeanInfo(),
  464. b.getPropertyDescriptors(),
  465. b.getDefaultPropertyIndex(),
  466. b.getEventSetDescriptors(),
  467. b.getDefaultEventIndex(),
  468. b.getMethodDescriptors(),
  469. icons);
  470. }
  471. }
  472. class ExplicitInfo
  473. {
  474. BeanDescriptor explicitBeanDescriptor;
  475. BeanInfo[] explicitBeanInfo;
  476. PropertyDescriptor[] explicitPropertyDescriptors;
  477. EventSetDescriptor[] explicitEventSetDescriptors;
  478. MethodDescriptor[] explicitMethodDescriptors;
  479. int defaultProperty;
  480. int defaultEvent;
  481. java.awt.Image[] im = new java.awt.Image[4];
  482. Class propertyStopClass;
  483. Class eventStopClass;
  484. Class methodStopClass;
  485. static Hashtable explicitBeanInfos = new Hashtable();
  486. static Vector emptyBeanInfos = new Vector();
  487. ExplicitInfo(Class beanClass, Class stopClass)
  488. {
  489. while(beanClass != null && !beanClass.equals(stopClass))
  490. {
  491. BeanInfo explicit = findExplicitBeanInfo(beanClass);
  492. if(explicit != null)
  493. {
  494. if(explicitBeanDescriptor == null)
  495. {
  496. explicitBeanDescriptor = explicit.getBeanDescriptor();
  497. }
  498. if(explicitBeanInfo == null)
  499. {
  500. explicitBeanInfo = explicit.getAdditionalBeanInfo();
  501. }
  502. if(explicitPropertyDescriptors == null)
  503. {
  504. if(explicit.getPropertyDescriptors() != null)
  505. {
  506. explicitPropertyDescriptors = explicit.getPropertyDescriptors();
  507. defaultProperty = explicit.getDefaultPropertyIndex();
  508. propertyStopClass = beanClass;
  509. }
  510. }
  511. if(explicitEventSetDescriptors == null)
  512. {
  513. if(explicit.getEventSetDescriptors() != null)
  514. {
  515. explicitEventSetDescriptors = explicit.getEventSetDescriptors();
  516. defaultEvent = explicit.getDefaultEventIndex();
  517. eventStopClass = beanClass;
  518. }
  519. }
  520. if(explicitMethodDescriptors == null)
  521. {
  522. if(explicit.getMethodDescriptors() != null)
  523. {
  524. explicitMethodDescriptors = explicit.getMethodDescriptors();
  525. methodStopClass = beanClass;
  526. }
  527. }
  528. if(im[0] == null && im[1] == null
  529. && im[2] == null && im[3] == null)
  530. {
  531. im[0] = explicit.getIcon(0);
  532. im[1] = explicit.getIcon(1);
  533. im[2] = explicit.getIcon(2);
  534. im[3] = explicit.getIcon(3);
  535. }
  536. }
  537. beanClass = beanClass.getSuperclass();
  538. }
  539. if(propertyStopClass == null)
  540. {
  541. propertyStopClass = stopClass;
  542. }
  543. if(eventStopClass == null)
  544. {
  545. eventStopClass = stopClass;
  546. }
  547. if(methodStopClass == null)
  548. {
  549. methodStopClass = stopClass;
  550. }
  551. }
  552. /** Throws away all cached data and makes sure we re-instantiate things
  553. * like BeanDescriptors again.
  554. */
  555. static void flushCaches() {
  556. explicitBeanInfos.clear();
  557. emptyBeanInfos.clear();
  558. }
  559. static BeanInfo findExplicitBeanInfo(Class beanClass)
  560. {
  561. BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass);
  562. if(retval != null)
  563. {
  564. return retval;
  565. }
  566. else if(emptyBeanInfos.indexOf(beanClass) != -1)
  567. {
  568. return null;
  569. }
  570. else
  571. {
  572. retval = reallyFindExplicitBeanInfo(beanClass);
  573. if(retval != null)
  574. {
  575. explicitBeanInfos.put(beanClass,retval);
  576. }
  577. else
  578. {
  579. emptyBeanInfos.addElement(beanClass);
  580. }
  581. return retval;
  582. }
  583. }
  584. static BeanInfo reallyFindExplicitBeanInfo(Class beanClass)
  585. {
  586. ClassLoader beanClassLoader = beanClass.getClassLoader();
  587. BeanInfo beanInfo;
  588. beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo");
  589. if (beanInfo == null)
  590. {
  591. String newName;
  592. newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo";
  593. for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++)
  594. {
  595. if (Introspector.beanInfoSearchPath[i].equals(""))
  596. beanInfo = getBeanInfo(beanClassLoader, newName);
  597. else
  598. beanInfo = getBeanInfo(beanClassLoader,
  599. Introspector.beanInfoSearchPath[i] + "."
  600. + newName);
  601. // Returns the beanInfo if it exists and the described class matches
  602. // the one we searched.
  603. if (beanInfo != null && beanInfo.getBeanDescriptor() != null &&
  604. beanInfo.getBeanDescriptor().getBeanClass() == beanClass)
  605. return beanInfo;
  606. }
  607. }
  608. return beanInfo;
  609. }
  610. /**
  611. * Returns an instance of the given class name when it can be loaded
  612. * through the given class loader, or null otherwise.
  613. */
  614. private static BeanInfo getBeanInfo(ClassLoader cl, String infoName)
  615. {
  616. try
  617. {
  618. return (BeanInfo) Class.forName(infoName, true, cl).newInstance();
  619. }
  620. catch (ClassNotFoundException cnfe)
  621. {
  622. return null;
  623. }
  624. catch (IllegalAccessException iae)
  625. {
  626. return null;
  627. }
  628. catch (InstantiationException ie)
  629. {
  630. return null;
  631. }
  632. }
  633. }