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