Source for java.beans.Introspector

   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 beanInfoCache = new Hashtable();
 186:   
 187:   private Introspector() {}
 188:   
 189:   /** 
 190:    * Get the BeanInfo for class <CODE>beanClass</CODE>,
 191:    * first by looking for explicit information, next by
 192:    * using standard design patterns to determine
 193:    * information about the class.
 194:    *
 195:    * @param beanClass the class to get BeanInfo about.
 196:    * @return the BeanInfo object representing the class.
 197:    */
 198:   public static BeanInfo getBeanInfo(Class beanClass) 
 199:     throws IntrospectionException 
 200:   {
 201:     BeanInfo cachedInfo;
 202:     synchronized(beanClass) 
 203:       {
 204:     cachedInfo = (BeanInfo)beanInfoCache.get(beanClass);
 205:     if(cachedInfo != null) 
 206:       {
 207:         return cachedInfo;
 208:       }
 209:     cachedInfo = getBeanInfo(beanClass,null);
 210:     beanInfoCache.put(beanClass,cachedInfo);
 211:     return cachedInfo;
 212:       }
 213:   }
 214:   
 215:   /**
 216:    * Returns a {@BeanInfo} instance for the given Bean class where a flag
 217:    * controls the usage of explicit BeanInfo class to retrieve that
 218:    * information.
 219:    * 
 220:    * <p>You have three options:</p>
 221:    * <p>With {@link #USE_ALL_BEANINFO} the result is the same as
 222:    * {@link #getBeanInfo(Class)}.</p>
 223:    * 
 224:    * <p>Calling the method with <code>flag</code> set to
 225:    * {@link #IGNORE_IMMEDIATE_BEANINFO} will let it use all
 226:    * explicit BeanInfo classes for the beans superclasses
 227:    * but not for the bean class itself. Furthermore eventset,
 228:    * property and method information is retrieved by introspection
 229:    * if the explicit <code>BeanInfos</code> did not provide such data
 230:    * (ie. return <code>null</code> on {@link BeanInfo.getMethodDescriptors},
 231:    * {@link BeanInfo.getEventSetDescriptors} and
 232:    * {@link BeanInfo.getPropertyDescriptors}.)
 233:    * </p>
 234:    * 
 235:    * <p>When the method is called with <code>flag</code< set to
 236:    * {@link #IGNORE_ALL_BEANINFO} all the bean data is retrieved
 237:    * by inspecting the class.</p>
 238:    * 
 239:    * <p>Note: Any unknown value for <code>flag</code> is interpreted
 240:    * as {@link #IGNORE_ALL_BEANINFO}</p>.
 241:    * 
 242:    * @param beanClass The class whose BeanInfo should be returned.
 243:    * @param flag Controls the usage of explicit <code>BeanInfo</code> classes.
 244:    * @return A BeanInfo object describing the class. 
 245:    * @throws IntrospectionException If something goes wrong while retrieving
 246:    *    the bean data.
 247:    */
 248:   public static BeanInfo getBeanInfo(Class beanClass, int flag)
 249:     throws IntrospectionException
 250:   {
 251:     IntrospectionIncubator ii;
 252:     BeanInfoEmbryo infoEmbryo;
 253:     
 254:     switch(flag)
 255:     {
 256:       case USE_ALL_BEANINFO:
 257:         return getBeanInfo(beanClass);
 258:       case IGNORE_IMMEDIATE_BEANINFO:
 259:         Class superclass = beanClass.getSuperclass();
 260:         ExplicitInfo explicit = new ExplicitInfo(superclass, null);
 261:         
 262:         ii = new IntrospectionIncubator();
 263:         if (explicit.explicitEventSetDescriptors != null)
 264:           ii.setEventStopClass(superclass);
 265:         
 266:         if (explicit.explicitMethodDescriptors != null)
 267:           ii.setMethodStopClass(superclass);
 268:         
 269:         if (explicit.explicitPropertyDescriptors != null)
 270:           ii.setPropertyStopClass(superclass);
 271:         
 272:         ii.addMethods(beanClass.getMethods());
 273: 
 274:         infoEmbryo = ii.getBeanInfoEmbryo();
 275:         merge(infoEmbryo, explicit);
 276: 
 277:         infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
 278:         
 279:         return infoEmbryo.getBeanInfo();
 280:       case IGNORE_ALL_BEANINFO:
 281:       default:
 282:         ii = new IntrospectionIncubator();
 283:         ii.addMethods(beanClass.getMethods());
 284:         infoEmbryo = ii.getBeanInfoEmbryo();
 285:         infoEmbryo.setBeanDescriptor(new BeanDescriptor(beanClass, null));
 286:         
 287:         return infoEmbryo.getBeanInfo();
 288:     }
 289:   }
 290: 
 291:   /**
 292:    * Flush all of the Introspector's internal caches.
 293:    *
 294:    * @since 1.2
 295:    */
 296:   public static void flushCaches()
 297:   {
 298:     beanInfoCache.clear();
 299: 
 300:     // Clears all the intermediate ExplicitInfo instances which
 301:     // have been created.
 302:     // This makes sure we have to retrieve stuff like BeanDescriptors
 303:     // again. (Remember that FeatureDescriptor can be modified by the user.)
 304:     ExplicitInfo.flushCaches();
 305:   }
 306: 
 307:   /**
 308:    * Flush the Introspector's internal cached information for a given
 309:    * class.
 310:    *
 311:    * @param clz the class to be flushed.
 312:    * @throws NullPointerException if clz is null.
 313:    * @since 1.2
 314:    */
 315:   public static void flushFromCaches(Class clz)
 316:   {
 317:     synchronized (clz)
 318:       {
 319:     beanInfoCache.remove(clz);
 320:       }
 321:   }
 322: 
 323:   /** Adds all explicity given bean info data to the introspected
 324:    * data.
 325:    * 
 326:    * @param infoEmbryo Bean info data retrieved by introspection.
 327:    * @param explicit Bean info data retrieved by BeanInfo classes.
 328:    */
 329:   private static void merge(BeanInfoEmbryo infoEmbryo, ExplicitInfo explicit)
 330:   {
 331:     PropertyDescriptor[] p = explicit.explicitPropertyDescriptors;
 332:     if(p!=null) 
 333:       {
 334:     for(int i=0;i<p.length;i++) 
 335:       {
 336:         if(!infoEmbryo.hasProperty(p[i])) 
 337:           {
 338:         infoEmbryo.addProperty(p[i]);
 339:           }
 340:       }
 341:     
 342:     // -1 should be used to denote a missing default property but
 343:     // for robustness reasons any value below zero is discarded.
 344:     // Not doing so would let Classpath fail where the JDK succeeds.
 345:     if(explicit.defaultProperty > -1) 
 346:       {
 347:         infoEmbryo.setDefaultPropertyName(p[explicit.defaultProperty].getName());
 348:       }
 349:       }
 350:     EventSetDescriptor[] e = explicit.explicitEventSetDescriptors;
 351:     if(e!=null) 
 352:       {
 353:     for(int i=0;i<e.length;i++) 
 354:       {
 355:         if(!infoEmbryo.hasEvent(e[i])) 
 356:           {
 357:         infoEmbryo.addEvent(e[i]);
 358:           }
 359:       }
 360:     
 361:     // -1 should be used to denote a missing default event but
 362:     // for robustness reasons any value below zero is discarded.
 363:     // Not doing so would let Classpath fail where the JDK succeeds.
 364:     if(explicit.defaultEvent > -1) 
 365:       {
 366:         infoEmbryo.setDefaultEventName(e[explicit.defaultEvent].getName());
 367:       }
 368:       }
 369:     MethodDescriptor[] m = explicit.explicitMethodDescriptors;
 370:     if(m!=null) 
 371:       {
 372:     for(int i=0;i<m.length;i++) 
 373:       {
 374:         if(!infoEmbryo.hasMethod(m[i])) 
 375:           {
 376:         infoEmbryo.addMethod(m[i]);
 377:           }
 378:       }
 379:       }
 380: 
 381:     infoEmbryo.setAdditionalBeanInfo(explicit.explicitBeanInfo);
 382:     infoEmbryo.setIcons(explicit.im);
 383:     
 384:   }
 385:   
 386:   /** 
 387:    * Get the BeanInfo for class <CODE>beanClass</CODE>,
 388:    * first by looking for explicit information, next by
 389:    * using standard design patterns to determine
 390:    * information about the class.  It crawls up the
 391:    * inheritance tree until it hits <CODE>topClass</CODE>.
 392:    *
 393:    * @param beanClass the Bean class.
 394:    * @param stopClass the class to stop at.
 395:    * @return the BeanInfo object representing the class.
 396:    */
 397:   public static BeanInfo getBeanInfo(Class beanClass, Class stopClass) 
 398:     throws IntrospectionException 
 399:   {
 400:     ExplicitInfo explicit = new ExplicitInfo(beanClass, stopClass);
 401: 
 402:     IntrospectionIncubator ii = new IntrospectionIncubator();
 403:     ii.setPropertyStopClass(explicit.propertyStopClass);
 404:     ii.setEventStopClass(explicit.eventStopClass);
 405:     ii.setMethodStopClass(explicit.methodStopClass);
 406:     ii.addMethods(beanClass.getMethods());
 407:     
 408:     BeanInfoEmbryo currentInfo = ii.getBeanInfoEmbryo();
 409:     
 410:     merge(currentInfo, explicit);
 411:     
 412:     //  Sets the info's BeanDescriptor to the one we extracted from the
 413:     // explicit BeanInfo instance(s) if they contained one. Otherwise we
 414:     // create the BeanDescriptor from scratch.
 415:     // Note: We do not create a copy the retrieved BeanDescriptor which will allow
 416:     // the user to modify the instance while it is cached. However this is how
 417:     // the RI does it.
 418:     currentInfo.setBeanDescriptor(
 419:         (explicit.explicitBeanDescriptor == null ? 
 420:             new BeanDescriptor(beanClass, null) :
 421:             explicit.explicitBeanDescriptor));    
 422:     return currentInfo.getBeanInfo();
 423:   }
 424:   
 425:   /** 
 426:    * Get the search path for BeanInfo classes.
 427:    *
 428:    * @return the BeanInfo search path.
 429:    */
 430:   public static String[] getBeanInfoSearchPath() 
 431:   {
 432:     return beanInfoSearchPath;
 433:   }
 434:   
 435:   /** 
 436:    * Set the search path for BeanInfo classes.
 437:    * @param beanInfoSearchPath the new BeanInfo search
 438:    *        path.
 439:    */
 440:   public static void setBeanInfoSearchPath(String[] beanInfoSearchPath) 
 441:   {
 442:     Introspector.beanInfoSearchPath = beanInfoSearchPath;
 443:   }
 444:   
 445:   /** 
 446:    * A helper method to convert a name to standard Java
 447:    * naming conventions: anything with two capitals as the
 448:    * first two letters remains the same, otherwise the
 449:    * first letter is decapitalized.  URL = URL, I = i,
 450:    * MyMethod = myMethod.
 451:    *
 452:    * @param name the name to decapitalize.
 453:    * @return the decapitalized name.
 454:    */
 455:   public static String decapitalize(String name) 
 456:   {
 457:     try 
 458:       {
 459:       if(!Character.isUpperCase(name.charAt(0))) 
 460:     {
 461:       return name;
 462:     } 
 463:       else 
 464:     {
 465:     try 
 466:       {
 467:       if(Character.isUpperCase(name.charAt(1))) 
 468:         {
 469:           return name;
 470:         } 
 471:       else 
 472:         {
 473:           char[] c = name.toCharArray();
 474:           c[0] = Character.toLowerCase(c[0]);
 475:           return new String(c);
 476:         }
 477:       } 
 478:     catch(StringIndexOutOfBoundsException E) 
 479:       {
 480:         char[] c = new char[1];
 481:         c[0] = Character.toLowerCase(name.charAt(0));
 482:         return new String(c);
 483:       }
 484:     }
 485:       } 
 486:     catch(StringIndexOutOfBoundsException E) 
 487:       {
 488:     return name;
 489:       } 
 490:     catch(NullPointerException E) 
 491:       {
 492:     return null;
 493:       }
 494:   }
 495: 
 496:   static BeanInfo copyBeanInfo(BeanInfo b) 
 497:   {
 498:     java.awt.Image[] icons = new java.awt.Image[4];
 499:     for(int i=1;i<=4;i++) 
 500:       {
 501:     icons[i-1] = b.getIcon(i);
 502:       }
 503: 
 504:     return new ExplicitBeanInfo(b.getBeanDescriptor(),
 505:                 b.getAdditionalBeanInfo(),
 506:                 b.getPropertyDescriptors(),
 507:                 b.getDefaultPropertyIndex(),
 508:                 b.getEventSetDescriptors(),
 509:                 b.getDefaultEventIndex(),
 510:                 b.getMethodDescriptors(),
 511:                 icons);
 512:   }
 513: }
 514: 
 515: class ExplicitInfo 
 516: {
 517:   BeanDescriptor explicitBeanDescriptor;
 518:   BeanInfo[] explicitBeanInfo;
 519:   
 520:   PropertyDescriptor[] explicitPropertyDescriptors;
 521:   EventSetDescriptor[] explicitEventSetDescriptors;
 522:   MethodDescriptor[] explicitMethodDescriptors;
 523:   
 524:   int defaultProperty;
 525:   int defaultEvent;
 526:   
 527:   java.awt.Image[] im = new java.awt.Image[4];
 528:   
 529:   Class propertyStopClass;
 530:   Class eventStopClass;
 531:   Class methodStopClass;
 532: 
 533:   static Hashtable explicitBeanInfos = new Hashtable();
 534:   static Vector emptyBeanInfos = new Vector();
 535: 
 536:   ExplicitInfo(Class beanClass, Class stopClass) 
 537:   {
 538:     while(beanClass != null && !beanClass.equals(stopClass)) 
 539:       {
 540: 
 541:     BeanInfo explicit = findExplicitBeanInfo(beanClass);
 542:     
 543: 
 544:     if(explicit != null) 
 545:       {
 546: 
 547:         if(explicitBeanDescriptor == null) 
 548:           {
 549:         explicitBeanDescriptor = explicit.getBeanDescriptor();
 550:           }
 551: 
 552:         if(explicitBeanInfo == null) 
 553:           {
 554:         explicitBeanInfo = explicit.getAdditionalBeanInfo();
 555:           }
 556: 
 557:         if(explicitPropertyDescriptors == null) 
 558:           {
 559:         if(explicit.getPropertyDescriptors() != null) 
 560:           {
 561:             explicitPropertyDescriptors = explicit.getPropertyDescriptors();
 562:             defaultProperty = explicit.getDefaultPropertyIndex();
 563:             propertyStopClass = beanClass;
 564:           }
 565:           }
 566: 
 567:         if(explicitEventSetDescriptors == null) 
 568:           {
 569:         if(explicit.getEventSetDescriptors() != null) 
 570:           {
 571:             explicitEventSetDescriptors = explicit.getEventSetDescriptors();
 572:             defaultEvent = explicit.getDefaultEventIndex();
 573:             eventStopClass = beanClass;
 574:           }
 575:           }
 576: 
 577:         if(explicitMethodDescriptors == null) 
 578:           {
 579:         if(explicit.getMethodDescriptors() != null) 
 580:           {
 581:             explicitMethodDescriptors = explicit.getMethodDescriptors();
 582:             methodStopClass = beanClass;
 583:           }
 584:           }
 585: 
 586:         if(im[0] == null && im[1] == null 
 587:            && im[2] == null && im[3] == null) 
 588:           {
 589:         im[0] = explicit.getIcon(0);
 590:         im[1] = explicit.getIcon(1);
 591:         im[2] = explicit.getIcon(2);
 592:         im[3] = explicit.getIcon(3);
 593:           }
 594:       }
 595:     beanClass = beanClass.getSuperclass();
 596:       }
 597: 
 598:     if(propertyStopClass == null) 
 599:       {
 600:     propertyStopClass = stopClass;
 601:       }
 602: 
 603:     if(eventStopClass == null) 
 604:       {
 605:     eventStopClass = stopClass;
 606:       }
 607: 
 608:     if(methodStopClass == null) 
 609:       {
 610:     methodStopClass = stopClass;
 611:       }
 612:   }
 613:   
 614:   /** Throws away all cached data and makes sure we re-instantiate things
 615:     * like BeanDescriptors again.
 616:     */
 617:   static void flushCaches() {
 618:     explicitBeanInfos.clear();
 619:     emptyBeanInfos.clear();
 620:   }
 621:   
 622:   static BeanInfo findExplicitBeanInfo(Class beanClass) 
 623:   {
 624:     BeanInfo retval = (BeanInfo)explicitBeanInfos.get(beanClass);
 625:     if(retval != null) 
 626:       {
 627:     return retval;
 628:       } 
 629:     else if(emptyBeanInfos.indexOf(beanClass) != -1) 
 630:       {
 631:     return null;
 632:       } 
 633:     else 
 634:       {
 635:     retval = reallyFindExplicitBeanInfo(beanClass);
 636:     if(retval != null) 
 637:       {
 638:         explicitBeanInfos.put(beanClass,retval);
 639:       } 
 640:     else 
 641:       {
 642:         emptyBeanInfos.addElement(beanClass);
 643:       }
 644:     return retval;
 645:       }
 646:   }
 647:   
 648:   static BeanInfo reallyFindExplicitBeanInfo(Class beanClass) 
 649:   {
 650:     ClassLoader beanClassLoader = beanClass.getClassLoader();
 651:     BeanInfo beanInfo;
 652: 
 653:     beanInfo = getBeanInfo(beanClassLoader, beanClass.getName() + "BeanInfo");
 654:     if (beanInfo == null)
 655:       {
 656:     String newName;
 657:     newName = ClassHelper.getTruncatedClassName(beanClass) + "BeanInfo";
 658: 
 659:     for(int i = 0; i < Introspector.beanInfoSearchPath.length; i++) 
 660:       {
 661:         if (Introspector.beanInfoSearchPath[i].equals("")) 
 662:           beanInfo = getBeanInfo(beanClassLoader, newName);
 663:         else 
 664:           beanInfo = getBeanInfo(beanClassLoader,
 665:                      Introspector.beanInfoSearchPath[i] + "."
 666:                      + newName);
 667: 
 668:         // Returns the beanInfo if it exists and the described class matches
 669:         // the one we searched.
 670:         if (beanInfo != null && beanInfo.getBeanDescriptor() != null &&
 671:             beanInfo.getBeanDescriptor().getBeanClass() == beanClass)
 672: 
 673:           return beanInfo;
 674:       }
 675:       }
 676: 
 677:     return beanInfo;
 678:   }
 679: 
 680:   /**
 681:    * Returns an instance of the given class name when it can be loaded
 682:    * through the given class loader, or null otherwise.
 683:    */
 684:   private static BeanInfo getBeanInfo(ClassLoader cl, String infoName)
 685:   {
 686:     try
 687:       {
 688:     return (BeanInfo) Class.forName(infoName, true, cl).newInstance();
 689:       }
 690:     catch (ClassNotFoundException cnfe)
 691:       {
 692:     return null;
 693:       }
 694:     catch (IllegalAccessException iae)
 695:       {
 696:     return null;
 697:       }
 698:     catch (InstantiationException ie)
 699:       {
 700:     return null;
 701:       }
 702:   }
 703:   
 704: }