Make Variant/CheckedValue subclassable
[yangtools.git] / common / concepts / src / main / java / org / opendaylight / yangtools / concepts / CheckedValue.java
1 /*
2  * Copyright (c) 2018 Pantheon Technologies, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.concepts;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.util.concurrent.FluentFuture;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.SettableFuture;
17 import java.util.concurrent.CompletableFuture;
18 import java.util.function.Consumer;
19 import java.util.function.Function;
20 import java.util.function.Supplier;
21 import javax.annotation.concurrent.ThreadSafe;
22 import org.eclipse.jdt.annotation.NonNullByDefault;
23 import org.eclipse.jdt.annotation.Nullable;
24
25 /**
26  * Utility holder similar to {@link java.util.Optional}, except the empty case contains an Exception, which should be
27  * reported, for example via throwing it. It provides analogous methods such as {@link #isPresent()},
28  * {@link #ifPresent(Consumer)}, {@link #get()}, {@link #orElse(Object)}, {@link #orElseGet(Supplier)},
29  * {@link #orElseThrow(Function)}.
30  *
31  * @param <T> Value type
32  * @param <E> Exception type
33  * @author Robert Varga
34  */
35 @Beta
36 @NonNullByDefault
37 @ThreadSafe
38 public class CheckedValue<T, E extends Exception> extends Variant<T, E> {
39     protected CheckedValue(final T value) {
40         super(value);
41     }
42
43     protected CheckedValue(final E violation, final @Nullable Void dummy) {
44         super(violation, dummy);
45     }
46
47     /**
48      * Create a new instance containing an {@link Exception}.
49      *
50      * @param cause Throwable
51      * @return A new instance
52      * @throws NullPointerException if {@code cause} is null
53      */
54     public static <T, E extends Exception> CheckedValue<T, E> ofException(final E cause) {
55         return new CheckedValue<>(cause, null);
56     }
57
58     /**
59      * Create a new instance containing specified value.
60      *
61      * @param value Value
62      * @return A new instance
63      * @throws NullPointerException if {@code value} is null
64      */
65     public static <T, E extends Exception> CheckedValue<T, E> ofValue(final T value) {
66         return new CheckedValue<>(value);
67     }
68
69     /**
70      * Convert a Variant into a {@link CheckedValue}, converting the second value into an exception.
71      *
72      * @param variant Input variant
73      * @return Resulting {@link CheckedValue}
74      */
75     public static <T, U, E extends Exception> CheckedValue<T, E> ofVariant(final Variant<T, U> variant,
76             final Function<U, E> mapper) {
77         requireNonNull(mapper);
78         return variant.isFirst() ? new CheckedValue(variant.first())
79                 : new CheckedValue(mapper.apply(variant.second()), null);
80     }
81
82     /**
83      * Return the contained value if {@link #isPresent()} would return true, throws {@link IllegalStateException}
84      * otherwise.
85      *
86      * @return Contained value
87      * @throws IllegalStateException if an error string is present.
88      */
89     public final T get() {
90         if (isFirst()) {
91             return first();
92         }
93         throw new IllegalStateException("Value is not present", second());
94     }
95
96     /**
97      * Return the contained error string if {@link #isPresent()} would return false, throws
98      * {@link IllegalStateException} otherwise.
99      *
100      * @return Throwable which was used to instantiate this object, or absent if it was instantiated using an error
101      *         string.
102      * @throws IllegalStateException if a value is present.
103      */
104     public final E getException() {
105         if (isSecond()) {
106             return second();
107         }
108         throw new IllegalStateException("Value " + first() + " is present");
109     }
110
111     /**
112      * Return true if a value is present.
113      *
114      * @return True if a value is present.
115      */
116     public final boolean isPresent() {
117         return isFirst();
118     }
119
120     /**
121      * If a value is present, invoke the specified consumer with the value, otherwise do nothing.
122      *
123      * @param consumer block to be executed if a value is present
124      * @throws NullPointerException if value is present and {@code consumer} is null
125      */
126     public final void ifPresent(final Consumer<? super T> consumer) {
127         if (isFirst()) {
128             consumer.accept(first());
129         }
130     }
131
132     @SuppressWarnings("unchecked")
133     public <U> CheckedValue<U, E> map(final Function<? super T, U> mapper) {
134         requireNonNull(mapper);
135         return isFirst() ? new CheckedValue<>(mapper.apply(first())) : (CheckedValue<U, E>) this;
136     }
137
138     @SuppressWarnings("unchecked")
139     public <X extends Exception> CheckedValue<T, X> mapException(final Function<? super E, X> mapper) {
140         requireNonNull(mapper);
141         if (isFirst()) {
142             return (CheckedValue<T, X>) this;
143         }
144         return new CheckedValue<>(mapper.apply(second()), null);
145     }
146
147
148     @SuppressWarnings("unchecked")
149     public <U> CheckedValue<U, E> flatMap(final Function<? super T, CheckedValue<U, E>> mapper) {
150         requireNonNull(mapper);
151         return isFirst() ? requireNonNull(mapper.apply(first())) : (CheckedValue<U, E>) this;
152     }
153
154     /**
155      * Return contained value if present, otherwise return supplied value.
156      *
157      * @param other Replacement value
158      * @return Contained value or {code other}
159      */
160     public final T orElse(final T other) {
161         return isFirst() ? first() : other;
162     }
163
164     /**
165      * Return contained value if present, otherwise return the value produced by a supplier.
166      *
167      * @param supplier Replacement value supplier
168      * @return Contained value or supplier's value
169      * @throws NullPointerException if {@code supplier} is null
170      */
171     public final T orElseGet(final Supplier<T> supplier) {
172         requireNonNull(supplier);
173         return isFirst() ? first() : supplier.get();
174     }
175
176     /**
177      * Return contained value if present or throw the exception alternative.
178      *
179      * @return Contained value
180      * @throws E When there is no contained value
181      */
182     public final <X extends Throwable> T orElseThrow() throws E {
183         if (isFirst()) {
184             return first();
185         }
186         throw second();
187     }
188
189     /**
190      * Return contained value if present or throw the exception alternative mapped through provided mapper.
191      *
192      * @param exceptionMapper Exception mapper
193      * @return Contained value
194      * @throws NullPointerException if {@code exceptionMapper} is null
195      * @throws X When there is no contained value
196      */
197     public final <X extends Throwable> T orElseThrow(final Function<E, X> exceptionMapper) throws X {
198         requireNonNull(exceptionMapper);
199         if (isFirst()) {
200             return first();
201         }
202         throw exceptionMapper.apply(second());
203     }
204
205     /**
206      * Return contained value if present or throw the exception supplied by supplier.
207      *
208      * @param supplier Exception supplier
209      * @return Contained value
210      * @throws NullPointerException if {@code exceptionMapper} is null
211      * @throws X When there is no contained value
212      */
213     public final <X extends Throwable> T orElseThrow(final Supplier<X> supplier) throws X {
214         requireNonNull(supplier);
215         if (isFirst()) {
216             return first();
217         }
218         throw supplier.get();
219     }
220
221     /**
222      * Complete target {@link CompletableFuture} either successfully or exceptionally based on the state of this object.
223      *
224      * @param future Future to complete
225      * @return True if this call has transitioned the future to a completed state, false otherwise.
226      * @throws NullPointerException if {code future} is null
227      */
228     public final boolean completeFuture(final CompletableFuture<T> future) {
229         return isFirst() ? future.complete(first()) : future.completeExceptionally(second());
230     }
231
232     /**
233      * Complete target {@link SettableFuture} either successfully or exceptionally based on the state of this object.
234      *
235      * @param future Future to complete
236      * @return True if this call has transitioned the future to a completed state, false otherwise.
237      * @throws NullPointerException if {code future} is null
238      */
239     public final boolean completeFuture(final SettableFuture<T> future) {
240         return isFirst() ? future.set(first()) : future.setException(second());
241     }
242
243     /**
244      * Transform this object into an immediately-completed {@link CompletableFuture}. The future will be successful
245      * if this object has a contained value or unsuccessful if this objects contains an exception.
246      *
247      * @return A {@link CompletableFuture}.
248      */
249     public final CompletableFuture<T> toCompletableFuture() {
250         if (isFirst()) {
251             return CompletableFuture.completedFuture(first());
252         }
253         // FIXME: Java 9: use CompletableFuture.failedFuture()
254         final CompletableFuture<T> future = new CompletableFuture<>();
255         future.completeExceptionally(second());
256         return future;
257     }
258
259     /**
260      * Transform this object into an immediately-completed {@link FluentFuture}. The future will be successful
261      * if this object has a contained value or unsuccessful if this objects contains an exception.
262      *
263      * @return A {@link FluentFuture}.
264      */
265     public final FluentFuture<T> toFluentFuture() {
266         final ListenableFuture<T> future;
267         if (isFirst()) {
268             future = Futures.immediateFuture(first());
269         } else {
270             future = Futures.immediateFailedFuture(second());
271         }
272         return FluentFuture.from(future);
273     }
274 }