/* * Javolution - Java(TM) Solution for Real-Time and Embedded Systems * Copyright (C) 2006 - Javolution (http://javolution.org/) * All rights reserved. * * Permission to use, copy, modify, and distribute this software is * freely granted, provided that this notice is preserved. */ package javolution.text; import j2me.lang.CharSequence; import javolution.text.TextFormat.Cursor; import java.io.IOException; /** *

This class provides utility methods to parse CharSequence * into primitive types and to format primitive types into any * Appendable.

* *

Methods from this class do not create temporary objects and * are typically faster than standard library methods (see * benchmark).

* *

The number of digits when formatting floating point numbers can be * specified. The default setting for double is 17 digits * or even 16 digits when the conversion is lossless back and forth * (mimic the standard library formatting). For example:[code] * TypeFormat.format(0.2, a) = "0.2" // 17 or 16 digits (as long as lossless conversion), remove trailing zeros. * TypeFormat.format(0.2, 17, false, false, a) = "0.20000000000000001" // Closest 17 digits number. * TypeFormat.format(0.2, 19, false, false, a) = "0.2000000000000000111" // Closest 19 digits. * TypeFormat.format(0.2, 4, false, false, a) = "0.2" // Fixed-point notation, remove trailing zeros. * TypeFormat.format(0.2, 4, false, true, a) = "0.2000" // Fixed-point notation, fixed number of digits. * TypeFormat.format(0.2, 4, true, false, a) = "2.0E-1" // Scientific notation, remove trailing zeros. * TypeFormat.format(0.2, 4, true, true, a) = "2.000E-1" // Scientific notation, fixed number of digits. * [/code]

* *

For non-primitive objects, formatting is typically performed using * specialized {@link TextFormat} instances.

* * @author Jean-Marie Dautelle * @version 4.1, November 30, 2006 */ public final class TypeFormat { /** * Default constructor (forbids derivation). */ private TypeFormat() { } /** * Parses the specified character sequence as a boolean. * * @param csq the character sequence to parse. * @return the corresponding boolean value. * @throws IllegalArgumentException if the specified character sequence * is different from "true" or "false" ignoring cases. */ public static boolean parseBoolean(CharSequence csq) { if ((csq.length() == 4) && (csq.charAt(0) == 't' || csq.charAt(0) == 'T') && (csq.charAt(1) == 'r' || csq.charAt(1) == 'R') && (csq.charAt(2) == 'u' || csq.charAt(2) == 'U') && (csq.charAt(3) == 'e' || csq.charAt(3) == 'E')) { return true; } else if ((csq.length() == 5) && (csq.charAt(0) == 'f' || csq.charAt(0) == 'F') && (csq.charAt(1) == 'a' || csq.charAt(1) == 'A') && (csq.charAt(2) == 'l' || csq.charAt(2) == 'L') && (csq.charAt(3) == 's' || csq.charAt(3) == 'S') && (csq.charAt(4) == 'e' || csq.charAt(4) == 'E')) { return false; } throw new IllegalArgumentException("Cannot parse " + csq + " as boolean"); } /** * Equivalent to {@link #parseBoolean(CharSequence)} * (for J2ME compatibility). */ public static boolean parseBoolean(String csq) { if ((csq.length() == 4) && (csq.charAt(0) == 't' || csq.charAt(0) == 'T') && (csq.charAt(1) == 'r' || csq.charAt(1) == 'R') && (csq.charAt(2) == 'u' || csq.charAt(2) == 'U') && (csq.charAt(3) == 'e' || csq.charAt(3) == 'E')) { return true; } else if ((csq.length() == 5) && (csq.charAt(0) == 'f' || csq.charAt(0) == 'F') && (csq.charAt(1) == 'a' || csq.charAt(1) == 'A') && (csq.charAt(2) == 'l' || csq.charAt(2) == 'L') && (csq.charAt(3) == 's' || csq.charAt(3) == 'S') && (csq.charAt(4) == 'e' || csq.charAt(4) == 'E')) { return false; } throw new IllegalArgumentException("Cannot parse " + csq + " as boolean"); } /** * Parses the specified character sequence from the specified position * as a boolean. * * @param csq the character sequence to parse. * @param cursor the current cursor position (being maintained). * @return the next boolean value. * @throws IllegalArgumentException if the character sequence from the * specified position is different from "true" or "false" ignoring * cases. */ public static boolean parseBoolean(CharSequence csq, Cursor cursor) { final int i = cursor.getIndex(); if ((cursor.getEndIndex() >= i + 4) && (csq.charAt(i) == 't' || csq.charAt(i) == 'T') && (csq.charAt(i + 1) == 'r' || csq.charAt(i + 1) == 'R') && (csq.charAt(i + 2) == 'u' || csq.charAt(i + 2) == 'U') && (csq.charAt(i + 3) == 'e' || csq.charAt(i + 3) == 'E')) { cursor.increment(4); return true; } if ((cursor.getEndIndex() >= i + 5) && (csq.charAt(i) == 'f' || csq.charAt(i) == 'F') && (csq.charAt(i + 1) == 'a' || csq.charAt(i + 1) == 'A') && (csq.charAt(i + 2) == 'l' || csq.charAt(i + 2) == 'L') && (csq.charAt(i + 3) == 's' || csq.charAt(i + 3) == 'S') && (csq.charAt(i + 4) == 'e' || csq.charAt(i + 4) == 'E')) { cursor.increment(5); return false; } throw new IllegalArgumentException("Cannot parse boolean " + csq.subSequence(cursor.getIndex(), cursor.getEndIndex())); } /** * Parses the specified character sequence as a signed decimal * byte. * * @param csq the character sequence to parse. * @return parseByte(csq, 10) * @throws NumberFormatException if the specified character sequence * does not contain a parsable byte. * @see #parseByte(CharSequence, int) */ public static byte parseByte(CharSequence csq) { return parseByte(csq, 10); } /** * Parses the specified character sequence as a signed byte * in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @return the corresponding byte. * @throws NumberFormatException if the specified character sequence * does not contain a parsable byte. */ public static byte parseByte(CharSequence csq, int radix) { int i = parseInt(csq, radix); if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE)) throw new NumberFormatException("Overflow"); return (byte) i; } /** * Parses the specified character sequence from the specified position * as a signed byte in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @param cursor the current cursor position (being maintained). * @return the corresponding byte. * @throws NumberFormatException if the specified character sequence * does not contain a parsable byte. */ public static byte parseByte(CharSequence csq, int radix, Cursor cursor) { int i = parseInt(csq, radix, cursor); if ((i < Byte.MIN_VALUE) || (i > Byte.MAX_VALUE)) throw new NumberFormatException("Overflow"); return (byte) i; } /** * Parses the specified character sequence as a signed decimal * short. * * @param csq the character sequence to parse. * @return parseShort(csq, 10) * @throws NumberFormatException if the specified character sequence * does not contain a parsable short. * @see #parseShort(CharSequence, int) */ public static short parseShort(CharSequence csq) { return parseShort(csq, 10); } /** * Parses the specified character sequence as a signed short * in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @return the corresponding short. * @throws NumberFormatException if the specified character sequence * does not contain a parsable short. */ public static short parseShort(CharSequence csq, int radix) { int i = parseInt(csq, radix); if ((i < Short.MIN_VALUE) || (i > Short.MAX_VALUE)) throw new NumberFormatException("Overflow"); return (short) i; } /** * Parses the specified character sequence from the specified position * as a signed short in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @param cursor the current cursor position (being maintained). * @return the corresponding short. * @throws NumberFormatException if the specified character sequence * does not contain a parsable short. */ public static short parseShort(CharSequence csq, int radix, Cursor cursor) { int i = parseInt(csq, radix, cursor); if ((i < Short.MIN_VALUE) || (i > Short.MAX_VALUE)) throw new NumberFormatException("Overflow"); return (short) i; } /** * Parses the specified character sequence as a signed int. * * @param csq the character sequence to parse. * @return parseInt(csq, 10) * @throws NumberFormatException if the specified character sequence * does not contain a parsable int. * @see #parseInt(CharSequence, int) */ public static int parseInt(CharSequence csq) { return parseInt(csq, 10); } /** * Equivalent to {@link #parseInt(CharSequence)} (for J2ME compatibility). */ public static int parseInt(String str) { return parseIntString(str, 10, null); } /** * Parses the specified character sequence as a signed int * in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @return the corresponding int. * @throws NumberFormatException if the specified character sequence * does not contain a parsable int. */ public static int parseInt(CharSequence csq, int radix) { return parseInt(csq, radix, null); } /** * Equivalent to {@link #parseInt(CharSequence, int)} * (for J2ME compatibility). */ public static int parseInt(String str, int radix) { return parseIntString(str, radix, null); } /** * Parses the specified character sequence from the specified position * as a signed int in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @param cursor the current cursor position (being maintained) or * null if the whole character sequence is parsed. * @return the corresponding int. * @throws NumberFormatException if the specified character sequence * does not contain a parsable int. */ public static int parseInt(CharSequence csq, int radix, Cursor cursor) { // Avoids dynamic cost of CharSequence.charAt if (csq instanceof CharArray) return parseIntCharArray((CharArray) csq, radix, cursor); if (csq instanceof TextBuilder) return parseIntTextBuilder((TextBuilder) csq, radix, cursor); if (csq instanceof Text) return parseIntText((Text) csq, radix, cursor); if (((Object) csq) instanceof String) return parseIntString((String) ((Object) csq), radix, cursor); return parseIntCharSequence(csq, radix, cursor); } private static int parseIntCharArray(CharArray csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { int newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Integer.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static int parseIntTextBuilder(TextBuilder csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { int newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Integer.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static int parseIntText(Text csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { int newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Integer.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static int parseIntString(String csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { int newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Integer.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static int parseIntCharSequence(CharSequence csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; int result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { int newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Integer.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } /** * Parses the specified character sequence as a decimal long. * * @param csq the character sequence to parse. * @return parseLong(csq, 10) * @throws NumberFormatException if the specified character sequence * does not contain a parsable long. * @see #parseLong(CharSequence, int) */ public static long parseLong(CharSequence csq) { return parseLong(csq, 10); } /** * Equivalent to {@link #parseLong(CharSequence)} * (for J2ME compatibility). */ public static long parseLong(String str) { return parseLongString(str, 10, null); } /** * Parses the specified character sequence as a signed long * in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @return the corresponding long. * @throws NumberFormatException if the specified character sequence * does not contain a parsable long. */ public static long parseLong(CharSequence csq, int radix) { return parseLong(csq, radix, null); } /** * Equivalent to {@link #parseLong(CharSequence, int)} * (for J2ME compatibility). */ public static long parseLong(String str, int radix) { return parseLongString(str, radix, null); } /** * Parses the specified character sequence from the specified position * as a signed long in the specified radix. * * @param csq the character sequence to parse. * @param radix the radix to be used while parsing. * @param cursor the current cursor position (being maintained) or * null if the whole character sequence is parsed. * @return the corresponding long. * @throws NumberFormatException if the specified character sequence * does not contain a parsable long. */ public static long parseLong(CharSequence csq, int radix, Cursor cursor) { // Avoids dynamic cost of CharSequence.charAt if (csq instanceof CharArray) return parseLongCharArray((CharArray) csq, radix, cursor); if (csq instanceof TextBuilder) return parseLongTextBuilder((TextBuilder) csq, radix, cursor); if (csq instanceof Text) return parseLongText((Text) csq, radix, cursor); if (((Object) csq) instanceof String) return parseLongString((String) ((Object) csq), radix, cursor); return parseLongCharSequence(csq, radix, cursor); } private static long parseLongCharArray(CharArray csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; long result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { long newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Long.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static long parseLongTextBuilder(TextBuilder csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; long result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { long newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Long.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static long parseLongText(Text csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; long result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { long newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Long.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static long parseLongString(String csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; long result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { long newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Long.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } private static long parseLongCharSequence(CharSequence csq, int radix, Cursor cursor) { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int end = (cursor != null) ? cursor.getEndIndex() : csq.length(); boolean isNegative = false; long result = 0; // Accumulates negatively (avoid MIN_VALUE overflow). int i = start; for (; i < end; i++) { char c = csq.charAt(i); int digit = (c <= '9') ? c - '0' : ((c <= 'Z') && (c >= 'A')) ? c - 'A' + 10 : ((c <= 'z') && (c >= 'a')) ? c - 'a' + 10 : -1; if ((digit >= 0) && (digit < radix)) { long newResult = result * radix - digit; if (newResult > result) throw new NumberFormatException("Overflow"); result = newResult; } else if ((c == '-') && (i == start)) { isNegative = true; } else if ((c == '+') && (i == start)) { // Ok. } else { if (cursor == null) throw new NumberFormatException("Incomplete parsing"); break; // Done. } } // Requires one valid digit character and checks for opposite overflow. if ((result == 0) && ((end == 0) || (csq.charAt(i - 1) != '0'))) throw new NumberFormatException("Cannot parse " + csq + " as int"); if ((result == Long.MIN_VALUE) && !isNegative) throw new NumberFormatException("Overflow"); if (cursor != null) cursor.setIndex(i); return isNegative ? result : -result; } /** * Parses the specified character sequence as a float. * * @param csq the character sequence to parse. * @return the float number represented by the specified character sequence. *@JVM-1.1+@ public static float parseFloat(CharSequence csq) { return (float) parseDouble(csq); } /**/ /** * Equivalent to {@link #parseFloat(CharSequence)} * (for J2ME compatibility). *@JVM-1.1+@ public static float parseFloat(String str) { return (float) parseDoubleString(str, null); } /**/ /** * Parses the specified character sequence from the specified position * as a float. * * @param csq the character sequence to parse. * @param cursor the current cursor position (being maintained). * @return the float number represented by the specified character sequence. *@JVM-1.1+@ public static float parseFloat(CharSequence csq, Cursor cursor) { return (float) parseDouble(csq, cursor); } /**/ /** * Parses the specified character sequence as a double. * The format must be of the form: * <decimal>{'.'<fraction>}{'E|e'<exponent>}. * * @param csq the character sequence to parse. * @return the double number represented by this character sequence. * @throws NumberFormatException if the character sequence does not contain * a parsable double. *@JVM-1.1+@ public static double parseDouble(CharSequence csq) throws NumberFormatException { return parseDouble(csq, null); } /**/ /** * Equivalent to {@link #parseDouble(CharSequence)} * (for J2ME compatibility). *@JVM-1.1+@ public static double parseDouble(String str) { return parseDoubleString(str, null); } /**/ /** * Parses the specified character sequence from the specified position * as a double. * * @param csq the character sequence to parse. * @param cursor the current cursor position (being maintained). * @return the double number represented by this character sequence. * @throws NumberFormatException if the character sequence does not contain * a parsable double. *@JVM-1.1+@ public static double parseDouble(CharSequence csq, Cursor cursor) throws NumberFormatException { // Avoids dynamic cost of CharSequence.charAt if (csq instanceof CharArray) return parseDoubleCharArray((CharArray) csq, cursor); if (csq instanceof TextBuilder) return parseDoubleTextBuilder((TextBuilder) csq, cursor); if (csq instanceof Text) return parseDoubleText((Text) csq, cursor); if (((Object) csq) instanceof String) return parseDoubleString((String) ((Object) csq), cursor); return parseDoubleCharSequence(csq, cursor); } private static double parseDoubleCharArray(CharArray csq, Cursor cursor) throws NumberFormatException { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int length = (cursor != null) ? cursor.getEndIndex() : csq.length(); int i = start; char c = csq.charAt(i); // Checks for NaN. if ((c == 'N') && match("NaN", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 3); return Double.NaN; } // Reads sign. boolean isNegative = (c == '-'); if ((isNegative || (c == '+')) && (++i < length)) { c = csq.charAt(i); } // Checks for Infinity. if ((c == 'I') && match("Infinity", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 8); return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } // Reads decimal and fraction (both merged to a long). long decimal = 0; int decimalPoint = -1; while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { long tmp = decimal * 10 + digit; if (tmp < decimal) throw new NumberFormatException("Too many digits - Overflow"); decimal = tmp; } else if ((c == '.') && (decimalPoint < 0)) { decimalPoint = i; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegative) { decimal = - decimal; } int fractionLength = (decimalPoint >= 0) ? i - decimalPoint - 1 : 0; // Reads exponent. int exp = 0; if ((i < length) && ((c == 'E') || (c == 'e'))) { c = csq.charAt(++i); boolean isNegativeExp = (c == '-'); if ((isNegativeExp || (c == '+')) && (++i < length)) { c = csq.charAt(i); } while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { int tmp = exp * 10 + digit; if (tmp < exp) throw new NumberFormatException("Exponent Overflow"); exp = tmp; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegativeExp) { exp = -exp; } } if (cursor != null) cursor.setIndex(i); else if (i < length) throw new NumberFormatException("Incomplete parsing"); return javolution.lang.MathLib.toDoublePow10(decimal, exp - fractionLength); } private static double parseDoubleTextBuilder(TextBuilder csq, Cursor cursor) throws NumberFormatException { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int length = (cursor != null) ? cursor.getEndIndex() : csq.length(); int i = start; char c = csq.charAt(i); // Checks for NaN. if ((c == 'N') && match("NaN", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 3); return Double.NaN; } // Reads sign. boolean isNegative = (c == '-'); if ((isNegative || (c == '+')) && (++i < length)) { c = csq.charAt(i); } // Checks for Infinity. if ((c == 'I') && match("Infinity", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 8); return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } // Reads decimal and fraction (both merged to a long). long decimal = 0; int decimalPoint = -1; while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { long tmp = decimal * 10 + digit; if (tmp < decimal) throw new NumberFormatException("Too many digits - Overflow"); decimal = tmp; } else if ((c == '.') && (decimalPoint < 0)) { decimalPoint = i; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegative) { decimal = - decimal; } int fractionLength = (decimalPoint >= 0) ? i - decimalPoint - 1 : 0; // Reads exponent. int exp = 0; if ((i < length) && ((c == 'E') || (c == 'e'))) { c = csq.charAt(++i); boolean isNegativeExp = (c == '-'); if ((isNegativeExp || (c == '+')) && (++i < length)) { c = csq.charAt(i); } while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { int tmp = exp * 10 + digit; if (tmp < exp) throw new NumberFormatException("Exponent Overflow"); exp = tmp; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegativeExp) { exp = -exp; } } if (cursor != null) cursor.setIndex(i); else if (i < length) throw new NumberFormatException("Incomplete parsing"); return javolution.lang.MathLib.toDoublePow10(decimal, exp - fractionLength); } private static double parseDoubleText(Text csq, Cursor cursor) throws NumberFormatException { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int length = (cursor != null) ? cursor.getEndIndex() : csq.length(); int i = start; char c = csq.charAt(i); // Checks for NaN. if ((c == 'N') && match("NaN", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 3); return Double.NaN; } // Reads sign. boolean isNegative = (c == '-'); if ((isNegative || (c == '+')) && (++i < length)) { c = csq.charAt(i); } // Checks for Infinity. if ((c == 'I') && match("Infinity", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 8); return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } // Reads decimal and fraction (both merged to a long). long decimal = 0; int decimalPoint = -1; while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { long tmp = decimal * 10 + digit; if (tmp < decimal) throw new NumberFormatException("Too many digits - Overflow"); decimal = tmp; } else if ((c == '.') && (decimalPoint < 0)) { decimalPoint = i; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegative) { decimal = - decimal; } int fractionLength = (decimalPoint >= 0) ? i - decimalPoint - 1 : 0; // Reads exponent. int exp = 0; if ((i < length) && ((c == 'E') || (c == 'e'))) { c = csq.charAt(++i); boolean isNegativeExp = (c == '-'); if ((isNegativeExp || (c == '+')) && (++i < length)) { c = csq.charAt(i); } while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { int tmp = exp * 10 + digit; if (tmp < exp) throw new NumberFormatException("Exponent Overflow"); exp = tmp; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegativeExp) { exp = -exp; } } if (cursor != null) cursor.setIndex(i); else if (i < length) throw new NumberFormatException("Incomplete parsing"); return javolution.lang.MathLib.toDoublePow10(decimal, exp - fractionLength); } private static double parseDoubleString(String csq, Cursor cursor) throws NumberFormatException { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int length = (cursor != null) ? cursor.getEndIndex() : csq.length(); int i = start; char c = csq.charAt(i); // Checks for NaN. if ((c == 'N') && match("NaN", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 3); return Double.NaN; } // Reads sign. boolean isNegative = (c == '-'); if ((isNegative || (c == '+')) && (++i < length)) { c = csq.charAt(i); } // Checks for Infinity. if ((c == 'I') && match("Infinity", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 8); return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } // Reads decimal and fraction (both merged to a long). long decimal = 0; int decimalPoint = -1; while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { long tmp = decimal * 10 + digit; if (tmp < decimal) throw new NumberFormatException("Too many digits - Overflow"); decimal = tmp; } else if ((c == '.') && (decimalPoint < 0)) { decimalPoint = i; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegative) { decimal = - decimal; } int fractionLength = (decimalPoint >= 0) ? i - decimalPoint - 1 : 0; // Reads exponent. int exp = 0; if ((i < length) && ((c == 'E') || (c == 'e'))) { c = csq.charAt(++i); boolean isNegativeExp = (c == '-'); if ((isNegativeExp || (c == '+')) && (++i < length)) { c = csq.charAt(i); } while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { int tmp = exp * 10 + digit; if (tmp < exp) throw new NumberFormatException("Exponent Overflow"); exp = tmp; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegativeExp) { exp = -exp; } } if (cursor != null) cursor.setIndex(i); else if (i < length) throw new NumberFormatException("Incomplete parsing"); return javolution.lang.MathLib.toDoublePow10(decimal, exp - fractionLength); } private static double parseDoubleCharSequence(CharSequence csq, Cursor cursor) throws NumberFormatException { // Parsing block identical for all CharSequences. final int start = (cursor != null) ? cursor.getIndex() : 0; final int length = (cursor != null) ? cursor.getEndIndex() : csq.length(); int i = start; char c = csq.charAt(i); // Checks for NaN. if ((c == 'N') && match("NaN", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 3); return Double.NaN; } // Reads sign. boolean isNegative = (c == '-'); if ((isNegative || (c == '+')) && (++i < length)) { c = csq.charAt(i); } // Checks for Infinity. if ((c == 'I') && match("Infinity", csq, i, length)) { if (cursor != null) cursor.setIndex(i + 8); return isNegative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; } // Reads decimal and fraction (both merged to a long). long decimal = 0; int decimalPoint = -1; while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { long tmp = decimal * 10 + digit; if (tmp < decimal) throw new NumberFormatException("Too many digits - Overflow"); decimal = tmp; } else if ((c == '.') && (decimalPoint < 0)) { decimalPoint = i; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegative) { decimal = - decimal; } int fractionLength = (decimalPoint >= 0) ? i - decimalPoint - 1 : 0; // Reads exponent. int exp = 0; if ((i < length) && ((c == 'E') || (c == 'e'))) { c = csq.charAt(++i); boolean isNegativeExp = (c == '-'); if ((isNegativeExp || (c == '+')) && (++i < length)) { c = csq.charAt(i); } while (true) { int digit = c - '0'; if ((digit >= 0) && (digit < 10)) { int tmp = exp * 10 + digit; if (tmp < exp) throw new NumberFormatException("Exponent Overflow"); exp = tmp; } else { break; // Done. } if (++i >= length) break; c = csq.charAt(i); } if (isNegativeExp) { exp = -exp; } } if (cursor != null) cursor.setIndex(i); else if (i < length) throw new NumberFormatException("Incomplete parsing"); return javolution.lang.MathLib.toDoublePow10(decimal, exp - fractionLength); } static boolean match(String str, CharSequence csq, int start, int length) { for (int i = 0; i < str.length(); i++) { if ((start + i >= length) || csq.charAt(start + i) != str.charAt(i)) return false; } return true; } static boolean match(String str, String csq, int start, int length) { for (int i = 0; i < str.length(); i++) { if ((start + i >= length) || csq.charAt(start + i) != str.charAt(i)) return false; } return true; } /**/ /** * Formats the specified boolean and appends the resulting * text to the Appendable argument. * * @param b a boolean. * @param a the Appendable to append. * @return the specified StringBuffer object. * @throws IOException if an I/O exception occurs. * @see #parseBoolean */ public static Appendable format(boolean b, Appendable a) throws IOException { return b ? a.append('t').append('r').append('u').append('e') : a .append('f').append('a').append('l').append('s').append('e'); } /** * Formats the specified int and appends the resulting * text (decimal representation) to the Appendable argument. * *

Note: This method is preferred to Appendable.append(int) * as it does not create temporary String * objects (several times faster for small numbers).

* * @param i the int number. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IOException if an I/O exception occurs. * @see #parseInt */ public static Appendable format(int i, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(i); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(i); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /** * Formats the specified int in the specified radix and appends * the resulting text to the Appendable argument. * * @param i the int number. * @param radix the radix. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IllegalArgumentException if radix is not in [2 .. 36] range. * @throws IOException if an I/O exception occurs. * @see #parseInt(CharSequence, int) */ public static Appendable format(int i, int radix, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(i, radix); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(i, radix); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /** * Formats the specified long and appends the resulting * text (decimal representation) to the Appendable argument. * *

Note: This method is preferred to Appendable.append(long) * as it does not create temporary String * objects (several times faster for small numbers).

* * @param l the long number. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IOException if an I/O exception occurs. * @see #parseLong */ public static Appendable format(long l, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(l); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(l); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /** * Formats the specified long in the specified radix and * appends the resulting text to the Appendable argument. * * @param l the long number. * @param radix the radix. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IllegalArgumentException if radix is not in [2 .. 36] range. * @throws IOException if an I/O exception occurs. * @see #parseLong(CharSequence, int) */ public static Appendable format(long l, int radix, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(l, radix); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(l, radix); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /** * Formats the specified float value. * * @param f the float value. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IOException if an I/O exception occurs. * @see TextBuilder#append(float) *@JVM-1.1+@ public static Appendable format(float f, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(f); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(f); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /**/ /** * Formats the specified double value (16 or 17 digits output). * * @param d the double value. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IOException if an I/O exception occurs. * @see TextBuilder#append(double) *@JVM-1.1+@ public static Appendable format(double d, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(d); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(d); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /**/ /** * Formats the specified double value according to the * specified formatting arguments. * * @param d the double value. * @param digits the number of significative digits (excludes exponent) or * -1 to mimic the standard library (16 or 17 digits). * @param scientific true to forces the use of the scientific * notation (e.g. 1.23E3); false * otherwise. * @param showZero true if trailing fractional zeros are * represented; false otherwise. * @param a the Appendable to append. * @return the specified Appendable object. * @throws IllegalArgumentException if (digits > 19)) * @throws IOException if an I/O exception occurs. * @see TextBuilder#append(double, int, boolean, boolean) *@JVM-1.1+@ public static Appendable format(double d, int digits, boolean scientific, boolean showZero, Appendable a) throws IOException { if (a instanceof TextBuilder) return ((TextBuilder) a).append(d, digits, scientific, showZero); TextBuilder tmp = TextBuilder.newInstance(); tmp.append(d, digits, scientific, showZero); appendTo(a, tmp); TextBuilder.recycle(tmp); return a; } /**/ /** * Appends to the specified appendable the text builder argument * (for text builder less than 32 characters). * * @param a the appendable. * @param txt the text to be append. * @throws IOException if an I/O exception occurs. */ private static void appendTo(Object to, TextBuilder txt) throws IOException { if (to instanceof StringBuffer) { txt.appendTo((StringBuffer) to); /* @JVM-1.5+@ } else if (to instanceof StringBuilder) { txt.appendTo((StringBuilder) to); /**/ } else { ((Appendable) to).append(txt); } } }