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.checkNotNull;
20 import static com.android.internal.util.Preconditions.checkArgumentFinite;
21 
22 /**
23  * Immutable class for describing width and height dimensions in some arbitrary
24  * unit.
25  * <p>
26  * Width and height are finite values stored as a floating point representation.
27  * </p>
28  */
29 public final class SizeF {
30     /**
31      * Create a new immutable SizeF instance.
32      *
33      * <p>Both the {@code width} and the {@code height} must be a finite number.
34      * In particular, {@code NaN} and positive/negative infinity are illegal values.</p>
35      *
36      * @param width The width of the size
37      * @param height The height of the size
38      *
39      * @throws IllegalArgumentException
40      *             if either {@code width} or {@code height} was not finite.
41      */
SizeF(final float width, final float height)42     public SizeF(final float width, final float height) {
43         mWidth = checkArgumentFinite(width, "width");
44         mHeight = checkArgumentFinite(height, "height");
45     }
46 
47     /**
48      * Get the width of the size (as an arbitrary unit).
49      * @return width
50      */
getWidth()51     public float getWidth() {
52         return mWidth;
53     }
54 
55     /**
56      * Get the height of the size (as an arbitrary unit).
57      * @return height
58      */
getHeight()59     public float getHeight() {
60         return mHeight;
61     }
62 
63     /**
64      * Check if this size is equal to another size.
65      *
66      * <p>Two sizes are equal if and only if both their widths and heights are the same.</p>
67      *
68      * <p>For this purpose, the width/height float values are considered to be the same if and only
69      * if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value
70      * when applied to each.</p>
71      *
72      * @return {@code true} if the objects were equal, {@code false} otherwise
73      */
74     @Override
equals(final Object obj)75     public boolean equals(final Object obj) {
76         if (obj == null) {
77             return false;
78         }
79         if (this == obj) {
80             return true;
81         }
82         if (obj instanceof SizeF) {
83             final SizeF other = (SizeF) obj;
84             return mWidth == other.mWidth && mHeight == other.mHeight;
85         }
86         return false;
87     }
88 
89     /**
90      * Return the size represented as a string with the format {@code "WxH"}
91      *
92      * @return string representation of the size
93      */
94     @Override
toString()95     public String toString() {
96         return mWidth + "x" + mHeight;
97     }
98 
invalidSizeF(String s)99     private static NumberFormatException invalidSizeF(String s) {
100         throw new NumberFormatException("Invalid SizeF: \"" + s + "\"");
101     }
102 
103     /**
104      * Parses the specified string as a size value.
105      * <p>
106      * The ASCII characters {@code \}{@code u002a} ('*') and
107      * {@code \}{@code u0078} ('x') are recognized as separators between
108      * the width and height.</p>
109      * <p>
110      * For any {@code SizeF s}: {@code SizeF.parseSizeF(s.toString()).equals(s)}.
111      * However, the method also handles sizes expressed in the
112      * following forms:</p>
113      * <p>
114      * "<i>width</i>{@code x}<i>height</i>" or
115      * "<i>width</i>{@code *}<i>height</i>" {@code => new SizeF(width, height)},
116      * where <i>width</i> and <i>height</i> are string floats potentially
117      * containing a sign, such as "-10.3", "+7" or "5.2", but not containing
118      * an {@code 'x'} (such as a float in hexadecimal string format).</p>
119      *
120      * <pre>{@code
121      * SizeF.parseSizeF("3.2*+6").equals(new SizeF(3.2f, 6.0f)) == true
122      * SizeF.parseSizeF("-3x-6").equals(new SizeF(-3.0f, -6.0f)) == true
123      * SizeF.parseSizeF("4 by 3") => throws NumberFormatException
124      * }</pre>
125      *
126      * @param string the string representation of a size value.
127      * @return the size value represented by {@code string}.
128      *
129      * @throws NumberFormatException if {@code string} cannot be parsed
130      * as a size value.
131      * @throws NullPointerException if {@code string} was {@code null}
132      */
parseSizeF(String string)133     public static SizeF parseSizeF(String string)
134             throws NumberFormatException {
135         checkNotNull(string, "string must not be null");
136 
137         int sep_ix = string.indexOf('*');
138         if (sep_ix < 0) {
139             sep_ix = string.indexOf('x');
140         }
141         if (sep_ix < 0) {
142             throw invalidSizeF(string);
143         }
144         try {
145             return new SizeF(Float.parseFloat(string.substring(0, sep_ix)),
146                     Float.parseFloat(string.substring(sep_ix + 1)));
147         } catch (NumberFormatException e) {
148             throw invalidSizeF(string);
149         } catch (IllegalArgumentException e) {
150             throw invalidSizeF(string);
151         }
152     }
153 
154     /**
155      * {@inheritDoc}
156      */
157     @Override
hashCode()158     public int hashCode() {
159         return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
160     }
161 
162     private final float mWidth;
163     private final float mHeight;
164 }
165