src/share/classes/java/util/Formatter.java

Print this page




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: