1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.beans;
18
19 import java.beans.IntrospectionException;
20 import java.beans.PropertyDescriptor;
21 import java.lang.reflect.Method;
22 import java.util.HashSet;
23 import java.util.Set;
24
25 import org.apache.commons.logging.LogFactory;
26
27 import org.springframework.core.BridgeMethodResolver;
28 import org.springframework.core.GenericTypeResolver;
29 import org.springframework.core.MethodParameter;
30 import org.springframework.util.ClassUtils;
31 import org.springframework.util.ObjectUtils;
32 import org.springframework.util.StringUtils;
33
34
35
36
37
38
39
40
41
42 final class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
43
44 private final Class<?> beanClass;
45
46 private final Method readMethod;
47
48 private final Method writeMethod;
49
50 private volatile Set<Method> ambiguousWriteMethods;
51
52 private MethodParameter writeMethodParameter;
53
54 private Class<?> propertyType;
55
56 private final Class<?> propertyEditorClass;
57
58
59 public GenericTypeAwarePropertyDescriptor(Class<?> beanClass, String propertyName,
60 Method readMethod, Method writeMethod, Class<?> propertyEditorClass)
61 throws IntrospectionException {
62
63 super(propertyName, null, null);
64
65 if (beanClass == null) {
66 throw new IntrospectionException("Bean class must not be null");
67 }
68 this.beanClass = beanClass;
69
70 Method readMethodToUse = BridgeMethodResolver.findBridgedMethod(readMethod);
71 Method writeMethodToUse = BridgeMethodResolver.findBridgedMethod(writeMethod);
72 if (writeMethodToUse == null && readMethodToUse != null) {
73
74
75
76 Method candidate = ClassUtils.getMethodIfAvailable(
77 this.beanClass, "set" + StringUtils.capitalize(getName()), (Class<?>[]) null);
78 if (candidate != null && candidate.getParameterTypes().length == 1) {
79 writeMethodToUse = candidate;
80 }
81 }
82 this.readMethod = readMethodToUse;
83 this.writeMethod = writeMethodToUse;
84
85 if (this.writeMethod != null) {
86 if (this.readMethod == null) {
87
88
89
90 Set<Method> ambiguousCandidates = new HashSet<Method>();
91 for (Method method : beanClass.getMethods()) {
92 if (method.getName().equals(writeMethodToUse.getName()) &&
93 !method.equals(writeMethodToUse) && !method.isBridge()) {
94 ambiguousCandidates.add(method);
95 }
96 }
97 if (!ambiguousCandidates.isEmpty()) {
98 this.ambiguousWriteMethods = ambiguousCandidates;
99 }
100 }
101 this.writeMethodParameter = new MethodParameter(this.writeMethod, 0);
102 GenericTypeResolver.resolveParameterType(this.writeMethodParameter, this.beanClass);
103 }
104
105 if (this.readMethod != null) {
106 this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
107 }
108 else if (this.writeMethodParameter != null) {
109 this.propertyType = this.writeMethodParameter.getParameterType();
110 }
111
112 this.propertyEditorClass = propertyEditorClass;
113 }
114
115
116 public Class<?> getBeanClass() {
117 return this.beanClass;
118 }
119
120 @Override
121 public Method getReadMethod() {
122 return this.readMethod;
123 }
124
125 @Override
126 public Method getWriteMethod() {
127 return this.writeMethod;
128 }
129
130 public Method getWriteMethodForActualAccess() {
131 Set<Method> ambiguousCandidates = this.ambiguousWriteMethods;
132 if (ambiguousCandidates != null) {
133 this.ambiguousWriteMethods = null;
134 LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).warn("Invalid JavaBean property '" +
135 getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
136 this.writeMethod + "]: " + ambiguousCandidates);
137 }
138 return this.writeMethod;
139 }
140
141 public MethodParameter getWriteMethodParameter() {
142 return this.writeMethodParameter;
143 }
144
145 @Override
146 public Class<?> getPropertyType() {
147 return this.propertyType;
148 }
149
150 @Override
151 public Class<?> getPropertyEditorClass() {
152 return this.propertyEditorClass;
153 }
154
155
156 @Override
157 public boolean equals(Object other) {
158 if (this == other) {
159 return true;
160 }
161 if (!(other instanceof GenericTypeAwarePropertyDescriptor)) {
162 return false;
163 }
164 GenericTypeAwarePropertyDescriptor otherPd = (GenericTypeAwarePropertyDescriptor) other;
165 return (getBeanClass().equals(otherPd.getBeanClass()) && PropertyDescriptorUtils.equals(this, otherPd));
166 }
167
168 @Override
169 public int hashCode() {
170 int hashCode = getBeanClass().hashCode();
171 hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getReadMethod());
172 hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(getWriteMethod());
173 return hashCode;
174 }
175
176 }