1 /*
2 Copyright 2011 Atlassian
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 package io.atlassian.fugue;
17
18 import java.util.Collections;
19 import java.util.function.Function;
20 import java.util.function.Predicate;
21 import java.util.function.Supplier;
22 import java.util.stream.Collector;
23 import java.util.stream.Collectors;
24
25 import static io.atlassian.fugue.Iterables.collect;
26 import static io.atlassian.fugue.Iterables.map;
27 import static io.atlassian.fugue.Suppliers.compose;
28 import static io.atlassian.fugue.Suppliers.ofInstance;
29
30 /**
31 * Utility functions for Eithers.
32 *
33 * @since 1.2
34 */
35 public class Eithers {
36
37 // /CLOVER:OFF
38
39 private Eithers() {}
40
41 // /CLOVER:ON
42
43 /**
44 * Extracts an object from an Either, regardless of the side in which it is
45 * stored, provided both sides contain the same type. This method will never
46 * return null.
47 *
48 * @param <T> the type for both the LHS and the RHS
49 * @param either use whichever side holds the value to return
50 * @return the value from whichever side holds it
51 */
52 public static <T> T merge(final Either<T, T> either) {
53 if (either.isLeft()) {
54 return either.left().get();
55 }
56 return either.right().get();
57 }
58
59 /**
60 * Creates an Either based on a boolean expression. If predicate is true, a
61 * Right will be returned containing the supplied right value; if it is false,
62 * a Left will be returned containing the supplied left value.
63 *
64 * @param <L> the LHS type
65 * @param <R> the RHS type
66 * @param predicate if predicate is true, a Right will be returned if it is
67 * false, a Left will be returned containing the supplied left value.
68 * @param left the LHS value
69 * @param right the RHS value
70 * @return an either with the appropriately selected value
71 */
72 public static <L, R> Either<L, R> cond(final boolean predicate, final L left, final R right) {
73 return predicate ? Either.<L, R> right(right) : Either.<L, R> left(left);
74 }
75
76 /**
77 * Simplifies extracting a value or throwing a checked exception from an
78 * Either.
79 *
80 * @param <A> the value type
81 * @param <X> the exception type
82 * @param either to extract from
83 * @return the value from the RHS
84 * @throws X the exception on the LHS
85 */
86 public static <X extends Exception, A> A getOrThrow(final Either<X, A> either) throws X {
87 if (either.isLeft()) {
88 throw either.left().get();
89 }
90 return either.right().get();
91 }
92
93 /**
94 * A predicate that tests if the supplied either is a left.
95 *
96 * @param <L> the LHS type
97 * @param <R> the RHS type
98 * @return the predicate testing left-hand-sidedness
99 */
100 public static <L, R> Predicate<Either<L, R>> isLeft() {
101 return Either::isLeft;
102 }
103
104 /**
105 * A predicate that tests if the supplied either is a right.
106 *
107 * @param <L> the LHS type
108 * @param <R> the RHS type
109 * @return the predicate testing right-hand-sidedness
110 */
111 public static <L, R> Predicate<Either<L, R>> isRight() {
112 return Either::isRight;
113 }
114
115 /**
116 * A function that maps an either to an option of its left type. The Function
117 * will return a defined {@link io.atlassian.fugue.Option} containing the
118 * either's left value if {Either#isLeft()} is true, an undefined
119 * {@link io.atlassian.fugue.Option} otherwise.
120 *
121 * @param <L> the LHS type
122 * @param <R> the RHS type
123 * @return the function returning a defined option for left-hand-sided eithers
124 */
125 public static <L, R> Function<Either<L, R>, Option<L>> leftMapper() {
126 return either -> either.left().toOption();
127 }
128
129 /**
130 * A function that maps an either to an option of its right type. The Function
131 * will return a defined {@link io.atlassian.fugue.Option} containing the
132 * either's right value if {Either#isRight()} is true, an undefined
133 * {@link io.atlassian.fugue.Option} otherwise.
134 *
135 * @param <L> the LHS type
136 * @param <R> the RHS type
137 * @return the function returning a defined option for right-hand-sided
138 * eithers
139 */
140 public static <L, R> Function<Either<L, R>, Option<R>> rightMapper() {
141 return either -> either.right().toOption();
142 }
143
144 /**
145 * Function to convert from an value to a
146 * {@link io.atlassian.fugue.Either.Left} containing that value.
147 *
148 * @param <L> left type.
149 * @param <R> right type.
150 * @return a {@link java.util.function.Function} returning a
151 * {@link io.atlassian.fugue.Either.Left}.
152 */
153 public static <L, R> Function<L, Either<L, R>> toLeft() {
154 return Either::left;
155 }
156
157 /**
158 * Function to convert from a value to a
159 * {@link io.atlassian.fugue.Either.Left} containing that value. Allows
160 * hinting the correct types.
161 *
162 * @param leftType expected left type.
163 * @param rightType expected right type.
164 * @param <L> left type.
165 * @param <R> right type.
166 * @return a {@link java.util.function.Function} returning a
167 * {@link io.atlassian.fugue.Either.Left}.
168 */
169 public static <L, R> Function<L, Either<L, R>> toLeft(final Class<L> leftType, final Class<R> rightType) {
170 return Eithers.toLeft();
171 }
172
173 /**
174 * Supplier returning a {@link io.atlassian.fugue.Either.Left}.
175 *
176 * @param l value to return inside the left.
177 * @param <L> left type.
178 * @param <R> right type.
179 * @return a {@link java.util.function.Supplier} returning a
180 * {@link io.atlassian.fugue.Either.Left}..
181 */
182 public static <L, R> Supplier<Either<L, R>> toLeft(final L l) {
183 return compose(Eithers.<L, R> toLeft(), ofInstance(l));
184 }
185
186 /**
187 * Supplier returning a {@link io.atlassian.fugue.Either.Left}. Allows hinting
188 * the correct right type.
189 *
190 * @param l value to return inside the left.
191 * @param rightType type hint for the right type of the either.
192 * @param <L> left type.
193 * @param <R> right type.
194 * @return a {@link java.util.function.Supplier} returning a
195 * {@link io.atlassian.fugue.Either.Left}.
196 */
197 public static <L, R> Supplier<Either<L, R>> toLeft(final L l, final Class<R> rightType) {
198 return Eithers.toLeft(l);
199 }
200
201 /**
202 * Function to convert from an value to a
203 * {@link io.atlassian.fugue.Either.Right}. Allows hinting the correct types.
204 *
205 * @param <L> left type.
206 * @param <R> right type.
207 * @return a {@link java.util.function.Function} returning a
208 * {@link io.atlassian.fugue.Either.Right}.
209 */
210 public static <L, R> Function<R, Either<L, R>> toRight() {
211 return Either::right;
212 }
213
214 /**
215 * Function to convert from a value to a
216 * {@link io.atlassian.fugue.Either.Right} containing that value. Allows
217 * hinting the correct types.
218 *
219 * @param leftType expected left type.
220 * @param rightType expected right type.
221 * @param <L> left type.
222 * @param <R> right type.
223 * @return a {@link java.util.function.Function} returning a
224 * {@link io.atlassian.fugue.Either.Right}.
225 */
226 public static <L, R> Function<R, Either<L, R>> toRight(final Class<L> leftType, final Class<R> rightType) {
227 return Eithers.toRight();
228 }
229
230 /**
231 * Supplier returning a {@link io.atlassian.fugue.Either.Right}.
232 *
233 * @param r value to return inside the right.
234 * @param <L> left type.
235 * @param <R> right type.
236 * @return a {@link java.util.function.Supplier} returning a
237 * {@link io.atlassian.fugue.Either.Right}..
238 */
239 public static <L, R> Supplier<Either<L, R>> toRight(final R r) {
240 return compose(Eithers.<L, R> toRight(), ofInstance(r));
241 }
242
243 /**
244 * Supplier returning a {@link io.atlassian.fugue.Either.Right}. Allows
245 * hinting the correct right type.
246 *
247 * @param r value to return inside the right.
248 * @param leftType type hint for the left type of the either.
249 * @param <L> left type.
250 * @param <R> right type.
251 * @return a {@link java.util.function.Supplier} returning a
252 * {@link io.atlassian.fugue.Either.Right}.
253 */
254 public static <L, R> Supplier<Either<L, R>> toRight(final Class<L> leftType, final R r) {
255 return Eithers.toRight(r);
256 }
257
258 /**
259 * Upcasts an {@link Either either} of left type L to an either of left type
260 * LL, which is a super type of L, keeping the right type unchanged.
261 *
262 * @param e the source either
263 * @param <L> the base type to upcast
264 * @param <LL> the super type of the contained left type
265 * @param <R> the contained right type
266 * @return an either of left type LL and right type R
267 * @since 2.0
268 */
269 public static <LL, L extends LL, R> Either<LL, R> upcastLeft(final Either<L, R> e) {
270 return e.left().map(Functions.<LL> identity());
271 }
272
273 /**
274 * Upcasts an {@link Either either} of right type R to an either of right type
275 * RR, which is a super type of R, keeping the left type unchanged.
276 *
277 * @param e the source either
278 * @param <L> the contained left type
279 * @param <R> the base type to upcast
280 * @param <RR> the super type of the contained right type
281 * @return an either of left type L and right type RR
282 * @since 2.0
283 */
284 public static <L, RR, R extends RR> Either<L, RR> upcastRight(final Either<L, R> e) {
285 return e.right().map(Functions.<RR> identity());
286 }
287
288 /**
289 * Takes an {@link java.lang.Iterable} of {@link Either eithers}, and collects
290 * the left values of every either which has a left value
291 *
292 * @param <L> the LHS type
293 * @param <R> the RHS type
294 * @param it iterable of eithers to filter and transform from
295 * @return the left values contained in the contents of it
296 */
297 public static <L, R> Iterable<L> filterLeft(final Iterable<Either<L, R>> it) {
298 return collect(it, Eithers.<L, R> leftMapper());
299 }
300
301 /**
302 * Takes an {@link java.lang.Iterable} of {@link Either eithers}, and collects
303 * the right values of every either which has a left value
304 *
305 * @param <L> the LHS type
306 * @param <R> the RHS type
307 * @param it iterable of eithers to filter and transform from
308 * @return the right values contained in the contents of it
309 */
310 public static <L, R> Iterable<R> filterRight(final Iterable<Either<L, R>> it) {
311 return Options.flatten(map(it, Eithers.<L, R> rightMapper()));
312 }
313
314 /**
315 * Collect the right values if there are only rights, otherwise return the
316 * first left encountered.
317 *
318 * @param <L> the LHS type
319 * @param <R> the RHS type
320 * @param eithers an Iterable of either values
321 * @return either the iterable of right values, or the first left encountered.
322 */
323 public static <L, R> Either<L, Iterable<R>> sequenceRight(final Iterable<Either<L, R>> eithers) {
324 return sequenceRight(eithers, Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
325 }
326
327 /**
328 * Collect the right values if there are only rights, otherwise return the
329 * first left encountered.
330 *
331 * @param <L> the LHS type
332 * @param <R> the RHS type
333 * @param <A> The intermediate accumulator type
334 * @param <C> The result type
335 * @param eithers an Iterable of either values
336 * @param collector result collector
337 * @since 4.6.0
338 * @return either the iterable of right values, or the first left encountered.
339 */
340 public static <L, R, A, C> Either<L, C> sequenceRight(final Iterable<Either<L, R>> eithers, final Collector<R, A, C> collector) {
341 final A accumulator = collector.supplier().get();
342 for (final Either<L, R> e : eithers) {
343 if (e.isLeft()) {
344 return e.left().as();
345 }
346 collector.accumulator().accept(accumulator, e.right().get());
347 }
348 return Either.right(collector.finisher().apply(accumulator));
349 }
350
351 /**
352 * Collect the left values if there are only lefts, otherwise return the first
353 * right encountered.
354 *
355 * @param <L> the LHS type
356 * @param <R> the RHS type
357 * @param eithers an Iterable of either values
358 * @return either the iterable of left values, or the first right encountered.
359 */
360 public static <L, R> Either<Iterable<L>, R> sequenceLeft(final Iterable<Either<L, R>> eithers) {
361 return sequenceLeft(eithers, Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
362 }
363
364 /**
365 * Collect the left values if there are only lefts, otherwise return the first
366 * right encountered.
367 *
368 * @param <L> the LHS type
369 * @param <R> the RHS type
370 * @param <A> The intermediate accumulator type
371 * @param <C> The result type
372 * @param eithers an Iterable of either values
373 * @param collector result collector
374 * @since 4.6.0
375 * @return either the iterable of left values, or the first right encountered.
376 */
377 public static <L, R, A, C> Either<C, R> sequenceLeft(final Iterable<Either<L, R>> eithers, final Collector<L, A, C> collector) {
378 final A accumulator = collector.supplier().get();
379 for (final Either<L, R> e : eithers) {
380 if (e.isRight()) {
381 return e.right().as();
382 }
383 collector.accumulator().accept(accumulator, e.left().get());
384 }
385 return Either.left(collector.finisher().apply(accumulator));
386 }
387 }