/*
 * Copyright 2016-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.stream.config;

import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.messaging.converter.MessageConverter;
import org.springframework.messaging.converter.SmartMessageConverter;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.support.MethodArgumentNotValidException;
import org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.Validator;

/**
 * @author Oleg Zhurakousky
 * @deprecated will be removed once https://jira.spring.io/browse/SPR-17503 is addressed
 */
@Deprecated
class SmartPayloadArgumentResolver extends PayloadArgumentResolver {

	private final MessageConverter messageConverter;

	SmartPayloadArgumentResolver(MessageConverter messageConverter) {
		super(messageConverter);
		this.messageConverter = messageConverter;
	}

	SmartPayloadArgumentResolver(MessageConverter messageConverter, Validator validator) {
		super(messageConverter, validator, true);
		this.messageConverter = messageConverter;
	}

	SmartPayloadArgumentResolver(MessageConverter messageConverter, Validator validator,
			boolean useDefaultResolution) {
		super(messageConverter, validator, useDefaultResolution);
		this.messageConverter = messageConverter;
	}

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return (!Message.class.isAssignableFrom(parameter.getParameterType())
				&& !MessageHeaders.class.isAssignableFrom(parameter.getParameterType())
				&& !parameter.hasParameterAnnotation(Header.class)
				&& !parameter.hasParameterAnnotation(Headers.class));
	}

	@Override
	@Nullable
	public Object resolveArgument(MethodParameter parameter, Message<?> message)
			throws Exception {
		Payload ann = parameter.getParameterAnnotation(Payload.class);
		if (ann != null && StringUtils.hasText(ann.expression())) {
			throw new IllegalStateException(
					"@Payload SpEL expressions not supported by this resolver");
		}

		Object payload = message.getPayload();
		if (isEmptyPayload(payload)) {
			if (ann == null || ann.required()) {
				String paramName = getParameterName(parameter);
				BindingResult bindingResult = new BeanPropertyBindingResult(payload,
						paramName);
				bindingResult.addError(
						new ObjectError(paramName, "Payload value must not be empty"));
				throw new MethodArgumentNotValidException(message, parameter,
						bindingResult);
			}
			else {
				return null;
			}
		}

		Class<?> targetClass = parameter.getParameterType();
		Class<?> payloadClass = payload.getClass();
		if (ClassUtils.isAssignable(payloadClass, targetClass)) {
			validate(message, parameter, payload);
			return payload;
		}
		else {
			if (this.messageConverter instanceof SmartMessageConverter) {
				SmartMessageConverter smartConverter = (SmartMessageConverter) this.messageConverter;
				payload = smartConverter.fromMessage(message, targetClass, parameter);
			}
			else {
				payload = this.messageConverter.fromMessage(message, targetClass);
			}
			if (payload == null) {
				throw new MessageConversionException(message,
						"Cannot convert from [" + payloadClass.getName() + "] to ["
								+ targetClass.getName() + "] for " + message);
			}
			validate(message, parameter, payload);
			return payload;
		}
	}

	private String getParameterName(MethodParameter param) {
		String paramName = param.getParameterName();
		return (paramName != null ? paramName : "Arg " + param.getParameterIndex());
	}

}

@@ -21,9 +21,9 @@
 -201
-8
+9
  the
@@ -1785,16 +1785,40 @@
 akousky%0A
+ * @author Gary Russell%0A
  * @depr
@@ -1898,16 +1898,57 @@
 dressed%0A
+ * (but see note about KafkaNull below).%0A
  */%0A@Dep
@@ -2962,24 +2962,308 @@
 lass));%0A%09%7D%0A%0A
+%09/**%0A%09 * Needed to support mapping KafkaNull to null in method invocation.%0A%09 */%0A%09@Override%0A%09protected boolean isEmptyPayload(Object payload) %7B%0A%09%09return super.isEmptyPayload(payload)%0A%09%09%09%09%7C%7C %22org.springframework.kafka.support.KafkaNull%22%0A%09%09%09%09%09%09.equals(payload.getClass().getName());%0A%09%7D%0A%0A
 %09@Override%0A%09

Support KafkaNull in payload conversion