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
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 char[] exp = (value == 0.0)
3271 ? new char[] {'+','0','0'} : fd.getExponent();
3272
3273 int newW = width;
3274 if (width != -1) {
3275 newW = adjustWidth(width - exp.length - 1, f, neg);
3276 }
3277 localizedMagnitude(sb, mant, 0, f, newW, l);
3278
3279 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3280
3281 char sign = exp[0];
3282 assert(sign == '+' || sign == '-');
3283 sb.append(sign);
3284
3285 localizedMagnitudeExp(sb, exp, 1, l);
3286 } else if (c == Conversion.DECIMAL_FLOAT) {
3287 // Create a new FormattedFloatingDecimal with the desired
3288 // precision.
3289 int prec = (precision == -1 ? 6 : precision);
3290
3291 FormattedFloatingDecimal fd
3292 = FormattedFloatingDecimal.valueOf(value, prec,
3293 FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3294
3295 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3296 addZeros(mant, prec);
3297
3298 // If the precision is zero and the '#' flag is set, add the
3299 // requested decimal point.
3300 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3301 mant.append('.');
3302
3303 int newW = width;
3304 if (width != -1)
3305 newW = adjustWidth(width, f, neg);
3306 localizedMagnitude(sb, mant, 0, f, newW, l);
3307 } else if (c == Conversion.GENERAL) {
3308 int prec = precision;
3309 if (precision == -1)
3310 prec = 6;
3311 else if (precision == 0)
3312 prec = 1;
3313
3314 char[] exp;
3315 StringBuilder mant = new StringBuilder();
3316 int expRounded;
3317 if (value == 0.0) {
3318 exp = null;
3319 mant.append('0');
3320 expRounded = 0;
3321 } else {
3322 FormattedFloatingDecimal fd
3323 = FormattedFloatingDecimal.valueOf(value, prec,
3324 FormattedFloatingDecimal.Form.GENERAL);
3325 exp = fd.getExponent();
3326 mant.append(fd.getMantissa());
3327 expRounded = fd.getExponentRounded();
3328 }
3329
3330 if (exp != null) {
3331 prec -= 1;
3332 } else {
3333 prec -= expRounded + 1;
3334 }
3335
3336 addZeros(mant, prec);
3337 // If the precision is zero and the '#' flag is set, add the
3338 // requested decimal point.
3339 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3340 mant.append('.');
3341 }
3342
3343 int newW = width;
3344 if (width != -1) {
3345 if (exp != null)
3346 newW = adjustWidth(width - exp.length - 1, f, neg);
3347 else
3348 newW = adjustWidth(width, f, neg);
3349 }
3350 localizedMagnitude(sb, mant, 0, f, newW, l);
3351
3352 if (exp != null) {
3353 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3354
3355 char sign = exp[0];
3356 assert(sign == '+' || sign == '-');
3357 sb.append(sign);
3358
3359 localizedMagnitudeExp(sb, exp, 1, l);
3360 }
3361 } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3362 int prec = precision;
3363 if (precision == -1)
3364 // assume that we want all of the digits
3365 prec = 0;
3366 else if (precision == 0)
3367 prec = 1;
3368
3369 String s = hexDouble(value, prec);
3370
3371 StringBuilder va = new StringBuilder();
3372 boolean upper = f.contains(Flags.UPPERCASE);
3373 sb.append(upper ? "0X" : "0x");
3374
3375 if (f.contains(Flags.ZERO_PAD)) {
3376 trailingZeros(sb, width - s.length() - 2);
3377 }
3378
3379 int idx = s.indexOf('p');
3380 if (upper) {
3381 String tmp = s.substring(0, idx);
3382 // don't localize hex
3383 tmp = tmp.toUpperCase(Locale.US);
3384 va.append(tmp);
3385 } else {
3386 va.append(s, 0, idx);
3387 }
3388 if (prec != 0) {
3389 addZeros(va, prec);
3390 }
3391 sb.append(va);
3392 sb.append(upper ? 'P' : 'p');
3393 sb.append(s, idx+1, s.length());
3394 }
3395 }
3396
3397 // Add zeros to the requested precision.
3398 private void addZeros(StringBuilder sb, int prec) {
3399 // Look for the dot. If we don't find one, the we'll need to add
3400 // it before we add the zeros.
3401 int len = sb.length();
3402 int i;
3403 for (i = 0; i < len; i++) {
3404 if (sb.charAt(i) == '.') {
3405 break;
3406 }
3407 }
3408 boolean needDot = false;
3409 if (i == len) {
3410 needDot = true;
3411 }
3412
3413 // Determine existing precision.
3414 int outPrec = len - i - (needDot ? 0 : 1);
3415 assert (outPrec <= prec);
3416 if (outPrec == prec) {
3417 return;
3418 }
3419
3420 // Add dot if previously determined to be necessary.
3421 if (needDot) {
3422 sb.append('.');
3423 }
3424
3425 // Add zeros.
3426 trailingZeros(sb, prec - outPrec);
3427 }
3428
3429 // Method assumes that d > 0.
3430 private String hexDouble(double d, int prec) {
3431 // Let Double.toHexString handle simple cases
3432 if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3433 // remove "0x"
3434 return Double.toHexString(d).substring(2);
3435 } else {
3436 assert(prec >= 1 && prec <= 12);
3437
3438 int exponent = Math.getExponent(d);
3439 boolean subnormal
3440 = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3441
3442 // If this is subnormal input so normalize (could be faster to
3443 // do as integer operation).
3444 if (subnormal) {
3445 scaleUp = Math.scalb(1.0, 54);
3446 d *= scaleUp;
3447 // Calculate the exponent. This is not just exponent + 54
3448 // since the former is not the normalized exponent.
3449 exponent = Math.getExponent(d);
3450 assert exponent >= DoubleConsts.MIN_EXPONENT &&
3451 exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3452 }
3453
3454 int precision = 1 + prec*4;
3455 int shiftDistance
3508 }
3509 }
3510 }
3511
3512 private void print(BigDecimal value, Locale l) throws IOException {
3513 if (c == Conversion.HEXADECIMAL_FLOAT)
3514 failConversion(c, value);
3515 StringBuilder sb = new StringBuilder();
3516 boolean neg = value.signum() == -1;
3517 BigDecimal v = value.abs();
3518 // leading sign indicator
3519 leadingSign(sb, neg);
3520
3521 // the value
3522 print(sb, v, l, f, c, precision, neg);
3523
3524 // trailing sign indicator
3525 trailingSign(sb, neg);
3526
3527 // justify based on width
3528 appendJustified(a, sb);
3529 }
3530
3531 // value > 0
3532 private void print(StringBuilder sb, BigDecimal value, Locale l,
3533 Flags f, char c, int precision, boolean neg)
3534 throws IOException
3535 {
3536 if (c == Conversion.SCIENTIFIC) {
3537 // Create a new BigDecimal with the desired precision.
3538 int prec = (precision == -1 ? 6 : precision);
3539 int scale = value.scale();
3540 int origPrec = value.precision();
3541 int nzeros = 0;
3542 int compPrec;
3543
3544 if (prec > origPrec - 1) {
3545 compPrec = origPrec;
3546 nzeros = prec - (origPrec - 1);
3547 } else {
3548 compPrec = prec + 1;
3549 }
3550
3551 MathContext mc = new MathContext(compPrec);
3552 BigDecimal v
3553 = new BigDecimal(value.unscaledValue(), scale, mc);
3554
3555 BigDecimalLayout bdl
3556 = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3557 BigDecimalLayoutForm.SCIENTIFIC);
3558
3559 StringBuilder mant = bdl.mantissa();
3560
3561 // Add a decimal point if necessary. The mantissa may not
3562 // contain a decimal point if the scale is zero (the internal
3563 // representation has no fractional part) or the original
3564 // precision is one. Append a decimal point if '#' is set or if
3565 // we require zero padding to get to the requested precision.
3566 if ((origPrec == 1 || !bdl.hasDot())
3567 && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3568 mant.append('.');
3569 }
3570
3571 // Add trailing zeros in the case precision is greater than
3572 // the number of available digits after the decimal separator.
3573 trailingZeros(mant, nzeros);
3574
3575 StringBuilder exp = bdl.exponent();
3576 int newW = width;
3577 if (width != -1) {
3578 newW = adjustWidth(width - exp.length() - 1, f, neg);
3579 }
3580 localizedMagnitude(sb, mant, 0, f, newW, l);
3581
3582 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3583
3584 Flags flags = f.dup().remove(Flags.GROUP);
3585 char sign = exp.charAt(0);
3586 assert(sign == '+' || sign == '-');
3587 sb.append(sign);
3588
3589 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3590 } else if (c == Conversion.DECIMAL_FLOAT) {
3591 // Create a new BigDecimal with the desired precision.
3592 int prec = (precision == -1 ? 6 : precision);
3593 int scale = value.scale();
3594
3595 if (scale > prec) {
3596 // more "scale" digits than the requested "precision"
3597 int compPrec = value.precision();
3598 if (compPrec <= scale) {
3599 // case of 0.xxxxxx
3600 value = value.setScale(prec, RoundingMode.HALF_UP);
3601 } else {
3602 compPrec -= (scale - prec);
3603 value = new BigDecimal(value.unscaledValue(),
3604 scale,
3605 new MathContext(compPrec));
3606 }
3607 }
3608 BigDecimalLayout bdl = new BigDecimalLayout(
3609 value.unscaledValue(),
3610 value.scale(),
3611 BigDecimalLayoutForm.DECIMAL_FLOAT);
3612
3613 StringBuilder mant = bdl.mantissa();
3614 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3615
3616 // Add a decimal point if necessary. The mantissa may not
3617 // contain a decimal point if the scale is zero (the internal
3618 // representation has no fractional part). Append a decimal
3619 // point if '#' is set or we require zero padding to get to the
3620 // requested precision.
3621 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3622 || nzeros > 0)) {
3623 mant.append('.');
3624 }
3625
3626 // Add trailing zeros if the precision is greater than the
3627 // number of available digits after the decimal separator.
3628 trailingZeros(mant, nzeros);
3629
3630 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3631 } else if (c == Conversion.GENERAL) {
3632 int prec = precision;
3633 if (precision == -1)
3634 prec = 6;
3635 else if (precision == 0)
3636 prec = 1;
3637
3638 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3639 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3640 if ((value.equals(BigDecimal.ZERO))
3641 || ((value.compareTo(tenToTheNegFour) != -1)
3642 && (value.compareTo(tenToThePrec) == -1))) {
3643
3644 int e = - value.scale()
3645 + (value.unscaledValue().toString().length() - 1);
3646
3647 // xxx.yyy
3648 // g precision (# sig digits) = #x + #y
3649 // f precision = #y
3650 // exponent = #x - 1
3669 }
3670
3671 private class BigDecimalLayout {
3672 private StringBuilder mant;
3673 private StringBuilder exp;
3674 private boolean dot = false;
3675 private int scale;
3676
3677 public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3678 layout(intVal, scale, form);
3679 }
3680
3681 public boolean hasDot() {
3682 return dot;
3683 }
3684
3685 public int scale() {
3686 return scale;
3687 }
3688
3689 public StringBuilder mantissa() {
3690 return mant;
3691 }
3692
3693 // The exponent will be formatted as a sign ('+' or '-') followed
3694 // by the exponent zero-padded to include at least two digits.
3695 public StringBuilder exponent() {
3696 return exp;
3697 }
3698
3699 private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3700 String coeff = intVal.toString();
3701 this.scale = scale;
3702
3703 // Construct a buffer, with sufficient capacity for all cases.
3704 // If E-notation is needed, length will be: +1 if negative, +1
3705 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3706 // exponent. Otherwise it could have +1 if negative, plus
3707 // leading "0.00000"
3708 int len = coeff.length();
3709 mant = new StringBuilder(len + 14);
3710
3711 if (scale == 0) {
3712 if (len > 1) {
3713 mant.append(coeff.charAt(0));
3714 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3715 mant.append('.');
3716 dot = true;
3717 mant.append(coeff, 1, len);
3718 exp = new StringBuilder("+");
3719 if (len < 10) {
3720 exp.append('0').append(len - 1);
3721 } else {
3722 exp.append(len - 1);
3723 }
3724 } else {
3725 mant.append(coeff, 1, len);
3726 }
3727 } else {
3728 mant.append(coeff);
3729 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3730 exp = new StringBuilder("+00");
3731 }
3732 }
3733 return;
3734 }
3735 long adjusted = -(long) scale + (len - 1);
3736 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3737 // count of padding zeros
3738 int pad = scale - len;
3739 if (pad >= 0) {
3740 // 0.xxx form
3741 mant.append("0.");
3742 dot = true;
3743 trailingZeros(mant, pad);
3744 mant.append(coeff);
3745 } else {
3746 if (-pad < len) {
3747 // xx.xx form
3748 mant.append(coeff, 0, -pad);
3749 mant.append('.');
3750 dot = true;
3751 mant.append(coeff, -pad, -pad + scale);
3752 } else {
3753 // xx form
3754 mant.append(coeff, 0, len);
3755 trailingZeros(mant, -scale);
3756 this.scale = 0;
3757 }
3758 }
3759 } else {
3760 // x.xxx form
3761 mant.append(coeff.charAt(0));
3762 if (len > 1) {
3763 mant.append('.');
3764 dot = true;
3765 mant.append(coeff, 1, len);
3766 }
3767 exp = new StringBuilder();
3768 if (adjusted != 0) {
3769 long abs = Math.abs(adjusted);
3770 // require sign
3771 exp.append(adjusted < 0 ? '-' : '+');
3772 if (abs < 10) {
3773 exp.append('0');
3774 }
3775 exp.append(abs);
3776 } else {
3777 exp.append("+00");
3778 }
3779 }
3780 }
3781 }
3782
3783 private int adjustWidth(int width, Flags f, boolean neg) {
3784 int newW = width;
3785 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3786 newW--;
3787 return newW;
3788 }
3789
3790 // Add trailing zeros
3791 private void trailingZeros(StringBuilder sb, int nzeros) {
3792 for (int i = 0; i < nzeros; i++) {
3793 sb.append('0');
3794 }
3795 }
3796
3797 private void print(Calendar t, char c, Locale l) throws IOException {
3798 StringBuilder sb = new StringBuilder();
3799 print(sb, t, c, l);
3800
3801 // justify based on width
3802 if (f.contains(Flags.UPPERCASE)) {
3803 appendJustified(a, sb.toString().toUpperCase());
3804 } else {
3805 appendJustified(a, sb);
3806 }
3807 }
3808
3809 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3810 throws IOException {
3811 if (sb == null)
3812 sb = new StringBuilder();
3813 switch (c) {
3814 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3815 case DateTime.HOUR_0: // 'I' (01 - 12)
3816 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3817 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3818 int i = t.get(Calendar.HOUR_OF_DAY);
3819 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3820 i = (i == 0 || i == 12 ? 12 : i % 12);
3821 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3822 || c == DateTime.HOUR_0
3823 ? Flags.ZERO_PAD
3824 : Flags.NONE);
3825 sb.append(localizedMagnitude(null, i, flags, 2, l));
3826 break;
3827 }
3828 case DateTime.MINUTE: { // 'M' (00 - 59)
3829 int i = t.get(Calendar.MINUTE);
3830 Flags flags = Flags.ZERO_PAD;
3963 // Composites
3964 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
3965 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
3966 char sep = ':';
3967 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
3968 print(sb, t, DateTime.MINUTE, l);
3969 if (c == DateTime.TIME) {
3970 sb.append(sep);
3971 print(sb, t, DateTime.SECOND, l);
3972 }
3973 break;
3974 }
3975 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
3976 char sep = ':';
3977 print(sb, t, DateTime.HOUR_0, l).append(sep);
3978 print(sb, t, DateTime.MINUTE, l).append(sep);
3979 print(sb, t, DateTime.SECOND, l).append(' ');
3980 // this may be in wrong place for some locales
3981 StringBuilder tsb = new StringBuilder();
3982 print(tsb, t, DateTime.AM_PM, l);
3983
3984 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
3985 break;
3986 }
3987 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
3988 char sep = ' ';
3989 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
3990 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
3991 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
3992 print(sb, t, DateTime.TIME, l).append(sep);
3993 print(sb, t, DateTime.ZONE, l).append(sep);
3994 print(sb, t, DateTime.YEAR_4, l);
3995 break;
3996 }
3997 case DateTime.DATE: { // 'D' (mm/dd/yy)
3998 char sep = '/';
3999 print(sb, t, DateTime.MONTH, l).append(sep);
4000 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4001 print(sb, t, DateTime.YEAR_2, l);
4002 break;
4003 }
4004 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4005 char sep = '-';
4006 print(sb, t, DateTime.YEAR_4, l).append(sep);
4007 print(sb, t, DateTime.MONTH, l).append(sep);
4008 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4009 break;
4010 }
4011 default:
4012 assert false;
4013 }
4014 return sb;
4015 }
4016
4017 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4018 StringBuilder sb = new StringBuilder();
4019 print(sb, t, c, l);
4020 // justify based on width
4021 if (f.contains(Flags.UPPERCASE)) {
4022 appendJustified(a, sb.toString().toUpperCase());
4023 } else {
4024 appendJustified(a, sb);
4025 }
4026 }
4027
4028 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4029 Locale l) throws IOException {
4030 if (sb == null)
4031 sb = new StringBuilder();
4032 try {
4033 switch (c) {
4034 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4035 int i = t.get(ChronoField.HOUR_OF_DAY);
4036 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4037 break;
4038 }
4039 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4040 int i = t.get(ChronoField.HOUR_OF_DAY);
4041 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4042 break;
4043 }
4044 case DateTime.HOUR_0: { // 'I' (01 - 12)
4045 int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4253
4254 // -- Methods to support throwing exceptions --
4255
4256 private void failMismatch(Flags f, char c) {
4257 String fs = f.toString();
4258 throw new FormatFlagsConversionMismatchException(fs, c);
4259 }
4260
4261 private void failConversion(char c, Object arg) {
4262 throw new IllegalFormatConversionException(c, arg.getClass());
4263 }
4264
4265 private char getZero(Locale l) {
4266 if ((l != null) && !l.equals(locale())) {
4267 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4268 return dfs.getZeroDigit();
4269 }
4270 return zero;
4271 }
4272
4273 private StringBuilder localizedMagnitude(StringBuilder sb,
4274 long value, Flags f, int width, Locale l) {
4275 return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);
4276 }
4277
4278 private StringBuilder localizedMagnitude(StringBuilder sb,
4279 CharSequence value, final int offset, Flags f, int width,
4280 Locale l) {
4281 if (sb == null) {
4282 sb = new StringBuilder();
4283 }
4284 int begin = sb.length();
4285
4286 char zero = getZero(l);
4287
4288 // determine localized grouping separator and size
4289 char grpSep = '\0';
4290 int grpSize = -1;
4291 char decSep = '\0';
4292
4293 int len = value.length();
4294 int dot = len;
4295 for (int j = offset; j < len; j++) {
4296 if (value.charAt(j) == '.') {
4297 dot = j;
4298 break;
4299 }
4300 }
4301
4302 if (dot < len) {
4303 if (l == null || l.equals(Locale.US)) {
4304 decSep = '.';
4305 } else {
4306 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4307 decSep = dfs.getDecimalSeparator();
4308 }
4309 }
4310
4311 if (f.contains(Flags.GROUP)) {
4312 if (l == null || l.equals(Locale.US)) {
4313 grpSep = ',';
4314 grpSize = 3;
4315 } else {
4316 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4317 grpSep = dfs.getGroupingSeparator();
4318 DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4319 grpSize = df.getGroupingSize();
4320 }
4321 }
4322
4323 // localize the digits inserting group separators as necessary
4324 for (int j = offset; j < len; j++) {
4325 if (j == dot) {
4326 sb.append(decSep);
4327 // no more group separators after the decimal separator
4328 grpSep = '\0';
4329 continue;
4330 }
4331
4332 char c = value.charAt(j);
4333 sb.append((char) ((c - '0') + zero));
4334 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4335 sb.append(grpSep);
4336 }
4337 }
4338
4339 // apply zero padding
4340 if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4341 for (int k = sb.length(); k < width; k++) {
4342 sb.insert(begin, zero);
4343 }
4344 }
4345
4346 return sb;
4347 }
4348
4349 // Specialized localization of exponents, where the source value can only
4350 // contain characters '0' through '9', starting at index offset, and no
4351 // group separators is added for any locale.
4352 private void localizedMagnitudeExp(StringBuilder sb, char[] value,
4353 final int offset, Locale l) {
4354 char zero = getZero(l);
4355
4356 int len = value.length;
4357 for (int j = offset; j < len; j++) {
4358 char c = value[j];
4359 sb.append((char) ((c - '0') + zero));
4360 }
4361 }
4362 }
4363
4364 private static class Flags {
4365 private int flags;
4366
4367 static final Flags NONE = new Flags(0); // ''
4368
4369 // duplicate declarations from Formattable.java
4370 static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'
4371 static final Flags UPPERCASE = new Flags(1<<1); // '^'
4372 static final Flags ALTERNATE = new Flags(1<<2); // '#'
4373
4374 // numerics
4375 static final Flags PLUS = new Flags(1<<3); // '+'
4376 static final Flags LEADING_SPACE = new Flags(1<<4); // ' '
4377 static final Flags ZERO_PAD = new Flags(1<<5); // '0'
4378 static final Flags GROUP = new Flags(1<<6); // ','
4379 static final Flags PARENTHESES = new Flags(1<<7); // '('
4380
4381 // indexing
4390 }
4391
4392 public boolean contains(Flags f) {
4393 return (flags & f.valueOf()) == f.valueOf();
4394 }
4395
4396 public Flags dup() {
4397 return new Flags(flags);
4398 }
4399
4400 private Flags add(Flags f) {
4401 flags |= f.valueOf();
4402 return this;
4403 }
4404
4405 public Flags remove(Flags f) {
4406 flags &= ~f.valueOf();
4407 return this;
4408 }
4409
4410 public static Flags parse(String s, int start, int end) {
4411 Flags f = new Flags(0);
4412 for (int i = start; i < end; i++) {
4413 char c = s.charAt(i);
4414 Flags v = parse(c);
4415 if (f.contains(v))
4416 throw new DuplicateFormatFlagsException(v.toString());
4417 f.add(v);
4418 }
4419 return f;
4420 }
4421
4422 // parse those flags which may be provided by users
4423 private static Flags parse(char c) {
4424 switch (c) {
4425 case '-': return LEFT_JUSTIFY;
4426 case '#': return ALTERNATE;
4427 case '+': return PLUS;
4428 case ' ': return LEADING_SPACE;
4429 case '0': return ZERO_PAD;
4430 case ',': return GROUP;
4431 case '(': return PARENTHESES;
4432 case '<': return PREVIOUS;
4433 default:
|