com.google.common.collect.Cut Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of guava Show documentation
Show all versions of guava Show documentation
Guava is a suite of core and expanded libraries that include
utility classes, Google's collections, I/O classes, and
much more.
/*
* 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.CheckForNull;
/**
* 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
*/
@SuppressWarnings("rawtypes") // https://github.com/google/guava/issues/989
@GwtCompatible
@ElementTypesAreNonnullByDefault
abstract class Cut implements Comparable>, Serializable {
final C endpoint;
Cut(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);
@CheckForNull
abstract C leastValueAbove(DiscreteDomain domain);
@CheckForNull
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: overridden 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(@CheckForNull 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 wastNotComparableToOurType) {
return false;
}
}
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 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() {
/*
* No code ever sees this bogus value for `endpoint`: This class overrides both methods that
* use the `endpoint` field, compareTo() and endpoint(). Additionally, the main implementation
* of Cut.compareTo checks for belowAll before reading accessing `endpoint` on another Cut
* instance.
*/
super("");
}
@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;
}
@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 Cut aboveAll() {
return (Cut) AboveAll.INSTANCE;
}
private static final class AboveAll extends Cut> {
private static final AboveAll INSTANCE = new AboveAll();
private AboveAll() {
// For discussion of "", see BelowAll.
super("");
}
@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;
}
@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 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:
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:
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
@CheckForNull
C greatestValueBelow(DiscreteDomain 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 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:
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:
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
@CheckForNull
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();
}
@Override
public String toString() {
return "/" + endpoint + "\\";
}
private static final long serialVersionUID = 0;
}
}