All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.compiler.beans.JAXXIntrospector Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%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