3838import org .springframework .core .convert .ConversionService ;
3939import org .springframework .core .convert .TypeDescriptor ;
4040import org .springframework .lang .Nullable ;
41- import org .springframework .util .Assert ;
4241import org .springframework .validation .BindException ;
4342import org .springframework .validation .BindingErrorProcessor ;
4443import org .springframework .validation .BindingResult ;
@@ -65,6 +64,7 @@ public class GraphQlArgumentBinder {
6564 */
6665 private static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 1024 ;
6766
67+
6868 @ Nullable
6969 private final SimpleTypeConverter typeConverter ;
7070
@@ -139,10 +139,8 @@ public Object bind(
139139 segments .push (name );
140140 }
141141
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 );
146144
147145 if (bindingResult .hasErrors ()) {
148146 throw new BindException (bindingResult );
@@ -159,51 +157,43 @@ private void initDataBinder(DataBinder binder) {
159157 @ SuppressWarnings ({"ConstantConditions" , "unchecked" })
160158 @ Nullable
161159 private Object bindRawValue (
162- Object rawValue , ResolvableType targetValueType , Class <?> targetValueClass ,
160+ Object rawValue , ResolvableType targetType , Class <?> targetClass ,
163161 BindingResult bindingResult , Stack <String > segments ) {
164162
165- boolean isOptional = targetValueClass == Optional .class ;
163+ boolean isOptional = ( targetClass == Optional .class ) ;
166164
167165 if (isOptional ) {
168- targetValueType = targetValueType .getNested (2 );
169- targetValueClass = targetValueType .resolve ();
166+ targetType = targetType .getNested (2 );
167+ targetClass = targetType .resolve ();
170168 }
171169
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 ;
175173 }
176174 else if (rawValue instanceof Collection ) {
177- targetValue = bindCollection ((Collection <Object >) rawValue , targetValueType , bindingResult , segments );
175+ value = bindCollection ((Collection <Object >) rawValue , targetType , targetClass , bindingResult , segments );
178176 }
179177 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 );
181179 }
182180 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 ));
185183 }
186184
187- return (isOptional ? Optional .ofNullable (targetValue ) : targetValue );
185+ return (isOptional ? Optional .ofNullable (value ) : value );
188186 }
189187
190188 private Collection <?> bindCollection (
191- Collection <Object > rawCollection , ResolvableType collectionType ,
189+ Collection <Object > rawCollection , ResolvableType collectionType , Class <?> collectionClass ,
192190 BindingResult bindingResult , Stack <String > segments ) {
193191
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-
202192 ResolvableType elementType = collectionType .asCollection ().getGeneric (0 );
203193 Class <?> elementClass = collectionType .asCollection ().getGeneric (0 ).resolve ();
204194 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
207197 }
208198
209199 Collection <Object > collection =
@@ -215,58 +205,80 @@ private Collection<?> bindCollection(
215205 collection .add (bindRawValue (rawValue , elementType , elementClass , bindingResult , segments ));
216206 segments .pop ();
217207 }
208+
218209 return collection ;
219210 }
220211
212+ private static String toArgumentPath (Stack <String > path ) {
213+ StringBuilder sb = new StringBuilder ();
214+ path .forEach (sb ::append );
215+ return sb .toString ();
216+ }
217+
221218 @ Nullable
222219 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 ) {
228222
229223 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 );
244225 }
245226
246- Object target ;
247227 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+ }
248249
249- // Default constructor + data binding via properties
250+ return target ;
251+ }
250252
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 ) {
258256
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 ());
263265
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 ();
265271 }
266272
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 ) {
268280
269- if (! segments .isEmpty () ) {
281+ if (segments .size () > 0 ) {
270282 segments .push ("." );
271283 }
272284
@@ -290,15 +302,15 @@ private Object bindMap(
290302 return BeanUtils .instantiateClass (constructor , args );
291303 }
292304 catch (BeanInstantiationException ex ) {
293- // Ignore if we had binding errors already
305+ // Ignore: we had binding errors to begin with
294306 if (bindingResult .hasErrors ()) {
295307 return null ;
296308 }
297309 throw ex ;
298310 }
299311 }
300312
301- private static MutablePropertyValues toPropertyValues (Map <String , Object > rawMap ) {
313+ private static MutablePropertyValues createPropertyValues (Map <String , Object > rawMap ) {
302314 MutablePropertyValues mpvs = new MutablePropertyValues ();
303315 Stack <String > segments = new Stack <>();
304316 for (String key : rawMap .keySet ()) {
@@ -337,41 +349,22 @@ else if (value instanceof Map) {
337349 }
338350 }
339351
340- private static String toArgumentPath (Stack <String > path ) {
341- StringBuilder sb = new StringBuilder ();
342- path .forEach (sb ::append );
343- return sb .toString ();
344- }
345-
346352 @ SuppressWarnings ("unchecked" )
347353 @ 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 ) {
356356
357+ Object value = null ;
357358 try {
358- return getTypeConverter ().convertIfNecessary (rawValue , type , descriptor );
359+ value = getTypeConverter ().convertIfNecessary (rawValue , ( Class <?>) type , TypeDescriptor . valueOf ( type ) );
359360 }
360361 catch (TypeMismatchException ex ) {
361362 String name = toArgumentPath (segments );
362363 ex .initPropertyName (name );
363364 bindingResult .recordFieldValue (name , type , rawValue );
364365 this .bindingErrorProcessor .processPropertyAccessException (ex , bindingResult );
365366 }
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 ;
375368 }
376369
377370}
0 commit comments