
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}