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