/* * 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 javax.annotation.Nullable; /** * 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 implements Comparable>, Serializable { final C endpoint; Cut(@Nullable C endpoint) { this.endpoint = endpoint; } abstract boolean isLessThan(C value); abstract BoundType typeAsLowerBound(); abstract BoundType typeAsUpperBound(); abstract Cut withLowerBoundType(BoundType boundType, DiscreteDomain domain); abstract Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain); abstract void describeAsLowerBound(StringBuilder sb); abstract void describeAsUpperBound(StringBuilder sb); abstract C leastValueAbove(DiscreteDomain domain); abstract C greatestValueBelow(DiscreteDomain 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 canonical(DiscreteDomain domain) { return this; } // note: overriden by {BELOW,ABOVE}_ALL @Override public int compareTo(Cut 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, but we'll catch a CCE if it's not Cut that = (Cut) obj; try { int compareResult = compareTo(that); return compareResult == 0; } catch (ClassCastException ignored) { } } return false; } /* * The implementation neither produces nor consumes any non-null instance of type C, so * casting the type parameter is safe. */ @SuppressWarnings("unchecked") static Cut belowAll() { return (Cut) BelowAll.INSTANCE; } private static final long serialVersionUID = 0; private static final class BelowAll extends Cut> { 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> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { throw new IllegalStateException(); } @Override Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> 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> domain) { return domain.minValue(); } @Override Comparable greatestValueBelow( DiscreteDomain> domain) { throw new AssertionError(); } @Override Cut> canonical( DiscreteDomain> domain) { try { return Cut.>belowValue(domain.minValue()); } catch (NoSuchElementException e) { return this; } } @Override public int compareTo(Cut> o) { return (o == this) ? 0 : -1; } 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 Cut aboveAll() { return (Cut) AboveAll.INSTANCE; } private static final class AboveAll extends Cut> { 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> withLowerBoundType(BoundType boundType, DiscreteDomain> domain) { throw new AssertionError("this statement should be unreachable"); } @Override Cut> withUpperBoundType(BoundType boundType, DiscreteDomain> domain) { throw new IllegalStateException(); } @Override void describeAsLowerBound(StringBuilder sb) { throw new AssertionError(); } @Override void describeAsUpperBound(StringBuilder sb) { sb.append("+\u221e)"); } @Override Comparable leastValueAbove( DiscreteDomain> domain) { throw new AssertionError(); } @Override Comparable greatestValueBelow( DiscreteDomain> domain) { return domain.maxValue(); } @Override public int compareTo(Cut> o) { return (o == this) ? 0 : 1; } private Object readResolve() { return INSTANCE; } private static final long serialVersionUID = 0; } static Cut belowValue(C endpoint) { return new BelowValue(endpoint); } private static final class BelowValue extends Cut { 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 withLowerBoundType(BoundType boundType, DiscreteDomain domain) { switch (boundType) { case CLOSED: return this; case OPEN: @Nullable C previous = domain.previous(endpoint); return (previous == null) ? Cut.belowAll() : new AboveValue(previous); default: throw new AssertionError(); } } @Override Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { switch (boundType) { case CLOSED: @Nullable C previous = domain.previous(endpoint); return (previous == null) ? Cut.aboveAll() : new AboveValue(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 domain) { return endpoint; } @Override C greatestValueBelow(DiscreteDomain domain) { return domain.previous(endpoint); } @Override public int hashCode() { return endpoint.hashCode(); } private static final long serialVersionUID = 0; } static Cut aboveValue(C endpoint) { return new AboveValue(endpoint); } private static final class AboveValue extends Cut { 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 withLowerBoundType(BoundType boundType, DiscreteDomain domain) { switch (boundType) { case OPEN: return this; case CLOSED: @Nullable C next = domain.next(endpoint); return (next == null) ? Cut.belowAll() : belowValue(next); default: throw new AssertionError(); } } @Override Cut withUpperBoundType(BoundType boundType, DiscreteDomain domain) { switch (boundType) { case OPEN: @Nullable C next = domain.next(endpoint); return (next == null) ? Cut.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 domain) { return domain.next(endpoint); } @Override C greatestValueBelow(DiscreteDomain domain) { return endpoint; } @Override Cut canonical(DiscreteDomain domain) { C next = leastValueAbove(domain); return (next != null) ? belowValue(next) : Cut.aboveAll(); } @Override public int hashCode() { return ~endpoint.hashCode(); } private static final long serialVersionUID = 0; } }