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