@@ -149,7 +149,7 @@ public Object bind(
149149
150150 if (isApproximableCollectionType (rawValue )) {
151151 segments .push (argumentName );
152- return createCollection ((Collection <Object >) rawValue , targetType , bindingResult , segments );
152+ return bindCollection ((Collection <Object >) rawValue , targetType , bindingResult , segments );
153153 }
154154
155155 if (targetClass == Optional .class ) {
@@ -160,7 +160,7 @@ public Object bind(
160160 // From Map
161161
162162 if (rawValue instanceof Map ) {
163- Object target = createValue ((Map <String , Object >) rawValue , targetType , bindingResult , segments );
163+ Object target = bindMap ((Map <String , Object >) rawValue , targetType , bindingResult , segments );
164164 return wrapAsOptionalIfNecessary (target , targetType );
165165 }
166166
@@ -195,7 +195,7 @@ private boolean isApproximableCollectionType(@Nullable Object rawValue) {
195195 }
196196
197197 @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
198- private <T > Collection <T > createCollection (
198+ private <T > Collection <T > bindCollection (
199199 Collection <Object > rawCollection , ResolvableType collectionType ,
200200 BindingResult bindingResult , Stack <String > segments ) {
201201
@@ -219,7 +219,7 @@ private <T> Collection<T> createCollection(
219219 collection .add ((T ) rawValue );
220220 }
221221 else if (rawValue instanceof Map ) {
222- collection .add ((T ) createValueOrNull ((Map <String , Object >) rawValue , elementType , bindingResult , segments ));
222+ collection .add ((T ) bindMap ((Map <String , Object >) rawValue , elementType , bindingResult , segments ));
223223 }
224224 else {
225225 collection .add ((T ) convertValue (rawValue , elementClass , bindingResult , segments ));
@@ -230,134 +230,128 @@ else if (rawValue instanceof Map) {
230230 }
231231
232232 @ Nullable
233- private Object createValueOrNull (
234- Map <String , Object > rawMap , ResolvableType targetType , BindingResult result , Stack <String > segments ) {
235-
236- try {
237- return createValue (rawMap , targetType , result , segments );
238- }
239- catch (BindException ex ) {
240- return null ;
241- }
242- }
243-
244233 @ SuppressWarnings ("unchecked" )
245- private Object createValue (
234+ private Object bindMap (
246235 Map <String , Object > rawMap , ResolvableType targetType , BindingResult bindingResult ,
247- Stack <String > segments ) throws BindException {
236+ Stack <String > segments ) {
248237
249- Class <?> targetClass = targetType .resolve ();
250- Assert .notNull (targetClass , "Unknown target class" );
251-
252- if (Map .class .isAssignableFrom (targetClass )) {
253- ResolvableType valueType = targetType .asMap ().getGeneric (1 );
254- Class <?> valueClass = valueType .resolve ();
255- if (valueClass == null ) {
256- bindingResult .rejectValue (toArgumentPath (segments ), "unknownMapValueType" , "Unknown Map value type" );
257- return Collections .emptyMap ();
258- }
259- Map <String , Object > map = CollectionFactory .createMap (targetClass , rawMap .size ());
260- for (Map .Entry <String , Object > entry : rawMap .entrySet ()) {
261- Object rawValue = entry .getValue ();
262- segments .push ("[" + entry .getKey () + "]" );
263- if (rawValue == null || valueType .isAssignableFrom (rawValue .getClass ())) {
264- map .put (entry .getKey (), entry .getValue ());
265- }
266- else if (rawValue instanceof Map ) {
267- map .put (entry .getKey (), createValueOrNull (
268- (Map <String , Object >) rawValue , valueType , bindingResult , segments ));
238+ try {
239+ Class <?> targetClass = targetType .resolve ();
240+ Assert .notNull (targetClass , "Unknown target class" );
241+
242+ if (Map .class .isAssignableFrom (targetClass )) {
243+ ResolvableType valueType = targetType .asMap ().getGeneric (1 );
244+ Class <?> valueClass = valueType .resolve ();
245+ if (valueClass == null ) {
246+ bindingResult .rejectValue (toArgumentPath (segments ), "unknownMapValueType" , "Unknown Map value type" );
247+ return Collections .emptyMap ();
269248 }
270- else {
271- map .put (entry .getKey (), convertValue (rawValue , valueClass , bindingResult , segments ));
249+ Map <String , Object > map = CollectionFactory .createMap (targetClass , rawMap .size ());
250+ for (Map .Entry <String , Object > entry : rawMap .entrySet ()) {
251+ Object rawValue = entry .getValue ();
252+ segments .push ("[" + entry .getKey () + "]" );
253+ if (rawValue == null || valueType .isAssignableFrom (rawValue .getClass ())) {
254+ map .put (entry .getKey (), entry .getValue ());
255+ }
256+ else if (rawValue instanceof Map ) {
257+ map .put (entry .getKey (), bindMap (
258+ (Map <String , Object >) rawValue , valueType , bindingResult , segments ));
259+ }
260+ else {
261+ map .put (entry .getKey (), convertValue (rawValue , valueClass , bindingResult , segments ));
262+ }
263+ segments .pop ();
272264 }
273- segments . pop () ;
265+ return map ;
274266 }
275- return map ;
276- }
277-
278- Object target ;
279- Constructor <?> ctor = BeanUtils .getResolvableConstructor (targetClass );
280267
281- // Default constructor + data binding via properties
268+ Object target ;
269+ Constructor <?> ctor = BeanUtils .getResolvableConstructor (targetClass );
282270
283- if (ctor .getParameterCount () == 0 ) {
284- target = BeanUtils .instantiateClass (ctor );
285- DataBinder dataBinder = new DataBinder (target );
286- initDataBinder (dataBinder );
287- dataBinder .getBindingResult ().setNestedPath (toArgumentPath (segments ));
288- dataBinder .setConversionService (getConversionService ());
289- dataBinder .bind (initBindValues (rawMap ));
271+ // Default constructor + data binding via properties
290272
291- if (dataBinder .getBindingResult ().hasErrors ()) {
292- addErrors (dataBinder , bindingResult , segments );
293- throw new BindException (bindingResult );
294- }
273+ if (ctor .getParameterCount () == 0 ) {
274+ target = BeanUtils .instantiateClass (ctor );
275+ DataBinder dataBinder = new DataBinder (target );
276+ initDataBinder (dataBinder );
277+ dataBinder .getBindingResult ().setNestedPath (toArgumentPath (segments ));
278+ dataBinder .setConversionService (getConversionService ());
279+ dataBinder .bind (toPropertyValues (rawMap ));
295280
296- return target ;
297- }
281+ if (dataBinder .getBindingResult ().hasErrors ()) {
282+ addDataBinderErrors (dataBinder , bindingResult , segments );
283+ throw new BindException (bindingResult );
284+ }
298285
299- // Data class constructor
286+ return target ;
287+ }
300288
301- if (!segments .isEmpty ()) {
302- segments .push ("." );
303- }
289+ // Data class constructor
304290
305- String [] paramNames = BeanUtils .getParameterNames (ctor );
306- Class <?>[] paramTypes = ctor .getParameterTypes ();
307- Object [] args = new Object [paramTypes .length ];
308-
309- for (int i = 0 ; i < paramNames .length ; i ++) {
310- String paramName = paramNames [i ];
311- Object rawValue = rawMap .get (paramName );
312- segments .push (paramName );
313- MethodParameter methodParam = new MethodParameter (ctor , i );
314- if (rawValue == null && methodParam .isOptional ()) {
315- args [i ] = (paramTypes [i ] == Optional .class ? Optional .empty () : null );
291+ if (!segments .isEmpty ()) {
292+ segments .push ("." );
316293 }
317- else if (paramTypes [i ] == Object .class ) {
318- args [i ] = rawValue ;
294+
295+ String [] paramNames = BeanUtils .getParameterNames (ctor );
296+ Class <?>[] paramTypes = ctor .getParameterTypes ();
297+ Object [] args = new Object [paramTypes .length ];
298+
299+ for (int i = 0 ; i < paramNames .length ; i ++) {
300+ String paramName = paramNames [i ];
301+ Object rawValue = rawMap .get (paramName );
302+ segments .push (paramName );
303+ MethodParameter methodParam = new MethodParameter (ctor , i );
304+ if (rawValue == null && methodParam .isOptional ()) {
305+ args [i ] = (paramTypes [i ] == Optional .class ? Optional .empty () : null );
306+ }
307+ else if (paramTypes [i ] == Object .class ) {
308+ args [i ] = rawValue ;
309+ }
310+ else if (isApproximableCollectionType (rawValue )) {
311+ ResolvableType elementType = ResolvableType .forMethodParameter (methodParam );
312+ args [i ] = bindCollection ((Collection <Object >) rawValue , elementType , bindingResult , segments );
313+ }
314+ else if (rawValue instanceof Map ) {
315+ boolean isOptional = (paramTypes [i ] == Optional .class );
316+ ResolvableType type = ResolvableType .forMethodParameter (methodParam .nestedIfOptional ());
317+ Object value = bindMap ((Map <String , Object >) rawValue , type , bindingResult , segments );
318+ args [i ] = (isOptional ? Optional .ofNullable (value ) : value );
319+ }
320+ else {
321+ args [i ] = convertValue (rawValue , paramTypes [i ], new TypeDescriptor (methodParam ), bindingResult , segments );
322+ }
323+ segments .pop ();
319324 }
320- else if ( isApproximableCollectionType ( rawValue )) {
321- ResolvableType elementType = ResolvableType . forMethodParameter ( methodParam );
322- args [ i ] = createCollection (( Collection < Object >) rawValue , elementType , bindingResult , segments );
325+
326+ if ( segments . size () > 1 ) {
327+ segments . pop ( );
323328 }
324- else if (rawValue instanceof Map ) {
325- boolean isOptional = (paramTypes [i ] == Optional .class );
326- ResolvableType type = ResolvableType .forMethodParameter (methodParam .nestedIfOptional ());
327- Object value = createValueOrNull ((Map <String , Object >) rawValue , type , bindingResult , segments );
328- args [i ] = (isOptional ? Optional .ofNullable (value ) : value );
329+
330+ try {
331+ return BeanUtils .instantiateClass (ctor , args );
329332 }
330- else {
331- args [i ] = convertValue (rawValue , paramTypes [i ], new TypeDescriptor (methodParam ), bindingResult , segments );
333+ catch (BeanInstantiationException ex ) {
334+ // Swallow if we had binding errors, it's as far as we could go
335+ checkBindingResult (bindingResult );
336+ throw ex ;
332337 }
333- segments .pop ();
334- }
335-
336- if (segments .size () > 1 ) {
337- segments .pop ();
338338 }
339-
340- try {
341- return BeanUtils .instantiateClass (ctor , args );
342- }
343- catch (BeanInstantiationException ex ) {
344- // Swallow if we had binding errors, it's as far as we could go
345- checkBindingResult (bindingResult );
346- throw ex ;
339+ catch (BindException ex ) {
340+ return null ;
347341 }
348342 }
349343
350- private MutablePropertyValues initBindValues (Map <String , Object > rawMap ) {
344+ private static MutablePropertyValues toPropertyValues (Map <String , Object > rawMap ) {
351345 MutablePropertyValues mpvs = new MutablePropertyValues ();
352346 Stack <String > segments = new Stack <>();
353347 for (String key : rawMap .keySet ()) {
354- addBindValues (mpvs , key , rawMap .get (key ), segments );
348+ addPropertyValue (mpvs , key , rawMap .get (key ), segments );
355349 }
356350 return mpvs ;
357351 }
358352
359353 @ SuppressWarnings ("unchecked" )
360- private void addBindValues (MutablePropertyValues mpvs , String name , Object value , Stack <String > segments ) {
354+ private static void addPropertyValue (MutablePropertyValues mpvs , String name , Object value , Stack <String > segments ) {
361355 if (value instanceof List ) {
362356 List <Object > items = (List <Object >) value ;
363357 if (items .isEmpty ()) {
@@ -367,15 +361,15 @@ private void addBindValues(MutablePropertyValues mpvs, String name, Object value
367361 }
368362 else {
369363 for (int i = 0 ; i < items .size (); i ++) {
370- addBindValues (mpvs , name + "[" + i + "]" , items .get (i ), segments );
364+ addPropertyValue (mpvs , name + "[" + i + "]" , items .get (i ), segments );
371365 }
372366 }
373367 }
374368 else if (value instanceof Map ) {
375369 segments .push (name + "." );
376370 Map <String , Object > map = (Map <String , Object >) value ;
377371 for (String key : map .keySet ()) {
378- addBindValues (mpvs , key , map .get (key ), segments );
372+ addPropertyValue (mpvs , key , map .get (key ), segments );
379373 }
380374 segments .pop ();
381375 }
@@ -386,7 +380,7 @@ else if (value instanceof Map) {
386380 }
387381 }
388382
389- private String toArgumentPath (Stack <String > path ) {
383+ private static String toArgumentPath (Stack <String > path ) {
390384 StringBuilder sb = new StringBuilder ();
391385 path .forEach (sb ::append );
392386 return sb .toString ();
@@ -415,7 +409,7 @@ private Object convertValue(
415409 return null ;
416410 }
417411
418- private void addErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
412+ private static void addDataBinderErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
419413 String path = (!segments .isEmpty () ? toArgumentPath (segments ) + "." : "" );
420414 binder .getBindingResult ().getFieldErrors ().forEach (error -> bindingResult .addError (
421415 new FieldError (bindingResult .getObjectName (), path + error .getField (),
0 commit comments