FileDocCategorySizeDatePackage
Introspector.javaAPI DocJava SE 5 API50424Fri Aug 26 14:56:56 BST 2005java.beans

Introspector

public class Introspector extends Object
The Introspector class provides a standard way for tools to learn about the properties, events, and methods supported by a target Java Bean.

For each of those three kinds of information, the Introspector will separately analyze the bean's class and superclasses looking for either explicit or implicit information and use that information to build a BeanInfo object that comprehensively describes the target bean.

For each class "Foo", explicit information may be available if there exists a corresponding "FooBeanInfo" class that provides a non-null value when queried for the information. We first look for the BeanInfo class by taking the full package-qualified name of the target bean class and appending "BeanInfo" to form a new class name. If this fails, then we take the final classname component of this name, and look for that class in each of the packages specified in the BeanInfo package search path.

Thus for a class such as "sun.xyz.OurButton" we would first look for a BeanInfo class called "sun.xyz.OurButtonBeanInfo" and if that failed we'd look in each package in the BeanInfo search path for an OurButtonBeanInfo class. With the default search path, this would mean looking for "sun.beans.infos.OurButtonBeanInfo".

If a class provides explicit BeanInfo about itself then we add that to the BeanInfo information we obtained from analyzing any derived classes, but we regard the explicit information as being definitive for the current class and its base classes, and do not proceed any further up the superclass chain.

If we don't find explicit BeanInfo on a class, we use low-level reflection to study the methods of the class and apply standard design patterns to identify property accessors, event sources, or public methods. We then proceed to analyze the class's superclass and add in the information from it (and possibly on up the superclass chain).

Because the Introspector caches BeanInfo classes for better performance, take care if you use it in an application that uses multiple class loaders. In general, when you destroy a ClassLoader that has been used to introspect classes, you should use the {@link #flushCaches Introspector.flushCaches} or {@link #flushFromCaches Introspector.flushFromCaches} method to flush all of the introspected classes out of the cache.

For more information about introspection and design patterns, please consult the JavaBeans specification.

Fields Summary
public static final int
USE_ALL_BEANINFO
public static final int
IGNORE_IMMEDIATE_BEANINFO
public static final int
IGNORE_ALL_BEANINFO
private static Map
declaredMethodCache
private static Map
beanInfoCache
private Class
beanClass
private BeanInfo
explicitBeanInfo
private BeanInfo
superBeanInfo
private BeanInfo[]
additionalBeanInfo
private boolean
propertyChangeSource
private static Class
eventListenerType
private String
defaultEventName
private String
defaultPropertyName
private int
defaultEventIndex
private int
defaultPropertyIndex
private Map
methods
private Map
properties
private Map
events
private static final String
DEFAULT_INFO_PATH
private static String[]
searchPath
private static final EventSetDescriptor[]
EMPTY_EVENTSETDESCRIPTORS
private static final String
ADD_PREFIX
private static final String
REMOVE_PREFIX
private static final String
GET_PREFIX
private static final String
SET_PREFIX
private static final String
IS_PREFIX
private static final String
BEANINFO_SUFFIX
private HashMap
pdStore
Constructors Summary
private Introspector(Class beanClass, Class stopClass, int flags)

	this.beanClass = beanClass;

	// Check stopClass is a superClass of startClass.
	if (stopClass != null) {
	    boolean isSuper = false;
	    for (Class c = beanClass.getSuperclass(); c != null; c = c.getSuperclass()) {
	        if (c == stopClass) {
		    isSuper = true;
	        }
	    }
	    if (!isSuper) {
	        throw new IntrospectionException(stopClass.getName() + " not superclass of " + 
					beanClass.getName());
	    }
	}

        if (flags == USE_ALL_BEANINFO) {
	    explicitBeanInfo = findExplicitBeanInfo(beanClass);
        }

	Class superClass = beanClass.getSuperclass();
	if (superClass != stopClass) {
	    int newFlags = flags;
	    if (newFlags == IGNORE_IMMEDIATE_BEANINFO) {
		newFlags = USE_ALL_BEANINFO;
	    }
	    superBeanInfo = getBeanInfo(superClass, stopClass, newFlags);
	}
	if (explicitBeanInfo != null) {
	    additionalBeanInfo = explicitBeanInfo.getAdditionalBeanInfo();
	} 
	if (additionalBeanInfo == null) {
	    additionalBeanInfo = new BeanInfo[0];
	}
    
Methods Summary
private voidaddEvent(java.beans.EventSetDescriptor esd)

	String key = esd.getName();
	if (esd.getName().equals("propertyChange")) {
	    propertyChangeSource = true;
	}
	EventSetDescriptor old = (EventSetDescriptor)events.get(key);
	if (old == null) {
	    events.put(key, esd);
	    return;
	}
	EventSetDescriptor composite = new EventSetDescriptor(old, esd);
	events.put(key, composite);
    
private voidaddMethod(java.beans.MethodDescriptor md)

	// We have to be careful here to distinguish method by both name
	// and argument lists.
	// This method gets called a *lot, so we try to be efficient.
	String name = md.getName();

	MethodDescriptor old = (MethodDescriptor)methods.get(name);
	if (old == null) {
	    // This is the common case.
	    methods.put(name, md);
	    return;
	}	

	// We have a collision on method names.  This is rare.

	// Check if old and md have the same type.
	String[] p1 = md.getParamNames();
	String[] p2 = old.getParamNames();

	boolean match = false;
	if (p1.length == p2.length) {
	    match = true;
	    for (int i = 0; i < p1.length; i++) {
		if (p1[i] != p2[i]) {
		    match = false;
		    break;
		}
	    }
	}
	if (match) {
	    MethodDescriptor composite = new MethodDescriptor(old, md);
	    methods.put(name, composite);
	    return;
	}

	// We have a collision on method names with different type signatures.
	// This is very rare.

	String longKey = makeQualifiedMethodName(name, p1);
	old = (MethodDescriptor)methods.get(longKey);
	if (old == null) {
	    methods.put(longKey, md);
	    return;
	}	
	MethodDescriptor composite = new MethodDescriptor(old, md);
	methods.put(longKey, composite);
    
private voidaddPropertyDescriptor(java.beans.PropertyDescriptor pd)
Adds the property descriptor to the list store.


                 
        
	String propName = pd.getName();
	List list = (List)pdStore.get(propName);
	if (list == null) {
	    list = new ArrayList();
	    pdStore.put(propName, list);
	}
	list.add(pd);
    
public static java.lang.Stringdecapitalize(java.lang.String name)
Utility method to take a string and convert it to normal Java variable name capitalization. This normally means converting the first character from upper case to lower case, but in the (unusual) special case when there is more than one character and both the first and second characters are upper case, we leave it alone.

Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays as "URL".

param
name The string to be decapitalized.
return
The decapitalized version of the string.

	if (name == null || name.length() == 0) {
	    return name;
	}
	if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
			Character.isUpperCase(name.charAt(0))){
	    return name;
	}
	char chars[] = name.toCharArray();
	chars[0] = Character.toLowerCase(chars[0]);
	return new String(chars);
    
