1717package org .springframework .bootstrap .bind ;
1818
1919import java .net .InetAddress ;
20+ import java .util .ArrayList ;
2021import java .util .LinkedHashMap ;
2122import java .util .List ;
2223import java .util .Map ;
2627import org .springframework .beans .InvalidPropertyException ;
2728import org .springframework .beans .MutablePropertyValues ;
2829import org .springframework .beans .PropertyValue ;
30+ import org .springframework .core .convert .TypeDescriptor ;
2931import org .springframework .util .StringUtils ;
3032import org .springframework .validation .DataBinder ;
3133
@@ -129,66 +131,228 @@ private MutablePropertyValues getProperyValuesForNamePrefix(
129131
130132 private void modifyProperty (MutablePropertyValues propertyValues , BeanWrapper target ,
131133 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 {
132147
133- String name = propertyValue .getName ();
134- StringBuilder builder = new StringBuilder ();
135- Class <?> type = target .getWrappedClass ();
148+ private List <PathNode > nodes ;
136149
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 ();
140158 }
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 ;
148173 }
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+ }
163275 }
164- modifyPopertiesForMap (nested , propertyValues , index , base );
165- break ;
166276 }
277+ return nodes ;
167278 }
279+
168280 }
169281
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 >());
181313 }
182- target = value ;
183- value = new LinkedHashMap <String , Object >();
184314 }
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+ }
188320
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 );
189352 }
190353
191354 private String getActualPropertyName (BeanWrapper target , String prefix , String name ) {
355+ prefix = StringUtils .hasText (prefix ) ? prefix + "." : "" ;
192356 for (Variation variation : Variation .values ()) {
193357 for (Manipulation manipulation : Manipulation .values ()) {
194358 // Apply all manipulations before attempting variations
@@ -199,7 +363,7 @@ private String getActualPropertyName(BeanWrapper target, String prefix, String n
199363 }
200364 }
201365 catch (InvalidPropertyException ex ) {
202- // swallow and contrinue
366+ // swallow and continue
203367 }
204368 }
205369 }
0 commit comments