src/share/classes/java/lang/Long.java

Print this page
rev 10062 : 8041972: Add improved parse/format methods for Long/Integer
Contributed-by: redestad

@@ -24,10 +24,11 @@
  */
 
 package java.lang;
 
 import java.lang.annotation.Native;
+import static java.lang.Integer.requireNonEmpty;
 import java.math.*;
 
 
 /**
  * The {@code Long} class wraps a value of the primitive type {@code

@@ -544,16 +545,80 @@
      *             the specified radix.
      * @throws     NumberFormatException  if the string does not contain a
      *             parsable {@code long}.
      */
     public static long parseLong(String s, int radix)
-              throws NumberFormatException
-    {
-        if (s == null) {
-            throw new NumberFormatException("null");
+              throws NumberFormatException {
+        requireNonEmpty(s);
+        return parseLong(s, radix, 0, s.length());
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code long} in
+     * the specified {@code radix}, beginning at the specified {@code beginIndex}
+     * and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code long}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the signed {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IllegalArgumentException   if {@code beginIndex} is greater
+     *             than or equal to {@code s.length()}.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is negative.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseLong(CharSequence s, int radix, int beginIndex)
+            throws NumberFormatException {
+        // forces a null check of s
+        return parseLong(s, radix, beginIndex, s.length());
         }
 
+    /**
+     * Parses the {@link CharSequence} argument as a signed {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the {@code long}
+     *                  representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the signed {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IllegalArgumentException   if {@code beginIndex} is greater
+     *             than or equal to {@code endIndex}.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, {@code endIndex} is negative or {@code endIndex}
+     *             is greater than {@code s.length()}
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseLong(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s.getClass(); // null check
+        if (beginIndex >= endIndex) {
+            throw new IllegalArgumentException("beginIndex must be less than endIndex");
+        }
         if (radix < Character.MIN_RADIX) {
             throw new NumberFormatException("radix " + radix +
                                             " less than Character.MIN_RADIX");
         }
         if (radix > Character.MAX_RADIX) {

@@ -561,47 +626,41 @@
                                             " greater than Character.MAX_RADIX");
         }
 
         long result = 0;
         boolean negative = false;
-        int i = 0, len = s.length();
+        int i = beginIndex, end = endIndex;
         long limit = -Long.MAX_VALUE;
         long multmin;
         int digit;
 
-        if (len > 0) {
-            char firstChar = s.charAt(0);
+        char firstChar = s.charAt(i);
             if (firstChar < '0') { // Possible leading "+" or "-"
                 if (firstChar == '-') {
                     negative = true;
                     limit = Long.MIN_VALUE;
-                } else if (firstChar != '+')
-                    throw NumberFormatException.forInputString(s);
-
-                if (len == 1) // Cannot have lone "+" or "-"
-                    throw NumberFormatException.forInputString(s);
+            } else if (firstChar != '+') {
+                throw NumberFormatException.forInputString(s.toString());
+            }
                 i++;
+            if (end == i) { // Cannot have lone "+" or "-"
+                throw NumberFormatException.forInputString(s.toString());
+            }
             }
             multmin = limit / radix;
-            while (i < len) {
+        while (i < end) {
                 // Accumulating negatively avoids surprises near MAX_VALUE
-                digit = Character.digit(s.charAt(i++),radix);
-                if (digit < 0) {
-                    throw NumberFormatException.forInputString(s);
-                }
-                if (result < multmin) {
-                    throw NumberFormatException.forInputString(s);
+            digit = Character.digit(s.charAt(i++), radix);
+            if (digit < 0 || result < multmin) {
+                throw NumberFormatException.forInputString(s.toString());
                 }
                 result *= radix;
                 if (result < limit + digit) {
-                    throw NumberFormatException.forInputString(s);
+                throw NumberFormatException.forInputString(s.toString());
                 }
                 result -= digit;
             }
-        } else {
-            throw NumberFormatException.forInputString(s);
-        }
         return negative ? result : -result;
     }
 
     /**
      * Parses the string argument as a signed decimal {@code long}.

@@ -626,11 +685,12 @@
      *             decimal.
      * @throws     NumberFormatException  if the string does not contain a
      *             parsable {@code long}.
      */
     public static long parseLong(String s) throws NumberFormatException {
-        return parseLong(s, 10);
+        requireNonEmpty(s);
+        return parseLong(s, 10, 0, s.length());
     }
 
     /**
      * Parses the string argument as an unsigned {@code long} in the
      * radix specified by the second argument.  An unsigned integer

@@ -674,32 +734,97 @@
      *             does not contain a parsable {@code long}.
      * @since 1.8
      */
     public static long parseUnsignedLong(String s, int radix)
                 throws NumberFormatException {
-        if (s == null)  {
-            throw new NumberFormatException("null");
+        requireNonEmpty(s);
+        return parseUnsignedLong(s, radix, 0);
+    }
+
+    /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to the end of the sequence.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code long} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @return     the unsigned {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IllegalArgumentException  if {@code beginIndex} is greater
+     *             than or equal to {@code s.length()}.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is negative.
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex)
+                throws NumberFormatException {
+        // forces a null check of s
+        return parseUnsignedLong(s, radix, beginIndex, s.length());
         }
 
-        int len = s.length();
+    /**
+     * Parses the {@link CharSequence} argument as an unsigned {@code long} in
+     * the specified {@code radix}, beginning at the specified
+     * {@code beginIndex} and extending to {@code endIndex - 1}.
+     *
+     * <p>The method does not take steps to guard against the
+     * {@code CharSequence} being mutated while parsing.
+     *
+     * @param      s   the {@code CharSequence} containing the unsigned
+     *                 {@code long} representation to be parsed
+     * @param      radix   the radix to be used while parsing {@code s}.
+     * @param      beginIndex   the beginning index, inclusive.
+     * @param      endIndex     the ending index, exclusive.
+     * @return     the unsigned {@code long} represented by the subsequence in
+     *             the specified radix.
+     * @throws     NullPointerException  if {@code s} is null.
+     * @throws     IllegalArgumentException  if {@code beginIndex} is greater
+     *             than or equal to {@code endIndex}.
+     * @throws     IndexOutOfBoundsException  if {@code beginIndex} is
+     *             negative, {@code endIndex} is negative or {@code endIndex}
+     *             is greater than {@code s.length()}
+     * @throws     NumberFormatException  if the {@code CharSequence} does not
+     *             contain a parsable unsigned {@code long} in the specified
+     *             {@code radix}, or if {@code radix} is either smaller than
+     *             {@link java.lang.Character#MIN_RADIX} or larger than
+     *             {@link java.lang.Character#MAX_RADIX}.
+     * @since  1.9
+     */
+    public static long parseUnsignedLong(CharSequence s, int radix, int beginIndex, int endIndex)
+                throws NumberFormatException {
+        s.getClass(); // null check
+        if (beginIndex >= endIndex) {
+            throw new IllegalArgumentException("beginIndex must be less than endIndex");
+        }
+        int start = beginIndex, len = endIndex - beginIndex;
         if (len > 0) {
-            char firstChar = s.charAt(0);
+            char firstChar = s.charAt(start);
             if (firstChar == '-') {
-                throw new
-                    NumberFormatException(String.format("Illegal leading minus sign " +
-                                                       "on unsigned string %s.", s));
+                throw new NumberFormatException(String.format("Illegal leading minus sign " +
+                        "on unsigned string %s.", s.subSequence(start, start + len)));
             } else {
                 if (len <= 12 || // Long.MAX_VALUE in Character.MAX_RADIX is 13 digits
                     (radix == 10 && len <= 18) ) { // Long.MAX_VALUE in base 10 is 19 digits
-                    return parseLong(s, radix);
+                    return parseLong(s, radix, start, start + len);
                 }
 
-                // No need for range checks on len due to testing above.
-                long first = parseLong(s.substring(0, len - 1), radix);
-                int second = Character.digit(s.charAt(len - 1), radix);
+                // No need for range checks on end due to testing above.
+                long first = parseLong(s, radix, start, start + len - 1);
+                int second = Character.digit(s.charAt(start + len - 1), radix);
                 if (second < 0) {
-                    throw new NumberFormatException("Bad digit at end of " + s);
+                    throw new NumberFormatException("Bad digit at end of " +
+                            s.subSequence(start, start + len));
                 }
                 long result = first * radix + second;
 
                 /*
                  * Test leftmost bits of multiprecision extension of first*radix

@@ -752,16 +877,16 @@
                      * upper bound is too small to overflow again after the
                      * signed long overflows to positive above 2^64 - 1. Hence
                      * result >= 0 implies overflow given C and D.
                      */
                     throw new NumberFormatException(String.format("String value %s exceeds " +
-                                                                  "range of unsigned long.", s));
+                            "range of unsigned long.", s.subSequence(start, start + len)));
                 }
                 return result;
             }
         } else {
-            throw NumberFormatException.forInputString(s);
+            throw NumberFormatException.forInputString("");
         }
     }
 
     /**
      * Parses the string argument as an unsigned decimal {@code long}. The

@@ -778,11 +903,12 @@
      * @throws    NumberFormatException  if the string does not contain a
      *            parsable unsigned integer.
      * @since 1.8
      */
     public static long parseUnsignedLong(String s) throws NumberFormatException {
-        return parseUnsignedLong(s, 10);
+        requireNonEmpty(s);
+        return parseUnsignedLong(s, 10, 0, s.length());
     }
 
     /**
      * Returns a {@code Long} object holding the value
      * extracted from the specified {@code String} when parsed

@@ -808,11 +934,12 @@
      *             radix.
      * @throws     NumberFormatException  If the {@code String} does not
      *             contain a parsable {@code long}.
      */
     public static Long valueOf(String s, int radix) throws NumberFormatException {
-        return Long.valueOf(parseLong(s, radix));
+        requireNonEmpty(s);
+        return Long.valueOf(parseLong(s, radix, 0, s.length()));
     }
 
     /**
      * Returns a {@code Long} object holding the value
      * of the specified {@code String}. The argument is

@@ -833,13 +960,13 @@
      * @return     a {@code Long} object holding the value
      *             represented by the string argument.
      * @throws     NumberFormatException  If the string cannot be parsed
      *             as a {@code long}.
      */
-    public static Long valueOf(String s) throws NumberFormatException
-    {
-        return Long.valueOf(parseLong(s, 10));
+    public static Long valueOf(String s) throws NumberFormatException {
+        requireNonEmpty(s);
+        return Long.valueOf(parseLong(s, 10, 0, s.length()));
     }
 
     private static class LongCache {
         private LongCache(){}
 

@@ -997,11 +1124,12 @@
      * @throws     NumberFormatException  if the {@code String} does not
      *             contain a parsable {@code long}.
      * @see        java.lang.Long#parseLong(java.lang.String, int)
      */
     public Long(String s) throws NumberFormatException {
-        this.value = parseLong(s, 10);
+        requireNonEmpty(s);
+        this.value = parseLong(s, 10, 0, s.length());
     }
 
     /**
      * Returns the value of this {@code Long} as a {@code byte} after
      * a narrowing primitive conversion.