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<Class<?>,BeanInfo> beanInfoCache =
 186:     new Hashtable<Class<?>,BeanInfo>();
 187: 
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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:    */
 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: 
 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: 
 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:     */
 618:   static void flushCaches() {
 619:         explicitBeanInfos.clear();
 620:         emptyBeanInfos.clear();
 621:   }
 622: 
 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: 
 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:    */
 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: }