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