1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.christianschenk.beanintrospect;
20
21 import java.beans.BeanInfo;
22 import java.beans.IntrospectionException;
23 import java.beans.Introspector;
24 import java.beans.PropertyDescriptor;
25 import java.lang.reflect.Method;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import org.apache.log4j.Logger;
32
33
34
35
36
37
38
39 public class BeanIntrospector {
40
41 private static final Logger log = Logger.getLogger(BeanIntrospector.class);
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 private final Map<Class<?>, Map<String, List<Method>>> methods;
60
61
62
63
64 private final int GETTER_INDEX = 0;
65
66
67
68
69 private final int SETTER_INDEX = 1;
70
71 private final Map<Class<?>, List<String>> ignore;
72
73 private final Map<String, String> methodNameMapping;
74
75 public BeanIntrospector() {
76 this.methods = new HashMap<Class<?>, Map<String, List<Method>>>();
77 this.ignore = new HashMap<Class<?>, List<String>>();
78 this.methodNameMapping = new HashMap<String, String>();
79 }
80
81
82
83
84 public void fill(final Object bean, final Object prototype) {
85 try {
86
87 final Class<?> beanClass = bean.getClass();
88 final Class<?> prototypeClass = prototype.getClass();
89
90 this.registerBean(beanClass);
91 this.registerBean(prototypeClass);
92
93 for (final String methodName : this.methods.get(beanClass).keySet()) {
94 log.debug("Setting method: " + methodName);
95 if (this.ignore.containsKey(beanClass) && this.ignore.get(beanClass).contains(methodName)) {
96 log.debug("Ignoring: " + methodName);
97 continue;
98 }
99
100
101 final List<Method> prototypeGetSetMethods = this.getGetSetMethodsForClass(prototypeClass, beanClass, methodName);
102 final List<Method> beanGetSetMethods = this.getGetSetMethodsForClass(beanClass, null, methodName);
103 final Method prototypeGetter = prototypeGetSetMethods.get(this.GETTER_INDEX);
104 final Method beanSetter = beanGetSetMethods.get(this.SETTER_INDEX);
105
106 beanSetter.invoke(bean, new Object[] { prototypeGetter.invoke(prototype, (Object[]) null) });
107 }
108 } catch (final Exception ex) {
109 throw new RuntimeException("Could not introspect object of class '" + bean.getClass().getName() + "'", ex);
110 }
111 }
112
113
114
115
116
117
118 private List<Method> getGetSetMethodsForClass(final Class<?> prototypeClass, final Class<?> beanClass, final String methodName) {
119 List<Method> prototypeGetSetMethods = this.methods.get(prototypeClass).get(methodName);
120
121
122 if (prototypeGetSetMethods == null) {
123 final String beanClassCanonicalNameAndMethodName = beanClass.getCanonicalName() + methodName;
124 if (this.methodNameMapping.containsKey(beanClassCanonicalNameAndMethodName)) {
125 final String compatibleMethodName = this.methodNameMapping.get(beanClassCanonicalNameAndMethodName);
126 log.debug("Using mapped method: " + compatibleMethodName);
127 prototypeGetSetMethods = this.methods.get(prototypeClass).get(compatibleMethodName);
128 } else {
129 throw new RuntimeException("Could not set '" + methodName + "' because class '" + prototypeClass.getCanonicalName() + "' has no compatible property");
130 }
131 }
132 return prototypeGetSetMethods;
133 }
134
135
136
137
138
139 private void registerBean(final Class<?> beanClass) throws IntrospectionException {
140 if (this.methods.containsKey(beanClass)) return;
141
142 final Map<String, List<Method>> beanMethods = new HashMap<String, List<Method>>();
143 final BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
144 for (final PropertyDescriptor propDesc : beanInfo.getPropertyDescriptors()) {
145 final Method getter = propDesc.getWriteMethod();
146 final Method setter = propDesc.getReadMethod();
147
148 if (getter == null || setter == null) continue;
149
150 log.debug("Adding setter/getter for '" + propDesc.getName() + "' from " + beanClass.getCanonicalName());
151 final List<Method> methods = new ArrayList<Method>(2);
152 methods.add(setter);
153 methods.add(getter);
154 beanMethods.put(propDesc.getName(), methods);
155 }
156 this.methods.put(beanClass, beanMethods);
157 }
158
159
160
161
162 public void ignoreMethodName(final Class<?> clazz, final String methodName) {
163 List<String> methods = this.ignore.get(clazz);
164 if (methods == null) methods = new ArrayList<String>();
165 methods.add(methodName.toLowerCase());
166 this.ignore.put(clazz, methods);
167 }
168
169
170
171
172
173 public void addMethodNameMapping(final Class<?> fromClass, final String fromMethod, final Class<?> toClass, final String toMethod) {
174 final String fromSignature = fromClass.getCanonicalName() + fromMethod.toLowerCase();
175 final String toSignature = toClass.getCanonicalName() + toMethod.toLowerCase();
176 this.methodNameMapping.put(fromSignature, toMethod.toLowerCase());
177 this.methodNameMapping.put(toSignature, fromMethod.toLowerCase());
178 }
179 }