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 }
1415