2222import java .lang .annotation .Annotation ;
2323import java .lang .reflect .Type ;
2424import java .util .ArrayList ;
25+ import java .util .Collection ;
2526import java .util .Collections ;
2627import java .util .EnumSet ;
2728import java .util .LinkedHashSet ;
2829import java .util .List ;
30+ import java .util .Optional ;
2931import java .util .Set ;
3032import javax .servlet .http .HttpServletRequest ;
3133
5961 *
6062 * @author Arjen Poutsma
6163 * @author Rossen Stoyanchev
64+ * @author Juergen Hoeller
6265 * @since 3.1
6366 */
6467public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
@@ -128,33 +131,33 @@ protected RequestResponseBodyAdviceChain getAdvice() {
128131 * reading from the given request.
129132 * @param <T> the expected type of the argument value to be created
130133 * @param webRequest the current request
131- * @param methodParam the method argument
134+ * @param parameter the method parameter descriptor (may be {@code null})
132135 * @param paramType the type of the argument value to be created
133136 * @return the created method argument value
134137 * @throws IOException if the reading from the request fails
135138 * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
136139 */
137- protected <T > Object readWithMessageConverters (NativeWebRequest webRequest , MethodParameter methodParam ,
140+ protected <T > Object readWithMessageConverters (NativeWebRequest webRequest , MethodParameter parameter ,
138141 Type paramType ) throws IOException , HttpMediaTypeNotSupportedException , HttpMessageNotReadableException {
139142
140143 HttpInputMessage inputMessage = createInputMessage (webRequest );
141- return readWithMessageConverters (inputMessage , methodParam , paramType );
144+ return readWithMessageConverters (inputMessage , parameter , paramType );
142145 }
143146
144147 /**
145148 * Create the method argument value of the expected parameter type by reading
146149 * from the given HttpInputMessage.
147150 * @param <T> the expected type of the argument value to be created
148151 * @param inputMessage the HTTP input message representing the current request
149- * @param param the method parameter descriptor (may be {@code null})
152+ * @param parameter the method parameter descriptor (may be {@code null})
150153 * @param targetType the target type, not necessarily the same as the method
151154 * parameter type, e.g. for {@code HttpEntity<String>}.
152155 * @return the created method argument value
153156 * @throws IOException if the reading from the request fails
154157 * @throws HttpMediaTypeNotSupportedException if no suitable message converter is found
155158 */
156159 @ SuppressWarnings ("unchecked" )
157- protected <T > Object readWithMessageConverters (HttpInputMessage inputMessage , MethodParameter param ,
160+ protected <T > Object readWithMessageConverters (HttpInputMessage inputMessage , MethodParameter parameter ,
158161 Type targetType ) throws IOException , HttpMediaTypeNotSupportedException , HttpMessageNotReadableException {
159162
160163 MediaType contentType ;
@@ -170,11 +173,11 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
170173 contentType = MediaType .APPLICATION_OCTET_STREAM ;
171174 }
172175
173- Class <?> contextClass = (param != null ? param .getContainingClass () : null );
176+ Class <?> contextClass = (parameter != null ? parameter .getContainingClass () : null );
174177 Class <T > targetClass = (targetType instanceof Class ? (Class <T >) targetType : null );
175178 if (targetClass == null ) {
176- ResolvableType resolvableType = (param != null ?
177- ResolvableType .forMethodParameter (param ) : ResolvableType .forType (targetType ));
179+ ResolvableType resolvableType = (parameter != null ?
180+ ResolvableType .forMethodParameter (parameter ) : ResolvableType .forType (targetType ));
178181 targetClass = (Class <T >) resolvableType .resolve ();
179182 }
180183
@@ -193,13 +196,12 @@ protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, Me
193196 logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
194197 }
195198 if (inputMessage .getBody () != null ) {
196- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
199+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , parameter , targetType , converterType );
197200 body = genericConverter .read (targetType , contextClass , inputMessage );
198- body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
201+ body = getAdvice ().afterBodyRead (body , inputMessage , parameter , targetType , converterType );
199202 }
200203 else {
201- body = null ;
202- body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
204+ body = getAdvice ().handleEmptyBody (null , inputMessage , parameter , targetType , converterType );
203205 }
204206 break ;
205207 }
@@ -210,13 +212,12 @@ else if (targetClass != null) {
210212 logger .debug ("Read [" + targetType + "] as \" " + contentType + "\" with [" + converter + "]" );
211213 }
212214 if (inputMessage .getBody () != null ) {
213- inputMessage = getAdvice ().beforeBodyRead (inputMessage , param , targetType , converterType );
215+ inputMessage = getAdvice ().beforeBodyRead (inputMessage , parameter , targetType , converterType );
214216 body = ((HttpMessageConverter <T >) converter ).read (targetClass , inputMessage );
215- body = getAdvice ().afterBodyRead (body , inputMessage , param , targetType , converterType );
217+ body = getAdvice ().afterBodyRead (body , inputMessage , parameter , targetType , converterType );
216218 }
217219 else {
218- body = null ;
219- body = getAdvice ().handleEmptyBody (body , inputMessage , param , targetType , converterType );
220+ body = getAdvice ().handleEmptyBody (null , inputMessage , parameter , targetType , converterType );
220221 }
221222 break ;
222223 }
@@ -254,12 +255,12 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques
254255 * Spring's {@link org.springframework.validation.annotation.Validated},
255256 * and custom annotations whose name starts with "Valid".
256257 * @param binder the DataBinder to be used
257- * @param methodParam the method parameter
258- * @see #isBindExceptionRequired
258+ * @param parameter the method parameter descriptor
259259 * @since 4.1.5
260+ * @see #isBindExceptionRequired
260261 */
261- protected void validateIfApplicable (WebDataBinder binder , MethodParameter methodParam ) {
262- Annotation [] annotations = methodParam .getParameterAnnotations ();
262+ protected void validateIfApplicable (WebDataBinder binder , MethodParameter parameter ) {
263+ Annotation [] annotations = parameter .getParameterAnnotations ();
263264 for (Annotation ann : annotations ) {
264265 Validated validatedAnn = AnnotationUtils .getAnnotation (ann , Validated .class );
265266 if (validatedAnn != null || ann .annotationType ().getSimpleName ().startsWith ("Valid" )) {
@@ -274,17 +275,37 @@ protected void validateIfApplicable(WebDataBinder binder, MethodParameter method
274275 /**
275276 * Whether to raise a fatal bind exception on validation errors.
276277 * @param binder the data binder used to perform data binding
277- * @param methodParam the method argument
278+ * @param parameter the method parameter descriptor
278279 * @return {@code true} if the next method argument is not of type {@link Errors}
279280 * @since 4.1.5
280281 */
281- protected boolean isBindExceptionRequired (WebDataBinder binder , MethodParameter methodParam ) {
282- int i = methodParam .getParameterIndex ();
283- Class <?>[] paramTypes = methodParam .getMethod ().getParameterTypes ();
282+ protected boolean isBindExceptionRequired (WebDataBinder binder , MethodParameter parameter ) {
283+ int i = parameter .getParameterIndex ();
284+ Class <?>[] paramTypes = parameter .getMethod ().getParameterTypes ();
284285 boolean hasBindingResult = (paramTypes .length > (i + 1 ) && Errors .class .isAssignableFrom (paramTypes [i + 1 ]));
285286 return !hasBindingResult ;
286287 }
287288
289+ /**
290+ * Adapt the given argument against the method parameter, if necessary.
291+ * @param arg the resolved argument
292+ * @param parameter the method parameter descriptor
293+ * @return the adapted argument, or the original resolved argument as-is
294+ * @since 4.3.5
295+ */
296+ protected Object adaptArgumentIfNecessary (Object arg , MethodParameter parameter ) {
297+ if (parameter .getParameterType () == Optional .class ) {
298+ if (arg == null || (arg instanceof Collection && ((Collection ) arg ).isEmpty ()) ||
299+ (arg instanceof Object [] && ((Object []) arg ).length == 0 )) {
300+ return Optional .empty ();
301+ }
302+ else {
303+ return Optional .of (arg );
304+ }
305+ }
306+ return arg ;
307+ }
308+
288309
289310 private static class EmptyBodyCheckingHttpInputMessage implements HttpInputMessage {
290311
0 commit comments