38
38
import org .springframework .core .convert .ConversionService ;
39
39
import org .springframework .core .convert .TypeDescriptor ;
40
40
import org .springframework .lang .Nullable ;
41
- import org .springframework .util .Assert ;
42
41
import org .springframework .validation .BindException ;
43
42
import org .springframework .validation .BindingErrorProcessor ;
44
43
import org .springframework .validation .BindingResult ;
@@ -65,6 +64,7 @@ public class GraphQlArgumentBinder {
65
64
*/
66
65
private static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 1024 ;
67
66
67
+
68
68
@ Nullable
69
69
private final SimpleTypeConverter typeConverter ;
70
70
@@ -139,10 +139,8 @@ public Object bind(
139
139
segments .push (name );
140
140
}
141
141
142
- Class <?> targetClass = targetType .resolve ();
143
- Assert .notNull (targetClass , "Could not determine target type from " + targetType );
144
-
145
- Object targetValue = bindRawValue (rawValue , targetType , targetClass , bindingResult , segments );
142
+ Object targetValue = bindRawValue (
143
+ rawValue , targetType , targetType .resolve (Object .class ), bindingResult , segments );
146
144
147
145
if (bindingResult .hasErrors ()) {
148
146
throw new BindException (bindingResult );
@@ -159,51 +157,43 @@ private void initDataBinder(DataBinder binder) {
159
157
@ SuppressWarnings ({"ConstantConditions" , "unchecked" })
160
158
@ Nullable
161
159
private Object bindRawValue (
162
- Object rawValue , ResolvableType targetValueType , Class <?> targetValueClass ,
160
+ Object rawValue , ResolvableType targetType , Class <?> targetClass ,
163
161
BindingResult bindingResult , Stack <String > segments ) {
164
162
165
- boolean isOptional = targetValueClass == Optional .class ;
163
+ boolean isOptional = ( targetClass == Optional .class ) ;
166
164
167
165
if (isOptional ) {
168
- targetValueType = targetValueType .getNested (2 );
169
- targetValueClass = targetValueType .resolve ();
166
+ targetType = targetType .getNested (2 );
167
+ targetClass = targetType .resolve ();
170
168
}
171
169
172
- Object targetValue ;
173
- if (rawValue == null || targetValueClass == Object .class ) {
174
- targetValue = rawValue ;
170
+ Object value ;
171
+ if (rawValue == null || targetClass == Object .class ) {
172
+ value = rawValue ;
175
173
}
176
174
else if (rawValue instanceof Collection ) {
177
- targetValue = bindCollection ((Collection <Object >) rawValue , targetValueType , bindingResult , segments );
175
+ value = bindCollection ((Collection <Object >) rawValue , targetType , targetClass , bindingResult , segments );
178
176
}
179
177
else if (rawValue instanceof Map ) {
180
- targetValue = bindMap ((Map <String , Object >) rawValue , targetValueType , bindingResult , segments );
178
+ value = bindMap ((Map <String , Object >) rawValue , targetType , targetClass , bindingResult , segments );
181
179
}
182
180
else {
183
- targetValue = (targetValueClass .isAssignableFrom (rawValue .getClass ()) ?
184
- rawValue : convertValue (rawValue , targetValueClass , bindingResult , segments ));
181
+ value = (targetClass .isAssignableFrom (rawValue .getClass ()) ?
182
+ rawValue : convertValue (rawValue , targetClass , bindingResult , segments ));
185
183
}
186
184
187
- return (isOptional ? Optional .ofNullable (targetValue ) : targetValue );
185
+ return (isOptional ? Optional .ofNullable (value ) : value );
188
186
}
189
187
190
188
private Collection <?> bindCollection (
191
- Collection <Object > rawCollection , ResolvableType collectionType ,
189
+ Collection <Object > rawCollection , ResolvableType collectionType , Class <?> collectionClass ,
192
190
BindingResult bindingResult , Stack <String > segments ) {
193
191
194
- Class <?> collectionClass = collectionType .resolve ();
195
- Assert .notNull (collectionClass , "Unknown Collection class" );
196
-
197
- if (!Collection .class .isAssignableFrom (collectionClass )) {
198
- bindingResult .rejectValue (toArgumentPath (segments ), "typeMismatch" , "Expected collection: " + collectionType );
199
- return Collections .emptyList ();
200
- }
201
-
202
192
ResolvableType elementType = collectionType .asCollection ().getGeneric (0 );
203
193
Class <?> elementClass = collectionType .asCollection ().getGeneric (0 ).resolve ();
204
194
if (elementClass == null ) {
205
- bindingResult .rejectValue (toArgumentPath (segments ), "unknownElementType " , "Unknown element type" );
206
- return Collections .emptyList ();
195
+ bindingResult .rejectValue (toArgumentPath (segments ), "unknownTargetType " , "Unknown target type" );
196
+ return Collections .emptyList (); // Keep going, report as many errors as we can
207
197
}
208
198
209
199
Collection <Object > collection =
@@ -215,58 +205,80 @@ private Collection<?> bindCollection(
215
205
collection .add (bindRawValue (rawValue , elementType , elementClass , bindingResult , segments ));
216
206
segments .pop ();
217
207
}
208
+
218
209
return collection ;
219
210
}
220
211
212
+ private static String toArgumentPath (Stack <String > path ) {
213
+ StringBuilder sb = new StringBuilder ();
214
+ path .forEach (sb ::append );
215
+ return sb .toString ();
216
+ }
217
+
221
218
@ Nullable
222
219
private Object bindMap (
223
- Map <String , Object > rawMap , ResolvableType targetType , BindingResult bindingResult ,
224
- Stack <String > segments ) {
225
-
226
- Class <?> targetClass = targetType .resolve ();
227
- Assert .notNull (targetClass , "Unknown target class" );
220
+ Map <String , Object > rawMap , ResolvableType targetType , Class <?> targetClass ,
221
+ BindingResult bindingResult , Stack <String > segments ) {
228
222
229
223
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 ();
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 ;
224
+ return bindMapToMap (rawMap , targetType , bindingResult , segments , targetClass );
244
225
}
245
226
246
- Object target ;
247
227
Constructor <?> constructor = BeanUtils .getResolvableConstructor (targetClass );
228
+ if (constructor .getParameterCount () > 0 ) {
229
+ return bindMapToObjectViaConstructor (rawMap , constructor , bindingResult , segments );
230
+ }
231
+
232
+ Object target = BeanUtils .instantiateClass (constructor );
233
+ DataBinder dataBinder = new DataBinder (target );
234
+ initDataBinder (dataBinder );
235
+ dataBinder .getBindingResult ().setNestedPath (toArgumentPath (segments ));
236
+ dataBinder .setConversionService (getConversionService ());
237
+ dataBinder .bind (createPropertyValues (rawMap ));
238
+
239
+ if (dataBinder .getBindingResult ().hasErrors ()) {
240
+ String nestedPath = dataBinder .getBindingResult ().getNestedPath ();
241
+ for (FieldError error : dataBinder .getBindingResult ().getFieldErrors ()) {
242
+ bindingResult .addError (
243
+ new FieldError (bindingResult .getObjectName (), nestedPath + error .getField (),
244
+ error .getRejectedValue (), error .isBindingFailure (), error .getCodes (),
245
+ error .getArguments (), error .getDefaultMessage ()));
246
+ }
247
+ return null ;
248
+ }
248
249
249
- // Default constructor + data binding via properties
250
+ return target ;
251
+ }
250
252
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 ));
253
+ private Map <?, Object > bindMapToMap (
254
+ Map <String , Object > rawMap , ResolvableType targetType , BindingResult bindingResult ,
255
+ Stack <String > segments , Class <?> targetClass ) {
258
256
259
- if (dataBinder .getBindingResult ().hasErrors ()) {
260
- copyBindingErrors (dataBinder , bindingResult , segments );
261
- return null ;
262
- }
257
+ ResolvableType valueType = targetType .asMap ().getGeneric (1 );
258
+ Class <?> valueClass = valueType .resolve ();
259
+ if (valueClass == null ) {
260
+ bindingResult .rejectValue (toArgumentPath (segments ), "unknownTargetType" , "Unknown target type" );
261
+ return Collections .emptyMap (); // Keep going, report as many errors as we can
262
+ }
263
+
264
+ Map <String , Object > map = CollectionFactory .createMap (targetClass , rawMap .size ());
263
265
264
- return target ;
266
+ for (Map .Entry <String , Object > entry : rawMap .entrySet ()) {
267
+ String key = entry .getKey ();
268
+ segments .push ("[" + key + "]" );
269
+ map .put (key , bindRawValue (entry .getValue (), valueType , valueClass , bindingResult , segments ));
270
+ segments .pop ();
265
271
}
266
272
267
- // Data class constructor
273
+ return map ;
274
+ }
275
+
276
+ @ Nullable
277
+ private Object bindMapToObjectViaConstructor (
278
+ Map <String , Object > rawMap , Constructor <?> constructor , BindingResult bindingResult ,
279
+ Stack <String > segments ) {
268
280
269
- if (! segments .isEmpty () ) {
281
+ if (segments .size () > 0 ) {
270
282
segments .push ("." );
271
283
}
272
284
@@ -290,15 +302,15 @@ private Object bindMap(
290
302
return BeanUtils .instantiateClass (constructor , args );
291
303
}
292
304
catch (BeanInstantiationException ex ) {
293
- // Ignore if we had binding errors already
305
+ // Ignore: we had binding errors to begin with
294
306
if (bindingResult .hasErrors ()) {
295
307
return null ;
296
308
}
297
309
throw ex ;
298
310
}
299
311
}
300
312
301
- private static MutablePropertyValues toPropertyValues (Map <String , Object > rawMap ) {
313
+ private static MutablePropertyValues createPropertyValues (Map <String , Object > rawMap ) {
302
314
MutablePropertyValues mpvs = new MutablePropertyValues ();
303
315
Stack <String > segments = new Stack <>();
304
316
for (String key : rawMap .keySet ()) {
@@ -337,41 +349,22 @@ else if (value instanceof Map) {
337
349
}
338
350
}
339
351
340
- private static String toArgumentPath (Stack <String > path ) {
341
- StringBuilder sb = new StringBuilder ();
342
- path .forEach (sb ::append );
343
- return sb .toString ();
344
- }
345
-
346
352
@ SuppressWarnings ("unchecked" )
347
353
@ Nullable
348
- private <T > T convertValue (@ Nullable Object rawValue , Class <T > type , BindingResult result , Stack <String > segments ) {
349
- return (T ) convertValue (rawValue , type , TypeDescriptor .valueOf (type ), result , segments );
350
- }
351
-
352
- @ Nullable
353
- private Object convertValue (
354
- @ Nullable Object rawValue , Class <?> type , TypeDescriptor descriptor ,
355
- BindingResult bindingResult , Stack <String > segments ) {
354
+ private <T > T convertValue (
355
+ @ Nullable Object rawValue , Class <T > type , BindingResult bindingResult , Stack <String > segments ) {
356
356
357
+ Object value = null ;
357
358
try {
358
- return getTypeConverter ().convertIfNecessary (rawValue , type , descriptor );
359
+ value = getTypeConverter ().convertIfNecessary (rawValue , ( Class <?>) type , TypeDescriptor . valueOf ( type ) );
359
360
}
360
361
catch (TypeMismatchException ex ) {
361
362
String name = toArgumentPath (segments );
362
363
ex .initPropertyName (name );
363
364
bindingResult .recordFieldValue (name , type , rawValue );
364
365
this .bindingErrorProcessor .processPropertyAccessException (ex , bindingResult );
365
366
}
366
- return null ;
367
- }
368
-
369
- private static void copyBindingErrors (DataBinder binder , BindingResult bindingResult , Stack <String > segments ) {
370
- String path = (!segments .isEmpty () ? toArgumentPath (segments ) + "." : "" );
371
- binder .getBindingResult ().getFieldErrors ().forEach (error -> bindingResult .addError (
372
- new FieldError (bindingResult .getObjectName (), path + error .getField (),
373
- error .getRejectedValue (), error .isBindingFailure (), error .getCodes (),
374
- error .getArguments (), error .getDefaultMessage ())));
367
+ return (T ) value ;
375
368
}
376
369
377
370
}
0 commit comments