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