Coverage Summary for Class: GeneralRange (com.google.common.collect)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| GeneralRange | 100% (1/1) | 31.8% (7/22) | 17.2% (20/116) |
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 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 11 * express or implied. See the License for the specific language governing permissions and 12 * limitations under the License. 13 */ 14 15 package com.google.common.collect; 16 17 import static com.google.common.base.Preconditions.checkArgument; 18 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.collect.BoundType.CLOSED; 20 import static com.google.common.collect.BoundType.OPEN; 21 import static com.google.common.collect.NullnessCasts.uncheckedCastNullableTToT; 22 23 import com.google.common.annotations.GwtCompatible; 24 import com.google.common.base.Objects; 25 import java.io.Serializable; 26 import java.util.Comparator; 27 import javax.annotation.CheckForNull; 28 import org.checkerframework.checker.nullness.qual.Nullable; 29 30 /** 31 * A generalized interval on any ordering, for internal use. Supports {@code null}. Unlike {@link 32 * Range}, this allows the use of an arbitrary comparator. This is designed for use in the 33 * implementation of subcollections of sorted collection types. 34 * 35 * <p>Whenever possible, use {@code Range} instead, which is better supported. 36 * 37 * @author Louis Wasserman 38 */ 39 @GwtCompatible(serializable = true) 40 @ElementTypesAreNonnullByDefault 41 final class GeneralRange<T extends @Nullable Object> implements Serializable { 42 /** Converts a Range to a GeneralRange. */ 43 static <T extends Comparable> GeneralRange<T> from(Range<T> range) { 44 T lowerEndpoint = range.hasLowerBound() ? range.lowerEndpoint() : null; 45 BoundType lowerBoundType = range.hasLowerBound() ? range.lowerBoundType() : OPEN; 46 47 T upperEndpoint = range.hasUpperBound() ? range.upperEndpoint() : null; 48 BoundType upperBoundType = range.hasUpperBound() ? range.upperBoundType() : OPEN; 49 return new GeneralRange<T>( 50 Ordering.natural(), 51 range.hasLowerBound(), 52 lowerEndpoint, 53 lowerBoundType, 54 range.hasUpperBound(), 55 upperEndpoint, 56 upperBoundType); 57 } 58 59 /** Returns the whole range relative to the specified comparator. */ 60 static <T extends @Nullable Object> GeneralRange<T> all(Comparator<? super T> comparator) { 61 return new GeneralRange<T>(comparator, false, null, OPEN, false, null, OPEN); 62 } 63 64 /** 65 * Returns everything above the endpoint relative to the specified comparator, with the specified 66 * endpoint behavior. 67 */ 68 static <T extends @Nullable Object> GeneralRange<T> downTo( 69 Comparator<? super T> comparator, @ParametricNullness T endpoint, BoundType boundType) { 70 return new GeneralRange<T>(comparator, true, endpoint, boundType, false, null, OPEN); 71 } 72 73 /** 74 * Returns everything below the endpoint relative to the specified comparator, with the specified 75 * endpoint behavior. 76 */ 77 static <T extends @Nullable Object> GeneralRange<T> upTo( 78 Comparator<? super T> comparator, @ParametricNullness T endpoint, BoundType boundType) { 79 return new GeneralRange<T>(comparator, false, null, OPEN, true, endpoint, boundType); 80 } 81 82 /** 83 * Returns everything between the endpoints relative to the specified comparator, with the 84 * specified endpoint behavior. 85 */ 86 static <T extends @Nullable Object> GeneralRange<T> range( 87 Comparator<? super T> comparator, 88 @ParametricNullness T lower, 89 BoundType lowerType, 90 @ParametricNullness T upper, 91 BoundType upperType) { 92 return new GeneralRange<T>(comparator, true, lower, lowerType, true, upper, upperType); 93 } 94 95 private final Comparator<? super T> comparator; 96 private final boolean hasLowerBound; 97 @CheckForNull private final T lowerEndpoint; 98 private final BoundType lowerBoundType; 99 private final boolean hasUpperBound; 100 @CheckForNull private final T upperEndpoint; 101 private final BoundType upperBoundType; 102 103 private GeneralRange( 104 Comparator<? super T> comparator, 105 boolean hasLowerBound, 106 @CheckForNull T lowerEndpoint, 107 BoundType lowerBoundType, 108 boolean hasUpperBound, 109 @CheckForNull T upperEndpoint, 110 BoundType upperBoundType) { 111 this.comparator = checkNotNull(comparator); 112 this.hasLowerBound = hasLowerBound; 113 this.hasUpperBound = hasUpperBound; 114 this.lowerEndpoint = lowerEndpoint; 115 this.lowerBoundType = checkNotNull(lowerBoundType); 116 this.upperEndpoint = upperEndpoint; 117 this.upperBoundType = checkNotNull(upperBoundType); 118 119 // Trigger any exception that the comparator would throw for the endpoints. 120 /* 121 * uncheckedCastNullableTToT is safe as long as the callers are careful to pass a "real" T 122 * whenever they pass `true` for the matching `has*Bound` parameter. 123 */ 124 if (hasLowerBound) { 125 comparator.compare( 126 uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(lowerEndpoint)); 127 } 128 if (hasUpperBound) { 129 comparator.compare( 130 uncheckedCastNullableTToT(upperEndpoint), uncheckedCastNullableTToT(upperEndpoint)); 131 } 132 133 if (hasLowerBound && hasUpperBound) { 134 int cmp = 135 comparator.compare( 136 uncheckedCastNullableTToT(lowerEndpoint), uncheckedCastNullableTToT(upperEndpoint)); 137 // be consistent with Range 138 checkArgument( 139 cmp <= 0, "lowerEndpoint (%s) > upperEndpoint (%s)", lowerEndpoint, upperEndpoint); 140 if (cmp == 0) { 141 checkArgument(lowerBoundType != OPEN | upperBoundType != OPEN); 142 } 143 } 144 } 145 146 Comparator<? super T> comparator() { 147 return comparator; 148 } 149 150 boolean hasLowerBound() { 151 return hasLowerBound; 152 } 153 154 boolean hasUpperBound() { 155 return hasUpperBound; 156 } 157 158 boolean isEmpty() { 159 // The casts are safe because of the has*Bound() checks. 160 return (hasUpperBound() && tooLow(uncheckedCastNullableTToT(getUpperEndpoint()))) 161 || (hasLowerBound() && tooHigh(uncheckedCastNullableTToT(getLowerEndpoint()))); 162 } 163 164 boolean tooLow(@ParametricNullness T t) { 165 if (!hasLowerBound()) { 166 return false; 167 } 168 // The cast is safe because of the hasLowerBound() check. 169 T lbound = uncheckedCastNullableTToT(getLowerEndpoint()); 170 int cmp = comparator.compare(t, lbound); 171 return cmp < 0 | (cmp == 0 & getLowerBoundType() == OPEN); 172 } 173 174 boolean tooHigh(@ParametricNullness T t) { 175 if (!hasUpperBound()) { 176 return false; 177 } 178 // The cast is safe because of the hasUpperBound() check. 179 T ubound = uncheckedCastNullableTToT(getUpperEndpoint()); 180 int cmp = comparator.compare(t, ubound); 181 return cmp > 0 | (cmp == 0 & getUpperBoundType() == OPEN); 182 } 183 184 boolean contains(@ParametricNullness T t) { 185 return !tooLow(t) && !tooHigh(t); 186 } 187 188 /** 189 * Returns the intersection of the two ranges, or an empty range if their intersection is empty. 190 */ 191 @SuppressWarnings("nullness") // TODO(cpovirk): Add casts as needed. Will be noisy and annoying... 192 GeneralRange<T> intersect(GeneralRange<T> other) { 193 checkNotNull(other); 194 checkArgument(comparator.equals(other.comparator)); 195 196 boolean hasLowBound = this.hasLowerBound; 197 T lowEnd = getLowerEndpoint(); 198 BoundType lowType = getLowerBoundType(); 199 if (!hasLowerBound()) { 200 hasLowBound = other.hasLowerBound; 201 lowEnd = other.getLowerEndpoint(); 202 lowType = other.getLowerBoundType(); 203 } else if (other.hasLowerBound()) { 204 int cmp = comparator.compare(getLowerEndpoint(), other.getLowerEndpoint()); 205 if (cmp < 0 || (cmp == 0 && other.getLowerBoundType() == OPEN)) { 206 lowEnd = other.getLowerEndpoint(); 207 lowType = other.getLowerBoundType(); 208 } 209 } 210 211 boolean hasUpBound = this.hasUpperBound; 212 T upEnd = getUpperEndpoint(); 213 BoundType upType = getUpperBoundType(); 214 if (!hasUpperBound()) { 215 hasUpBound = other.hasUpperBound; 216 upEnd = other.getUpperEndpoint(); 217 upType = other.getUpperBoundType(); 218 } else if (other.hasUpperBound()) { 219 int cmp = comparator.compare(getUpperEndpoint(), other.getUpperEndpoint()); 220 if (cmp > 0 || (cmp == 0 && other.getUpperBoundType() == OPEN)) { 221 upEnd = other.getUpperEndpoint(); 222 upType = other.getUpperBoundType(); 223 } 224 } 225 226 if (hasLowBound && hasUpBound) { 227 int cmp = comparator.compare(lowEnd, upEnd); 228 if (cmp > 0 || (cmp == 0 && lowType == OPEN && upType == OPEN)) { 229 // force allowed empty range 230 lowEnd = upEnd; 231 lowType = OPEN; 232 upType = CLOSED; 233 } 234 } 235 236 return new GeneralRange<T>(comparator, hasLowBound, lowEnd, lowType, hasUpBound, upEnd, upType); 237 } 238 239 @Override 240 public boolean equals(@CheckForNull Object obj) { 241 if (obj instanceof GeneralRange) { 242 GeneralRange<?> r = (GeneralRange<?>) obj; 243 return comparator.equals(r.comparator) 244 && hasLowerBound == r.hasLowerBound 245 && hasUpperBound == r.hasUpperBound 246 && getLowerBoundType().equals(r.getLowerBoundType()) 247 && getUpperBoundType().equals(r.getUpperBoundType()) 248 && Objects.equal(getLowerEndpoint(), r.getLowerEndpoint()) 249 && Objects.equal(getUpperEndpoint(), r.getUpperEndpoint()); 250 } 251 return false; 252 } 253 254 @Override 255 public int hashCode() { 256 return Objects.hashCode( 257 comparator, 258 getLowerEndpoint(), 259 getLowerBoundType(), 260 getUpperEndpoint(), 261 getUpperBoundType()); 262 } 263 264 @CheckForNull private transient GeneralRange<T> reverse; 265 266 /** Returns the same range relative to the reversed comparator. */ 267 GeneralRange<T> reverse() { 268 GeneralRange<T> result = reverse; 269 if (result == null) { 270 result = 271 new GeneralRange<T>( 272 Ordering.from(comparator).reverse(), 273 hasUpperBound, 274 getUpperEndpoint(), 275 getUpperBoundType(), 276 hasLowerBound, 277 getLowerEndpoint(), 278 getLowerBoundType()); 279 result.reverse = this; 280 return this.reverse = result; 281 } 282 return result; 283 } 284 285 @Override 286 public String toString() { 287 return comparator 288 + ":" 289 + (lowerBoundType == CLOSED ? '[' : '(') 290 + (hasLowerBound ? lowerEndpoint : "-\u221e") 291 + ',' 292 + (hasUpperBound ? upperEndpoint : "\u221e") 293 + (upperBoundType == CLOSED ? ']' : ')'); 294 } 295 296 @CheckForNull 297 T getLowerEndpoint() { 298 return lowerEndpoint; 299 } 300 301 BoundType getLowerBoundType() { 302 return lowerBoundType; 303 } 304 305 @CheckForNull 306 T getUpperEndpoint() { 307 return upperEndpoint; 308 } 309 310 BoundType getUpperBoundType() { 311 return upperBoundType; 312 } 313 }