001/*
002 * Copyright 2002-2020 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.core;
018
019import java.util.function.Supplier;
020
021import org.springframework.lang.Nullable;
022import org.springframework.util.Assert;
023
024/**
025 * Describes the semantics of a reactive type including boolean checks for
026 * {@link #isMultiValue()}, {@link #isNoValue()}, and {@link #supportsEmpty()}.
027 *
028 * @author Rossen Stoyanchev
029 * @since 5.0
030 */
031public final class ReactiveTypeDescriptor {
032
033        private final Class<?> reactiveType;
034
035        private final boolean multiValue;
036
037        private final boolean noValue;
038
039        @Nullable
040        private final Supplier<?> emptyValueSupplier;
041
042        private final boolean deferred;
043
044
045        private ReactiveTypeDescriptor(Class<?> reactiveType, boolean multiValue, boolean noValue,
046                        @Nullable Supplier<?> emptySupplier) {
047
048                this(reactiveType, multiValue, noValue, emptySupplier, true);
049        }
050
051        private ReactiveTypeDescriptor(Class<?> reactiveType, boolean multiValue, boolean noValue,
052                        @Nullable Supplier<?> emptySupplier, boolean deferred) {
053
054                Assert.notNull(reactiveType, "'reactiveType' must not be null");
055                this.reactiveType = reactiveType;
056                this.multiValue = multiValue;
057                this.noValue = noValue;
058                this.emptyValueSupplier = emptySupplier;
059                this.deferred = deferred;
060        }
061
062
063        /**
064         * Return the reactive type for this descriptor.
065         */
066        public Class<?> getReactiveType() {
067                return this.reactiveType;
068        }
069
070        /**
071         * Return {@code true} if the reactive type can produce more than 1 value
072         * can be produced and is therefore a good fit to adapt to {@code Flux}.
073         * A {@code false} return value implies the reactive type can produce 1
074         * value at most and is therefore a good fit to adapt to {@code Mono}.
075         */
076        public boolean isMultiValue() {
077                return this.multiValue;
078        }
079
080        /**
081         * Return {@code true} if the reactive type does not produce any values and
082         * only provides completion and error signals.
083         */
084        public boolean isNoValue() {
085                return this.noValue;
086        }
087
088        /**
089         * Return {@code true} if the reactive type can complete with no values.
090         */
091        public boolean supportsEmpty() {
092                return (this.emptyValueSupplier != null);
093        }
094
095        /**
096         * Return an empty-value instance for the underlying reactive or async type.
097         * Use of this type implies {@link #supportsEmpty()} is true.
098         */
099        public Object getEmptyValue() {
100                Assert.state(this.emptyValueSupplier != null, "Empty values not supported");
101                return this.emptyValueSupplier.get();
102        }
103
104        /**
105         * Whether the underlying operation is deferred and needs to be started
106         * explicitly, e.g. via subscribing (or similar), or whether it is triggered
107         * without the consumer having any control.
108         * @since 5.2.7
109         */
110        public boolean isDeferred() {
111                return this.deferred;
112        }
113
114
115        @Override
116        public boolean equals(@Nullable Object other) {
117                if (this == other) {
118                        return true;
119                }
120                if (other == null || getClass() != other.getClass()) {
121                        return false;
122                }
123                return this.reactiveType.equals(((ReactiveTypeDescriptor) other).reactiveType);
124        }
125
126        @Override
127        public int hashCode() {
128                return this.reactiveType.hashCode();
129        }
130
131
132        /**
133         * Descriptor for a reactive type that can produce 0..N values.
134         * @param type the reactive type
135         * @param emptySupplier a supplier of an empty-value instance of the reactive type
136         */
137        public static ReactiveTypeDescriptor multiValue(Class<?> type, Supplier<?> emptySupplier) {
138                return new ReactiveTypeDescriptor(type, true, false, emptySupplier);
139        }
140
141        /**
142         * Descriptor for a reactive type that can produce 0..1 values.
143         * @param type the reactive type
144         * @param emptySupplier a supplier of an empty-value instance of the reactive type
145         */
146        public static ReactiveTypeDescriptor singleOptionalValue(Class<?> type, Supplier<?> emptySupplier) {
147                return new ReactiveTypeDescriptor(type, false, false, emptySupplier);
148        }
149
150        /**
151         * Descriptor for a reactive type that must produce 1 value to complete.
152         * @param type the reactive type
153         */
154        public static ReactiveTypeDescriptor singleRequiredValue(Class<?> type) {
155                return new ReactiveTypeDescriptor(type, false, false, null);
156        }
157
158        /**
159         * Descriptor for a reactive type that does not produce any values.
160         * @param type the reactive type
161         * @param emptySupplier a supplier of an empty-value instance of the reactive type
162         */
163        public static ReactiveTypeDescriptor noValue(Class<?> type, Supplier<?> emptySupplier) {
164                return new ReactiveTypeDescriptor(type, false, true, emptySupplier);
165        }
166
167        /**
168         * The same as {@link #singleOptionalValue(Class, Supplier)} but for a
169         * non-deferred, async type such as {@link java.util.concurrent.CompletableFuture}.
170         * @param type the reactive type
171         * @param emptySupplier a supplier of an empty-value instance of the reactive type
172         * @since 5.2.7
173         */
174        public static ReactiveTypeDescriptor nonDeferredAsyncValue(Class<?> type, Supplier<?> emptySupplier) {
175                return new ReactiveTypeDescriptor(type, false, false, emptySupplier, false);
176        }
177
178}