blob: ab89197895d5a59413f256b56186392fd211224d [file] [log] [blame]
/*
* 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 com.google.gwt.core.client.JavaScriptObject;
import java.io.Serializable;
/**
* Abstract base class for numeric wrapper classes.
*/
public abstract class Number implements Serializable {
/**
* Stores a regular expression object to verify format of float values.
*/
protected static JavaScriptObject 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 __Digits {
final static char[] digits = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
}
/**
* 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];
}
}
}
/**
* @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 long __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;
}
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 __parseDouble(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 new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + " out of range");
}
int length = s.length();
int startIndex = (length > 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 = __parseInt(s, radix);
if (__isNaN(toReturn)) {
throw NumberFormatException.forInputString(s);
} else if (toReturn < lowerBound || 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 new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix + " out of range");
}
final String orig = s;
int length = s.length();
boolean negative = (length > 0) && (s.charAt(0) == '-');
if (negative) {
s = s.substring(1);
length--;
}
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
int maxNumericDigit = '0' + Math.min(radix, 10);
int maxLowerCaseDigit = radix + 'a' - 10;
int maxUpperCaseDigit = radix + 'A' - 10;
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c >= '0' && c < maxNumericDigit) {
continue;
}
if (c >= 'a' && c < maxLowerCaseDigit) {
continue;
}
if (c >= 'A' && c < maxUpperCaseDigit) {
continue;
}
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 = - __parseInt(s.substring(0, head), radix);
s = s.substring(head);
length -= head;
firstTime = false;
}
while (length >= maxDigits) {
head = __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 new NumberFormatException(s);
}
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
*/
private static native boolean __isNaN(double x) /*-{
return isNaN(x);
}-*/;
/**
* @skip
*
* @param str
* @return {@code true} if the string matches {@link #floatRegex}, {@code false} otherwise
*/
private static native boolean __isValidDouble(String str) /*-{
var floatRegex = @java.lang.Number::floatRegex;
if (!floatRegex) {
// Disallow '.' with no digits on either side
floatRegex = @java.lang.Number::floatRegex =
/^\s*[+-]?(NaN|Infinity|((\d+\.?\d*)|(\.\d+))([eE][+-]?\d+)?[dDfF]?)\s*$/;
}
return floatRegex.test(str);
}-*/;
/**
* @skip
*
* @return The floating-point representation of <code>str</code>.
*/
private static native double __parseDouble(String str) /*-{
return parseFloat(str);
}-*/;
/**
* @skip
*
* Invokes the global JS function <code>parseInt()</code>.
*/
private static native int __parseInt(String s, int radix) /*-{
return parseInt(s, radix);
}-*/;
// 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();
}
}