1/*
2 * Copyright (C) 2011 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package com.google.common.collect;
16
17import static com.google.common.base.Preconditions.checkArgument;
18import static com.google.common.base.Preconditions.checkNotNull;
19import static com.google.common.collect.BoundType.CLOSED;
20
21import com.google.common.annotations.GwtCompatible;
22
23import java.util.Collection;
24
25import javax.annotation.Nullable;
26
27/**
28 * An implementation of {@link ContiguousSet} that contains one or more elements.
29 *
30 * @author Gregory Kick
31 */
32@GwtCompatible(emulated = true)
33@SuppressWarnings("unchecked") // allow ungenerified Comparable types
34final class RegularContiguousSet<C extends Comparable> extends ContiguousSet<C> {
35  private final Range<C> range;
36
37  RegularContiguousSet(Range<C> range, DiscreteDomain<C> domain) {
38    super(domain);
39    this.range = range;
40  }
41
42  // Abstract method doesn't exist in GWT emulation
43  /* @Override */ ContiguousSet<C> headSetImpl(C toElement, boolean inclusive) {
44    return range.intersection(Ranges.upTo(toElement, BoundType.forBoolean(inclusive)))
45        .asSet(domain);
46  }
47
48  // Abstract method doesn't exist in GWT emulation
49  /* @Override */ int indexOf(Object target) {
50    return contains(target) ? (int) domain.distance(first(), (C) target) : -1;
51  }
52
53  // Abstract method doesn't exist in GWT emulation
54  /* @Override */ ContiguousSet<C> subSetImpl(C fromElement, boolean fromInclusive, C toElement,
55      boolean toInclusive) {
56    return range.intersection(Ranges.range(fromElement, BoundType.forBoolean(fromInclusive),
57        toElement, BoundType.forBoolean(toInclusive))).asSet(domain);
58  }
59
60  // Abstract method doesn't exist in GWT emulation
61  /* @Override */ ContiguousSet<C> tailSetImpl(C fromElement, boolean inclusive) {
62    return range.intersection(Ranges.downTo(fromElement, BoundType.forBoolean(inclusive)))
63        .asSet(domain);
64  }
65
66  @Override public UnmodifiableIterator<C> iterator() {
67    return new AbstractLinkedIterator<C>(first()) {
68      final C last = last();
69
70      @Override
71      protected C computeNext(C previous) {
72        return equalsOrThrow(previous, last) ? null : domain.next(previous);
73      }
74    };
75  }
76
77  private static boolean equalsOrThrow(Comparable<?> left, @Nullable Comparable<?> right) {
78    return right != null && Range.compareOrThrow(left, right) == 0;
79  }
80
81  @Override boolean isPartialView() {
82    return false;
83  }
84
85  @Override public C first() {
86    return range.lowerBound.leastValueAbove(domain);
87  }
88
89  @Override public C last() {
90    return range.upperBound.greatestValueBelow(domain);
91  }
92
93  @Override public int size() {
94    long distance = domain.distance(first(), last());
95    return (distance >= Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) distance + 1;
96  }
97
98  @Override public boolean contains(Object object) {
99    try {
100      return range.contains((C) object);
101    } catch (ClassCastException e) {
102      return false;
103    }
104  }
105
106  @Override public boolean containsAll(Collection<?> targets) {
107    try {
108      return range.containsAll((Iterable<? extends C>) targets);
109    } catch (ClassCastException e) {
110      return false;
111    }
112  }
113
114  @Override public boolean isEmpty() {
115    return false;
116  }
117
118  // copied to make sure not to use the GWT-emulated version
119  @Override public Object[] toArray() {
120    return ObjectArrays.toArrayImpl(this);
121  }
122
123  // copied to make sure not to use the GWT-emulated version
124  @Override public <T> T[] toArray(T[] other) {
125    return ObjectArrays.toArrayImpl(this, other);
126  }
127
128  @Override public ContiguousSet<C> intersection(ContiguousSet<C> other) {
129    checkNotNull(other);
130    checkArgument(this.domain.equals(other.domain));
131    if (other.isEmpty()) {
132      return other;
133    } else {
134      C lowerEndpoint = Ordering.natural().max(this.first(), other.first());
135      C upperEndpoint = Ordering.natural().min(this.last(), other.last());
136      return (lowerEndpoint.compareTo(upperEndpoint) < 0)
137          ? Ranges.closed(lowerEndpoint, upperEndpoint).asSet(domain)
138          : new EmptyContiguousSet<C>(domain);
139    }
140  }
141
142  @Override public Range<C> range() {
143    return range(CLOSED, CLOSED);
144  }
145
146  @Override public Range<C> range(BoundType lowerBoundType, BoundType upperBoundType) {
147    return Ranges.create(range.lowerBound.withLowerBoundType(lowerBoundType, domain),
148        range.upperBound.withUpperBoundType(upperBoundType, domain));
149  }
150
151  @Override public boolean equals(Object object) {
152    if (object == this) {
153      return true;
154    } else if (object instanceof RegularContiguousSet<?>) {
155      RegularContiguousSet<?> that = (RegularContiguousSet<?>) object;
156      if (this.domain.equals(that.domain)) {
157        return this.first().equals(that.first())
158            && this.last().equals(that.last());
159      }
160    }
161    return super.equals(object);
162  }
163
164  // copied to make sure not to use the GWT-emulated version
165  @Override public int hashCode() {
166    return Sets.hashCodeImpl(this);
167  }
168
169  private static final long serialVersionUID = 0;
170}
171
172