1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import static com.android.internal.util.Preconditions.*; 20 21 import android.annotation.Nullable; 22 23 import java.util.Objects; 24 25 /** 26 * Immutable class for describing the range of two numeric values. 27 * <p> 28 * A range (or "interval") defines the inclusive boundaries around a contiguous span of 29 * values of some {@link Comparable} type; for example, 30 * "integers from 1 to 100 inclusive." 31 * </p> 32 * <p> 33 * All ranges are bounded, and the left side of the range is always {@code <=} 34 * the right side of the range. 35 * </p> 36 * 37 * <p>Although the implementation itself is immutable, there is no restriction that objects 38 * stored must also be immutable. If mutable objects are stored here, then the range 39 * effectively becomes mutable. </p> 40 */ 41 @android.ravenwood.annotation.RavenwoodKeepWholeClass 42 public final class Range<T extends Comparable<? super T>> { 43 /** 44 * Create a new immutable range. 45 * 46 * <p> 47 * The endpoints are {@code [lower, upper]}; that 48 * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal} 49 * to {@code upper}. 50 * </p> 51 * 52 * @param lower The lower endpoint (inclusive) 53 * @param upper The upper endpoint (inclusive) 54 * 55 * @throws NullPointerException if {@code lower} or {@code upper} is {@code null} 56 */ Range(final T lower, final T upper)57 public Range(final T lower, final T upper) { 58 mLower = checkNotNull(lower, "lower must not be null"); 59 mUpper = checkNotNull(upper, "upper must not be null"); 60 61 if (lower.compareTo(upper) > 0) { 62 throw new IllegalArgumentException("lower must be less than or equal to upper"); 63 } 64 } 65 66 /** 67 * Create a new immutable range, with the argument types inferred. 68 * 69 * <p> 70 * The endpoints are {@code [lower, upper]}; that 71 * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal} 72 * to {@code upper}. 73 * </p> 74 * 75 * @param lower The lower endpoint (inclusive) 76 * @param upper The upper endpoint (inclusive) 77 * 78 * @throws NullPointerException if {@code lower} or {@code upper} is {@code null} 79 */ create(final T lower, final T upper)80 public static <T extends Comparable<? super T>> Range<T> create(final T lower, final T upper) { 81 return new Range<T>(lower, upper); 82 } 83 84 /** 85 * Get the lower endpoint. 86 * 87 * @return a non-{@code null} {@code T} reference 88 */ getLower()89 public T getLower() { 90 return mLower; 91 } 92 93 /** 94 * Get the upper endpoint. 95 * 96 * @return a non-{@code null} {@code T} reference 97 */ getUpper()98 public T getUpper() { 99 return mUpper; 100 } 101 102 /** 103 * Checks if the {@code value} is within the bounds of this range. 104 * 105 * <p>A value is considered to be within this range if it's {@code >=} 106 * the lower endpoint <i>and</i> {@code <=} the upper endpoint (using the {@link Comparable} 107 * interface.)</p> 108 * 109 * @param value a non-{@code null} {@code T} reference 110 * @return {@code true} if the value is within this inclusive range, {@code false} otherwise 111 * 112 * @throws NullPointerException if {@code value} was {@code null} 113 */ contains(T value)114 public boolean contains(T value) { 115 checkNotNull(value, "value must not be null"); 116 117 boolean gteLower = value.compareTo(mLower) >= 0; 118 boolean lteUpper = value.compareTo(mUpper) <= 0; 119 120 return gteLower && lteUpper; 121 } 122 123 /** 124 * Checks if another {@code range} is within the bounds of this range. 125 * 126 * <p>A range is considered to be within this range if both of its endpoints 127 * are within this range.</p> 128 * 129 * @param range a non-{@code null} {@code T} reference 130 * @return {@code true} if the range is within this inclusive range, {@code false} otherwise 131 * 132 * @throws NullPointerException if {@code range} was {@code null} 133 */ contains(Range<T> range)134 public boolean contains(Range<T> range) { 135 checkNotNull(range, "value must not be null"); 136 137 boolean gteLower = range.mLower.compareTo(mLower) >= 0; 138 boolean lteUpper = range.mUpper.compareTo(mUpper) <= 0; 139 140 return gteLower && lteUpper; 141 } 142 143 /** 144 * Compare two ranges for equality. 145 * 146 * <p>A range is considered equal if and only if both the lower and upper endpoints 147 * are also equal.</p> 148 * 149 * @return {@code true} if the ranges are equal, {@code false} otherwise 150 */ 151 @Override equals(@ullable Object obj)152 public boolean equals(@Nullable Object obj) { 153 if (obj == null) { 154 return false; 155 } else if (this == obj) { 156 return true; 157 } else if (obj instanceof Range) { 158 @SuppressWarnings("rawtypes") 159 Range other = (Range) obj; 160 return mLower.equals(other.mLower) && mUpper.equals(other.mUpper); 161 } 162 return false; 163 } 164 165 /** 166 * Clamps {@code value} to this range. 167 * 168 * <p>If the value is within this range, it is returned. Otherwise, if it 169 * is {@code <} than the lower endpoint, the lower endpoint is returned, 170 * else the upper endpoint is returned. Comparisons are performed using the 171 * {@link Comparable} interface.</p> 172 * 173 * @param value a non-{@code null} {@code T} reference 174 * @return {@code value} clamped to this range. 175 */ clamp(T value)176 public T clamp(T value) { 177 checkNotNull(value, "value must not be null"); 178 179 if (value.compareTo(mLower) < 0) { 180 return mLower; 181 } else if (value.compareTo(mUpper) > 0) { 182 return mUpper; 183 } else { 184 return value; 185 } 186 } 187 188 /** 189 * Returns the intersection of this range and another {@code range}. 190 * <p> 191 * E.g. if a {@code <} b {@code <} c {@code <} d, the 192 * intersection of [a, c] and [b, d] ranges is [b, c]. 193 * As the endpoints are object references, there is no guarantee 194 * which specific endpoint reference is used from the input ranges:</p> 195 * <p> 196 * E.g. if a {@code ==} a' {@code <} b {@code <} c, the 197 * intersection of [a, b] and [a', c] ranges could be either 198 * [a, b] or ['a, b], where [a, b] could be either the exact 199 * input range, or a newly created range with the same endpoints.</p> 200 * 201 * @param range a non-{@code null} {@code Range<T>} reference 202 * @return the intersection of this range and the other range. 203 * 204 * @throws NullPointerException if {@code range} was {@code null} 205 * @throws IllegalArgumentException if the ranges are disjoint. 206 */ intersect(Range<T> range)207 public Range<T> intersect(Range<T> range) { 208 checkNotNull(range, "range must not be null"); 209 210 int cmpLower = range.mLower.compareTo(mLower); 211 int cmpUpper = range.mUpper.compareTo(mUpper); 212 213 if (cmpLower <= 0 && cmpUpper >= 0) { 214 // range includes this 215 return this; 216 } else if (cmpLower >= 0 && cmpUpper <= 0) { 217 // this inludes range 218 return range; 219 } else { 220 return Range.create( 221 cmpLower <= 0 ? mLower : range.mLower, 222 cmpUpper >= 0 ? mUpper : range.mUpper); 223 } 224 } 225 226 /** 227 * Returns the intersection of this range and the inclusive range 228 * specified by {@code [lower, upper]}. 229 * <p> 230 * See {@link #intersect(Range)} for more details.</p> 231 * 232 * @param lower a non-{@code null} {@code T} reference 233 * @param upper a non-{@code null} {@code T} reference 234 * @return the intersection of this range and the other range 235 * 236 * @throws NullPointerException if {@code lower} or {@code upper} was {@code null} 237 * @throws IllegalArgumentException if the ranges are disjoint. 238 */ intersect(T lower, T upper)239 public Range<T> intersect(T lower, T upper) { 240 checkNotNull(lower, "lower must not be null"); 241 checkNotNull(upper, "upper must not be null"); 242 243 int cmpLower = lower.compareTo(mLower); 244 int cmpUpper = upper.compareTo(mUpper); 245 246 if (cmpLower <= 0 && cmpUpper >= 0) { 247 // [lower, upper] includes this 248 return this; 249 } else { 250 return Range.create( 251 cmpLower <= 0 ? mLower : lower, 252 cmpUpper >= 0 ? mUpper : upper); 253 } 254 } 255 256 /** 257 * Returns the smallest range that includes this range and 258 * another {@code range}. 259 * <p> 260 * E.g. if a {@code <} b {@code <} c {@code <} d, the 261 * extension of [a, c] and [b, d] ranges is [a, d]. 262 * As the endpoints are object references, there is no guarantee 263 * which specific endpoint reference is used from the input ranges:</p> 264 * <p> 265 * E.g. if a {@code ==} a' {@code <} b {@code <} c, the 266 * extension of [a, b] and [a', c] ranges could be either 267 * [a, c] or ['a, c], where ['a, c] could be either the exact 268 * input range, or a newly created range with the same endpoints.</p> 269 * 270 * @param range a non-{@code null} {@code Range<T>} reference 271 * @return the extension of this range and the other range. 272 * 273 * @throws NullPointerException if {@code range} was {@code null} 274 */ extend(Range<T> range)275 public Range<T> extend(Range<T> range) { 276 checkNotNull(range, "range must not be null"); 277 278 int cmpLower = range.mLower.compareTo(mLower); 279 int cmpUpper = range.mUpper.compareTo(mUpper); 280 281 if (cmpLower <= 0 && cmpUpper >= 0) { 282 // other includes this 283 return range; 284 } else if (cmpLower >= 0 && cmpUpper <= 0) { 285 // this inludes other 286 return this; 287 } else { 288 return Range.create( 289 cmpLower >= 0 ? mLower : range.mLower, 290 cmpUpper <= 0 ? mUpper : range.mUpper); 291 } 292 } 293 294 /** 295 * Returns the smallest range that includes this range and 296 * the inclusive range specified by {@code [lower, upper]}. 297 * <p> 298 * See {@link #extend(Range)} for more details.</p> 299 * 300 * @param lower a non-{@code null} {@code T} reference 301 * @param upper a non-{@code null} {@code T} reference 302 * @return the extension of this range and the other range. 303 * 304 * @throws NullPointerException if {@code lower} or {@code 305 * upper} was {@code null} 306 */ extend(T lower, T upper)307 public Range<T> extend(T lower, T upper) { 308 checkNotNull(lower, "lower must not be null"); 309 checkNotNull(upper, "upper must not be null"); 310 311 int cmpLower = lower.compareTo(mLower); 312 int cmpUpper = upper.compareTo(mUpper); 313 314 if (cmpLower >= 0 && cmpUpper <= 0) { 315 // this inludes other 316 return this; 317 } else { 318 return Range.create( 319 cmpLower >= 0 ? mLower : lower, 320 cmpUpper <= 0 ? mUpper : upper); 321 } 322 } 323 324 /** 325 * Returns the smallest range that includes this range and 326 * the {@code value}. 327 * <p> 328 * See {@link #extend(Range)} for more details, as this method is 329 * equivalent to {@code extend(Range.create(value, value))}.</p> 330 * 331 * @param value a non-{@code null} {@code T} reference 332 * @return the extension of this range and the value. 333 * 334 * @throws NullPointerException if {@code value} was {@code null} 335 */ extend(T value)336 public Range<T> extend(T value) { 337 checkNotNull(value, "value must not be null"); 338 return extend(value, value); 339 } 340 341 /** 342 * Return the range as a string representation {@code "[lower, upper]"}. 343 * 344 * @return string representation of the range 345 */ 346 @Override toString()347 public String toString() { 348 return String.format("[%s, %s]", mLower, mUpper); 349 } 350 351 /** 352 * {@inheritDoc} 353 */ 354 @Override hashCode()355 public int hashCode() { 356 return Objects.hash(mLower, mUpper); 357 } 358 359 private final T mLower; 360 private final T mUpper; 361 } 362