
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 three possible types (a disjoint union). An instance of Either3 is either an instance of 034 * Left, Middle, 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, middle, 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 value type 046 * @param <M> the middle value type 047 * @param <R> the right value type 048 */ 049public class Either3<L, M, R> { 050 051 private final L left; 052 private final M middle; 053 private final R right; 054 055 /* 056 * Private constructor. Use static factory methods for instantiation. See {@link Eithers}. 057 */ 058 Either3(L left, M middle, R right) { 059 checkArgument(left != null ^ middle != null ^ right != null, "left, middle, and right are mutually exclusive"); 060 this.left = left; 061 this.middle = middle; 062 this.right = right; 063 } 064 065 /** 066 * Swaps the position of the left and right types. The middle type is unchanged. 067 * The value is unchanged. 068 * 069 * @return a new Either3 with the left and right types swapped 070 */ 071 public Either3<R, M, L> swap() { 072 if (isRight()) { 073 return Eithers.forLeft3(right); 074 } else if (isMiddle()) { 075 return Eithers.forMiddle3(middle); 076 } else { 077 return Eithers.forRight3(left); 078 } 079 } 080 081 /** 082 * Rotates the types of the Either3 to the right. The right type becomes the left type, the left type becomes the middle type, 083 * and the middle type becomes the right type. The values are unchanged. 084 * 085 * @return new Either3 with the types rotated 086 */ 087 public Either3<R, L, M> rotate() { 088 if (isRight()) { 089 return Eithers.forLeft3(right); 090 } else if (isMiddle()) { 091 return Eithers.forRight3(middle); 092 } else { 093 return Eithers.forMiddle3(left); 094 } 095 } 096 097 /** 098 * Returns the left value. Throws an exception if the left value is not present. 099 * 100 * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function, Function)} 101 * 102 * @throws IllegalStateException if the left value is not present 103 * @return the left value 104 */ 105 public L leftOrThrow() { 106 checkState(isLeft()); 107 return left; 108 } 109 110 /** 111 * Returns the middle value. Throws an exception if the middle value is not present. 112 * 113 * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function, Function)} 114 * 115 * @throws IllegalStateException if the middle value is not present 116 * @return the middle value 117 */ 118 public M middleOrThrow() { 119 checkState(isMiddle()); 120 return middle; 121 } 122 123 /** 124 * Returns the right value. Throws an exception if the right value is not present. 125 * 126 * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function, Function)} 127 * Alternatively, use {@link #orElse(Object)} or {@link #orElseGet(Supplier)} 128 * 129 * @throws IllegalStateException if the right value is not present 130 * @return the right value 131 */ 132 public R rightOrThrow() { 133 checkState(isRight()); 134 return right; 135 } 136 137 /** 138 * Alias for {@link #rightOrThrow()}. 139 * 140 * It's generally preferred to pass functions to the Either using {@link #fold(Function, Function, Function)} 141 * Alternatively, use {@link #orElse(Object)} or {@link #orElseGet(Supplier)} 142 * 143 * @throws IllegalStateException if the right value is not present 144 * @return the right value 145 */ 146 public R getOrThrow() { 147 return rightOrThrow(); 148 } 149 150 /** 151 * Returns the right value if present, otherwise return the provided default value. 152 * 153 * @param defaultValue the value to return if the right value is not present 154 * @return the right value if present, otherwise the default value 155 */ 156 public R orElse(R defaultValue) { 157 if (isRight()) { 158 return right; 159 } else { 160 return defaultValue; 161 } 162 } 163 164 /** 165 * Returns the right value if present, otherwise return the result of the provided supplier. 166 * 167 * @param defaultSupplier the supplier to provide a default value if the right value is not present 168 * @return the right value if present, otherwise the result of the supplier 169 */ 170 public R orElseGet(Supplier<R> defaultSupplier) { 171 if (isRight()) { 172 return right; 173 } else { 174 return defaultSupplier.get(); 175 } 176 } 177 178 /** 179 * Returns true if the value is the left type. Otherwise, returns false. 180 * 181 * @return true if left is present 182 */ 183 public boolean isLeft() { 184 return left != null; 185 } 186 187 /** 188 * Returns true if value is the middle type. Otherwise, returns false. 189 * 190 * @return true if middle is present 191 */ 192 public boolean isMiddle() { 193 return middle != null; 194 } 195 196 /** 197 * Returns true if the value is the right type. Otherwise, returns false. 198 * 199 * @return true if right is present 200 */ 201 public boolean isRight() { 202 return right != null; 203 } 204 205 /** 206 * Executes the provided consumer if the right value is present. 207 * 208 * @param forRight the consumer to execute if the right value is present 209 */ 210 public void forEach(Consumer<? super R> forRight) { 211 checkNotNull(forRight); 212 if (isRight()) { 213 forRight.accept(right); 214 } 215 } 216 217 /** 218 * Executes the provided consumer if the right value is present, returning the Either3 unchanged. 219 * 220 * @param forRight the consumer to execute if the right value is present 221 * @return the Either3 unchanged 222 */ 223 public Either3<L, M, R> peek(Consumer<? super R> forRight) { 224 checkNotNull(forRight); 225 if (isRight()) { 226 forRight.accept(right); 227 } 228 229 return this; 230 } 231 232 /** 233 * Maps the right value to a new value using the provided function. 234 * 235 * If the right value is not present, returns the left or middle value unchanged. 236 * 237 * @param <T> the type of the new value 238 * @param mapRight the function to map the right value to a new value 239 * @return a new Either3 with the right value mapped to a new value, or the left or middle value 240 */ 241 public <T> Either3<L, M, T> map(Function<? super R, ? extends T> mapRight) { 242 checkNotNull(mapRight); 243 if (isRight()) { 244 return Eithers.forRight3(mapRight.apply(right)); 245 } 246 247 return propagate(); 248 } 249 250 /** 251 * Maps the Either to a new Either using the provided function. 252 * 253 * If the right value is present, the function is applied to the right value. 254 * 255 * If the right value is not present, the left or middle value is returned. 256 * 257 * @param <T> the type of the new right value 258 * @param flatMapRight the function to map the Either to a new Either 259 * @return a new Either3 with the right value mapped to a new Either, or the left or middle value 260 */ 261 public <T> Either3<L, M, T> flatMap(Function<? super R, ? extends Either3<L, M, ? extends T>> flatMapRight) { 262 checkNotNull(flatMapRight); 263 if (isRight()) { 264 return narrow(flatMapRight.apply(right)); 265 } 266 267 return propagate(); 268 } 269 270 /** 271 * Maps the left, middle, or right value to a new value using the provided functions. 272 * 273 * The function is sometimes known as "reduce". 274 * 275 * If the right value is present, the foldRight function is applied to the right value. 276 * If the middle value is present, the foldMiddle function is applied to the middle value. 277 * If the left value is present, the foldLeft function is applied to the left value. 278 * 279 * @param <T> the type of the new value 280 * @param foldLeft the function to map the left value to a new value, if present 281 * @param foldMiddle the function to map the middle value to a new value, if present 282 * @param foldRight the function to map the right value to a new value, if present 283 * @return the new value 284 */ 285 public <T> T fold( 286 Function<? super L, ? extends T> foldLeft, 287 Function<? super M, ? extends T> foldMiddle, 288 Function<? super R, ? extends T> foldRight) { 289 checkNotNull(foldLeft); 290 checkNotNull(foldMiddle); 291 checkNotNull(foldRight); 292 if (isRight()) { 293 return foldRight.apply(right); 294 } else if (isMiddle()) { 295 return foldMiddle.apply(middle); 296 } else { 297 return foldLeft.apply(left); 298 } 299 } 300 301 /** 302 * Transforms the Either to a new value using the provided function. The function is applied to the entire Either, 303 * regardless of which value is present. This is in contrast to the map, flatMap, and fold functions, which only apply 304 * when the right value is present. 305 * 306 * @param <U> the type of the new value 307 * @param transform the function to transform the Either to a new value 308 * @return the new value 309 */ 310 public <U> U transform(Function<? super Either3<? super L, ? super M, ? super R>, ? extends U> transform) { 311 return transform.apply(this); 312 } 313 314 /** 315 * Returns a stream of the right value if present, otherwise an empty stream. 316 * Mainly useful for converting to a stream for further processing with standard Java 317 * APIs like Stream.map, Stream.filter, etc. 318 * 319 * @return the stream of the right value if present 320 */ 321 public Stream<R> stream() { 322 if (isRight()) { 323 return Stream.of(right); 324 } else { 325 return Stream.of(); 326 } 327 } 328 329 /** 330 * Returns an Optional containing the right value if present, otherwise an empty Optional. 331 * Mainly useful for converting to an Optional for further processing with standard Java 332 * APIs, like Optional.map, Optional.filter, etc. 333 * 334 * @return an Optional containing the right value if present 335 */ 336 public Optional<R> optional() { 337 if (isRight()) { 338 return Optional.of(right); 339 } 340 341 return Optional.empty(); 342 } 343 344 @SuppressWarnings("unchecked") 345 protected <T> Either3<L, M, T> narrow(Either3<L, M, ? extends T> wide) { 346 return (Either3<L, M, T>) wide; 347 } 348 349 @SuppressWarnings("unchecked") 350 protected <T> Either3<L, M, T> propagate() { 351 return (Either3<L, M, T>) this; 352 } 353 354 @Override 355 public boolean equals(Object obj) { 356 if (obj instanceof Either3<?, ?, ?>) { 357 Either3<?, ?, ?> other = (Either3<?, ?, ?>) obj; 358 return (this.left == other.left && this.right == other.right && this.middle == other.middle) 359 || (this.left != null && other.left != null && this.left.equals(other.left)) 360 || (this.middle != null && other.middle != null && this.middle.equals(other.middle)) 361 || (this.right != null && other.right != null && this.right.equals(other.right)); 362 } 363 364 return false; 365 } 366 367 @Override 368 public int hashCode() { 369 if (this.left != null) { 370 return this.left.hashCode(); 371 } 372 373 if (this.middle != null) { 374 return this.middle.hashCode(); 375 } 376 377 return this.right.hashCode(); 378 } 379}