blob: ce09f6129f3d682281231c813ec05a65213220b6 [file] [log] [blame]
/*
* 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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.stream.Collector;
import org.checkerframework.checker.nullness.qual.Nullable;
/**
* GWT emulation of {@link com.google.common.collect.ImmutableSortedSet}.
*
* @author Hayward Chan
*/
public abstract class ImmutableSortedSet<E> extends ForwardingImmutableSet<E>
implements SortedSet<E>, SortedIterable<E> {
// TODO(cpovirk): split into ImmutableSortedSet/ForwardingImmutableSortedSet?
// In the non-emulated source, this is in ImmutableSortedSetFauxverideShim,
// which overrides ImmutableSet & which ImmutableSortedSet extends.
// It is necessary here because otherwise the builder() method
// would be inherited from the emulated ImmutableSet.
// TODO(cpovirk): should we be including other methods from the shim here and
// in ImmutableSortedMap?
@Deprecated
public static <E> ImmutableSortedSet.Builder<E> builder() {
throw new UnsupportedOperationException();
}
// TODO: Can we find a way to remove this @SuppressWarnings even for eclipse?
@SuppressWarnings("unchecked")
private static final Comparator NATURAL_ORDER = Ordering.natural();
@SuppressWarnings("unchecked")
private static final ImmutableSortedSet<Object> NATURAL_EMPTY_SET =
new RegularImmutableSortedSet<Object>(new TreeSet<Object>(NATURAL_ORDER), false);
static <E> ImmutableSortedSet<E> emptySet(Comparator<? super E> comparator) {
checkNotNull(comparator);
if (NATURAL_ORDER.equals(comparator)) {
return of();
} else {
return new RegularImmutableSortedSet<E>(new TreeSet<E>(comparator), false);
}
}
public static <E> Collector<E, ?, ImmutableSortedSet<E>> toImmutableSortedSet(
Comparator<? super E> comparator) {
return CollectCollectors.toImmutableSortedSet(comparator);
}
@SuppressWarnings("unchecked")
public static <E> ImmutableSortedSet<E> of() {
return (ImmutableSortedSet<E>) NATURAL_EMPTY_SET;
}
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(E element) {
return ofInternal(Ordering.natural(), element);
}
@SuppressWarnings("unchecked")
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(E e1, E e2) {
return ofInternal(Ordering.natural(), e1, e2);
}
@SuppressWarnings("unchecked")
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(E e1, E e2, E e3) {
return ofInternal(Ordering.natural(), e1, e2, e3);
}
@SuppressWarnings("unchecked")
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(E e1, E e2, E e3, E e4) {
return ofInternal(Ordering.natural(), e1, e2, e3, e4);
}
@SuppressWarnings("unchecked")
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
E e1, E e2, E e3, E e4, E e5) {
return ofInternal(Ordering.natural(), e1, e2, e3, e4, e5);
}
@SuppressWarnings("unchecked")
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) {
int size = remaining.length + 6;
List<E> all = new ArrayList<E>(size);
Collections.addAll(all, e1, e2, e3, e4, e5, e6);
Collections.addAll(all, remaining);
// This is messed up. See TODO at top of file.
return ofInternal(Ordering.natural(), (E[]) all.toArray(new Comparable[0]));
}
private static <E> ImmutableSortedSet<E> ofInternal(
Comparator<? super E> comparator, E... elements) {
checkNotNull(elements);
switch (elements.length) {
case 0:
return emptySet(comparator);
default:
SortedSet<E> delegate = new TreeSet<E>(comparator);
for (E element : elements) {
checkNotNull(element);
delegate.add(element);
}
return new RegularImmutableSortedSet<E>(delegate, false);
}
}
public static <E> ImmutableSortedSet<E> copyOf(Collection<? extends E> elements) {
return copyOfInternal((Ordering<E>) Ordering.natural(), (Collection) elements, false);
}
public static <E> ImmutableSortedSet<E> copyOf(Iterable<? extends E> elements) {
return copyOfInternal((Ordering<E>) Ordering.natural(), (Iterable) elements, false);
}
public static <E> ImmutableSortedSet<E> copyOf(Iterator<? extends E> elements) {
return copyOfInternal((Ordering<E>) Ordering.natural(), (Iterator) elements);
}
public static <E extends Comparable<? super E>> ImmutableSortedSet<E> copyOf(E[] elements) {
return ofInternal(Ordering.natural(), elements);
}
public static <E> ImmutableSortedSet<E> copyOf(
Comparator<? super E> comparator, Iterable<? extends E> elements) {
checkNotNull(comparator);
return copyOfInternal(comparator, elements, false);
}
public static <E> ImmutableSortedSet<E> copyOf(
Comparator<? super E> comparator, Collection<? extends E> elements) {
checkNotNull(comparator);
return copyOfInternal(comparator, elements, false);
}
public static <E> ImmutableSortedSet<E> copyOf(
Comparator<? super E> comparator, Iterator<? extends E> elements) {
checkNotNull(comparator);
return copyOfInternal(comparator, elements);
}
@SuppressWarnings("unchecked")
public static <E> ImmutableSortedSet<E> copyOfSorted(SortedSet<E> sortedSet) {
Comparator<? super E> comparator = sortedSet.comparator();
if (comparator == null) {
comparator = NATURAL_ORDER;
}
return copyOfInternal(comparator, sortedSet.iterator());
}
private static <E> ImmutableSortedSet<E> copyOfInternal(
Comparator<? super E> comparator, Iterable<? extends E> elements, boolean fromSortedSet) {
checkNotNull(comparator);
boolean hasSameComparator = fromSortedSet || hasSameComparator(elements, comparator);
if (hasSameComparator && (elements instanceof ImmutableSortedSet)) {
@SuppressWarnings("unchecked")
ImmutableSortedSet<E> result = (ImmutableSortedSet<E>) elements;
boolean isSubset =
(result instanceof RegularImmutableSortedSet)
&& ((RegularImmutableSortedSet) result).isSubset;
if (!isSubset) {
// Only return the original copy if this immutable sorted set isn't
// a subset of another, to avoid memory leak.
return result;
}
}
return copyOfInternal(comparator, elements.iterator());
}
private static <E> ImmutableSortedSet<E> copyOfInternal(
Comparator<? super E> comparator, Iterator<? extends E> elements) {
checkNotNull(comparator);
if (!elements.hasNext()) {
return emptySet(comparator);
}
SortedSet<E> delegate = new TreeSet<E>(comparator);
while (elements.hasNext()) {
E element = elements.next();
checkNotNull(element);
delegate.add(element);
}
return new RegularImmutableSortedSet<E>(delegate, false);
}
private static boolean hasSameComparator(Iterable<?> elements, Comparator<?> comparator) {
if (elements instanceof SortedSet) {
SortedSet<?> sortedSet = (SortedSet<?>) elements;
Comparator<?> comparator2 = sortedSet.comparator();
return (comparator2 == null)
? comparator == Ordering.natural()
: comparator.equals(comparator2);
}
return false;
}
// Assumes that delegate doesn't have null elements and comparator.
static <E> ImmutableSortedSet<E> unsafeDelegateSortedSet(
SortedSet<E> delegate, boolean isSubset) {
return delegate.isEmpty()
? emptySet(delegate.comparator())
: new RegularImmutableSortedSet<E>(delegate, isSubset);
}
private final transient SortedSet<E> sortedDelegate;
/**
* Scary constructor for ContiguousSet. This constructor (in this file, the GWT emulation of
* ImmutableSortedSet) creates an empty sortedDelegate, which, in a vacuum, sets this object's
* contents to empty. By contrast, the non-GWT constructor with the same signature uses the
* comparator only as a comparator. It does NOT assume empty contents. (It requires an
* implementation of iterator() to define its contents, and methods like contains() are
* implemented in terms of that method (though they will likely be overridden by subclasses for
* performance reasons).) This means that a call to this method have can different behavior in GWT
* and non-GWT environments UNLESS subclasses are careful to always override all methods
* implemented in terms of sortedDelegate (except comparator()).
*/
ImmutableSortedSet(Comparator<? super E> comparator) {
this(Sets.newTreeSet(comparator));
}
ImmutableSortedSet(SortedSet<E> sortedDelegate) {
super(sortedDelegate);
this.sortedDelegate = Collections.unmodifiableSortedSet(sortedDelegate);
}
public Comparator<? super E> comparator() {
return sortedDelegate.comparator();
}
@Override // needed to unify SortedIterable and Collection iterator() methods
public UnmodifiableIterator<E> iterator() {
return super.iterator();
}
@Override
public Object[] toArray() {
return ObjectArrays.toArrayImpl(this);
}
@Override
public <T> T[] toArray(T[] other) {
return ObjectArrays.toArrayImpl(this, other);
}
@Override
public boolean contains(@Nullable Object object) {
try {
// This set never contains null. We need to explicitly check here
// because some comparator might throw NPE (e.g. the natural ordering).
return object != null && sortedDelegate.contains(object);
} catch (ClassCastException e) {
return false;
}
}
@Override
public boolean containsAll(Collection<?> targets) {
for (Object target : targets) {
if (target == null) {
// This set never contains null. We need to explicitly check here
// because some comparator might throw NPE (e.g. the natural ordering).
return false;
}
}
try {
return sortedDelegate.containsAll(targets);
} catch (ClassCastException e) {
return false;
}
}
public E first() {
return sortedDelegate.first();
}
public ImmutableSortedSet<E> headSet(E toElement) {
checkNotNull(toElement);
try {
return unsafeDelegateSortedSet(sortedDelegate.headSet(toElement), true);
} catch (IllegalArgumentException e) {
return emptySet(comparator());
}
}
E higher(E e) {
checkNotNull(e);
Iterator<E> iterator = tailSet(e).iterator();
while (iterator.hasNext()) {
E higher = iterator.next();
if (comparator().compare(e, higher) < 0) {
return higher;
}
}
return null;
}
public E ceiling(E e) {
ImmutableSortedSet<E> set = tailSet(e, true);
return !set.isEmpty() ? set.first() : null;
}
public E floor(E e) {
ImmutableSortedSet<E> set = headSet(e, true);
return !set.isEmpty() ? set.last() : null;
}
public ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) {
checkNotNull(toElement);
if (inclusive) {
E tmp = higher(toElement);
if (tmp == null) {
return this;
}
toElement = tmp;
}
return headSet(toElement);
}
public E last() {
return sortedDelegate.last();
}
public ImmutableSortedSet<E> subSet(E fromElement, E toElement) {
return subSet(fromElement, true, toElement, false);
}
ImmutableSortedSet<E> subSet(
E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) {
checkNotNull(fromElement);
checkNotNull(toElement);
int cmp = comparator().compare(fromElement, toElement);
checkArgument(cmp <= 0, "fromElement (%s) is less than toElement (%s)", fromElement, toElement);
if (cmp == 0 && !(fromInclusive && toInclusive)) {
return emptySet(comparator());
}
return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive);
}
public ImmutableSortedSet<E> tailSet(E fromElement) {
checkNotNull(fromElement);
try {
return unsafeDelegateSortedSet(sortedDelegate.tailSet(fromElement), true);
} catch (IllegalArgumentException e) {
return emptySet(comparator());
}
}
public ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) {
checkNotNull(fromElement);
if (!inclusive) {
E tmp = higher(fromElement);
if (tmp == null) {
return emptySet(comparator());
}
fromElement = tmp;
}
return tailSet(fromElement);
}
public static <E> Builder<E> orderedBy(Comparator<E> comparator) {
return new Builder<E>(comparator);
}
public static <E extends Comparable<?>> Builder<E> reverseOrder() {
return new Builder<E>(Ordering.natural().reverse());
}
public static <E extends Comparable<?>> Builder<E> naturalOrder() {
return new Builder<E>(Ordering.natural());
}
public static final class Builder<E> extends ImmutableSet.Builder<E> {
private final Comparator<? super E> comparator;
public Builder(Comparator<? super E> comparator) {
this.comparator = checkNotNull(comparator);
}
@Override
public Builder<E> add(E element) {
super.add(element);
return this;
}
@Override
public Builder<E> add(E... elements) {
super.add(elements);
return this;
}
@Override
public Builder<E> addAll(Iterable<? extends E> elements) {
super.addAll(elements);
return this;
}
@Override
public Builder<E> addAll(Iterator<? extends E> elements) {
super.addAll(elements);
return this;
}
Builder<E> combine(Builder<E> builder) {
super.combine(builder);
return this;
}
@Override
public ImmutableSortedSet<E> build() {
return copyOfInternal(comparator, contents.iterator());
}
}
}