You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jdo-commits@db.apache.org by mb...@apache.org on 2005/07/15 12:47:04 UTC
svn commit: r219174 - in
/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java:
Bundle.properties reflection/ReflectionJavaTypeIntrospector.java
Author: mbo
Date: Fri Jul 15 03:46:58 2005
New Revision: 219174
URL: http://svn.apache.org/viewcvs?rev=219174&view=rev
Log:
Add support for protected properties to reflection based JavaModel
Modified:
incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties
incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaTypeIntrospector.java
Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties?rev=219174&r1=219173&r2=219174&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/Bundle.properties Fri Jul 15 03:46:58 2005
@@ -95,3 +95,11 @@
#NOI18N
# {0} - Class
ERR_CannotIntrospectClass=Cannot introspect class ''{0}''.
+#NOI18N
+# {0} - Method name
+# {1} - Property name
+ERR_CannotCreatePropertyDescriptor=Cannot create PropertyDescriptor for property ''{0}'' using method ''{1}''.
+#NOI18N
+# {0} - Method name
+# {1} - Property name
+ERR_CannotSetWriteMethod=Cannot set write method of PropertyDescriptor for property ''{0}''.
Modified: incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaTypeIntrospector.java
URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaTypeIntrospector.java?rev=219174&r1=219173&r2=219174&view=diff
==============================================================================
--- incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaTypeIntrospector.java (original)
+++ incubator/jdo/trunk/core20/src/java/org/apache/jdo/impl/model/java/reflection/ReflectionJavaTypeIntrospector.java Fri Jul 15 03:46:58 2005
@@ -22,6 +22,15 @@
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
import org.apache.jdo.model.ModelFatalException;
import org.apache.jdo.model.java.JavaMethod;
@@ -29,8 +38,8 @@
import org.apache.jdo.util.I18NHelper;
/**
- * Helper clas to intropsect a ReflectionJavaType representing a class for
- * properties.
+ * Helper class to introspect a ReflectionJavaType representing a class to
+ * find its properties.
*
* @author Michael Bouschen
* @since JDO 2.0
@@ -38,45 +47,346 @@
public class ReflectionJavaTypeIntrospector
{
/** I18N support */
- private final static I18NHelper msg =
- I18NHelper.getInstance("org.apache.jdo.impl.model.java.Bundle"); //NOI18N
+ private final static I18NHelper msg = I18NHelper.getInstance(
+ "org.apache.jdo.impl.model.java.Bundle"); //NOI18N
/**
- * Returns a map of properties for the specified class. The key is the
- * property name, value is the JavaProperty instance.
+ * Adds declared properties to the specified ReflectionJavaType instance.
* @param beanClass the class to be introspected
- * @return a map of JavaProperty instances
*/
public void addDeclaredJavaProperties(ReflectionJavaType beanClass)
{
Class clazz = beanClass.getJavaClass();
+ PropertyDescriptor[] descrs =
+ getPublicAndProtectedPropertyDescriptors(clazz);
+ if (descrs != null) {
+ for (int i = 0; i < descrs.length; i++) {
+ PropertyDescriptor descr = descrs[i];
+ if (descr == null) continue;
+ String name = descr.getName();
+ JavaType type =
+ beanClass.getJavaTypeForClass(descr.getPropertyType());
+ Method getter = descr.getReadMethod();
+ JavaMethod javaGetter = (getter == null) ? null :
+ beanClass.createJavaMethod(getter);
+ Method setter = descr.getWriteMethod();
+ JavaMethod javaSetter = (setter == null) ? null :
+ beanClass.createJavaMethod(setter);
+ beanClass.createJavaProperty(name, javaGetter,
+ javaSetter, type);
+ }
+ }
+ }
+
+ // ===== Implementation using java.beans.Introspector =====
+
+ /**
+ * Returns an array of PropertyDescriptor instances representing the
+ * declared public properties of the specified class.
+ * @param clazz the class to be introspected
+ * @return array of PropertyDescriptor instances for declared public
+ * properties.
+ */
+ public PropertyDescriptor[] getPublicPropertyDescriptors(Class clazz) {
try {
BeanInfo beanInfo = Introspector.getBeanInfo(
clazz, clazz.getSuperclass());
- PropertyDescriptor[] descrs =
- beanInfo.getPropertyDescriptors();
- if (descrs != null) {
- for (int i = 0; i < descrs.length; i++) {
- PropertyDescriptor descr = descrs[i];
- if (descr == null) continue;
- String name = descr.getName();
- JavaType type =
- beanClass.getJavaTypeForClass(descr.getPropertyType());
- Method getter = descr.getReadMethod();
- JavaMethod javaGetter = (getter == null) ? null :
- beanClass.createJavaMethod(getter);
- Method setter = descr.getWriteMethod();
- JavaMethod javaSetter = (setter == null) ? null :
- beanClass.createJavaMethod(setter);
- beanClass.createJavaProperty(name, javaGetter,
- javaSetter, type);
- }
- }
+ return beanInfo.getPropertyDescriptors();
}
catch (IntrospectionException ex) {
throw new ModelFatalException(msg.msg(
- "ERR_CannotIntrospectClass", beanClass.getName()), ex); //NOI18N
+ "ERR_CannotIntrospectClass", clazz.getName()), ex); //NOI18N
+ }
+ }
+
+ // ===== Implementation using hand-written Introspector =====
+
+ /**
+ * Returns an array of PropertyDescriptor instances representing the
+ * declared public and protected properties of the specified class.
+ * @param clazz the class to be introspected
+ * @return array of PropertyDescriptor instances for declared public and
+ * protected properties.
+ */
+ public PropertyDescriptor[] getPublicAndProtectedPropertyDescriptors(
+ Class clazz) {
+ return new PropertyStore(clazz).getPropertyDescriptors();
+ }
+
+ /**
+ * Helper class to introspect a class in order to find properties.
+ * The class provides a public method {@link #getPropertyDescriptors()}
+ * returning an array of PropertyDescriptors. Each PropertyDescriptor
+ * represents a public or protected property of the class specified as
+ * constructor argument. This code is inpired by the implementation
+ * of java.beans.Introspector class.
+ * <p>
+ * Class PropertyStore uses the following algorithm to identify the
+ * properties:
+ * <ul>
+ * <li>Iterate the declared non-static methods that are public or
+ * protected.</li>
+ * <li>A no-arg method returning a value and having a name staring with
+ * "get" is a potential getter method of a property.</li>
+ * <li>A no-arg method returning a boolean value and a name starting with
+ * "is" is a potential getter method of a property.</li>
+ * <li>A void method with a single argument and having a name starting
+ * with "set" is a potential setter method of a property.</li>
+ * <li>If there exsists an "is" and a "get" method, the "is" method is
+ * used as the getter method. </li>
+ * <li>If there is a getter method and multiple setter methods, it chooses
+ * the setter where the argument has exactly the same type as the getter
+ * return type.</li>
+ * <li>If there no such matching getter method, none of the setter methods
+ * correspond to a property.</li>
+ * </ul>
+ */
+ static class PropertyStore extends HashMap {
+
+ private static final String GET_PREFIX = "get"; //NOI18N
+ private static final int GET_PREFIX_LENGTH = 3; //NOI18N
+ private static final String SET_PREFIX = "set"; //NOI18N
+ private static final int SET_PREFIX_LENGTH = 3; //NOI18N
+ private static final String IS_PREFIX = "is"; //NOI18N
+ private static final int IS_PREFIX_LENGTH = 2; //NOI18N
+
+ /** The declared method instances for the specified class. */
+ private final Method[] declaredMethods;
+
+ /** Constructor. */
+ public PropertyStore(final Class clazz) {
+ this.declaredMethods = (Method[]) AccessController.doPrivileged(
+ new PrivilegedAction() {
+ public Object run() {
+ return clazz.getDeclaredMethods();
+ }});
+ }
+
+ /**
+ * Returns an array of PropertyDescriptors. Each PropertyDescriptor
+ * represents a public or protected property of the class specified as
+ * constructor argument.
+ * @return array of public and protected properties
+ */
+ public PropertyDescriptor[] getPropertyDescriptors() {
+ // iterate all declared methods
+ for (int i = 0; i < declaredMethods.length; i++) {
+ Method method = declaredMethods[i];
+ int mods = method.getModifiers();
+
+ // we are only interested in non-static methods that are
+ // public or protected.
+ if (Modifier.isStatic(mods) ||
+ (!Modifier.isPublic(mods) && !Modifier.isProtected(mods))) {
+ continue;
+ }
+
+ String name = method.getName();
+ Class paramTypes[] = method.getParameterTypes();
+ Class resultType = method.getReturnType();
+
+ switch (paramTypes.length) {
+ case 0:
+ // no args => possible getter
+ if (name.startsWith(GET_PREFIX) &&
+ resultType != void.class) {
+ String propName = Introspector.decapitalize(
+ name.substring(GET_PREFIX_LENGTH));
+ addGetter(propName, method);
+ }
+ else if (name.startsWith(IS_PREFIX) &&
+ resultType == boolean.class) {
+ String propName = Introspector.decapitalize(
+ name.substring(IS_PREFIX_LENGTH));
+ addGetter(propName, method);
+ }
+ break;
+ case 1:
+ // one arg => possible setter
+ if (name.startsWith(SET_PREFIX) &&
+ resultType == void.class) {
+ String propName = Introspector.decapitalize(
+ name.substring(GET_PREFIX_LENGTH));
+ addSetter(propName, method);
+ }
+ break;
+ }
+ }
+
+ // Now merge getters and setters
+ List properties = processProperties();
+ return (PropertyDescriptor[]) properties.toArray(
+ new PropertyDescriptor[properties.size()]);
+ }
+
+ /**
+ * Adds a getter method to the methods list for the property with the
+ * specified name.
+ * @param propName the name of the property.
+ * @param method the getter method.
+ */
+ private void addGetter(String propName, Method method) {
+ try {
+ addPropertyDescriptor(
+ propName, new PropertyDescriptor(propName, method, null));
+ }
+ catch (IntrospectionException ex) {
+ throw new ModelFatalException(
+ msg.msg("ERR_CannotCreatePropertyDescriptor", //NOI18N
+ propName, method.getName()), ex);
+ }
+ }
+
+ /**
+ * Adds a setter method to the methods list for the property with the
+ * specified name.
+ * @param propName the name of the property.
+ * @param method the setter method.
+ */
+ private void addSetter(String propName, Method method) {
+ try {
+ addPropertyDescriptor(
+ propName, new PropertyDescriptor(propName, null, method));
+ }
+ catch (IntrospectionException ex) {
+ throw new ModelFatalException(
+ msg.msg("ERR_CannotCreatePropertyDescriptor", //NOI18N
+ propName, method.getName()), ex);
+ }
+ }
+
+ /**
+ * Adds a the specified (incomplete) PropertyDescriptor to the list of
+ * PropertyDescriptor candidates managed by this PropertyStore. The
+ * method initializes the list of PropertyDescriptors, in case it is
+ * the first PropertyDescriptor for the property with the specified
+ * name.
+ * @param propName the name of the property.
+ * @param pd new PropertyDescriptor.
+ */
+ private synchronized void addPropertyDescriptor(
+ String propName, PropertyDescriptor pd) {
+ if (pd == null) {
+ // nothing to be added
+ return;
+ }
+
+ List list = (List) get(propName);
+ if (list == null) {
+ list = new ArrayList();
+ put(propName, list);
+ }
+ list.add(pd);
+ }
+
+ /**
+ * The method returns a list of PropertyDescriptors for the properties
+ * managed by this PropertyStore. It iterates the all properties
+ * and analyzes the candidate PropertyDescriptors (by calling method
+ * {@link #processProperty(List)}.
+ * @return list of PropertyDescriptors
+ */
+ private synchronized List processProperties() {
+ List result = new ArrayList();
+ for (Iterator i = values().iterator(); i.hasNext();) {
+ PropertyDescriptor pd = processProperty((List) i.next());
+ if (pd != null) {
+ result.add(pd);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * The method analyzes the specified list of candidate
+ * PropertyDescriptors and returns a single PropertyDescriptor
+ * describing the property. It iterates the candidate list in order to
+ * find a getter PropertyDescriptor. If there is such a
+ * PropertyDescriptor it looks for a corresponding setter
+ * PropertyDescriptor and updates the getter PropertyDescriptor with
+ * the write method of the setter. It then returns the getter
+ * PropertyDescriptor. If there is no getter PropertyDescriptor and a
+ * single setter PropertyDescriptor it returns the setter
+ * PropertyDescriptor. Otherwise it returns <code>null</code> which
+ * means the list of candidate PropertyDescriptors does not qualify
+ * for a valid property.
+ * @param candidates the list of candidate PropertyDescriptors
+ * @return a PropertyDescriptor describing a property or
+ * <code>null</code> if the candidate PropertyDescriptors do not
+ * qualify for a valid property.
+ */
+ private PropertyDescriptor processProperty(List candidates) {
+ if (candidates == null)
+ return null;
+
+ PropertyDescriptor getter = null;
+ PropertyDescriptor setter = null;
+
+ // First iteration: check getter methods
+ for (Iterator i = candidates.iterator(); i.hasNext();) {
+ PropertyDescriptor pd = (PropertyDescriptor) i.next();
+ if (pd.getReadMethod() != null) {
+ if (getter != null) {
+ // Found getter, but do not overwrite "is" getter
+ // stored before
+ String name = getter.getReadMethod().getName();
+ if (!name.startsWith(IS_PREFIX)) {
+ getter = pd;
+ }
+ }
+ else {
+ // Store getter
+ getter = pd;
+ }
+ }
+ }
+
+ // Second iteration: check setter methods. This cannot be combined
+ // with the first iteration, because I need the property type of
+ // the getter to find the corresponding setter.
+ for (Iterator i = candidates.iterator(); i.hasNext();) {
+ PropertyDescriptor pd = (PropertyDescriptor) i.next();
+ if (pd.getWriteMethod() != null) {
+ if (getter != null) {
+ if (pd.getPropertyType() == getter.getPropertyType()) {
+ // Found setter that corresponds to getter =>
+ // store setter and stop iterating the candidates
+ setter = pd;
+ break;
+ }
+ }
+ else if (setter != null) {
+ // Found multiple setters w/o getter =>
+ // no property, remove stored setter
+ setter = null;
+ break;
+ }
+ else {
+ // Found setter => store it
+ setter = pd;
+ }
+ }
+ }
+
+ // check stored getter and setter
+ if (getter != null) {
+ if (setter != null) {
+ // getter and setter => merge setter into getter and
+ // return getter
+ try {
+ getter.setWriteMethod(setter.getWriteMethod());
+ }
+ catch (IntrospectionException ex) {
+ throw new ModelFatalException(
+ msg.msg("ERR_CannotSetWriteMethod", //NOI18N
+ getter.getName()), ex);
+ }
+ }
+ return getter;
+ }
+ return setter;
}
}
+
}