@@ -139,160 +139,35 @@ private void modifyProperty(MutablePropertyValues propertyValues, BeanWrapper ta
139139 }
140140 }
141141
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+ */
142154 protected String normalizePath (BeanWrapper wrapper , String path ) {
143155 return initializePath (wrapper , new BeanPath (path ), 0 );
144156 }
145157
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-
282158 private String initializePath (BeanWrapper wrapper , BeanPath path , int index ) {
159+
283160 String prefix = path .prefix (index );
284161 String key = path .name (index );
285- if (key == null ) {
286- return path .toString ();
287- }
288162 if (path .isProperty (index )) {
289163 key = getActualPropertyName (wrapper , prefix , key );
290164 path .rename (index , key );
291165 }
292- if (index >= path .length () - 1 ) {
166+ if (path .name (++ index ) == null ) {
293167 return path .toString ();
294168 }
295- String name = path .prefix (++index );
169+
170+ String name = path .prefix (index );
296171 TypeDescriptor descriptor = wrapper .getPropertyTypeDescriptor (name );
297172 if (descriptor == null || descriptor .isMap ()) {
298173 if (descriptor != null ) {
@@ -302,20 +177,18 @@ private String initializePath(BeanWrapper wrapper, BeanPath path, int index) {
302177 extendMapIfNecessary (wrapper , path , index );
303178 }
304179 else if (descriptor .isCollection ()) {
305- // TODO: test collection extension
306180 extendCollectionIfNecessary (wrapper , path , index );
307181 }
308182 else if (descriptor .getType ().equals (Object .class )) {
309183 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 >());
313187 }
314188 }
315- if (index < path .length ()) {
316- return initializePath (wrapper , path , index );
317- }
318- return path .toString ();
189+
190+ return initializePath (wrapper , path , index );
191+
319192 }
320193
321194 private void extendCollectionIfNecessary (BeanWrapper wrapper , BeanPath path , int index ) {
@@ -439,4 +312,136 @@ public Map<String, Object> getMap() {
439312 }
440313 }
441314
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+
442447}
0 commit comments