diff --git a/api/current.txt b/api/current.txt index 63d62bda4f72b..a2c4597d95cda 100644 --- a/api/current.txt +++ b/api/current.txt @@ -31334,10 +31334,17 @@ package android.util { public final class Range { ctor public Range(T, T); + method public T clamp(T); + method public boolean contains(T); + method public boolean contains(android.util.Range); method public static android.util.Range create(T, T); + method public android.util.Range extend(android.util.Range); + method public android.util.Range extend(T, T); + method public android.util.Range extend(T); method public T getLower(); method public T getUpper(); - method public boolean inRange(T); + method public android.util.Range intersect(android.util.Range); + method public android.util.Range intersect(T, T); } public final class Rational extends java.lang.Number implements java.lang.Comparable { @@ -31353,6 +31360,7 @@ package android.util { method public boolean isNaN(); method public boolean isZero(); method public long longValue(); + method public static android.util.Rational parseRational(java.lang.String) throws java.lang.NumberFormatException; field public static final android.util.Rational NEGATIVE_INFINITY; field public static final android.util.Rational NaN; field public static final android.util.Rational POSITIVE_INFINITY; @@ -31363,6 +31371,7 @@ package android.util { ctor public Size(int, int); method public int getHeight(); method public int getWidth(); + method public static android.util.Size parseSize(java.lang.String) throws java.lang.NumberFormatException; } public final class SizeF { diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index 3907e7708acee..211d01a9e2efd 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -99,16 +99,16 @@ public final class Range> { /** * 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 >=} then - * the lower endpoint and {@code <=} to the upper endpoint (using the {@link Comparable} - * interface.

+ *

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 inRange(T value) { + public boolean contains(T value) { checkNotNull(value, "value must not be null"); boolean gteLower = value.compareTo(mLower) >= 0; @@ -117,6 +117,26 @@ public final class Range> { 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. * @@ -139,6 +159,182 @@ public final class Range> { 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]"}. * diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java index 9952859aea721..80d26d943ed75 100644 --- a/core/java/android/util/Rational.java +++ b/core/java/android/util/Rational.java @@ -536,8 +536,67 @@ public final class Rational extends Number implements Comparable { } else { // finite value if (gcd(mNumerator, mDenominator) > 1) { throw new InvalidObjectException( - "Rational must be deserialized from a reduced form for finite values"); + "Rational must be deserialized from a reduced form for finite values"); } } } + + private static NumberFormatException invalidRational(String s) { + throw new NumberFormatException("Invalid Rational: \"" + s + "\""); + } + + /** + * Parses the specified string as a rational value. + *

The ASCII characters {@code \}{@code u003a} (':') and + * {@code \}{@code u002f} ('/') are recognized as separators between + * the numerator and denumerator.

+ *

+ * For any {@code Rational r}: {@code Rational.parseRational(r.toString()).equals(r)}. + * However, the method also handles rational numbers expressed in the + * following forms:

+ *

+ * "num{@code /}den" or + * "num{@code :}den" {@code => new Rational(num, den);}, + * where num and den are string integers potentially + * containing a sign, such as "-10", "+7" or "5".

+ * + *
{@code
+     * Rational.parseRational("3:+6").equals(new Rational(1, 2)) == true
+     * Rational.parseRational("-3/-6").equals(new Rational(1, 2)) == true
+     * Rational.parseRational("4.56") => throws NumberFormatException
+     * }
+ * + * @param string the string representation of a rational value. + * @return the rational value represented by {@code string}. + * + * @throws NumberFormatException if {@code string} cannot be parsed + * as a rational value. + * @throws NullPointerException if {@code string} was {@code null} + */ + public static Rational parseRational(String string) + throws NumberFormatException { + checkNotNull(string, "string must not be null"); + + if (string.equals("NaN")) { + return NaN; + } else if (string.equals("Infinity")) { + return POSITIVE_INFINITY; + } else if (string.equals("-Infinity")) { + return NEGATIVE_INFINITY; + } + + int sep_ix = string.indexOf(':'); + if (sep_ix < 0) { + sep_ix = string.indexOf('/'); + } + if (sep_ix < 0) { + throw invalidRational(string); + } + try { + return new Rational(Integer.parseInt(string.substring(0, sep_ix)), + Integer.parseInt(string.substring(sep_ix + 1))); + } catch (NumberFormatException e) { + throw invalidRational(string); + } + } } diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java index ba1a35f474d6a..d58f7785e4a2e 100644 --- a/core/java/android/util/Size.java +++ b/core/java/android/util/Size.java @@ -16,6 +16,8 @@ package android.util; +import static com.android.internal.util.Preconditions.*; + /** * Immutable class for describing width and height dimensions in pixels. */ @@ -84,6 +86,58 @@ public final class Size { return mWidth + "x" + mHeight; } + private static NumberFormatException invalidSize(String s) { + throw new NumberFormatException("Invalid Size: \"" + s + "\""); + } + + /** + * Parses the specified string as a size value. + *

+ * The ASCII characters {@code \}{@code u002a} ('*') and + * {@code \}{@code u0078} ('x') are recognized as separators between + * the width and height.

+ *

+ * For any {@code Size s}: {@code Size.parseSize(s.toString()).equals(s)}. + * However, the method also handles sizes expressed in the + * following forms:

+ *

+ * "width{@code x}height" or + * "width{@code *}height" {@code => new Size(width, height)}, + * where width and height are string integers potentially + * containing a sign, such as "-10", "+7" or "5".

+ * + *
{@code
+     * Size.parseSize("3*+6").equals(new Size(3, 6)) == true
+     * Size.parseSize("-3x-6").equals(new Size(-3, -6)) == true
+     * Size.parseSize("4 by 3") => throws NumberFormatException
+     * }
+ * + * @param string the string representation of a size value. + * @return the size value represented by {@code string}. + * + * @throws NumberFormatException if {@code string} cannot be parsed + * as a size value. + * @throws NullPointerException if {@code string} was {@code null} + */ + public static Size parseSize(String string) + throws NumberFormatException { + checkNotNull(string, "string must not be null"); + + int sep_ix = string.indexOf('*'); + if (sep_ix < 0) { + sep_ix = string.indexOf('x'); + } + if (sep_ix < 0) { + throw invalidSize(string); + } + try { + return new Size(Integer.parseInt(string.substring(0, sep_ix)), + Integer.parseInt(string.substring(sep_ix + 1))); + } catch (NumberFormatException e) { + throw invalidSize(string); + } + } + /** * {@inheritDoc} */ diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java index d90a4bc2f8146..9dd2732cb0f39 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RangeTest.java @@ -137,12 +137,12 @@ public class RangeTest extends junit.framework.TestCase { } private static > void assertInRange(Range object, T needle) { - assertAction("in-range", object, needle, true, object.inRange(needle)); + assertAction("in-range", object, needle, true, object.contains(needle)); } private static > void assertOutOfRange(Range object, T needle) { - assertAction("out-of-range", object, needle, false, object.inRange(needle)); + assertAction("out-of-range", object, needle, false, object.contains(needle)); } private static > void assertUpper(Range object, T expected) {