001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.reactive.result.method;
018
019import java.lang.annotation.Annotation;
020import java.util.function.BiPredicate;
021import java.util.function.Predicate;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025
026import org.springframework.core.MethodParameter;
027import org.springframework.core.ReactiveAdapter;
028import org.springframework.core.ReactiveAdapterRegistry;
029import org.springframework.util.Assert;
030
031/**
032 * Base class for {@link HandlerMethodArgumentResolver} implementations with access to a
033 * {@code ReactiveAdapterRegistry} and methods to check for method parameter support.
034 *
035 * @author Rossen Stoyanchev
036 * @author Juergen Hoeller
037 * @since 5.0
038 */
039public abstract class HandlerMethodArgumentResolverSupport implements HandlerMethodArgumentResolver {
040
041        protected final Log logger = LogFactory.getLog(getClass());
042
043        private final ReactiveAdapterRegistry adapterRegistry;
044
045
046        protected HandlerMethodArgumentResolverSupport(ReactiveAdapterRegistry adapterRegistry) {
047                Assert.notNull(adapterRegistry, "ReactiveAdapterRegistry is required");
048                this.adapterRegistry = adapterRegistry;
049        }
050
051
052        /**
053         * Return the configured {@link ReactiveAdapterRegistry}.
054         */
055        public ReactiveAdapterRegistry getAdapterRegistry() {
056                return this.adapterRegistry;
057        }
058
059
060        /**
061         * Evaluate the {@code Predicate} on the method parameter type or on
062         * the generic type within a reactive type wrapper.
063         */
064        protected boolean checkParameterType(MethodParameter parameter, Predicate<Class<?>> predicate) {
065                Class<?> type = parameter.getParameterType();
066                ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
067                if (adapter != null) {
068                        assertHasValues(adapter, parameter);
069                        type = parameter.nested().getNestedParameterType();
070                }
071                return predicate.test(type);
072        }
073
074        private void assertHasValues(ReactiveAdapter adapter, MethodParameter param) {
075                if (adapter.isNoValue()) {
076                        throw new IllegalArgumentException(
077                                        "No value reactive types not supported: " + param.getGenericParameterType());
078                }
079        }
080
081        /**
082         * Evaluate the {@code Predicate} on the method parameter type but raise an
083         * {@code IllegalStateException} if the same matches the generic type
084         * within a reactive type wrapper.
085         */
086        protected boolean checkParameterTypeNoReactiveWrapper(MethodParameter parameter, Predicate<Class<?>> predicate) {
087                Class<?> type = parameter.getParameterType();
088                ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
089                if (adapter != null) {
090                        assertHasValues(adapter, parameter);
091                        type = parameter.nested().getNestedParameterType();
092                }
093                if (predicate.test(type)) {
094                        if (adapter == null) {
095                                return true;
096                        }
097                        throw buildReactiveWrapperException(parameter);
098                }
099                return false;
100        }
101
102        private IllegalStateException buildReactiveWrapperException(MethodParameter parameter) {
103                return new IllegalStateException(getClass().getSimpleName() +
104                                " does not support reactive type wrapper: " + parameter.getGenericParameterType());
105        }
106
107        /**
108         * Evaluate the {@code Predicate} on the method parameter type if it has the
109         * given annotation, nesting within {@link java.util.Optional} if necessary,
110         * but raise an {@code IllegalStateException} if the same matches the generic
111         * type within a reactive type wrapper.
112         */
113        protected <A extends Annotation> boolean checkAnnotatedParamNoReactiveWrapper(
114                        MethodParameter parameter, Class<A> annotationType, BiPredicate<A, Class<?>> typePredicate) {
115
116                A annotation = parameter.getParameterAnnotation(annotationType);
117                if (annotation == null) {
118                        return false;
119                }
120
121                parameter = parameter.nestedIfOptional();
122                Class<?> type = parameter.getNestedParameterType();
123
124                ReactiveAdapter adapter = getAdapterRegistry().getAdapter(type);
125                if (adapter != null) {
126                        assertHasValues(adapter, parameter);
127                        parameter = parameter.nested();
128                        type = parameter.getNestedParameterType();
129                }
130
131                if (typePredicate.test(annotation, type)) {
132                        if (adapter == null) {
133                                return true;
134                        }
135                        throw buildReactiveWrapperException(parameter);
136                }
137
138                return false;
139        }
140
141}