34
34
import org .springframework .beans .SimpleTypeConverter ;
35
35
import org .springframework .beans .TypeMismatchException ;
36
36
import org .springframework .core .CollectionFactory ;
37
- import org .springframework .core .MethodParameter ;
38
37
import org .springframework .core .ResolvableType ;
39
38
import org .springframework .core .convert .ConversionService ;
40
39
import org .springframework .core .convert .TypeDescriptor ;
@@ -113,7 +112,7 @@ public void addDataBinderInitializer(Consumer<DataBinder> dataBinderInitializer)
113
112
* Bind a single argument, or the full arguments map, onto an object of the
114
113
* given target type.
115
114
* @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
117
116
* use the full arguments map
118
117
* @param targetType the type of Object to create
119
118
* @return the created Object, possibly {@code null}
@@ -124,82 +123,78 @@ public void addDataBinderInitializer(Consumer<DataBinder> dataBinderInitializer)
124
123
* is the argument path where the issue occurred.
125
124
*/
126
125
@ Nullable
127
- @ SuppressWarnings ("unchecked" )
128
126
public Object bind (
129
- DataFetchingEnvironment environment , @ Nullable String argumentName , ResolvableType targetType )
127
+ DataFetchingEnvironment environment , @ Nullable String name , ResolvableType targetType )
130
128
throws BindException {
131
129
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 ());
141
132
142
- DataBinder binder = new DataBinder (null , argumentName != null ? argumentName : "arguments " );
133
+ DataBinder binder = new DataBinder (null , name != null ? ( "Arguments[" + name + "]" ) : "Arguments " );
143
134
initDataBinder (binder );
144
135
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
- }
154
136
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
+ }
166
141
167
- // From Scalar
142
+ Class <?> targetClass = targetType .resolve ();
143
+ Assert .notNull (targetClass , "Could not determine target type from " + targetType );
168
144
169
- if (targetClass .isInstance (rawValue )) {
170
- return wrapAsOptionalIfNecessary (rawValue , targetType );
171
- }
145
+ Object targetValue = bindRawValue (rawValue , targetType , targetClass , bindingResult , segments );
172
146
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 );
178
149
}
150
+
151
+ return targetValue ;
179
152
}
180
153
181
154
private void initDataBinder (DataBinder binder ) {
182
155
binder .setAutoGrowCollectionLimit (DEFAULT_AUTO_GROW_COLLECTION_LIMIT );
183
156
this .dataBinderInitializers .forEach (initializer -> initializer .accept (binder ));
184
157
}
185
158
159
+ @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
186
160
@ 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
+ }
190
171
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 );
195
188
}
196
189
197
- @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
198
- private <T > Collection <T > bindCollection (
190
+ private Collection <?> bindCollection (
199
191
Collection <Object > rawCollection , ResolvableType collectionType ,
200
192
BindingResult bindingResult , Stack <String > segments ) {
201
193
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 )) {
203
198
bindingResult .rejectValue (toArgumentPath (segments ), "typeMismatch" , "Expected collection: " + collectionType );
204
199
return Collections .emptyList ();
205
200
}
@@ -211,133 +206,95 @@ private <T> Collection<T> bindCollection(
211
206
return Collections .emptyList ();
212
207
}
213
208
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 ;
216
213
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 ));
227
216
segments .pop ();
228
217
}
229
218
return collection ;
230
219
}
231
220
232
221
@ Nullable
233
- @ SuppressWarnings ("unchecked" )
234
222
private Object bindMap (
235
223
Map <String , Object > rawMap , ResolvableType targetType , BindingResult bindingResult ,
236
224
Stack <String > segments ) {
237
225
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 ();
266
235
}
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
+ }
267
245
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 );
272
248
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
280
250
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 ));
285
258
286
- return target ;
259
+ if (dataBinder .getBindingResult ().hasErrors ()) {
260
+ copyBindingErrors (dataBinder , bindingResult , segments );
261
+ return null ;
287
262
}
288
263
289
- // Data class constructor
264
+ return target ;
265
+ }
290
266
291
- if (!segments .isEmpty ()) {
292
- segments .push ("." );
293
- }
267
+ // Data class constructor
294
268
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
+ }
325
272
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 ];
329
276
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 ();
338
287
}
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 ;
341
298
}
342
299
}
343
300
@@ -409,18 +366,12 @@ private Object convertValue(
409
366
return null ;
410
367
}
411
368
412
- private static void addDataBinderErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
369
+ private static void copyBindingErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
413
370
String path = (!segments .isEmpty () ? toArgumentPath (segments ) + "." : "" );
414
371
binder .getBindingResult ().getFieldErrors ().forEach (error -> bindingResult .addError (
415
372
new FieldError (bindingResult .getObjectName (), path + error .getField (),
416
373
error .getRejectedValue (), error .isBindingFailure (), error .getCodes (),
417
374
error .getArguments (), error .getDefaultMessage ())));
418
375
}
419
376
420
- private void checkBindingResult (BindingResult bindingResult ) throws BindException {
421
- if (bindingResult .hasErrors ()) {
422
- throw new BindException (bindingResult );
423
- }
424
- }
425
-
426
377
}
0 commit comments