17
17
package org .springframework .bootstrap .bind ;
18
18
19
19
import java .net .InetAddress ;
20
+ import java .util .ArrayList ;
20
21
import java .util .LinkedHashMap ;
21
22
import java .util .List ;
22
23
import java .util .Map ;
26
27
import org .springframework .beans .InvalidPropertyException ;
27
28
import org .springframework .beans .MutablePropertyValues ;
28
29
import org .springframework .beans .PropertyValue ;
30
+ import org .springframework .core .convert .TypeDescriptor ;
29
31
import org .springframework .util .StringUtils ;
30
32
import org .springframework .validation .DataBinder ;
31
33
@@ -129,66 +131,228 @@ private MutablePropertyValues getProperyValuesForNamePrefix(
129
131
130
132
private void modifyProperty (MutablePropertyValues propertyValues , BeanWrapper target ,
131
133
PropertyValue propertyValue , int index ) {
134
+ String oldName = propertyValue .getName ();
135
+ String name = normalizePath (target , oldName );
136
+ if (!name .equals (oldName )) {
137
+ propertyValues .setPropertyValueAt (
138
+ new PropertyValue (name , propertyValue .getValue ()), index );
139
+ }
140
+ }
141
+
142
+ protected String normalizePath (BeanWrapper wrapper , String path ) {
143
+ return initializePath (wrapper , new BeanPath (path ), 0 );
144
+ }
145
+
146
+ private static class BeanPath {
132
147
133
- String name = propertyValue .getName ();
134
- StringBuilder builder = new StringBuilder ();
135
- Class <?> type = target .getWrappedClass ();
148
+ private List <PathNode > nodes ;
136
149
137
- for (String key : StringUtils .delimitedListToStringArray (name , "." )) {
138
- if (builder .length () != 0 ) {
139
- builder .append ("." );
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 ();
140
158
}
141
- String oldKey = key ;
142
- key = getActualPropertyName (target , builder .toString (), oldKey );
143
- builder .append (key );
144
- String base = builder .toString ();
145
- if (!oldKey .equals (key )) {
146
- propertyValues .setPropertyValueAt (
147
- new PropertyValue (base , propertyValue .getValue ()), index );
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 ;
148
173
}
149
- type = target .getPropertyType (base );
150
-
151
- // Any nested properties that are maps, are assumed to be simple nested
152
- // maps of maps...
153
- if (type != null && Map .class .isAssignableFrom (type )) {
154
- Map <String , Object > nested = new LinkedHashMap <String , Object >();
155
- if (target .getPropertyValue (base ) != null ) {
156
- @ SuppressWarnings ("unchecked" )
157
- Map <String , Object > existing = (Map <String , Object >) target
158
- .getPropertyValue (base );
159
- nested = existing ;
160
- }
161
- else {
162
- target .setPropertyValue (base , nested );
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
+ }
163
275
}
164
- modifyPopertiesForMap (nested , propertyValues , index , base );
165
- break ;
166
276
}
277
+ return nodes ;
167
278
}
279
+
168
280
}
169
281
170
- private void modifyPopertiesForMap (Map <String , Object > target ,
171
- MutablePropertyValues propertyValues , int index , String base ) {
172
- PropertyValue propertyValue = propertyValues .getPropertyValueList ().get (index );
173
- String name = propertyValue .getName ();
174
- String suffix = name .substring (base .length ());
175
- Map <String , Object > value = new LinkedHashMap <String , Object >();
176
- String [] tree = StringUtils .delimitedListToStringArray (
177
- suffix .startsWith ("." ) ? suffix .substring (1 ) : suffix , "." );
178
- for (int j = 0 ; j < tree .length - 1 ; j ++) {
179
- if (!target .containsKey (tree [j ])) {
180
- target .put (tree [j ], value );
282
+ private String initializePath (BeanWrapper wrapper , BeanPath path , int index ) {
283
+ String prefix = path .prefix (index );
284
+ String key = path .name (index );
285
+ if (key == null ) {
286
+ return path .toString ();
287
+ }
288
+ if (path .isProperty (index )) {
289
+ key = getActualPropertyName (wrapper , prefix , key );
290
+ path .rename (index , key );
291
+ }
292
+ if (index >= path .length () - 1 ) {
293
+ return path .toString ();
294
+ }
295
+ String name = path .prefix (++index );
296
+ TypeDescriptor descriptor = wrapper .getPropertyTypeDescriptor (name );
297
+ if (descriptor == null || descriptor .isMap ()) {
298
+ if (descriptor != null ) {
299
+ wrapper .getPropertyValue (name + "[foo]" );
300
+ }
301
+ path .mapIndex (index );
302
+ extendMapIfNecessary (wrapper , path , index );
303
+ }
304
+ else if (descriptor .isCollection ()) {
305
+ // TODO: test collection extension
306
+ extendCollectionIfNecessary (wrapper , path , index );
307
+ }
308
+ else if (descriptor .getType ().equals (Object .class )) {
309
+ path .mapIndex (index );
310
+ name = path .prefix (index + 1 );
311
+ if (wrapper .getPropertyValue (name ) == null ) {
312
+ wrapper .setPropertyValue (name , new LinkedHashMap <String , Object >());
181
313
}
182
- target = value ;
183
- value = new LinkedHashMap <String , Object >();
184
314
}
185
- String refName = base + suffix .replaceAll ("\\ .([a-zA-Z0-9]*)" , "[$1]" );
186
- propertyValues .setPropertyValueAt (
187
- new PropertyValue (refName , propertyValue .getValue ()), index );
315
+ if (index < path .length ()) {
316
+ return initializePath (wrapper , path , index );
317
+ }
318
+ return path .toString ();
319
+ }
188
320
321
+ private void extendCollectionIfNecessary (BeanWrapper wrapper , BeanPath path , int index ) {
322
+ String name = path .prefix (index );
323
+ TypeDescriptor elementDescriptor = wrapper .getPropertyTypeDescriptor (name )
324
+ .getElementTypeDescriptor ();
325
+ if (!elementDescriptor .isMap () && !elementDescriptor .isCollection ()
326
+ && !elementDescriptor .getType ().equals (Object .class )) {
327
+ return ;
328
+ }
329
+ Object extend = new LinkedHashMap <String , Object >();
330
+ if (!elementDescriptor .isMap () && path .isArrayIndex (index + 1 )) {
331
+ extend = new ArrayList <Object >();
332
+ }
333
+ wrapper .setPropertyValue (path .prefix (index + 1 ), extend );
334
+ }
335
+
336
+ private void extendMapIfNecessary (BeanWrapper wrapper , BeanPath path , int index ) {
337
+ String name = path .prefix (index );
338
+ TypeDescriptor parent = wrapper .getPropertyTypeDescriptor (name );
339
+ if (parent == null ) {
340
+ return ;
341
+ }
342
+ TypeDescriptor descriptor = parent .getMapValueTypeDescriptor ();
343
+ if (!descriptor .isMap () && !descriptor .isCollection ()
344
+ && !descriptor .getType ().equals (Object .class )) {
345
+ return ;
346
+ }
347
+ Object extend = new LinkedHashMap <String , Object >();
348
+ if (descriptor .isCollection ()) {
349
+ extend = new ArrayList <Object >();
350
+ }
351
+ wrapper .setPropertyValue (path .prefix (index + 1 ), extend );
189
352
}
190
353
191
354
private String getActualPropertyName (BeanWrapper target , String prefix , String name ) {
355
+ prefix = StringUtils .hasText (prefix ) ? prefix + "." : "" ;
192
356
for (Variation variation : Variation .values ()) {
193
357
for (Manipulation manipulation : Manipulation .values ()) {
194
358
// Apply all manipulations before attempting variations
@@ -199,7 +363,7 @@ private String getActualPropertyName(BeanWrapper target, String prefix, String n
199
363
}
200
364
}
201
365
catch (InvalidPropertyException ex ) {
202
- // swallow and contrinue
366
+ // swallow and continue
203
367
}
204
368
}
205
369
}
0 commit comments