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