0001 /*
0002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.beans;
0027
0028 import com.sun.beans.finder.ClassFinder;
0029
0030 import java.lang.ref.Reference;
0031 import java.lang.ref.SoftReference;
0032
0033 import java.lang.reflect.Method;
0034 import java.lang.reflect.Modifier;
0035
0036 import java.security.AccessController;
0037 import java.security.PrivilegedAction;
0038
0039 import java.util.Collections;
0040 import java.util.Map;
0041 import java.util.ArrayList;
0042 import java.util.HashMap;
0043 import java.util.Iterator;
0044 import java.util.EventListener;
0045 import java.util.List;
0046 import java.util.WeakHashMap;
0047 import java.util.TreeMap;
0048 import sun.reflect.misc.ReflectUtil;
0049
0050 /**
0051 * The Introspector class provides a standard way for tools to learn about
0052 * the properties, events, and methods supported by a target Java Bean.
0053 * <p>
0054 * For each of those three kinds of information, the Introspector will
0055 * separately analyze the bean's class and superclasses looking for
0056 * either explicit or implicit information and use that information to
0057 * build a BeanInfo object that comprehensively describes the target bean.
0058 * <p>
0059 * For each class "Foo", explicit information may be available if there exists
0060 * a corresponding "FooBeanInfo" class that provides a non-null value when
0061 * queried for the information. We first look for the BeanInfo class by
0062 * taking the full package-qualified name of the target bean class and
0063 * appending "BeanInfo" to form a new class name. If this fails, then
0064 * we take the final classname component of this name, and look for that
0065 * class in each of the packages specified in the BeanInfo package search
0066 * path.
0067 * <p>
0068 * Thus for a class such as "sun.xyz.OurButton" we would first look for a
0069 * BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd
0070 * look in each package in the BeanInfo search path for an OurButtonBeanInfo
0071 * class. With the default search path, this would mean looking for
0072 * "sun.beans.infos.OurButtonBeanInfo".
0073 * <p>
0074 * If a class provides explicit BeanInfo about itself then we add that to
0075 * the BeanInfo information we obtained from analyzing any derived classes,
0076 * but we regard the explicit information as being definitive for the current
0077 * class and its base classes, and do not proceed any further up the superclass
0078 * chain.
0079 * <p>
0080 * If we don't find explicit BeanInfo on a class, we use low-level
0081 * reflection to study the methods of the class and apply standard design
0082 * patterns to identify property accessors, event sources, or public
0083 * methods. We then proceed to analyze the class's superclass and add
0084 * in the information from it (and possibly on up the superclass chain).
0085 *
0086 * <p>
0087 * Because the Introspector caches BeanInfo classes for better performance,
0088 * take care if you use it in an application that uses
0089 * multiple class loaders.
0090 * In general, when you destroy a <code>ClassLoader</code>
0091 * that has been used to introspect classes,
0092 * you should use the
0093 * {@link #flushCaches <code>Introspector.flushCaches</code>}
0094 * or
0095 * {@link #flushFromCaches <code>Introspector.flushFromCaches</code>} method
0096 * to flush all of the introspected classes out of the cache.
0097 *
0098 * <P>
0099 * For more information about introspection and design patterns, please
0100 * consult the
0101 * <a href="http://java.sun.com/products/javabeans/docs/index.html">JavaBeans specification</a>.
0102 */
0103
0104 public class Introspector {
0105
0106 // Flags that can be used to control getBeanInfo:
0107 public final static int USE_ALL_BEANINFO = 1;
0108 public final static int IGNORE_IMMEDIATE_BEANINFO = 2;
0109 public final static int IGNORE_ALL_BEANINFO = 3;
0110
0111 // Static Caches to speed up introspection.
0112 private static Map declaredMethodCache = Collections
0113 .synchronizedMap(new WeakHashMap());
0114 private static Map beanInfoCache = Collections
0115 .synchronizedMap(new WeakHashMap());
0116
0117 private Class beanClass;
0118 private BeanInfo explicitBeanInfo;
0119 private BeanInfo super BeanInfo;
0120 private BeanInfo additionalBeanInfo[];
0121
0122 private boolean propertyChangeSource = false;
0123 private static Class eventListenerType = EventListener.class;
0124
0125 // These should be removed.
0126 private String defaultEventName;
0127 private String defaultPropertyName;
0128 private int defaultEventIndex = -1;
0129 private int defaultPropertyIndex = -1;
0130
0131 // Methods maps from Method objects to MethodDescriptors
0132 private Map methods;
0133
0134 // properties maps from String names to PropertyDescriptors
0135 private Map properties;
0136
0137 // events maps from String names to EventSetDescriptors
0138 private Map events;
0139
0140 private final static String DEFAULT_INFO_PATH = "sun.beans.infos";
0141
0142 private static String[] searchPath = { DEFAULT_INFO_PATH };
0143
0144 private final static EventSetDescriptor[] EMPTY_EVENTSETDESCRIPTORS = new EventSetDescriptor[0];
0145
0146 static final String ADD_PREFIX = "add";
0147 static final String REMOVE_PREFIX = "remove";
0148 static final String GET_PREFIX = "get";
0149 static final String SET_PREFIX = "set";
0150 static final String IS_PREFIX = "is";
0151
0152 private static final String BEANINFO_SUFFIX = "BeanInfo";
0153
0154 //======================================================================
0155 // Public methods
0156 //======================================================================
0157
0158 /**
0159 * Introspect on a Java Bean and learn about all its properties, exposed
0160 * methods, and events.
0161 * <p>
0162 * If the BeanInfo class for a Java Bean has been previously Introspected
0163 * then the BeanInfo class is retrieved from the BeanInfo cache.
0164 *
0165 * @param beanClass The bean class to be analyzed.
0166 * @return A BeanInfo object describing the target bean.
0167 * @exception IntrospectionException if an exception occurs during
0168 * introspection.
0169 * @see #flushCaches
0170 * @see #flushFromCaches
0171 */
0172 public static BeanInfo getBeanInfo(Class<?> beanClass)
0173 throws IntrospectionException {
0174 if (!ReflectUtil.isPackageAccessible(beanClass)) {
0175 return (new Introspector(beanClass, null, USE_ALL_BEANINFO))
0176 .getBeanInfo();
0177 }
0178 BeanInfo bi = (BeanInfo) beanInfoCache.get(beanClass);
0179 if (bi == null) {
0180 bi = (new Introspector(beanClass, null, USE_ALL_BEANINFO))
0181 .getBeanInfo();
0182 beanInfoCache.put(beanClass, bi);
0183 }
0184 return bi;
0185 }
0186
0187 /**
0188 * Introspect on a Java bean and learn about all its properties, exposed
0189 * methods, and events, subject to some control flags.
0190 * <p>
0191 * If the BeanInfo class for a Java Bean has been previously Introspected
0192 * based on the same arguments then the BeanInfo class is retrieved
0193 * from the BeanInfo cache.
0194 *
0195 * @param beanClass The bean class to be analyzed.
0196 * @param flags Flags to control the introspection.
0197 * If flags == USE_ALL_BEANINFO then we use all of the BeanInfo
0198 * classes we can discover.
0199 * If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any
0200 * BeanInfo associated with the specified beanClass.
0201 * If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo
0202 * associated with the specified beanClass or any of its
0203 * parent classes.
0204 * @return A BeanInfo object describing the target bean.
0205 * @exception IntrospectionException if an exception occurs during
0206 * introspection.
0207 */
0208 public static BeanInfo getBeanInfo(Class<?> beanClass, int flags)
0209 throws IntrospectionException {
0210 return getBeanInfo(beanClass, null, flags);
0211 }
0212
0213 /**
0214 * Introspect on a Java bean and learn all about its properties, exposed
0215 * methods, below a given "stop" point.
0216 * <p>
0217 * If the BeanInfo class for a Java Bean has been previously Introspected
0218 * based on the same arguments, then the BeanInfo class is retrieved
0219 * from the BeanInfo cache.
0220 *
0221 * @param beanClass The bean class to be analyzed.
0222 * @param stopClass The baseclass at which to stop the analysis. Any
0223 * methods/properties/events in the stopClass or in its baseclasses
0224 * will be ignored in the analysis.
0225 * @exception IntrospectionException if an exception occurs during
0226 * introspection.
0227 */
0228 public static BeanInfo getBeanInfo(Class<?> beanClass,
0229 Class<?> stopClass) throws IntrospectionException {
0230 return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
0231 }
0232
0233 /**
0234 * Only called from the public getBeanInfo methods. This method caches
0235 * the Introspected BeanInfo based on the arguments.
0236 */
0237 private static BeanInfo getBeanInfo(Class beanClass,
0238 Class stopClass, int flags) throws IntrospectionException {
0239 BeanInfo bi;
0240 if (stopClass == null && flags == USE_ALL_BEANINFO) {
0241 // Same parameters to take advantage of caching.
0242 bi = getBeanInfo(beanClass);
0243 } else {
0244 bi = (new Introspector(beanClass, stopClass, flags))
0245 .getBeanInfo();
0246 }
0247 return bi;
0248
0249 // Old behaviour: Make an independent copy of the BeanInfo.
0250 //return new GenericBeanInfo(bi);
0251 }
0252
0253 /**
0254 * Utility method to take a string and convert it to normal Java variable
0255 * name capitalization. This normally means converting the first
0256 * character from upper case to lower case, but in the (unusual) special
0257 * case when there is more than one character and both the first and
0258 * second characters are upper case, we leave it alone.
0259 * <p>
0260 * Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
0261 * as "URL".
0262 *
0263 * @param name The string to be decapitalized.
0264 * @return The decapitalized version of the string.
0265 */
0266 public static String decapitalize(String name) {
0267 if (name == null || name.length() == 0) {
0268 return name;
0269 }
0270 if (name.length() > 1 && Character.isUpperCase(name.charAt(1))
0271 && Character.isUpperCase(name.charAt(0))) {
0272 return name;
0273 }
0274 char chars[] = name.toCharArray();
0275 chars[0] = Character.toLowerCase(chars[0]);
0276 return new String(chars);
0277 }
0278
0279 /**
0280 * Gets the list of package names that will be used for
0281 * finding BeanInfo classes.
0282 *
0283 * @return The array of package names that will be searched in
0284 * order to find BeanInfo classes. The default value
0285 * for this array is implementation-dependent; e.g.
0286 * Sun implementation initially sets to {"sun.beans.infos"}.
0287 */
0288
0289 public static synchronized String[] getBeanInfoSearchPath() {
0290 // Return a copy of the searchPath.
0291 String result[] = new String[searchPath.length];
0292 for (int i = 0; i < searchPath.length; i++) {
0293 result[i] = searchPath[i];
0294 }
0295 return result;
0296 }
0297
0298 /**
0299 * Change the list of package names that will be used for
0300 * finding BeanInfo classes. The behaviour of
0301 * this method is undefined if parameter path
0302 * is null.
0303 *
0304 * <p>First, if there is a security manager, its <code>checkPropertiesAccess</code>
0305 * method is called. This could result in a SecurityException.
0306 *
0307 * @param path Array of package names.
0308 * @exception SecurityException if a security manager exists and its
0309 * <code>checkPropertiesAccess</code> method doesn't allow setting
0310 * of system properties.
0311 * @see SecurityManager#checkPropertiesAccess
0312 */
0313
0314 public static synchronized void setBeanInfoSearchPath(String path[]) {
0315 SecurityManager sm = System.getSecurityManager();
0316 if (sm != null) {
0317 sm.checkPropertiesAccess();
0318 }
0319 searchPath = path;
0320 }
0321
0322 /**
0323 * Flush all of the Introspector's internal caches. This method is
0324 * not normally required. It is normally only needed by advanced
0325 * tools that update existing "Class" objects in-place and need
0326 * to make the Introspector re-analyze existing Class objects.
0327 */
0328
0329 public static void flushCaches() {
0330 beanInfoCache.clear();
0331 declaredMethodCache.clear();
0332 }
0333
0334 /**
0335 * Flush the Introspector's internal cached information for a given class.
0336 * This method is not normally required. It is normally only needed
0337 * by advanced tools that update existing "Class" objects in-place
0338 * and need to make the Introspector re-analyze an existing Class object.
0339 *
0340 * Note that only the direct state associated with the target Class
0341 * object is flushed. We do not flush state for other Class objects
0342 * with the same name, nor do we flush state for any related Class
0343 * objects (such as subclasses), even though their state may include
0344 * information indirectly obtained from the target Class object.
0345 *
0346 * @param clz Class object to be flushed.
0347 * @throws NullPointerException If the Class object is null.
0348 */
0349 public static void flushFromCaches(Class<?> clz) {
0350 if (clz == null) {
0351 throw new NullPointerException();
0352 }
0353 beanInfoCache.remove(clz);
0354 declaredMethodCache.remove(clz);
0355 }
0356
0357 //======================================================================
0358 // Private implementation methods
0359 //======================================================================
0360
0361 private Introspector(Class beanClass, Class stopClass, int flags)
0362 throws IntrospectionException {
0363 this .beanClass = beanClass;
0364
0365 // Check stopClass is a superClass of startClass.
0366 if (stopClass != null) {
0367 boolean isSuper = false;
0368 for (Class c = beanClass.getSuperclass(); c != null; c = c
0369 .getSuperclass()) {
0370 if (c == stopClass) {
0371 isSuper = true;
0372 }
0373 }
0374 if (!isSuper) {
0375 throw new IntrospectionException(stopClass.getName()
0376 + " not superclass of " + beanClass.getName());
0377 }
0378 }
0379
0380 if (flags == USE_ALL_BEANINFO) {
0381 explicitBeanInfo = findExplicitBeanInfo(beanClass);
0382 }
0383
0384 Class super Class = beanClass.getSuperclass();
0385 if (super Class != stopClass) {
0386 int newFlags = flags;
0387 if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
0388 newFlags = USE_ALL_BEANINFO;
0389 }
0390 super BeanInfo = getBeanInfo(super Class, stopClass, newFlags);
0391 }
0392 if (explicitBeanInfo != null) {
0393 additionalBeanInfo = explicitBeanInfo
0394 .getAdditionalBeanInfo();
0395 }
0396 if (additionalBeanInfo == null) {
0397 additionalBeanInfo = new BeanInfo[0];
0398 }
0399 }
0400
0401 /**
0402 * Constructs a GenericBeanInfo class from the state of the Introspector
0403 */
0404 private BeanInfo getBeanInfo() throws IntrospectionException {
0405
0406 // the evaluation order here is import, as we evaluate the
0407 // event sets and locate PropertyChangeListeners before we
0408 // look for properties.
0409 BeanDescriptor bd = getTargetBeanDescriptor();
0410 MethodDescriptor mds[] = getTargetMethodInfo();
0411 EventSetDescriptor esds[] = getTargetEventInfo();
0412 PropertyDescriptor pds[] = getTargetPropertyInfo();
0413
0414 int defaultEvent = getTargetDefaultEventIndex();
0415 int defaultProperty = getTargetDefaultPropertyIndex();
0416
0417 return new GenericBeanInfo(bd, esds, defaultEvent, pds,
0418 defaultProperty, mds, explicitBeanInfo);
0419
0420 }
0421
0422 /**
0423 * Looks for an explicit BeanInfo class that corresponds to the Class.
0424 * First it looks in the existing package that the Class is defined in,
0425 * then it checks to see if the class is its own BeanInfo. Finally,
0426 * the BeanInfo search path is prepended to the class and searched.
0427 *
0428 * @return Instance of an explicit BeanInfo class or null if one isn't found.
0429 */
0430 private static synchronized BeanInfo findExplicitBeanInfo(
0431 Class beanClass) {
0432 String name = beanClass.getName() + BEANINFO_SUFFIX;
0433 try {
0434 return (java.beans.BeanInfo) instantiate(beanClass, name);
0435 } catch (Exception ex) {
0436 // Just drop through
0437
0438 }
0439 // Now try checking if the bean is its own BeanInfo.
0440 try {
0441 if (isSubclass(beanClass, java.beans.BeanInfo.class)) {
0442 return (java.beans.BeanInfo) beanClass.newInstance();
0443 }
0444 } catch (Exception ex) {
0445 // Just drop through
0446 }
0447 // Now try looking for <searchPath>.fooBeanInfo
0448 name = name.substring(name.lastIndexOf('.') + 1);
0449
0450 for (int i = 0; i < searchPath.length; i++) {
0451 // This optimization will only use the BeanInfo search path if is has changed
0452 // from the original or trying to get the ComponentBeanInfo.
0453 if (!DEFAULT_INFO_PATH.equals(searchPath[i])
0454 || DEFAULT_INFO_PATH.equals(searchPath[i])
0455 && "ComponentBeanInfo".equals(name)) {
0456 try {
0457 String fullName = searchPath[i] + "." + name;
0458 java.beans.BeanInfo bi = (java.beans.BeanInfo) instantiate(
0459 beanClass, fullName);
0460
0461 // Make sure that the returned BeanInfo matches the class.
0462 if (bi.getBeanDescriptor() != null) {
0463 if (bi.getBeanDescriptor().getBeanClass() == beanClass) {
0464 return bi;
0465 }
0466 } else if (bi.getPropertyDescriptors() != null) {
0467 PropertyDescriptor[] pds = bi
0468 .getPropertyDescriptors();
0469 for (int j = 0; j < pds.length; j++) {
0470 Method method = pds[j].getReadMethod();
0471 if (method == null) {
0472 method = pds[j].getWriteMethod();
0473 }
0474 if (method != null
0475 && method.getDeclaringClass() == beanClass) {
0476 return bi;
0477 }
0478 }
0479 } else if (bi.getMethodDescriptors() != null) {
0480 MethodDescriptor[] mds = bi
0481 .getMethodDescriptors();
0482 for (int j = 0; j < mds.length; j++) {
0483 Method method = mds[j].getMethod();
0484 if (method != null
0485 && method.getDeclaringClass() == beanClass) {
0486 return bi;
0487 }
0488 }
0489 }
0490 } catch (Exception ex) {
0491 // Silently ignore any errors.
0492 }
0493 }
0494 }
0495 return null;
0496 }
0497
0498 /**
0499 * @return An array of PropertyDescriptors describing the editable
0500 * properties supported by the target bean.
0501 */
0502
0503 private PropertyDescriptor[] getTargetPropertyInfo() {
0504
0505 // Check if the bean has its own BeanInfo that will provide
0506 // explicit information.
0507 PropertyDescriptor[] explicitProperties = null;
0508 if (explicitBeanInfo != null) {
0509 explicitProperties = getPropertyDescriptors(this .explicitBeanInfo);
0510 }
0511
0512 if (explicitProperties == null && super BeanInfo != null) {
0513 // We have no explicit BeanInfo properties. Check with our parent.
0514 addPropertyDescriptors(getPropertyDescriptors(this .super BeanInfo));
0515 }
0516
0517 for (int i = 0; i < additionalBeanInfo.length; i++) {
0518 addPropertyDescriptors(additionalBeanInfo[i]
0519 .getPropertyDescriptors());
0520 }
0521
0522 if (explicitProperties != null) {
0523 // Add the explicit BeanInfo data to our results.
0524 addPropertyDescriptors(explicitProperties);
0525
0526 } else {
0527
0528 // Apply some reflection to the current class.
0529
0530 // First get an array of all the public methods at this level
0531 Method methodList[] = getPublicDeclaredMethods(beanClass);
0532
0533 // Now analyze each method.
0534 for (int i = 0; i < methodList.length; i++) {
0535 Method method = methodList[i];
0536 if (method == null || method.isSynthetic()) {
0537 continue;
0538 }
0539 // skip static methods.
0540 int mods = method.getModifiers();
0541 if (Modifier.isStatic(mods)) {
0542 continue;
0543 }
0544 String name = method.getName();
0545 Class argTypes[] = method.getParameterTypes();
0546 Class resultType = method.getReturnType();
0547 int argCount = argTypes.length;
0548 PropertyDescriptor pd = null;
0549
0550 if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
0551 // Optimization. Don't bother with invalid propertyNames.
0552 continue;
0553 }
0554
0555 try {
0556
0557 if (argCount == 0) {
0558 if (name.startsWith(GET_PREFIX)) {
0559 // Simple getter
0560 pd = new PropertyDescriptor(this .beanClass,
0561 name.substring(3), method, null);
0562 } else if (resultType == boolean.class
0563 && name.startsWith(IS_PREFIX)) {
0564 // Boolean getter
0565 pd = new PropertyDescriptor(this .beanClass,
0566 name.substring(2), method, null);
0567 }
0568 } else if (argCount == 1) {
0569 if (argTypes[0] == int.class
0570 && name.startsWith(GET_PREFIX)) {
0571 pd = new IndexedPropertyDescriptor(
0572 this .beanClass, name.substring(3),
0573 null, null, method, null);
0574 } else if (resultType == void.class
0575 && name.startsWith(SET_PREFIX)) {
0576 // Simple setter
0577 pd = new PropertyDescriptor(this .beanClass,
0578 name.substring(3), null, method);
0579 if (throwsException(method,
0580 PropertyVetoException.class)) {
0581 pd.setConstrained(true);
0582 }
0583 }
0584 } else if (argCount == 2) {
0585 if (argTypes[0] == int.class
0586 && name.startsWith(SET_PREFIX)) {
0587 pd = new IndexedPropertyDescriptor(
0588 this .beanClass, name.substring(3),
0589 null, null, null, method);
0590 if (throwsException(method,
0591 PropertyVetoException.class)) {
0592 pd.setConstrained(true);
0593 }
0594 }
0595 }
0596 } catch (IntrospectionException ex) {
0597 // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
0598 // constructor fins that the method violates details of the deisgn
0599 // pattern, e.g. by having an empty name, or a getter returning
0600 // void , or whatever.
0601 pd = null;
0602 }
0603
0604 if (pd != null) {
0605 // If this class or one of its base classes is a PropertyChange
0606 // source, then we assume that any properties we discover are "bound".
0607 if (propertyChangeSource) {
0608 pd.setBound(true);
0609 }
0610 addPropertyDescriptor(pd);
0611 }
0612 }
0613 }
0614 processPropertyDescriptors();
0615
0616 // Allocate and populate the result array.
0617 PropertyDescriptor result[] = new PropertyDescriptor[properties
0618 .size()];
0619 result = (PropertyDescriptor[]) properties.values().toArray(
0620 result);
0621
0622 // Set the default index.
0623 if (defaultPropertyName != null) {
0624 for (int i = 0; i < result.length; i++) {
0625 if (defaultPropertyName.equals(result[i].getName())) {
0626 defaultPropertyIndex = i;
0627 }
0628 }
0629 }
0630
0631 return result;
0632 }
0633
0634 private HashMap pdStore = new HashMap();
0635
0636 /**
0637 * Adds the property descriptor to the list store.
0638 */
0639 private void addPropertyDescriptor(PropertyDescriptor pd) {
0640 String propName = pd.getName();
0641 List list = (List) pdStore.get(propName);
0642 if (list == null) {
0643 list = new ArrayList();
0644 pdStore.put(propName, list);
0645 }
0646 if (this .beanClass != pd.getClass0()) {
0647 // replace existing property descriptor
0648 // only if we have types to resolve
0649 // in the context of this.beanClass
0650 try {
0651 String name = pd.getName();
0652 Method read = pd.getReadMethod();
0653 Method write = pd.getWriteMethod();
0654 boolean cls = true;
0655 if (read != null)
0656 cls = cls
0657 && read.getGenericReturnType() instanceof Class;
0658 if (write != null)
0659 cls = cls
0660 && write.getGenericParameterTypes()[0] instanceof Class;
0661 if (pd instanceof IndexedPropertyDescriptor) {
0662 IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
0663 Method readI = ipd.getIndexedReadMethod();
0664 Method writeI = ipd.getIndexedWriteMethod();
0665 if (readI != null)
0666 cls = cls
0667 && readI.getGenericReturnType() instanceof Class;
0668 if (writeI != null)
0669 cls = cls
0670 && writeI.getGenericParameterTypes()[1] instanceof Class;
0671 if (!cls) {
0672 pd = new IndexedPropertyDescriptor(
0673 this .beanClass, name, read, write,
0674 readI, writeI);
0675 }
0676 } else if (!cls) {
0677 pd = new PropertyDescriptor(this .beanClass, name,
0678 read, write);
0679 }
0680 } catch (IntrospectionException e) {
0681 }
0682 }
0683 list.add(pd);
0684 }
0685
0686 private void addPropertyDescriptors(PropertyDescriptor[] descriptors) {
0687 if (descriptors != null) {
0688 for (PropertyDescriptor descriptor : descriptors) {
0689 addPropertyDescriptor(descriptor);
0690 }
0691 }
0692 }
0693
0694 private PropertyDescriptor[] getPropertyDescriptors(BeanInfo info) {
0695 PropertyDescriptor[] descriptors = info
0696 .getPropertyDescriptors();
0697 int index = info.getDefaultPropertyIndex();
0698 if ((0 <= index) && (index < descriptors.length)) {
0699 this .defaultPropertyName = descriptors[index].getName();
0700 }
0701 return descriptors;
0702 }
0703
0704 /**
0705 * Populates the property descriptor table by merging the
0706 * lists of Property descriptors.
0707 */
0708 private void processPropertyDescriptors() {
0709 if (properties == null) {
0710 properties = new TreeMap();
0711 }
0712
0713 List list;
0714
0715 PropertyDescriptor pd, gpd, spd;
0716 IndexedPropertyDescriptor ipd, igpd, ispd;
0717
0718 Iterator it = pdStore.values().iterator();
0719 while (it.hasNext()) {
0720 pd = null;
0721 gpd = null;
0722 spd = null;
0723 ipd = null;
0724 igpd = null;
0725 ispd = null;
0726
0727 list = (List) it.next();
0728
0729 // First pass. Find the latest getter method. Merge properties
0730 // of previous getter methods.
0731 for (int i = 0; i < list.size(); i++) {
0732 pd = (PropertyDescriptor) list.get(i);
0733 if (pd instanceof IndexedPropertyDescriptor) {
0734 ipd = (IndexedPropertyDescriptor) pd;
0735 if (ipd.getIndexedReadMethod() != null) {
0736 if (igpd != null) {
0737 igpd = new IndexedPropertyDescriptor(igpd,
0738 ipd);
0739 } else {
0740 igpd = ipd;
0741 }
0742 }
0743 } else {
0744 if (pd.getReadMethod() != null) {
0745 if (gpd != null) {
0746 // Don't replace the existing read
0747 // method if it starts with "is"
0748 Method method = gpd.getReadMethod();
0749 if (!method.getName().startsWith(IS_PREFIX)) {
0750 gpd = new PropertyDescriptor(gpd, pd);
0751 }
0752 } else {
0753 gpd = pd;
0754 }
0755 }
0756 }
0757 }
0758
0759 // Second pass. Find the latest setter method which
0760 // has the same type as the getter method.
0761 for (int i = 0; i < list.size(); i++) {
0762 pd = (PropertyDescriptor) list.get(i);
0763 if (pd instanceof IndexedPropertyDescriptor) {
0764 ipd = (IndexedPropertyDescriptor) pd;
0765 if (ipd.getIndexedWriteMethod() != null) {
0766 if (igpd != null) {
0767 if (igpd.getIndexedPropertyType() == ipd
0768 .getIndexedPropertyType()) {
0769 if (ispd != null) {
0770 ispd = new IndexedPropertyDescriptor(
0771 ispd, ipd);
0772 } else {
0773 ispd = ipd;
0774 }
0775 }
0776 } else {
0777 if (ispd != null) {
0778 ispd = new IndexedPropertyDescriptor(
0779 ispd, ipd);
0780 } else {
0781 ispd = ipd;
0782 }
0783 }
0784 }
0785 } else {
0786 if (pd.getWriteMethod() != null) {
0787 if (gpd != null) {
0788 if (gpd.getPropertyType() == pd
0789 .getPropertyType()) {
0790 if (spd != null) {
0791 spd = new PropertyDescriptor(spd,
0792 pd);
0793 } else {
0794 spd = pd;
0795 }
0796 }
0797 } else {
0798 if (spd != null) {
0799 spd = new PropertyDescriptor(spd, pd);
0800 } else {
0801 spd = pd;
0802 }
0803 }
0804 }
0805 }
0806 }
0807
0808 // At this stage we should have either PDs or IPDs for the
0809 // representative getters and setters. The order at which the
0810 // property descriptors are determined represent the
0811 // precedence of the property ordering.
0812 pd = null;
0813 ipd = null;
0814
0815 if (igpd != null && ispd != null) {
0816 // Complete indexed properties set
0817 // Merge any classic property descriptors
0818 if (gpd != null) {
0819 PropertyDescriptor tpd = mergePropertyDescriptor(
0820 igpd, gpd);
0821 if (tpd instanceof IndexedPropertyDescriptor) {
0822 igpd = (IndexedPropertyDescriptor) tpd;
0823 }
0824 }
0825 if (spd != null) {
0826 PropertyDescriptor tpd = mergePropertyDescriptor(
0827 ispd, spd);
0828 if (tpd instanceof IndexedPropertyDescriptor) {
0829 ispd = (IndexedPropertyDescriptor) tpd;
0830 }
0831 }
0832 if (igpd == ispd) {
0833 pd = igpd;
0834 } else {
0835 pd = mergePropertyDescriptor(igpd, ispd);
0836 }
0837 } else if (gpd != null && spd != null) {
0838 // Complete simple properties set
0839 if (gpd == spd) {
0840 pd = gpd;
0841 } else {
0842 pd = mergePropertyDescriptor(gpd, spd);
0843 }
0844 } else if (ispd != null) {
0845 // indexed setter
0846 pd = ispd;
0847 // Merge any classic property descriptors
0848 if (spd != null) {
0849 pd = mergePropertyDescriptor(ispd, spd);
0850 }
0851 if (gpd != null) {
0852 pd = mergePropertyDescriptor(ispd, gpd);
0853 }
0854 } else if (igpd != null) {
0855 // indexed getter
0856 pd = igpd;
0857 // Merge any classic property descriptors
0858 if (gpd != null) {
0859 pd = mergePropertyDescriptor(igpd, gpd);
0860 }
0861 if (spd != null) {
0862 pd = mergePropertyDescriptor(igpd, spd);
0863 }
0864 } else if (spd != null) {
0865 // simple setter
0866 pd = spd;
0867 } else if (gpd != null) {
0868 // simple getter
0869 pd = gpd;
0870 }
0871
0872 // Very special case to ensure that an IndexedPropertyDescriptor
0873 // doesn't contain less information than the enclosed
0874 // PropertyDescriptor. If it does, then recreate as a
0875 // PropertyDescriptor. See 4168833
0876 if (pd instanceof IndexedPropertyDescriptor) {
0877 ipd = (IndexedPropertyDescriptor) pd;
0878 if (ipd.getIndexedReadMethod() == null
0879 && ipd.getIndexedWriteMethod() == null) {
0880 pd = new PropertyDescriptor(ipd);
0881 }
0882 }
0883
0884 // Find the first property descriptor
0885 // which does not have getter and setter methods.
0886 // See regression bug 4984912.
0887 if ((pd == null) && (list.size() > 0)) {
0888 pd = (PropertyDescriptor) list.get(0);
0889 }
0890
0891 if (pd != null) {
0892 properties.put(pd.getName(), pd);
0893 }
0894 }
0895 }
0896
0897 /**
0898 * Adds the property descriptor to the indexedproperty descriptor only if the
0899 * types are the same.
0900 *
0901 * The most specific property descriptor will take precedence.
0902 */
0903 private PropertyDescriptor mergePropertyDescriptor(
0904 IndexedPropertyDescriptor ipd, PropertyDescriptor pd) {
0905 PropertyDescriptor result = null;
0906
0907 Class propType = pd.getPropertyType();
0908 Class ipropType = ipd.getIndexedPropertyType();
0909
0910 if (propType.isArray()
0911 && propType.getComponentType() == ipropType) {
0912 if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0913 result = new IndexedPropertyDescriptor(pd, ipd);
0914 } else {
0915 result = new IndexedPropertyDescriptor(ipd, pd);
0916 }
0917 } else {
0918 // Cannot merge the pd because of type mismatch
0919 // Return the most specific pd
0920 if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
0921 result = ipd;
0922 } else {
0923 result = pd;
0924 // Try to add methods which may have been lost in the type change
0925 // See 4168833
0926 Method write = result.getWriteMethod();
0927 Method read = result.getReadMethod();
0928
0929 if (read == null && write != null) {
0930 read = findMethod(result.getClass0(), GET_PREFIX
0931 + NameGenerator
0932 .capitalize(result.getName()), 0);
0933 if (read != null) {
0934 try {
0935 result.setReadMethod(read);
0936 } catch (IntrospectionException ex) {
0937 // no consequences for failure.
0938 }
0939 }
0940 }
0941 if (write == null && read != null) {
0942 write = findMethod(result.getClass0(), SET_PREFIX
0943 + NameGenerator
0944 .capitalize(result.getName()), 1,
0945 new Class[] { FeatureDescriptor
0946 .getReturnType(result.getClass0(),
0947 read) });
0948 if (write != null) {
0949 try {
0950 result.setWriteMethod(write);
0951 } catch (IntrospectionException ex) {
0952 // no consequences for failure.
0953 }
0954 }
0955 }
0956 }
0957 }
0958 return result;
0959 }
0960
0961 // Handle regular pd merge
0962 private PropertyDescriptor mergePropertyDescriptor(
0963 PropertyDescriptor pd1, PropertyDescriptor pd2) {
0964 if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
0965 return new PropertyDescriptor(pd1, pd2);
0966 } else {
0967 return new PropertyDescriptor(pd2, pd1);
0968 }
0969 }
0970
0971 // Handle regular ipd merge
0972 private PropertyDescriptor mergePropertyDescriptor(
0973 IndexedPropertyDescriptor ipd1,
0974 IndexedPropertyDescriptor ipd2) {
0975 if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
0976 return new IndexedPropertyDescriptor(ipd1, ipd2);
0977 } else {
0978 return new IndexedPropertyDescriptor(ipd2, ipd1);
0979 }
0980 }
0981
0982 /**
0983 * @return An array of EventSetDescriptors describing the kinds of
0984 * events fired by the target bean.
0985 */
0986 private EventSetDescriptor[] getTargetEventInfo()
0987 throws IntrospectionException {
0988 if (events == null) {
0989 events = new HashMap();
0990 }
0991
0992 // Check if the bean has its own BeanInfo that will provide
0993 // explicit information.
0994 EventSetDescriptor[] explicitEvents = null;
0995 if (explicitBeanInfo != null) {
0996 explicitEvents = explicitBeanInfo.getEventSetDescriptors();
0997 int ix = explicitBeanInfo.getDefaultEventIndex();
0998 if (ix >= 0 && ix < explicitEvents.length) {
0999 defaultEventName = explicitEvents[ix].getName();
1000 }
1001 }
1002
1003 if (explicitEvents == null && super BeanInfo != null) {
1004 // We have no explicit BeanInfo events. Check with our parent.
1005 EventSetDescriptor super s[] = super BeanInfo
1006 .getEventSetDescriptors();
1007 for (int i = 0; i < super s.length; i++) {
1008 addEvent(super s[i]);
1009 }
1010 int ix = super BeanInfo.getDefaultEventIndex();
1011 if (ix >= 0 && ix < super s.length) {
1012 defaultEventName = super s[ix].getName();
1013 }
1014 }
1015
1016 for (int i = 0; i < additionalBeanInfo.length; i++) {
1017 EventSetDescriptor additional[] = additionalBeanInfo[i]
1018 .getEventSetDescriptors();
1019 if (additional != null) {
1020 for (int j = 0; j < additional.length; j++) {
1021 addEvent(additional[j]);
1022 }
1023 }
1024 }
1025
1026 if (explicitEvents != null) {
1027 // Add the explicit explicitBeanInfo data to our results.
1028 for (int i = 0; i < explicitEvents.length; i++) {
1029 addEvent(explicitEvents[i]);
1030 }
1031
1032 } else {
1033
1034 // Apply some reflection to the current class.
1035
1036 // Get an array of all the public beans methods at this level
1037 Method methodList[] = getPublicDeclaredMethods(beanClass);
1038
1039 // Find all suitable "add", "remove" and "get" Listener methods
1040 // The name of the listener type is the key for these hashtables
1041 // i.e, ActionListener
1042 Map adds = null;
1043 Map removes = null;
1044 Map gets = null;
1045
1046 for (int i = 0; i < methodList.length; i++) {
1047 Method method = methodList[i];
1048 if (method == null) {
1049 continue;
1050 }
1051 // skip static methods.
1052 int mods = method.getModifiers();
1053 if (Modifier.isStatic(mods)) {
1054 continue;
1055 }
1056 String name = method.getName();
1057 // Optimization avoid getParameterTypes
1058 if (!name.startsWith(ADD_PREFIX)
1059 && !name.startsWith(REMOVE_PREFIX)
1060 && !name.startsWith(GET_PREFIX)) {
1061 continue;
1062 }
1063
1064 Class argTypes[] = FeatureDescriptor.getParameterTypes(
1065 beanClass, method);
1066 Class resultType = FeatureDescriptor.getReturnType(
1067 beanClass, method);
1068
1069 if (name.startsWith(ADD_PREFIX)
1070 && argTypes.length == 1
1071 && resultType == Void.TYPE
1072 && Introspector.isSubclass(argTypes[0],
1073 eventListenerType)) {
1074 String listenerName = name.substring(3);
1075 if (listenerName.length() > 0
1076 && argTypes[0].getName().endsWith(
1077 listenerName)) {
1078 if (adds == null) {
1079 adds = new HashMap();
1080 }
1081 adds.put(listenerName, method);
1082 }
1083 } else if (name.startsWith(REMOVE_PREFIX)
1084 && argTypes.length == 1
1085 && resultType == Void.TYPE
1086 && Introspector.isSubclass(argTypes[0],
1087 eventListenerType)) {
1088 String listenerName = name.substring(6);
1089 if (listenerName.length() > 0
1090 && argTypes[0].getName().endsWith(
1091 listenerName)) {
1092 if (removes == null) {
1093 removes = new HashMap();
1094 }
1095 removes.put(listenerName, method);
1096 }
1097 } else if (name.startsWith(GET_PREFIX)
1098 && argTypes.length == 0
1099 && resultType.isArray()
1100 && Introspector.isSubclass(resultType
1101 .getComponentType(), eventListenerType)) {
1102 String listenerName = name.substring(3, name
1103 .length() - 1);
1104 if (listenerName.length() > 0
1105 && resultType.getComponentType().getName()
1106 .endsWith(listenerName)) {
1107 if (gets == null) {
1108 gets = new HashMap();
1109 }
1110 gets.put(listenerName, method);
1111 }
1112 }
1113 }
1114
1115 if (adds != null && removes != null) {
1116 // Now look for matching addFooListener+removeFooListener pairs.
1117 // Bonus if there is a matching getFooListeners method as well.
1118 Iterator keys = adds.keySet().iterator();
1119 while (keys.hasNext()) {
1120 String listenerName = (String) keys.next();
1121 // Skip any "add" which doesn't have a matching "remove" or
1122 // a listener name that doesn't end with Listener
1123 if (removes.get(listenerName) == null
1124 || !listenerName.endsWith("Listener")) {
1125 continue;
1126 }
1127 String eventName = decapitalize(listenerName
1128 .substring(0, listenerName.length() - 8));
1129 Method addMethod = (Method) adds.get(listenerName);
1130 Method removeMethod = (Method) removes
1131 .get(listenerName);
1132 Method getMethod = null;
1133 if (gets != null) {
1134 getMethod = (Method) gets.get(listenerName);
1135 }
1136 Class argType = FeatureDescriptor
1137 .getParameterTypes(beanClass, addMethod)[0];
1138
1139 // generate a list of Method objects for each of the target methods:
1140 Method allMethods[] = getPublicDeclaredMethods(argType);
1141 List validMethods = new ArrayList(allMethods.length);
1142 for (int i = 0; i < allMethods.length; i++) {
1143 if (allMethods[i] == null) {
1144 continue;
1145 }
1146
1147 if (isEventHandler(allMethods[i])) {
1148 validMethods.add(allMethods[i]);
1149 }
1150 }
1151 Method[] methods = (Method[]) validMethods
1152 .toArray(new Method[validMethods.size()]);
1153
1154 EventSetDescriptor esd = new EventSetDescriptor(
1155 eventName, argType, methods, addMethod,
1156 removeMethod, getMethod);
1157
1158 // If the adder method throws the TooManyListenersException then it
1159 // is a Unicast event source.
1160 if (throwsException(addMethod,
1161 java.util.TooManyListenersException.class)) {
1162 esd.setUnicast(true);
1163 }
1164 addEvent(esd);
1165 }
1166 } // if (adds != null ...
1167 }
1168 EventSetDescriptor[] result;
1169 if (events.size() == 0) {
1170 result = EMPTY_EVENTSETDESCRIPTORS;
1171 } else {
1172 // Allocate and populate the result array.
1173 result = new EventSetDescriptor[events.size()];
1174 result = (EventSetDescriptor[]) events.values().toArray(
1175 result);
1176
1177 // Set the default index.
1178 if (defaultEventName != null) {
1179 for (int i = 0; i < result.length; i++) {
1180 if (defaultEventName.equals(result[i].getName())) {
1181 defaultEventIndex = i;
1182 }
1183 }
1184 }
1185 }
1186 return result;
1187 }
1188
1189 private void addEvent(EventSetDescriptor esd) {
1190 String key = esd.getName();
1191 if (esd.getName().equals("propertyChange")) {
1192 propertyChangeSource = true;
1193 }
1194 EventSetDescriptor old = (EventSetDescriptor) events.get(key);
1195 if (old == null) {
1196 events.put(key, esd);
1197 return;
1198 }
1199 EventSetDescriptor composite = new EventSetDescriptor(old, esd);
1200 events.put(key, composite);
1201 }
1202
1203 /**
1204 * @return An array of MethodDescriptors describing the private
1205 * methods supported by the target bean.
1206 */
1207 private MethodDescriptor[] getTargetMethodInfo() {
1208 if (methods == null) {
1209 methods = new HashMap(100);
1210 }
1211
1212 // Check if the bean has its own BeanInfo that will provide
1213 // explicit information.
1214 MethodDescriptor[] explicitMethods = null;
1215 if (explicitBeanInfo != null) {
1216 explicitMethods = explicitBeanInfo.getMethodDescriptors();
1217 }
1218
1219 if (explicitMethods == null && super BeanInfo != null) {
1220 // We have no explicit BeanInfo methods. Check with our parent.
1221 MethodDescriptor super s[] = super BeanInfo
1222 .getMethodDescriptors();
1223 for (int i = 0; i < super s.length; i++) {
1224 addMethod(super s[i]);
1225 }
1226 }
1227
1228 for (int i = 0; i < additionalBeanInfo.length; i++) {
1229 MethodDescriptor additional[] = additionalBeanInfo[i]
1230 .getMethodDescriptors();
1231 if (additional != null) {
1232 for (int j = 0; j < additional.length; j++) {
1233 addMethod(additional[j]);
1234 }
1235 }
1236 }
1237
1238 if (explicitMethods != null) {
1239 // Add the explicit explicitBeanInfo data to our results.
1240 for (int i = 0; i < explicitMethods.length; i++) {
1241 addMethod(explicitMethods[i]);
1242 }
1243
1244 } else {
1245
1246 // Apply some reflection to the current class.
1247
1248 // First get an array of all the beans methods at this level
1249 Method methodList[] = getPublicDeclaredMethods(beanClass);
1250
1251 // Now analyze each method.
1252 for (int i = 0; i < methodList.length; i++) {
1253 Method method = methodList[i];
1254 if (method == null) {
1255 continue;
1256 }
1257 MethodDescriptor md = new MethodDescriptor(method);
1258 addMethod(md);
1259 }
1260 }
1261
1262 // Allocate and populate the result array.
1263 MethodDescriptor result[] = new MethodDescriptor[methods.size()];
1264 result = (MethodDescriptor[]) methods.values().toArray(result);
1265
1266 return result;
1267 }
1268
1269 private void addMethod(MethodDescriptor md) {
1270 // We have to be careful here to distinguish method by both name
1271 // and argument lists.
1272 // This method gets called a *lot, so we try to be efficient.
1273 String name = md.getName();
1274
1275 MethodDescriptor old = (MethodDescriptor) methods.get(name);
1276 if (old == null) {
1277 // This is the common case.
1278 methods.put(name, md);
1279 return;
1280 }
1281
1282 // We have a collision on method names. This is rare.
1283
1284 // Check if old and md have the same type.
1285 String[] p1 = md.getParamNames();
1286 String[] p2 = old.getParamNames();
1287
1288 boolean match = false;
1289 if (p1.length == p2.length) {
1290 match = true;
1291 for (int i = 0; i < p1.length; i++) {
1292 if (p1[i] != p2[i]) {
1293 match = false;
1294 break;
1295 }
1296 }
1297 }
1298 if (match) {
1299 MethodDescriptor composite = new MethodDescriptor(old, md);
1300 methods.put(name, composite);
1301 return;
1302 }
1303
1304 // We have a collision on method names with different type signatures.
1305 // This is very rare.
1306
1307 String longKey = makeQualifiedMethodName(name, p1);
1308 old = (MethodDescriptor) methods.get(longKey);
1309 if (old == null) {
1310 methods.put(longKey, md);
1311 return;
1312 }
1313 MethodDescriptor composite = new MethodDescriptor(old, md);
1314 methods.put(longKey, composite);
1315 }
1316
1317 /**
1318 * Creates a key for a method in a method cache.
1319 */
1320 private static String makeQualifiedMethodName(String name,
1321 String[] params) {
1322 StringBuffer sb = new StringBuffer(name);
1323 sb.append('=');
1324 for (int i = 0; i < params.length; i++) {
1325 sb.append(':');
1326 sb.append(params[i]);
1327 }
1328 return sb.toString();
1329 }
1330
1331 private int getTargetDefaultEventIndex() {
1332 return defaultEventIndex;
1333 }
1334
1335 private int getTargetDefaultPropertyIndex() {
1336 return defaultPropertyIndex;
1337 }
1338
1339 private BeanDescriptor getTargetBeanDescriptor() {
1340 // Use explicit info, if available,
1341 if (explicitBeanInfo != null) {
1342 BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
1343 if (bd != null) {
1344 return (bd);
1345 }
1346 }
1347 // OK, fabricate a default BeanDescriptor.
1348 return (new BeanDescriptor(beanClass));
1349 }
1350
1351 private boolean isEventHandler(Method m) {
1352 // We assume that a method is an event handler if it has a single
1353 // argument, whose type inherit from java.util.Event.
1354 Class argTypes[] = FeatureDescriptor.getParameterTypes(
1355 beanClass, m);
1356 if (argTypes.length != 1) {
1357 return false;
1358 }
1359 if (isSubclass(argTypes[0], java.util.EventObject.class)) {
1360 return true;
1361 }
1362 return false;
1363 }
1364
1365 /*
1366 * Internal method to return *public* methods within a class.
1367 */
1368 private static synchronized Method[] getPublicDeclaredMethods(
1369 Class clz) {
1370 // Looking up Class.getDeclaredMethods is relatively expensive,
1371 // so we cache the results.
1372 Method[] result = null;
1373 if (!ReflectUtil.isPackageAccessible(clz)) {
1374 return new Method[0];
1375 }
1376 final Class fclz = clz;
1377 Reference ref = (Reference) declaredMethodCache.get(fclz);
1378 if (ref != null) {
1379 result = (Method[]) ref.get();
1380 if (result != null) {
1381 return result;
1382 }
1383 }
1384
1385 // We have to raise privilege for getDeclaredMethods
1386 result = (Method[]) AccessController
1387 .doPrivileged(new PrivilegedAction() {
1388 public Object run() {
1389 return fclz.getDeclaredMethods();
1390 }
1391 });
1392
1393 // Null out any non-public methods.
1394 for (int i = 0; i < result.length; i++) {
1395 Method method = result[i];
1396 int mods = method.getModifiers();
1397 if (!Modifier.isPublic(mods)) {
1398 result[i] = null;
1399 }
1400 }
1401 // Add it to the cache.
1402 declaredMethodCache.put(fclz, new SoftReference(result));
1403 return result;
1404 }
1405
1406 //======================================================================
1407 // Package private support methods.
1408 //======================================================================
1409
1410 /**
1411 * Internal support for finding a target methodName with a given
1412 * parameter list on a given class.
1413 */
1414 private static Method internalFindMethod(Class start,
1415 String methodName, int argCount, Class args[]) {
1416 // For overriden methods we need to find the most derived version.
1417 // So we start with the given class and walk up the superclass chain.
1418
1419 Method method = null;
1420
1421 for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
1422 Method methods[] = getPublicDeclaredMethods(cl);
1423 for (int i = 0; i < methods.length; i++) {
1424 method = methods[i];
1425 if (method == null) {
1426 continue;
1427 }
1428
1429 // make sure method signature matches.
1430 Class params[] = FeatureDescriptor.getParameterTypes(
1431 start, method);
1432 if (method.getName().equals(methodName)
1433 && params.length == argCount) {
1434 if (args != null) {
1435 boolean different = false;
1436 if (argCount > 0) {
1437 for (int j = 0; j < argCount; j++) {
1438 if (params[j] != args[j]) {
1439 different = true;
1440 continue;
1441 }
1442 }
1443 if (different) {
1444 continue;
1445 }
1446 }
1447 }
1448 return method;
1449 }
1450 }
1451 }
1452 method = null;
1453
1454 // Now check any inherited interfaces. This is necessary both when
1455 // the argument class is itself an interface, and when the argument
1456 // class is an abstract class.
1457 Class ifcs[] = start.getInterfaces();
1458 for (int i = 0; i < ifcs.length; i++) {
1459 // Note: The original implementation had both methods calling
1460 // the 3 arg method. This is preserved but perhaps it should
1461 // pass the args array instead of null.
1462 method = internalFindMethod(ifcs[i], methodName, argCount,
1463 null);
1464 if (method != null) {
1465 break;
1466 }
1467 }
1468 return method;
1469 }
1470
1471 /**
1472 * Find a target methodName on a given class.
1473 */
1474 static Method findMethod(Class cls, String methodName, int argCount) {
1475 return findMethod(cls, methodName, argCount, null);
1476 }
1477
1478 /**
1479 * Find a target methodName with specific parameter list on a given class.
1480 * <p>
1481 * Used in the contructors of the EventSetDescriptor,
1482 * PropertyDescriptor and the IndexedPropertyDescriptor.
1483 * <p>
1484 * @param cls The Class object on which to retrieve the method.
1485 * @param methodName Name of the method.
1486 * @param argCount Number of arguments for the desired method.
1487 * @param args Array of argument types for the method.
1488 * @return the method or null if not found
1489 */
1490 static Method findMethod(Class cls, String methodName,
1491 int argCount, Class args[]) {
1492 if (methodName == null) {
1493 return null;
1494 }
1495 return internalFindMethod(cls, methodName, argCount, args);
1496 }
1497
1498 /**
1499 * Return true if class a is either equivalent to class b, or
1500 * if class a is a subclass of class b, i.e. if a either "extends"
1501 * or "implements" b.
1502 * Note tht either or both "Class" objects may represent interfaces.
1503 */
1504 static boolean isSubclass(Class a, Class b) {
1505 // We rely on the fact that for any given java class or
1506 // primtitive type there is a unqiue Class object, so
1507 // we can use object equivalence in the comparisons.
1508 if (a == b) {
1509 return true;
1510 }
1511 if (a == null || b == null) {
1512 return false;
1513 }
1514 for (Class x = a; x != null; x = x.getSuperclass()) {
1515 if (x == b) {
1516 return true;
1517 }
1518 if (b.isInterface()) {
1519 Class interfaces[] = x.getInterfaces();
1520 for (int i = 0; i < interfaces.length; i++) {
1521 if (isSubclass(interfaces[i], b)) {
1522 return true;
1523 }
1524 }
1525 }
1526 }
1527 return false;
1528 }
1529
1530 /**
1531 * Return true iff the given method throws the given exception.
1532 */
1533 private boolean throwsException(Method method, Class exception) {
1534 Class exs[] = method.getExceptionTypes();
1535 for (int i = 0; i < exs.length; i++) {
1536 if (exs[i] == exception) {
1537 return true;
1538 }
1539 }
1540 return false;
1541 }
1542
1543 /**
1544 * Try to create an instance of a named class.
1545 * First try the classloader of "sibling", then try the system
1546 * classloader then the class loader of the current Thread.
1547 */
1548 static Object instantiate(Class sibling, String className)
1549 throws InstantiationException, IllegalAccessException,
1550 ClassNotFoundException {
1551 // First check with sibling's classloader (if any).
1552 ClassLoader cl = sibling.getClassLoader();
1553 Class cls = ClassFinder.findClass(className, cl);
1554 return cls.newInstance();
1555 }
1556
1557 } // end class Introspector
1558
1559 //===========================================================================
1560
1561 /**
1562 * Package private implementation support class for Introspector's
1563 * internal use.
1564 * <p>
1565 * Mostly this is used as a placeholder for the descriptors.
1566 */
1567
1568 class GenericBeanInfo extends SimpleBeanInfo {
1569
1570 private BeanDescriptor beanDescriptor;
1571 private EventSetDescriptor[] events;
1572 private int defaultEvent;
1573 private PropertyDescriptor[] properties;
1574 private int defaultProperty;
1575 private MethodDescriptor[] methods;
1576 private BeanInfo targetBeanInfo;
1577
1578 public GenericBeanInfo(BeanDescriptor beanDescriptor,
1579 EventSetDescriptor[] events, int defaultEvent,
1580 PropertyDescriptor[] properties, int defaultProperty,
1581 MethodDescriptor[] methods, BeanInfo targetBeanInfo) {
1582 this .beanDescriptor = beanDescriptor;
1583 this .events = events;
1584 this .defaultEvent = defaultEvent;
1585 this .properties = properties;
1586 this .defaultProperty = defaultProperty;
1587 this .methods = methods;
1588 this .targetBeanInfo = targetBeanInfo;
1589 }
1590
1591 /**
1592 * Package-private dup constructor
1593 * This must isolate the new object from any changes to the old object.
1594 */
1595 GenericBeanInfo(GenericBeanInfo old) {
1596
1597 beanDescriptor = new BeanDescriptor(old.beanDescriptor);
1598 if (old.events != null) {
1599 int len = old.events.length;
1600 events = new EventSetDescriptor[len];
1601 for (int i = 0; i < len; i++) {
1602 events[i] = new EventSetDescriptor(old.events[i]);
1603 }
1604 }
1605 defaultEvent = old.defaultEvent;
1606 if (old.properties != null) {
1607 int len = old.properties.length;
1608 properties = new PropertyDescriptor[len];
1609 for (int i = 0; i < len; i++) {
1610 PropertyDescriptor oldp = old.properties[i];
1611 if (oldp instanceof IndexedPropertyDescriptor) {
1612 properties[i] = new IndexedPropertyDescriptor(
1613 (IndexedPropertyDescriptor) oldp);
1614 } else {
1615 properties[i] = new PropertyDescriptor(oldp);
1616 }
1617 }
1618 }
1619 defaultProperty = old.defaultProperty;
1620 if (old.methods != null) {
1621 int len = old.methods.length;
1622 methods = new MethodDescriptor[len];
1623 for (int i = 0; i < len; i++) {
1624 methods[i] = new MethodDescriptor(old.methods[i]);
1625 }
1626 }
1627 targetBeanInfo = old.targetBeanInfo;
1628 }
1629
1630 public PropertyDescriptor[] getPropertyDescriptors() {
1631 return properties;
1632 }
1633
1634 public int getDefaultPropertyIndex() {
1635 return defaultProperty;
1636 }
1637
1638 public EventSetDescriptor[] getEventSetDescriptors() {
1639 return events;
1640 }
1641
1642 public int getDefaultEventIndex() {
1643 return defaultEvent;
1644 }
1645
1646 public MethodDescriptor[] getMethodDescriptors() {
1647 return methods;
1648 }
1649
1650 public BeanDescriptor getBeanDescriptor() {
1651 return beanDescriptor;
1652 }
1653
1654 public java.awt.Image getIcon(int iconKind) {
1655 if (targetBeanInfo != null) {
1656 return targetBeanInfo.getIcon(iconKind);
1657 }
1658 return super.getIcon(iconKind);
1659 }
1660 }
|