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