2481 * specifier that is incompatible with the given arguments,
2482 * insufficient arguments given the format string, or other
2483 * illegal conditions. For specification of all possible
2484 * formatting errors, see the <a href="#detail">Details</a>
2485 * section of the formatter class specification.
2486 *
2487 * @throws FormatterClosedException
2488 * If this formatter has been closed by invoking its {@link
2489 * #close()} method
2490 *
2491 * @return This formatter
2492 */
2493 public Formatter format(Locale l, String format, Object ... args) {
2494 ensureOpen();
2495
2496 // index of last argument referenced
2497 int last = -1;
2498 // last ordinary index
2499 int lasto = -1;
2500
2501 FormatString[] fsa = parse(format);
2502 for (FormatString fs : fsa) {
2503 int index = fs.index();
2504 try {
2505 switch (index) {
2506 case -2: // fixed string, "%n", or "%%"
2507 fs.print(null, l);
2508 break;
2509 case -1: // relative index
2510 if (last < 0 || (args != null && last > args.length - 1))
2511 throw new MissingFormatArgumentException(fs.toString());
2512 fs.print((args == null ? null : args[last]), l);
2513 break;
2514 case 0: // ordinary index
2515 lasto++;
2516 last = lasto;
2517 if (args != null && lasto > args.length - 1)
2518 throw new MissingFormatArgumentException(fs.toString());
2519 fs.print((args == null ? null : args[lasto]), l);
2520 break;
2521 default: // explicit index
2524 throw new MissingFormatArgumentException(fs.toString());
2525 fs.print((args == null ? null : args[last]), l);
2526 break;
2527 }
2528 } catch (IOException x) {
2529 lastException = x;
2530 }
2531 }
2532 return this;
2533 }
2534
2535 // %[argument_index$][flags][width][.precision][t]conversion
2536 private static final String formatSpecifier
2537 = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
2538
2539 private static Pattern fsPattern = Pattern.compile(formatSpecifier);
2540
2541 /**
2542 * Finds format specifiers in the format string.
2543 */
2544 private FormatString[] parse(String s) {
2545 ArrayList<FormatString> al = new ArrayList<>();
2546 Matcher m = fsPattern.matcher(s);
2547 for (int i = 0, len = s.length(); i < len; ) {
2548 if (m.find(i)) {
2549 // Anything between the start of the string and the beginning
2550 // of the format specifier is either fixed text or contains
2551 // an invalid format string.
2552 if (m.start() != i) {
2553 // Make sure we didn't miss any invalid format specifiers
2554 checkText(s, i, m.start());
2555 // Assume previous characters were fixed text
2556 al.add(new FixedString(s.substring(i, m.start())));
2557 }
2558
2559 al.add(new FormatSpecifier(m));
2560 i = m.end();
2561 } else {
2562 // No more valid format specifiers. Check for possible invalid
2563 // format specifiers.
2564 checkText(s, i, len);
2565 // The rest of the string is fixed text
2566 al.add(new FixedString(s.substring(i)));
2567 break;
2568 }
2569 }
2570 return al.toArray(new FormatString[al.size()]);
2571 }
2572
2573 private static void checkText(String s, int start, int end) {
2574 for (int i = start; i < end; i++) {
2575 // Any '%' found in the region starts an invalid format specifier.
2576 if (s.charAt(i) == '%') {
2577 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
2578 throw new UnknownFormatConversionException(String.valueOf(c));
2579 }
2580 }
2581 }
2582
2583 private interface FormatString {
2584 int index();
2585 void print(Object arg, Locale l) throws IOException;
2586 String toString();
2587 }
2588
2589 private class FixedString implements FormatString {
2590 private String s;
2591 FixedString(String s) { this.s = s; }
2592 public int index() { return -2; }
2593 public void print(Object arg, Locale l)
2594 throws IOException { a.append(s); }
2595 public String toString() { return s; }
2596 }
2597
2598 /**
2599 * Enum for {@code BigDecimal} formatting.
2600 */
2601 public enum BigDecimalLayoutForm {
2602 /**
2603 * Format the {@code BigDecimal} in computerized scientific notation.
2604 */
2605 SCIENTIFIC,
2606
2607 /**
2608 * Format the {@code BigDecimal} as a decimal number.
2609 */
2610 DECIMAL_FLOAT
2611 };
2612
2613 private class FormatSpecifier implements FormatString {
2614 private int index = -1;
2615 private Flags f = Flags.NONE;
2618 private boolean dt = false;
2619 private char c;
2620
2621 private int index(String s) {
2622 if (s != null) {
2623 try {
2624 index = Integer.parseInt(s.substring(0, s.length() - 1));
2625 } catch (NumberFormatException x) {
2626 assert(false);
2627 }
2628 } else {
2629 index = 0;
2630 }
2631 return index;
2632 }
2633
2634 public int index() {
2635 return index;
2636 }
2637
2638 private Flags flags(String s) {
2639 f = Flags.parse(s);
2640 if (f.contains(Flags.PREVIOUS))
2641 index = -1;
2642 return f;
2643 }
2644
2645 Flags flags() {
2646 return f;
2647 }
2648
2649 private int width(String s) {
2650 width = -1;
2651 if (s != null) {
2652 try {
2653 width = Integer.parseInt(s);
2654 if (width < 0)
2655 throw new IllegalFormatWidthException(width);
2656 } catch (NumberFormatException x) {
2657 assert(false);
2658 }
2659 }
2666
2667 private int precision(String s) {
2668 precision = -1;
2669 if (s != null) {
2670 try {
2671 // remove the '.'
2672 precision = Integer.parseInt(s.substring(1));
2673 if (precision < 0)
2674 throw new IllegalFormatPrecisionException(precision);
2675 } catch (NumberFormatException x) {
2676 assert(false);
2677 }
2678 }
2679 return precision;
2680 }
2681
2682 int precision() {
2683 return precision;
2684 }
2685
2686 private char conversion(String s) {
2687 c = s.charAt(0);
2688 if (!dt) {
2689 if (!Conversion.isValid(c))
2690 throw new UnknownFormatConversionException(String.valueOf(c));
2691 if (Character.isUpperCase(c))
2692 f.add(Flags.UPPERCASE);
2693 c = Character.toLowerCase(c);
2694 if (Conversion.isText(c))
2695 index = -2;
2696 }
2697 return c;
2698 }
2699
2700 private char conversion() {
2701 return c;
2702 }
2703
2704 FormatSpecifier(Matcher m) {
2705 int idx = 1;
2706
2707 index(m.group(idx++));
2708 flags(m.group(idx++));
2709 width(m.group(idx++));
2710 precision(m.group(idx++));
2711
2712 String tT = m.group(idx++);
2713 if (tT != null) {
2714 dt = true;
2715 if (tT.equals("T"))
2716 f.add(Flags.UPPERCASE);
2717 }
2718
2719 conversion(m.group(idx));
2720
2721 if (dt)
2722 checkDateTime();
2723 else if (Conversion.isGeneral(c))
2724 checkGeneral();
2725 else if (Conversion.isCharacter(c))
2726 checkCharacter();
2727 else if (Conversion.isInteger(c))
2728 checkInteger();
2729 else if (Conversion.isFloat(c))
2730 checkFloat();
2731 else if (Conversion.isText(c))
2732 checkText();
2733 else
2734 throw new UnknownFormatConversionException(String.valueOf(c));
2735 }
2736
2737 public void print(Object arg, Locale l) throws IOException {
2738 if (dt) {
2739 printDateTime(arg, l);
2892 s = ((arg instanceof Boolean)
2893 ? ((Boolean)arg).toString()
2894 : Boolean.toString(true));
2895 else
2896 s = Boolean.toString(false);
2897 print(s);
2898 }
2899
2900 private void printHashCode(Object arg) throws IOException {
2901 String s = (arg == null
2902 ? "null"
2903 : Integer.toHexString(arg.hashCode()));
2904 print(s);
2905 }
2906
2907 private void print(String s) throws IOException {
2908 if (precision != -1 && precision < s.length())
2909 s = s.substring(0, precision);
2910 if (f.contains(Flags.UPPERCASE))
2911 s = s.toUpperCase();
2912 a.append(justify(s));
2913 }
2914
2915 private String justify(String s) {
2916 if (width == -1)
2917 return s;
2918 StringBuilder sb = new StringBuilder();
2919 boolean pad = f.contains(Flags.LEFT_JUSTIFY);
2920 int sp = width - s.length();
2921 if (!pad)
2922 for (int i = 0; i < sp; i++) sb.append(' ');
2923 sb.append(s);
2924 if (pad)
2925 for (int i = 0; i < sp; i++) sb.append(' ');
2926 return sb.toString();
2927 }
2928
2929 public String toString() {
2930 StringBuilder sb = new StringBuilder("%");
2931 // Flags.UPPERCASE is set internally for legal conversions.
2932 Flags dupf = f.dup().remove(Flags.UPPERCASE);
2933 sb.append(dupf.toString());
2934 if (index > 0)
2935 sb.append(index).append('$');
2936 if (width != -1)
2937 sb.append(width);
2938 if (precision != -1)
2939 sb.append('.').append(precision);
2940 if (dt)
2941 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2942 sb.append(f.contains(Flags.UPPERCASE)
2943 ? Character.toUpperCase(c) : c);
2944 return sb.toString();
2945 }
2946
3071 print(v, l);
3072 }
3073
3074 private void print(int value, Locale l) throws IOException {
3075 long v = value;
3076 if (value < 0
3077 && (c == Conversion.OCTAL_INTEGER
3078 || c == Conversion.HEXADECIMAL_INTEGER)) {
3079 v += (1L << 32);
3080 assert v >= 0 : v;
3081 }
3082 print(v, l);
3083 }
3084
3085 private void print(long value, Locale l) throws IOException {
3086
3087 StringBuilder sb = new StringBuilder();
3088
3089 if (c == Conversion.DECIMAL_INTEGER) {
3090 boolean neg = value < 0;
3091 char[] va;
3092 if (value < 0)
3093 va = Long.toString(value, 10).substring(1).toCharArray();
3094 else
3095 va = Long.toString(value, 10).toCharArray();
3096
3097 // leading sign indicator
3098 leadingSign(sb, neg);
3099
3100 // the value
3101 localizedMagnitude(sb, va, f, adjustWidth(width, f, neg), l);
3102
3103 // trailing sign indicator
3104 trailingSign(sb, neg);
3105 } else if (c == Conversion.OCTAL_INTEGER) {
3106 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3107 Flags.PLUS);
3108 String s = Long.toOctalString(value);
3109 int len = (f.contains(Flags.ALTERNATE)
3110 ? s.length() + 1
3111 : s.length());
3112
3113 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3114 if (f.contains(Flags.ALTERNATE))
3115 sb.append('0');
3116 if (f.contains(Flags.ZERO_PAD))
3117 for (int i = 0; i < width - len; i++) sb.append('0');
3118 sb.append(s);
3119 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3120 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3121 Flags.PLUS);
3122 String s = Long.toHexString(value);
3123 int len = (f.contains(Flags.ALTERNATE)
3124 ? s.length() + 2
3125 : s.length());
3126
3127 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3128 if (f.contains(Flags.ALTERNATE))
3129 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3130 if (f.contains(Flags.ZERO_PAD))
3131 for (int i = 0; i < width - len; i++) sb.append('0');
3132 if (f.contains(Flags.UPPERCASE))
3133 s = s.toUpperCase();
3134 sb.append(s);
3135 }
3136
3137 // justify based on width
3138 a.append(justify(sb.toString()));
3139 }
3140
3141 // neg := val < 0
3142 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3143 if (!neg) {
3144 if (f.contains(Flags.PLUS)) {
3145 sb.append('+');
3146 } else if (f.contains(Flags.LEADING_SPACE)) {
3147 sb.append(' ');
3148 }
3149 } else {
3150 if (f.contains(Flags.PARENTHESES))
3151 sb.append('(');
3152 else
3153 sb.append('-');
3154 }
3155 return sb;
3156 }
3157
3158 // neg := val < 0
3159 private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3160 if (neg && f.contains(Flags.PARENTHESES))
3161 sb.append(')');
3162 return sb;
3163 }
3164
3165 private void print(BigInteger value, Locale l) throws IOException {
3166 StringBuilder sb = new StringBuilder();
3167 boolean neg = value.signum() == -1;
3168 BigInteger v = value.abs();
3169
3170 // leading sign indicator
3171 leadingSign(sb, neg);
3172
3173 // the value
3174 if (c == Conversion.DECIMAL_INTEGER) {
3175 char[] va = v.toString().toCharArray();
3176 localizedMagnitude(sb, va, f, adjustWidth(width, f, neg), l);
3177 } else if (c == Conversion.OCTAL_INTEGER) {
3178 String s = v.toString(8);
3179
3180 int len = s.length() + sb.length();
3181 if (neg && f.contains(Flags.PARENTHESES))
3182 len++;
3183
3184 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3185 if (f.contains(Flags.ALTERNATE)) {
3186 len++;
3187 sb.append('0');
3188 }
3189 if (f.contains(Flags.ZERO_PAD)) {
3190 for (int i = 0; i < width - len; i++)
3191 sb.append('0');
3192 }
3193 sb.append(s);
3194 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3195 String s = v.toString(16);
3196
3197 int len = s.length() + sb.length();
3198 if (neg && f.contains(Flags.PARENTHESES))
3199 len++;
3200
3201 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3202 if (f.contains(Flags.ALTERNATE)) {
3203 len += 2;
3204 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3205 }
3206 if (f.contains(Flags.ZERO_PAD))
3207 for (int i = 0; i < width - len; i++)
3208 sb.append('0');
3209 if (f.contains(Flags.UPPERCASE))
3210 s = s.toUpperCase();
3211 sb.append(s);
3212 }
3213
3214 // trailing sign indicator
3215 trailingSign(sb, (value.signum() == -1));
3216
3217 // justify based on width
3218 a.append(justify(sb.toString()));
3219 }
3220
3221 private void print(float value, Locale l) throws IOException {
3222 print((double) value, l);
3223 }
3224
3225 private void print(double value, Locale l) throws IOException {
3226 StringBuilder sb = new StringBuilder();
3227 boolean neg = Double.compare(value, 0.0) == -1;
3228
3229 if (!Double.isNaN(value)) {
3230 double v = Math.abs(value);
3231
3232 // leading sign indicator
3233 leadingSign(sb, neg);
3234
3235 // the value
3236 if (!Double.isInfinite(v))
3237 print(sb, v, l, f, c, precision, neg);
3238 else
3239 sb.append(f.contains(Flags.UPPERCASE)
3240 ? "INFINITY" : "Infinity");
3241
3242 // trailing sign indicator
3243 trailingSign(sb, neg);
3244 } else {
3245 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3246 }
3247
3248 // justify based on width
3249 a.append(justify(sb.toString()));
3250 }
3251
3252 // !Double.isInfinite(value) && !Double.isNaN(value)
3253 private void print(StringBuilder sb, double value, Locale l,
3254 Flags f, char c, int precision, boolean neg)
3255 throws IOException
3256 {
3257 if (c == Conversion.SCIENTIFIC) {
3258 // Create a new FormattedFloatingDecimal with the desired
3259 // precision.
3260 int prec = (precision == -1 ? 6 : precision);
3261
3262 FormattedFloatingDecimal fd
3263 = FormattedFloatingDecimal.valueOf(value, prec,
3264 FormattedFloatingDecimal.Form.SCIENTIFIC);
3265
3266 char[] mant = addZeros(fd.getMantissa(), prec);
3267
3268 // If the precision is zero and the '#' flag is set, add the
3269 // requested decimal point.
3270 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3271 mant = addDot(mant);
3272
3273 char[] exp = (value == 0.0)
3274 ? new char[] {'+','0','0'} : fd.getExponent();
3275
3276 int newW = width;
3277 if (width != -1)
3278 newW = adjustWidth(width - exp.length - 1, f, neg);
3279 localizedMagnitude(sb, mant, f, newW, l);
3280
3281 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3282
3283 Flags flags = f.dup().remove(Flags.GROUP);
3284 char sign = exp[0];
3285 assert(sign == '+' || sign == '-');
3286 sb.append(sign);
3287
3288 char[] tmp = new char[exp.length - 1];
3289 System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3290 sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3291 } else if (c == Conversion.DECIMAL_FLOAT) {
3292 // Create a new FormattedFloatingDecimal with the desired
3293 // precision.
3294 int prec = (precision == -1 ? 6 : precision);
3295
3296 FormattedFloatingDecimal fd
3297 = FormattedFloatingDecimal.valueOf(value, prec,
3298 FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3299
3300 char[] mant = addZeros(fd.getMantissa(), prec);
3301
3302 // If the precision is zero and the '#' flag is set, add the
3303 // requested decimal point.
3304 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3305 mant = addDot(mant);
3306
3307 int newW = width;
3308 if (width != -1)
3309 newW = adjustWidth(width, f, neg);
3310 localizedMagnitude(sb, mant, f, newW, l);
3311 } else if (c == Conversion.GENERAL) {
3312 int prec = precision;
3313 if (precision == -1)
3314 prec = 6;
3315 else if (precision == 0)
3316 prec = 1;
3317
3318 char[] exp;
3319 char[] mant;
3320 int expRounded;
3321 if (value == 0.0) {
3322 exp = null;
3323 mant = new char[] {'0'};
3324 expRounded = 0;
3325 } else {
3326 FormattedFloatingDecimal fd
3327 = FormattedFloatingDecimal.valueOf(value, prec,
3328 FormattedFloatingDecimal.Form.GENERAL);
3329 exp = fd.getExponent();
3330 mant = fd.getMantissa();
3331 expRounded = fd.getExponentRounded();
3332 }
3333
3334 if (exp != null) {
3335 prec -= 1;
3336 } else {
3337 prec -= expRounded + 1;
3338 }
3339
3340 mant = addZeros(mant, prec);
3341 // If the precision is zero and the '#' flag is set, add the
3342 // requested decimal point.
3343 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3344 mant = addDot(mant);
3345
3346 int newW = width;
3347 if (width != -1) {
3348 if (exp != null)
3349 newW = adjustWidth(width - exp.length - 1, f, neg);
3350 else
3351 newW = adjustWidth(width, f, neg);
3352 }
3353 localizedMagnitude(sb, mant, f, newW, l);
3354
3355 if (exp != null) {
3356 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3357
3358 Flags flags = f.dup().remove(Flags.GROUP);
3359 char sign = exp[0];
3360 assert(sign == '+' || sign == '-');
3361 sb.append(sign);
3362
3363 char[] tmp = new char[exp.length - 1];
3364 System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3365 sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3366 }
3367 } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3368 int prec = precision;
3369 if (precision == -1)
3370 // assume that we want all of the digits
3371 prec = 0;
3372 else if (precision == 0)
3373 prec = 1;
3374
3375 String s = hexDouble(value, prec);
3376
3377 char[] va;
3378 boolean upper = f.contains(Flags.UPPERCASE);
3379 sb.append(upper ? "0X" : "0x");
3380
3381 if (f.contains(Flags.ZERO_PAD))
3382 for (int i = 0; i < width - s.length() - 2; i++)
3383 sb.append('0');
3384
3385 int idx = s.indexOf('p');
3386 va = s.substring(0, idx).toCharArray();
3387 if (upper) {
3388 String tmp = new String(va);
3389 // don't localize hex
3390 tmp = tmp.toUpperCase(Locale.US);
3391 va = tmp.toCharArray();
3392 }
3393 sb.append(prec != 0 ? addZeros(va, prec) : va);
3394 sb.append(upper ? 'P' : 'p');
3395 sb.append(s.substring(idx+1));
3396 }
3397 }
3398
3399 // Add zeros to the requested precision.
3400 private char[] addZeros(char[] v, int prec) {
3401 // Look for the dot. If we don't find one, the we'll need to add
3402 // it before we add the zeros.
3403 int i;
3404 for (i = 0; i < v.length; i++) {
3405 if (v[i] == '.')
3406 break;
3407 }
3408 boolean needDot = false;
3409 if (i == v.length) {
3410 needDot = true;
3411 }
3412
3413 // Determine existing precision.
3414 int outPrec = v.length - i - (needDot ? 0 : 1);
3415 assert (outPrec <= prec);
3416 if (outPrec == prec)
3417 return v;
3418
3419 // Create new array with existing contents.
3420 char[] tmp
3421 = new char[v.length + prec - outPrec + (needDot ? 1 : 0)];
3422 System.arraycopy(v, 0, tmp, 0, v.length);
3423
3424 // Add dot if previously determined to be necessary.
3425 int start = v.length;
3426 if (needDot) {
3427 tmp[v.length] = '.';
3428 start++;
3429 }
3430
3431 // Add zeros.
3432 for (int j = start; j < tmp.length; j++)
3433 tmp[j] = '0';
3434
3435 return tmp;
3436 }
3437
3438 // Method assumes that d > 0.
3439 private String hexDouble(double d, int prec) {
3440 // Let Double.toHexString handle simple cases
3441 if(!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13)
3442 // remove "0x"
3443 return Double.toHexString(d).substring(2);
3444 else {
3445 assert(prec >= 1 && prec <= 12);
3446
3447 int exponent = Math.getExponent(d);
3448 boolean subnormal
3449 = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3450
3451 // If this is subnormal input so normalize (could be faster to
3452 // do as integer operation).
3453 if (subnormal) {
3454 scaleUp = Math.scalb(1.0, 54);
3455 d *= scaleUp;
3456 // Calculate the exponent. This is not just exponent + 54
3457 // since the former is not the normalized exponent.
3458 exponent = Math.getExponent(d);
3459 assert exponent >= DoubleConsts.MIN_EXPONENT &&
3460 exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3461 }
3462
3463 int precision = 1 + prec*4;
3464 int shiftDistance
3517 }
3518 }
3519 }
3520
3521 private void print(BigDecimal value, Locale l) throws IOException {
3522 if (c == Conversion.HEXADECIMAL_FLOAT)
3523 failConversion(c, value);
3524 StringBuilder sb = new StringBuilder();
3525 boolean neg = value.signum() == -1;
3526 BigDecimal v = value.abs();
3527 // leading sign indicator
3528 leadingSign(sb, neg);
3529
3530 // the value
3531 print(sb, v, l, f, c, precision, neg);
3532
3533 // trailing sign indicator
3534 trailingSign(sb, neg);
3535
3536 // justify based on width
3537 a.append(justify(sb.toString()));
3538 }
3539
3540 // value > 0
3541 private void print(StringBuilder sb, BigDecimal value, Locale l,
3542 Flags f, char c, int precision, boolean neg)
3543 throws IOException
3544 {
3545 if (c == Conversion.SCIENTIFIC) {
3546 // Create a new BigDecimal with the desired precision.
3547 int prec = (precision == -1 ? 6 : precision);
3548 int scale = value.scale();
3549 int origPrec = value.precision();
3550 int nzeros = 0;
3551 int compPrec;
3552
3553 if (prec > origPrec - 1) {
3554 compPrec = origPrec;
3555 nzeros = prec - (origPrec - 1);
3556 } else {
3557 compPrec = prec + 1;
3558 }
3559
3560 MathContext mc = new MathContext(compPrec);
3561 BigDecimal v
3562 = new BigDecimal(value.unscaledValue(), scale, mc);
3563
3564 BigDecimalLayout bdl
3565 = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3566 BigDecimalLayoutForm.SCIENTIFIC);
3567
3568 char[] mant = bdl.mantissa();
3569
3570 // Add a decimal point if necessary. The mantissa may not
3571 // contain a decimal point if the scale is zero (the internal
3572 // representation has no fractional part) or the original
3573 // precision is one. Append a decimal point if '#' is set or if
3574 // we require zero padding to get to the requested precision.
3575 if ((origPrec == 1 || !bdl.hasDot())
3576 && (nzeros > 0 || (f.contains(Flags.ALTERNATE))))
3577 mant = addDot(mant);
3578
3579 // Add trailing zeros in the case precision is greater than
3580 // the number of available digits after the decimal separator.
3581 mant = trailingZeros(mant, nzeros);
3582
3583 char[] exp = bdl.exponent();
3584 int newW = width;
3585 if (width != -1)
3586 newW = adjustWidth(width - exp.length - 1, f, neg);
3587 localizedMagnitude(sb, mant, f, newW, l);
3588
3589 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3590
3591 Flags flags = f.dup().remove(Flags.GROUP);
3592 char sign = exp[0];
3593 assert(sign == '+' || sign == '-');
3594 sb.append(exp[0]);
3595
3596 char[] tmp = new char[exp.length - 1];
3597 System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3598 sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3599 } else if (c == Conversion.DECIMAL_FLOAT) {
3600 // Create a new BigDecimal with the desired precision.
3601 int prec = (precision == -1 ? 6 : precision);
3602 int scale = value.scale();
3603
3604 if (scale > prec) {
3605 // more "scale" digits than the requested "precision"
3606 int compPrec = value.precision();
3607 if (compPrec <= scale) {
3608 // case of 0.xxxxxx
3609 value = value.setScale(prec, RoundingMode.HALF_UP);
3610 } else {
3611 compPrec -= (scale - prec);
3612 value = new BigDecimal(value.unscaledValue(),
3613 scale,
3614 new MathContext(compPrec));
3615 }
3616 }
3617 BigDecimalLayout bdl = new BigDecimalLayout(
3618 value.unscaledValue(),
3619 value.scale(),
3620 BigDecimalLayoutForm.DECIMAL_FLOAT);
3621
3622 char mant[] = bdl.mantissa();
3623 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3624
3625 // Add a decimal point if necessary. The mantissa may not
3626 // contain a decimal point if the scale is zero (the internal
3627 // representation has no fractional part). Append a decimal
3628 // point if '#' is set or we require zero padding to get to the
3629 // requested precision.
3630 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE) || nzeros > 0))
3631 mant = addDot(bdl.mantissa());
3632
3633 // Add trailing zeros if the precision is greater than the
3634 // number of available digits after the decimal separator.
3635 mant = trailingZeros(mant, nzeros);
3636
3637 localizedMagnitude(sb, mant, f, adjustWidth(width, f, neg), l);
3638 } else if (c == Conversion.GENERAL) {
3639 int prec = precision;
3640 if (precision == -1)
3641 prec = 6;
3642 else if (precision == 0)
3643 prec = 1;
3644
3645 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3646 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3647 if ((value.equals(BigDecimal.ZERO))
3648 || ((value.compareTo(tenToTheNegFour) != -1)
3649 && (value.compareTo(tenToThePrec) == -1))) {
3650
3651 int e = - value.scale()
3652 + (value.unscaledValue().toString().length() - 1);
3653
3654 // xxx.yyy
3655 // g precision (# sig digits) = #x + #y
3656 // f precision = #y
3657 // exponent = #x - 1
3676 }
3677
3678 private class BigDecimalLayout {
3679 private StringBuilder mant;
3680 private StringBuilder exp;
3681 private boolean dot = false;
3682 private int scale;
3683
3684 public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3685 layout(intVal, scale, form);
3686 }
3687
3688 public boolean hasDot() {
3689 return dot;
3690 }
3691
3692 public int scale() {
3693 return scale;
3694 }
3695
3696 // char[] with canonical string representation
3697 public char[] layoutChars() {
3698 StringBuilder sb = new StringBuilder(mant);
3699 if (exp != null) {
3700 sb.append('E');
3701 sb.append(exp);
3702 }
3703 return toCharArray(sb);
3704 }
3705
3706 public char[] mantissa() {
3707 return toCharArray(mant);
3708 }
3709
3710 // The exponent will be formatted as a sign ('+' or '-') followed
3711 // by the exponent zero-padded to include at least two digits.
3712 public char[] exponent() {
3713 return toCharArray(exp);
3714 }
3715
3716 private char[] toCharArray(StringBuilder sb) {
3717 if (sb == null)
3718 return null;
3719 char[] result = new char[sb.length()];
3720 sb.getChars(0, result.length, result, 0);
3721 return result;
3722 }
3723
3724 private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3725 char coeff[] = intVal.toString().toCharArray();
3726 this.scale = scale;
3727
3728 // Construct a buffer, with sufficient capacity for all cases.
3729 // If E-notation is needed, length will be: +1 if negative, +1
3730 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3731 // exponent. Otherwise it could have +1 if negative, plus
3732 // leading "0.00000"
3733 mant = new StringBuilder(coeff.length + 14);
3734
3735 if (scale == 0) {
3736 int len = coeff.length;
3737 if (len > 1) {
3738 mant.append(coeff[0]);
3739 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3740 mant.append('.');
3741 dot = true;
3742 mant.append(coeff, 1, len - 1);
3743 exp = new StringBuilder("+");
3744 if (len < 10)
3745 exp.append("0").append(len - 1);
3746 else
3747 exp.append(len - 1);
3748 } else {
3749 mant.append(coeff, 1, len - 1);
3750 }
3751 } else {
3752 mant.append(coeff);
3753 if (form == BigDecimalLayoutForm.SCIENTIFIC)
3754 exp = new StringBuilder("+00");
3755 }
3756 return;
3757 }
3758 long adjusted = -(long) scale + (coeff.length - 1);
3759 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3760 // count of padding zeros
3761 int pad = scale - coeff.length;
3762 if (pad >= 0) {
3763 // 0.xxx form
3764 mant.append("0.");
3765 dot = true;
3766 for (; pad > 0 ; pad--) mant.append('0');
3767 mant.append(coeff);
3768 } else {
3769 if (-pad < coeff.length) {
3770 // xx.xx form
3771 mant.append(coeff, 0, -pad);
3772 mant.append('.');
3773 dot = true;
3774 mant.append(coeff, -pad, scale);
3775 } else {
3776 // xx form
3777 mant.append(coeff, 0, coeff.length);
3778 for (int i = 0; i < -scale; i++)
3779 mant.append('0');
3780 this.scale = 0;
3781 }
3782 }
3783 } else {
3784 // x.xxx form
3785 mant.append(coeff[0]);
3786 if (coeff.length > 1) {
3787 mant.append('.');
3788 dot = true;
3789 mant.append(coeff, 1, coeff.length-1);
3790 }
3791 exp = new StringBuilder();
3792 if (adjusted != 0) {
3793 long abs = Math.abs(adjusted);
3794 // require sign
3795 exp.append(adjusted < 0 ? '-' : '+');
3796 if (abs < 10)
3797 exp.append('0');
3798 exp.append(abs);
3799 } else {
3800 exp.append("+00");
3801 }
3802 }
3803 }
3804 }
3805
3806 private int adjustWidth(int width, Flags f, boolean neg) {
3807 int newW = width;
3808 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3809 newW--;
3810 return newW;
3811 }
3812
3813 // Add a '.' to th mantissa if required
3814 private char[] addDot(char[] mant) {
3815 char[] tmp = mant;
3816 tmp = new char[mant.length + 1];
3817 System.arraycopy(mant, 0, tmp, 0, mant.length);
3818 tmp[tmp.length - 1] = '.';
3819 return tmp;
3820 }
3821
3822 // Add trailing zeros in the case precision is greater than the number
3823 // of available digits after the decimal separator.
3824 private char[] trailingZeros(char[] mant, int nzeros) {
3825 char[] tmp = mant;
3826 if (nzeros > 0) {
3827 tmp = new char[mant.length + nzeros];
3828 System.arraycopy(mant, 0, tmp, 0, mant.length);
3829 for (int i = mant.length; i < tmp.length; i++)
3830 tmp[i] = '0';
3831 }
3832 return tmp;
3833 }
3834
3835 private void print(Calendar t, char c, Locale l) throws IOException
3836 {
3837 StringBuilder sb = new StringBuilder();
3838 print(sb, t, c, l);
3839
3840 // justify based on width
3841 String s = justify(sb.toString());
3842 if (f.contains(Flags.UPPERCASE))
3843 s = s.toUpperCase();
3844
3845 a.append(s);
3846 }
3847
3848 private Appendable print(StringBuilder sb, Calendar t, char c,
3849 Locale l)
3850 throws IOException
3851 {
3852 if (sb == null)
3853 sb = new StringBuilder();
3854 switch (c) {
3855 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3856 case DateTime.HOUR_0: // 'I' (01 - 12)
3857 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3858 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3859 int i = t.get(Calendar.HOUR_OF_DAY);
3860 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3861 i = (i == 0 || i == 12 ? 12 : i % 12);
3862 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3863 || c == DateTime.HOUR_0
3864 ? Flags.ZERO_PAD
3865 : Flags.NONE);
3866 sb.append(localizedMagnitude(null, i, flags, 2, l));
3867 break;
3868 }
3869 case DateTime.MINUTE: { // 'M' (00 - 59)
3870 int i = t.get(Calendar.MINUTE);
3871 Flags flags = Flags.ZERO_PAD;
4004 // Composites
4005 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4006 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4007 char sep = ':';
4008 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4009 print(sb, t, DateTime.MINUTE, l);
4010 if (c == DateTime.TIME) {
4011 sb.append(sep);
4012 print(sb, t, DateTime.SECOND, l);
4013 }
4014 break;
4015 }
4016 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4017 char sep = ':';
4018 print(sb, t, DateTime.HOUR_0, l).append(sep);
4019 print(sb, t, DateTime.MINUTE, l).append(sep);
4020 print(sb, t, DateTime.SECOND, l).append(' ');
4021 // this may be in wrong place for some locales
4022 StringBuilder tsb = new StringBuilder();
4023 print(tsb, t, DateTime.AM_PM, l);
4024 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4025 break;
4026 }
4027 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4028 char sep = ' ';
4029 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4030 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4031 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4032 print(sb, t, DateTime.TIME, l).append(sep);
4033 print(sb, t, DateTime.ZONE, l).append(sep);
4034 print(sb, t, DateTime.YEAR_4, l);
4035 break;
4036 }
4037 case DateTime.DATE: { // 'D' (mm/dd/yy)
4038 char sep = '/';
4039 print(sb, t, DateTime.MONTH, l).append(sep);
4040 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4041 print(sb, t, DateTime.YEAR_2, l);
4042 break;
4043 }
4044 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4045 char sep = '-';
4046 print(sb, t, DateTime.YEAR_4, l).append(sep);
4047 print(sb, t, DateTime.MONTH, l).append(sep);
4048 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4049 break;
4050 }
4051 default:
4052 assert false;
4053 }
4054 return sb;
4055 }
4056
4057 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4058 StringBuilder sb = new StringBuilder();
4059 print(sb, t, c, l);
4060 // justify based on width
4061 String s = justify(sb.toString());
4062 if (f.contains(Flags.UPPERCASE))
4063 s = s.toUpperCase();
4064 a.append(s);
4065 }
4066
4067 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4068 Locale l) throws IOException {
4069 if (sb == null)
4070 sb = new StringBuilder();
4071 try {
4072 switch (c) {
4073 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4074 int i = t.get(ChronoField.HOUR_OF_DAY);
4075 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4076 break;
4077 }
4078 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4079 int i = t.get(ChronoField.HOUR_OF_DAY);
4080 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4081 break;
4082 }
4083 case DateTime.HOUR_0: { // 'I' (01 - 12)
4084 int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4292
4293 // -- Methods to support throwing exceptions --
4294
4295 private void failMismatch(Flags f, char c) {
4296 String fs = f.toString();
4297 throw new FormatFlagsConversionMismatchException(fs, c);
4298 }
4299
4300 private void failConversion(char c, Object arg) {
4301 throw new IllegalFormatConversionException(c, arg.getClass());
4302 }
4303
4304 private char getZero(Locale l) {
4305 if ((l != null) && !l.equals(locale())) {
4306 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4307 return dfs.getZeroDigit();
4308 }
4309 return zero;
4310 }
4311
4312 private StringBuilder
4313 localizedMagnitude(StringBuilder sb, long value, Flags f,
4314 int width, Locale l)
4315 {
4316 char[] va = Long.toString(value, 10).toCharArray();
4317 return localizedMagnitude(sb, va, f, width, l);
4318 }
4319
4320 private StringBuilder
4321 localizedMagnitude(StringBuilder sb, char[] value, Flags f,
4322 int width, Locale l)
4323 {
4324 if (sb == null)
4325 sb = new StringBuilder();
4326 int begin = sb.length();
4327
4328 char zero = getZero(l);
4329
4330 // determine localized grouping separator and size
4331 char grpSep = '\0';
4332 int grpSize = -1;
4333 char decSep = '\0';
4334
4335 int len = value.length;
4336 int dot = len;
4337 for (int j = 0; j < len; j++) {
4338 if (value[j] == '.') {
4339 dot = j;
4340 break;
4341 }
4342 }
4343
4344 if (dot < len) {
4345 if (l == null || l.equals(Locale.US)) {
4346 decSep = '.';
4347 } else {
4348 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4349 decSep = dfs.getDecimalSeparator();
4350 }
4351 }
4352
4353 if (f.contains(Flags.GROUP)) {
4354 if (l == null || l.equals(Locale.US)) {
4355 grpSep = ',';
4356 grpSize = 3;
4357 } else {
4358 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4359 grpSep = dfs.getGroupingSeparator();
4360 DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4361 grpSize = df.getGroupingSize();
4362 }
4363 }
4364
4365 // localize the digits inserting group separators as necessary
4366 for (int j = 0; j < len; j++) {
4367 if (j == dot) {
4368 sb.append(decSep);
4369 // no more group separators after the decimal separator
4370 grpSep = '\0';
4371 continue;
4372 }
4373
4374 char c = value[j];
4375 sb.append((char) ((c - '0') + zero));
4376 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1))
4377 sb.append(grpSep);
4378 }
4379
4380 // apply zero padding
4381 len = sb.length();
4382 if (width != -1 && f.contains(Flags.ZERO_PAD))
4383 for (int k = 0; k < width - len; k++)
4384 sb.insert(begin, zero);
4385
4386 return sb;
4387 }
4388 }
4389
4390 private static class Flags {
4391 private int flags;
4392
4393 static final Flags NONE = new Flags(0); // ''
4394
4395 // duplicate declarations from Formattable.java
4396 static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'
4397 static final Flags UPPERCASE = new Flags(1<<1); // '^'
4398 static final Flags ALTERNATE = new Flags(1<<2); // '#'
4399
4400 // numerics
4401 static final Flags PLUS = new Flags(1<<3); // '+'
4402 static final Flags LEADING_SPACE = new Flags(1<<4); // ' '
4403 static final Flags ZERO_PAD = new Flags(1<<5); // '0'
4404 static final Flags GROUP = new Flags(1<<6); // ','
4416 }
4417
4418 public boolean contains(Flags f) {
4419 return (flags & f.valueOf()) == f.valueOf();
4420 }
4421
4422 public Flags dup() {
4423 return new Flags(flags);
4424 }
4425
4426 private Flags add(Flags f) {
4427 flags |= f.valueOf();
4428 return this;
4429 }
4430
4431 public Flags remove(Flags f) {
4432 flags &= ~f.valueOf();
4433 return this;
4434 }
4435
4436 public static Flags parse(String s) {
4437 char[] ca = s.toCharArray();
4438 Flags f = new Flags(0);
4439 for (char c : ca) {
4440 Flags v = parse(c);
4441 if (f.contains(v))
4442 throw new DuplicateFormatFlagsException(v.toString());
4443 f.add(v);
4444 }
4445 return f;
4446 }
4447
4448 // parse those flags which may be provided by users
4449 private static Flags parse(char c) {
4450 switch (c) {
4451 case '-': return LEFT_JUSTIFY;
4452 case '#': return ALTERNATE;
4453 case '+': return PLUS;
4454 case ' ': return LEADING_SPACE;
4455 case '0': return ZERO_PAD;
4456 case ',': return GROUP;
4457 case '(': return PARENTHESES;
4458 case '<': return PREVIOUS;
4459 default:
|
2481 * specifier that is incompatible with the given arguments,
2482 * insufficient arguments given the format string, or other
2483 * illegal conditions. For specification of all possible
2484 * formatting errors, see the <a href="#detail">Details</a>
2485 * section of the formatter class specification.
2486 *
2487 * @throws FormatterClosedException
2488 * If this formatter has been closed by invoking its {@link
2489 * #close()} method
2490 *
2491 * @return This formatter
2492 */
2493 public Formatter format(Locale l, String format, Object ... args) {
2494 ensureOpen();
2495
2496 // index of last argument referenced
2497 int last = -1;
2498 // last ordinary index
2499 int lasto = -1;
2500
2501 List<FormatString> fsa = parse(format);
2502 for (FormatString fs : fsa) {
2503 int index = fs.index();
2504 try {
2505 switch (index) {
2506 case -2: // fixed string, "%n", or "%%"
2507 fs.print(null, l);
2508 break;
2509 case -1: // relative index
2510 if (last < 0 || (args != null && last > args.length - 1))
2511 throw new MissingFormatArgumentException(fs.toString());
2512 fs.print((args == null ? null : args[last]), l);
2513 break;
2514 case 0: // ordinary index
2515 lasto++;
2516 last = lasto;
2517 if (args != null && lasto > args.length - 1)
2518 throw new MissingFormatArgumentException(fs.toString());
2519 fs.print((args == null ? null : args[lasto]), l);
2520 break;
2521 default: // explicit index
2524 throw new MissingFormatArgumentException(fs.toString());
2525 fs.print((args == null ? null : args[last]), l);
2526 break;
2527 }
2528 } catch (IOException x) {
2529 lastException = x;
2530 }
2531 }
2532 return this;
2533 }
2534
2535 // %[argument_index$][flags][width][.precision][t]conversion
2536 private static final String formatSpecifier
2537 = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
2538
2539 private static Pattern fsPattern = Pattern.compile(formatSpecifier);
2540
2541 /**
2542 * Finds format specifiers in the format string.
2543 */
2544 private List<FormatString> parse(String s) {
2545 ArrayList<FormatString> al = new ArrayList<>();
2546 Matcher m = fsPattern.matcher(s);
2547 for (int i = 0, len = s.length(); i < len; ) {
2548 if (m.find(i)) {
2549 // Anything between the start of the string and the beginning
2550 // of the format specifier is either fixed text or contains
2551 // an invalid format string.
2552 if (m.start() != i) {
2553 // Make sure we didn't miss any invalid format specifiers
2554 checkText(s, i, m.start());
2555 // Assume previous characters were fixed text
2556 al.add(new FixedString(s, i, m.start()));
2557 }
2558
2559 al.add(new FormatSpecifier(s, m));
2560 i = m.end();
2561 } else {
2562 // No more valid format specifiers. Check for possible invalid
2563 // format specifiers.
2564 checkText(s, i, len);
2565 // The rest of the string is fixed text
2566 al.add(new FixedString(s, i, s.length()));
2567 break;
2568 }
2569 }
2570 return al;
2571 }
2572
2573 private static void checkText(String s, int start, int end) {
2574 for (int i = start; i < end; i++) {
2575 // Any '%' found in the region starts an invalid format specifier.
2576 if (s.charAt(i) == '%') {
2577 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
2578 throw new UnknownFormatConversionException(String.valueOf(c));
2579 }
2580 }
2581 }
2582
2583 private interface FormatString {
2584 int index();
2585 void print(Object arg, Locale l) throws IOException;
2586 String toString();
2587 }
2588
2589 private class FixedString implements FormatString {
2590 private String s;
2591 private int start;
2592 private int end;
2593 FixedString(String s, int start, int end) {
2594 this.s = s;
2595 this.start = start;
2596 this.end = end;
2597 }
2598 public int index() { return -2; }
2599 public void print(Object arg, Locale l)
2600 throws IOException { a.append(s, start, end); }
2601 public String toString() { return s.substring(start, end); }
2602 }
2603
2604 /**
2605 * Enum for {@code BigDecimal} formatting.
2606 */
2607 public enum BigDecimalLayoutForm {
2608 /**
2609 * Format the {@code BigDecimal} in computerized scientific notation.
2610 */
2611 SCIENTIFIC,
2612
2613 /**
2614 * Format the {@code BigDecimal} as a decimal number.
2615 */
2616 DECIMAL_FLOAT
2617 };
2618
2619 private class FormatSpecifier implements FormatString {
2620 private int index = -1;
2621 private Flags f = Flags.NONE;
2624 private boolean dt = false;
2625 private char c;
2626
2627 private int index(String s) {
2628 if (s != null) {
2629 try {
2630 index = Integer.parseInt(s.substring(0, s.length() - 1));
2631 } catch (NumberFormatException x) {
2632 assert(false);
2633 }
2634 } else {
2635 index = 0;
2636 }
2637 return index;
2638 }
2639
2640 public int index() {
2641 return index;
2642 }
2643
2644 private Flags flags(String s, int start, int end) {
2645 f = Flags.parse(s, start, end);
2646 if (f.contains(Flags.PREVIOUS))
2647 index = -1;
2648 return f;
2649 }
2650
2651 Flags flags() {
2652 return f;
2653 }
2654
2655 private int width(String s) {
2656 width = -1;
2657 if (s != null) {
2658 try {
2659 width = Integer.parseInt(s);
2660 if (width < 0)
2661 throw new IllegalFormatWidthException(width);
2662 } catch (NumberFormatException x) {
2663 assert(false);
2664 }
2665 }
2672
2673 private int precision(String s) {
2674 precision = -1;
2675 if (s != null) {
2676 try {
2677 // remove the '.'
2678 precision = Integer.parseInt(s.substring(1));
2679 if (precision < 0)
2680 throw new IllegalFormatPrecisionException(precision);
2681 } catch (NumberFormatException x) {
2682 assert(false);
2683 }
2684 }
2685 return precision;
2686 }
2687
2688 int precision() {
2689 return precision;
2690 }
2691
2692 private char conversion(char conv) {
2693 c = conv;
2694 if (!dt) {
2695 if (!Conversion.isValid(conv)) {
2696 throw new UnknownFormatConversionException(String.valueOf(c));
2697 }
2698 if (Character.isUpperCase(conv)) {
2699 f.add(Flags.UPPERCASE);
2700 c = Character.toLowerCase(conv);
2701 }
2702 if (Conversion.isText(conv)) {
2703 index = -2;
2704 }
2705 }
2706 return c;
2707 }
2708
2709 private char conversion() {
2710 return c;
2711 }
2712
2713 FormatSpecifier(String s, Matcher m) {
2714 int idx = 1;
2715
2716 index(m.group(idx++));
2717 flags(s, m.start(idx), m.end(idx++));
2718 width(m.group(idx++));
2719 precision(m.group(idx++));
2720
2721 int tTStart = m.start(idx);
2722 int tTEnd = m.end(idx++);
2723 if (tTStart != -1 && tTEnd != -1) {
2724 dt = true;
2725 if (tTStart == tTEnd - 1 && s.charAt(tTStart) == 'T') {
2726 f.add(Flags.UPPERCASE);
2727 }
2728 }
2729
2730 conversion(s.charAt(m.start(idx)));
2731
2732 if (dt)
2733 checkDateTime();
2734 else if (Conversion.isGeneral(c))
2735 checkGeneral();
2736 else if (Conversion.isCharacter(c))
2737 checkCharacter();
2738 else if (Conversion.isInteger(c))
2739 checkInteger();
2740 else if (Conversion.isFloat(c))
2741 checkFloat();
2742 else if (Conversion.isText(c))
2743 checkText();
2744 else
2745 throw new UnknownFormatConversionException(String.valueOf(c));
2746 }
2747
2748 public void print(Object arg, Locale l) throws IOException {
2749 if (dt) {
2750 printDateTime(arg, l);
2903 s = ((arg instanceof Boolean)
2904 ? ((Boolean)arg).toString()
2905 : Boolean.toString(true));
2906 else
2907 s = Boolean.toString(false);
2908 print(s);
2909 }
2910
2911 private void printHashCode(Object arg) throws IOException {
2912 String s = (arg == null
2913 ? "null"
2914 : Integer.toHexString(arg.hashCode()));
2915 print(s);
2916 }
2917
2918 private void print(String s) throws IOException {
2919 if (precision != -1 && precision < s.length())
2920 s = s.substring(0, precision);
2921 if (f.contains(Flags.UPPERCASE))
2922 s = s.toUpperCase();
2923 appendJustified(a, s);
2924 }
2925
2926 private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
2927 if (width == -1) {
2928 return a.append(cs);
2929 }
2930 boolean padLeft = f.contains(Flags.LEFT_JUSTIFY);
2931 int sp = width - cs.length();
2932 if (padLeft) {
2933 a.append(cs);
2934 }
2935 for (int i = 0; i < sp; i++) {
2936 a.append(' ');
2937 }
2938 if (!padLeft) {
2939 a.append(cs);
2940 }
2941 return a;
2942 }
2943
2944 public String toString() {
2945 StringBuilder sb = new StringBuilder("%");
2946 // Flags.UPPERCASE is set internally for legal conversions.
2947 Flags dupf = f.dup().remove(Flags.UPPERCASE);
2948 sb.append(dupf.toString());
2949 if (index > 0)
2950 sb.append(index).append('$');
2951 if (width != -1)
2952 sb.append(width);
2953 if (precision != -1)
2954 sb.append('.').append(precision);
2955 if (dt)
2956 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2957 sb.append(f.contains(Flags.UPPERCASE)
2958 ? Character.toUpperCase(c) : c);
2959 return sb.toString();
2960 }
2961
3086 print(v, l);
3087 }
3088
3089 private void print(int value, Locale l) throws IOException {
3090 long v = value;
3091 if (value < 0
3092 && (c == Conversion.OCTAL_INTEGER
3093 || c == Conversion.HEXADECIMAL_INTEGER)) {
3094 v += (1L << 32);
3095 assert v >= 0 : v;
3096 }
3097 print(v, l);
3098 }
3099
3100 private void print(long value, Locale l) throws IOException {
3101
3102 StringBuilder sb = new StringBuilder();
3103
3104 if (c == Conversion.DECIMAL_INTEGER) {
3105 boolean neg = value < 0;
3106 String valueStr;
3107 if (neg) {
3108 if (value == Long.MIN_VALUE) {
3109 valueStr = "9223372036854775808";
3110 } else {
3111 valueStr = Long.toString(-value, 10);
3112 }
3113 } else {
3114 valueStr = Long.toString(value, 10);
3115 }
3116
3117 // leading sign indicator
3118 leadingSign(sb, neg);
3119
3120 // the value
3121 localizedMagnitude(sb, valueStr, 0, f, adjustWidth(width, f, neg), l);
3122
3123 // trailing sign indicator
3124 trailingSign(sb, neg);
3125 } else if (c == Conversion.OCTAL_INTEGER) {
3126 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3127 Flags.PLUS);
3128 String s = Long.toOctalString(value);
3129 int len = (f.contains(Flags.ALTERNATE)
3130 ? s.length() + 1
3131 : s.length());
3132
3133 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3134 if (f.contains(Flags.ALTERNATE))
3135 sb.append('0');
3136 if (f.contains(Flags.ZERO_PAD)) {
3137 trailingZeros(sb, width - len);
3138 }
3139 sb.append(s);
3140 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3141 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3142 Flags.PLUS);
3143 String s = Long.toHexString(value);
3144 int len = (f.contains(Flags.ALTERNATE)
3145 ? s.length() + 2
3146 : s.length());
3147
3148 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3149 if (f.contains(Flags.ALTERNATE))
3150 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3151 if (f.contains(Flags.ZERO_PAD)) {
3152 trailingZeros(sb, width - len);
3153 }
3154 if (f.contains(Flags.UPPERCASE))
3155 s = s.toUpperCase();
3156 sb.append(s);
3157 }
3158
3159 // justify based on width
3160 appendJustified(a, sb);
3161 }
3162
3163 // neg := val < 0
3164 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3165 if (!neg) {
3166 if (f.contains(Flags.PLUS)) {
3167 sb.append('+');
3168 } else if (f.contains(Flags.LEADING_SPACE)) {
3169 sb.append(' ');
3170 }
3171 } else {
3172 if (f.contains(Flags.PARENTHESES))
3173 sb.append('(');
3174 else
3175 sb.append('-');
3176 }
3177 return sb;
3178 }
3179
3180 // neg := val < 0
3181 private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3182 if (neg && f.contains(Flags.PARENTHESES))
3183 sb.append(')');
3184 return sb;
3185 }
3186
3187 private void print(BigInteger value, Locale l) throws IOException {
3188 StringBuilder sb = new StringBuilder();
3189 boolean neg = value.signum() == -1;
3190 BigInteger v = value.abs();
3191
3192 // leading sign indicator
3193 leadingSign(sb, neg);
3194
3195 // the value
3196 if (c == Conversion.DECIMAL_INTEGER) {
3197 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);
3198 } else if (c == Conversion.OCTAL_INTEGER) {
3199 String s = v.toString(8);
3200
3201 int len = s.length() + sb.length();
3202 if (neg && f.contains(Flags.PARENTHESES))
3203 len++;
3204
3205 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3206 if (f.contains(Flags.ALTERNATE)) {
3207 len++;
3208 sb.append('0');
3209 }
3210 if (f.contains(Flags.ZERO_PAD)) {
3211 trailingZeros(sb, width - len);
3212 }
3213 sb.append(s);
3214 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3215 String s = v.toString(16);
3216
3217 int len = s.length() + sb.length();
3218 if (neg && f.contains(Flags.PARENTHESES))
3219 len++;
3220
3221 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3222 if (f.contains(Flags.ALTERNATE)) {
3223 len += 2;
3224 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3225 }
3226 if (f.contains(Flags.ZERO_PAD)) {
3227 trailingZeros(sb, width - len);
3228 }
3229 if (f.contains(Flags.UPPERCASE))
3230 s = s.toUpperCase();
3231 sb.append(s);
3232 }
3233
3234 // trailing sign indicator
3235 trailingSign(sb, (value.signum() == -1));
3236
3237 // justify based on width
3238 appendJustified(a, sb);
3239 }
3240
3241 private void print(float value, Locale l) throws IOException {
3242 print((double) value, l);
3243 }
3244
3245 private void print(double value, Locale l) throws IOException {
3246 StringBuilder sb = new StringBuilder();
3247 boolean neg = Double.compare(value, 0.0) == -1;
3248
3249 if (!Double.isNaN(value)) {
3250 double v = Math.abs(value);
3251
3252 // leading sign indicator
3253 leadingSign(sb, neg);
3254
3255 // the value
3256 if (!Double.isInfinite(v))
3257 print(sb, v, l, f, c, precision, neg);
3258 else
3259 sb.append(f.contains(Flags.UPPERCASE)
3260 ? "INFINITY" : "Infinity");
3261
3262 // trailing sign indicator
3263 trailingSign(sb, neg);
3264 } else {
3265 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3266 }
3267
3268 // justify based on width
3269 appendJustified(a, sb);
3270 }
3271
3272 // !Double.isInfinite(value) && !Double.isNaN(value)
3273 private void print(StringBuilder sb, double value, Locale l,
3274 Flags f, char c, int precision, boolean neg)
3275 throws IOException
3276 {
3277 if (c == Conversion.SCIENTIFIC) {
3278 // Create a new FormattedFloatingDecimal with the desired
3279 // precision.
3280 int prec = (precision == -1 ? 6 : precision);
3281
3282 FormattedFloatingDecimal fd
3283 = FormattedFloatingDecimal.valueOf(value, prec,
3284 FormattedFloatingDecimal.Form.SCIENTIFIC);
3285
3286 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3287 addZeros(mant, prec);
3288
3289 // If the precision is zero and the '#' flag is set, add the
3290 // requested decimal point.
3291 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3292 mant.append('.');
3293 }
3294
3295 StringBuilder exp = new StringBuilder();
3296 if (value == 0.0) {
3297 exp.append("+00");
3298 } else {
3299 exp.append(fd.getExponent());
3300 }
3301
3302 int newW = width;
3303 if (width != -1)
3304 newW = adjustWidth(width - exp.length() - 1, f, neg);
3305 localizedMagnitude(sb, mant, 0, f, newW, l);
3306
3307 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3308
3309 Flags flags = f.dup().remove(Flags.GROUP);
3310 char sign = exp.charAt(0);
3311 assert(sign == '+' || sign == '-');
3312 sb.append(sign);
3313
3314 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3315 } else if (c == Conversion.DECIMAL_FLOAT) {
3316 // Create a new FormattedFloatingDecimal with the desired
3317 // precision.
3318 int prec = (precision == -1 ? 6 : precision);
3319
3320 FormattedFloatingDecimal fd
3321 = FormattedFloatingDecimal.valueOf(value, prec,
3322 FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3323
3324 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3325 addZeros(mant, prec);
3326
3327 // If the precision is zero and the '#' flag is set, add the
3328 // requested decimal point.
3329 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3330 mant.append('.');
3331
3332 int newW = width;
3333 if (width != -1)
3334 newW = adjustWidth(width, f, neg);
3335 localizedMagnitude(sb, mant, 0, f, newW, l);
3336 } else if (c == Conversion.GENERAL) {
3337 int prec = precision;
3338 if (precision == -1)
3339 prec = 6;
3340 else if (precision == 0)
3341 prec = 1;
3342
3343 StringBuilder exp;
3344 StringBuilder mant = new StringBuilder();
3345 int expRounded;
3346 if (value == 0.0) {
3347 exp = null;
3348 mant.append('0');
3349 expRounded = 0;
3350 } else {
3351 FormattedFloatingDecimal fd
3352 = FormattedFloatingDecimal.valueOf(value, prec,
3353 FormattedFloatingDecimal.Form.GENERAL);
3354 char[] expArray = fd.getExponent();
3355 if (expArray != null) {
3356 exp = new StringBuilder().append(expArray);
3357 } else {
3358 exp = null;
3359 }
3360 mant.append(fd.getMantissa());
3361 expRounded = fd.getExponentRounded();
3362 }
3363
3364 if (exp != null) {
3365 prec -= 1;
3366 } else {
3367 prec -= expRounded + 1;
3368 }
3369
3370 addZeros(mant, prec);
3371 // If the precision is zero and the '#' flag is set, add the
3372 // requested decimal point.
3373 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3374 mant.append('.');
3375 }
3376
3377 int newW = width;
3378 if (width != -1) {
3379 if (exp != null)
3380 newW = adjustWidth(width - exp.length() - 1, f, neg);
3381 else
3382 newW = adjustWidth(width, f, neg);
3383 }
3384 localizedMagnitude(sb, mant, 0, f, newW, l);
3385
3386 if (exp != null) {
3387 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3388
3389 Flags flags = f.dup().remove(Flags.GROUP);
3390 char sign = exp.charAt(0);
3391 assert(sign == '+' || sign == '-');
3392 sb.append(sign);
3393
3394 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3395 }
3396 } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3397 int prec = precision;
3398 if (precision == -1)
3399 // assume that we want all of the digits
3400 prec = 0;
3401 else if (precision == 0)
3402 prec = 1;
3403
3404 String s = hexDouble(value, prec);
3405
3406 StringBuilder va = new StringBuilder();
3407 boolean upper = f.contains(Flags.UPPERCASE);
3408 sb.append(upper ? "0X" : "0x");
3409
3410 if (f.contains(Flags.ZERO_PAD)) {
3411 trailingZeros(sb, width - s.length() - 2);
3412 }
3413
3414 int idx = s.indexOf('p');
3415 if (upper) {
3416 String tmp = s.substring(0, idx);
3417 // don't localize hex
3418 tmp = tmp.toUpperCase(Locale.US);
3419 va.append(tmp);
3420 } else {
3421 va.append(s, 0, idx);
3422 }
3423 if (prec != 0) {
3424 addZeros(va, prec);
3425 }
3426 sb.append(va);
3427 sb.append(upper ? 'P' : 'p');
3428 sb.append(s, idx+1, s.length());
3429 }
3430 }
3431
3432 // Add zeros to the requested precision.
3433 private void addZeros(StringBuilder sb, int prec) {
3434 // Look for the dot. If we don't find one, the we'll need to add
3435 // it before we add the zeros.
3436 int len = sb.length();
3437 int i;
3438 for (i = 0; i < len; i++) {
3439 if (sb.charAt(i) == '.') {
3440 break;
3441 }
3442 }
3443 boolean needDot = false;
3444 if (i == len) {
3445 needDot = true;
3446 }
3447
3448 // Determine existing precision.
3449 int outPrec = len - i - (needDot ? 0 : 1);
3450 assert (outPrec <= prec);
3451 if (outPrec == prec) {
3452 return;
3453 }
3454
3455 // Add dot if previously determined to be necessary.
3456 if (needDot) {
3457 sb.append('.');
3458 }
3459
3460 // Add zeros.
3461 trailingZeros(sb, prec - outPrec);
3462 }
3463
3464 // Method assumes that d > 0.
3465 private String hexDouble(double d, int prec) {
3466 // Let Double.toHexString handle simple cases
3467 if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3468 // remove "0x"
3469 return Double.toHexString(d).substring(2);
3470 } else {
3471 assert(prec >= 1 && prec <= 12);
3472
3473 int exponent = Math.getExponent(d);
3474 boolean subnormal
3475 = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3476
3477 // If this is subnormal input so normalize (could be faster to
3478 // do as integer operation).
3479 if (subnormal) {
3480 scaleUp = Math.scalb(1.0, 54);
3481 d *= scaleUp;
3482 // Calculate the exponent. This is not just exponent + 54
3483 // since the former is not the normalized exponent.
3484 exponent = Math.getExponent(d);
3485 assert exponent >= DoubleConsts.MIN_EXPONENT &&
3486 exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3487 }
3488
3489 int precision = 1 + prec*4;
3490 int shiftDistance
3543 }
3544 }
3545 }
3546
3547 private void print(BigDecimal value, Locale l) throws IOException {
3548 if (c == Conversion.HEXADECIMAL_FLOAT)
3549 failConversion(c, value);
3550 StringBuilder sb = new StringBuilder();
3551 boolean neg = value.signum() == -1;
3552 BigDecimal v = value.abs();
3553 // leading sign indicator
3554 leadingSign(sb, neg);
3555
3556 // the value
3557 print(sb, v, l, f, c, precision, neg);
3558
3559 // trailing sign indicator
3560 trailingSign(sb, neg);
3561
3562 // justify based on width
3563 appendJustified(a, sb);
3564 }
3565
3566 // value > 0
3567 private void print(StringBuilder sb, BigDecimal value, Locale l,
3568 Flags f, char c, int precision, boolean neg)
3569 throws IOException
3570 {
3571 if (c == Conversion.SCIENTIFIC) {
3572 // Create a new BigDecimal with the desired precision.
3573 int prec = (precision == -1 ? 6 : precision);
3574 int scale = value.scale();
3575 int origPrec = value.precision();
3576 int nzeros = 0;
3577 int compPrec;
3578
3579 if (prec > origPrec - 1) {
3580 compPrec = origPrec;
3581 nzeros = prec - (origPrec - 1);
3582 } else {
3583 compPrec = prec + 1;
3584 }
3585
3586 MathContext mc = new MathContext(compPrec);
3587 BigDecimal v
3588 = new BigDecimal(value.unscaledValue(), scale, mc);
3589
3590 BigDecimalLayout bdl
3591 = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3592 BigDecimalLayoutForm.SCIENTIFIC);
3593
3594 StringBuilder mant = bdl.mantissa();
3595
3596 // Add a decimal point if necessary. The mantissa may not
3597 // contain a decimal point if the scale is zero (the internal
3598 // representation has no fractional part) or the original
3599 // precision is one. Append a decimal point if '#' is set or if
3600 // we require zero padding to get to the requested precision.
3601 if ((origPrec == 1 || !bdl.hasDot())
3602 && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3603 mant.append('.');
3604 }
3605
3606 // Add trailing zeros in the case precision is greater than
3607 // the number of available digits after the decimal separator.
3608 trailingZeros(mant, nzeros);
3609
3610 StringBuilder exp = bdl.exponent();
3611 int newW = width;
3612 if (width != -1) {
3613 newW = adjustWidth(width - exp.length() - 1, f, neg);
3614 }
3615 localizedMagnitude(sb, mant, 0, f, newW, l);
3616
3617 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3618
3619 Flags flags = f.dup().remove(Flags.GROUP);
3620 char sign = exp.charAt(0);
3621 assert(sign == '+' || sign == '-');
3622 sb.append(sign);
3623
3624 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3625 } else if (c == Conversion.DECIMAL_FLOAT) {
3626 // Create a new BigDecimal with the desired precision.
3627 int prec = (precision == -1 ? 6 : precision);
3628 int scale = value.scale();
3629
3630 if (scale > prec) {
3631 // more "scale" digits than the requested "precision"
3632 int compPrec = value.precision();
3633 if (compPrec <= scale) {
3634 // case of 0.xxxxxx
3635 value = value.setScale(prec, RoundingMode.HALF_UP);
3636 } else {
3637 compPrec -= (scale - prec);
3638 value = new BigDecimal(value.unscaledValue(),
3639 scale,
3640 new MathContext(compPrec));
3641 }
3642 }
3643 BigDecimalLayout bdl = new BigDecimalLayout(
3644 value.unscaledValue(),
3645 value.scale(),
3646 BigDecimalLayoutForm.DECIMAL_FLOAT);
3647
3648 StringBuilder mant = bdl.mantissa();
3649 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3650
3651 // Add a decimal point if necessary. The mantissa may not
3652 // contain a decimal point if the scale is zero (the internal
3653 // representation has no fractional part). Append a decimal
3654 // point if '#' is set or we require zero padding to get to the
3655 // requested precision.
3656 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3657 || nzeros > 0)) {
3658 mant.append('.');
3659 }
3660
3661 // Add trailing zeros if the precision is greater than the
3662 // number of available digits after the decimal separator.
3663 trailingZeros(mant, nzeros);
3664
3665 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3666 } else if (c == Conversion.GENERAL) {
3667 int prec = precision;
3668 if (precision == -1)
3669 prec = 6;
3670 else if (precision == 0)
3671 prec = 1;
3672
3673 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3674 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3675 if ((value.equals(BigDecimal.ZERO))
3676 || ((value.compareTo(tenToTheNegFour) != -1)
3677 && (value.compareTo(tenToThePrec) == -1))) {
3678
3679 int e = - value.scale()
3680 + (value.unscaledValue().toString().length() - 1);
3681
3682 // xxx.yyy
3683 // g precision (# sig digits) = #x + #y
3684 // f precision = #y
3685 // exponent = #x - 1
3704 }
3705
3706 private class BigDecimalLayout {
3707 private StringBuilder mant;
3708 private StringBuilder exp;
3709 private boolean dot = false;
3710 private int scale;
3711
3712 public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3713 layout(intVal, scale, form);
3714 }
3715
3716 public boolean hasDot() {
3717 return dot;
3718 }
3719
3720 public int scale() {
3721 return scale;
3722 }
3723
3724 public StringBuilder mantissa() {
3725 return mant;
3726 }
3727
3728 // The exponent will be formatted as a sign ('+' or '-') followed
3729 // by the exponent zero-padded to include at least two digits.
3730 public StringBuilder exponent() {
3731 return exp;
3732 }
3733
3734 private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3735 String coeff = intVal.toString();
3736 this.scale = scale;
3737
3738 // Construct a buffer, with sufficient capacity for all cases.
3739 // If E-notation is needed, length will be: +1 if negative, +1
3740 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3741 // exponent. Otherwise it could have +1 if negative, plus
3742 // leading "0.00000"
3743 int len = coeff.length();
3744 mant = new StringBuilder(len + 14);
3745
3746 if (scale == 0) {
3747 if (len > 1) {
3748 mant.append(coeff.charAt(0));
3749 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3750 mant.append('.');
3751 dot = true;
3752 mant.append(coeff, 1, len);
3753 exp = new StringBuilder("+");
3754 if (len < 10) {
3755 exp.append('0').append(len - 1);
3756 } else {
3757 exp.append(len - 1);
3758 }
3759 } else {
3760 mant.append(coeff, 1, len);
3761 }
3762 } else {
3763 mant.append(coeff);
3764 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3765 exp = new StringBuilder("+00");
3766 }
3767 }
3768 return;
3769 }
3770 long adjusted = -(long) scale + (len - 1);
3771 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3772 // count of padding zeros
3773 int pad = scale - len;
3774 if (pad >= 0) {
3775 // 0.xxx form
3776 mant.append("0.");
3777 dot = true;
3778 trailingZeros(mant, pad);
3779 mant.append(coeff);
3780 } else {
3781 if (-pad < len) {
3782 // xx.xx form
3783 mant.append(coeff, 0, -pad);
3784 mant.append('.');
3785 dot = true;
3786 mant.append(coeff, -pad, -pad + scale);
3787 } else {
3788 // xx form
3789 mant.append(coeff, 0, len);
3790 trailingZeros(mant, -scale);
3791 this.scale = 0;
3792 }
3793 }
3794 } else {
3795 // x.xxx form
3796 mant.append(coeff.charAt(0));
3797 if (len > 1) {
3798 mant.append('.');
3799 dot = true;
3800 mant.append(coeff, 1, len);
3801 }
3802 exp = new StringBuilder();
3803 if (adjusted != 0) {
3804 long abs = Math.abs(adjusted);
3805 // require sign
3806 exp.append(adjusted < 0 ? '-' : '+');
3807 if (abs < 10) {
3808 exp.append('0');
3809 }
3810 exp.append(abs);
3811 } else {
3812 exp.append("+00");
3813 }
3814 }
3815 }
3816 }
3817
3818 private int adjustWidth(int width, Flags f, boolean neg) {
3819 int newW = width;
3820 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3821 newW--;
3822 return newW;
3823 }
3824
3825 // Add trailing zeros
3826 private void trailingZeros(StringBuilder sb, int nzeros) {
3827 for (int i = 0; i < nzeros; i++) {
3828 sb.append('0');
3829 }
3830 }
3831
3832 private void print(Calendar t, char c, Locale l) throws IOException {
3833 StringBuilder sb = new StringBuilder();
3834 print(sb, t, c, l);
3835
3836 // justify based on width
3837 if (f.contains(Flags.UPPERCASE)) {
3838 appendJustified(a, sb.toString().toUpperCase());
3839 } else {
3840 appendJustified(a, sb);
3841 }
3842 }
3843
3844 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3845 throws IOException {
3846 if (sb == null)
3847 sb = new StringBuilder();
3848 switch (c) {
3849 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3850 case DateTime.HOUR_0: // 'I' (01 - 12)
3851 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3852 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3853 int i = t.get(Calendar.HOUR_OF_DAY);
3854 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3855 i = (i == 0 || i == 12 ? 12 : i % 12);
3856 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3857 || c == DateTime.HOUR_0
3858 ? Flags.ZERO_PAD
3859 : Flags.NONE);
3860 sb.append(localizedMagnitude(null, i, flags, 2, l));
3861 break;
3862 }
3863 case DateTime.MINUTE: { // 'M' (00 - 59)
3864 int i = t.get(Calendar.MINUTE);
3865 Flags flags = Flags.ZERO_PAD;
3998 // Composites
3999 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4000 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
4001 char sep = ':';
4002 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4003 print(sb, t, DateTime.MINUTE, l);
4004 if (c == DateTime.TIME) {
4005 sb.append(sep);
4006 print(sb, t, DateTime.SECOND, l);
4007 }
4008 break;
4009 }
4010 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
4011 char sep = ':';
4012 print(sb, t, DateTime.HOUR_0, l).append(sep);
4013 print(sb, t, DateTime.MINUTE, l).append(sep);
4014 print(sb, t, DateTime.SECOND, l).append(' ');
4015 // this may be in wrong place for some locales
4016 StringBuilder tsb = new StringBuilder();
4017 print(tsb, t, DateTime.AM_PM, l);
4018
4019 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4020 break;
4021 }
4022 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4023 char sep = ' ';
4024 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4025 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4026 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4027 print(sb, t, DateTime.TIME, l).append(sep);
4028 print(sb, t, DateTime.ZONE, l).append(sep);
4029 print(sb, t, DateTime.YEAR_4, l);
4030 break;
4031 }
4032 case DateTime.DATE: { // 'D' (mm/dd/yy)
4033 char sep = '/';
4034 print(sb, t, DateTime.MONTH, l).append(sep);
4035 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4036 print(sb, t, DateTime.YEAR_2, l);
4037 break;
4038 }
4039 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4040 char sep = '-';
4041 print(sb, t, DateTime.YEAR_4, l).append(sep);
4042 print(sb, t, DateTime.MONTH, l).append(sep);
4043 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4044 break;
4045 }
4046 default:
4047 assert false;
4048 }
4049 return sb;
4050 }
4051
4052 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4053 StringBuilder sb = new StringBuilder();
4054 print(sb, t, c, l);
4055 // justify based on width
4056 if (f.contains(Flags.UPPERCASE)) {
4057 appendJustified(a, sb.toString().toUpperCase());
4058 } else {
4059 appendJustified(a, sb);
4060 }
4061 }
4062
4063 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4064 Locale l) throws IOException {
4065 if (sb == null)
4066 sb = new StringBuilder();
4067 try {
4068 switch (c) {
4069 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4070 int i = t.get(ChronoField.HOUR_OF_DAY);
4071 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4072 break;
4073 }
4074 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4075 int i = t.get(ChronoField.HOUR_OF_DAY);
4076 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4077 break;
4078 }
4079 case DateTime.HOUR_0: { // 'I' (01 - 12)
4080 int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4288
4289 // -- Methods to support throwing exceptions --
4290
4291 private void failMismatch(Flags f, char c) {
4292 String fs = f.toString();
4293 throw new FormatFlagsConversionMismatchException(fs, c);
4294 }
4295
4296 private void failConversion(char c, Object arg) {
4297 throw new IllegalFormatConversionException(c, arg.getClass());
4298 }
4299
4300 private char getZero(Locale l) {
4301 if ((l != null) && !l.equals(locale())) {
4302 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4303 return dfs.getZeroDigit();
4304 }
4305 return zero;
4306 }
4307
4308 private StringBuilder localizedMagnitude(StringBuilder sb,
4309 long value, Flags f, int width, Locale l) {
4310 return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);
4311 }
4312
4313 private StringBuilder localizedMagnitude(StringBuilder sb,
4314 CharSequence value, final int offset, Flags f, int width,
4315 Locale l) {
4316 if (sb == null) {
4317 sb = new StringBuilder();
4318 }
4319 int begin = sb.length();
4320
4321 char zero = getZero(l);
4322
4323 // determine localized grouping separator and size
4324 char grpSep = '\0';
4325 int grpSize = -1;
4326 char decSep = '\0';
4327
4328 int len = value.length();
4329 int dot = len;
4330 for (int j = offset; j < len; j++) {
4331 if (value.charAt(j) == '.') {
4332 dot = j;
4333 break;
4334 }
4335 }
4336
4337 if (dot < len) {
4338 if (l == null || l.equals(Locale.US)) {
4339 decSep = '.';
4340 } else {
4341 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4342 decSep = dfs.getDecimalSeparator();
4343 }
4344 }
4345
4346 if (f.contains(Flags.GROUP)) {
4347 if (l == null || l.equals(Locale.US)) {
4348 grpSep = ',';
4349 grpSize = 3;
4350 } else {
4351 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4352 grpSep = dfs.getGroupingSeparator();
4353 DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4354 grpSize = df.getGroupingSize();
4355 }
4356 }
4357
4358 // localize the digits inserting group separators as necessary
4359 for (int j = offset; j < len; j++) {
4360 if (j == dot) {
4361 sb.append(decSep);
4362 // no more group separators after the decimal separator
4363 grpSep = '\0';
4364 continue;
4365 }
4366
4367 char c = value.charAt(j);
4368 sb.append((char) ((c - '0') + zero));
4369 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4370 sb.append(grpSep);
4371 }
4372 }
4373
4374 // apply zero padding
4375 if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4376 sb.ensureCapacity(width);
4377 for (int k = sb.length(); k < width; k++) {
4378 sb.insert(begin, zero);
4379 }
4380 }
4381
4382 return sb;
4383 }
4384 }
4385
4386 private static class Flags {
4387 private int flags;
4388
4389 static final Flags NONE = new Flags(0); // ''
4390
4391 // duplicate declarations from Formattable.java
4392 static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'
4393 static final Flags UPPERCASE = new Flags(1<<1); // '^'
4394 static final Flags ALTERNATE = new Flags(1<<2); // '#'
4395
4396 // numerics
4397 static final Flags PLUS = new Flags(1<<3); // '+'
4398 static final Flags LEADING_SPACE = new Flags(1<<4); // ' '
4399 static final Flags ZERO_PAD = new Flags(1<<5); // '0'
4400 static final Flags GROUP = new Flags(1<<6); // ','
4412 }
4413
4414 public boolean contains(Flags f) {
4415 return (flags & f.valueOf()) == f.valueOf();
4416 }
4417
4418 public Flags dup() {
4419 return new Flags(flags);
4420 }
4421
4422 private Flags add(Flags f) {
4423 flags |= f.valueOf();
4424 return this;
4425 }
4426
4427 public Flags remove(Flags f) {
4428 flags &= ~f.valueOf();
4429 return this;
4430 }
4431
4432 public static Flags parse(String s, int start, int end) {
4433 Flags f = new Flags(0);
4434 for (int i = start; i < end; i++) {
4435 char c = s.charAt(i);
4436 Flags v = parse(c);
4437 if (f.contains(v))
4438 throw new DuplicateFormatFlagsException(v.toString());
4439 f.add(v);
4440 }
4441 return f;
4442 }
4443
4444 // parse those flags which may be provided by users
4445 private static Flags parse(char c) {
4446 switch (c) {
4447 case '-': return LEFT_JUSTIFY;
4448 case '#': return ALTERNATE;
4449 case '+': return PLUS;
4450 case ' ': return LEADING_SPACE;
4451 case '0': return ZERO_PAD;
4452 case ',': return GROUP;
4453 case '(': return PARENTHESES;
4454 case '<': return PREVIOUS;
4455 default:
|