/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.util; import static com.android.internal.util.Preconditions.*; import android.hardware.camera2.utils.HashCodeHelpers; /** * Immutable class for describing the range of two numeric values. *

* A range (or "interval") defines the inclusive boundaries around a contiguous span of * values of some {@link Comparable} type; for example, * "integers from 1 to 100 inclusive." *

*

* All ranges are bounded, and the left side of the range is always {@code >=} * the right side of the range. *

* *

Although the implementation itself is immutable, there is no restriction that objects * stored must also be immutable. If mutable objects are stored here, then the range * effectively becomes mutable.

*/ public final class Range> { /** * Create a new immutable range. * *

* The endpoints are {@code [lower, upper]}; that * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal} * to {@code upper}. *

* * @param lower The lower endpoint (inclusive) * @param upper The upper endpoint (inclusive) * * @throws NullPointerException if {@code lower} or {@code upper} is {@code null} */ public Range(final T lower, final T upper) { mLower = checkNotNull(lower, "lower must not be null"); mUpper = checkNotNull(upper, "upper must not be null"); if (lower.compareTo(upper) > 0) { throw new IllegalArgumentException("lower must be less than or equal to upper"); } } /** * Create a new immutable range, with the argument types inferred. * *

* The endpoints are {@code [lower, upper]}; that * is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal} * to {@code upper}. *

* * @param lower The lower endpoint (inclusive) * @param upper The upper endpoint (inclusive) * * @throws NullPointerException if {@code lower} or {@code upper} is {@code null} */ public static > Range create(final T lower, final T upper) { return new Range(lower, upper); } /** * Get the lower endpoint. * * @return a non-{@code null} {@code T} reference */ public T getLower() { return mLower; } /** * Get the upper endpoint. * * @return a non-{@code null} {@code T} reference */ public T getUpper() { return mUpper; } /** * Checks if the {@code value} is within the bounds of this range. * *

A value is considered to be within this range if it's {@code >=} * the lower endpoint and {@code <=} the upper endpoint (using the {@link Comparable} * interface.)

* * @param value a non-{@code null} {@code T} reference * @return {@code true} if the value is within this inclusive range, {@code false} otherwise * * @throws NullPointerException if {@code value} was {@code null} */ public boolean contains(T value) { checkNotNull(value, "value must not be null"); boolean gteLower = value.compareTo(mLower) >= 0; boolean lteUpper = value.compareTo(mUpper) <= 0; return gteLower && lteUpper; } /** * Checks if another {@code range} is within the bounds of this range. * *

A range is considered to be within this range if both of its endpoints * are within this range.

* * @param range a non-{@code null} {@code T} reference * @return {@code true} if the range is within this inclusive range, {@code false} otherwise * * @throws NullPointerException if {@code range} was {@code null} */ public boolean contains(Range range) { checkNotNull(range, "value must not be null"); boolean gteLower = range.mLower.compareTo(mLower) >= 0; boolean lteUpper = range.mUpper.compareTo(mUpper) <= 0; return gteLower && lteUpper; } /** * Compare two ranges for equality. * *

A range is considered equal if and only if both the lower and upper endpoints * are also equal.

* * @return {@code true} if the ranges are equal, {@code false} otherwise */ @Override public boolean equals(Object obj) { if (obj == null) { return false; } else if (this == obj) { return true; } else if (obj instanceof Range) { @SuppressWarnings("rawtypes") Range other = (Range) obj; return mLower.equals(other.mLower) && mUpper.equals(other.mUpper); } return false; } /** * Clamps {@code value} to this range. * *

If the value is within this range, it is returned. Otherwise, if it * is {@code <} than the lower endpoint, the lower endpoint is returned, * else the upper endpoint is returned. Comparisons are performed using the * {@link Comparable} interface.

* * @param value a non-{@code null} {@code T} reference * @return {@code value} clamped to this range. */ public T clamp(T value) { checkNotNull(value, "value must not be null"); if (value.compareTo(mLower) < 0) { return mLower; } else if (value.compareTo(mUpper) > 0) { return mUpper; } else { return value; } } /** * Returns the intersection of this range and another {@code range}. *

* E.g. if a {@code <} b {@code <} c {@code <} d, the * intersection of [a, c] and [b, d] ranges is [b, c]. * As the endpoints are object references, there is no guarantee * which specific endpoint reference is used from the input ranges:

*

* E.g. if a {@code ==} a' {@code <} b {@code <} c, the * intersection of [a, b] and [a', c] ranges could be either * [a, b] or ['a, b], where [a, b] could be either the exact * input range, or a newly created range with the same endpoints.

* * @param range a non-{@code null} {@code Range} reference * @return the intersection of this range and the other range. * * @throws NullPointerException if {@code range} was {@code null} * @throws IllegalArgumentException if the ranges are disjoint. */ public Range intersect(Range range) { checkNotNull(range, "range must not be null"); int cmpLower = range.mLower.compareTo(mLower); int cmpUpper = range.mUpper.compareTo(mUpper); if (cmpLower <= 0 && cmpUpper >= 0) { // range includes this return this; } else if (cmpLower >= 0 && cmpUpper <= 0) { // this inludes range return range; } else { return Range.create( cmpLower <= 0 ? mLower : range.mLower, cmpUpper >= 0 ? mUpper : range.mUpper); } } /** * Returns the intersection of this range and the inclusive range * specified by {@code [lower, upper]}. *

* See {@link #intersect(Range)} for more details.

* * @param lower a non-{@code null} {@code T} reference * @param upper a non-{@code null} {@code T} reference * @return the intersection of this range and the other range * * @throws NullPointerException if {@code lower} or {@code upper} was {@code null} * @throws IllegalArgumentException if the ranges are disjoint. */ public Range intersect(T lower, T upper) { checkNotNull(lower, "lower must not be null"); checkNotNull(upper, "upper must not be null"); int cmpLower = lower.compareTo(mLower); int cmpUpper = upper.compareTo(mUpper); if (cmpLower <= 0 && cmpUpper >= 0) { // [lower, upper] includes this return this; } else { return Range.create( cmpLower <= 0 ? mLower : lower, cmpUpper >= 0 ? mUpper : upper); } } /** * Returns the smallest range that includes this range and * another {@code range}. *

* E.g. if a {@code <} b {@code <} c {@code <} d, the * extension of [a, c] and [b, d] ranges is [a, d]. * As the endpoints are object references, there is no guarantee * which specific endpoint reference is used from the input ranges:

*

* E.g. if a {@code ==} a' {@code <} b {@code <} c, the * extension of [a, b] and [a', c] ranges could be either * [a, c] or ['a, c], where ['a, c] could be either the exact * input range, or a newly created range with the same endpoints.

* * @param range a non-{@code null} {@code Range} reference * @return the extension of this range and the other range. * * @throws NullPointerException if {@code range} was {@code null} */ public Range extend(Range range) { checkNotNull(range, "range must not be null"); int cmpLower = range.mLower.compareTo(mLower); int cmpUpper = range.mUpper.compareTo(mUpper); if (cmpLower <= 0 && cmpUpper >= 0) { // other includes this return range; } else if (cmpLower >= 0 && cmpUpper <= 0) { // this inludes other return this; } else { return Range.create( cmpLower >= 0 ? mLower : range.mLower, cmpUpper <= 0 ? mUpper : range.mUpper); } } /** * Returns the smallest range that includes this range and * the inclusive range specified by {@code [lower, upper]}. *

* See {@link #extend(Range)} for more details.

* * @param lower a non-{@code null} {@code T} reference * @param upper a non-{@code null} {@code T} reference * @return the extension of this range and the other range. * * @throws NullPointerException if {@code lower} or {@code * upper} was {@code null} */ public Range extend(T lower, T upper) { checkNotNull(lower, "lower must not be null"); checkNotNull(upper, "upper must not be null"); int cmpLower = lower.compareTo(mLower); int cmpUpper = upper.compareTo(mUpper); if (cmpLower >= 0 && cmpUpper <= 0) { // this inludes other return this; } else { return Range.create( cmpLower >= 0 ? mLower : lower, cmpUpper <= 0 ? mUpper : upper); } } /** * Returns the smallest range that includes this range and * the {@code value}. *

* See {@link #extend(Range)} for more details, as this method is * equivalent to {@code extend(Range.create(value, value))}.

* * @param value a non-{@code null} {@code T} reference * @return the extension of this range and the value. * * @throws NullPointerException if {@code value} was {@code null} */ public Range extend(T value) { checkNotNull(value, "value must not be null"); return extend(value, value); } /** * Return the range as a string representation {@code "[lower, upper]"}. * * @return string representation of the range */ @Override public String toString() { return String.format("[%s, %s]", mLower, mUpper); } /** * {@inheritDoc} */ @Override public int hashCode() { return HashCodeHelpers.hashCodeGeneric(mLower, mUpper); } private final T mLower; private final T mUpper; };