1 /*
2  * Copyright 2015-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.domain;
17
18 import lombok.AccessLevel;
19 import lombok.NonNull;
20 import lombok.RequiredArgsConstructor;
21 import lombok.Value;
22
23 import java.util.Optional;
24
25 import org.springframework.util.Assert;
26
27 /**
28  * Simple value object to work with ranges and boundaries.
29  *
30  * @author Oliver Gierke
31  * @author Mark Paluch
32  * @since 1.10
33  */

34 @Value
35 @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
36 public class Range<T extends Comparable<T>> {
37
38     private final static Range<?> UNBOUNDED = Range.of(Bound.unbounded(), Bound.UNBOUNDED);
39
40     /**
41      * The lower bound of the range.
42      */

43     private final @NonNull Bound<T> lowerBound;
44
45     /**
46      * The upper bound of the range.
47      */

48     private final @NonNull Bound<T> upperBound;
49
50     /**
51      * Returns an unbounded {@link Range}.
52      *
53      * @return
54      * @since 2.0
55      */

56     @SuppressWarnings("unchecked")
57     public static <T extends Comparable<T>> Range<T> unbounded() {
58         return (Range<T>) UNBOUNDED;
59     }
60
61     /**
62      * Creates a new {@link Range} with inclusive bounds for both values.
63      *
64      * @param <T>
65      * @param from must not be {@literal null}.
66      * @param to must not be {@literal null}.
67      * @return
68      * @since 2.2
69      */

70     public static <T extends Comparable<T>> Range<T> closed(T from, T to) {
71         return new Range<>(Bound.inclusive(from), Bound.inclusive(to));
72     }
73
74     /**
75      * Creates a new {@link Range} with exclusive bounds for both values.
76      *
77      * @param <T>
78      * @param from must not be {@literal null}.
79      * @param to must not be {@literal null}.
80      * @return
81      * @since 2.2
82      */

83     public static <T extends Comparable<T>> Range<T> open(T from, T to) {
84         return new Range<>(Bound.exclusive(from), Bound.exclusive(to));
85     }
86
87     /**
88      * Creates a new left-open {@link Range}, i.e. left exclusive, right inclusive.
89      *
90      * @param <T>
91      * @param from must not be {@literal null}.
92      * @param to must not be {@literal null}.
93      * @return
94      * @since 2.2
95      */

96     public static <T extends Comparable<T>> Range<T> leftOpen(T from, T to) {
97         return new Range<>(Bound.exclusive(from), Bound.inclusive(to));
98     }
99
100     /**
101      * Creates a new right-open {@link Range}, i.e. left inclusive, right exclusive.
102      *
103      * @param <T>
104      * @param from must not be {@literal null}.
105      * @param to must not be {@literal null}.
106      * @return
107      * @since 2.2
108      */

109     public static <T extends Comparable<T>> Range<T> rightOpen(T from, T to) {
110         return new Range<>(Bound.inclusive(from), Bound.exclusive(to));
111     }
112
113     /**
114      * Creates a left-unbounded {@link Range} (the left bound set to {@link Bound#unbounded()}) with the given right
115      * bound.
116      *
117      * @param <T>
118      * @param to the right {@link Bound}, must not be {@literal null}.
119      * @return
120      * @since 2.2
121      */

122     public static <T extends Comparable<T>> Range<T> leftUnbounded(Bound<T> to) {
123         return new Range<>(Bound.unbounded(), to);
124     }
125
126     /**
127      * Creates a right-unbounded {@link Range} (the right bound set to {@link Bound#unbounded()}) with the given left
128      * bound.
129      *
130      * @param <T>
131      * @param from the left {@link Bound}, must not be {@literal null}.
132      * @return
133      * @since 2.2
134      */

135     public static <T extends Comparable<T>> Range<T> rightUnbounded(Bound<T> from) {
136         return new Range<>(from, Bound.unbounded());
137     }
138
139     /**
140      * Create a {@link RangeBuilder} given the lower {@link Bound}.
141      *
142      * @param lower must not be {@literal null}.
143      * @return
144      * @since 2.0
145      */

146     public static <T extends Comparable<T>> RangeBuilder<T> from(Bound<T> lower) {
147
148         Assert.notNull(lower, "Lower bound must not be null!");
149         return new RangeBuilder<>(lower);
150     }
151
152     /**
153      * Creates a new {@link Range} with the given lower and upper bound. Prefer {@link #from(Bound)} for a more builder
154      * style API.
155      *
156      * @param lowerBound must not be {@literal null}.
157      * @param upperBound must not be {@literal null}.
158      * @since 2.0
159      * @see #from(Bound)
160      */

161     public static <T extends Comparable<T>> Range<T> of(Bound<T> lowerBound, Bound<T> upperBound) {
162         return new Range<>(lowerBound, upperBound);
163     }
164
165     /**
166      * Creates a new Range with the given value as sole member.
167      *
168      * @param <T>
169      * @param value must not be {@literal null}.
170      * @return
171      * @see Range#closed(Comparable, Comparable)
172      */

173     public static <T extends Comparable<T>> Range<T> just(T value) {
174         return Range.closed(value, value);
175     }
176
177     /**
178      * Returns whether the {@link Range} contains the given value.
179      *
180      * @param value must not be {@literal null}.
181      * @return
182      */

183     public boolean contains(T value) {
184
185         Assert.notNull(value, "Reference value must not be null!");
186
187         boolean greaterThanLowerBound = lowerBound.getValue() //
188                 .map(it -> lowerBound.isInclusive() ? it.compareTo(value) <= 0 : it.compareTo(value) < 0) //
189                 .orElse(true);
190
191         boolean lessThanUpperBound = upperBound.getValue() //
192                 .map(it -> upperBound.isInclusive() ? it.compareTo(value) >= 0 : it.compareTo(value) > 0) //
193                 .orElse(true);
194
195         return greaterThanLowerBound && lessThanUpperBound;
196     }
197
198     /*
199      * (non-Javadoc)
200      * @see java.lang.Object#toString()
201      */

202     @Override
203     public String toString() {
204         return String.format("%s-%s", lowerBound.toPrefixString(), upperBound.toSuffixString());
205     }
206
207     /**
208      * Value object representing a boundary. A boundary can either be {@link #unbounded() unbounded},
209      * {@link #inclusive(Comparable) including its value} or {@link #exclusive(Comparable) its value}.
210      *
211      * @author Mark Paluch
212      * @since 2.0
213      * @soundtrack Mohamed Ragab - Excelsior Sessions (March 2017)
214      */

215     @Value
216     @RequiredArgsConstructor(access = AccessLevel.PRIVATE)
217     public static class Bound<T extends Comparable<T>> {
218
219         @SuppressWarnings({ "rawtypes""unchecked" }) //
220         private static final Bound<?> UNBOUNDED = new Bound(Optional.empty(), true);
221
222         private final Optional<T> value;
223         private final boolean inclusive;
224
225         /**
226          * Creates an unbounded {@link Bound}.
227          */

228         @SuppressWarnings("unchecked")
229         public static <T extends Comparable<T>> Bound<T> unbounded() {
230             return (Bound<T>) UNBOUNDED;
231         }
232
233         /**
234          * Returns whether this boundary is bounded.
235          *
236          * @return
237          */

238         public boolean isBounded() {
239             return value.isPresent();
240         }
241
242         /**
243          * Creates a boundary including {@code value}.
244          *
245          * @param value must not be {@literal null}.
246          * @return
247          */

248         public static <T extends Comparable<T>> Bound<T> inclusive(T value) {
249
250             Assert.notNull(value, "Value must not be null!");
251             return new Bound<>(Optional.of(value), true);
252         }
253
254         /**
255          * Creates a boundary including {@code value}.
256          *
257          * @param value must not be {@literal null}.
258          * @return
259          */

260         public static Bound<Integer> inclusive(int value) {
261             return inclusive((Integer) value);
262         }
263
264         /**
265          * Creates a boundary including {@code value}.
266          *
267          * @param value must not be {@literal null}.
268          * @return
269          */

270         public static Bound<Long> inclusive(long value) {
271             return inclusive((Long) value);
272         }
273
274         /**
275          * Creates a boundary including {@code value}.
276          *
277          * @param value must not be {@literal null}.
278          * @return
279          */

280         public static Bound<Float> inclusive(float value) {
281             return inclusive((Float) value);
282         }
283
284         /**
285          * Creates a boundary including {@code value}.
286          *
287          * @param value must not be {@literal null}.
288          * @return
289          */

290         public static Bound<Double> inclusive(double value) {
291             return inclusive((Double) value);
292         }
293
294         /**
295          * Creates a boundary excluding {@code value}.
296          *
297          * @param value must not be {@literal null}.
298          * @return
299          */

300         public static <T extends Comparable<T>> Bound<T> exclusive(T value) {
301
302             Assert.notNull(value, "Value must not be null!");
303             return new Bound<>(Optional.of(value), false);
304         }
305
306         /**
307          * Creates a boundary excluding {@code value}.
308          *
309          * @param value must not be {@literal null}.
310          * @return
311          */

312         public static Bound<Integer> exclusive(int value) {
313             return exclusive((Integer) value);
314         }
315
316         /**
317          * Creates a boundary excluding {@code value}.
318          *
319          * @param value must not be {@literal null}.
320          * @return
321          */

322         public static Bound<Long> exclusive(long value) {
323             return exclusive((Long) value);
324         }
325
326         /**
327          * Creates a boundary excluding {@code value}.
328          *
329          * @param value must not be {@literal null}.
330          * @return
331          */

332         public static Bound<Float> exclusive(float value) {
333             return exclusive((Float) value);
334         }
335
336         /**
337          * Creates a boundary excluding {@code value}.
338          *
339          * @param value must not be {@literal null}.
340          * @return
341          */

342         public static Bound<Double> exclusive(double value) {
343             return exclusive((Double) value);
344         }
345
346         String toPrefixString() {
347
348             return getValue() //
349                     .map(Object::toString) //
350                     .map(it -> isInclusive() ? "[".concat(it) : "(".concat(it)) //
351                     .orElse("unbounded");
352         }
353
354         String toSuffixString() {
355
356             return getValue() //
357                     .map(Object::toString) //
358                     .map(it -> isInclusive() ? it.concat("]") : it.concat(")")) //
359                     .orElse("unbounded");
360         }
361
362         /*
363          * (non-Javadoc)
364          * @see java.lang.Object#toString()
365          */

366         @Override
367         public String toString() {
368             return value.map(Object::toString).orElse("unbounded");
369         }
370
371     }
372
373     /**
374      * Builder for {@link Range} allowing to specify the upper boundary.
375      *
376      * @author Mark Paluch
377      * @since 2.0
378      * @soundtrack Aly and Fila - Future Sound Of Egypt 493
379      */

380     public static class RangeBuilder<T extends Comparable<T>> {
381
382         private final Bound<T> lower;
383
384         RangeBuilder(Bound<T> lower) {
385             this.lower = lower;
386         }
387
388         /**
389          * Create a {@link Range} given the upper {@link Bound}.
390          *
391          * @param upper must not be {@literal null}.
392          * @return
393          */

394         public Range<T> to(Bound<T> upper) {
395
396             Assert.notNull(upper, "Upper bound must not be null!");
397             return new Range<>(lower, upper);
398         }
399     }
400 }
401