001/*-
002 * #%L
003 * HAPI FHIR - Core Library
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.util.monad;
021
022import java.util.Optional;
023import java.util.function.Consumer;
024import java.util.function.Function;
025import java.util.function.Supplier;
026import java.util.stream.Stream;
027
028import static com.google.common.base.Preconditions.checkArgument;
029import static com.google.common.base.Preconditions.checkNotNull;
030import static com.google.common.base.Preconditions.checkState;
031
032/**
033 * Represents a value of one of two possible types (a disjoint union). An instance of Either is either an instance of
034 * Left or Right, only one of which can be present at a time.
035 *
036 * This type is right-biased, meaning that operations like map and flatMap will apply to the right value if it is present.
037 * By convention, the preferred type for a given context should be the right value.
038 *
039 * By convention, the left value is used for error handling, when used in a context where one of the values is an error or exception.
040 *
041 * While this class does provide methods for accessing the left and right values, it is generally recommended to use
042 * the map, flatMap, and fold methods to work with the values. These methods are more idiomatic and less error-prone, and they
043 * are less likely to result in IllegalStateExceptions.
044 *
045 * @param <L> the left type
046 * @param <R> the right type
047 */
048public class Either<L, R> {
049
050        private final L left;
051        private final R right;
052
053        /*
054         * Private constructor. Use static factory methods for instantiation. See {@link Eithers}.
055         */
056        Either(L left, R right) {
057                checkArgument(left == null ^ right == null, "left and right are mutually exclusive");
058                this.left = left;
059                this.right = right;
060        }
061
062        /**
063         * Swaps the left and right types. The value is unchanged.
064         *
065         * @return a new Either instance with the left and right types swapped
066         */
067        public Either<R, L> swap() {
068                if (isRight()) {
069                        return Eithers.forLeft(right);
070                }
071
072                return Eithers.forRight(left);
073        }
074
075        /**
076         * Returns true if the value is the left type, false if it is right.
077         *
078         * @return true if left is present
079         */
080        public boolean isLeft() {
081                return left != null;
082        }
083
084        /**
085         * Returns true if the value is the right type, false if it is left.
086         *
087         * @return true is right is present
088         */
089        public boolean isRight() {
090                return right != null;
091        }
092
093        /**
094         * Returns the left value if it is present, otherwise throws an IllegalStateException.
095         *
096         * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function)}
097         *
098         * @throws IllegalStateException if the left value is not present
099         * @return the left value
100         */
101        public L leftOrThrow() {
102                checkState(isLeft());
103                return left;
104        }
105
106        /**
107         * Returns the right value if it is present, otherwise throws an IllegalStateException.
108         *
109         * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function)}
110         * Alternatively, use {@link #orElse(Object)} or {@link #orElseGet(Supplier)}
111         *
112         * @throws IllegalStateException if the right value is not present
113         * @return the right value
114         */
115        public R rightOrThrow() {
116                checkState(isRight());
117                return right;
118        }
119
120        /**
121         * Alias for {@link #rightOrThrow()}.
122         *
123         * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function)}
124         * Alternatively, use {@link #orElse(Object)} or {@link #orElseGet(Supplier)}
125         *
126         * @throws IllegalStateException if the right value is not present
127         * @return the right value
128         */
129        public R getOrThrow() {
130                return rightOrThrow();
131        }
132
133        /**
134         * Returns the right value if it is present, otherwise returns the provided default value.
135         *
136         * @param defaultValue the value to return if the right value is not present
137         * @return the right value if it is present, otherwise the default value
138         */
139        public R orElse(R defaultValue) {
140                if (isRight()) {
141                        return right;
142                } else {
143                        return defaultValue;
144                }
145        }
146
147        /**
148         * Returns the right value if it is present, otherwise returns the result of the provided supplier.
149         * @param defaultSupplier the supplier to provide a default value if the right value is not present
150         * @return the right value if it is present, otherwise the result of the supplier
151         */
152        public R orElseGet(Supplier<R> defaultSupplier) {
153                if (isRight()) {
154                        return right;
155                } else {
156                        return defaultSupplier.get();
157                }
158        }
159
160        /**
161         * Executes the provided consumer if the right value is present
162         * @param forRight the consumer to execute if the right value is present
163         */
164        public void forEach(Consumer<? super R> forRight) {
165                checkNotNull(forRight);
166                if (isRight()) {
167                        forRight.accept(right);
168                }
169        }
170
171        /**
172         * Executes the provided consumer if the right value is present, returning the Either unchanged.
173         *
174         * @param forRight the consumer to execute if the right value is present
175         * @return the Either unchanged
176         */
177        public Either<L, R> peek(Consumer<? super R> forRight) {
178                checkNotNull(forRight);
179                if (isRight()) {
180                        forRight.accept(right);
181                }
182
183                return this;
184        }
185
186        /**
187         * Maps the right value to a new value using the provided function.
188         *
189         * If the right value is not present, returns the left value unchanged.
190         *
191         * @param <T> the new right type
192         * @param mapRight the function to map the right value to a new value
193         * @return the Either with the right value mapped to a new value, or the left value unchanged
194         */
195        @SuppressWarnings("unchecked")
196        public <T> Either<L, T> map(Function<? super R, ? extends T> mapRight) {
197                checkNotNull(mapRight);
198                if (isLeft()) {
199                        return (Either<L, T>) this;
200                }
201
202                return Eithers.forRight(mapRight.apply(right));
203        }
204
205        /**
206         * Maps the right value to a new Either using the provided function.
207         *
208         * If the right value is not present, returns the left value unchanged.
209         *
210         * @param <T> the new right type
211         * @param flatMapRight the function to map the right value to a new Either
212         * @return a new Either instance with the right value mapped to a new Either, or the left value unchanged
213         */
214        @SuppressWarnings("unchecked")
215        public <T> Either<L, T> flatMap(Function<? super R, ? extends Either<L, ? extends T>> flatMapRight) {
216                checkNotNull(flatMapRight);
217                if (isLeft()) {
218                        return (Either<L, T>) this;
219                }
220
221                return (Either<L, T>) flatMapRight.apply(right);
222        }
223
224        /**
225         * Maps the left or right value to a new value using the provided functions.
226         *
227         * The function is sometimes known as "reduce".
228         *
229         * If the right value is present, the foldRight function is applied to the right value.
230         * If the left value is present, the foldLeft function is applied to the left value.
231         *
232         * @param <T> the type of the new value
233         * @param foldLeft the function to map the left value to a new value, if present
234         * @param foldRight the function to map the right value to a new value, if present
235         * @return the new value
236         */
237        public <T> T fold(Function<? super L, ? extends T> foldLeft, Function<? super R, ? extends T> foldRight) {
238                checkNotNull(foldLeft);
239                checkNotNull(foldRight);
240                if (isRight()) {
241                        return foldRight.apply(right);
242                } else {
243                        return foldLeft.apply(left);
244                }
245        }
246
247        /**
248         * Transforms the Either to a new value using the provided function. The function is applied to the entire Either,
249         * regardless of which value is present. This is in contrast to the map, flatMap, and fold functions, which only apply
250         * when the right value is present.
251         *
252         * @param <U> the type of the new value
253         * @param transform the function to transform the Either to a new value
254         * @return the new value
255         */
256        public <U> U transform(Function<? super Either<? super L, ? super R>, ? extends U> transform) {
257                return transform.apply(this);
258        }
259
260        /**
261         * Returns a stream of the right value if present, otherwise an empty stream.
262         * Mainly useful for converting to a stream for further processing with standard Java
263         * APIs like Stream.map, Stream.filter, etc.
264         *
265         * @return the stream of the right value if present
266         */
267        public Stream<R> stream() {
268                if (isRight()) {
269                        return Stream.of(right);
270                } else {
271                        return Stream.of();
272                }
273        }
274
275        /**
276         * Returns an Optional containing the right value if present, otherwise an empty Optional.
277         * Mainly useful for converting to an Optional for further processing with standard Java
278         * APIs, like Optional.map, Optional.filter, etc.
279         *
280         * @return an Optional containing the right value if present
281         */
282        public Optional<R> optional() {
283                if (isRight()) {
284                        return Optional.of(right);
285                }
286
287                return Optional.empty();
288        }
289
290        @Override
291        public boolean equals(Object obj) {
292                if (obj instanceof Either<?, ?>) {
293                        Either<?, ?> other = (Either<?, ?>) obj;
294                        return (this.left == other.left && this.right == other.right)
295                                        || (this.left != null && other.left != null && this.left.equals(other.left))
296                                        || (this.right != null && other.right != null && this.right.equals(other.right));
297                }
298
299                return false;
300        }
301
302        @Override
303        public int hashCode() {
304                if (this.left != null) {
305                        return this.left.hashCode();
306                }
307
308                return this.right.hashCode();
309        }
310}