jaxx.compiler.beans.JAXXIntrospector Maven / Gradle / Ivy
/*
* #%L
* JAXX :: Compiler
*
* $Id: JAXXIntrospector.java 2308 2011-09-23 11:49:08Z tchemit $
* $HeadURL: http://svn.nuiton.org/svn/jaxx/tags/jaxx-2.5.21/jaxx-compiler/src/main/java/jaxx/compiler/beans/JAXXIntrospector.java $
* %%
* Copyright (C) 2008 - 2010 CodeLutin
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
package jaxx.compiler.beans;
import jaxx.compiler.reflect.ClassDescriptor;
import jaxx.compiler.reflect.ClassDescriptorHelper;
import jaxx.compiler.reflect.MethodDescriptor;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyChangeListener;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
/**
* Performs introspection on a ClassDescriptor
. Ideally, I could just have copied Sun's Introspector
* and changed a few things, but the licensing terms are incompatible. This implementation is incomplete -- it only
* bothers to report info that JAXX actually checks. It also relaxes some of Introspector's rules a bit, but I
* don't believe it results in any meaningful incompatibilities.
*
* JAXX uses its own introspector rather than the built-in
* java.beans.Introspector
so that it can introspect {@link ClassDescriptor},
* not just java.lang.Class
.
*/
public class JAXXIntrospector {
private ClassDescriptor classDescriptor;
private Map propertyDescriptors = new HashMap();
private Map eventSetDescriptors = new HashMap();
private JAXXIntrospector(ClassDescriptor classDescriptor) {
this.classDescriptor = classDescriptor;
}
/**
* Returns the JAXXBeanInfo
for a given class.
*
* @param classDescriptor the class to introspect
* @return the JAXXBeanInfo
for the bean class
*/
public static JAXXBeanInfo getJAXXBeanInfo(ClassDescriptor classDescriptor) {
JAXXIntrospector introspector = new JAXXIntrospector(classDescriptor);
return introspector.createBeanInfo();
}
private JAXXBeanInfo createBeanInfo() {
ClassDescriptor explicitInfoClass = classDescriptor;
BeanInfo explicitBeanInfo = null;
while (explicitInfoClass != null) {
explicitBeanInfo = getExplicitBeanInfo(explicitInfoClass);
if (explicitBeanInfo != null) {
break;
}
explicitInfoClass = explicitInfoClass.getSuperclass();
}
if (explicitBeanInfo != null) {
PropertyDescriptor[] explicitProperties = explicitBeanInfo.getPropertyDescriptors();
for (PropertyDescriptor explicitProperty : explicitProperties) {
Class> type = explicitProperty.getPropertyType();
Method readMethod = explicitProperty.getReadMethod();
Method writeMethod = explicitProperty.getWriteMethod();
try {
ClassDescriptor typeDescriptor = ClassDescriptorHelper.getClassDescriptor(type.getName(), type.getClassLoader());
JAXXPropertyDescriptor propertyDescriptor = new JAXXPropertyDescriptor(classDescriptor, explicitProperty.getName(),
readMethod != null ? classDescriptor.getMethodDescriptor(readMethod.getName()) : null,
writeMethod != null ? classDescriptor.getMethodDescriptor(writeMethod.getName(), typeDescriptor) : null);
propertyDescriptor.setBound(explicitProperty.isBound());
Enumeration attributeNames = explicitProperty.attributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
propertyDescriptor.setValue(name, explicitProperty.getValue(name));
}
propertyDescriptors.put(propertyDescriptor.getName(), propertyDescriptor);
} catch (ClassNotFoundException e) {
throw new RuntimeException("Internal error: Could not find ClassDescriptor corresponding to Java " + type, e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Internal error: Could not find expected MethodDescriptor in " + classDescriptor, e);
}
}
}
// if the class broadcasts PropertyChangeEvent, assume all properties are bound (java.beans.Introspector
// does the same)
boolean propertyChangeSource;
try {
classDescriptor.getMethodDescriptor("addPropertyChangeListener", ClassDescriptorHelper.getClassDescriptor(PropertyChangeListener.class));
propertyChangeSource = true;
} catch (NoSuchMethodException e) {
propertyChangeSource = false;
}
MethodDescriptor[] methods = classDescriptor.getMethodDescriptors();
for (MethodDescriptor method : methods) {
String name = method.getName();
if (name.startsWith("get") && name.length() > 3 && Character.isUpperCase(name.charAt(3)) && method.getParameterTypes().length == 0) {
String propertyName = Introspector.decapitalize(name.substring(3));
if (!propertyDescriptors.containsKey(propertyName)) {
propertyDescriptors.put(propertyName, new JAXXPropertyDescriptor(classDescriptor, propertyName, method, null, propertyChangeSource));
}
} else if (name.startsWith("is") && name.length() > 2 && Character.isUpperCase(name.charAt(2)) && method.getParameterTypes().length == 0) {
String propertyName = Introspector.decapitalize(name.substring(2));
if (!propertyDescriptors.containsKey(propertyName)) {
propertyDescriptors.put(propertyName, new JAXXPropertyDescriptor(classDescriptor, propertyName, method, null, propertyChangeSource));
}
} else if (name.startsWith("set") && name.length() > 3 && Character.isUpperCase(name.charAt(3)) && method.getParameterTypes().length == 1) {
String propertyName = Introspector.decapitalize(name.substring(3));
if (!propertyDescriptors.containsKey(propertyName)) {
propertyDescriptors.put(propertyName, new JAXXPropertyDescriptor(classDescriptor, propertyName, null, method, propertyChangeSource));
}
} else if (name.startsWith("add") && name.length() > 3 && Character.isUpperCase(name.charAt(3))) {
ClassDescriptor[] parameters = method.getParameterTypes();
if (parameters.length != 1 || !ClassDescriptorHelper.getClassDescriptor(EventListener.class).isAssignableFrom(parameters[0])) {
continue; // not an event listener method
}
try {
String eventSetName = method.getName().substring(3);
MethodDescriptor remove = classDescriptor.getMethodDescriptor("remove" + eventSetName, parameters);
eventSetDescriptors.put(eventSetName, new JAXXEventSetDescriptor(classDescriptor, eventSetName, method, remove, parameters[0].getMethodDescriptors()));
} catch (NoSuchMethodException e) {
// no matching remove method, not a valid event
}
}
}
JAXXBeanDescriptor beanDescriptor = new JAXXBeanDescriptor(classDescriptor);
if (explicitBeanInfo != null) {
BeanDescriptor explicitBeanDescriptor = explicitBeanInfo.getBeanDescriptor();
if (explicitBeanDescriptor != null) {
Enumeration attributeNames =
explicitBeanDescriptor.attributeNames();
while (attributeNames.hasMoreElements()) {
String name = attributeNames.nextElement();
beanDescriptor.setValue(name, explicitBeanDescriptor.getValue(name));
}
}
}
return new JAXXBeanInfo(beanDescriptor,
propertyDescriptors.values().toArray(new JAXXPropertyDescriptor[propertyDescriptors.size()]),
eventSetDescriptors.values().toArray(new JAXXEventSetDescriptor[eventSetDescriptors.size()]));
}
private static BeanInfo getExplicitBeanInfo(ClassDescriptor classDescriptor) {
try {
Class> beanClass = Class.forName(classDescriptor.getName(), true, classDescriptor.getClassLoader()); // see if there is a class by that name in this package
Method findExplicitBeanInfo = Introspector.class.getDeclaredMethod("findExplicitBeanInfo", new Class[]{Class.class});
findExplicitBeanInfo.setAccessible(true);
return (BeanInfo) findExplicitBeanInfo.invoke(null, beanClass);
} catch (ClassNotFoundException e) {
return null; // happens for uncompiled classes
} catch (NoClassDefFoundError e) {
return null; // wrong case, etc.
} catch (NoSuchMethodException e) {
throw new RuntimeException("Error: could not find method 'findExplicitBeanInfo' in java.beans.Introspector. You are most likely running a version of Java against which JAXX has not been tested.");
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy