]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db/src/org/simantics/db/function/DbOptional.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.db / src / org / simantics / db / function / DbOptional.java
1 package org.simantics.db.function;
2
3 import java.util.NoSuchElementException;
4 import java.util.Objects;
5
6 import org.simantics.db.exception.DatabaseException;
7
8 /**
9  * A container object which may or may not contain a non-null value. If a value
10  * is present, {@code isPresent()} will return {@code true} and {@code get()}
11  * will return the value.
12  *
13  * <p>
14  * Additional methods that depend on the presence or absence of a contained
15  * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
16  * (return a default value if value not present) and
17  * {@link #ifPresent(org.simantics.db.function.DbConsumer) ifPresent()} (execute
18  * a block of code if the value is present).
19  *
20  * <p>
21  * This is a value-based class; use of identity-sensitive operations (including
22  * reference equality ({@code ==}), identity hash code, or synchronization) on
23  * instances of {@code DbOptional} may have unpredictable results and should be
24  * avoided.
25  *
26  * @since 1.25
27  */
28 public final class DbOptional<T> {
29     /**
30      * Common instance for {@code empty()}.
31      */
32     private static final DbOptional<?> EMPTY = new DbOptional<>();
33
34     /**
35      * If non-null, the value; if null, indicates no value is present
36      */
37     private final T value;
38
39     /**
40      * Constructs an empty instance.
41      *
42      * @implNote Generally only one empty instance, {@link Optional#EMPTY},
43      * should exist per VM.
44      */
45     private DbOptional() {
46         this.value = null;
47     }
48
49     /**
50      * Returns an empty {@code Optional} instance.  No value is present for this
51      * Optional.
52      *
53      * @apiNote Though it may be tempting to do so, avoid testing if an object
54      * is empty by comparing with {@code ==} against instances returned by
55      * {@code Option.empty()}. There is no guarantee that it is a singleton.
56      * Instead, use {@link #isPresent()}.
57      *
58      * @param <T> Type of the non-existent value
59      * @return an empty {@code Optional}
60      */
61     public static<T> DbOptional<T> empty() {
62         @SuppressWarnings("unchecked")
63         DbOptional<T> t = (DbOptional<T>) EMPTY;
64         return t;
65     }
66
67     /**
68      * Constructs an instance with the value present.
69      *
70      * @param value the non-null value to be present
71      * @throws NullPointerException if value is null
72      */
73     private DbOptional(T value) {
74         this.value = Objects.requireNonNull(value);
75     }
76
77     /**
78      * Returns an {@code Optional} with the specified present non-null value.
79      *
80      * @param <T> the class of the value
81      * @param value the value to be present, which must be non-null
82      * @return an {@code Optional} with the value present
83      * @throws NullPointerException if value is null
84      */
85     public static <T> DbOptional<T> of(T value) {
86         return new DbOptional<>(value);
87     }
88
89     /**
90      * Returns an {@code Optional} describing the specified value, if non-null,
91      * otherwise returns an empty {@code Optional}.
92      *
93      * @param <T> the class of the value
94      * @param value the possibly-null value to describe
95      * @return an {@code Optional} with a present value if the specified value
96      * is non-null, otherwise an empty {@code Optional}
97      */
98     public static <T> DbOptional<T> ofNullable(T value) {
99         return value == null ? empty() : of(value);
100     }
101
102     /**
103      * If a value is present in this {@code Optional}, returns the value,
104      * otherwise throws {@code NoSuchElementException}.
105      *
106      * @return the non-null value held by this {@code Optional}
107      * @throws NoSuchElementException if there is no value present
108      *
109      * @see Optional#isPresent()
110      */
111     public T get() {
112         if (value == null) {
113             throw new NoSuchElementException("No value present");
114         }
115         return value;
116     }
117
118     /**
119      * Return {@code true} if there is a value present, otherwise {@code false}.
120      *
121      * @return {@code true} if there is a value present, otherwise {@code false}
122      */
123     public boolean isPresent() {
124         return value != null;
125     }
126
127     /**
128      * If a value is present, invoke the specified consumer with the value,
129      * otherwise do nothing.
130      *
131      * @param consumer block to be executed if a value is present
132      * @throws DatabaseException 
133      * @throws NullPointerException if value is present and {@code consumer} is
134      * null
135      */
136     public void ifPresent(DbConsumer<? super T> consumer) throws DatabaseException {
137         if (value != null)
138             consumer.accept(value);
139     }
140
141     /**
142      * If a value is present, and the value matches the given predicate,
143      * return an {@code Optional} describing the value, otherwise return an
144      * empty {@code Optional}.
145      *
146      * @param predicate a predicate to apply to the value, if present
147      * @return an {@code Optional} describing the value of this {@code Optional}
148      * if a value is present and the value matches the given predicate,
149      * otherwise an empty {@code Optional}
150      * @throws DatabaseException 
151      * @throws NullPointerException if the predicate is null
152      */
153     public DbOptional<T> filter(DbPredicate<? super T> predicate) throws DatabaseException {
154         Objects.requireNonNull(predicate);
155         if (!isPresent())
156             return this;
157         else
158             return predicate.test(value) ? this : empty();
159     }
160
161     /**
162      * If a value is present, apply the provided mapping function to it,
163      * and if the result is non-null, return an {@code Optional} describing the
164      * result.  Otherwise return an empty {@code Optional}.
165      *
166      * @apiNote This method supports post-processing on optional values, without
167      * the need to explicitly check for a return status.  For example, the
168      * following code traverses a stream of file names, selects one that has
169      * not yet been processed, and then opens that file, returning an
170      * {@code Optional<FileInputStream>}:
171      *
172      * <pre>{@code
173      *     Optional<FileInputStream> fis =
174      *         names.stream().filter(name -> !isProcessedYet(name))
175      *                       .findFirst()
176      *                       .map(name -> new FileInputStream(name));
177      * }</pre>
178      *
179      * Here, {@code findFirst} returns an {@code Optional<String>}, and then
180      * {@code map} returns an {@code Optional<FileInputStream>} for the desired
181      * file if one exists.
182      *
183      * @param <U> The type of the result of the mapping function
184      * @param mapper a mapping function to apply to the value, if present
185      * @return an {@code Optional} describing the result of applying a mapping
186      * function to the value of this {@code Optional}, if a value is present,
187      * otherwise an empty {@code Optional}
188      * @throws DatabaseException 
189      * @throws NullPointerException if the mapping function is null
190      */
191     public<U> DbOptional<U> map(DbFunction<? super T, ? extends U> mapper) throws DatabaseException {
192         Objects.requireNonNull(mapper);
193         if (!isPresent())
194             return empty();
195         else {
196             return DbOptional.ofNullable(mapper.apply(value));
197         }
198     }
199
200     /**
201      * If a value is present, apply the provided {@code Optional}-bearing
202      * mapping function to it, return that result, otherwise return an empty
203      * {@code Optional}.  This method is similar to {@link #map(Function)},
204      * but the provided mapper is one whose result is already an {@code Optional},
205      * and if invoked, {@code flatMap} does not wrap it with an additional
206      * {@code Optional}.
207      *
208      * @param <U> The type parameter to the {@code Optional} returned by
209      * @param mapper a mapping function to apply to the value, if present
210      *           the mapping function
211      * @return the result of applying an {@code Optional}-bearing mapping
212      * function to the value of this {@code Optional}, if a value is present,
213      * otherwise an empty {@code Optional}
214      * @throws DatabaseException 
215      * @throws NullPointerException if the mapping function is null or returns
216      * a null result
217      */
218     public<U> DbOptional<U> flatMap(DbFunction<? super T, DbOptional<U>> mapper) throws DatabaseException {
219         Objects.requireNonNull(mapper);
220         if (!isPresent())
221             return empty();
222         else {
223             return Objects.requireNonNull(mapper.apply(value));
224         }
225     }
226
227     /**
228      * Return the value if present, otherwise return {@code other}.
229      *
230      * @param other the value to be returned if there is no value present, may
231      * be null
232      * @return the value, if present, otherwise {@code other}
233      */
234     public T orElse(T other) {
235         return value != null ? value : other;
236     }
237
238     /**
239      * Return the value if present, otherwise invoke {@code other} and return
240      * the result of that invocation.
241      *
242      * @param other a {@code Supplier} whose result is returned if no value
243      * is present
244      * @return the value if present otherwise the result of {@code other.get()}
245      * @throws DatabaseException 
246      * @throws NullPointerException if value is not present and {@code other} is
247      * null
248      */
249     public T orElseGet(DbSupplier<? extends T> other) throws DatabaseException {
250         return value != null ? value : other.get();
251     }
252
253     /**
254      * Return the contained value, if present, otherwise throw an exception
255      * to be created by the provided supplier.
256      *
257      * @apiNote A method reference to the exception constructor with an empty
258      * argument list can be used as the supplier. For example,
259      * {@code IllegalStateException::new}
260      *
261      * @param <X> Type of the exception to be thrown
262      * @param exceptionSupplier The supplier which will return the exception to
263      * be thrown
264      * @return the present value
265      * @throws X if there is no value present
266      * @throws DatabaseException 
267      * @throws NullPointerException if no value is present and
268      * {@code exceptionSupplier} is null
269      */
270     public <X extends Throwable> T orElseThrow(DbSupplier<? extends X> exceptionSupplier) throws X, DatabaseException {
271         if (value != null) {
272             return value;
273         } else {
274             throw exceptionSupplier.get();
275         }
276     }
277
278     /**
279      * Indicates whether some other object is "equal to" this Optional. The
280      * other object is considered equal if:
281      * <ul>
282      * <li>it is also an {@code Optional} and;
283      * <li>both instances have no value present or;
284      * <li>the present values are "equal to" each other via {@code equals()}.
285      * </ul>
286      *
287      * @param obj an object to be tested for equality
288      * @return {code true} if the other object is "equal to" this object
289      * otherwise {@code false}
290      */
291     @Override
292     public boolean equals(Object obj) {
293         if (this == obj) {
294             return true;
295         }
296
297         if (!(obj instanceof DbOptional)) {
298             return false;
299         }
300
301         DbOptional<?> other = (DbOptional<?>) obj;
302         return Objects.equals(value, other.value);
303     }
304
305     /**
306      * Returns the hash code value of the present value, if any, or 0 (zero) if
307      * no value is present.
308      *
309      * @return hash code value of the present value or 0 if no value is present
310      */
311     @Override
312     public int hashCode() {
313         return Objects.hashCode(value);
314     }
315
316     /**
317      * Returns a non-empty string representation of this Optional suitable for
318      * debugging. The exact presentation format is unspecified and may vary
319      * between implementations and versions.
320      *
321      * @implSpec If a value is present the result must include its string
322      * representation in the result. Empty and present DbOptionals must be
323      * unambiguously differentiable.
324      *
325      * @return the string representation of this instance
326      */
327     @Override
328     public String toString() {
329         return value != null
330             ? String.format("DbOptional[%s]", value)
331             : "DbOptional.empty";
332     }
333 }