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); // ','
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 padLeft = f.contains(Flags.LEFT_JUSTIFY);
2915 int sp = width - cs.length();
2916 if (padLeft) {
2917 a.append(cs);
2918 }
2919 for (int i = 0; i < sp; i++) {
2920 a.append(' ');
2921 }
2922 if (!padLeft) {
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;
3091 if (neg) {
3092 if (value == Long.MIN_VALUE) {
3093 valueStr = "9223372036854775808";
3094 } else {
3095 valueStr = Long.toString(-value, 10);
3096 }
3097 } else {
3098 valueStr = Long.toString(value, 10);
3099 }
3100
3101 // leading sign indicator
3102 leadingSign(sb, neg);
3103
3104 // the value
3105 localizedMagnitude(sb, valueStr, 0, f, adjustWidth(width, f, neg), l);
3106
3107 // trailing sign indicator
3108 trailingSign(sb, neg);
3109 } else if (c == Conversion.OCTAL_INTEGER) {
3110 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3111 Flags.PLUS);
3112 String s = Long.toOctalString(value);
3113 int len = (f.contains(Flags.ALTERNATE)
3114 ? s.length() + 1
3115 : s.length());
3116
3117 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3118 if (f.contains(Flags.ALTERNATE))
3119 sb.append('0');
3120 if (f.contains(Flags.ZERO_PAD)) {
3121 trailingZeros(sb, width - len);
3122 }
3123 sb.append(s);
3124 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3125 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3126 Flags.PLUS);
3127 String s = Long.toHexString(value);
3128 int len = (f.contains(Flags.ALTERNATE)
3129 ? s.length() + 2
3130 : s.length());
3131
3132 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3133 if (f.contains(Flags.ALTERNATE))
3134 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3135 if (f.contains(Flags.ZERO_PAD)) {
3136 trailingZeros(sb, width - len);
3137 }
3138 if (f.contains(Flags.UPPERCASE))
3139 s = s.toUpperCase();
3140 sb.append(s);
3141 }
3142
3143 // justify based on width
3144 appendJustified(a, sb);
3145 }
3146
3147 // neg := val < 0
3148 private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3149 if (!neg) {
3150 if (f.contains(Flags.PLUS)) {
3151 sb.append('+');
3152 } else if (f.contains(Flags.LEADING_SPACE)) {
3153 sb.append(' ');
3154 }
3155 } else {
3156 if (f.contains(Flags.PARENTHESES))
3157 sb.append('(');
3158 else
3159 sb.append('-');
3160 }
3161 return sb;
3162 }
3163
3164 // neg := val < 0
3165 private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3166 if (neg && f.contains(Flags.PARENTHESES))
3167 sb.append(')');
3168 return sb;
3169 }
3170
3171 private void print(BigInteger value, Locale l) throws IOException {
3172 StringBuilder sb = new StringBuilder();
3173 boolean neg = value.signum() == -1;
3174 BigInteger v = value.abs();
3175
3176 // leading sign indicator
3177 leadingSign(sb, neg);
3178
3179 // the value
3180 if (c == Conversion.DECIMAL_INTEGER) {
3181 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);
3182 } else if (c == Conversion.OCTAL_INTEGER) {
3183 String s = v.toString(8);
3184
3185 int len = s.length() + sb.length();
3186 if (neg && f.contains(Flags.PARENTHESES))
3187 len++;
3188
3189 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3190 if (f.contains(Flags.ALTERNATE)) {
3191 len++;
3192 sb.append('0');
3193 }
3194 if (f.contains(Flags.ZERO_PAD)) {
3195 trailingZeros(sb, width - len);
3196 }
3197 sb.append(s);
3198 } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3199 String s = v.toString(16);
3200
3201 int len = s.length() + sb.length();
3202 if (neg && f.contains(Flags.PARENTHESES))
3203 len++;
3204
3205 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3206 if (f.contains(Flags.ALTERNATE)) {
3207 len += 2;
3208 sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3209 }
3210 if (f.contains(Flags.ZERO_PAD)) {
3211 trailingZeros(sb, width - len);
3212 }
3213 if (f.contains(Flags.UPPERCASE))
3214 s = s.toUpperCase();
3215 sb.append(s);
3216 }
3217
3218 // trailing sign indicator
3219 trailingSign(sb, (value.signum() == -1));
3220
3221 // justify based on width
3222 appendJustified(a, sb);
3223 }
3224
3225 private void print(float value, Locale l) throws IOException {
3226 print((double) value, l);
3227 }
3228
3229 private void print(double value, Locale l) throws IOException {
3230 StringBuilder sb = new StringBuilder();
3231 boolean neg = Double.compare(value, 0.0) == -1;
3232
3233 if (!Double.isNaN(value)) {
3234 double v = Math.abs(value);
3235
3236 // leading sign indicator
3237 leadingSign(sb, neg);
3238
3239 // the value
3240 if (!Double.isInfinite(v))
3241 print(sb, v, l, f, c, precision, neg);
3242 else
3243 sb.append(f.contains(Flags.UPPERCASE)
3244 ? "INFINITY" : "Infinity");
3245
3246 // trailing sign indicator
3247 trailingSign(sb, neg);
3248 } else {
3249 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3250 }
3251
3252 // justify based on width
3253 appendJustified(a, sb);
3254 }
3255
3256 // !Double.isInfinite(value) && !Double.isNaN(value)
3257 private void print(StringBuilder sb, double value, Locale l,
3258 Flags f, char c, int precision, boolean neg)
3259 throws IOException
3260 {
3261 if (c == Conversion.SCIENTIFIC) {
3262 // Create a new FormattedFloatingDecimal with the desired
3263 // precision.
3264 int prec = (precision == -1 ? 6 : precision);
3265
3266 FormattedFloatingDecimal fd
3267 = FormattedFloatingDecimal.valueOf(value, prec,
3268 FormattedFloatingDecimal.Form.SCIENTIFIC);
3269
3270 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3271 addZeros(mant, prec);
3272
3273 // If the precision is zero and the '#' flag is set, add the
3274 // requested decimal point.
3275 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3276 mant.append('.');
3277 }
3278
3279 StringBuilder exp = new StringBuilder();
3280 if (value == 0.0) {
3281 exp.append("+00");
3282 } else {
3283 exp.append(fd.getExponent());
3284 }
3285
3286 int newW = width;
3287 if (width != -1)
3288 newW = adjustWidth(width - exp.length() - 1, f, neg);
3289 localizedMagnitude(sb, mant, 0, f, newW, l);
3290
3291 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3292
3293 Flags flags = f.dup().remove(Flags.GROUP);
3294 char sign = exp.charAt(0);
3295 assert(sign == '+' || sign == '-');
3296 sb.append(sign);
3297
3298 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3299 } else if (c == Conversion.DECIMAL_FLOAT) {
3300 // Create a new FormattedFloatingDecimal with the desired
3301 // precision.
3302 int prec = (precision == -1 ? 6 : precision);
3303
3304 FormattedFloatingDecimal fd
3305 = FormattedFloatingDecimal.valueOf(value, prec,
3306 FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3307
3308 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3309 addZeros(mant, prec);
3310
3311 // If the precision is zero and the '#' flag is set, add the
3312 // requested decimal point.
3313 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3314 mant.append('.');
3315
3316 int newW = width;
3317 if (width != -1)
3318 newW = adjustWidth(width, f, neg);
3319 localizedMagnitude(sb, mant, 0, f, newW, l);
3320 } else if (c == Conversion.GENERAL) {
3321 int prec = precision;
3322 if (precision == -1)
3323 prec = 6;
3324 else if (precision == 0)
3325 prec = 1;
3326
3327 StringBuilder exp;
3328 StringBuilder mant = new StringBuilder();
3329 int expRounded;
3330 if (value == 0.0) {
3331 exp = null;
3332 mant.append('0');
3333 expRounded = 0;
3334 } else {
3335 FormattedFloatingDecimal fd
3336 = FormattedFloatingDecimal.valueOf(value, prec,
3337 FormattedFloatingDecimal.Form.GENERAL);
3338 char[] expArray = fd.getExponent();
3339 if (expArray != null) {
3340 exp = new StringBuilder().append(expArray);
3341 } else {
3342 exp = null;
3343 }
3344 mant.append(fd.getMantissa());
3345 expRounded = fd.getExponentRounded();
3346 }
3347
3348 if (exp != null) {
3349 prec -= 1;
3350 } else {
3351 prec -= expRounded + 1;
3352 }
3353
3354 addZeros(mant, prec);
3355 // If the precision is zero and the '#' flag is set, add the
3356 // requested decimal point.
3357 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3358 mant.append('.');
3359 }
3360
3361 int newW = width;
3362 if (width != -1) {
3363 if (exp != null)
3364 newW = adjustWidth(width - exp.length() - 1, f, neg);
3365 else
3366 newW = adjustWidth(width, f, neg);
3367 }
3368 localizedMagnitude(sb, mant, 0, f, newW, l);
3369
3370 if (exp != null) {
3371 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3372
3373 Flags flags = f.dup().remove(Flags.GROUP);
3374 char sign = exp.charAt(0);
3375 assert(sign == '+' || sign == '-');
3376 sb.append(sign);
3377
3378 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3379 }
3380 } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3381 int prec = precision;
3382 if (precision == -1)
3383 // assume that we want all of the digits
3384 prec = 0;
3385 else if (precision == 0)
3386 prec = 1;
3387
3388 String s = hexDouble(value, prec);
3389
3390 StringBuilder va = new StringBuilder();
3391 boolean upper = f.contains(Flags.UPPERCASE);
3392 sb.append(upper ? "0X" : "0x");
3393
3394 if (f.contains(Flags.ZERO_PAD)) {
3395 trailingZeros(sb, width - s.length() - 2);
3396 }
3397
3398 int idx = s.indexOf('p');
3399 if (upper) {
3400 String tmp = s.substring(0, idx);
3401 // don't localize hex
3402 tmp = tmp.toUpperCase(Locale.US);
3403 va.append(tmp);
3404 } else {
3405 va.append(s, 0, idx);
3406 }
3407 if (prec != 0) {
3408 addZeros(va, prec);
3409 }
3410 sb.append(va);
3411 sb.append(upper ? 'P' : 'p');
3412 sb.append(s, idx+1, s.length());
3413 }
3414 }
3415
3416 // Add zeros to the requested precision.
3417 private void addZeros(StringBuilder sb, int prec) {
3418 // Look for the dot. If we don't find one, the we'll need to add
3419 // it before we add the zeros.
3420 int len = sb.length();
3421 int i;
3422 for (i = 0; i < len; i++) {
3423 if (sb.charAt(i) == '.') {
3424 break;
3425 }
3426 }
3427 boolean needDot = false;
3428 if (i == len) {
3429 needDot = true;
3430 }
3431
3432 // Determine existing precision.
3433 int outPrec = len - i - (needDot ? 0 : 1);
3434 assert (outPrec <= prec);
3435 if (outPrec == prec) {
3436 return;
3437 }
3438
3439 // Add dot if previously determined to be necessary.
3440 if (needDot) {
3441 sb.append('.');
3442 }
3443
3444 // Add zeros.
3445 trailingZeros(sb, prec - outPrec);
3446 }
3447
3448 // Method assumes that d > 0.
3449 private String hexDouble(double d, int prec) {
3450 // Let Double.toHexString handle simple cases
3451 if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3452 // remove "0x"
3453 return Double.toHexString(d).substring(2);
3454 } else {
3455 assert(prec >= 1 && prec <= 12);
3456
3457 int exponent = Math.getExponent(d);
3458 boolean subnormal
3459 = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3460
3461 // If this is subnormal input so normalize (could be faster to
3462 // do as integer operation).
3463 if (subnormal) {
3464 scaleUp = Math.scalb(1.0, 54);
3465 d *= scaleUp;
3466 // Calculate the exponent. This is not just exponent + 54
3467 // since the former is not the normalized exponent.
3468 exponent = Math.getExponent(d);
3469 assert exponent >= DoubleConsts.MIN_EXPONENT &&
3470 exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3471 }
3472
3473 int precision = 1 + prec*4;
3474 int shiftDistance
3527 }
3528 }
3529 }
3530
3531 private void print(BigDecimal value, Locale l) throws IOException {
3532 if (c == Conversion.HEXADECIMAL_FLOAT)
3533 failConversion(c, value);
3534 StringBuilder sb = new StringBuilder();
3535 boolean neg = value.signum() == -1;
3536 BigDecimal v = value.abs();
3537 // leading sign indicator
3538 leadingSign(sb, neg);
3539
3540 // the value
3541 print(sb, v, l, f, c, precision, neg);
3542
3543 // trailing sign indicator
3544 trailingSign(sb, neg);
3545
3546 // justify based on width
3547 appendJustified(a, sb);
3548 }
3549
3550 // value > 0
3551 private void print(StringBuilder sb, BigDecimal value, Locale l,
3552 Flags f, char c, int precision, boolean neg)
3553 throws IOException
3554 {
3555 if (c == Conversion.SCIENTIFIC) {
3556 // Create a new BigDecimal with the desired precision.
3557 int prec = (precision == -1 ? 6 : precision);
3558 int scale = value.scale();
3559 int origPrec = value.precision();
3560 int nzeros = 0;
3561 int compPrec;
3562
3563 if (prec > origPrec - 1) {
3564 compPrec = origPrec;
3565 nzeros = prec - (origPrec - 1);
3566 } else {
3567 compPrec = prec + 1;
3568 }
3569
3570 MathContext mc = new MathContext(compPrec);
3571 BigDecimal v
3572 = new BigDecimal(value.unscaledValue(), scale, mc);
3573
3574 BigDecimalLayout bdl
3575 = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3576 BigDecimalLayoutForm.SCIENTIFIC);
3577
3578 StringBuilder mant = bdl.mantissa();
3579
3580 // Add a decimal point if necessary. The mantissa may not
3581 // contain a decimal point if the scale is zero (the internal
3582 // representation has no fractional part) or the original
3583 // precision is one. Append a decimal point if '#' is set or if
3584 // we require zero padding to get to the requested precision.
3585 if ((origPrec == 1 || !bdl.hasDot())
3586 && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3587 mant.append('.');
3588 }
3589
3590 // Add trailing zeros in the case precision is greater than
3591 // the number of available digits after the decimal separator.
3592 trailingZeros(mant, nzeros);
3593
3594 StringBuilder exp = bdl.exponent();
3595 int newW = width;
3596 if (width != -1) {
3597 newW = adjustWidth(width - exp.length() - 1, f, neg);
3598 }
3599 localizedMagnitude(sb, mant, 0, f, newW, l);
3600
3601 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3602
3603 Flags flags = f.dup().remove(Flags.GROUP);
3604 char sign = exp.charAt(0);
3605 assert(sign == '+' || sign == '-');
3606 sb.append(sign);
3607
3608 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3609 } else if (c == Conversion.DECIMAL_FLOAT) {
3610 // Create a new BigDecimal with the desired precision.
3611 int prec = (precision == -1 ? 6 : precision);
3612 int scale = value.scale();
3613
3614 if (scale > prec) {
3615 // more "scale" digits than the requested "precision"
3616 int compPrec = value.precision();
3617 if (compPrec <= scale) {
3618 // case of 0.xxxxxx
3619 value = value.setScale(prec, RoundingMode.HALF_UP);
3620 } else {
3621 compPrec -= (scale - prec);
3622 value = new BigDecimal(value.unscaledValue(),
3623 scale,
3624 new MathContext(compPrec));
3625 }
3626 }
3627 BigDecimalLayout bdl = new BigDecimalLayout(
3628 value.unscaledValue(),
3629 value.scale(),
3630 BigDecimalLayoutForm.DECIMAL_FLOAT);
3631
3632 StringBuilder mant = bdl.mantissa();
3633 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3634
3635 // Add a decimal point if necessary. The mantissa may not
3636 // contain a decimal point if the scale is zero (the internal
3637 // representation has no fractional part). Append a decimal
3638 // point if '#' is set or we require zero padding to get to the
3639 // requested precision.
3640 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3641 || nzeros > 0)) {
3642 mant.append('.');
3643 }
3644
3645 // Add trailing zeros if the precision is greater than the
3646 // number of available digits after the decimal separator.
3647 trailingZeros(mant, nzeros);
3648
3649 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3650 } else if (c == Conversion.GENERAL) {
3651 int prec = precision;
3652 if (precision == -1)
3653 prec = 6;
3654 else if (precision == 0)
3655 prec = 1;
3656
3657 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3658 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3659 if ((value.equals(BigDecimal.ZERO))
3660 || ((value.compareTo(tenToTheNegFour) != -1)
3661 && (value.compareTo(tenToThePrec) == -1))) {
3662
3663 int e = - value.scale()
3664 + (value.unscaledValue().toString().length() - 1);
3665
3666 // xxx.yyy
3667 // g precision (# sig digits) = #x + #y
3668 // f precision = #y
3669 // exponent = #x - 1
3688 }
3689
3690 private class BigDecimalLayout {
3691 private StringBuilder mant;
3692 private StringBuilder exp;
3693 private boolean dot = false;
3694 private int scale;
3695
3696 public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3697 layout(intVal, scale, form);
3698 }
3699
3700 public boolean hasDot() {
3701 return dot;
3702 }
3703
3704 public int scale() {
3705 return scale;
3706 }
3707
3708 public StringBuilder mantissa() {
3709 return mant;
3710 }
3711
3712 // The exponent will be formatted as a sign ('+' or '-') followed
3713 // by the exponent zero-padded to include at least two digits.
3714 public StringBuilder exponent() {
3715 return exp;
3716 }
3717
3718 private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3719 String coeff = intVal.toString();
3720 this.scale = scale;
3721
3722 // Construct a buffer, with sufficient capacity for all cases.
3723 // If E-notation is needed, length will be: +1 if negative, +1
3724 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3725 // exponent. Otherwise it could have +1 if negative, plus
3726 // leading "0.00000"
3727 int len = coeff.length();
3728 mant = new StringBuilder(len + 14);
3729
3730 if (scale == 0) {
3731 if (len > 1) {
3732 mant.append(coeff.charAt(0));
3733 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3734 mant.append('.');
3735 dot = true;
3736 mant.append(coeff, 1, len);
3737 exp = new StringBuilder("+");
3738 if (len < 10) {
3739 exp.append('0').append(len - 1);
3740 } else {
3741 exp.append(len - 1);
3742 }
3743 } else {
3744 mant.append(coeff, 1, len);
3745 }
3746 } else {
3747 mant.append(coeff);
3748 if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3749 exp = new StringBuilder("+00");
3750 }
3751 }
3752 return;
3753 }
3754 long adjusted = -(long) scale + (len - 1);
3755 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3756 // count of padding zeros
3757 int pad = scale - len;
3758 if (pad >= 0) {
3759 // 0.xxx form
3760 mant.append("0.");
3761 dot = true;
3762 trailingZeros(mant, pad);
3763 mant.append(coeff);
3764 } else {
3765 if (-pad < len) {
3766 // xx.xx form
3767 mant.append(coeff, 0, -pad);
3768 mant.append('.');
3769 dot = true;
3770 mant.append(coeff, -pad, -pad + scale);
3771 } else {
3772 // xx form
3773 mant.append(coeff, 0, len);
3774 trailingZeros(mant, -scale);
3775 this.scale = 0;
3776 }
3777 }
3778 } else {
3779 // x.xxx form
3780 mant.append(coeff.charAt(0));
3781 if (len > 1) {
3782 mant.append('.');
3783 dot = true;
3784 mant.append(coeff, 1, len);
3785 }
3786 exp = new StringBuilder();
3787 if (adjusted != 0) {
3788 long abs = Math.abs(adjusted);
3789 // require sign
3790 exp.append(adjusted < 0 ? '-' : '+');
3791 if (abs < 10) {
3792 exp.append('0');
3793 }
3794 exp.append(abs);
3795 } else {
3796 exp.append("+00");
3797 }
3798 }
3799 }
3800 }
3801
3802 private int adjustWidth(int width, Flags f, boolean neg) {
3803 int newW = width;
3804 if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3805 newW--;
3806 return newW;
3807 }
3808
3809 // Add trailing zeros
3810 private void trailingZeros(StringBuilder sb, int nzeros) {
3811 for (int i = 0; i < nzeros; i++) {
3812 sb.append('0');
3813 }
3814 }
3815
3816 private void print(Calendar t, char c, Locale l) throws IOException {
3817 StringBuilder sb = new StringBuilder();
3818 print(sb, t, c, l);
3819
3820 // justify based on width
3821 if (f.contains(Flags.UPPERCASE)) {
3822 appendJustified(a, sb.toString().toUpperCase());
3823 } else {
3824 appendJustified(a, sb);
3825 }
3826 }
3827
3828 private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3829 throws IOException {
3830 if (sb == null)
3831 sb = new StringBuilder();
3832 switch (c) {
3833 case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3834 case DateTime.HOUR_0: // 'I' (01 - 12)
3835 case DateTime.HOUR_OF_DAY: // 'k' (0 - 23) -- like H
3836 case DateTime.HOUR: { // 'l' (1 - 12) -- like I
3837 int i = t.get(Calendar.HOUR_OF_DAY);
3838 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3839 i = (i == 0 || i == 12 ? 12 : i % 12);
3840 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3841 || c == DateTime.HOUR_0
3842 ? Flags.ZERO_PAD
3843 : Flags.NONE);
3844 sb.append(localizedMagnitude(null, i, flags, 2, l));
3845 break;
3846 }
3847 case DateTime.MINUTE: { // 'M' (00 - 59)
3848 int i = t.get(Calendar.MINUTE);
3849 Flags flags = Flags.ZERO_PAD;
3982 // Composites
3983 case DateTime.TIME: // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
3984 case DateTime.TIME_24_HOUR: { // 'R' (hh:mm same as %H:%M)
3985 char sep = ':';
3986 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
3987 print(sb, t, DateTime.MINUTE, l);
3988 if (c == DateTime.TIME) {
3989 sb.append(sep);
3990 print(sb, t, DateTime.SECOND, l);
3991 }
3992 break;
3993 }
3994 case DateTime.TIME_12_HOUR: { // 'r' (hh:mm:ss [AP]M)
3995 char sep = ':';
3996 print(sb, t, DateTime.HOUR_0, l).append(sep);
3997 print(sb, t, DateTime.MINUTE, l).append(sep);
3998 print(sb, t, DateTime.SECOND, l).append(' ');
3999 // this may be in wrong place for some locales
4000 StringBuilder tsb = new StringBuilder();
4001 print(tsb, t, DateTime.AM_PM, l);
4002
4003 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4004 break;
4005 }
4006 case DateTime.DATE_TIME: { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4007 char sep = ' ';
4008 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4009 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4010 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4011 print(sb, t, DateTime.TIME, l).append(sep);
4012 print(sb, t, DateTime.ZONE, l).append(sep);
4013 print(sb, t, DateTime.YEAR_4, l);
4014 break;
4015 }
4016 case DateTime.DATE: { // 'D' (mm/dd/yy)
4017 char sep = '/';
4018 print(sb, t, DateTime.MONTH, l).append(sep);
4019 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4020 print(sb, t, DateTime.YEAR_2, l);
4021 break;
4022 }
4023 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4024 char sep = '-';
4025 print(sb, t, DateTime.YEAR_4, l).append(sep);
4026 print(sb, t, DateTime.MONTH, l).append(sep);
4027 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4028 break;
4029 }
4030 default:
4031 assert false;
4032 }
4033 return sb;
4034 }
4035
4036 private void print(TemporalAccessor t, char c, Locale l) throws IOException {
4037 StringBuilder sb = new StringBuilder();
4038 print(sb, t, c, l);
4039 // justify based on width
4040 if (f.contains(Flags.UPPERCASE)) {
4041 appendJustified(a, sb.toString().toUpperCase());
4042 } else {
4043 appendJustified(a, sb);
4044 }
4045 }
4046
4047 private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4048 Locale l) throws IOException {
4049 if (sb == null)
4050 sb = new StringBuilder();
4051 try {
4052 switch (c) {
4053 case DateTime.HOUR_OF_DAY_0: { // 'H' (00 - 23)
4054 int i = t.get(ChronoField.HOUR_OF_DAY);
4055 sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4056 break;
4057 }
4058 case DateTime.HOUR_OF_DAY: { // 'k' (0 - 23) -- like H
4059 int i = t.get(ChronoField.HOUR_OF_DAY);
4060 sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4061 break;
4062 }
4063 case DateTime.HOUR_0: { // 'I' (01 - 12)
4064 int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4272
4273 // -- Methods to support throwing exceptions --
4274
4275 private void failMismatch(Flags f, char c) {
4276 String fs = f.toString();
4277 throw new FormatFlagsConversionMismatchException(fs, c);
4278 }
4279
4280 private void failConversion(char c, Object arg) {
4281 throw new IllegalFormatConversionException(c, arg.getClass());
4282 }
4283
4284 private char getZero(Locale l) {
4285 if ((l != null) && !l.equals(locale())) {
4286 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4287 return dfs.getZeroDigit();
4288 }
4289 return zero;
4290 }
4291
4292 private StringBuilder localizedMagnitude(StringBuilder sb,
4293 long value, Flags f, int width, Locale l) {
4294 return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);
4295 }
4296
4297 private StringBuilder localizedMagnitude(StringBuilder sb,
4298 CharSequence value, final int offset, Flags f, int width,
4299 Locale l) {
4300 if (sb == null) {
4301 sb = new StringBuilder();
4302 }
4303 int begin = sb.length();
4304
4305 char zero = getZero(l);
4306
4307 // determine localized grouping separator and size
4308 char grpSep = '\0';
4309 int grpSize = -1;
4310 char decSep = '\0';
4311
4312 int len = value.length();
4313 int dot = len;
4314 for (int j = offset; j < len; j++) {
4315 if (value.charAt(j) == '.') {
4316 dot = j;
4317 break;
4318 }
4319 }
4320
4321 if (dot < len) {
4322 if (l == null || l.equals(Locale.US)) {
4323 decSep = '.';
4324 } else {
4325 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4326 decSep = dfs.getDecimalSeparator();
4327 }
4328 }
4329
4330 if (f.contains(Flags.GROUP)) {
4331 if (l == null || l.equals(Locale.US)) {
4332 grpSep = ',';
4333 grpSize = 3;
4334 } else {
4335 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4336 grpSep = dfs.getGroupingSeparator();
4337 DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4338 grpSize = df.getGroupingSize();
4339 }
4340 }
4341
4342 // localize the digits inserting group separators as necessary
4343 for (int j = offset; j < len; j++) {
4344 if (j == dot) {
4345 sb.append(decSep);
4346 // no more group separators after the decimal separator
4347 grpSep = '\0';
4348 continue;
4349 }
4350
4351 char c = value.charAt(j);
4352 sb.append((char) ((c - '0') + zero));
4353 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4354 sb.append(grpSep);
4355 }
4356 }
4357
4358 // apply zero padding
4359 if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4360 sb.ensureCapacity(width);
4361 for (int k = sb.length(); k < width; k++) {
4362 sb.insert(begin, zero);
4363 }
4364 }
4365
4366 return sb;
4367 }
4368 }
4369
4370 private static class Flags {
4371 private int flags;
4372
4373 static final Flags NONE = new Flags(0); // ''
4374
4375 // duplicate declarations from Formattable.java
4376 static final Flags LEFT_JUSTIFY = new Flags(1<<0); // '-'
4377 static final Flags UPPERCASE = new Flags(1<<1); // '^'
4378 static final Flags ALTERNATE = new Flags(1<<2); // '#'
4379
4380 // numerics
4381 static final Flags PLUS = new Flags(1<<3); // '+'
4382 static final Flags LEADING_SPACE = new Flags(1<<4); // ' '
4383 static final Flags ZERO_PAD = new Flags(1<<5); // '0'
4384 static final Flags GROUP = new Flags(1<<6); // ','
4396 }
4397
4398 public boolean contains(Flags f) {
4399 return (flags & f.valueOf()) == f.valueOf();
4400 }
4401
4402 public Flags dup() {
4403 return new Flags(flags);
4404 }
4405
4406 private Flags add(Flags f) {
4407 flags |= f.valueOf();
4408 return this;
4409 }
4410
4411 public Flags remove(Flags f) {
4412 flags &= ~f.valueOf();
4413 return this;
4414 }
4415
4416 public static Flags parse(String s, int start, int end) {
4417 Flags f = new Flags(0);
4418 for (int i = start; i < end; i++) {
4419 char c = s.charAt(i);
4420 Flags v = parse(c);
4421 if (f.contains(v))
4422 throw new DuplicateFormatFlagsException(v.toString());
4423 f.add(v);
4424 }
4425 return f;
4426 }
4427
4428 // parse those flags which may be provided by users
4429 private static Flags parse(char c) {
4430 switch (c) {
4431 case '-': return LEFT_JUSTIFY;
4432 case '#': return ALTERNATE;
4433 case '+': return PLUS;
4434 case ' ': return LEADING_SPACE;
4435 case '0': return ZERO_PAD;
4436 case ',': return GROUP;
4437 case '(': return PARENTHESES;
4438 case '<': return PREVIOUS;
4439 default:
|