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 }
2660 return width;
2661 }
2662
2663 int width() {
2664 return width;
2665 }
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); // ','
4405 static final Flags PARENTHESES = new Flags(1<<7); // '('
4406
4407 // indexing
4408 static final Flags PREVIOUS = new Flags(1<<8); // '<'
4409
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 private int width(String s) {
2652 width = -1;
2653 if (s != null) {
2654 try {
2655 width = Integer.parseInt(s);
2656 if (width < 0)
2657 throw new IllegalFormatWidthException(width);
2658 } catch (NumberFormatException x) {
2659 assert(false);
2660 }
2661 }
2662 return width;
2663 }
2664
2665 private int precision(String s) {
2666 precision = -1;
2667 if (s != null) {
2668 try {
2669 // remove the '.'
2670 precision = Integer.parseInt(s.substring(1));
2671 if (precision < 0)
2672 throw new IllegalFormatPrecisionException(precision);
2673 } catch (NumberFormatException x) {
2674 assert(false);
2675 }
2676 }
2677 return precision;
2678 }
2679
2680 private char conversion(char conv) {
2681 c = conv;
2682 if (!dt) {
2683 if (!Conversion.isValid(c)) {
2684 throw new UnknownFormatConversionException(String.valueOf(c));
2685 }
2686 if (Character.isUpperCase(c)) {
2687 f.add(Flags.UPPERCASE);
2688 c = Character.toLowerCase(c);
2689 }
2690 if (Conversion.isText(c)) {
2691 index = -2;
2692 }
2693 }
2694 return c;
2695 }
2696
2697 FormatSpecifier(String s, Matcher m) {
2698 int idx = 1;
2699
2700 index(m.group(idx++));
2701 flags(s, m.start(idx), m.end(idx++));
2702 width(m.group(idx++));
2703 precision(m.group(idx++));
2704
2705 int tTStart = m.start(idx);
2706 int tTEnd = m.end(idx++);
2707 if (tTStart != -1 && tTEnd != -1) {
2708 dt = true;
2709 if (tTStart == tTEnd - 1 && s.charAt(tTStart) == 'T') {
2710 f.add(Flags.UPPERCASE);
2711 }
2712 }
2713
2714 conversion(s.charAt(m.start(idx)));
2715
2716 if (dt)
2717 checkDateTime();
2718 else if (Conversion.isGeneral(c))
2719 checkGeneral();
2720 else if (Conversion.isCharacter(c))
2721 checkCharacter();
2722 else if (Conversion.isInteger(c))
2723 checkInteger();
2724 else if (Conversion.isFloat(c))
2725 checkFloat();
2726 else if (Conversion.isText(c))
2727 checkText();
2728 else
2729 throw new UnknownFormatConversionException(String.valueOf(c));
2730 }
2731
2732 public void print(Object arg, Locale l) throws IOException {
2733 if (dt) {
2734 printDateTime(arg, l);
2887 s = ((arg instanceof Boolean)
2888 ? ((Boolean)arg).toString()
2889 : Boolean.toString(true));
2890 else
2891 s = Boolean.toString(false);
2892 print(s);
2893 }
2894
2895 private void printHashCode(Object arg) throws IOException {
2896 String s = (arg == null
2897 ? "null"
2898 : Integer.toHexString(arg.hashCode()));
2899 print(s);
2900 }
2901
2902 private void print(String s) throws IOException {
2903 if (precision != -1 && precision < s.length())
2904 s = s.substring(0, precision);
2905 if (f.contains(Flags.UPPERCASE))
2906 s = s.toUpperCase();
2907 appendJustified(a, s);
2908 }
2909
2910 private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
2911 if (width == -1) {
2912 return a.append(cs);
2913 }
2914 boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
2915 int sp = width - cs.length();
2916 if (padRight) {
2917 a.append(cs);
2918 }
2919 for (int i = 0; i < sp; i++) {
2920 a.append(' ');
2921 }
2922 if (!padRight) {
2923 a.append(cs);
2924 }
2925 return a;
2926 }
2927
2928 public String toString() {
2929 StringBuilder sb = new StringBuilder("%");
2930 // Flags.UPPERCASE is set internally for legal conversions.
2931 Flags dupf = f.dup().remove(Flags.UPPERCASE);
2932 sb.append(dupf.toString());
2933 if (index > 0)
2934 sb.append(index).append('$');
2935 if (width != -1)
2936 sb.append(width);
2937 if (precision != -1)
2938 sb.append('.').append(precision);
2939 if (dt)
2940 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2941 sb.append(f.contains(Flags.UPPERCASE)
2942 ? Character.toUpperCase(c) : c);
2943 return sb.toString();
2944 }
2945
3070 print(v, l);
3071 }
3072
3073 private void print(int value, Locale l) throws IOException {
3074 long v = value;
3075 if (value < 0
3076 && (c == Conversion.OCTAL_INTEGER
3077 || c == Conversion.HEXADECIMAL_INTEGER)) {
3078 v += (1L << 32);
3079 assert v >= 0 : v;
3080 }
3081 print(v, l);
3082 }
3083
3084 private void print(long value, Locale l) throws IOException {
3085
3086 StringBuilder sb = new StringBuilder();
3087
3088 if (c == Conversion.DECIMAL_INTEGER) {
3089 boolean neg = value < 0;
3090 String valueStr = Long.toString(value, 10);
3091
3092 // leading sign indicator
3093 leadingSign(sb, neg);
3094
3095 // the value
3096 localizedMagnitude(sb, valueStr, neg ? 1 : 0, f, adjustWidth(width, f, neg), l);
3097
3098 // trailing sign indicator
3099 trailingSign(sb, neg);
3100 } else if (c == Conversion.OCTAL_INTEGER) {
3101 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3102 Flags.PLUS);
3103 String s = Long.toOctalString(value);
3104 int len = (f.contains(Flags.ALTERNATE)
3105 ? s.length() + 1
3106 : s.length());
3107
3108 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3109 if (f.contains(Flags.ALTERNATE))
3110 sb.append('0');
3111 if (f.contains(Flags.ZERO_PAD)) {
3112 trailingZeros(sb, width - len);
3113 }
3114 sb.append(s);
3115 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3116 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3117 Flags.PLUS);
3118 String s = Long.toHexString(value);
3119 int len = (f.contains(Flags.ALTERNATE)
3120 ? s.length() + 2
3121 : s.length());
3122
3123 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3124 if (f.contains(Flags.ALTERNATE))
3125 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3126 if (f.contains(Flags.ZERO_PAD)) {
3127 trailingZeros(sb, width - len);
3128 }
3129 if (f.contains(Flags.UPPERCASE))
3130 s = s.toUpperCase();
3131 sb.append(s);
3132 }
3133
3134 // justify based on width
3135 appendJustified(a, sb);
3136 }
3137
3138 // neg := val < 0
3139 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3140 if (!neg) {
3141 if (f.contains(Flags.PLUS)) {
3142 sb.append('+');
3143 } else if (f.contains(Flags.LEADING_SPACE)) {
3144 sb.append(' ');
3145 }
3146 } else {
3147 if (f.contains(Flags.PARENTHESES))
3148 sb.append('(');
3149 else
3150 sb.append('-');
3151 }
3152 return sb;
3153 }
3154
3155 // neg := val < 0
3156 private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3157 if (neg && f.contains(Flags.PARENTHESES))
3158 sb.append(')');
3159 return sb;
3160 }
3161
3162 private void print(BigInteger value, Locale l) throws IOException {
3163 StringBuilder sb = new StringBuilder();
3164 boolean neg = value.signum() == -1;
3165 BigInteger v = value.abs();
3166
3167 // leading sign indicator
3168 leadingSign(sb, neg);
3169
3170 // the value
3171 if (c == Conversion.DECIMAL_INTEGER) {
3172 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);
3173 } else if (c == Conversion.OCTAL_INTEGER) {
3174 String s = v.toString(8);
3175
3176 int len = s.length() + sb.length();
3177 if (neg && f.contains(Flags.PARENTHESES))
3178 len++;
3179
3180 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3181 if (f.contains(Flags.ALTERNATE)) {
3182 len++;
3183 sb.append('0');
3184 }
3185 if (f.contains(Flags.ZERO_PAD)) {
3186 trailingZeros(sb, width - len);
3187 }
3188 sb.append(s);
3189 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3190 String s = v.toString(16);
3191
3192 int len = s.length() + sb.length();
3193 if (neg && f.contains(Flags.PARENTHESES))
3194 len++;
3195
3196 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3197 if (f.contains(Flags.ALTERNATE)) {
3198 len += 2;
3199 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3200 }
3201 if (f.contains(Flags.ZERO_PAD)) {
3202 trailingZeros(sb, width - len);
3203 }
3204 if (f.contains(Flags.UPPERCASE))
3205 s = s.toUpperCase();
3206 sb.append(s);
3207 }
3208
3209 // trailing sign indicator
3210 trailingSign(sb, (value.signum() == -1));
3211
3212 // justify based on width
3213 appendJustified(a, sb);
3214 }
3215
3216 private void print(float value, Locale l) throws IOException {
3217 print((double) value, l);
3218 }
3219
3220 private void print(double value, Locale l) throws IOException {
3221 StringBuilder sb = new StringBuilder();
3222 boolean neg = Double.compare(value, 0.0) == -1;
3223
3224 if (!Double.isNaN(value)) {
3225 double v = Math.abs(value);
3226
3227 // leading sign indicator
3228 leadingSign(sb, neg);
3229
3230 // the value
3231 if (!Double.isInfinite(v))
3232 print(sb, v, l, f, c, precision, neg);
3233 else
3234 sb.append(f.contains(Flags.UPPERCASE)
3235 ? "INFINITY" : "Infinity");
3236
3237 // trailing sign indicator
3238 trailingSign(sb, neg);
3239 } else {
3240 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3241 }
3242
3243 // justify based on width
3244 appendJustified(a, sb);
3245 }
3246
3247 // !Double.isInfinite(value) && !Double.isNaN(value)
3248 private void print(StringBuilder sb, double value, Locale l,
3249 Flags f, char c, int precision, boolean neg)
3250 throws IOException
3251 {
3252 if (c == Conversion.SCIENTIFIC) {
3253 // Create a new FormattedFloatingDecimal with the desired
3254 // precision.
3255 int prec = (precision == -1 ? 6 : precision);
3256
3257 FormattedFloatingDecimal fd
3258 = FormattedFloatingDecimal.valueOf(value, prec,
3259 FormattedFloatingDecimal.Form.SCIENTIFIC);
3260
3261 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3262 addZeros(mant, prec);
3263
3264 // If the precision is zero and the '#' flag is set, add the
3265 // requested decimal point.
3266 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3267 mant.append('.');
3268 }
3269
3270 StringBuilder exp = new StringBuilder();
3271 if (value == 0.0) {
3272 exp.append("+00");
3273 } else {
3274 exp.append(fd.getExponent());
3275 }
3276
3277 int newW = width;
3278 if (width != -1)
3279 newW = adjustWidth(width - exp.length() - 1, f, neg);
3280 localizedMagnitude(sb, mant, 0, f, newW, l);
3281
3282 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3283
3284 Flags flags = f.dup().remove(Flags.GROUP);
3285 char sign = exp.charAt(0);
3286 assert(sign == '+' || sign == '-');
3287 sb.append(sign);
3288
3289 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3290 } else if (c == Conversion.DECIMAL_FLOAT) {
3291 // Create a new FormattedFloatingDecimal with the desired
3292 // precision.
3293 int prec = (precision == -1 ? 6 : precision);
3294
3295 FormattedFloatingDecimal fd
3296 = FormattedFloatingDecimal.valueOf(value, prec,
3297 FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3298
3299 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3300 addZeros(mant, 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.append('.');
3306
3307 int newW = width;
3308 if (width != -1)
3309 newW = adjustWidth(width, f, neg);
3310 localizedMagnitude(sb, mant, 0, 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 StringBuilder mant = new StringBuilder();
3320 int expRounded;
3321 if (value == 0.0) {
3322 exp = null;
3323 mant.append('0');
3324 expRounded = 0;
3325 } else {
3326 FormattedFloatingDecimal fd
3327 = FormattedFloatingDecimal.valueOf(value, prec,
3328 FormattedFloatingDecimal.Form.GENERAL);
3329 exp = fd.getExponent();
3330 mant.append(fd.getMantissa());
3331 expRounded = fd.getExponentRounded();
3332 }
3333
3334 if (exp != null) {
3335 prec -= 1;
3336 } else {
3337 prec -= expRounded + 1;
3338 }
3339
3340 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.append('.');
3345 }
3346
3347 int newW = width;
3348 if (width != -1) {
3349 if (exp != null)
3350 newW = adjustWidth(width - exp.length - 1, f, neg);
3351 else
3352 newW = adjustWidth(width, f, neg);
3353 }
3354 localizedMagnitude(sb, mant, 0, f, newW, l);
3355
3356 if (exp != null) {
3357 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3358
3359 char sign = exp[0];
3360 assert(sign == '+' || sign == '-');
3361 sb.append(sign);
3362
3363 localizedMagnitudeExp(sb, exp, 1, l);
3364 }
3365 } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3366 int prec = precision;
3367 if (precision == -1)
3368 // assume that we want all of the digits
3369 prec = 0;
3370 else if (precision == 0)
3371 prec = 1;
3372
3373 String s = hexDouble(value, prec);
3374
3375 StringBuilder va = new StringBuilder();
3376 boolean upper = f.contains(Flags.UPPERCASE);
3377 sb.append(upper ? "0X" : "0x");
3378
3379 if (f.contains(Flags.ZERO_PAD)) {
3380 trailingZeros(sb, width - s.length() - 2);
3381 }
3382
3383 int idx = s.indexOf('p');
3384 if (upper) {
3385 String tmp = s.substring(0, idx);
3386 // don't localize hex
3387 tmp = tmp.toUpperCase(Locale.US);
3388 va.append(tmp);
3389 } else {
3390 va.append(s, 0, idx);
3391 }
3392 if (prec != 0) {
3393 addZeros(va, prec);
3394 }
3395 sb.append(va);
3396 sb.append(upper ? 'P' : 'p');
3397 sb.append(s, idx+1, s.length());
3398 }
3399 }
3400
3401 // Add zeros to the requested precision.
3402 private void addZeros(StringBuilder sb, int prec) {
3403 // Look for the dot. If we don't find one, the we'll need to add
3404 // it before we add the zeros.
3405 int len = sb.length();
3406 int i;
3407 for (i = 0; i < len; i++) {
3408 if (sb.charAt(i) == '.') {
3409 break;
3410 }
3411 }
3412 boolean needDot = false;
3413 if (i == len) {
3414 needDot = true;
3415 }
3416
3417 // Determine existing precision.
3418 int outPrec = len - i - (needDot ? 0 : 1);
3419 assert (outPrec <= prec);
3420 if (outPrec == prec) {
3421 return;
3422 }
3423
3424 // Add dot if previously determined to be necessary.
3425 if (needDot) {
3426 sb.append('.');
3427 }
3428
3429 // Add zeros.
3430 trailingZeros(sb, prec - outPrec);
3431 }
3432
3433 // Method assumes that d > 0.
3434 private String hexDouble(double d, int prec) {
3435 // Let Double.toHexString handle simple cases
3436 if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3437 // remove "0x"
3438 return Double.toHexString(d).substring(2);
3439 } else {
3440 assert(prec >= 1 && prec <= 12);
3441
3442 int exponent = Math.getExponent(d);
3443 boolean subnormal
3444 = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3445
3446 // If this is subnormal input so normalize (could be faster to
3447 // do as integer operation).
3448 if (subnormal) {
3449 scaleUp = Math.scalb(1.0, 54);
3450 d *= scaleUp;
3451 // Calculate the exponent. This is not just exponent + 54
3452 // since the former is not the normalized exponent.
3453 exponent = Math.getExponent(d);
3454 assert exponent >= DoubleConsts.MIN_EXPONENT &&
3455 exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3456 }
3457
3458 int precision = 1 + prec*4;
3459 int shiftDistance
3512 }
3513 }
3514 }
3515
3516 private void print(BigDecimal value, Locale l) throws IOException {
3517 if (c == Conversion.HEXADECIMAL_FLOAT)
3518 failConversion(c, value);
3519 StringBuilder sb = new StringBuilder();
3520 boolean neg = value.signum() == -1;
3521 BigDecimal v = value.abs();
3522 // leading sign indicator
3523 leadingSign(sb, neg);
3524
3525 // the value
3526 print(sb, v, l, f, c, precision, neg);
3527
3528 // trailing sign indicator
3529 trailingSign(sb, neg);
3530
3531 // justify based on width
3532 appendJustified(a, sb);
3533 }
3534
3535 // value > 0
3536 private void print(StringBuilder sb, BigDecimal value, Locale l,
3537 Flags f, char c, int precision, boolean neg)
3538 throws IOException
3539 {
3540 if (c == Conversion.SCIENTIFIC) {
3541 // Create a new BigDecimal with the desired precision.
3542 int prec = (precision == -1 ? 6 : precision);
3543 int scale = value.scale();
3544 int origPrec = value.precision();
3545 int nzeros = 0;
3546 int compPrec;
3547
3548 if (prec > origPrec - 1) {
3549 compPrec = origPrec;
3550 nzeros = prec - (origPrec - 1);
3551 } else {
3552 compPrec = prec + 1;
3553 }
3554
3555 MathContext mc = new MathContext(compPrec);
3556 BigDecimal v
3557 = new BigDecimal(value.unscaledValue(), scale, mc);
3558
3559 BigDecimalLayout bdl
3560 = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3561 BigDecimalLayoutForm.SCIENTIFIC);
3562
3563 StringBuilder mant = bdl.mantissa();
3564
3565 // Add a decimal point if necessary. The mantissa may not
3566 // contain a decimal point if the scale is zero (the internal
3567 // representation has no fractional part) or the original
3568 // precision is one. Append a decimal point if '#' is set or if
3569 // we require zero padding to get to the requested precision.
3570 if ((origPrec == 1 || !bdl.hasDot())
3571 && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3572 mant.append('.');
3573 }
3574
3575 // Add trailing zeros in the case precision is greater than
3576 // the number of available digits after the decimal separator.
3577 trailingZeros(mant, nzeros);
3578
3579 StringBuilder exp = bdl.exponent();
3580 int newW = width;
3581 if (width != -1) {
3582 newW = adjustWidth(width - exp.length() - 1, f, neg);
3583 }
3584 localizedMagnitude(sb, mant, 0, f, newW, l);
3585
3586 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3587
3588 Flags flags = f.dup().remove(Flags.GROUP);
3589 char sign = exp.charAt(0);
3590 assert(sign == '+' || sign == '-');
3591 sb.append(sign);
3592
3593 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3594 } else if (c == Conversion.DECIMAL_FLOAT) {
3595 // Create a new BigDecimal with the desired precision.
3596 int prec = (precision == -1 ? 6 : precision);
3597 int scale = value.scale();
3598
3599 if (scale > prec) {
3600 // more "scale" digits than the requested "precision"
3601 int compPrec = value.precision();
3602 if (compPrec <= scale) {
3603 // case of 0.xxxxxx
3604 value = value.setScale(prec, RoundingMode.HALF_UP);
3605 } else {
3606 compPrec -= (scale - prec);
3607 value = new BigDecimal(value.unscaledValue(),
3608 scale,
3609 new MathContext(compPrec));
3610 }
3611 }
3612 BigDecimalLayout bdl = new BigDecimalLayout(
3613 value.unscaledValue(),
3614 value.scale(),
3615 BigDecimalLayoutForm.DECIMAL_FLOAT);
3616
3617 StringBuilder mant = bdl.mantissa();
3618 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3619
3620 // Add a decimal point if necessary. The mantissa may not
3621 // contain a decimal point if the scale is zero (the internal
3622 // representation has no fractional part). Append a decimal
3623 // point if '#' is set or we require zero padding to get to the
3624 // requested precision.
3625 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3626 || nzeros > 0)) {
3627 mant.append('.');
3628 }
3629
3630 // Add trailing zeros if the precision is greater than the
3631 // number of available digits after the decimal separator.
3632 trailingZeros(mant, nzeros);
3633
3634 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3635 } else if (c == Conversion.GENERAL) {
3636 int prec = precision;
3637 if (precision == -1)
3638 prec = 6;
3639 else if (precision == 0)
3640 prec = 1;
3641
3642 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3643 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3644 if ((value.equals(BigDecimal.ZERO))
3645 || ((value.compareTo(tenToTheNegFour) != -1)
3646 && (value.compareTo(tenToThePrec) == -1))) {
3647
3648 int e = - value.scale()
3649 + (value.unscaledValue().toString().length() - 1);
3650
3651 // xxx.yyy
3652 // g precision (# sig digits) = #x + #y
3653 // f precision = #y
3654 // exponent = #x - 1
3673 }
3674
3675 private class BigDecimalLayout {
3676 private StringBuilder mant;
3677 private StringBuilder exp;
3678 private boolean dot = false;
3679 private int scale;
3680
3681 public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3682 layout(intVal, scale, form);
3683 }
3684
3685 public boolean hasDot() {
3686 return dot;
3687 }
3688
3689 public int scale() {
3690 return scale;
3691 }
3692
3693 public StringBuilder mantissa() {
3694 return mant;
3695 }
3696
3697 // The exponent will be formatted as a sign ('+' or '-') followed
3698 // by the exponent zero-padded to include at least two digits.
3699 public StringBuilder exponent() {
3700 return exp;
3701 }
3702
3703 private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3704 String coeff = intVal.toString();
3705 this.scale = scale;
3706
3707 // Construct a buffer, with sufficient capacity for all cases.
3708 // If E-notation is needed, length will be: +1 if negative, +1
3709 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3710 // exponent. Otherwise it could have +1 if negative, plus
3711 // leading "0.00000"
3712 int len = coeff.length();
3713 mant = new StringBuilder(len + 14);
3714
3715 if (scale == 0) {
3716 if (len > 1) {
3717 mant.append(coeff.charAt(0));
3718 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3719 mant.append('.');
3720 dot = true;
3721 mant.append(coeff, 1, len);
3722 exp = new StringBuilder("+");
3723 if (len < 10) {
3724 exp.append('0').append(len - 1);
3725 } else {
3726 exp.append(len - 1);
3727 }
3728 } else {
3729 mant.append(coeff, 1, len);
3730 }
3731 } else {
3732 mant.append(coeff);
3733 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3734 exp = new StringBuilder("+00");
3735 }
3736 }
3737 return;
3738 }
3739 long adjusted = -(long) scale + (len - 1);
3740 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3741 // count of padding zeros
3742 int pad = scale - len;
3743 if (pad >= 0) {
3744 // 0.xxx form
3745 mant.append("0.");
3746 dot = true;
3747 trailingZeros(mant, pad);
3748 mant.append(coeff);
3749 } else {
3750 if (-pad < len) {
3751 // xx.xx form
3752 mant.append(coeff, 0, -pad);
3753 mant.append('.');
3754 dot = true;
3755 mant.append(coeff, -pad, -pad + scale);
3756 } else {
3757 // xx form
3758 mant.append(coeff, 0, len);
3759 trailingZeros(mant, -scale);
3760 this.scale = 0;
3761 }
3762 }
3763 } else {
3764 // x.xxx form
3765 mant.append(coeff.charAt(0));
3766 if (len > 1) {
3767 mant.append('.');
3768 dot = true;
3769 mant.append(coeff, 1, len);
3770 }
3771 exp = new StringBuilder();
3772 if (adjusted != 0) {
3773 long abs = Math.abs(adjusted);
3774 // require sign
3775 exp.append(adjusted < 0 ? '-' : '+');
3776 if (abs < 10) {
3777 exp.append('0');
3778 }
3779 exp.append(abs);
3780 } else {
3781 exp.append("+00");
3782 }
3783 }
3784 }
3785 }
3786
3787 private int adjustWidth(int width, Flags f, boolean neg) {
3788 int newW = width;
3789 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3790 newW--;
3791 return newW;
3792 }
3793
3794 // Add trailing zeros
3795 private void trailingZeros(StringBuilder sb, int nzeros) {
3796 for (int i = 0; i < nzeros; i++) {
3797 sb.append('0');
3798 }
3799 }
3800
3801 private void print(Calendar t, char c, Locale l) throws IOException {
3802 StringBuilder sb = new StringBuilder();
3803 print(sb, t, c, l);
3804
3805 // justify based on width
3806 if (f.contains(Flags.UPPERCASE)) {
3807 appendJustified(a, sb.toString().toUpperCase());
3808 } else {
3809 appendJustified(a, sb);
3810 }
3811 }
3812
3813 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3814 throws IOException {
3815 if (sb == null)
3816 sb = new StringBuilder();
3817 switch (c) {
3818 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3819 case DateTime.HOUR_0: // 'I' (01 - 12)
3820 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3821 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3822 int i = t.get(Calendar.HOUR_OF_DAY);
3823 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3824 i = (i == 0 || i == 12 ? 12 : i % 12);
3825 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3826 || c == DateTime.HOUR_0
3827 ? Flags.ZERO_PAD
3828 : Flags.NONE);
3829 sb.append(localizedMagnitude(null, i, flags, 2, l));
3830 break;
3831 }
3832 case DateTime.MINUTE: { // 'M' (00 - 59)
3833 int i = t.get(Calendar.MINUTE);
3834 Flags flags = Flags.ZERO_PAD;
3967 // Composites
3968 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
3969 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
3970 char sep = ':';
3971 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
3972 print(sb, t, DateTime.MINUTE, l);
3973 if (c == DateTime.TIME) {
3974 sb.append(sep);
3975 print(sb, t, DateTime.SECOND, l);
3976 }
3977 break;
3978 }
3979 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
3980 char sep = ':';
3981 print(sb, t, DateTime.HOUR_0, l).append(sep);
3982 print(sb, t, DateTime.MINUTE, l).append(sep);
3983 print(sb, t, DateTime.SECOND, l).append(' ');
3984 // this may be in wrong place for some locales
3985 StringBuilder tsb = new StringBuilder();
3986 print(tsb, t, DateTime.AM_PM, l);
3987
3988 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
3989 break;
3990 }
3991 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
3992 char sep = ' ';
3993 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
3994 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
3995 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
3996 print(sb, t, DateTime.TIME, l).append(sep);
3997 print(sb, t, DateTime.ZONE, l).append(sep);
3998 print(sb, t, DateTime.YEAR_4, l);
3999 break;
4000 }
4001 case DateTime.DATE: { // 'D' (mm/dd/yy)
4002 char sep = '/';
4003 print(sb, t, DateTime.MONTH, l).append(sep);
4004 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4005 print(sb, t, DateTime.YEAR_2, l);
4006 break;
4007 }
4008 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4009 char sep = '-';
4010 print(sb, t, DateTime.YEAR_4, l).append(sep);
4011 print(sb, t, DateTime.MONTH, l).append(sep);
4012 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4013 break;
4014 }
4015 default:
4016 assert false;
4017 }
4018 return sb;
4019 }
4020
4021 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4022 StringBuilder sb = new StringBuilder();
4023 print(sb, t, c, l);
4024 // justify based on width
4025 if (f.contains(Flags.UPPERCASE)) {
4026 appendJustified(a, sb.toString().toUpperCase());
4027 } else {
4028 appendJustified(a, sb);
4029 }
4030 }
4031
4032 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4033 Locale l) throws IOException {
4034 if (sb == null)
4035 sb = new StringBuilder();
4036 try {
4037 switch (c) {
4038 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4039 int i = t.get(ChronoField.HOUR_OF_DAY);
4040 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4041 break;
4042 }
4043 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4044 int i = t.get(ChronoField.HOUR_OF_DAY);
4045 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4046 break;
4047 }
4048 case DateTime.HOUR_0: { // 'I' (01 - 12)
4049 int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4257
4258 // -- Methods to support throwing exceptions --
4259
4260 private void failMismatch(Flags f, char c) {
4261 String fs = f.toString();
4262 throw new FormatFlagsConversionMismatchException(fs, c);
4263 }
4264
4265 private void failConversion(char c, Object arg) {
4266 throw new IllegalFormatConversionException(c, arg.getClass());
4267 }
4268
4269 private char getZero(Locale l) {
4270 if ((l != null) && !l.equals(locale())) {
4271 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4272 return dfs.getZeroDigit();
4273 }
4274 return zero;
4275 }
4276
4277 private StringBuilder localizedMagnitude(StringBuilder sb,
4278 long value, Flags f, int width, Locale l) {
4279 return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);
4280 }
4281
4282 private StringBuilder localizedMagnitude(StringBuilder sb,
4283 CharSequence value, final int offset, Flags f, int width,
4284 Locale l) {
4285 if (sb == null) {
4286 sb = new StringBuilder();
4287 }
4288 int begin = sb.length();
4289
4290 char zero = getZero(l);
4291
4292 // determine localized grouping separator and size
4293 char grpSep = '\0';
4294 int grpSize = -1;
4295 char decSep = '\0';
4296
4297 int len = value.length();
4298 int dot = len;
4299 for (int j = offset; j < len; j++) {
4300 if (value.charAt(j) == '.') {
4301 dot = j;
4302 break;
4303 }
4304 }
4305
4306 if (dot < len) {
4307 if (l == null || l.equals(Locale.US)) {
4308 decSep = '.';
4309 } else {
4310 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4311 decSep = dfs.getDecimalSeparator();
4312 }
4313 }
4314
4315 if (f.contains(Flags.GROUP)) {
4316 if (l == null || l.equals(Locale.US)) {
4317 grpSep = ',';
4318 grpSize = 3;
4319 } else {
4320 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4321 grpSep = dfs.getGroupingSeparator();
4322 DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4323 grpSize = df.getGroupingSize();
4324 }
4325 }
4326
4327 // localize the digits inserting group separators as necessary
4328 for (int j = offset; j < len; j++) {
4329 if (j == dot) {
4330 sb.append(decSep);
4331 // no more group separators after the decimal separator
4332 grpSep = '\0';
4333 continue;
4334 }
4335
4336 char c = value.charAt(j);
4337 sb.append((char) ((c - '0') + zero));
4338 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4339 sb.append(grpSep);
4340 }
4341 }
4342
4343 // apply zero padding
4344 if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4345 for (int k = sb.length(); k < width; k++) {
4346 sb.insert(begin, zero);
4347 }
4348 }
4349
4350 return sb;
4351 }
4352 }
4353
4354 private void localizedMagnitudeExp(StringBuilder sb, char[] value,
4355 final int offset, Locale l) {
4356
4357 char zero = getZero(l);
4358
4359 int len = value.length;
4360 for (int j = offset; j < len; j++) {
4361 if (value[j] == '.') {
4362 if (l == null || l.equals(Locale.US)) {
4363 sb.append('.');
4364 } else {
4365 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4366 sb.append(dfs.getDecimalSeparator());
4367 }
4368 }
4369 char c = value[j];
4370 sb.append((char) ((c - '0') + zero));
4371 }
4372
4373 }
4374
4375 private static class Flags {
4376 private int flags;
4377
4378 static final Flags NONE = new Flags(0); // ''
4379
4380 // duplicate declarations from Formattable.java
4381 static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'
4382 static final Flags UPPERCASE = new Flags(1<<1); // '^'
4383 static final Flags ALTERNATE = new Flags(1<<2); // '#'
4384
4385 // numerics
4386 static final Flags PLUS = new Flags(1<<3); // '+'
4387 static final Flags LEADING_SPACE = new Flags(1<<4); // ' '
4388 static final Flags ZERO_PAD = new Flags(1<<5); // '0'
4389 static final Flags GROUP = new Flags(1<<6); // ','
4390 static final Flags PARENTHESES = new Flags(1<<7); // '('
4391
4392 // indexing
4393 static final Flags PREVIOUS = new Flags(1<<8); // '<'
4394
4401 }
4402
4403 public boolean contains(Flags f) {
4404 return (flags & f.valueOf()) == f.valueOf();
4405 }
4406
4407 public Flags dup() {
4408 return new Flags(flags);
4409 }
4410
4411 private Flags add(Flags f) {
4412 flags |= f.valueOf();
4413 return this;
4414 }
4415
4416 public Flags remove(Flags f) {
4417 flags &= ~f.valueOf();
4418 return this;
4419 }
4420
4421 public static Flags parse(String s, int start, int end) {
4422 Flags f = new Flags(0);
4423 for (int i = start; i < end; i++) {
4424 char c = s.charAt(i);
4425 Flags v = parse(c);
4426 if (f.contains(v))
4427 throw new DuplicateFormatFlagsException(v.toString());
4428 f.add(v);
4429 }
4430 return f;
4431 }
4432
4433 // parse those flags which may be provided by users
4434 private static Flags parse(char c) {
4435 switch (c) {
4436 case '-': return LEFT_JUSTIFY;
4437 case '#': return ALTERNATE;
4438 case '+': return PLUS;
4439 case ' ': return LEADING_SPACE;
4440 case '0': return ZERO_PAD;
4441 case ',': return GROUP;
4442 case '(': return PARENTHESES;
4443 case '<': return PREVIOUS;
4444 default:
|