@@ -139,160 +139,35 @@ private void modifyProperty(MutablePropertyValues propertyValues, BeanWrapper ta
139
139
}
140
140
}
141
141
142
+ /**
143
+ * Normalize a bean property path to a format understood by a BeanWrapper. This is
144
+ * used so that
145
+ * <ul>
146
+ * <li>Fuzzy matching can be employed for bean property names</li>
147
+ * <li>Period separators can be used instead of indexing ([...]) for map keys</li>
148
+ * </ul>
149
+ *
150
+ * @param wrapper a bean wrapper for the object to bind
151
+ * @param path the bean path to bind
152
+ * @return a transformed path with correct bean wrapper syntax
153
+ */
142
154
protected String normalizePath (BeanWrapper wrapper , String path ) {
143
155
return initializePath (wrapper , new BeanPath (path ), 0 );
144
156
}
145
157
146
- private static class BeanPath {
147
-
148
- private List <PathNode > nodes ;
149
-
150
- public BeanPath (String path ) {
151
- this .nodes = splitPath (path );
152
- }
153
-
154
- public void mapIndex (int index ) {
155
- PathNode node = this .nodes .get (index );
156
- if (node instanceof PropertyNode ) {
157
- node = ((PropertyNode ) node ).mapIndex ();
158
- }
159
- this .nodes .set (index , node );
160
- }
161
-
162
- public String prefix (int index ) {
163
- return range (0 , index );
164
- }
165
-
166
- public void rename (int index , String name ) {
167
- this .nodes .get (index ).name = name ;
168
- }
169
-
170
- public String name (int index ) {
171
- if (index < this .nodes .size ()) {
172
- return this .nodes .get (index ).name ;
173
- }
174
- return null ;
175
- }
176
-
177
- public int length () {
178
- return this .nodes .size ();
179
- }
180
-
181
- private String range (int start , int end ) {
182
- StringBuilder builder = new StringBuilder ();
183
- for (int i = start ; i < end ; i ++) {
184
- PathNode node = this .nodes .get (i );
185
- builder .append (node );
186
- }
187
- if (builder .toString ().startsWith (("." ))) {
188
- builder .replace (0 , 1 , "" );
189
- }
190
- return builder .toString ();
191
- }
192
-
193
- public boolean isArrayIndex (int index ) {
194
- return this .nodes .get (index ) instanceof ArrayIndexNode ;
195
- }
196
-
197
- public boolean isProperty (int index ) {
198
- return this .nodes .get (index ) instanceof PropertyNode ;
199
- }
200
-
201
- @ Override
202
- public String toString () {
203
- return prefix (this .nodes .size ());
204
- }
205
-
206
- private static class PathNode {
207
-
208
- protected String name ;
209
-
210
- public PathNode (String name ) {
211
- this .name = name ;
212
- }
213
-
214
- }
215
-
216
- private static class ArrayIndexNode extends PathNode {
217
-
218
- public ArrayIndexNode (String name ) {
219
- super (name );
220
- }
221
-
222
- @ Override
223
- public String toString () {
224
- return "[" + this .name + "]" ;
225
- }
226
-
227
- }
228
-
229
- private static class MapIndexNode extends PathNode {
230
-
231
- public MapIndexNode (String name ) {
232
- super (name );
233
- }
234
-
235
- @ Override
236
- public String toString () {
237
- return "[" + this .name + "]" ;
238
- }
239
- }
240
-
241
- private static class PropertyNode extends PathNode {
242
-
243
- public PropertyNode (String name ) {
244
- super (name );
245
- }
246
-
247
- public MapIndexNode mapIndex () {
248
- return new MapIndexNode (this .name );
249
- }
250
-
251
- @ Override
252
- public String toString () {
253
- return "." + this .name ;
254
- }
255
- }
256
-
257
- private List <PathNode > splitPath (String path ) {
258
- List <PathNode > nodes = new ArrayList <PathNode >();
259
- for (String name : StringUtils .delimitedListToStringArray (path , "." )) {
260
- for (String sub : StringUtils .delimitedListToStringArray (name , "[" )) {
261
- if (StringUtils .hasText (sub )) {
262
- if (sub .endsWith ("]" )) {
263
- sub = sub .substring (0 , sub .length () - 1 );
264
- if (sub .matches ("[0-9]+" )) {
265
- nodes .add (new ArrayIndexNode (sub ));
266
- }
267
- else {
268
- nodes .add (new MapIndexNode (sub ));
269
- }
270
- }
271
- else {
272
- nodes .add (new PropertyNode (sub ));
273
- }
274
- }
275
- }
276
- }
277
- return nodes ;
278
- }
279
-
280
- }
281
-
282
158
private String initializePath (BeanWrapper wrapper , BeanPath path , int index ) {
159
+
283
160
String prefix = path .prefix (index );
284
161
String key = path .name (index );
285
- if (key == null ) {
286
- return path .toString ();
287
- }
288
162
if (path .isProperty (index )) {
289
163
key = getActualPropertyName (wrapper , prefix , key );
290
164
path .rename (index , key );
291
165
}
292
- if (index >= path .length () - 1 ) {
166
+ if (path .name (++ index ) == null ) {
293
167
return path .toString ();
294
168
}
295
- String name = path .prefix (++index );
169
+
170
+ String name = path .prefix (index );
296
171
TypeDescriptor descriptor = wrapper .getPropertyTypeDescriptor (name );
297
172
if (descriptor == null || descriptor .isMap ()) {
298
173
if (descriptor != null ) {
@@ -302,20 +177,18 @@ private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
302
177
extendMapIfNecessary (wrapper , path , index );
303
178
}
304
179
else if (descriptor .isCollection ()) {
305
- // TODO: test collection extension
306
180
extendCollectionIfNecessary (wrapper , path , index );
307
181
}
308
182
else if (descriptor .getType ().equals (Object .class )) {
309
183
path .mapIndex (index );
310
- name = path .prefix (index + 1 );
311
- if (wrapper .getPropertyValue (name ) == null ) {
312
- wrapper .setPropertyValue (name , new LinkedHashMap <String , Object >());
184
+ String next = path .prefix (index + 1 );
185
+ if (wrapper .getPropertyValue (next ) == null ) {
186
+ wrapper .setPropertyValue (next , new LinkedHashMap <String , Object >());
313
187
}
314
188
}
315
- if (index < path .length ()) {
316
- return initializePath (wrapper , path , index );
317
- }
318
- return path .toString ();
189
+
190
+ return initializePath (wrapper , path , index );
191
+
319
192
}
320
193
321
194
private void extendCollectionIfNecessary (BeanWrapper wrapper , BeanPath path , int index ) {
@@ -439,4 +312,136 @@ public Map<String, Object> getMap() {
439
312
}
440
313
}
441
314
315
+ private static class BeanPath {
316
+
317
+ private List <PathNode > nodes ;
318
+
319
+ public BeanPath (String path ) {
320
+ this .nodes = splitPath (path );
321
+ }
322
+
323
+ public void mapIndex (int index ) {
324
+ PathNode node = this .nodes .get (index );
325
+ if (node instanceof PropertyNode ) {
326
+ node = ((PropertyNode ) node ).mapIndex ();
327
+ }
328
+ this .nodes .set (index , node );
329
+ }
330
+
331
+ public String prefix (int index ) {
332
+ return range (0 , index );
333
+ }
334
+
335
+ public void rename (int index , String name ) {
336
+ this .nodes .get (index ).name = name ;
337
+ }
338
+
339
+ public String name (int index ) {
340
+ if (index < this .nodes .size ()) {
341
+ return this .nodes .get (index ).name ;
342
+ }
343
+ return null ;
344
+ }
345
+
346
+ private String range (int start , int end ) {
347
+ StringBuilder builder = new StringBuilder ();
348
+ for (int i = start ; i < end ; i ++) {
349
+ PathNode node = this .nodes .get (i );
350
+ builder .append (node );
351
+ }
352
+ if (builder .toString ().startsWith (("." ))) {
353
+ builder .replace (0 , 1 , "" );
354
+ }
355
+ return builder .toString ();
356
+ }
357
+
358
+ public boolean isArrayIndex (int index ) {
359
+ return this .nodes .get (index ) instanceof ArrayIndexNode ;
360
+ }
361
+
362
+ public boolean isProperty (int index ) {
363
+ return this .nodes .get (index ) instanceof PropertyNode ;
364
+ }
365
+
366
+ @ Override
367
+ public String toString () {
368
+ return prefix (this .nodes .size ());
369
+ }
370
+
371
+ private static class PathNode {
372
+
373
+ protected String name ;
374
+
375
+ public PathNode (String name ) {
376
+ this .name = name ;
377
+ }
378
+
379
+ }
380
+
381
+ private static class ArrayIndexNode extends PathNode {
382
+
383
+ public ArrayIndexNode (String name ) {
384
+ super (name );
385
+ }
386
+
387
+ @ Override
388
+ public String toString () {
389
+ return "[" + this .name + "]" ;
390
+ }
391
+
392
+ }
393
+
394
+ private static class MapIndexNode extends PathNode {
395
+
396
+ public MapIndexNode (String name ) {
397
+ super (name );
398
+ }
399
+
400
+ @ Override
401
+ public String toString () {
402
+ return "[" + this .name + "]" ;
403
+ }
404
+ }
405
+
406
+ private static class PropertyNode extends PathNode {
407
+
408
+ public PropertyNode (String name ) {
409
+ super (name );
410
+ }
411
+
412
+ public MapIndexNode mapIndex () {
413
+ return new MapIndexNode (this .name );
414
+ }
415
+
416
+ @ Override
417
+ public String toString () {
418
+ return "." + this .name ;
419
+ }
420
+ }
421
+
422
+ private List <PathNode > splitPath (String path ) {
423
+ List <PathNode > nodes = new ArrayList <PathNode >();
424
+ for (String name : StringUtils .delimitedListToStringArray (path , "." )) {
425
+ for (String sub : StringUtils .delimitedListToStringArray (name , "[" )) {
426
+ if (StringUtils .hasText (sub )) {
427
+ if (sub .endsWith ("]" )) {
428
+ sub = sub .substring (0 , sub .length () - 1 );
429
+ if (sub .matches ("[0-9]+" )) {
430
+ nodes .add (new ArrayIndexNode (sub ));
431
+ }
432
+ else {
433
+ nodes .add (new MapIndexNode (sub ));
434
+ }
435
+ }
436
+ else {
437
+ nodes .add (new PropertyNode (sub ));
438
+ }
439
+ }
440
+ }
441
+ }
442
+ return nodes ;
443
+ }
444
+
445
+ }
446
+
442
447
}
0 commit comments