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

16 package org.springframework.data.util;
17
18 import lombok.AccessLevel;
19 import lombok.AllArgsConstructor;
20 import lombok.EqualsAndHashCode;
21 import lombok.RequiredArgsConstructor;
22
23 import java.util.Optional;
24 import java.util.function.Function;
25 import java.util.function.Supplier;
26
27 import org.springframework.lang.Nullable;
28 import org.springframework.util.Assert;
29
30 /**
31  * Simple value type to delay the creation of an object using a {@link Supplier} returning the produced object for
32  * subsequent lookups. Note, that no concurrency control is applied during the lookup of {@link #get()}, which means in
33  * concurrent access scenarios, the provided {@link Supplier} can be called multiple times.
34  *
35  * @author Oliver Gierke
36  * @author Mark Paluch
37  * @author Henning Rohlfs
38  * @since 2.0
39  */

40 @RequiredArgsConstructor
41 @AllArgsConstructor(access = AccessLevel.PRIVATE)
42 @EqualsAndHashCode
43 public class Lazy<T> implements Supplier<T> {
44
45     private static final Lazy<?> EMPTY = new Lazy<>(() -> nullnulltrue);
46
47     private final Supplier<? extends T> supplier;
48     private @Nullable T value = null;
49     private volatile boolean resolved = false;
50
51     /**
52      * Creates a new {@link Lazy} to produce an object lazily.
53      *
54      * @param <T> the type of which to produce an object of eventually.
55      * @param supplier the {@link Supplier} to create the object lazily.
56      * @return
57      */

58     public static <T> Lazy<T> of(Supplier<? extends T> supplier) {
59         return new Lazy<>(supplier);
60     }
61
62     /**
63      * Creates a new {@link Lazy} to return the given value.
64      *
65      * @param <T> the type of the value to return eventually.
66      * @param value the value to return.
67      * @return
68      */

69     public static <T> Lazy<T> of(T value) {
70
71         Assert.notNull(value, "Value must not be null!");
72
73         return new Lazy<>(() -> value);
74     }
75
76     /**
77      * Creates a pre-resolved empty {@link Lazy}.
78      *
79      * @return
80      * @since 2.1
81      */

82     @SuppressWarnings("unchecked")
83     public static <T> Lazy<T> empty() {
84         return (Lazy<T>) EMPTY;
85     }
86
87     /**
88      * Returns the value created by the configured {@link Supplier}. Will return the calculated instance for subsequent
89      * lookups.
90      *
91      * @return
92      */

93     public T get() {
94
95         T value = getNullable();
96
97         if (value == null) {
98             throw new IllegalStateException("Expected lazy evaluation to yield a non-null value but got null!");
99         }
100
101         return value;
102     }
103
104     /**
105      * Returns the {@link Optional} value created by the configured {@link Supplier}, allowing the absence of values in
106      * contrast to {@link #get()}. Will return the calculated instance for subsequent lookups.
107      *
108      * @return
109      */

110     public Optional<T> getOptional() {
111         return Optional.ofNullable(getNullable());
112     }
113
114     /**
115      * Returns a new Lazy that will consume the given supplier in case the current one does not yield in a result.
116      *
117      * @param supplier must not be {@literal null}.
118      * @return
119      */

120     public Lazy<T> or(Supplier<? extends T> supplier) {
121
122         Assert.notNull(supplier, "Supplier must not be null!");
123
124         return Lazy.of(() -> orElseGet(supplier));
125     }
126
127     /**
128      * Returns a new Lazy that will return the given value in case the current one does not yield in a result.
129      *
130      * @param supplier must not be {@literal null}.
131      * @return
132      */

133     public Lazy<T> or(T value) {
134
135         Assert.notNull(value, "Value must not be null!");
136
137         return Lazy.of(() -> orElse(value));
138     }
139
140     /**
141      * Returns the value of the lazy computation or the given default value in case the computation yields
142      * {@literal null}.
143      *
144      * @param value
145      * @return
146      */

147     @Nullable
148     public T orElse(@Nullable T value) {
149
150         T nullable = getNullable();
151
152         return nullable == null ? value : nullable;
153     }
154
155     /**
156      * Returns the value of the lazy computation or the value produced by the given {@link Supplier} in case the original
157      * value is {@literal null}.
158      *
159      * @param supplier must not be {@literal null}.
160      * @return
161      */

162     @Nullable
163     private T orElseGet(Supplier<? extends T> supplier) {
164
165         Assert.notNull(supplier, "Default value supplier must not be null!");
166
167         T value = getNullable();
168
169         return value == null ? supplier.get() : value;
170     }
171
172     /**
173      * Creates a new {@link Lazy} with the given {@link Function} lazily applied to the current one.
174      *
175      * @param function must not be {@literal null}.
176      * @return
177      */

178     public <S> Lazy<S> map(Function<? super T, ? extends S> function) {
179
180         Assert.notNull(function, "Function must not be null!");
181
182         return Lazy.of(() -> function.apply(get()));
183     }
184
185     /**
186      * Creates a new {@link Lazy} with the given {@link Function} lazily applied to the current one.
187      *
188      * @param function must not be {@literal null}.
189      * @return
190      */

191     public <S> Lazy<S> flatMap(Function<? super T, Lazy<? extends S>> function) {
192
193         Assert.notNull(function, "Function must not be null!");
194
195         return Lazy.of(() -> function.apply(get()).get());
196     }
197
198     /**
199      * Returns the value of the lazy evaluation.
200      *
201      * @return
202      * @since 2.2
203      */

204     @Nullable
205     public T getNullable() {
206
207         if (resolved) {
208             return value;
209         }
210
211         this.value = supplier.get();
212         this.resolved = true;
213
214         return value;
215     }
216 }
217