1
1
/*
2
- * Copyright 2002-2014 the original author or authors.
2
+ * Copyright 2002-2015 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
17
17
package org .springframework .beans ;
18
18
19
19
import java .beans .PropertyDescriptor ;
20
+ import java .lang .reflect .Field ;
20
21
import java .util .ArrayList ;
21
22
import java .util .Collections ;
22
23
import java .util .List ;
23
24
24
25
import org .springframework .util .ObjectUtils ;
26
+ import org .springframework .util .ReflectionUtils ;
25
27
import org .springframework .util .StringUtils ;
26
28
27
29
/**
31
33
* @author Alef Arendsen
32
34
* @author Arjen Poutsma
33
35
* @author Juergen Hoeller
36
+ * @author Stephane Nicoll
34
37
* @since 2.0
35
38
* @see #forProperty(String, Class)
36
39
*/
37
- final class PropertyMatches {
40
+ abstract class PropertyMatches {
38
41
39
42
//---------------------------------------------------------------------
40
43
// Static section
@@ -60,7 +63,26 @@ public static PropertyMatches forProperty(String propertyName, Class<?> beanClas
60
63
* @param maxDistance the maximum property distance allowed for matches
61
64
*/
62
65
public static PropertyMatches forProperty (String propertyName , Class <?> beanClass , int maxDistance ) {
63
- return new PropertyMatches (propertyName , beanClass , maxDistance );
66
+ return new BeanPropertyMatches (propertyName , beanClass , maxDistance );
67
+ }
68
+
69
+ /**
70
+ * Create PropertyMatches for the given field property.
71
+ * @param propertyName the name of the field to find possible matches for
72
+ * @param beanClass the bean class to search for matches
73
+ */
74
+ public static PropertyMatches forField (String propertyName , Class <?> beanClass ) {
75
+ return forField (propertyName , beanClass , DEFAULT_MAX_DISTANCE );
76
+ }
77
+
78
+ /**
79
+ * Create PropertyMatches for the given field property.
80
+ * @param propertyName the name of the field to find possible matches for
81
+ * @param beanClass the bean class to search for matches
82
+ * @param maxDistance the maximum property distance allowed for matches
83
+ */
84
+ public static PropertyMatches forField (String propertyName , Class <?> beanClass , int maxDistance ) {
85
+ return new FieldPropertyMatches (propertyName , beanClass , maxDistance );
64
86
}
65
87
66
88
@@ -74,13 +96,19 @@ public static PropertyMatches forProperty(String propertyName, Class<?> beanClas
74
96
75
97
76
98
/**
77
- * Create a new PropertyMatches instance for the given property.
99
+ * Create a new PropertyMatches instance for the given property and possible matches .
78
100
*/
79
- private PropertyMatches (String propertyName , Class <?> beanClass , int maxDistance ) {
101
+ private PropertyMatches (String propertyName , String [] possibleMatches ) {
80
102
this .propertyName = propertyName ;
81
- this .possibleMatches = calculateMatches ( BeanUtils . getPropertyDescriptors ( beanClass ), maxDistance ) ;
103
+ this .possibleMatches = possibleMatches ;
82
104
}
83
105
106
+ /**
107
+ * Return the name of the requested property.
108
+ */
109
+ public String getPropertyName () {
110
+ return propertyName ;
111
+ }
84
112
85
113
/**
86
114
* Return the calculated possible matches.
@@ -93,54 +121,7 @@ public String[] getPossibleMatches() {
93
121
* Build an error message for the given invalid property name,
94
122
* indicating the possible property matches.
95
123
*/
96
- public String buildErrorMessage () {
97
- StringBuilder msg = new StringBuilder ();
98
- msg .append ("Bean property '" );
99
- msg .append (this .propertyName );
100
- msg .append ("' is not writable or has an invalid setter method. " );
101
-
102
- if (ObjectUtils .isEmpty (this .possibleMatches )) {
103
- msg .append ("Does the parameter type of the setter match the return type of the getter?" );
104
- }
105
- else {
106
- msg .append ("Did you mean " );
107
- for (int i = 0 ; i < this .possibleMatches .length ; i ++) {
108
- msg .append ('\'' );
109
- msg .append (this .possibleMatches [i ]);
110
- if (i < this .possibleMatches .length - 2 ) {
111
- msg .append ("', " );
112
- }
113
- else if (i == this .possibleMatches .length - 2 ){
114
- msg .append ("', or " );
115
- }
116
- }
117
- msg .append ("'?" );
118
- }
119
- return msg .toString ();
120
- }
121
-
122
-
123
- /**
124
- * Generate possible property alternatives for the given property and
125
- * class. Internally uses the {@code getStringDistance} method, which
126
- * in turn uses the Levenshtein algorithm to determine the distance between
127
- * two Strings.
128
- * @param propertyDescriptors the JavaBeans property descriptors to search
129
- * @param maxDistance the maximum distance to accept
130
- */
131
- private String [] calculateMatches (PropertyDescriptor [] propertyDescriptors , int maxDistance ) {
132
- List <String > candidates = new ArrayList <String >();
133
- for (PropertyDescriptor pd : propertyDescriptors ) {
134
- if (pd .getWriteMethod () != null ) {
135
- String possibleAlternative = pd .getName ();
136
- if (calculateStringDistance (this .propertyName , possibleAlternative ) <= maxDistance ) {
137
- candidates .add (possibleAlternative );
138
- }
139
- }
140
- }
141
- Collections .sort (candidates );
142
- return StringUtils .toStringArray (candidates );
143
- }
124
+ public abstract String buildErrorMessage ();
144
125
145
126
/**
146
127
* Calculate the distance between the given two Strings
@@ -149,7 +130,7 @@ private String[] calculateMatches(PropertyDescriptor[] propertyDescriptors, int
149
130
* @param s2 the second String
150
131
* @return the distance value
151
132
*/
152
- private int calculateStringDistance (String s1 , String s2 ) {
133
+ private static int calculateStringDistance (String s1 , String s2 ) {
153
134
if (s1 .length () == 0 ) {
154
135
return s2 .length ();
155
136
}
@@ -184,4 +165,115 @@ private int calculateStringDistance(String s1, String s2) {
184
165
return d [s1 .length ()][s2 .length ()];
185
166
}
186
167
168
+ private static class BeanPropertyMatches extends PropertyMatches {
169
+
170
+ private BeanPropertyMatches (String propertyName , Class <?> beanClass , int maxDistance ) {
171
+ super (propertyName , calculateMatches (propertyName ,
172
+ BeanUtils .getPropertyDescriptors (beanClass ), maxDistance ));
173
+ }
174
+
175
+ /**
176
+ * Generate possible property alternatives for the given property and
177
+ * class. Internally uses the {@code getStringDistance} method, which
178
+ * in turn uses the Levenshtein algorithm to determine the distance between
179
+ * two Strings.
180
+ * @param propertyDescriptors the JavaBeans property descriptors to search
181
+ * @param maxDistance the maximum distance to accept
182
+ */
183
+ private static String [] calculateMatches (String propertyName , PropertyDescriptor [] propertyDescriptors , int maxDistance ) {
184
+ List <String > candidates = new ArrayList <String >();
185
+ for (PropertyDescriptor pd : propertyDescriptors ) {
186
+ if (pd .getWriteMethod () != null ) {
187
+ String possibleAlternative = pd .getName ();
188
+ if (calculateStringDistance (propertyName , possibleAlternative ) <= maxDistance ) {
189
+ candidates .add (possibleAlternative );
190
+ }
191
+ }
192
+ }
193
+ Collections .sort (candidates );
194
+ return StringUtils .toStringArray (candidates );
195
+ }
196
+
197
+
198
+ @ Override
199
+ public String buildErrorMessage () {
200
+ String propertyName = getPropertyName ();
201
+ String [] possibleMatches = getPossibleMatches ();
202
+ StringBuilder msg = new StringBuilder ();
203
+ msg .append ("Bean property '" );
204
+ msg .append (propertyName );
205
+ msg .append ("' is not writable or has an invalid setter method. " );
206
+
207
+ if (ObjectUtils .isEmpty (possibleMatches )) {
208
+ msg .append ("Does the parameter type of the setter match the return type of the getter?" );
209
+ }
210
+ else {
211
+ msg .append ("Did you mean " );
212
+ for (int i = 0 ; i < possibleMatches .length ; i ++) {
213
+ msg .append ('\'' );
214
+ msg .append (possibleMatches [i ]);
215
+ if (i < possibleMatches .length - 2 ) {
216
+ msg .append ("', " );
217
+ }
218
+ else if (i == possibleMatches .length - 2 ) {
219
+ msg .append ("', or " );
220
+ }
221
+ }
222
+ msg .append ("'?" );
223
+ }
224
+ return msg .toString ();
225
+ }
226
+
227
+ }
228
+
229
+ private static class FieldPropertyMatches extends PropertyMatches {
230
+
231
+ private FieldPropertyMatches (String propertyName , Class <?> beanClass , int maxDistance ) {
232
+ super (propertyName , calculateMatches (propertyName , beanClass , maxDistance ));
233
+ }
234
+
235
+ private static String [] calculateMatches (final String propertyName , Class <?> beanClass , final int maxDistance ) {
236
+ final List <String > candidates = new ArrayList <String >();
237
+ ReflectionUtils .doWithFields (beanClass , new ReflectionUtils .FieldCallback () {
238
+ @ Override
239
+ public void doWith (Field field ) throws IllegalArgumentException , IllegalAccessException {
240
+ String possibleAlternative = field .getName ();
241
+ if (calculateStringDistance (propertyName , possibleAlternative ) <= maxDistance ) {
242
+ candidates .add (possibleAlternative );
243
+ }
244
+ }
245
+ });
246
+ Collections .sort (candidates );
247
+ return StringUtils .toStringArray (candidates );
248
+ }
249
+
250
+
251
+ @ Override
252
+ public String buildErrorMessage () {
253
+ String propertyName = getPropertyName ();
254
+ String [] possibleMatches = getPossibleMatches ();
255
+ StringBuilder msg = new StringBuilder ();
256
+ msg .append ("Bean property '" );
257
+ msg .append (propertyName );
258
+ msg .append ("' has no matching field. " );
259
+
260
+ if (!ObjectUtils .isEmpty (possibleMatches )) {
261
+ msg .append ("Did you mean " );
262
+ for (int i = 0 ; i < possibleMatches .length ; i ++) {
263
+ msg .append ('\'' );
264
+ msg .append (possibleMatches [i ]);
265
+ if (i < possibleMatches .length - 2 ) {
266
+ msg .append ("', " );
267
+ }
268
+ else if (i == possibleMatches .length - 2 ) {
269
+ msg .append ("', or " );
270
+ }
271
+ }
272
+ msg .append ("'?" );
273
+ }
274
+ return msg .toString ();
275
+ }
276
+
277
+ }
278
+
187
279
}
0 commit comments