introspector.java
来自「《移动Agent技术》一书的所有章节源代码。」· Java 代码 · 共 937 行 · 第 1/3 页
JAVA
937 行
/*
* @(#)Introspector.java 1.73 98/07/08
*
* Copyright 1996-1998 by Sun Microsystems, Inc.,
* 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of Sun Microsystems, Inc. ("Confidential Information"). You
* shall not disclose such Confidential Information and shall use
* it only in accordance with the terms of the license agreement
* you entered into with Sun.
*/
package java.beans;
import java.lang.reflect.*;
/**
* The Introspector class provides a standard way for tools to learn about
* the properties, events, and methods supported by a target Java Bean.
* <p>
* 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.
* <p>
* 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.
* <p>
* 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".
* <p>
* 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.
* <p>
* 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).
*/
public class Introspector {
//======================================================================
// Public methods
//======================================================================
/**
* Introspect on a Java bean and learn about all its properties, exposed
* methods, and events.
*
* @param beanClass The bean class to be analyzed.
* @return A BeanInfo object describing the target bean.
* @exception IntrospectionException if an exception occurs during
* introspection.
*/
public static BeanInfo getBeanInfo(Class beanClass) throws IntrospectionException {
BeanInfo bi = (BeanInfo)beanInfoCache.get(beanClass);
if (bi == null) {
bi = (new Introspector(beanClass, null)).getBeanInfo();
beanInfoCache.put(beanClass, bi);
}
return bi;
}
/**
* Introspect on a Java bean and learn all about its properties, exposed
* methods, below a given "stop" point.
*
* @param bean 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.
*/
public static BeanInfo getBeanInfo(Class beanClass, Class stopClass)
throws IntrospectionException {
return (new Introspector(beanClass, stopClass)).getBeanInfo();
}
/**
* 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.
* <p>
* 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.
*/
public static String decapitalize(String name) {
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);
}
/**
* @return The array of package names that will be searched in
* order to find BeanInfo classes.
* <p> This is initially set to {"sun.beans.infos"}.
*/
public static String[] getBeanInfoSearchPath() {
return searchPath;
}
/**
* Change the list of package names that will be used for
* finding BeanInfo classes.
* @param path Array of package names.
*/
public static void setBeanInfoSearchPath(String path[]) {
searchPath = path;
}
//======================================================================
// Private implementation methods
//======================================================================
private Introspector(Class beanClass, Class stopClass)
throws IntrospectionException {
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());
}
}
informant = findInformant(beanClass);
if (beanClass.getSuperclass() != stopClass) {
if (stopClass == null) {
superBeanInfo = Introspector.getBeanInfo(
beanClass.getSuperclass());
} else {
superBeanInfo = Introspector.getBeanInfo(
beanClass.getSuperclass(), stopClass);
}
}
if (informant != null) {
additionalBeanInfo = informant.getAdditionalBeanInfo();
}
if (additionalBeanInfo == null) {
additionalBeanInfo = new BeanInfo[0];
}
}
private BeanInfo getBeanInfo() throws IntrospectionException {
// the evaluation order here is import, as we evaluate the
// event sets and locate PropertyChangeListeners before we
// look for properties.
BeanDescriptor bd = getTargetBeanDescriptor();
EventSetDescriptor esds[] = getTargetEventInfo();
int defaultEvent = getTargetDefaultEventIndex();
PropertyDescriptor pds[] = getTargetPropertyInfo();
int defaultProperty = getTargetDefaultPropertyIndex();
MethodDescriptor mds[] = getTargetMethodInfo();
return new GenericBeanInfo(bd, esds, defaultEvent, pds,
defaultProperty, mds, informant);
}
private BeanInfo findInformant(Class beanClass) {
String name = beanClass.getName() + "BeanInfo";
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
while (name.indexOf('.') > 0) {
name = name.substring(name.indexOf('.')+1);
}
for (int i = 0; i < searchPath.length; i++) {
try {
String fullName = searchPath[i] + "." + name;
return (java.beans.BeanInfo)instantiate(beanClass, fullName);
} catch (Exception ex) {
// Silently ignore any errors.
}
}
return null;
}
/**
* @return An array of PropertyDescriptors describing the editable
* properties supported by the target bean.
*/
private PropertyDescriptor[] getTargetPropertyInfo() throws IntrospectionException {
// Check if the bean has its own BeanInfo that will provide
// explicit information.
PropertyDescriptor[] explicit = null;
if (informant != null) {
explicit = informant.getPropertyDescriptors();
int ix = informant.getDefaultPropertyIndex();
if (ix >= 0 && ix < explicit.length) {
defaultPropertyName = explicit[ix].getName();
}
}
if (explicit == 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++) {
addProperty(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++) {
addProperty(additional[j]);
}
}
}
if (explicit != null) {
// Add the explicit informant data to our results.
for (int i = 0 ; i < explicit.length; i++) {
addProperty(explicit[i]);
}
} else {
// Apply some reflection to the current class.
// First get an array of all the beans methods at this level
Method methodList[] = getDeclaredMethods(beanClass);
// Now analyze each method.
for (int i = 0; i < methodList.length; i++) {
Method method = methodList[i];
// skip static and non-public methods.
int mods = method.getModifiers();
if (Modifier.isStatic(mods) || !Modifier.isPublic(mods)) {
continue;
}
String name = method.getName();
Class argTypes[] = method.getParameterTypes();
Class resultType = method.getReturnType();
int argCount = argTypes.length;
PropertyDescriptor pd = null;
try {
if (argCount == 0) {
if (name.startsWith("get")) {
// Simple getter
pd = new PropertyDescriptor(decapitalize(name.substring(3)),
method, null);
} else if (resultType == boolean.class && name.startsWith("is")) {
// Boolean getter
pd = new PropertyDescriptor(decapitalize(name.substring(2)),
method, null);
}
} else if (argCount == 1) {
if (argTypes[0] == int.class && name.startsWith("get")) {
pd = new IndexedPropertyDescriptor(
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?