| /* |
| * Copyright 2007 Google Inc. |
| * |
| * 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 java.lang; |
| |
| import java.io.Serializable; |
| import javaemul.internal.JsUtils; |
| import javaemul.internal.NativeRegExp; |
| import jsinterop.annotations.JsMethod; |
| import jsinterop.annotations.JsType; |
| |
| /** |
| * Abstract base class for numeric wrapper classes. |
| */ |
| public abstract class Number implements Serializable { |
| |
| /** |
| * Stores a regular expression object to verify the format of float values. |
| */ |
| private static NativeRegExp floatRegex; |
| |
| // CHECKSTYLE_OFF: A special need to use unusual identifiers to avoid |
| // introducing name collisions. |
| |
| static class __Decode { |
| public final String payload; |
| public final int radix; |
| |
| public __Decode(int radix, String payload) { |
| this.radix = radix; |
| this.payload = payload; |
| } |
| } |
| |
| /** |
| * Use nested class to avoid clinit on outer. |
| */ |
| static class __ParseLong { |
| /** |
| * The number of digits (excluding minus sign and leading zeros) to process |
| * at a time. The largest value expressible in maxDigits digits as well as |
| * the factor radix^maxDigits must be strictly less than 2^31. |
| */ |
| private static final int[] maxDigitsForRadix = {-1, -1, // unused |
| 30, // base 2 |
| 19, // base 3 |
| 15, // base 4 |
| 13, // base 5 |
| 11, 11, // base 6-7 |
| 10, // base 8 |
| 9, 9, // base 9-10 |
| 8, 8, 8, 8, // base 11-14 |
| 7, 7, 7, 7, 7, 7, 7, // base 15-21 |
| 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // base 22-35 |
| 5 // base 36 |
| }; |
| |
| /** |
| * A table of values radix*maxDigitsForRadix[radix]. |
| */ |
| private static final int[] maxDigitsRadixPower = new int[37]; |
| |
| /** |
| * The largest number of digits (excluding minus sign and leading zeros) that |
| * can fit into a long for a given radix between 2 and 36, inclusive. |
| */ |
| private static final int[] maxLengthForRadix = {-1, -1, // unused |
| 63, // base 2 |
| 40, // base 3 |
| 32, // base 4 |
| 28, // base 5 |
| 25, // base 6 |
| 23, // base 7 |
| 21, // base 8 |
| 20, // base 9 |
| 19, // base 10 |
| 19, // base 11 |
| 18, // base 12 |
| 18, // base 13 |
| 17, // base 14 |
| 17, // base 15 |
| 16, // base 16 |
| 16, // base 17 |
| 16, // base 18 |
| 15, // base 19 |
| 15, // base 20 |
| 15, // base 21 |
| 15, // base 22 |
| 14, // base 23 |
| 14, // base 24 |
| 14, // base 25 |
| 14, // base 26 |
| 14, // base 27 |
| 14, // base 28 |
| 13, // base 29 |
| 13, // base 30 |
| 13, // base 31 |
| 13, // base 32 |
| 13, // base 33 |
| 13, // base 34 |
| 13, // base 35 |
| 13 // base 36 |
| }; |
| |
| /** |
| * A table of floor(MAX_VALUE / maxDigitsRadixPower). |
| */ |
| private static final long[] maxValueForRadix = new long[37]; |
| |
| static { |
| for (int i = 2; i <= 36; i++) { |
| maxDigitsRadixPower[i] = (int) Math.pow(i, maxDigitsForRadix[i]); |
| maxValueForRadix[i] = Long.MAX_VALUE / maxDigitsRadixPower[i]; |
| } |
| } |
| } |
| |
| @JsType(isNative = true, name = "Number$impl", namespace = "java.lang") |
| private static class JavaLangNumber { } |
| |
| @JsMethod |
| private static boolean $isInstance(Object instance) { |
| return "number".equals(JsUtils.typeOf(instance)) || instance instanceof JavaLangNumber; |
| } |
| |
| /** |
| * @skip |
| * |
| * This function will determine the radix that the string is expressed in |
| * based on the parsing rules defined in the Javadocs for Integer.decode() and |
| * invoke __parseAndValidateInt. |
| */ |
| protected static int __decodeAndValidateInt(String s, int lowerBound, |
| int upperBound) throws NumberFormatException { |
| __Decode decode = __decodeNumberString(s); |
| return __parseAndValidateInt(decode.payload, decode.radix, lowerBound, |
| upperBound); |
| } |
| |
| protected static __Decode __decodeNumberString(String s) { |
| final boolean negative; |
| if (s.startsWith("-")) { |
| negative = true; |
| s = s.substring(1); |
| } else { |
| negative = false; |
| if (s.startsWith("+")) { |
| s = s.substring(1); |
| } |
| } |
| |
| final int radix; |
| if (s.startsWith("0x") || s.startsWith("0X")) { |
| s = s.substring(2); |
| radix = 16; |
| } else if (s.startsWith("#")) { |
| s = s.substring(1); |
| radix = 16; |
| } else if (s.startsWith("0")) { |
| radix = 8; |
| } else { |
| radix = 10; |
| } |
| |
| if (negative) { |
| s = "-" + s; |
| } |
| return new __Decode(radix, s); |
| } |
| |
| /** |
| * @skip |
| * |
| * This function contains common logic for parsing a String as a floating- |
| * point number and validating the range. |
| */ |
| protected static double __parseAndValidateDouble(String s) throws NumberFormatException { |
| if (!__isValidDouble(s)) { |
| throw NumberFormatException.forInputString(s); |
| } |
| return JsUtils.parseFloat(s); |
| } |
| |
| /** |
| * @skip |
| * |
| * This function contains common logic for parsing a String in a given radix |
| * and validating the result. |
| */ |
| protected static int __parseAndValidateInt(String s, int radix, int lowerBound, int upperBound) |
| throws NumberFormatException { |
| if (s == null) { |
| throw NumberFormatException.forNullInputString(); |
| } |
| if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { |
| throw NumberFormatException.forRadix(radix); |
| } |
| |
| int length = s.length(); |
| int startIndex = (length > 0) && (s.charAt(0) == '-' || s.charAt(0) == '+') ? 1 : 0; |
| |
| for (int i = startIndex; i < length; i++) { |
| if (Character.digit(s.charAt(i), radix) == -1) { |
| throw NumberFormatException.forInputString(s); |
| } |
| } |
| |
| int toReturn = JsUtils.parseInt(s, radix); |
| // isTooLow is separated into its own variable to avoid a bug in BlackBerry OS 7. See |
| // https://code.google.com/p/google-web-toolkit/issues/detail?id=7291. |
| boolean isTooLow = toReturn < lowerBound; |
| if (Double.isNaN(toReturn)) { |
| throw NumberFormatException.forInputString(s); |
| } else if (isTooLow || toReturn > upperBound) { |
| throw NumberFormatException.forInputString(s); |
| } |
| |
| return toReturn; |
| } |
| |
| /** |
| * @skip |
| * |
| * This function contains common logic for parsing a String in a given radix |
| * and validating the result. |
| */ |
| protected static long __parseAndValidateLong(String s, int radix) throws NumberFormatException { |
| if (s == null) { |
| throw NumberFormatException.forNullInputString(); |
| } |
| if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { |
| throw NumberFormatException.forRadix(radix); |
| } |
| |
| final String orig = s; |
| |
| int length = s.length(); |
| boolean negative = false; |
| if (length > 0) { |
| char c = s.charAt(0); |
| if (c == '-' || c == '+') { |
| s = s.substring(1); |
| length--; |
| negative = (c == '-'); |
| } |
| } |
| if (length == 0) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| |
| // Strip leading zeros |
| while (s.length() > 0 && s.charAt(0) == '0') { |
| s = s.substring(1); |
| length--; |
| } |
| |
| // Immediately eject numbers that are too long -- this avoids more complex |
| // overflow handling below |
| if (length > __ParseLong.maxLengthForRadix[radix]) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| |
| // Validate the digits |
| for (int i = 0; i < length; i++) { |
| if (Character.digit(s.charAt(i), radix) == -1) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| } |
| |
| long toReturn = 0; |
| int maxDigits = __ParseLong.maxDigitsForRadix[radix]; |
| long radixPower = __ParseLong.maxDigitsRadixPower[radix]; |
| long minValue = -__ParseLong.maxValueForRadix[radix]; |
| |
| boolean firstTime = true; |
| int head = length % maxDigits; |
| if (head > 0) { |
| // accumulate negative numbers, as -Long.MAX_VALUE == Long.MIN_VALUE + 1 |
| // (in other words, -Long.MIN_VALUE overflows, see issue 7308) |
| toReturn = - JsUtils.parseInt(s.substring(0, head), radix); |
| s = s.substring(head); |
| length -= head; |
| firstTime = false; |
| } |
| |
| while (length >= maxDigits) { |
| head = JsUtils.parseInt(s.substring(0, maxDigits), radix); |
| s = s.substring(maxDigits); |
| length -= maxDigits; |
| if (!firstTime) { |
| // Check whether multiplying by radixPower will overflow |
| if (toReturn < minValue) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| toReturn *= radixPower; |
| } else { |
| firstTime = false; |
| } |
| toReturn -= head; |
| } |
| |
| // A positive value means we overflowed Long.MIN_VALUE |
| if (toReturn > 0) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| |
| if (!negative) { |
| toReturn = -toReturn; |
| // A negative value means we overflowed Long.MAX_VALUE |
| if (toReturn < 0) { |
| throw NumberFormatException.forInputString(orig); |
| } |
| } |
| return toReturn; |
| } |
| |
| /** |
| * @skip |
| * |
| * @param str |
| * @return {@code true} if the string matches the float format, {@code false} otherwise |
| */ |
| private static boolean __isValidDouble(String str) { |
| if (floatRegex == null) { |
| floatRegex = |
| new NativeRegExp( |
| "^\\s*[+-]?(NaN|Infinity|((\\d+\\.?\\d*)|(\\.\\d+))([eE][+-]?\\d+)?[dDfF]?)\\s*$"); |
| } |
| return floatRegex.test(str); |
| } |
| |
| // CHECKSTYLE_ON |
| |
| public byte byteValue() { |
| return (byte) intValue(); |
| } |
| |
| public abstract double doubleValue(); |
| |
| public abstract float floatValue(); |
| |
| public abstract int intValue(); |
| |
| public abstract long longValue(); |
| |
| public short shortValue() { |
| return (short) intValue(); |
| } |
| } |