1 /* 2 * Copyright 2022 Google LLC 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 com.google.android.libraries.mobiledatadownload.internal.util; 17 18 import java.util.ArrayList; 19 import java.util.Arrays; 20 import java.util.Collections; 21 import java.util.Comparator; 22 import java.util.List; 23 import org.checkerframework.checker.nullness.qual.Nullable; 24 25 /** An object that contains either one of the left field or right field, but not both. */ 26 public final class Either<A, B> { 27 28 private final boolean hasLeft; 29 private final @Nullable A left; 30 private final @Nullable B right; 31 Either(boolean hasLeft, @Nullable A left, @Nullable B right)32 private Either(boolean hasLeft, @Nullable A left, @Nullable B right) { 33 this.hasLeft = hasLeft; 34 this.left = left; 35 this.right = right; 36 } 37 makeLeft(A left)38 public static <A, B> Either<A, B> makeLeft(A left) { 39 return new Either<>(true, left, null); 40 } 41 makeRight(B right)42 public static <A, B> Either<A, B> makeRight(B right) { 43 return new Either<>(false, null, right); 44 } 45 hasLeft()46 public boolean hasLeft() { 47 return hasLeft; 48 } 49 hasRight()50 public boolean hasRight() { 51 return !hasLeft; 52 } 53 getLeft()54 public @Nullable A getLeft() { 55 if (!hasLeft()) { 56 throw new IllegalStateException("Either was not left"); 57 } 58 return left; 59 } 60 getRight()61 public @Nullable B getRight() { 62 if (!hasRight()) { 63 throw new IllegalStateException("Either was not right"); 64 } 65 return right; 66 } 67 68 @Override 69 @SuppressWarnings("unchecked") equals(@ullable Object obj)70 public boolean equals(@Nullable Object obj) { 71 if (!(obj instanceof Either)) { 72 return false; 73 } 74 Either<A, B> either = (Either<A, B>) obj; 75 if (hasLeft()) { 76 return either.hasLeft() && equals(getLeft(), either.getLeft()); 77 } else { 78 return either.hasRight() && equals(getRight(), either.getRight()); 79 } 80 } 81 82 /** Compares two List-based Either by (a copy of) their sorted inner List. */ sortedEquals( @ullable Either<List<A>, B> first, @Nullable Either<List<A>, B> second, Comparator<A> comparatorForSorting)83 public static <A, B> boolean sortedEquals( 84 @Nullable Either<List<A>, B> first, 85 @Nullable Either<List<A>, B> second, 86 Comparator<A> comparatorForSorting) { 87 // Only sort if the Lists will actually be compared. This uses final variable "left" rather than 88 // getLeft() (which "could" change between invocations) to satisfy the Nullness Checker. 89 if (first != null 90 && first.hasLeft() 91 && first.left != null 92 && second != null 93 && second.hasLeft() 94 && second.left != null) { 95 List<A> sortedFirstLeft = new ArrayList<>(first.left); 96 List<A> sortedSecondLeft = new ArrayList<>(second.left); 97 Collections.sort(sortedFirstLeft, comparatorForSorting); 98 Collections.sort(sortedSecondLeft, comparatorForSorting); 99 return sortedFirstLeft.equals(sortedSecondLeft); 100 } 101 102 return equals(first, second); 103 } 104 105 // Inline definition of Objects.equals() since it's not available on all API levels. equals(@ullable Object a, @Nullable Object b)106 public static boolean equals(@Nullable Object a, @Nullable Object b) { 107 return (a == b) || (a != null && a.equals(b)); 108 } 109 110 @Override hashCode()111 public int hashCode() { 112 return Arrays.hashCode(new Object[] {hasLeft, left, right}); 113 } 114 } 115