/* * 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 { 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 Either makeLeft(A left) { return new Either<>(true, left, null); } public static Either 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 either = (Either) 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 boolean sortedEquals( @Nullable Either, B> first, @Nullable Either, B> second, Comparator 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 sortedFirstLeft = new ArrayList<>(first.left); List 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}); } }