@@ -149,7 +149,7 @@ public Object bind(
149149
150150if (isApproximableCollectionType (rawValue )) {
151151segments .push (argumentName );
152- return createCollection ((Collection <Object >) rawValue , targetType , bindingResult , segments );
152+ return bindCollection ((Collection <Object >) rawValue , targetType , bindingResult , segments );
153153}
154154
155155if (targetClass == Optional .class ) {
@@ -160,7 +160,7 @@ public Object bind(
160160// From Map
161161
162162if (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 );
164164return 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 (
199199Collection <Object > rawCollection , ResolvableType collectionType ,
200200BindingResult bindingResult , Stack <String > segments ) {
201201
@@ -219,7 +219,7 @@ private <T> Collection<T> createCollection(
219219collection .add ((T ) rawValue );
220220}
221221else 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}
224224else {
225225collection .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 (
246235Map <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 ) {
351345MutablePropertyValues mpvs = new MutablePropertyValues ();
352346Stack <String > segments = new Stack <>();
353347for (String key : rawMap .keySet ()) {
354- addBindValues (mpvs , key , rawMap .get (key ), segments );
348+ addPropertyValue (mpvs , key , rawMap .get (key ), segments );
355349}
356350return 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 ) {
361355if (value instanceof List ) {
362356List <Object > items = (List <Object >) value ;
363357if (items .isEmpty ()) {
@@ -367,15 +361,15 @@ private void addBindValues(MutablePropertyValues mpvs, String name, Object value
367361}
368362else {
369363for (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}
374368else if (value instanceof Map ) {
375369segments .push (name + "." );
376370Map <String , Object > map = (Map <String , Object >) value ;
377371for (String key : map .keySet ()) {
378- addBindValues (mpvs , key , map .get (key ), segments );
372+ addPropertyValue (mpvs , key , map .get (key ), segments );
379373}
380374segments .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 ) {
390384StringBuilder sb = new StringBuilder ();
391385path .forEach (sb ::append );
392386return sb .toString ();
@@ -415,7 +409,7 @@ private Object convertValue(
415409return 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 ) {
419413String path = (!segments .isEmpty () ? toArgumentPath (segments ) + "." : "" );
420414binder .getBindingResult ().getFieldErrors ().forEach (error -> bindingResult .addError (
421415new FieldError (bindingResult .getObjectName (), path + error .getField (),
0 commit comments