private static synchronized java.beans.BeanInfofindExplicitBeanInfo(java.lang.Class beanClass)
Looks for an explicit BeanInfo class that corresponds to the Class. First it looks in the existing package that the Class is defined in, then it checks to see if the class is its own BeanInfo. Finally, the BeanInfo search path is prepended to the class and searched.

return
Instance of an explicit BeanInfo class or null if one isn't found.

	String name = beanClass.getName() + BEANINFO_SUFFIX;
        try {
	    return (java.beans.BeanInfo)instantiate(beanClass, name);
	} catch (Exception ex) {
	    // Just drop through

        }
	// Now try checking if the bean is its own BeanInfo.
        try {
	    if (isSubclass(beanClass, java.beans.BeanInfo.class)) {
	        return (java.beans.BeanInfo)beanClass.newInstance();
	    }
	} catch (Exception ex) {
	    // Just drop through
        }
	// Now try looking for <searchPath>.fooBeanInfo
 	name = name.substring(name.lastIndexOf('.")+1);

	for (int i = 0; i < searchPath.length; i++) {
	    // This optimization will only use the BeanInfo search path if is has changed
	    // from the original or trying to get the ComponentBeanInfo. 
	    if (!DEFAULT_INFO_PATH.equals(searchPath[i]) || 
		DEFAULT_INFO_PATH.equals(searchPath[i]) && "ComponentBeanInfo".equals(name)) {
		try {
		    String fullName = searchPath[i] + "." + name;
		    java.beans.BeanInfo bi = (java.beans.BeanInfo)instantiate(beanClass, fullName);

		    // Make sure that the returned BeanInfo matches the class.
		    if (bi.getBeanDescriptor() != null) {
			if (bi.getBeanDescriptor().getBeanClass() == beanClass) {
			    return bi;
			}
		    } else if (bi.getPropertyDescriptors() != null) {
			PropertyDescriptor[] pds = bi.getPropertyDescriptors();
			for (int j = 0; j < pds.length; j++) {
			    Method method = pds[j].getReadMethod();
			    if (method == null) {
				method = pds[j].getWriteMethod();
			    }
			    if (method != null && method.getDeclaringClass() == beanClass) {
				return bi;
			    }
			}
		    } else if (bi.getMethodDescriptors() != null) {
			MethodDescriptor[] mds = bi.getMethodDescriptors();
			for (int j = 0; j < mds.length; j++) {
			    Method method = mds[j].getMethod();
			    if (method != null && method.getDeclaringClass() == beanClass) {
				return bi;
			    }
			}
		    }
		} catch (Exception ex) {
		    // Silently ignore any errors.
		}
	    }
	}
	return null;
    
static java.lang.reflect.MethodfindMethod(java.lang.Class cls, java.lang.String methodName, int argCount)
Find a target methodName on a given class.

	return findMethod(cls, methodName, argCount, null);
    
static java.lang.reflect.MethodfindMethod(java.lang.Class cls, java.lang.String methodName, int argCount, java.lang.Class[] args)
Find a target methodName with specific parameter list on a given class.

Used in the contructors of the EventSetDescriptor, PropertyDescriptor and the IndexedPropertyDescriptor.

param
cls The Class object on which to retrieve the method.
param
methodName Name of the method.
param
argCount Number of arguments for the desired method.
param
args Array of argument types for the method.
return
the method or null if not found

        if (methodName == null) {
            return null;
        }
        return internalFindMethod(cls, methodName, argCount, args);
    
public static voidflushCaches()
Flush all of the Introspector's internal caches. This method is not normally required. It is normally only needed by advanced tools that update existing "Class" objects in-place and need to make the Introspector re-analyze existing Class objects.

	beanInfoCache.clear();
	declaredMethodCache.clear();
    
public static voidflushFromCaches(java.lang.Class clz)
Flush the Introspector's internal cached information for a given class. This method is not normally required. It is normally only needed by advanced tools that update existing "Class" objects in-place and need to make the Introspector re-analyze an existing Class object. Note that only the direct state associated with the target Class object is flushed. We do not flush state for other Class objects with the same name, nor do we flush state for any related Class objects (such as subclasses), even though their state may include information indirectly obtained from the target Class object.

param
clz Class object to be flushed.
throws
NullPointerException If the Class object is null.

	if (clz == null) {
	    throw new NullPointerException();
	}
	beanInfoCache.remove(clz);
	declaredMethodCache.remove(clz);
    
public static java.beans.BeanInfogetBeanInfo(java.lang.Class beanClass)
Introspect on a Java Bean and learn about all its properties, exposed methods, and events.

If the BeanInfo class for a Java Bean has been previously Introspected then the BeanInfo class is retrieved from the BeanInfo cache.

param
beanClass The bean class to be analyzed.
return
A BeanInfo object describing the target bean.
exception
IntrospectionException if an exception occurs during introspection.
see
#flushCaches
see
#flushFromCaches


    //======================================================================
    // 				Public methods
    //======================================================================

                                                                                          
        
	 
    
	if (!ReflectUtil.isPackageAccessible(beanClass)) {
	    return (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
	}
	BeanInfo bi = (BeanInfo)beanInfoCache.get(beanClass);
	if (bi == null) {
	    bi = (new Introspector(beanClass, null, USE_ALL_BEANINFO)).getBeanInfo();
	    beanInfoCache.put(beanClass, bi);
	}
	return bi;
    
private java.beans.BeanInfogetBeanInfo()
Constructs a GenericBeanInfo class from the state of the Introspector


	// the evaluation order here is import, as we evaluate the
	// event sets and locate PropertyChangeListeners before we
	// look for properties.
	BeanDescriptor bd = getTargetBeanDescriptor();
	MethodDescriptor mds[] = getTargetMethodInfo();
	EventSetDescriptor esds[] = getTargetEventInfo();
	PropertyDescriptor pds[] = getTargetPropertyInfo();

	int defaultEvent = getTargetDefaultEventIndex();
	int defaultProperty = getTargetDefaultPropertyIndex();

        return new GenericBeanInfo(bd, esds, defaultEvent, pds,
			defaultProperty, mds, explicitBeanInfo);
	
    
public static java.beans.BeanInfogetBeanInfo(java.lang.Class beanClass, int flags)
Introspect on a Java bean and learn about all its properties, exposed methods, and events, subject to some control flags.

If the BeanInfo class for a Java Bean has been previously Introspected based on the same arguments then the BeanInfo class is retrieved from the BeanInfo cache.

param
beanClass The bean class to be analyzed.
param
flags Flags to control the introspection. If flags == USE_ALL_BEANINFO then we use all of the BeanInfo classes we can discover. If flags == IGNORE_IMMEDIATE_BEANINFO then we ignore any BeanInfo associated with the specified beanClass. If flags == IGNORE_ALL_BEANINFO then we ignore all BeanInfo associated with the specified beanClass or any of its parent classes.
return
A BeanInfo object describing the target bean.
exception
IntrospectionException if an exception occurs during introspection.

	return getBeanInfo(beanClass, null, flags);
    
public static java.beans.BeanInfogetBeanInfo(java.lang.Class beanClass, java.lang.Class stopClass)
Introspect on a Java bean and learn all about its properties, exposed methods, below a given "stop" point.

If the BeanInfo class for a Java Bean has been previously Introspected based on the same arguments, then the BeanInfo class is retrieved from the BeanInfo cache.

param
beanClass The bean class to be analyzed.
param
stopClass The baseclass at which to stop the analysis. Any methods/properties/events in the stopClass or in its baseclasses will be ignored in the analysis.
exception
IntrospectionException if an exception occurs during introspection.

	return getBeanInfo(beanClass, stopClass, USE_ALL_BEANINFO);
    
private static java.beans.BeanInfogetBeanInfo(java.lang.Class beanClass, java.lang.Class stopClass, int flags)
Only called from the public getBeanInfo methods. This method caches the Introspected BeanInfo based on the arguments.

	BeanInfo bi;	
	if (stopClass == null && flags == USE_ALL_BEANINFO) {
	    // Same parameters to take advantage of caching.
	    bi = getBeanInfo(beanClass);
	} else {
	    bi = (new Introspector(beanClass, stopClass, flags)).getBeanInfo();
	}
	return bi;

	// Old behaviour: Make an independent copy of the BeanInfo.
	//return new GenericBeanInfo(bi);
    
public static synchronized java.lang.String[]getBeanInfoSearchPath()
Gets the list of package names that will be used for finding BeanInfo classes.

return
The array of package names that will be searched in order to find BeanInfo classes. The default value for this array is implementation-dependent; e.g. Sun implementation initially sets to {"sun.beans.infos"}.

	// Return a copy of the searchPath.
	String result[] = new String[searchPath.length];
	for (int i = 0; i < searchPath.length; i++) {
	    result[i] = searchPath[i];
	}
	return result;
    
private static synchronized java.lang.reflect.Method[]getPublicDeclaredMethods(java.lang.Class clz)

	// Looking up Class.getDeclaredMethods is relatively expensive,
	// so we cache the results.
	Method[] result = null;
	if (!ReflectUtil.isPackageAccessible(clz)) {
	    return new Method[0];
	}
	final Class fclz = clz;
	Reference ref = (Reference)declaredMethodCache.get(fclz);
	if (ref != null) {
	    result = (Method[])ref.get();
	    if (result != null) {
		return result;
	    }
	}

	// We have to raise privilege for getDeclaredMethods
	result = (Method[]) AccessController.doPrivileged(new PrivilegedAction() {
		public Object run() {
		    return fclz.getDeclaredMethods();
		}
	    });


	// Null out any non-public methods.
	for (int i = 0; i < result.length; i++) {
	    Method method = result[i];
	    int mods = method.getModifiers();
	    if (!Modifier.isPublic(mods)) {
	 	result[i] = null;
	    }
        }    
	// Add it to the cache.
	declaredMethodCache.put(fclz, new SoftReference(result));
	return result;
    
private java.beans.BeanDescriptorgetTargetBeanDescriptor()

	// Use explicit info, if available,
	if (explicitBeanInfo != null) {
	    BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
	    if (bd != null) {
		return (bd);
	    }
	}
	// OK, fabricate a default BeanDescriptor.
	return (new BeanDescriptor(beanClass));
    
private intgetTargetDefaultEventIndex()

	return defaultEventIndex;
    
private intgetTargetDefaultPropertyIndex()

	return defaultPropertyIndex;
    
private java.beans.EventSetDescriptor[]getTargetEventInfo()

return
An array of EventSetDescriptors describing the kinds of events fired by the target bean.

	if (events == null) {
	    events = new HashMap();
	}

	// Check if the bean has its own BeanInfo that will provide
	// explicit information.
        EventSetDescriptor[] explicitEvents = null;
	if (explicitBeanInfo != null) {
	    explicitEvents = explicitBeanInfo.getEventSetDescriptors();
	    int ix = explicitBeanInfo.getDefaultEventIndex();
	    if (ix >= 0 && ix < explicitEvents.length) {
		defaultEventName = explicitEvents[ix].getName();
	    }
	}

	if (explicitEvents == null && superBeanInfo != null) {
	    // We have no explicit BeanInfo events.  Check with our parent.
	    EventSetDescriptor supers[] = superBeanInfo.getEventSetDescriptors();
	    for (int i = 0 ; i < supers.length; i++) {
		addEvent(supers[i]);
	    }
	    int ix = superBeanInfo.getDefaultEventIndex();
	    if (ix >= 0 && ix < supers.length) {
		defaultEventName = supers[ix].getName();
	    }
	}

	for (int i = 0; i < additionalBeanInfo.length; i++) {
	    EventSetDescriptor additional[] = additionalBeanInfo[i].getEventSetDescriptors();
	    if (additional != null) {
	        for (int j = 0 ; j < additional.length; j++) {
		    addEvent(additional[j]);
	        }
	    }
	}

	if (explicitEvents != null) {
	    // Add the explicit explicitBeanInfo data to our results.
	    for (int i = 0 ; i < explicitEvents.length; i++) {
		addEvent(explicitEvents[i]);
	    }

	} else {

	    // Apply some reflection to the current class.

	    // Get an array of all the public beans methods at this level
	    Method methodList[] = getPublicDeclaredMethods(beanClass);

	    // Find all suitable "add", "remove" and "get" Listener methods
	    // The name of the listener type is the key for these hashtables
	    // i.e, ActionListener
	    Map adds = null;
	    Map removes = null;
	    Map gets = null;

	    for (int i = 0; i < methodList.length; i++) {
	        Method method = methodList[i];
		if (method == null) {
		    continue;
		}
	        // skip static methods.
		int mods = method.getModifiers();
		if (Modifier.isStatic(mods)) {
		    continue;
		}
	        String name = method.getName();
		// Optimization avoid getParameterTypes
		if (!name.startsWith(ADD_PREFIX) && !name.startsWith(REMOVE_PREFIX) 
		    && !name.startsWith(GET_PREFIX)) {
		    continue;
		}

	        Class argTypes[] = method.getParameterTypes();
	        Class resultType = method.getReturnType();

	        if (name.startsWith(ADD_PREFIX) && argTypes.length == 1 &&
		    resultType == Void.TYPE &&
		    Introspector.isSubclass(argTypes[0], eventListenerType)) {
		    String listenerName = name.substring(3);
		    if (listenerName.length() > 0 && 
			argTypes[0].getName().endsWith(listenerName)) {
			if (adds == null) {
			    adds = new HashMap();
			}
			adds.put(listenerName, method);
		    }
		}
		else if (name.startsWith(REMOVE_PREFIX) && argTypes.length == 1 &&
			 resultType == Void.TYPE &&
			 Introspector.isSubclass(argTypes[0], eventListenerType)) {
		    String listenerName = name.substring(6);
		    if (listenerName.length() > 0 && 
			argTypes[0].getName().endsWith(listenerName)) {
			if (removes == null) {
			    removes = new HashMap();
			}
			removes.put(listenerName, method);
		    }
	        } 
		else if (name.startsWith(GET_PREFIX) && argTypes.length == 0 &&
			 resultType.isArray() && 
			 Introspector.isSubclass(resultType.getComponentType(), 
						 eventListenerType)) {
		    String listenerName  = name.substring(3, name.length() - 1);
		    if (listenerName.length() > 0 && 
			resultType.getComponentType().getName().endsWith(listenerName)) {
			if (gets == null) {
			    gets = new HashMap();
			}
			gets.put(listenerName, method);
		    }
		}
	    }

	    if (adds != null && removes != null) {
		// Now look for matching addFooListener+removeFooListener pairs.
		// Bonus if there is a matching getFooListeners method as well.
		Iterator keys = adds.keySet().iterator();
		while (keys.hasNext()) {
		    String listenerName = (String) keys.next();
		    // Skip any "add" which doesn't have a matching "remove" or
		    // a listener name that doesn't end with Listener
		    if (removes.get(listenerName) == null || !listenerName.endsWith("Listener")) {
			continue;
		    } 
		    String eventName = decapitalize(listenerName.substring(0, listenerName.length()-8));
		    Method addMethod = (Method)adds.get(listenerName);
		    Method removeMethod = (Method)removes.get(listenerName);
		    Method getMethod = null;
		    if (gets != null) {
			getMethod = (Method)gets.get(listenerName);
		    }
		    Class argType = addMethod.getParameterTypes()[0];
		    
		    // generate a list of Method objects for each of the target methods:
		    Method allMethods[] = getPublicDeclaredMethods(argType);
		    List validMethods = new ArrayList(allMethods.length);
		    for (int i = 0; i < allMethods.length; i++) {
			if (allMethods[i] == null) {
			    continue;
			}
		    
			if (isEventHandler(allMethods[i])) {
			    validMethods.add(allMethods[i]);
			}
		    }
		    Method[] methods = (Method[])validMethods.toArray(new Method[validMethods.size()]);

		    EventSetDescriptor esd = new EventSetDescriptor(eventName, argType,
								    methods, addMethod, 
								    removeMethod, 
								    getMethod);

		    // If the adder method throws the TooManyListenersException then it
		    // is a Unicast event source.
		    if (throwsException(addMethod,
					java.util.TooManyListenersException.class)) {
			esd.setUnicast(true);
		    }
		    addEvent(esd);
		}
	    } // if (adds != null ...
	}
	EventSetDescriptor[] result;
	if (events.size() == 0) {
	    result = EMPTY_EVENTSETDESCRIPTORS;
	} else {
	    // Allocate and populate the result array.
	    result = new EventSetDescriptor[events.size()];
	    result = (EventSetDescriptor[])events.values().toArray(result);

	    // Set the default index. 
	    if (defaultEventName != null) {
		for (int i = 0; i < result.length; i++) {
		    if (defaultEventName.equals(result[i].getName())) {
			defaultEventIndex = i;
		    }
		}
	    }
	}
	return result;
    
private java.beans.MethodDescriptor[]getTargetMethodInfo()

return
An array of MethodDescriptors describing the private methods supported by the target bean.

	if (methods == null) {
	    methods = new HashMap(100);
	}

	// Check if the bean has its own BeanInfo that will provide
	// explicit information.
        MethodDescriptor[] explicitMethods = null;
	if (explicitBeanInfo != null) {
	    explicitMethods = explicitBeanInfo.getMethodDescriptors();
	}

	if (explicitMethods == null && superBeanInfo != null) {
	    // We have no explicit BeanInfo methods.  Check with our parent.
	    MethodDescriptor supers[] = superBeanInfo.getMethodDescriptors();
	    for (int i = 0 ; i < supers.length; i++) {
		addMethod(supers[i]);
	    }
	}

	for (int i = 0; i < additionalBeanInfo.length; i++) {
	    MethodDescriptor additional[] = additionalBeanInfo[i].getMethodDescriptors();
	    if (additional != null) {
	        for (int j = 0 ; j < additional.length; j++) {
		    addMethod(additional[j]);
	        }
	    }
	}

	if (explicitMethods != null) {
	    // Add the explicit explicitBeanInfo data to our results.
	    for (int i = 0 ; i < explicitMethods.length; i++) {
		addMethod(explicitMethods[i]);
	    }

	} else {

	    // Apply some reflection to the current class.

	    // First get an array of all the beans methods at this level
	    Method methodList[] = getPublicDeclaredMethods(beanClass);

	    // Now analyze each method.
	    for (int i = 0; i < methodList.length; i++) {
	        Method method = methodList[i];
		if (method == null) {
		    continue;
		}
		MethodDescriptor md = new MethodDescriptor(method);
		addMethod(md);
	    }
	}

	// Allocate and populate the result array.
	MethodDescriptor result[] = new MethodDescriptor[methods.size()];
	result = (MethodDescriptor[])methods.values().toArray(result);

	return result;
    
private java.beans.PropertyDescriptor[]getTargetPropertyInfo()

return
An array of PropertyDescriptors describing the editable properties supported by the target bean.


	// Check if the bean has its own BeanInfo that will provide
	// explicit information.
        PropertyDescriptor[] explicitProperties = null;
	if (explicitBeanInfo != null) {
	    explicitProperties = explicitBeanInfo.getPropertyDescriptors();
	    int ix = explicitBeanInfo.getDefaultPropertyIndex();
	    if (ix >= 0 && ix < explicitProperties.length) {
		defaultPropertyName = explicitProperties[ix].getName();
	    }
        }

	if (explicitProperties == null && superBeanInfo != null) {
	    // We have no explicit BeanInfo properties.  Check with our parent.
	    PropertyDescriptor supers[] = superBeanInfo.getPropertyDescriptors();
	    for (int i = 0 ; i < supers.length; i++) {
		addPropertyDescriptor(supers[i]);
	    }
	    int ix = superBeanInfo.getDefaultPropertyIndex();
	    if (ix >= 0 && ix < supers.length) {
		defaultPropertyName = supers[ix].getName();
	    }
	}

	for (int i = 0; i < additionalBeanInfo.length; i++) {
	    PropertyDescriptor additional[] = additionalBeanInfo[i].getPropertyDescriptors();
	    if (additional != null) {
	        for (int j = 0 ; j < additional.length; j++) {
		    addPropertyDescriptor(additional[j]);
	        }
	    }
	}

	if (explicitProperties != null) {
	    // Add the explicit BeanInfo data to our results.
	    for (int i = 0 ; i < explicitProperties.length; i++) {
		addPropertyDescriptor(explicitProperties[i]);
	    }

	} else {

	    // Apply some reflection to the current class.

	    // First get an array of all the public methods at this level
	    Method methodList[] = getPublicDeclaredMethods(beanClass);

	    // Now analyze each method.
	    for (int i = 0; i < methodList.length; i++) {
	        Method method = methodList[i];
		if (method == null) {
		    continue;
		}
	        // skip static methods.
		int mods = method.getModifiers();
		if (Modifier.isStatic(mods)) {
		    continue;
		}
	        String name = method.getName();
	        Class argTypes[] = method.getParameterTypes();
	        Class resultType = method.getReturnType();
		int argCount = argTypes.length;
		PropertyDescriptor pd = null;

		if (name.length() <= 3 && !name.startsWith(IS_PREFIX)) {
		    // Optimization. Don't bother with invalid propertyNames.
		    continue;
		}

		try {

	            if (argCount == 0) {
		        if (name.startsWith(GET_PREFIX)) {
		            // Simple getter
	                    pd = new PropertyDescriptor(decapitalize(name.substring(3)),
						method, null);
	                } else if (resultType == boolean.class && name.startsWith(IS_PREFIX)) {
		            // Boolean getter
	                    pd = new PropertyDescriptor(decapitalize(name.substring(2)),
						method, null);
		        }
	            } else if (argCount == 1) {
		        if (argTypes[0] == int.class && name.startsWith(GET_PREFIX)) {
		            pd = new IndexedPropertyDescriptor(
						decapitalize(name.substring(3)),
						null, null,
						method,	null);
		        } else if (resultType == void.class && name.startsWith(SET_PREFIX)) {
		            // Simple setter
	                    pd = new PropertyDescriptor(decapitalize(name.substring(3)),
						null, method);
		            if (throwsException(method, PropertyVetoException.class)) {
			        pd.setConstrained(true);
			    }			
		        }
	            } else if (argCount == 2) {
			    if (argTypes[0] == int.class && name.startsWith(SET_PREFIX)) {
	                    pd = new IndexedPropertyDescriptor(
						decapitalize(name.substring(3)),
						null, null,
						null, method);
		            if (throwsException(method, PropertyVetoException.class)) {
			        pd.setConstrained(true);			
			    }
			}
		    }
		} catch (IntrospectionException ex) {
		    // This happens if a PropertyDescriptor or IndexedPropertyDescriptor
	            // constructor fins that the method violates details of the deisgn
		    // pattern, e.g. by having an empty name, or a getter returning
		    // void , or whatever.
		    pd = null;
		}

		if (pd != null) {
		    // If this class or one of its base classes is a PropertyChange
		    // source, then we assume that any properties we discover are "bound".
		    if (propertyChangeSource) {
			pd.setBound(true);
		    }
		    addPropertyDescriptor(pd);
		}
	    }
	}
	processPropertyDescriptors();

	// Allocate and populate the result array.
	PropertyDescriptor result[] = new PropertyDescriptor[properties.size()];
	result = (PropertyDescriptor[])properties.values().toArray(result);

	// Set the default index. 
	if (defaultPropertyName != null) {
	    for (int i = 0; i < result.length; i++) {
		if (defaultPropertyName.equals(result[i].getName())) {
		    defaultPropertyIndex = i;
		}
	    }
	}

	return result;
    
static java.lang.Objectinstantiate(java.lang.Class sibling, java.lang.String className)
Try to create an instance of a named class. First try the classloader of "sibling", then try the system classloader then the class loader of the current Thread.

	// First check with sibling's classloader (if any). 
	ClassLoader cl = sibling.getClassLoader();
	if (cl != null) {
	    try {
	        Class cls = cl.loadClass(className);
		return cls.newInstance();
	    } catch (Exception ex) {
	        // Just drop through and try the system classloader.
	    }
        }

	// Now try the system classloader.
	try {
	    cl = ClassLoader.getSystemClassLoader();
	    if (cl != null) {
	        Class cls = cl.loadClass(className);
		return cls.newInstance();
	    }
        } catch (Exception ex) {
	    // We're not allowed to access the system class loader or
	    // the class creation failed.
	    // Drop through.
	}

	// Use the classloader from the current Thread.
	cl = Thread.currentThread().getContextClassLoader();
	Class cls = cl.loadClass(className);
	return cls.newInstance();
    
private static java.lang.reflect.MethodinternalFindMethod(java.lang.Class start, java.lang.String methodName, int argCount, java.lang.Class[] args)
Internal support for finding a target methodName with a given parameter list on a given class.

        // For overriden methods we need to find the most derived version.
        // So we start with the given class and walk up the superclass chain.

	Method method = null;

        for (Class cl = start; cl != null; cl = cl.getSuperclass()) {
            Method methods[] = getPublicDeclaredMethods(cl);
            for (int i = 0; i < methods.length; i++) {
                method = methods[i];
                if (method == null) {
                    continue;
                }

                // make sure method signature matches.
                Class params[] = method.getParameterTypes();
                if (method.getName().equals(methodName) && 
                    params.length == argCount) {
		    if (args != null) {
			boolean different = false;
			if (argCount > 0) {
			    for (int j = 0; j < argCount; j++) {
				if (params[j] != args[j]) {
				    different = true;
				    continue;
				}
			    }
			    if (different) {
				continue;
			    }
			}
		    }
                    return method;
                }
            }
        }
	method = null;

        // Now check any inherited interfaces.  This is necessary both when
        // the argument class is itself an interface, and when the argument
        // class is an abstract class.
        Class ifcs[] = start.getInterfaces();
        for (int i = 0 ; i < ifcs.length; i++) {
	    // Note: The original implementation had both methods calling
	    // the 3 arg method. This is preserved but perhaps it should
	    // pass the args array instead of null.
            method = internalFindMethod(ifcs[i], methodName, argCount, null);
	    if (method != null) {
		break;
	    }
        }
        return method;
    
private booleanisEventHandler(java.lang.reflect.Method m)

	// We assume that a method is an event handler if it has a single
        // argument, whose type inherit from java.util.Event.
	Class argTypes[] = m.getParameterTypes();
	if (argTypes.length != 1) {
	    return false;
	}
	if (isSubclass(argTypes[0], java.util.EventObject.class)) {
	    return true;
	}
	return false;
    
static booleanisSubclass(java.lang.Class a, java.lang.Class b)
Return true if class a is either equivalent to class b, or if class a is a subclass of class b, i.e. if a either "extends" or "implements" b. Note tht either or both "Class" objects may represent interfaces.

	// We rely on the fact that for any given java class or
        // primtitive type there is a unqiue Class object, so
	// we can use object equivalence in the comparisons.
	if (a == b) {
	    return true;
	}
	if (a == null || b == null) {
	    return false;
	}
	for (Class x = a; x != null; x = x.getSuperclass()) {
	    if (x == b) {	
		return true;
	    }
	    if (b.isInterface()) {
		Class interfaces[] = x.getInterfaces();
		for (int i = 0; i < interfaces.length; i++) {
		    if (isSubclass(interfaces[i], b)) {
			return true;
		    }
		}
	    }
	}
	return false;
    
private static java.lang.StringmakeQualifiedMethodName(java.lang.String name, java.lang.String[] params)
Creates a key for a method in a method cache.

	StringBuffer sb = new StringBuffer(name);
	sb.append('=");
	for (int i = 0; i < params.length; i++) {
	    sb.append(':");
	    sb.append(params[i]);
	}
	return sb.toString();
    
private java.beans.PropertyDescriptormergePropertyDescriptor(java.beans.IndexedPropertyDescriptor ipd, java.beans.PropertyDescriptor pd)
Adds the property descriptor to the indexedproperty descriptor only if the types are the same. The most specific property descriptor will take precedence.

	PropertyDescriptor result = null;

	Class propType = pd.getPropertyType();
	Class ipropType = ipd.getIndexedPropertyType();

	if (propType.isArray() && propType.getComponentType() == ipropType) {
	    if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
		result = new IndexedPropertyDescriptor(pd, ipd);
	    } else {
		result = new IndexedPropertyDescriptor(ipd, pd);
	    }
	} else {
	    // Cannot merge the pd because of type mismatch
	    // Return the most specific pd
	    if (pd.getClass0().isAssignableFrom(ipd.getClass0())) {
		result = ipd;
	    } else {
		result = pd;
		// Try to add methods which may have been lost in the type change
		// See 4168833
		Method write = result.getWriteMethod();
		Method read = result.getReadMethod();
		    		    
		if (read == null && write != null) {
		    read = findMethod(result.getClass0(), 
				      "get" + result.capitalize(result.getName()), 0);
		    if (read != null) {
			try {
			    result.setReadMethod(read);
			} catch (IntrospectionException ex) {
			    // no consequences for failure.
			}
		    }
		}
		if (write == null && read != null) {
		    write = findMethod(result.getClass0(), 
				       "set" + result.capitalize(result.getName()), 1,
				       new Class[] { read.getReturnType() });
		    if (write != null) {
			try {
			    result.setWriteMethod(write);
			} catch (IntrospectionException ex) {
			    // no consequences for failure.
			}
		    }
		}
	    }
	}
	return result;
    
private java.beans.PropertyDescriptormergePropertyDescriptor(java.beans.PropertyDescriptor pd1, java.beans.PropertyDescriptor pd2)

	if (pd1.getClass0().isAssignableFrom(pd2.getClass0())) {
	    return new PropertyDescriptor(pd1, pd2);
	} else {
	    return new PropertyDescriptor(pd2, pd1);
	}
    
private java.beans.PropertyDescriptormergePropertyDescriptor(java.beans.IndexedPropertyDescriptor ipd1, java.beans.IndexedPropertyDescriptor ipd2)

	if (ipd1.getClass0().isAssignableFrom(ipd2.getClass0())) {
	    return new IndexedPropertyDescriptor(ipd1, ipd2);
	} else {
	    return new IndexedPropertyDescriptor(ipd2, ipd1);
	}
    
private voidprocessPropertyDescriptors()
Populates the property descriptor table by merging the lists of Property descriptors.

	if (properties == null) {
	    properties = new TreeMap();
	}

	List list;

	PropertyDescriptor pd, gpd, spd;
	IndexedPropertyDescriptor ipd, igpd, ispd;

	Iterator it = pdStore.values().iterator();
	while (it.hasNext()) {
	    pd = null; gpd = null; spd = null; 
	    ipd = null; igpd = null; ispd = null;

	    list = (List)it.next();

	    // First pass. Find the latest getter method. Merge properties
	    // of previous getter methods.
	    for (int i = 0; i < list.size(); i++) {
		pd = (PropertyDescriptor)list.get(i);
		if (pd instanceof IndexedPropertyDescriptor) {
		    ipd = (IndexedPropertyDescriptor)pd;
		    if (ipd.getIndexedReadMethod() != null) {
			if (igpd != null) {
			    igpd = new IndexedPropertyDescriptor(igpd, ipd);
			} else {
			    igpd = ipd;
			}
		    }
		} else {
		    if (pd.getReadMethod() != null) {
			if (gpd != null) {
			    // Don't replace the existing read
			    // method if it starts with "is"
			    Method method = gpd.getReadMethod();
			    if (!method.getName().startsWith(IS_PREFIX)) {
				gpd = new PropertyDescriptor(gpd, pd);
			    }
			} else {
			    gpd = pd;
			}
		    }
		}
	    }

	    // Second pass. Find the latest setter method which
	    // has the same type as the getter method.
	    for (int i = 0; i < list.size(); i++) {
		pd = (PropertyDescriptor)list.get(i);
		if (pd instanceof IndexedPropertyDescriptor) {
		    ipd = (IndexedPropertyDescriptor)pd;
		    if (ipd.getIndexedWriteMethod() != null) {
			if (igpd != null) {
			    if (igpd.getIndexedPropertyType() 
				== ipd.getIndexedPropertyType()) {
				if (ispd != null) {
				    ispd = new IndexedPropertyDescriptor(ispd, ipd);
				} else {
				    ispd = ipd;
				}
			    }
			} else {
			    if (ispd != null) {
				ispd = new IndexedPropertyDescriptor(ispd, ipd);
			    } else {
				ispd = ipd;
			    }
			}
		    }
		} else {
		    if (pd.getWriteMethod() != null) {
			if (gpd != null) {
			    if (gpd.getPropertyType() == pd.getPropertyType()) {
				if (spd != null) {
				    spd = new PropertyDescriptor(spd, pd);
				} else {
				    spd = pd;
				}
			    }
			} else {
			    if (spd != null) {
				spd = new PropertyDescriptor(spd, pd);
			    } else {
				spd = pd;
			    }
			}
		    }
		}
	    }

	    // At this stage we should have either PDs or IPDs for the
	    // representative getters and setters. The order at which the 
	    // property descriptors are determined represent the 
	    // precedence of the property ordering.
	    pd = null; ipd = null;

	    if (igpd != null && ispd != null) {
		// Complete indexed properties set
		// Merge any classic property descriptors
		if (gpd != null) {
		    PropertyDescriptor tpd = mergePropertyDescriptor(igpd, gpd);
		    if (tpd instanceof IndexedPropertyDescriptor) {
			igpd = (IndexedPropertyDescriptor)tpd;
		    }
		}
		if (spd != null) {
		    PropertyDescriptor tpd = mergePropertyDescriptor(ispd, spd);
		    if (tpd instanceof IndexedPropertyDescriptor) {
			ispd = (IndexedPropertyDescriptor)tpd;
		    }
		}
		if (igpd == ispd) {
		    pd = igpd;
		} else {
		    pd = mergePropertyDescriptor(igpd, ispd);
		}
	    } else if (gpd != null && spd != null) {
		// Complete simple properties set
		if (gpd == spd) {
		    pd = gpd;
		} else {
		    pd = mergePropertyDescriptor(gpd, spd);
		}
	    } else if (ispd != null) {
		// indexed setter
		pd = ispd;
		// Merge any classic property descriptors
		if (spd != null) {
		    pd = mergePropertyDescriptor(ispd, spd);
		}
		if (gpd != null) {
		    pd = mergePropertyDescriptor(ispd, gpd);
		}
	    } else if (igpd != null) {
		// indexed getter
		pd = igpd;
		// Merge any classic property descriptors
		if (gpd != null) {
		    pd = mergePropertyDescriptor(igpd, gpd);
		}
		if (spd != null) {
		    pd = mergePropertyDescriptor(igpd, spd);
		}
	    } else if (spd != null) {
		// simple setter
		pd = spd;
	    } else if (gpd != null) {
		// simple getter
		pd = gpd;
	    }

	    // Very special case to ensure that an IndexedPropertyDescriptor
	    // doesn't contain less information than the enclosed 
	    // PropertyDescriptor. If it does, then recreate as a 
	    // PropertyDescriptor. See 4168833
	    if (pd instanceof IndexedPropertyDescriptor) {
		ipd = (IndexedPropertyDescriptor)pd;
		if (ipd.getIndexedReadMethod() == null && ipd.getIndexedWriteMethod() == null) {
		    pd = new PropertyDescriptor(ipd);
		}
	    }

	    if (pd != null) {
		properties.put(pd.getName(), pd);
	    }
	}
    
public static synchronized voidsetBeanInfoSearchPath(java.lang.String[] path)
Change the list of package names that will be used for finding BeanInfo classes. The behaviour of this method is undefined if parameter path is null.

First, if there is a security manager, its checkPropertiesAccess method is called. This could result in a SecurityException.

param
path Array of package names.
exception
SecurityException if a security manager exists and its checkPropertiesAccess method doesn't allow setting of system properties.
see
SecurityManager#checkPropertiesAccess

	SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
	    sm.checkPropertiesAccess();
	}
	searchPath = path;
    
private booleanthrowsException(java.lang.reflect.Method method, java.lang.Class exception)
Return true iff the given method throws the given exception.

	Class exs[] = method.getExceptionTypes();
	for (int i = 0; i < exs.length; i++) {
	    if (exs[i] == exception) {
		return true;
	    }
	}
	return false;