blob: e80f79dea96f08ff9b8faeb9e38a45bf25c97ad4 [file] [log] [blame]
/*
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.libraries.mobiledatadownload.internal.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
/** An object that contains either one of the left field or right field, but not both. */
public final class Either<A, B> {
private final boolean hasLeft;
private final @Nullable A left;
private final @Nullable B right;
private Either(boolean hasLeft, @Nullable A left, @Nullable B right) {
this.hasLeft = hasLeft;
this.left = left;
this.right = right;
}
public static <A, B> Either<A, B> makeLeft(A left) {
return new Either<>(true, left, null);
}
public static <A, B> Either<A, B> makeRight(B right) {
return new Either<>(false, null, right);
}
public boolean hasLeft() {
return hasLeft;
}
public boolean hasRight() {
return !hasLeft;
}
public @Nullable A getLeft() {
if (!hasLeft()) {
throw new IllegalStateException("Either was not left");
}
return left;
}
public @Nullable B getRight() {
if (!hasRight()) {
throw new IllegalStateException("Either was not right");
}
return right;
}
@Override
@SuppressWarnings("unchecked")
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof Either)) {
return false;
}
Either<A, B> either = (Either<A, B>) obj;
if (hasLeft()) {
return either.hasLeft() && equals(getLeft(), either.getLeft());
} else {
return either.hasRight() && equals(getRight(), either.getRight());
}
}
/** Compares two List-based Either by (a copy of) their sorted inner List. */
public static <A, B> boolean sortedEquals(
@Nullable Either<List<A>, B> first,
@Nullable Either<List<A>, B> second,
Comparator<A> comparatorForSorting) {
// Only sort if the Lists will actually be compared. This uses final variable "left" rather than
// getLeft() (which "could" change between invocations) to satisfy the Nullness Checker.
if (first != null
&& first.hasLeft()
&& first.left != null
&& second != null
&& second.hasLeft()
&& second.left != null) {
List<A> sortedFirstLeft = new ArrayList<>(first.left);
List<A> sortedSecondLeft = new ArrayList<>(second.left);
Collections.sort(sortedFirstLeft, comparatorForSorting);
Collections.sort(sortedSecondLeft, comparatorForSorting);
return sortedFirstLeft.equals(sortedSecondLeft);
}
return equals(first, second);
}
// Inline definition of Objects.equals() since it's not available on all API levels.
public static boolean equals(@Nullable Object a, @Nullable Object b) {
return (a == b) || (a != null && a.equals(b));
}
@Override
public int hashCode() {
return Arrays.hashCode(new Object[] {hasLeft, left, right});
}
}