3434import org .springframework .beans .SimpleTypeConverter ;
3535import org .springframework .beans .TypeMismatchException ;
3636import org .springframework .core .CollectionFactory ;
37- import org .springframework .core .MethodParameter ;
3837import org .springframework .core .ResolvableType ;
3938import org .springframework .core .convert .ConversionService ;
4039import org .springframework .core .convert .TypeDescriptor ;
@@ -113,7 +112,7 @@ public void addDataBinderInitializer(Consumer<DataBinder> dataBinderInitializer)
113112 * Bind a single argument, or the full arguments map, onto an object of the
114113 * given target type.
115114 * @param environment for access to the arguments
116- * @param argumentName the name of the argument to bind, or {@code null} to
115+ * @param name the name of the argument to bind, or {@code null} to
117116 * use the full arguments map
118117 * @param targetType the type of Object to create
119118 * @return the created Object, possibly {@code null}
@@ -124,82 +123,78 @@ public void addDataBinderInitializer(Consumer<DataBinder> dataBinderInitializer)
124123 * is the argument path where the issue occurred.
125124 */
126125 @ Nullable
127- @ SuppressWarnings ("unchecked" )
128126 public Object bind (
129- DataFetchingEnvironment environment , @ Nullable String argumentName , ResolvableType targetType )
127+ DataFetchingEnvironment environment , @ Nullable String name , ResolvableType targetType )
130128 throws BindException {
131129
132- Object rawValue = (argumentName != null ?
133- environment .getArgument (argumentName ) : environment .getArguments ());
134-
135- if (rawValue == null ) {
136- return wrapAsOptionalIfNecessary (null , targetType );
137- }
138-
139- Class <?> targetClass = targetType .resolve ();
140- Assert .notNull (targetClass , "Could not determine target type from " + targetType );
130+ Object rawValue = (name != null ?
131+ environment .getArgument (name ) : environment .getArguments ());
141132
142- DataBinder binder = new DataBinder (null , argumentName != null ? argumentName : "arguments " );
133+ DataBinder binder = new DataBinder (null , name != null ? ( "Arguments[" + name + "]" ) : "Arguments " );
143134 initDataBinder (binder );
144135 BindingResult bindingResult = binder .getBindingResult ();
145- Stack <String > segments = new Stack <>();
146-
147- try {
148- // From Collection
149-
150- if (isApproximableCollectionType (rawValue )) {
151- segments .push (argumentName );
152- return bindCollection ((Collection <Object >) rawValue , targetType , bindingResult , segments );
153- }
154136
155- if (targetClass == Optional .class ) {
156- targetClass = targetType .getNested (2 ).resolve ();
157- Assert .notNull (targetClass , "Could not determine Optional<T> type from " + targetType );
158- }
159-
160- // From Map
161-
162- if (rawValue instanceof Map ) {
163- Object target = bindMap ((Map <String , Object >) rawValue , targetType , bindingResult , segments );
164- return wrapAsOptionalIfNecessary (target , targetType );
165- }
137+ Stack <String > segments = new Stack <>();
138+ if (name != null ) {
139+ segments .push (name );
140+ }
166141
167- // From Scalar
142+ Class <?> targetClass = targetType .resolve ();
143+ Assert .notNull (targetClass , "Could not determine target type from " + targetType );
168144
169- if (targetClass .isInstance (rawValue )) {
170- return wrapAsOptionalIfNecessary (rawValue , targetType );
171- }
145+ Object targetValue = bindRawValue (rawValue , targetType , targetClass , bindingResult , segments );
172146
173- Object target = convertValue (rawValue , targetClass , bindingResult , segments );
174- return wrapAsOptionalIfNecessary (target , targetType );
175- }
176- finally {
177- checkBindingResult (bindingResult );
147+ if (bindingResult .hasErrors ()) {
148+ throw new BindException (bindingResult );
178149 }
150+
151+ return targetValue ;
179152 }
180153
181154 private void initDataBinder (DataBinder binder ) {
182155 binder .setAutoGrowCollectionLimit (DEFAULT_AUTO_GROW_COLLECTION_LIMIT );
183156 this .dataBinderInitializers .forEach (initializer -> initializer .accept (binder ));
184157 }
185158
159+ @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
186160 @ Nullable
187- private Object wrapAsOptionalIfNecessary (@ Nullable Object value , ResolvableType type ) {
188- return (type .resolve (Object .class ).equals (Optional .class ) ? Optional .ofNullable (value ) : value );
189- }
161+ private Object bindRawValue (
162+ Object rawValue , ResolvableType targetValueType , Class <?> targetValueClass ,
163+ BindingResult bindingResult , Stack <String > segments ) {
164+
165+ boolean isOptional = targetValueClass == Optional .class ;
166+
167+ if (isOptional ) {
168+ targetValueType = targetValueType .getNested (2 );
169+ targetValueClass = targetValueType .resolve ();
170+ }
190171
191- private boolean isApproximableCollectionType (@ Nullable Object rawValue ) {
192- return (rawValue != null &&
193- (CollectionFactory .isApproximableCollectionType (rawValue .getClass ()) ||
194- rawValue instanceof List )); // it may be SingletonList
172+ Object targetValue ;
173+ if (rawValue == null || targetValueClass == Object .class ) {
174+ targetValue = rawValue ;
175+ }
176+ else if (rawValue instanceof Collection ) {
177+ targetValue = bindCollection ((Collection <Object >) rawValue , targetValueType , bindingResult , segments );
178+ }
179+ else if (rawValue instanceof Map ) {
180+ targetValue = bindMap ((Map <String , Object >) rawValue , targetValueType , bindingResult , segments );
181+ }
182+ else {
183+ targetValue = (targetValueClass .isAssignableFrom (rawValue .getClass ()) ?
184+ rawValue : convertValue (rawValue , targetValueClass , bindingResult , segments ));
185+ }
186+
187+ return (isOptional ? Optional .ofNullable (targetValue ) : targetValue );
195188 }
196189
197- @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
198- private <T > Collection <T > bindCollection (
190+ private Collection <?> bindCollection (
199191 Collection <Object > rawCollection , ResolvableType collectionType ,
200192 BindingResult bindingResult , Stack <String > segments ) {
201193
202- if (!Collection .class .isAssignableFrom (collectionType .resolve ())) {
194+ Class <?> collectionClass = collectionType .resolve ();
195+ Assert .notNull (collectionClass , "Unknown Collection class" );
196+
197+ if (!Collection .class .isAssignableFrom (collectionClass )) {
203198 bindingResult .rejectValue (toArgumentPath (segments ), "typeMismatch" , "Expected collection: " + collectionType );
204199 return Collections .emptyList ();
205200 }
@@ -211,133 +206,95 @@ private <T> Collection<T> bindCollection(
211206 return Collections .emptyList ();
212207 }
213208
214- Collection <T > collection = CollectionFactory .createCollection (collectionType .getRawClass (), elementClass , rawCollection .size ());
215- int i = 0 ;
209+ Collection <Object > collection =
210+ CollectionFactory .createCollection (collectionClass , elementClass , rawCollection .size ());
211+
212+ int index = 0 ;
216213 for (Object rawValue : rawCollection ) {
217- segments .push ("[" + i ++ + "]" );
218- if (rawValue == null || elementClass .isAssignableFrom (rawValue .getClass ())) {
219- collection .add ((T ) rawValue );
220- }
221- else if (rawValue instanceof Map ) {
222- collection .add ((T ) bindMap ((Map <String , Object >) rawValue , elementType , bindingResult , segments ));
223- }
224- else {
225- collection .add ((T ) convertValue (rawValue , elementClass , bindingResult , segments ));
226- }
214+ segments .push ("[" + index ++ + "]" );
215+ collection .add (bindRawValue (rawValue , elementType , elementClass , bindingResult , segments ));
227216 segments .pop ();
228217 }
229218 return collection ;
230219 }
231220
232221 @ Nullable
233- @ SuppressWarnings ("unchecked" )
234222 private Object bindMap (
235223 Map <String , Object > rawMap , ResolvableType targetType , BindingResult bindingResult ,
236224 Stack <String > segments ) {
237225
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 ();
248- }
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 ();
264- }
265- return map ;
226+ Class <?> targetClass = targetType .resolve ();
227+ Assert .notNull (targetClass , "Unknown target class" );
228+
229+ if (Map .class .isAssignableFrom (targetClass )) {
230+ ResolvableType valueType = targetType .asMap ().getGeneric (1 );
231+ Class <?> valueClass = valueType .resolve ();
232+ if (valueClass == null ) {
233+ bindingResult .rejectValue (toArgumentPath (segments ), "unknownMapValueType" , "Unknown Map value type" );
234+ return Collections .emptyMap ();
266235 }
236+ Map <String , Object > map = CollectionFactory .createMap (targetClass , rawMap .size ());
237+ for (Map .Entry <String , Object > entry : rawMap .entrySet ()) {
238+ String key = entry .getKey ();
239+ segments .push ("[" + key + "]" );
240+ map .put (key , bindRawValue (entry .getValue (), valueType , valueClass , bindingResult , segments ));
241+ segments .pop ();
242+ }
243+ return map ;
244+ }
267245
268- Object target ;
269- Constructor <?> ctor = BeanUtils .getResolvableConstructor (targetClass );
270-
271- // Default constructor + data binding via properties
246+ Object target ;
247+ Constructor <?> constructor = BeanUtils .getResolvableConstructor (targetClass );
272248
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 ));
249+ // Default constructor + data binding via properties
280250
281- if (dataBinder .getBindingResult ().hasErrors ()) {
282- addDataBinderErrors (dataBinder , bindingResult , segments );
283- throw new BindException (bindingResult );
284- }
251+ if (constructor .getParameterCount () == 0 ) {
252+ target = BeanUtils .instantiateClass (constructor );
253+ DataBinder dataBinder = new DataBinder (target );
254+ initDataBinder (dataBinder );
255+ dataBinder .getBindingResult ().setNestedPath (toArgumentPath (segments ));
256+ dataBinder .setConversionService (getConversionService ());
257+ dataBinder .bind (toPropertyValues (rawMap ));
285258
286- return target ;
259+ if (dataBinder .getBindingResult ().hasErrors ()) {
260+ copyBindingErrors (dataBinder , bindingResult , segments );
261+ return null ;
287262 }
288263
289- // Data class constructor
264+ return target ;
265+ }
290266
291- if (!segments .isEmpty ()) {
292- segments .push ("." );
293- }
267+ // Data class constructor
294268
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 ();
324- }
269+ if (!segments .isEmpty ()) {
270+ segments .push ("." );
271+ }
325272
326- if ( segments . size () > 1 ) {
327- segments . pop ();
328- }
273+ String [] paramNames = BeanUtils . getParameterNames ( constructor );
274+ Class <?>[] paramTypes = constructor . getParameterTypes ();
275+ Object [] args = new Object [ paramTypes . length ];
329276
330- try {
331- return BeanUtils .instantiateClass (ctor , args );
332- }
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 ;
337- }
277+ for (int i = 0 ; i < paramNames .length ; i ++) {
278+ String name = paramNames [i ];
279+ segments .push (name );
280+ ResolvableType paramType = ResolvableType .forConstructorParameter (constructor , i );
281+ args [i ] = bindRawValue (rawMap .get (name ), paramType , paramTypes [i ], bindingResult , segments );
282+ segments .pop ();
283+ }
284+
285+ if (segments .size () > 1 ) {
286+ segments .pop ();
338287 }
339- catch (BindException ex ) {
340- return null ;
288+
289+ try {
290+ return BeanUtils .instantiateClass (constructor , args );
291+ }
292+ catch (BeanInstantiationException ex ) {
293+ // Ignore if we had binding errors already
294+ if (bindingResult .hasErrors ()) {
295+ return null ;
296+ }
297+ throw ex ;
341298 }
342299 }
343300
@@ -409,18 +366,12 @@ private Object convertValue(
409366 return null ;
410367 }
411368
412- private static void addDataBinderErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
369+ private static void copyBindingErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
413370 String path = (!segments .isEmpty () ? toArgumentPath (segments ) + "." : "" );
414371 binder .getBindingResult ().getFieldErrors ().forEach (error -> bindingResult .addError (
415372 new FieldError (bindingResult .getObjectName (), path + error .getField (),
416373 error .getRejectedValue (), error .isBindingFailure (), error .getCodes (),
417374 error .getArguments (), error .getDefaultMessage ())));
418375 }
419376
420- private void checkBindingResult (BindingResult bindingResult ) throws BindException {
421- if (bindingResult .hasErrors ()) {
422- throw new BindException (bindingResult );
423- }
424- }
425-
426377}
0 commit comments