• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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