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