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