1616package io .micronaut .http .bind .binders ;
1717
1818import io .micronaut .core .annotation .AnnotationMetadata ;
19+ import io .micronaut .core .annotation .Nullable ;
20+ import io .micronaut .core .beans .BeanIntrospection ;
21+ import io .micronaut .core .beans .BeanIntrospector ;
1922import io .micronaut .core .bind .annotation .AbstractArgumentBinder ;
23+ import io .micronaut .core .bind .annotation .Bindable ;
2024import io .micronaut .core .convert .ArgumentConversionContext ;
25+ import io .micronaut .core .convert .ConversionError ;
2126import io .micronaut .core .convert .ConversionService ;
2227import io .micronaut .core .convert .format .Format ;
2328import io .micronaut .core .convert .value .ConvertibleMultiValues ;
2833import io .micronaut .http .uri .UriMatchVariable ;
2934
3035import java .util .Collections ;
36+ import java .util .List ;
3137import java .util .Optional ;
3238
3339/**
@@ -53,7 +59,7 @@ public QueryValueArgumentBinder(ConversionService conversionService) {
5359 * Constructor.
5460 *
5561 * @param conversionService conversion service
56- * @param argument The argument
62+ * @param argument The argument
5763 */
5864 public QueryValueArgumentBinder (ConversionService conversionService , Argument <T > argument ) {
5965 super (conversionService , argument );
@@ -91,6 +97,18 @@ public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?
9197 return BindingResult .unsatisfied ();
9298 }
9399
100+ BindingResult <T > bindSimpleResult = bindSimple (context , source , annotationMetadata , parameters , argument );
101+ if (bindSimpleResult .isSatisfied ()) {
102+ return bindSimpleResult ;
103+ }
104+ return bindPojo (context , parameters , argument );
105+ }
106+
107+ private BindingResult <T > bindSimple (ArgumentConversionContext <T > context ,
108+ HttpRequest <?> source ,
109+ AnnotationMetadata annotationMetadata ,
110+ ConvertibleMultiValues <String > parameters ,
111+ Argument <T > argument ) {
94112 // First try converting from the ConvertibleMultiValues type and if conversion is successful, return it.
95113 // Otherwise, use the given uri template to deduce what to do with the variable
96114 Optional <T > multiValueConversion ;
@@ -130,6 +148,57 @@ public BindingResult<T> bind(ArgumentConversionContext<T> context, HttpRequest<?
130148 return doBind (context , parameters , BindingResult .unsatisfied ());
131149 }
132150
151+ private BindingResult <T > bindPojo (ArgumentConversionContext <T > context ,
152+ ConvertibleMultiValues <String > parameters ,
153+ Argument <T > argument ) {
154+ Optional <BeanIntrospection <T >> introspectionOpt = BeanIntrospector .SHARED .findIntrospection (argument .getType ());
155+ if (introspectionOpt .isEmpty ()) {
156+ return BindingResult .unsatisfied ();
157+ }
158+
159+ BeanIntrospection <T > introspection = introspectionOpt .get ();
160+ BeanIntrospection .Builder <T > introspectionBuilder = introspection .builder ();
161+ Argument <?>[] builderArguments = introspectionBuilder .getBuilderArguments ();
162+
163+ for (int index = 0 ; index < builderArguments .length ; index ++) {
164+ Argument <?> builderArg = builderArguments [index ];
165+ String propertyName = builderArg .getName ();
166+ List <String > values = parameters .getAll (propertyName );
167+ boolean hasNoValue = values .isEmpty ();
168+ @ Nullable String defaultValue = hasNoValue ? builderArg
169+ .getAnnotationMetadata ()
170+ .stringValue (Bindable .class , "defaultValue" ).orElse (null ) : null ;
171+
172+ ArgumentConversionContext <?> conversionContext = context .with (builderArg );
173+ Optional <?> converted = hasNoValue ? conversionService .convert (defaultValue , conversionContext ) : conversionService .convert (values , conversionContext );
174+ if (converted .isPresent ()) {
175+ try {
176+ @ SuppressWarnings ({"unchecked" })
177+ Argument <Object > rawArg = (Argument <Object >) builderArg ;
178+ introspectionBuilder .with (index , rawArg , converted .get ());
179+ } catch (Exception e ) {
180+ context .reject (builderArg , e );
181+ return BindingResult .unsatisfied ();
182+ }
183+ } else if (conversionContext .hasErrors ()) {
184+ ConversionError conversionError = conversionContext .getLastError ().orElse (null );
185+ if (conversionError != null ) {
186+ Exception cause = conversionError .getCause ();
187+ context .reject (builderArg , cause );
188+ return BindingResult .unsatisfied ();
189+ }
190+ }
191+ }
192+
193+ try {
194+ T instance = introspectionBuilder .build ();
195+ return () -> Optional .of (instance );
196+ } catch (Exception e ) {
197+ context .reject (argument , e );
198+ return BindingResult .unsatisfied ();
199+ }
200+ }
201+
133202 @ Override
134203 protected String getParameterName (Argument <T > argument ) {
135204 return argument .getAnnotationMetadata ().stringValue (QueryValue .class ).orElse (argument .getName ());
0 commit comments