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