1b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin/*
2b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * Copyright (C) 2014 The Android Open Source Project
3b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin *
4b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
5b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * you may not use this file except in compliance with the License.
6b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * You may obtain a copy of the License at
7b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin *
8b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
9b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin *
10b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * Unless required by applicable law or agreed to in writing, software
11b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
12b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * See the License for the specific language governing permissions and
14b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * limitations under the License.
15b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin */
16b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
17b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkinpackage android.util;
18b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
190f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnarimport static com.android.internal.util.Preconditions.checkNotNull;
205b836f29a3f56f9d13b6004417330c41ec0c18aeJeff Sharkeyimport static com.android.internal.util.Preconditions.checkArgumentFinite;
215b836f29a3f56f9d13b6004417330c41ec0c18aeJeff Sharkey
22b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin/**
23b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * Immutable class for describing width and height dimensions in some arbitrary
24b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * unit.
25b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * <p>
26b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * Width and height are finite values stored as a floating point representation.
27b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin * </p>
28b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin */
295ef33984d0cc50bf4654b0d8e9557ac34d44fdddJeff Sharkeypublic final class SizeF {
30b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
31b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * Create a new immutable SizeF instance.
32b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
33b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * <p>Both the {@code width} and the {@code height} must be a finite number.
34b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * In particular, {@code NaN} and positive/negative infinity are illegal values.</p>
35b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
36b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @param width The width of the size
37b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @param height The height of the size
38b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
39b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @throws IllegalArgumentException
40b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *             if either {@code width} or {@code height} was not finite.
41b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
42b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public SizeF(final float width, final float height) {
43b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        mWidth = checkArgumentFinite(width, "width");
44b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        mHeight = checkArgumentFinite(height, "height");
45b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
46b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
47b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
48b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * Get the width of the size (as an arbitrary unit).
49b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @return width
50b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
51b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public float getWidth() {
52b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        return mWidth;
53b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
54b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
55b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
56b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * Get the height of the size (as an arbitrary unit).
57b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @return height
58b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
59b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public float getHeight() {
60b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        return mHeight;
61b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
62b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
63b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
64b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * Check if this size is equal to another size.
65b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
66b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * <p>Two sizes are equal if and only if both their widths and heights are the same.</p>
67b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
68b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * <p>For this purpose, the width/height float values are considered to be the same if and only
69b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value
70b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * when applied to each.</p>
71b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
72b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @return {@code true} if the objects were equal, {@code false} otherwise
73b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
74b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    @Override
75b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public boolean equals(final Object obj) {
76b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        if (obj == null) {
77b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin            return false;
78b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        }
79b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        if (this == obj) {
80b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin            return true;
81b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        }
82b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        if (obj instanceof SizeF) {
83b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin            final SizeF other = (SizeF) obj;
84b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin            return mWidth == other.mWidth && mHeight == other.mHeight;
85b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        }
86b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        return false;
87b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
88b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
89b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
90b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * Return the size represented as a string with the format {@code "WxH"}
91b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     *
92b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * @return string representation of the size
93b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
94b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    @Override
95b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public String toString() {
96b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        return mWidth + "x" + mHeight;
97b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
98b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
990f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar    private static NumberFormatException invalidSizeF(String s) {
1000f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        throw new NumberFormatException("Invalid SizeF: \"" + s + "\"");
1010f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar    }
1020f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar
1030f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar    /**
1040f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * Parses the specified string as a size value.
1050f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * <p>
1060f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * The ASCII characters {@code \}{@code u002a} ('*') and
1070f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * {@code \}{@code u0078} ('x') are recognized as separators between
1080f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * the width and height.</p>
1090f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * <p>
1100f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * For any {@code SizeF s}: {@code SizeF.parseSizeF(s.toString()).equals(s)}.
1110f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * However, the method also handles sizes expressed in the
1120f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * following forms:</p>
1130f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * <p>
1140f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * "<i>width</i>{@code x}<i>height</i>" or
1150f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * "<i>width</i>{@code *}<i>height</i>" {@code => new SizeF(width, height)},
1160f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * where <i>width</i> and <i>height</i> are string floats potentially
1170f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * containing a sign, such as "-10.3", "+7" or "5.2", but not containing
1180f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * an {@code 'x'} (such as a float in hexadecimal string format).</p>
1190f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     *
1200f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * <pre>{@code
1210f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * SizeF.parseSizeF("3.2*+6").equals(new SizeF(3.2f, 6.0f)) == true
1220f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * SizeF.parseSizeF("-3x-6").equals(new SizeF(-3.0f, -6.0f)) == true
1230f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * SizeF.parseSizeF("4 by 3") => throws NumberFormatException
1240f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * }</pre>
1250f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     *
1260f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * @param string the string representation of a size value.
1270f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * @return the size value represented by {@code string}.
1280f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     *
1290f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * @throws NumberFormatException if {@code string} cannot be parsed
1300f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * as a size value.
1310f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     * @throws NullPointerException if {@code string} was {@code null}
1320f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar     */
1330f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar    public static SizeF parseSizeF(String string)
1340f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            throws NumberFormatException {
1350f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        checkNotNull(string, "string must not be null");
1360f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar
1370f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        int sep_ix = string.indexOf('*');
1380f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        if (sep_ix < 0) {
1390f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            sep_ix = string.indexOf('x');
1400f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        }
1410f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        if (sep_ix < 0) {
1420f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            throw invalidSizeF(string);
1430f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        }
1440f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        try {
1450f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            return new SizeF(Float.parseFloat(string.substring(0, sep_ix)),
1460f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar                    Float.parseFloat(string.substring(sep_ix + 1)));
1470f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        } catch (NumberFormatException e) {
1480f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            throw invalidSizeF(string);
1490f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        } catch (IllegalArgumentException e) {
1500f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar            throw invalidSizeF(string);
1510f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar        }
1520f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar    }
1530f491d7513f41449ce50095958f4dd7f5e17df91Lajos Molnar
154b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    /**
155b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     * {@inheritDoc}
156b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin     */
157b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    @Override
158b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    public int hashCode() {
159b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin        return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
160b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    }
161b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin
162b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    private final float mWidth;
163b3a78b2ca9655396e2d73950221d187b7e5bb3baIgor Murashkin    private final float mHeight;
1645b836f29a3f56f9d13b6004417330c41ec0c18aeJeff Sharkey}
165