diff options
author | Elliott Hughes <enh@google.com> | 2010-04-19 14:11:56 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-04-19 14:11:56 -0700 |
commit | 95b613e08fe901506977b0e5860184f0baf09a6f (patch) | |
tree | c9ff840ea9c77ff548b86f6af941d4a14ee5d3a0 | |
parent | c8def9d19d8b2c4c4699c17df688a8a8f730c813 (diff) | |
parent | 6dda210e2d2e01727a6894c11300fd43cbd9b593 (diff) | |
download | libcore-95b613e08fe901506977b0e5860184f0baf09a6f.zip libcore-95b613e08fe901506977b0e5860184f0baf09a6f.tar.gz libcore-95b613e08fe901506977b0e5860184f0baf09a6f.tar.bz2 |
Merge "Speed up %d for locales with non-ASCII digits." into dalvik-dev
-rw-r--r-- | luni/src/main/java/java/util/Formatter.java | 1545 |
1 files changed, 717 insertions, 828 deletions
diff --git a/luni/src/main/java/java/util/Formatter.java b/luni/src/main/java/java/util/Formatter.java index db35ef1..a27e7a1 100644 --- a/luni/src/main/java/java/util/Formatter.java +++ b/luni/src/main/java/java/util/Formatter.java @@ -529,6 +529,9 @@ format("%6.0E", 123.456f);</td> public final class Formatter implements Closeable, Flushable { private static final char[] ZEROS = new char[] { '0', '0', '0', '0', '0', '0', '0', '0', '0' }; + // The cached line separator. + private static String lineSeparator; + /** * The enumeration giving the available styles for formatting very large * decimal numbers. @@ -544,13 +547,16 @@ public final class Formatter implements Closeable, Flushable { DECIMAL_FLOAT } + // User-settable parameters. private Appendable out; - private Locale locale; + // Implementation details. + private Object arg; private boolean closed = false; - + private FormatToken formatToken; private IOException lastIOException; + private LocaleData localeData; /** * Constructs a {@code Formatter}. @@ -824,9 +830,7 @@ public final class Formatter implements Closeable, Flushable { * @throws UnsupportedEncodingException * if the charset with the specified name is not supported. */ - public Formatter(OutputStream os, String csn) - throws UnsupportedEncodingException { - + public Formatter(OutputStream os, String csn) throws UnsupportedEncodingException { this(os, csn, Locale.getDefault()); } @@ -870,7 +874,7 @@ public final class Formatter implements Closeable, Flushable { locale = Locale.getDefault(); } - private void checkClosed() { + private void checkNotClosed() { if (closed) { throw new FormatterClosedException(); } @@ -884,7 +888,7 @@ public final class Formatter implements Closeable, Flushable { * if the {@code Formatter} has been closed. */ public Locale locale() { - checkClosed(); + checkNotClosed(); return locale; } @@ -896,7 +900,7 @@ public final class Formatter implements Closeable, Flushable { * if the {@code Formatter} has been closed. */ public Appendable out() { - checkClosed(); + checkNotClosed(); return out; } @@ -911,7 +915,7 @@ public final class Formatter implements Closeable, Flushable { */ @Override public String toString() { - checkClosed(); + checkNotClosed(); return out.toString(); } @@ -923,7 +927,7 @@ public final class Formatter implements Closeable, Flushable { * if the {@code Formatter} has been closed. */ public void flush() { - checkClosed(); + checkNotClosed(); if (out instanceof Flushable) { try { ((Flushable) out).flush(); @@ -986,17 +990,10 @@ public final class Formatter implements Closeable, Flushable { * if the {@code Formatter} has been closed. */ public Formatter format(String format, Object... args) { - doFormat(format, args); - return this; + return format(this.locale, format, args); } /** - * Cached transformer. Improves performance when format() is called multiple - * times. - */ - private Transformer transformer; - - /** * Writes a formatted string to the output destination of the {@code Formatter}. * * @param l @@ -1022,7 +1019,8 @@ public final class Formatter implements Closeable, Flushable { public Formatter format(Locale l, String format, Object... args) { Locale originalLocale = locale; try { - this.locale = l; + this.locale = (l == null ? Locale.US : l); + this.localeData = LocaleData.get(locale); doFormat(format, args); } finally { this.locale = originalLocale; @@ -1031,15 +1029,9 @@ public final class Formatter implements Closeable, Flushable { } private void doFormat(String format, Object... args) { - checkClosed(); - - // Reuse the previous transformer if the locale matches. - if (transformer == null || !transformer.locale.equals(locale)) { - transformer = new Transformer(this, locale); - } + checkNotClosed(); FormatSpecifierParser fsp = new FormatSpecifierParser(format); - int currentObjectIndex = 0; Object lastArgument = null; boolean hasLastArgumentSet = false; @@ -1068,7 +1060,7 @@ public final class Formatter implements Closeable, Flushable { hasLastArgumentSet = true; } - CharSequence substitution = transformer.transform(token, argument); + CharSequence substitution = transform(token, argument); // The substitution is null if we called Formattable.formatTo. if (substitution != null) { outputCharSequence(substitution, 0, substitution.length()); @@ -1398,923 +1390,820 @@ public final class Formatter implements Closeable, Flushable { } } + private NumberFormat getNumberFormat() { + return LocaleCache.getNumberFormat(locale); + } + /* - * Transforms the argument to the formatted string according to the format - * information contained in the format token. + * Gets the formatted string according to the format token and the + * argument. */ - private static class Transformer { - private Formatter formatter; - private FormatToken formatToken; - private Object arg; - private Locale locale; - private LocaleData localeData; - private static String lineSeparator; - - Transformer(Formatter formatter, Locale locale) { - this.formatter = formatter; - this.locale = (locale == null ? Locale.US : locale); - this.localeData = LocaleData.get(locale); - } - - private NumberFormat getNumberFormat() { - return LocaleCache.getNumberFormat(locale); - } - - /* - * Gets the formatted string according to the format token and the - * argument. - */ - CharSequence transform(FormatToken token, Object argument) { - this.formatToken = token; - this.arg = argument; - - // There are only two format specifiers that matter: "%d" and "%s". - // Nothing else is common in the wild. We fast-path these two to - // avoid the heavyweight machinery needed to cope with flags, width, - // and precision. - if (token.isDefault()) { - switch (token.getConversionType()) { - case 's': - if (arg == null) { - return "null"; - } else if (!(arg instanceof Formattable)) { - return arg.toString(); - } - break; - case 'd': - if (arg instanceof Integer || arg instanceof Long || arg instanceof Short || arg instanceof Byte) { - if (localeData.zeroDigit == '0') { - return arg.toString(); - } - } - } - } - - formatToken.checkFlags(arg); - CharSequence result; + private CharSequence transform(FormatToken token, Object argument) { + this.formatToken = token; + this.arg = argument; + + // There are only two format specifiers that matter: "%d" and "%s". + // Nothing else is common in the wild. We fast-path these two to + // avoid the heavyweight machinery needed to cope with flags, width, + // and precision. + if (token.isDefault()) { switch (token.getConversionType()) { - case 'B': - case 'b': { - result = transformFromBoolean(); - break; - } - case 'H': - case 'h': { - result = transformFromHashCode(); - break; - } - case 'S': - case 's': { - result = transformFromString(); - break; - } - case 'C': - case 'c': { - result = transformFromCharacter(); - break; - } - case 'd': - case 'o': - case 'x': - case 'X': { - if (arg == null || arg instanceof BigInteger) { - result = transformFromBigInteger(); - } else { - result = transformFromInteger(); - } - break; - } - case 'e': - case 'E': - case 'g': - case 'G': - case 'f': - case 'a': - case 'A': { - result = transformFromFloat(); - break; - } - case '%': { - result = transformFromPercent(); - break; - } - case 'n': { - result = transformFromLineSeparator(); - break; - } - case 't': - case 'T': { - result = transformFromDateTime(); - break; + case 's': + if (arg == null) { + return "null"; + } else if (!(arg instanceof Formattable)) { + return arg.toString(); } - default: - throw token.unknownFormatConversionException(); - } - - if (Character.isUpperCase(token.getConversionType())) { - if (result != null) { - result = result.toString().toUpperCase(locale); + break; + case 'd': + if (arg instanceof Integer || arg instanceof Long || arg instanceof Short || arg instanceof Byte) { + String result = arg.toString(); + return (localeData.zeroDigit == '0') ? result : localizeDigits(result); } } - return result; } - private IllegalFormatConversionException badArgumentType() { - throw new IllegalFormatConversionException(formatToken.getConversionType(), - arg.getClass()); - } - - /* - * Transforms the Boolean argument to a formatted string. - */ - private CharSequence transformFromBoolean() { - CharSequence result; - if (arg instanceof Boolean) { - result = arg.toString(); - } else if (arg == null) { - result = "false"; + formatToken.checkFlags(arg); + CharSequence result; + switch (token.getConversionType()) { + case 'B': case 'b': + result = transformFromBoolean(); + break; + case 'H': case 'h': + result = transformFromHashCode(); + break; + case 'S': case 's': + result = transformFromString(); + break; + case 'C': case 'c': + result = transformFromCharacter(); + break; + case 'd': case 'o': case 'x': case 'X': + if (arg == null || arg instanceof BigInteger) { + result = transformFromBigInteger(); } else { - result = "true"; - } - return padding(result, 0); - } + result = transformFromInteger(); + } + break; + case 'A': case 'a': case 'E': case 'e': case 'f': case 'G': case 'g': + result = transformFromFloat(); + break; + case '%': + result = transformFromPercent(); + break; + case 'n': + result = transformFromLineSeparator(); + break; + case 't': case 'T': + result = transformFromDateTime(); + break; + default: + throw token.unknownFormatConversionException(); + } + + if (Character.isUpperCase(token.getConversionType())) { + if (result != null) { + result = result.toString().toUpperCase(locale); + } + } + return result; + } - /* - * Transforms the hash code of the argument to a formatted string. - */ - private CharSequence transformFromHashCode() { - CharSequence result; - if (arg == null) { - result = "null"; - } else { - result = Integer.toHexString(arg.hashCode()); - } - return padding(result, 0); + private IllegalFormatConversionException badArgumentType() { + throw new IllegalFormatConversionException(formatToken.getConversionType(), arg.getClass()); + } + + /** + * Returns a CharSequence corresponding to {@code s} with all the ASCII digits replaced + * by digits appropriate to this formatter's locale. Other characters remain unchanged. + */ + private CharSequence localizeDigits(String s) { + int length = s.length(); + int offsetToLocalizedDigits = localeData.zeroDigit - '0'; + StringBuilder result = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch >= '0' && ch <= '9') { + ch += offsetToLocalizedDigits; + } + result.append(ch); + } + return result; + } + + private CharSequence transformFromBoolean() { + CharSequence result; + if (arg instanceof Boolean) { + result = arg.toString(); + } else if (arg == null) { + result = "false"; + } else { + result = "true"; } + return padding(result, 0); + } - /* - * Transforms the String to a formatted string. - */ - private CharSequence transformFromString() { - if (arg instanceof Formattable) { - int flags = 0; - if (formatToken.flagMinus) { - flags |= FormattableFlags.LEFT_JUSTIFY; - } - if (formatToken.flagSharp) { - flags |= FormattableFlags.ALTERNATE; - } - if (Character.isUpperCase(formatToken.getConversionType())) { - flags |= FormattableFlags.UPPERCASE; - } - ((Formattable) arg).formatTo(formatter, flags, formatToken.getWidth(), - formatToken.getPrecision()); - // all actions have been taken out in the - // Formattable.formatTo, thus there is nothing to do, just - // returns null, which tells the Parser to add nothing to the - // output. - return null; - } - CharSequence result = arg != null ? arg.toString() : "null"; - return padding(result, 0); + private CharSequence transformFromHashCode() { + CharSequence result; + if (arg == null) { + result = "null"; + } else { + result = Integer.toHexString(arg.hashCode()); } + return padding(result, 0); + } - /* - * Transforms the Character to a formatted string. - */ - private CharSequence transformFromCharacter() { - if (arg == null) { - return padding("null", 0); + private CharSequence transformFromString() { + if (arg instanceof Formattable) { + int flags = 0; + if (formatToken.flagMinus) { + flags |= FormattableFlags.LEFT_JUSTIFY; } - if (arg instanceof Character) { - return padding(String.valueOf(arg), 0); - } else if (arg instanceof Byte || arg instanceof Short || arg instanceof Integer) { - int codePoint = ((Number) arg).intValue(); - if (!Character.isValidCodePoint(codePoint)) { - throw new IllegalFormatCodePointException(codePoint); - } - CharSequence result = (codePoint < Character.MIN_SUPPLEMENTARY_CODE_POINT) - ? String.valueOf((char) codePoint) - : String.valueOf(Character.toChars(codePoint)); - return padding(result, 0); - } else { - throw badArgumentType(); + if (formatToken.flagSharp) { + flags |= FormattableFlags.ALTERNATE; } + if (Character.isUpperCase(formatToken.getConversionType())) { + flags |= FormattableFlags.UPPERCASE; + } + ((Formattable) arg).formatTo(this, flags, formatToken.getWidth(), + formatToken.getPrecision()); + // all actions have been taken out in the + // Formattable.formatTo, thus there is nothing to do, just + // returns null, which tells the Parser to add nothing to the + // output. + return null; } + CharSequence result = arg != null ? arg.toString() : "null"; + return padding(result, 0); + } - /* - * Transforms percent to a formatted string. Only '-' is legal flag. - * Precision and arguments are illegal. - */ - private CharSequence transformFromPercent() { - return padding("%", 0); + private CharSequence transformFromCharacter() { + if (arg == null) { + return padding("null", 0); } - - /* - * Transforms line separator to a formatted string. Any flag, width, - * precision or argument is illegal. - */ - private CharSequence transformFromLineSeparator() { - if (lineSeparator == null) { - lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() { - public String run() { - return System.getProperty("line.separator"); - } - }); + if (arg instanceof Character) { + return padding(String.valueOf(arg), 0); + } else if (arg instanceof Byte || arg instanceof Short || arg instanceof Integer) { + int codePoint = ((Number) arg).intValue(); + if (!Character.isValidCodePoint(codePoint)) { + throw new IllegalFormatCodePointException(codePoint); } - return lineSeparator; + CharSequence result = (codePoint < Character.MIN_SUPPLEMENTARY_CODE_POINT) + ? String.valueOf((char) codePoint) + : String.valueOf(Character.toChars(codePoint)); + return padding(result, 0); + } else { + throw badArgumentType(); } + } - /* - * Pads characters to the formatted string. - */ - private CharSequence padding(CharSequence source, int startIndex) { - boolean sourceIsStringBuilder = (source instanceof StringBuilder); - - int start = startIndex; - int width = formatToken.getWidth(); - int precision = formatToken.getPrecision(); - - int length = source.length(); - if (precision >= 0) { - length = Math.min(length, precision); - if (sourceIsStringBuilder) { - ((StringBuilder) source).setLength(length); - } else { - source = source.subSequence(0, length); - } - } - if (width > 0) { - width = Math.max(source.length(), width); - } - if (length >= width) { - return source; - } + private CharSequence transformFromPercent() { + return padding("%", 0); + } - char paddingChar = '\u0020'; // space as padding char. - if (formatToken.flagZero) { - if (formatToken.getConversionType() == 'd') { - paddingChar = localeData.zeroDigit; - } else { - paddingChar = '0'; // No localized digits for bases other than decimal. + private CharSequence transformFromLineSeparator() { + if (lineSeparator == null) { + lineSeparator = AccessController.doPrivileged(new PrivilegedAction<String>() { + public String run() { + return System.getProperty("line.separator"); } - } else { - // if padding char is space, always pad from the start. - start = 0; - } - char[] paddingChars = new char[width - length]; - Arrays.fill(paddingChars, paddingChar); - - boolean paddingRight = formatToken.flagMinus; - StringBuilder result = toStringBuilder(source); - if (paddingRight) { - result.append(paddingChars); - } else { - result.insert(start, paddingChars); - } - return result; + }); } + return lineSeparator; + } - private StringBuilder toStringBuilder(CharSequence cs) { - return cs instanceof StringBuilder ? (StringBuilder) cs : new StringBuilder(cs); - } + private CharSequence padding(CharSequence source, int startIndex) { + int start = startIndex; + int width = formatToken.getWidth(); + int precision = formatToken.getPrecision(); - private StringBuilder wrapParentheses(StringBuilder result) { - result.setCharAt(0, '('); // Replace the '-'. - if (formatToken.flagZero) { - formatToken.setWidth(formatToken.getWidth() - 1); - result = (StringBuilder) padding(result, 1); - result.append(')'); + int length = source.length(); + if (precision >= 0) { + length = Math.min(length, precision); + if (source instanceof StringBuilder) { + ((StringBuilder) source).setLength(length); } else { - result.append(')'); - result = (StringBuilder) padding(result, 0); + source = source.subSequence(0, length); } - return result; + } + if (width > 0) { + width = Math.max(source.length(), width); + } + if (length >= width) { + return source; } - /* - * Transforms the Integer to a formatted string. - */ - private CharSequence transformFromInteger() { - int startIndex = 0; - StringBuilder result = new StringBuilder(); - char currentConversionType = formatToken.getConversionType(); - - long value; - if (arg instanceof Long) { - value = ((Long) arg).longValue(); - } else if (arg instanceof Integer) { - value = ((Integer) arg).longValue(); - } else if (arg instanceof Short) { - value = ((Short) arg).longValue(); - } else if (arg instanceof Byte) { - value = ((Byte) arg).longValue(); - } else { - throw badArgumentType(); - } - - if (formatToken.flagSharp) { - if (currentConversionType == 'o') { - result.append("0"); - startIndex += 1; - } else { - result.append("0x"); - startIndex += 2; - } - } - - if ('d' == currentConversionType) { - if (formatToken.flagComma || localeData.zeroDigit != '0') { - NumberFormat numberFormat = getNumberFormat(); - numberFormat.setGroupingUsed(formatToken.flagComma); - result.append(numberFormat.format(arg)); - } else { - result.append(value); - } - - if (value < 0) { - if (formatToken.flagParenthesis) { - return wrapParentheses(result); - } else if (formatToken.flagZero) { - startIndex++; - } - } else { - if (formatToken.flagAdd) { - result.insert(0, '+'); - startIndex += 1; - } else if (formatToken.flagSpace) { - result.insert(0, ' '); - startIndex += 1; - } - } + char paddingChar = '\u0020'; // space as padding char. + if (formatToken.flagZero) { + if (formatToken.getConversionType() == 'd') { + paddingChar = localeData.zeroDigit; } else { - // Undo sign-extension, since we'll be using Long.to(Octal|Hex)String. - if (arg instanceof Byte) { - value &= 0xffL; - } else if (arg instanceof Short) { - value &= 0xffffL; - } else if (arg instanceof Integer) { - value &= 0xffffffffL; - } - if ('o' == currentConversionType) { - result.append(Long.toOctalString(value)); - } else { - result.append(Long.toHexString(value)); - } + paddingChar = '0'; // No localized digits for bases other than decimal. } - - return padding(result, startIndex); + } else { + // if padding char is space, always pad from the start. + start = 0; } + char[] paddingChars = new char[width - length]; + Arrays.fill(paddingChars, paddingChar); - private CharSequence transformFromSpecialNumber() { - if (!(arg instanceof Number) || arg instanceof BigDecimal) { - return null; - } + boolean paddingRight = formatToken.flagMinus; + StringBuilder result = toStringBuilder(source); + if (paddingRight) { + result.append(paddingChars); + } else { + result.insert(start, paddingChars); + } + return result; + } - Number number = (Number) arg; - double d = number.doubleValue(); - String source = null; - if (Double.isNaN(d)) { - source = "NaN"; - } else if (d == Double.POSITIVE_INFINITY) { - if (formatToken.flagAdd) { - source = "+Infinity"; - } else if (formatToken.flagSpace) { - source = " Infinity"; - } else { - source = "Infinity"; - } - } else if (d == Double.NEGATIVE_INFINITY) { - if (formatToken.flagParenthesis) { - source = "(Infinity)"; - } else { - source = "-Infinity"; - } - } else { - return null; - } + private StringBuilder toStringBuilder(CharSequence cs) { + return cs instanceof StringBuilder ? (StringBuilder) cs : new StringBuilder(cs); + } - formatToken.setPrecision(FormatToken.UNSET); - formatToken.flagZero = false; - return padding(source, 0); + private StringBuilder wrapParentheses(StringBuilder result) { + result.setCharAt(0, '('); // Replace the '-'. + if (formatToken.flagZero) { + formatToken.setWidth(formatToken.getWidth() - 1); + result = (StringBuilder) padding(result, 1); + result.append(')'); + } else { + result.append(')'); + result = (StringBuilder) padding(result, 0); } + return result; + } - private CharSequence transformFromNull() { - formatToken.flagZero = false; - return padding("null", 0); + private CharSequence transformFromInteger() { + int startIndex = 0; + StringBuilder result = new StringBuilder(); + char currentConversionType = formatToken.getConversionType(); + + long value; + if (arg instanceof Long) { + value = ((Long) arg).longValue(); + } else if (arg instanceof Integer) { + value = ((Integer) arg).longValue(); + } else if (arg instanceof Short) { + value = ((Short) arg).longValue(); + } else if (arg instanceof Byte) { + value = ((Byte) arg).longValue(); + } else { + throw badArgumentType(); } - /* - * Transforms a BigInteger to a formatted string. - */ - private CharSequence transformFromBigInteger() { - int startIndex = 0; - boolean isNegative = false; - StringBuilder result = new StringBuilder(); - BigInteger bigInt = (BigInteger) arg; - char currentConversionType = formatToken.getConversionType(); - - if (bigInt == null) { - return transformFromNull(); + if (formatToken.flagSharp) { + if (currentConversionType == 'o') { + result.append("0"); + startIndex += 1; + } else { + result.append("0x"); + startIndex += 2; } + } - isNegative = (bigInt.compareTo(BigInteger.ZERO) < 0); - - if ('d' == currentConversionType) { + if ('d' == currentConversionType) { + if (formatToken.flagComma) { NumberFormat numberFormat = getNumberFormat(); - numberFormat.setGroupingUsed(formatToken.flagComma); - result.append(numberFormat.format(bigInt)); - } else if ('o' == currentConversionType) { - // convert BigInteger to a string presentation using radix 8 - result.append(bigInt.toString(8)); + numberFormat.setGroupingUsed(true); + result.append(numberFormat.format(arg)); + } else if (localeData.zeroDigit != '0') { + result.append(localizeDigits(Long.toString(value))); } else { - // convert BigInteger to a string presentation using radix 16 - result.append(bigInt.toString(16)); - } - if (formatToken.flagSharp) { - startIndex = isNegative ? 1 : 0; - if (currentConversionType == 'o') { - result.insert(startIndex, "0"); - startIndex += 1; - } else if (currentConversionType == 'x' || currentConversionType == 'X') { - result.insert(startIndex, "0x"); - startIndex += 2; - } + result.append(value); } - if (!isNegative) { + if (value < 0) { + if (formatToken.flagParenthesis) { + return wrapParentheses(result); + } else if (formatToken.flagZero) { + startIndex++; + } + } else { if (formatToken.flagAdd) { result.insert(0, '+'); startIndex += 1; - } - if (formatToken.flagSpace) { + } else if (formatToken.flagSpace) { result.insert(0, ' '); startIndex += 1; } } - - /* pad paddingChar to the output */ - if (isNegative && formatToken.flagParenthesis) { - return wrapParentheses(result); + } else { + // Undo sign-extension, since we'll be using Long.to(Octal|Hex)String. + if (arg instanceof Byte) { + value &= 0xffL; + } else if (arg instanceof Short) { + value &= 0xffffL; + } else if (arg instanceof Integer) { + value &= 0xffffffffL; } - if (isNegative && formatToken.flagZero) { - startIndex++; + if ('o' == currentConversionType) { + result.append(Long.toOctalString(value)); + } else { + result.append(Long.toHexString(value)); } - return padding(result, startIndex); } - /* - * Transforms a Float,Double or BigDecimal to a formatted string. - */ - private CharSequence transformFromFloat() { - StringBuilder result = new StringBuilder(); - int startIndex = 0; - char currentConversionType = formatToken.getConversionType(); + return padding(result, startIndex); + } - if (arg == null) { - return transformFromNull(); - } + private CharSequence transformFromSpecialNumber() { + if (!(arg instanceof Number) || arg instanceof BigDecimal) { + return null; + } - if (!(arg instanceof Float || arg instanceof Double || arg instanceof BigDecimal)) { - throw badArgumentType(); + Number number = (Number) arg; + double d = number.doubleValue(); + String source = null; + if (Double.isNaN(d)) { + source = "NaN"; + } else if (d == Double.POSITIVE_INFINITY) { + if (formatToken.flagAdd) { + source = "+Infinity"; + } else if (formatToken.flagSpace) { + source = " Infinity"; + } else { + source = "Infinity"; } - - CharSequence specialNumberResult = transformFromSpecialNumber(); - if (null != specialNumberResult) { - return specialNumberResult; + } else if (d == Double.NEGATIVE_INFINITY) { + if (formatToken.flagParenthesis) { + source = "(Infinity)"; + } else { + source = "-Infinity"; } + } else { + return null; + } - if (currentConversionType != 'a' && currentConversionType != 'A' && - !formatToken.isPrecisionSet()) { - formatToken.setPrecision(FormatToken.DEFAULT_PRECISION); - } + formatToken.setPrecision(FormatToken.UNSET); + formatToken.flagZero = false; + return padding(source, 0); + } - // output result - FloatUtil floatUtil = new FloatUtil(result, formatToken, - (DecimalFormat) getNumberFormat(), localeData, arg); - floatUtil.transform(currentConversionType); + private CharSequence transformFromNull() { + formatToken.flagZero = false; + return padding("null", 0); + } - formatToken.setPrecision(FormatToken.UNSET); + private CharSequence transformFromBigInteger() { + int startIndex = 0; + StringBuilder result = new StringBuilder(); + BigInteger bigInt = (BigInteger) arg; + char currentConversionType = formatToken.getConversionType(); - if (localeData.minusSign == result.charAt(0)) { - if (formatToken.flagParenthesis) { - return wrapParentheses(result); - } - } else { - if (formatToken.flagSpace) { - result.insert(0, ' '); - startIndex++; - } - if (formatToken.flagAdd) { - result.insert(0, floatUtil.getAddSign()); - startIndex++; - } - } + if (bigInt == null) { + return transformFromNull(); + } - char firstChar = result.charAt(0); - if (formatToken.flagZero && (firstChar == floatUtil.getAddSign() || firstChar == localeData.minusSign)) { - startIndex = 1; - } + boolean isNegative = (bigInt.compareTo(BigInteger.ZERO) < 0); - if (currentConversionType == 'a' || currentConversionType == 'A') { + if ('d' == currentConversionType) { + NumberFormat numberFormat = getNumberFormat(); + numberFormat.setGroupingUsed(formatToken.flagComma); + result.append(numberFormat.format(bigInt)); + } else if ('o' == currentConversionType) { + // convert BigInteger to a string presentation using radix 8 + result.append(bigInt.toString(8)); + } else { + // convert BigInteger to a string presentation using radix 16 + result.append(bigInt.toString(16)); + } + if (formatToken.flagSharp) { + startIndex = isNegative ? 1 : 0; + if (currentConversionType == 'o') { + result.insert(startIndex, "0"); + startIndex += 1; + } else if (currentConversionType == 'x' || currentConversionType == 'X') { + result.insert(startIndex, "0x"); startIndex += 2; } - return padding(result, startIndex); } - /* - * Transforms a Date to a formatted string. - */ - private CharSequence transformFromDateTime() { - if (arg == null) { - return transformFromNull(); + if (!isNegative) { + if (formatToken.flagAdd) { + result.insert(0, '+'); + startIndex += 1; + } + if (formatToken.flagSpace) { + result.insert(0, ' '); + startIndex += 1; } + } - Calendar calendar; - if (arg instanceof Calendar) { - calendar = (Calendar) arg; + /* pad paddingChar to the output */ + if (isNegative && formatToken.flagParenthesis) { + return wrapParentheses(result); + } + if (isNegative && formatToken.flagZero) { + startIndex++; + } + return padding(result, startIndex); + } + + private CharSequence transformFromDateTime() { + if (arg == null) { + return transformFromNull(); + } + + Calendar calendar; + if (arg instanceof Calendar) { + calendar = (Calendar) arg; + } else { + Date date = null; + if (arg instanceof Long) { + date = new Date(((Long) arg).longValue()); + } else if (arg instanceof Date) { + date = (Date) arg; } else { - Date date = null; - if (arg instanceof Long) { - date = new Date(((Long) arg).longValue()); - } else if (arg instanceof Date) { - date = (Date) arg; - } else { - throw badArgumentType(); - } - calendar = Calendar.getInstance(locale); - calendar.setTime(date); + throw badArgumentType(); } + calendar = Calendar.getInstance(locale); + calendar.setTime(date); + } - StringBuilder result = new StringBuilder(); - if (!appendT(result, formatToken.getDateSuffix(), calendar)) { - throw formatToken.unknownFormatConversionException(); - } - return padding(result, 0); + StringBuilder result = new StringBuilder(); + if (!appendT(result, formatToken.getDateSuffix(), calendar)) { + throw formatToken.unknownFormatConversionException(); } + return padding(result, 0); + } - private boolean appendT(StringBuilder result, char conversion, Calendar calendar) { - switch (conversion) { - case 'A': - result.append(localeData.longWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]); - return true; - case 'a': - result.append(localeData.shortWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]); - return true; - case 'B': - result.append(localeData.longMonthNames[calendar.get(Calendar.MONTH)]); - return true; - case 'b': case 'h': - result.append(localeData.shortMonthNames[calendar.get(Calendar.MONTH)]); - return true; - case 'C': - appendLocalized(result, calendar.get(Calendar.YEAR) / 100, 2); - return true; - case 'D': - appendT(result, 'm', calendar); - result.append('/'); - appendT(result, 'd', calendar); - result.append('/'); - appendT(result, 'y', calendar); - return true; - case 'F': - appendT(result, 'Y', calendar); - result.append('-'); - appendT(result, 'm', calendar); - result.append('-'); - appendT(result, 'd', calendar); - return true; - case 'H': - appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 2); - return true; - case 'I': - appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 2); - return true; - case 'L': - appendLocalized(result, calendar.get(Calendar.MILLISECOND), 3); - return true; - case 'M': - appendLocalized(result, calendar.get(Calendar.MINUTE), 2); - return true; - case 'N': - appendLocalized(result, calendar.get(Calendar.MILLISECOND) * 1000000L, 9); - return true; - case 'Q': - appendLocalized(result, calendar.getTimeInMillis(), 0); - return true; - case 'R': - appendT(result, 'H', calendar); - result.append(':'); - appendT(result, 'M', calendar); - return true; - case 'S': - appendLocalized(result, calendar.get(Calendar.SECOND), 2); - return true; - case 'T': - appendT(result, 'H', calendar); - result.append(':'); - appendT(result, 'M', calendar); - result.append(':'); - appendT(result, 'S', calendar); - return true; - case 'Y': - appendLocalized(result, calendar.get(Calendar.YEAR), 4); - return true; - case 'Z': - TimeZone timeZone = calendar.getTimeZone(); - result.append(timeZone.getDisplayName(timeZone.inDaylightTime(calendar.getTime()), - TimeZone.SHORT, locale)); - return true; - case 'c': - appendT(result, 'a', calendar); - result.append(' '); - appendT(result, 'b', calendar); - result.append(' '); - appendT(result, 'd', calendar); - result.append(' '); - appendT(result, 'T', calendar); - result.append(' '); - appendT(result, 'Z', calendar); - result.append(' '); - appendT(result, 'Y', calendar); - return true; - case 'd': - appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 2); - return true; - case 'e': - appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 0); - return true; - case 'j': - appendLocalized(result, calendar.get(Calendar.DAY_OF_YEAR), 3); - return true; - case 'k': - appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 0); - return true; - case 'l': - appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 0); - return true; - case 'm': - // Calendar.JANUARY is 0; humans want January represented as 1. - appendLocalized(result, calendar.get(Calendar.MONTH) + 1, 2); - return true; - case 'p': - result.append(localeData.amPm[calendar.get(Calendar.AM_PM)].toLowerCase(locale)); - return true; - case 'r': - appendT(result, 'I', calendar); - result.append(':'); - appendT(result, 'M', calendar); - result.append(':'); - appendT(result, 'S', calendar); - result.append(' '); - result.append(localeData.amPm[calendar.get(Calendar.AM_PM)]); - return true; - case 's': - appendLocalized(result, calendar.getTimeInMillis() / 1000, 0); - return true; - case 'y': - appendLocalized(result, calendar.get(Calendar.YEAR) % 100, 2); - return true; - case 'z': - long offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); - char sign = '+'; - if (offset < 0) { - sign = '-'; - offset = -offset; - } - result.append(sign); - appendLocalized(result, offset / 3600000, 2); - appendLocalized(result, (offset % 3600000) / 60000, 2); - return true; - } - return false; + private boolean appendT(StringBuilder result, char conversion, Calendar calendar) { + switch (conversion) { + case 'A': + result.append(localeData.longWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]); + return true; + case 'a': + result.append(localeData.shortWeekdayNames[calendar.get(Calendar.DAY_OF_WEEK)]); + return true; + case 'B': + result.append(localeData.longMonthNames[calendar.get(Calendar.MONTH)]); + return true; + case 'b': case 'h': + result.append(localeData.shortMonthNames[calendar.get(Calendar.MONTH)]); + return true; + case 'C': + appendLocalized(result, calendar.get(Calendar.YEAR) / 100, 2); + return true; + case 'D': + appendT(result, 'm', calendar); + result.append('/'); + appendT(result, 'd', calendar); + result.append('/'); + appendT(result, 'y', calendar); + return true; + case 'F': + appendT(result, 'Y', calendar); + result.append('-'); + appendT(result, 'm', calendar); + result.append('-'); + appendT(result, 'd', calendar); + return true; + case 'H': + appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 2); + return true; + case 'I': + appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 2); + return true; + case 'L': + appendLocalized(result, calendar.get(Calendar.MILLISECOND), 3); + return true; + case 'M': + appendLocalized(result, calendar.get(Calendar.MINUTE), 2); + return true; + case 'N': + appendLocalized(result, calendar.get(Calendar.MILLISECOND) * 1000000L, 9); + return true; + case 'Q': + appendLocalized(result, calendar.getTimeInMillis(), 0); + return true; + case 'R': + appendT(result, 'H', calendar); + result.append(':'); + appendT(result, 'M', calendar); + return true; + case 'S': + appendLocalized(result, calendar.get(Calendar.SECOND), 2); + return true; + case 'T': + appendT(result, 'H', calendar); + result.append(':'); + appendT(result, 'M', calendar); + result.append(':'); + appendT(result, 'S', calendar); + return true; + case 'Y': + appendLocalized(result, calendar.get(Calendar.YEAR), 4); + return true; + case 'Z': + TimeZone timeZone = calendar.getTimeZone(); + result.append(timeZone.getDisplayName(timeZone.inDaylightTime(calendar.getTime()), + TimeZone.SHORT, locale)); + return true; + case 'c': + appendT(result, 'a', calendar); + result.append(' '); + appendT(result, 'b', calendar); + result.append(' '); + appendT(result, 'd', calendar); + result.append(' '); + appendT(result, 'T', calendar); + result.append(' '); + appendT(result, 'Z', calendar); + result.append(' '); + appendT(result, 'Y', calendar); + return true; + case 'd': + appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 2); + return true; + case 'e': + appendLocalized(result, calendar.get(Calendar.DAY_OF_MONTH), 0); + return true; + case 'j': + appendLocalized(result, calendar.get(Calendar.DAY_OF_YEAR), 3); + return true; + case 'k': + appendLocalized(result, calendar.get(Calendar.HOUR_OF_DAY), 0); + return true; + case 'l': + appendLocalized(result, to12Hour(calendar.get(Calendar.HOUR)), 0); + return true; + case 'm': + // Calendar.JANUARY is 0; humans want January represented as 1. + appendLocalized(result, calendar.get(Calendar.MONTH) + 1, 2); + return true; + case 'p': + result.append(localeData.amPm[calendar.get(Calendar.AM_PM)].toLowerCase(locale)); + return true; + case 'r': + appendT(result, 'I', calendar); + result.append(':'); + appendT(result, 'M', calendar); + result.append(':'); + appendT(result, 'S', calendar); + result.append(' '); + result.append(localeData.amPm[calendar.get(Calendar.AM_PM)]); + return true; + case 's': + appendLocalized(result, calendar.getTimeInMillis() / 1000, 0); + return true; + case 'y': + appendLocalized(result, calendar.get(Calendar.YEAR) % 100, 2); + return true; + case 'z': + long offset = calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); + char sign = '+'; + if (offset < 0) { + sign = '-'; + offset = -offset; + } + result.append(sign); + appendLocalized(result, offset / 3600000, 2); + appendLocalized(result, (offset % 3600000) / 60000, 2); + return true; } - - private int to12Hour(int hour) { - return hour == 0 ? 12 : hour; + return false; + } + + private int to12Hour(int hour) { + return hour == 0 ? 12 : hour; + } + + private void appendLocalized(StringBuilder result, long value, int width) { + int paddingIndex = result.length(); + char zeroDigit = localeData.zeroDigit; + if (zeroDigit == '0') { + result.append(value); + } else { + result.append(localizeDigits(Long.toString(value))); } - - private void appendLocalized(StringBuilder result, long value, int width) { - int paddingIndex = result.length(); - char zeroDigit = localeData.zeroDigit; - if (zeroDigit == '0') { - result.append(value); - } else { - NumberFormat numberFormat = getNumberFormat(); - numberFormat.setGroupingUsed(false); - result.append(numberFormat.format(value)); - } - int zeroCount = width - (result.length() - paddingIndex); - if (zeroCount <= 0) { - return; - } - if (zeroDigit == '0') { - result.insert(paddingIndex, ZEROS, 0, zeroCount); - } else { - for (int i = 0; i < zeroCount; ++i) { - result.insert(paddingIndex, zeroDigit); - } + int zeroCount = width - (result.length() - paddingIndex); + if (zeroCount <= 0) { + return; + } + if (zeroDigit == '0') { + result.insert(paddingIndex, ZEROS, 0, zeroCount); + } else { + for (int i = 0; i < zeroCount; ++i) { + result.insert(paddingIndex, zeroDigit); } } } - // TODO: merge this into Transformer; the distinction is not obviously useful. - private static class FloatUtil { - private final StringBuilder result; - private final DecimalFormat decimalFormat; - private final LocaleData localeData; - private final FormatToken formatToken; - private final Object argument; + private CharSequence transformFromFloat() { + if (arg == null) { + return transformFromNull(); + } - FloatUtil(StringBuilder result, FormatToken formatToken, DecimalFormat decimalFormat, - LocaleData localeData, Object argument) { - this.result = result; - this.formatToken = formatToken; - this.decimalFormat = decimalFormat; - this.localeData = localeData; - this.argument = argument; + if (!(arg instanceof Float || arg instanceof Double || arg instanceof BigDecimal)) { + throw badArgumentType(); } - void transform(char conversionType) { - switch (conversionType) { - case 'a': case 'A': - transform_a(); - break; - case 'e': case 'E': - transform_e(); - break; - case 'f': - transform_f(); - break; - case 'g': - case 'G': - transform_g(); - break; - default: - throw formatToken.unknownFormatConversionException(); - } + CharSequence specialNumberResult = transformFromSpecialNumber(); + if (specialNumberResult != null) { + return specialNumberResult; } - char getAddSign() { - return '+'; + char conversionType = formatToken.getConversionType(); + if (conversionType != 'a' && conversionType != 'A' && !formatToken.isPrecisionSet()) { + formatToken.setPrecision(FormatToken.DEFAULT_PRECISION); } - void transform_e() { - StringBuilder pattern = new StringBuilder(); - pattern.append('0'); - if (formatToken.getPrecision() > 0) { - pattern.append('.'); - char[] zeros = new char[formatToken.getPrecision()]; - Arrays.fill(zeros, '0'); // This is a *pattern* character, so no localization. - pattern.append(zeros); + StringBuilder result = new StringBuilder(); + switch (conversionType) { + case 'a': case 'A': + transform_a(result); + break; + case 'e': case 'E': + transform_e(result); + break; + case 'f': + transform_f(result); + break; + case 'g': + case 'G': + transform_g(result); + break; + default: + throw formatToken.unknownFormatConversionException(); + } + + formatToken.setPrecision(FormatToken.UNSET); + + int startIndex = 0; + if (localeData.minusSign == result.charAt(0)) { + if (formatToken.flagParenthesis) { + return wrapParentheses(result); + } + } else { + if (formatToken.flagSpace) { + result.insert(0, ' '); + startIndex++; } - pattern.append("E+00"); - decimalFormat.applyPattern(pattern.toString()); - String formattedString = decimalFormat.format(argument); - result.append(formattedString.replace('E', 'e')); - - // if the flag is sharp and decimal separator is always given out. - if (formatToken.flagSharp && formatToken.getPrecision() == 0) { - int indexOfE = result.indexOf("e"); - result.insert(indexOfE, localeData.decimalSeparator); + if (formatToken.flagAdd) { + result.insert(0, '+'); + startIndex++; } } - void transform_g() { - int precision = formatToken.getPrecision(); - precision = (0 == precision ? 1 : precision); - formatToken.setPrecision(precision); + char firstChar = result.charAt(0); + if (formatToken.flagZero && (firstChar == '+' || firstChar == localeData.minusSign)) { + startIndex = 1; + } - if (0.0 == ((Number) argument).doubleValue()) { - precision--; - formatToken.setPrecision(precision); - transform_f(); - return; - } + if (conversionType == 'a' || conversionType == 'A') { + startIndex += 2; + } + return padding(result, startIndex); + } + + private void transform_e(StringBuilder result) { + StringBuilder pattern = new StringBuilder(); + pattern.append('0'); + if (formatToken.getPrecision() > 0) { + pattern.append('.'); + char[] zeros = new char[formatToken.getPrecision()]; + Arrays.fill(zeros, '0'); // This is a *pattern* character, so no localization. + pattern.append(zeros); + } + pattern.append("E+00"); + DecimalFormat decimalFormat = (DecimalFormat) getNumberFormat(); + decimalFormat.applyPattern(pattern.toString()); + String formattedString = decimalFormat.format(arg); + result.append(formattedString.replace('E', 'e')); + + // if the flag is sharp and decimal separator is always given out. + if (formatToken.flagSharp && formatToken.getPrecision() == 0) { + int indexOfE = result.indexOf("e"); + result.insert(indexOfE, localeData.decimalSeparator); + } + } - boolean requireScientificRepresentation = true; - double d = ((Number) argument).doubleValue(); - d = Math.abs(d); - if (Double.isInfinite(d)) { - precision = formatToken.getPrecision(); - precision--; + private void transform_g(StringBuilder result) { + int precision = formatToken.getPrecision(); + precision = (0 == precision ? 1 : precision); + formatToken.setPrecision(precision); + + double d = ((Number) arg).doubleValue(); + if (d == 0.0) { + precision--; + formatToken.setPrecision(precision); + transform_f(result); + return; + } + + boolean requireScientificRepresentation = true; + d = Math.abs(d); + if (Double.isInfinite(d)) { + precision = formatToken.getPrecision(); + precision--; + formatToken.setPrecision(precision); + transform_e(result); + return; + } + BigDecimal b = new BigDecimal(d, new MathContext(precision)); + d = b.doubleValue(); + long l = b.longValue(); + + if (d >= 1 && d < Math.pow(10, precision)) { + if (l < Math.pow(10, precision)) { + requireScientificRepresentation = false; + precision -= String.valueOf(l).length(); + precision = precision < 0 ? 0 : precision; + l = Math.round(d * Math.pow(10, precision + 1)); + if (String.valueOf(l).length() <= formatToken.getPrecision()) { + precision++; + } formatToken.setPrecision(precision); - transform_e(); - return; } - BigDecimal b = new BigDecimal(d, new MathContext(precision)); - d = b.doubleValue(); - long l = b.longValue(); - - if (d >= 1 && d < Math.pow(10, precision)) { - if (l < Math.pow(10, precision)) { - requireScientificRepresentation = false; - precision -= String.valueOf(l).length(); - precision = precision < 0 ? 0 : precision; - l = Math.round(d * Math.pow(10, precision + 1)); - if (String.valueOf(l).length() <= formatToken - .getPrecision()) { - precision++; - } - formatToken.setPrecision(precision); + } else { + l = b.movePointRight(4).longValue(); + if (d >= Math.pow(10, -4) && d < 1) { + requireScientificRepresentation = false; + precision += 4 - String.valueOf(l).length(); + l = b.movePointRight(precision + 1).longValue(); + if (String.valueOf(l).length() <= formatToken.getPrecision()) { + precision++; } - - } else { - l = b.movePointRight(4).longValue(); - if (d >= Math.pow(10, -4) && d < 1) { - requireScientificRepresentation = false; - precision += 4 - String.valueOf(l).length(); - l = b.movePointRight(precision + 1).longValue(); - if (String.valueOf(l).length() <= formatToken - .getPrecision()) { - precision++; - } - l = b.movePointRight(precision).longValue(); - if (l >= Math.pow(10, precision - 4)) { - formatToken.setPrecision(precision); - } + l = b.movePointRight(precision).longValue(); + if (l >= Math.pow(10, precision - 4)) { + formatToken.setPrecision(precision); } } - if (requireScientificRepresentation) { - precision = formatToken.getPrecision(); - precision--; - formatToken.setPrecision(precision); - transform_e(); - } else { - transform_f(); - } } + if (requireScientificRepresentation) { + precision = formatToken.getPrecision(); + precision--; + formatToken.setPrecision(precision); + transform_e(result); + } else { + transform_f(result); + } + } - void transform_f() { - // TODO: store a default DecimalFormat we can clone? - String pattern = "0.000000"; - if (formatToken.flagComma || formatToken.getPrecision() != 6) { - StringBuilder patternBuilder = new StringBuilder(); - if (formatToken.flagComma) { - patternBuilder.append(','); - int groupingSize = decimalFormat.getGroupingSize(); - if (groupingSize > 1) { - char[] sharps = new char[groupingSize - 1]; - Arrays.fill(sharps, '#'); - patternBuilder.append(sharps); - } + private void transform_f(StringBuilder result) { + // TODO: store a default DecimalFormat we can clone? + String pattern = "0.000000"; + DecimalFormat decimalFormat = (DecimalFormat) getNumberFormat(); + if (formatToken.flagComma || formatToken.getPrecision() != 6) { + StringBuilder patternBuilder = new StringBuilder(); + if (formatToken.flagComma) { + patternBuilder.append(','); + int groupingSize = decimalFormat.getGroupingSize(); + if (groupingSize > 1) { + char[] sharps = new char[groupingSize - 1]; + Arrays.fill(sharps, '#'); + patternBuilder.append(sharps); } - patternBuilder.append('0'); - if (formatToken.getPrecision() > 0) { - patternBuilder.append('.'); - char[] zeros = new char[formatToken.getPrecision()]; - Arrays.fill(zeros, '0'); // This is a *pattern* character, so no localization. - patternBuilder.append(zeros); - } - pattern = patternBuilder.toString(); } - // TODO: if DecimalFormat.toPattern was cheap, we could make this cheap (preferably *in* DecimalFormat). - decimalFormat.applyPattern(pattern); - result.append(decimalFormat.format(argument)); - // if the flag is sharp and decimal separator is always given out. - if (formatToken.flagSharp && formatToken.getPrecision() == 0) { - result.append(localeData.decimalSeparator); + patternBuilder.append('0'); + if (formatToken.getPrecision() > 0) { + patternBuilder.append('.'); + char[] zeros = new char[formatToken.getPrecision()]; + Arrays.fill(zeros, '0'); // This is a *pattern* character, so no localization. + patternBuilder.append(zeros); } + pattern = patternBuilder.toString(); } + // TODO: if DecimalFormat.toPattern was cheap, we could make this cheap (preferably *in* DecimalFormat). + decimalFormat.applyPattern(pattern); + result.append(decimalFormat.format(arg)); + // if the flag is sharp and decimal separator is always given out. + if (formatToken.flagSharp && formatToken.getPrecision() == 0) { + result.append(localeData.decimalSeparator); + } + } - void transform_a() { - if (argument instanceof Float) { - Float F = (Float) argument; - result.append(Float.toHexString(F.floatValue())); - } else if (argument instanceof Double) { - Double D = (Double) argument; - result.append(Double.toHexString(D.doubleValue())); - } else { - throw badArgumentType(); - } - - if (!formatToken.isPrecisionSet()) { - return; - } + private void transform_a(StringBuilder result) { + if (arg instanceof Float) { + result.append(Float.toHexString(((Float) arg).floatValue())); + } else if (arg instanceof Double) { + result.append(Double.toHexString(((Double) arg).doubleValue())); + } else { + throw badArgumentType(); + } - int precision = formatToken.getPrecision(); - precision = (0 == precision ? 1 : precision); - int indexOfFirstFractionalDigit = result.indexOf(".") + 1; - int indexOfP = result.indexOf("p"); - int fractionalLength = indexOfP - indexOfFirstFractionalDigit; + if (!formatToken.isPrecisionSet()) { + return; + } - if (fractionalLength == precision) { - return; - } + int precision = formatToken.getPrecision(); + precision = (0 == precision ? 1 : precision); + int indexOfFirstFractionalDigit = result.indexOf(".") + 1; + int indexOfP = result.indexOf("p"); + int fractionalLength = indexOfP - indexOfFirstFractionalDigit; - if (fractionalLength < precision) { - char zeros[] = new char[precision - fractionalLength]; - Arrays.fill(zeros, '0'); // %a shouldn't be localized. - result.insert(indexOfP, zeros); - return; - } - result.delete(indexOfFirstFractionalDigit + precision, indexOfP); + if (fractionalLength == precision) { + return; } - private IllegalFormatConversionException badArgumentType() { - throw new IllegalFormatConversionException(formatToken.getConversionType(), - argument.getClass()); + if (fractionalLength < precision) { + char zeros[] = new char[precision - fractionalLength]; + Arrays.fill(zeros, '0'); // %a shouldn't be localized. + result.insert(indexOfP, zeros); + return; } + result.delete(indexOfFirstFractionalDigit + precision, indexOfP); } private static class FormatSpecifierParser { |