/*
 * Copyright (C) 2009 The Guava Authors
 *
 * 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.common.collect;

import static com.google.common.base.Preconditions.checkNotNull;

import com.google.common.annotations.GwtCompatible;
import com.google.common.primitives.Booleans;
import java.io.Serializable;
import java.util.NoSuchElementException;
import org.checkerframework.checker.nullness.qual.Nullable;

Implementation detail for the internal structure of Range instances. Represents a unique way of "cutting" a "number line" (actually of instances of type C, not necessarily "numbers") into two sections; this can be done below a certain value, above a certain value, below all values or above all values. With this object defined in this way, an interval can always be represented by a pair of Cut instances.
Author:Kevin Bourrillion
/** * Implementation detail for the internal structure of {@link Range} instances. Represents a unique * way of "cutting" a "number line" (actually of instances of type {@code C}, not necessarily * "numbers") into two sections; this can be done below a certain value, above a certain value, * below all values or above all values. With this object defined in this way, an interval can * always be represented by a pair of {@code Cut} instances. * * @author Kevin Bourrillion */
@GwtCompatible abstract class Cut<C extends Comparable> implements Comparable<Cut<C>>, Serializable { final @Nullable C endpoint; Cut(@Nullable C endpoint) { this.endpoint = endpoint; } abstract boolean isLessThan(C value); abstract BoundType typeAsLowerBound(); abstract BoundType typeAsUpperBound(); abstract Cut<C> withLowerBoundType(BoundType boundType, DiscreteDomain<C> domain); abstract Cut<C> withUpperBoundType(BoundType boundType, DiscreteDomain<C> domain); abstract void describeAsLowerBound(StringBuilder sb); abstract void describeAsUpperBound(StringBuilder sb); abstract C leastValueAbove(DiscreteDomain<C> domain); abstract C greatestValueBelow(DiscreteDomain<C> domain); /* * The canonical form is a BelowValue cut whenever possible, otherwise ABOVE_ALL, or * (only in the case of types that are unbounded below) BELOW_ALL. */ Cut<C> canonical(DiscreteDomain<C> domain) { return this; } // note: overridden by {BELOW,ABOVE}_ALL @Override public int compareTo(Cut<C> that) { if (that == belowAll()) { return 1; } if (that == aboveAll()) { return -1; } int result = Range.compareOrThrow(endpoint, that.endpoint); if (result != 0) { return result; } // same value. below comes before above return Booleans.compare(this instanceof AboveValue, that instanceof AboveValue); } C endpoint() { return endpoint; } @SuppressWarnings("unchecked") // catching CCE @Override public boolean equals(Object obj) { if (obj instanceof Cut) { // It might not really be a Cut<C>, but we'll catch a CCE if it's not Cut<C> that = (Cut<C>) obj; try { int compareResult = compareTo(that); return compareResult == 0; } catch (ClassCastException ignored) { } } return false; } // Prevent "missing hashCode" warning by explicitly forcing subclasses implement it @Override public abstract int hashCode(); /* * The implementation neither produces nor consumes any non-null instance of type C, so * casting the type parameter is safe. */ @SuppressWarnings("unchecked") static <C extends Comparable> Cut<C> belowAll() { return (Cut<C>) BelowAll.INSTANCE; } private static final long serialVersionUID = 0; private static final class BelowAll extends Cut<Comparable<?>> { private static final BelowAll INSTANCE = new BelowAll(); private BelowAll() { super(null); } @Override Comparable<?> endpoint() { throw new IllegalStateException("range unbounded on this side"); } @Override boolean isLessThan(Comparable<?> value) { return true; } @Override BoundType typeAsLowerBound() { throw new IllegalStateException(); } @Override BoundType typeAsUpperBound() { throw new AssertionError("this statement should be unreachable"); } @Override Cut<Comparable<?>> withLowerBoundType( BoundType boundType, DiscreteDomain<Comparable<?>> domain) { throw new IllegalStateException(); } @Override Cut<Comparable<?>> withUpperBoundType( BoundType boundType, DiscreteDomain<Comparable<?>> domain) { throw new AssertionError("this statement should be unreachable"); } @Override void describeAsLowerBound(StringBuilder sb) { sb.append("(-\u221e"); } @Override void describeAsUpperBound(StringBuilder sb) { throw new AssertionError(); } @Override Comparable<?> leastValueAbove(DiscreteDomain<Comparable<?>> domain) { return domain.minValue(); } @Override Comparable<?> greatestValueBelow(DiscreteDomain<Comparable<?>> domain) { throw new AssertionError(); } @Override Cut<Comparable<?>> canonical(DiscreteDomain<Comparable<?>> domain) { try { return Cut.<Comparable<?>>belowValue(domain.minValue()); } catch (NoSuchElementException e) { return this; } } @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : -1; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public String toString() { return "-\u221e"; } private Object readResolve() { return INSTANCE; } private static final long serialVersionUID = 0; } /* * The implementation neither produces nor consumes any non-null instance of * type C, so casting the type parameter is safe. */ @SuppressWarnings("unchecked") static <C extends Comparable> Cut<C> aboveAll() { return (Cut<C>) AboveAll.INSTANCE; } private static final class AboveAll extends Cut<Comparable<?>> { private static final AboveAll INSTANCE = new AboveAll(); private AboveAll() { super(null); } @Override Comparable<?> endpoint() { throw new IllegalStateException("range unbounded on this side"); } @Override boolean isLessThan(Comparable<?> value) { return false; } @Override BoundType typeAsLowerBound() { throw new AssertionError("this statement should be unreachable"); } @Override BoundType typeAsUpperBound() { throw new IllegalStateException(); } @Override Cut<Comparable<?>> withLowerBoundType( BoundType boundType, DiscreteDomain<Comparable<?>> domain) { throw new AssertionError("this statement should be unreachable"); } @Override Cut<Comparable<?>> withUpperBoundType( BoundType boundType, DiscreteDomain<Comparable<?>> domain) { throw new IllegalStateException(); } @Override void describeAsLowerBound(StringBuilder sb) { throw new AssertionError(); } @Override void describeAsUpperBound(StringBuilder sb) { sb.append("+\u221e)"); } @Override Comparable<?> leastValueAbove(DiscreteDomain<Comparable<?>> domain) { throw new AssertionError(); } @Override Comparable<?> greatestValueBelow(DiscreteDomain<Comparable<?>> domain) { return domain.maxValue(); } @Override public int compareTo(Cut<Comparable<?>> o) { return (o == this) ? 0 : 1; } @Override public int hashCode() { return System.identityHashCode(this); } @Override public String toString() { return "+\u221e"; } private Object readResolve() { return INSTANCE; } private static final long serialVersionUID = 0; } static <C extends Comparable> Cut<C> belowValue(C endpoint) { return new BelowValue<C>(endpoint); } private static final class BelowValue<C extends Comparable> extends Cut<C> { BelowValue(C endpoint) { super(checkNotNull(endpoint)); } @Override boolean isLessThan(C value) { return Range.compareOrThrow(endpoint, value) <= 0; } @Override BoundType typeAsLowerBound() { return BoundType.CLOSED; } @Override BoundType typeAsUpperBound() { return BoundType.OPEN; } @Override Cut<C> withLowerBoundType(BoundType boundType, DiscreteDomain<C> domain) { switch (boundType) { case CLOSED: return this; case OPEN: @Nullable C previous = domain.previous(endpoint); return (previous == null) ? Cut.<C>belowAll() : new AboveValue<C>(previous); default: throw new AssertionError(); } } @Override Cut<C> withUpperBoundType(BoundType boundType, DiscreteDomain<C> domain) { switch (boundType) { case CLOSED: @Nullable C previous = domain.previous(endpoint); return (previous == null) ? Cut.<C>aboveAll() : new AboveValue<C>(previous); case OPEN: return this; default: throw new AssertionError(); } } @Override void describeAsLowerBound(StringBuilder sb) { sb.append('[').append(endpoint); } @Override void describeAsUpperBound(StringBuilder sb) { sb.append(endpoint).append(')'); } @Override C leastValueAbove(DiscreteDomain<C> domain) { return endpoint; } @Override C greatestValueBelow(DiscreteDomain<C> domain) { return domain.previous(endpoint); } @Override public int hashCode() { return endpoint.hashCode(); } @Override public String toString() { return "\\" + endpoint + "/"; } private static final long serialVersionUID = 0; } static <C extends Comparable> Cut<C> aboveValue(C endpoint) { return new AboveValue<C>(endpoint); } private static final class AboveValue<C extends Comparable> extends Cut<C> { AboveValue(C endpoint) { super(checkNotNull(endpoint)); } @Override boolean isLessThan(C value) { return Range.compareOrThrow(endpoint, value) < 0; } @Override BoundType typeAsLowerBound() { return BoundType.OPEN; } @Override BoundType typeAsUpperBound() { return BoundType.CLOSED; } @Override Cut<C> withLowerBoundType(BoundType boundType, DiscreteDomain<C> domain) { switch (boundType) { case OPEN: return this; case CLOSED: @Nullable C next = domain.next(endpoint); return (next == null) ? Cut.<C>belowAll() : belowValue(next); default: throw new AssertionError(); } } @Override Cut<C> withUpperBoundType(BoundType boundType, DiscreteDomain<C> domain) { switch (boundType) { case OPEN: @Nullable C next = domain.next(endpoint); return (next == null) ? Cut.<C>aboveAll() : belowValue(next); case CLOSED: return this; default: throw new AssertionError(); } } @Override void describeAsLowerBound(StringBuilder sb) { sb.append('(').append(endpoint); } @Override void describeAsUpperBound(StringBuilder sb) { sb.append(endpoint).append(']'); } @Override C leastValueAbove(DiscreteDomain<C> domain) { return domain.next(endpoint); } @Override C greatestValueBelow(DiscreteDomain<C> domain) { return endpoint; } @Override Cut<C> canonical(DiscreteDomain<C> domain) { C next = leastValueAbove(domain); return (next != null) ? belowValue(next) : Cut.<C>aboveAll(); } @Override public int hashCode() { return ~endpoint.hashCode(); } @Override public String toString() { return "/" + endpoint + "\\"; } private static final long serialVersionUID = 0; } }