001 package org.trails.descriptor;
002
003 import java.beans.*;
004 import java.lang.reflect.InvocationTargetException;
005 import java.util.ArrayList;
006 import java.util.List;
007
008 import org.apache.commons.beanutils.BeanUtils;
009 import org.apache.commons.logging.Log;
010 import org.apache.commons.logging.LogFactory;
011 import org.trails.util.Utils;
012
013 /**
014 * Generate descriptors using reflection on the underlying class.
015 * ReflectionDescriptorFactory.buildClassDescriptor() is the only public method
016 * here.
017 */
018 public class ReflectionDescriptorFactory implements DescriptorFactory
019 {
020 protected static final Log LOG = LogFactory.getLog(ReflectionDescriptorFactory.class);
021
022 private List propertyExcludes = new ArrayList();
023
024 private List methodExcludes = new ArrayList();
025
026 /**
027 * Given a type, build a property descriptor
028 *
029 * @param type The type to build for
030 * @return a completed property descriptor
031 */
032 public IClassDescriptor buildClassDescriptor(Class type)
033 {
034 try
035 {
036 IClassDescriptor descriptor = new TrailsClassDescriptor(type);
037 BeanInfo beanInfo = Introspector.getBeanInfo(type);
038 BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
039 // Note there's various places and ways to uncamelcase the display name. However
040 // we don't want to un-camelcase the possibly customized displayName
041 // Also, because Introspector uses static methods, it's less clean to replace it
042 // with a custom implementation. Proxy doesn't scale well and an aspect would
043 // only work if it's run. So decided to deal with uncamelcasing displayname here
044 beanDescriptor.setDisplayName(Utils.unCamelCase(beanDescriptor.getDisplayName()) );
045 BeanUtils.copyProperties(descriptor, beanInfo.getBeanDescriptor());
046 descriptor.setPropertyDescriptors(buildPropertyDescriptors(type,beanInfo, descriptor));
047 descriptor.setMethodDescriptors(buildMethodDescriptors(type, beanInfo, descriptor));
048 return descriptor;
049
050 } catch (IllegalAccessException e)
051 {
052 LOG.error(e.getMessage());
053 e.printStackTrace();
054 } catch (InvocationTargetException e)
055 {
056 LOG.error(e.getMessage());
057 e.printStackTrace();
058 } catch (Exception e)
059 {
060 LOG.error(e.toString());
061 e.printStackTrace();
062 }
063 return null;
064 }
065
066 /**
067 * Build the set of property descriptors for this type
068 *
069 * @param beanType the aggregating class
070 * @param beanInfo the BeanInfo, already gathered
071 * @param parentClassDescriptor reference to the aggregating class, used for recovery with
072 * graph traversal
073 * @return ObjectReferenceDescriptor if this property is an association,
074 * otherwise a TrailsPropertyDescriptor
075 * @throws Exception
076 */
077 protected ArrayList<IPropertyDescriptor> buildPropertyDescriptors(
078 Class beanType, BeanInfo beanInfo,
079 IClassDescriptor parentClassDescriptor) throws Exception
080 {
081 ArrayList<IPropertyDescriptor> result = new ArrayList<IPropertyDescriptor>();
082 for (PropertyDescriptor beanPropDescriptor : beanInfo.getPropertyDescriptors())
083 {
084 if (!isExcluded(beanPropDescriptor.getName(), getPropertyExcludes()))
085 {
086 beanPropDescriptor.setDisplayName(Utils.unCamelCase(beanPropDescriptor.getDisplayName()) );
087
088 Class<?> propertyType = beanPropDescriptor.getPropertyType();
089 TrailsPropertyDescriptor propDescriptor = new TrailsPropertyDescriptor(beanType,propertyType);
090 BeanUtils.copyProperties(propDescriptor, beanPropDescriptor);
091 result.add(propDescriptor);
092 }
093 }
094 return result;
095 }
096
097 protected ArrayList<IMethodDescriptor> buildMethodDescriptors(Class type, BeanInfo beanInfo,
098 IClassDescriptor parentClassDescriptor)
099 throws Exception
100 {
101 ArrayList<IMethodDescriptor> result = new ArrayList<IMethodDescriptor>();
102 for (MethodDescriptor beanMethodDescriptor : beanInfo.getMethodDescriptors())
103 {
104 if (!isExcluded(beanMethodDescriptor.getMethod().getName(), getMethodExcludes()))
105 {
106 TrailsMethodDescriptor methodDescriptor = new TrailsMethodDescriptor(type,
107 beanMethodDescriptor.getMethod().getName(), beanMethodDescriptor.getMethod().getReturnType(),
108 beanMethodDescriptor.getMethod().getParameterTypes());
109 methodDescriptor.setDisplayName(Utils.unCamelCase(beanMethodDescriptor.getDisplayName()));
110 result.add(methodDescriptor);
111 }
112 }
113 return result;
114 }
115
116 protected boolean isExcluded(String name, List excludes)
117 {
118 for (Object exclude1 : excludes)
119 {
120 String exclude = (String) exclude1;
121
122 if (name.matches(exclude))
123 {
124 return true;
125 }
126 }
127
128 return false;
129 }
130
131 public List getMethodExcludes()
132 {
133 return methodExcludes;
134 }
135
136 public void setMethodExcludes(List methodExcludes)
137 {
138 this.methodExcludes = methodExcludes;
139 }
140
141 public List getPropertyExcludes()
142 {
143 return propertyExcludes;
144 }
145
146 public void setPropertyExcludes(List propertyExcludes)
147 {
148 this.propertyExcludes = propertyExcludes;
149 }
150 }