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