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

Print this page
rev 10110 : 8050142: Optimize java.util.Formatter
Reviewed-by:
Contributed-by: claes.redestad@oracle.com


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);


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 
2947         private void checkGeneral() {
2948             if ((c == Conversion.BOOLEAN || c == Conversion.HASHCODE)


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);


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             a.append(justify(s));
2920         }
2921 
2922         private String justify(String s) {
2923             if (width == -1) {
2924                 return s;
2925             }
2926             boolean pad = f.contains(Flags.LEFT_JUSTIFY);
2927             int sp = width - s.length();
2928             if (sp <= 0) {
2929                 return s;
2930             }
2931             StringBuilder sb = new StringBuilder();
2932             if (!pad) {
2933                 for (int i = 0; i < sp; i++) {
2934                     sb.append(' ');
2935                 }
2936             }
2937             sb.append(s);
2938             if (pad) {
2939                 for (int i = 0; i < sp; i++) {
2940                     sb.append(' ');
2941                 }
2942             }
2943             return sb.toString();
2944         }
2945 
2946         // mutates sb
2947         private StringBuilder justify(StringBuilder sb) {
2948             if (width == -1) {
2949                 return sb;
2950             }
2951             boolean pad = f.contains(Flags.LEFT_JUSTIFY);
2952             int sp = width - sb.length();
2953             if (sp <= 0) {
2954                 return sb;
2955             } else if (!pad) {
2956                 StringBuilder tmp = new StringBuilder(sp);
2957                 for (int i = 0; i < sp; i++) {
2958                     tmp.append(' ');
2959                 }
2960                 sb.insert(0, tmp);
2961             } else {
2962                 for (int i = 0; i < sp; i++) {
2963                     sb.append(' ');
2964                 }
2965             }
2966             return sb;
2967         }
2968 
2969         public String toString() {
2970             StringBuilder sb = new StringBuilder("%");
2971             // Flags.UPPERCASE is set internally for legal conversions.
2972             Flags dupf = f.dup().remove(Flags.UPPERCASE);
2973             sb.append(dupf.toString());
2974             if (index > 0)
2975                 sb.append(index).append('$');
2976             if (width != -1)
2977                 sb.append(width);
2978             if (precision != -1)
2979                 sb.append('.').append(precision);
2980             if (dt)
2981                 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2982             sb.append(f.contains(Flags.UPPERCASE)
2983                       ? Character.toUpperCase(c) : c);
2984             return sb.toString();
2985         }
2986 
2987         private void checkGeneral() {
2988             if ((c == Conversion.BOOLEAN || c == Conversion.HASHCODE)


3111             print(v, l);
3112         }
3113 
3114         private void print(int value, Locale l) throws IOException {
3115             long v = value;
3116             if (value < 0
3117                 && (c == Conversion.OCTAL_INTEGER
3118                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3119                 v += (1L << 32);
3120                 assert v >= 0 : v;
3121             }
3122             print(v, l);
3123         }
3124 
3125         private void print(long value, Locale l) throws IOException {
3126 
3127             StringBuilder sb = new StringBuilder();
3128 
3129             if (c == Conversion.DECIMAL_INTEGER) {
3130                 boolean neg = value < 0;
3131                 String valueStr;
3132                 if (neg) {
3133                     if (value == Long.MIN_VALUE) {
3134                         valueStr = "9223372036854775808";
3135                     } else {
3136                         valueStr = Long.toString(-value, 10);
3137                     }
3138                 } else {
3139                     valueStr = Long.toString(value, 10);
3140                 }
3141 
3142                 // leading sign indicator
3143                 leadingSign(sb, neg);
3144 
3145                 // the value
3146                 localizedMagnitude(sb, valueStr, 0, f, adjustWidth(width, f, neg), l);
3147 
3148                 // trailing sign indicator
3149                 trailingSign(sb, neg);
3150             } else if (c == Conversion.OCTAL_INTEGER) {
3151                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3152                               Flags.PLUS);
3153                 String s = Long.toOctalString(value);
3154                 int len = (f.contains(Flags.ALTERNATE)
3155                            ? s.length() + 1
3156                            : s.length());
3157 
3158                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3159                 if (f.contains(Flags.ALTERNATE))
3160                     sb.append('0');
3161                 if (f.contains(Flags.ZERO_PAD))
3162                     for (int i = 0; i < width - len; i++) sb.append('0');
3163                 sb.append(s);
3164             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3165                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3166                               Flags.PLUS);
3167                 String s = Long.toHexString(value);
3168                 int len = (f.contains(Flags.ALTERNATE)
3169                            ? s.length() + 2
3170                            : s.length());
3171 
3172                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3173                 if (f.contains(Flags.ALTERNATE))
3174                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3175                 if (f.contains(Flags.ZERO_PAD))
3176                     for (int i = 0; i < width - len; i++) sb.append('0');
3177                 if (f.contains(Flags.UPPERCASE))
3178                     s = s.toUpperCase();
3179                 sb.append(s);
3180             }
3181 
3182             // justify based on width
3183             a.append(justify(sb));
3184         }
3185 
3186         // neg := val < 0
3187         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3188             if (!neg) {
3189                 if (f.contains(Flags.PLUS)) {
3190                     sb.append('+');
3191                 } else if (f.contains(Flags.LEADING_SPACE)) {
3192                     sb.append(' ');
3193                 }
3194             } else {
3195                 if (f.contains(Flags.PARENTHESES))
3196                     sb.append('(');
3197                 else
3198                     sb.append('-');
3199             }
3200             return sb;
3201         }
3202 
3203         // neg := val < 0
3204         private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3205             if (neg && f.contains(Flags.PARENTHESES))
3206                 sb.append(')');
3207             return sb;
3208         }
3209 
3210         private void print(BigInteger value, Locale l) throws IOException {
3211             StringBuilder sb = new StringBuilder();
3212             boolean neg = value.signum() == -1;
3213             BigInteger v = value.abs();
3214 
3215             // leading sign indicator
3216             leadingSign(sb, neg);
3217 
3218             // the value
3219             if (c == Conversion.DECIMAL_INTEGER) {
3220                 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);

3221             } else if (c == Conversion.OCTAL_INTEGER) {
3222                 String s = v.toString(8);
3223 
3224                 int len = s.length() + sb.length();
3225                 if (neg && f.contains(Flags.PARENTHESES))
3226                     len++;
3227 
3228                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3229                 if (f.contains(Flags.ALTERNATE)) {
3230                     len++;
3231                     sb.append('0');
3232                 }
3233                 if (f.contains(Flags.ZERO_PAD)) {
3234                     for (int i = 0; i < width - len; i++)
3235                         sb.append('0');
3236                 }
3237                 sb.append(s);
3238             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3239                 String s = v.toString(16);
3240 


3242                 if (neg && f.contains(Flags.PARENTHESES))
3243                     len++;
3244 
3245                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3246                 if (f.contains(Flags.ALTERNATE)) {
3247                     len += 2;
3248                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3249                 }
3250                 if (f.contains(Flags.ZERO_PAD))
3251                     for (int i = 0; i < width - len; i++)
3252                         sb.append('0');
3253                 if (f.contains(Flags.UPPERCASE))
3254                     s = s.toUpperCase();
3255                 sb.append(s);
3256             }
3257 
3258             // trailing sign indicator
3259             trailingSign(sb, (value.signum() == -1));
3260 
3261             // justify based on width
3262             a.append(justify(sb));
3263         }
3264 
3265         private void print(float value, Locale l) throws IOException {
3266             print((double) value, l);
3267         }
3268 
3269         private void print(double value, Locale l) throws IOException {
3270             StringBuilder sb = new StringBuilder();
3271             boolean neg = Double.compare(value, 0.0) == -1;
3272 
3273             if (!Double.isNaN(value)) {
3274                 double v = Math.abs(value);
3275 
3276                 // leading sign indicator
3277                 leadingSign(sb, neg);
3278 
3279                 // the value
3280                 if (!Double.isInfinite(v))
3281                     print(sb, v, l, f, c, precision, neg);
3282                 else
3283                     sb.append(f.contains(Flags.UPPERCASE)
3284                               ? "INFINITY" : "Infinity");
3285 
3286                 // trailing sign indicator
3287                 trailingSign(sb, neg);
3288             } else {
3289                 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3290             }
3291 
3292             // justify based on width
3293             a.append(justify(sb));
3294         }
3295 
3296         // !Double.isInfinite(value) && !Double.isNaN(value)
3297         private void print(StringBuilder sb, double value, Locale l,
3298                            Flags f, char c, int precision, boolean neg)
3299             throws IOException
3300         {
3301             if (c == Conversion.SCIENTIFIC) {
3302                 // Create a new FormattedFloatingDecimal with the desired
3303                 // precision.
3304                 int prec = (precision == -1 ? 6 : precision);
3305 
3306                 FormattedFloatingDecimal fd
3307                         = FormattedFloatingDecimal.valueOf(value, prec,
3308                           FormattedFloatingDecimal.Form.SCIENTIFIC);
3309 
3310                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3311                 addZeros(mant, prec);
3312 
3313                 // If the precision is zero and the '#' flag is set, add the
3314                 // requested decimal point.
3315                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3316                     mant.append('.');
3317                 }
3318 
3319                 StringBuilder exp = new StringBuilder();
3320                 if (value == 0.0) {
3321                     exp.append("+00");
3322                 } else {
3323                     exp.append(fd.getExponent());
3324                 }
3325 
3326                 int newW = width;
3327                 if (width != -1)
3328                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3329                 localizedMagnitude(sb, mant, 0, f, newW, l);
3330 
3331                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3332 
3333                 Flags flags = f.dup().remove(Flags.GROUP);
3334                 char sign = exp.charAt(0);
3335                 assert(sign == '+' || sign == '-');
3336                 sb.append(sign);
3337 
3338                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3339             } else if (c == Conversion.DECIMAL_FLOAT) {
3340                 // Create a new FormattedFloatingDecimal with the desired
3341                 // precision.
3342                 int prec = (precision == -1 ? 6 : precision);
3343 
3344                 FormattedFloatingDecimal fd
3345                         = FormattedFloatingDecimal.valueOf(value, prec,
3346                           FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3347 
3348                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3349                 addZeros(mant, prec);
3350 
3351                 // If the precision is zero and the '#' flag is set, add the
3352                 // requested decimal point.
3353                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3354                     mant.append('.');
3355 
3356                 int newW = width;
3357                 if (width != -1)
3358                     newW = adjustWidth(width, f, neg);
3359                 localizedMagnitude(sb, mant, 0, f, newW, l);
3360             } else if (c == Conversion.GENERAL) {
3361                 int prec = precision;
3362                 if (precision == -1)
3363                     prec = 6;
3364                 else if (precision == 0)
3365                     prec = 1;
3366 
3367                 StringBuilder exp;
3368                 StringBuilder mant = new StringBuilder();
3369                 int expRounded;
3370                 if (value == 0.0) {
3371                     exp = null;
3372                     mant.append('0');
3373                     expRounded = 0;
3374                 } else {
3375                     FormattedFloatingDecimal fd
3376                         = FormattedFloatingDecimal.valueOf(value, prec,
3377                           FormattedFloatingDecimal.Form.GENERAL);
3378                     char[] expArray = fd.getExponent();
3379                     if (expArray != null) {
3380                         exp = new StringBuilder().append(expArray);
3381                     } else {
3382                         exp = null;
3383                     }
3384                     mant.append(fd.getMantissa());
3385                     expRounded = fd.getExponentRounded();
3386                 }
3387 
3388                 if (exp != null) {
3389                     prec -= 1;
3390                 } else {
3391                     prec -= expRounded + 1;
3392                 }
3393 
3394                 addZeros(mant, prec);
3395                 // If the precision is zero and the '#' flag is set, add the
3396                 // requested decimal point.
3397                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3398                     mant.append('.');
3399                 }
3400 
3401                 int newW = width;
3402                 if (width != -1) {
3403                     if (exp != null)
3404                         newW = adjustWidth(width - exp.length() - 1, f, neg);
3405                     else
3406                         newW = adjustWidth(width, f, neg);
3407                 }
3408                 localizedMagnitude(sb, mant, 0, f, newW, l);
3409 
3410                 if (exp != null) {
3411                     sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3412 
3413                     Flags flags = f.dup().remove(Flags.GROUP);
3414                     char sign = exp.charAt(0);
3415                     assert(sign == '+' || sign == '-');
3416                     sb.append(sign);
3417 
3418                     sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3419                 }
3420             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3421                 int prec = precision;
3422                 if (precision == -1)
3423                     // assume that we want all of the digits
3424                     prec = 0;
3425                 else if (precision == 0)
3426                     prec = 1;
3427 
3428                 String s = hexDouble(value, prec);
3429 
3430                 StringBuilder va = new StringBuilder();
3431                 boolean upper = f.contains(Flags.UPPERCASE);
3432                 sb.append(upper ? "0X" : "0x");
3433 
3434                 if (f.contains(Flags.ZERO_PAD))
3435                     for (int i = 0; i < width - s.length() - 2; i++)
3436                         sb.append('0');
3437 
3438                 int idx = s.indexOf('p');

3439                 if (upper) {
3440                     String tmp = s.substring(0, idx);
3441                     // don't localize hex
3442                     tmp = tmp.toUpperCase(Locale.US);
3443                     va.append(tmp);
3444                 } else {
3445                     va.append(s, 0, idx);
3446                 }
3447                 if (prec != 0) {
3448                     addZeros(va, prec);
3449                 }
3450                 sb.append(va);
3451                 sb.append(upper ? 'P' : 'p');
3452                 sb.append(s, idx+1, s.length());
3453             }
3454         }
3455 
3456         // Add zeros to the requested precision.
3457         private void addZeros(StringBuilder sb, int prec) {
3458             // Look for the dot.  If we don't find one, the we'll need to add
3459             // it before we add the zeros.
3460             int len = sb.length();
3461             int i;
3462             for (i = 0; i < len; i++) {
3463                 if (sb.charAt(i) == '.') {
3464                     break;
3465                 }
3466             }
3467             boolean needDot = false;
3468             if (i == len) {
3469                 needDot = true;
3470             }
3471 
3472             // Determine existing precision.
3473             int outPrec = len - i - (needDot ? 0 : 1);
3474             assert (outPrec <= prec);
3475             if (outPrec == prec) {
3476                 return;
3477             }




3478 
3479             // Add dot if previously determined to be necessary.

3480             if (needDot) {
3481                 sb.append('.');

3482             }
3483 
3484             // Add zeros.
3485             for (int j = outPrec; j < prec; j++) {
3486                 sb.append('0');
3487             }

3488         }
3489 
3490         // Method assumes that d > 0.
3491         private String hexDouble(double d, int prec) {
3492             // Let Double.toHexString handle simple cases
3493             if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3494                 // remove "0x"
3495                 return Double.toHexString(d).substring(2);
3496             } else {
3497                 assert(prec >= 1 && prec <= 12);
3498 
3499                 int exponent  = Math.getExponent(d);
3500                 boolean subnormal
3501                     = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3502 
3503                 // If this is subnormal input so normalize (could be faster to
3504                 // do as integer operation).
3505                 if (subnormal) {
3506                     scaleUp = Math.scalb(1.0, 54);
3507                     d *= scaleUp;
3508                     // Calculate the exponent.  This is not just exponent + 54
3509                     // since the former is not the normalized exponent.
3510                     exponent = Math.getExponent(d);
3511                     assert exponent >= DoubleConsts.MIN_EXPONENT &&
3512                         exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3513                 }
3514 
3515                 int precision = 1 + prec*4;
3516                 int shiftDistance


3569                 }
3570             }
3571         }
3572 
3573         private void print(BigDecimal value, Locale l) throws IOException {
3574             if (c == Conversion.HEXADECIMAL_FLOAT)
3575                 failConversion(c, value);
3576             StringBuilder sb = new StringBuilder();
3577             boolean neg = value.signum() == -1;
3578             BigDecimal v = value.abs();
3579             // leading sign indicator
3580             leadingSign(sb, neg);
3581 
3582             // the value
3583             print(sb, v, l, f, c, precision, neg);
3584 
3585             // trailing sign indicator
3586             trailingSign(sb, neg);
3587 
3588             // justify based on width
3589             a.append(justify(sb));
3590         }
3591 
3592         // value > 0
3593         private void print(StringBuilder sb, BigDecimal value, Locale l,
3594                            Flags f, char c, int precision, boolean neg)
3595             throws IOException
3596         {
3597             if (c == Conversion.SCIENTIFIC) {
3598                 // Create a new BigDecimal with the desired precision.
3599                 int prec = (precision == -1 ? 6 : precision);
3600                 int scale = value.scale();
3601                 int origPrec = value.precision();
3602                 int nzeros = 0;
3603                 int compPrec;
3604 
3605                 if (prec > origPrec - 1) {
3606                     compPrec = origPrec;
3607                     nzeros = prec - (origPrec - 1);
3608                 } else {
3609                     compPrec = prec + 1;
3610                 }
3611 
3612                 MathContext mc = new MathContext(compPrec);
3613                 BigDecimal v
3614                     = new BigDecimal(value.unscaledValue(), scale, mc);
3615 
3616                 BigDecimalLayout bdl
3617                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3618                                            BigDecimalLayoutForm.SCIENTIFIC);
3619 
3620                 StringBuilder mant = bdl.mantissa();
3621 
3622                 // Add a decimal point if necessary.  The mantissa may not
3623                 // contain a decimal point if the scale is zero (the internal
3624                 // representation has no fractional part) or the original
3625                 // precision is one. Append a decimal point if '#' is set or if
3626                 // we require zero padding to get to the requested precision.
3627                 if ((origPrec == 1 || !bdl.hasDot())
3628                         && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3629                     mant.append('.');
3630                 }
3631 
3632                 // Add trailing zeros in the case precision is greater than
3633                 // the number of available digits after the decimal separator.
3634                 trailingZeros(mant, nzeros);
3635 
3636                 StringBuilder exp = bdl.exponent();
3637                 int newW = width;
3638                 if (width != -1) {
3639                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3640                 }
3641                 localizedMagnitude(sb, mant, 0, f, newW, l);
3642 
3643                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3644 
3645                 Flags flags = f.dup().remove(Flags.GROUP);
3646                 char sign = exp.charAt(0);
3647                 assert(sign == '+' || sign == '-');
3648                 sb.append(sign);
3649 
3650                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3651             } else if (c == Conversion.DECIMAL_FLOAT) {
3652                 // Create a new BigDecimal with the desired precision.
3653                 int prec = (precision == -1 ? 6 : precision);
3654                 int scale = value.scale();
3655 
3656                 if (scale > prec) {
3657                     // more "scale" digits than the requested "precision"
3658                     int compPrec = value.precision();
3659                     if (compPrec <= scale) {
3660                         // case of 0.xxxxxx
3661                         value = value.setScale(prec, RoundingMode.HALF_UP);
3662                     } else {
3663                         compPrec -= (scale - prec);
3664                         value = new BigDecimal(value.unscaledValue(),
3665                                                scale,
3666                                                new MathContext(compPrec));
3667                     }
3668                 }
3669                 BigDecimalLayout bdl = new BigDecimalLayout(
3670                                            value.unscaledValue(),
3671                                            value.scale(),
3672                                            BigDecimalLayoutForm.DECIMAL_FLOAT);
3673 
3674                 StringBuilder mant = bdl.mantissa();
3675                 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3676 
3677                 // Add a decimal point if necessary.  The mantissa may not
3678                 // contain a decimal point if the scale is zero (the internal
3679                 // representation has no fractional part).  Append a decimal
3680                 // point if '#' is set or we require zero padding to get to the
3681                 // requested precision.
3682                 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3683                         || nzeros > 0)) {
3684                     mant.append('.');
3685                 }
3686 
3687                 // Add trailing zeros if the precision is greater than the
3688                 // number of available digits after the decimal separator.
3689                 trailingZeros(mant, nzeros);
3690 
3691                 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3692             } else if (c == Conversion.GENERAL) {
3693                 int prec = precision;
3694                 if (precision == -1)
3695                     prec = 6;
3696                 else if (precision == 0)
3697                     prec = 1;
3698 
3699                 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3700                 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3701                 if ((value.equals(BigDecimal.ZERO))
3702                     || ((value.compareTo(tenToTheNegFour) != -1)
3703                         && (value.compareTo(tenToThePrec) == -1))) {
3704 
3705                     int e = - value.scale()
3706                         + (value.unscaledValue().toString().length() - 1);
3707 
3708                     // xxx.yyy
3709                     //   g precision (# sig digits) = #x + #y
3710                     //   f precision = #y
3711                     //   exponent = #x - 1


3730         }
3731 
3732         private class BigDecimalLayout {
3733             private StringBuilder mant;
3734             private StringBuilder exp;
3735             private boolean dot = false;
3736             private int scale;
3737 
3738             public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3739                 layout(intVal, scale, form);
3740             }
3741 
3742             public boolean hasDot() {
3743                 return dot;
3744             }
3745 
3746             public int scale() {
3747                 return scale;
3748             }
3749 
3750             public StringBuilder mantissa() {
3751                 return mant;










3752             }
3753 
3754             // The exponent will be formatted as a sign ('+' or '-') followed
3755             // by the exponent zero-padded to include at least two digits.
3756             public StringBuilder exponent() {
3757                 return exp;








3758             }
3759 
3760             private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3761                 String coeff = intVal.toString();
3762                 this.scale = scale;
3763 
3764                 // Construct a buffer, with sufficient capacity for all cases.
3765                 // If E-notation is needed, length will be: +1 if negative, +1
3766                 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3767                 // exponent.  Otherwise it could have +1 if negative, plus
3768                 // leading "0.00000"
3769                 int len = coeff.length();
3770                 mant = new StringBuilder(len + 14);
3771 
3772                 if (scale == 0) {

3773                     if (len > 1) {
3774                         mant.append(coeff.charAt(0));
3775                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3776                             mant.append('.');
3777                             dot = true;
3778                             mant.append(coeff, 1, len);
3779                             exp = new StringBuilder("+");
3780                             if (len < 10) {
3781                                 exp.append("0").append(len - 1);
3782                             } else {
3783                                 exp.append(len - 1);
3784                             }
3785                         } else {
3786                             mant.append(coeff, 1, len);
3787                         }
3788                     } else {
3789                         mant.append(coeff);
3790                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3791                             exp = new StringBuilder("+00");
3792                         }
3793                     }
3794                     return;
3795                 }
3796                 long adjusted = -(long) scale + (len - 1);
3797                 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3798                     // count of padding zeros
3799                     int pad = scale - len;
3800                     if (pad >= 0) {
3801                         // 0.xxx form
3802                         mant.append("0.");
3803                         dot = true;
3804                         for (; pad > 0 ; pad--) {
3805                             mant.append('0');
3806                         }
3807                         mant.append(coeff);
3808                     } else {
3809                         if (-pad < len) {
3810                             // xx.xx form
3811                             mant.append(coeff, 0, -pad);
3812                             mant.append('.');
3813                             dot = true;
3814                             mant.append(coeff, -pad, -pad + scale);
3815                         } else {
3816                             // xx form
3817                             mant.append(coeff, 0, len);
3818                             for (int i = 0; i < -scale; i++) {
3819                                 mant.append('0');
3820                             }
3821                             this.scale = 0;
3822                         }
3823                     }
3824                 } else {
3825                     // x.xxx form
3826                     mant.append(coeff.charAt(0));
3827                     if (len > 1) {
3828                         mant.append('.');
3829                         dot = true;
3830                         mant.append(coeff, 1, len);
3831                     }
3832                     exp = new StringBuilder();
3833                     if (adjusted != 0) {
3834                         long abs = Math.abs(adjusted);
3835                         // require sign
3836                         exp.append(adjusted < 0 ? '-' : '+');
3837                         if (abs < 10) {
3838                             exp.append('0');
3839                         }
3840                         exp.append(abs);
3841                     } else {
3842                         exp.append("+00");
3843                     }
3844                 }
3845             }
3846         }
3847 
3848         private int adjustWidth(int width, Flags f, boolean neg) {
3849             int newW = width;
3850             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3851                 newW--;
3852             return newW;
3853         }
3854 









3855         // Add trailing zeros in the case precision is greater than the number
3856         // of available digits after the decimal separator.
3857         private void trailingZeros(StringBuilder mant, int nzeros) {
3858             for (int i = 0; i < nzeros; i++) {
3859                 mant.append('0');




3860             }

3861         }
3862 
3863         private void print(Calendar t, char c, Locale l)  throws IOException {

3864             StringBuilder sb = new StringBuilder();
3865             print(sb, t, c, l);
3866 
3867             // justify based on width
3868             sb = justify(sb);
3869             if (f.contains(Flags.UPPERCASE)) {
3870                 a.append(sb.toString().toUpperCase());
3871             } else {
3872                 a.append(sb);
3873             }
3874         }
3875 
3876         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3877                 throws IOException {


3878             if (sb == null)
3879                 sb = new StringBuilder();
3880             switch (c) {
3881             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3882             case DateTime.HOUR_0:        // 'I' (01 - 12)
3883             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3884             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3885                 int i = t.get(Calendar.HOUR_OF_DAY);
3886                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3887                     i = (i == 0 || i == 12 ? 12 : i % 12);
3888                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3889                                || c == DateTime.HOUR_0
3890                                ? Flags.ZERO_PAD
3891                                : Flags.NONE);
3892                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3893                 break;
3894             }
3895             case DateTime.MINUTE:      { // 'M' (00 - 59)
3896                 int i = t.get(Calendar.MINUTE);
3897                 Flags flags = Flags.ZERO_PAD;


4030             // Composites
4031             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4032             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4033                 char sep = ':';
4034                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4035                 print(sb, t, DateTime.MINUTE, l);
4036                 if (c == DateTime.TIME) {
4037                     sb.append(sep);
4038                     print(sb, t, DateTime.SECOND, l);
4039                 }
4040                 break;
4041             }
4042             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4043                 char sep = ':';
4044                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4045                 print(sb, t, DateTime.MINUTE, l).append(sep);
4046                 print(sb, t, DateTime.SECOND, l).append(' ');
4047                 // this may be in wrong place for some locales
4048                 StringBuilder tsb = new StringBuilder();
4049                 print(tsb, t, DateTime.AM_PM, l);
4050 
4051                 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4052                 break;
4053             }
4054             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4055                 char sep = ' ';
4056                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4057                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4058                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4059                 print(sb, t, DateTime.TIME, l).append(sep);
4060                 print(sb, t, DateTime.ZONE, l).append(sep);
4061                 print(sb, t, DateTime.YEAR_4, l);
4062                 break;
4063             }
4064             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4065                 char sep = '/';
4066                 print(sb, t, DateTime.MONTH, l).append(sep);
4067                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4068                 print(sb, t, DateTime.YEAR_2, l);
4069                 break;
4070             }
4071             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4072                 char sep = '-';
4073                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4074                 print(sb, t, DateTime.MONTH, l).append(sep);
4075                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4076                 break;
4077             }
4078             default:
4079                 assert false;
4080             }
4081             return sb;
4082         }
4083 
4084         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4085             StringBuilder sb = new StringBuilder();
4086             print(sb, t, c, l);
4087             // justify based on width
4088             sb = justify(sb);
4089             if (f.contains(Flags.UPPERCASE)) {
4090                 a.append(sb.toString().toUpperCase());
4091             } else {
4092                 a.append(sb);
4093             }
4094         }
4095 
4096         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4097                                  Locale l) throws IOException {
4098             if (sb == null)
4099                 sb = new StringBuilder();
4100             try {
4101                 switch (c) {
4102                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4103                     int i = t.get(ChronoField.HOUR_OF_DAY);
4104                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4105                     break;
4106                 }
4107                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4108                     int i = t.get(ChronoField.HOUR_OF_DAY);
4109                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4110                     break;
4111                 }
4112                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4113                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);


4321 
4322         // -- Methods to support throwing exceptions --
4323 
4324         private void failMismatch(Flags f, char c) {
4325             String fs = f.toString();
4326             throw new FormatFlagsConversionMismatchException(fs, c);
4327         }
4328 
4329         private void failConversion(char c, Object arg) {
4330             throw new IllegalFormatConversionException(c, arg.getClass());
4331         }
4332 
4333         private char getZero(Locale l) {
4334             if ((l != null) &&  !l.equals(locale())) {
4335                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4336                 return dfs.getZeroDigit();
4337             }
4338             return zero;
4339         }
4340 
4341         private StringBuilder localizedMagnitude(StringBuilder sb,
4342                 long value, Flags f, int width, Locale l) {
4343             return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);



4344         }
4345 
4346         private StringBuilder localizedMagnitude(StringBuilder sb,
4347                 CharSequence value, final int offset, Flags f, int width,
4348                 Locale l) {
4349             if (sb == null) {

4350                 sb = new StringBuilder();
4351             }
4352             int begin = sb.length();
4353 
4354             char zero = getZero(l);
4355 
4356             // determine localized grouping separator and size
4357             char grpSep = '\0';
4358             int  grpSize = -1;
4359             char decSep = '\0';
4360 
4361             int len = value.length();
4362             int dot = len;
4363             for (int j = offset; j < len; j++) {
4364                 if (value.charAt(j) == '.') {
4365                     dot = j;
4366                     break;
4367                 }
4368             }
4369 
4370             if (dot < len) {
4371                 if (l == null || l.equals(Locale.US)) {
4372                     decSep  = '.';
4373                 } else {
4374                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4375                     decSep  = dfs.getDecimalSeparator();
4376                 }
4377             }
4378 
4379             if (f.contains(Flags.GROUP)) {
4380                 if (l == null || l.equals(Locale.US)) {
4381                     grpSep = ',';
4382                     grpSize = 3;
4383                 } else {
4384                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4385                     grpSep = dfs.getGroupingSeparator();
4386                     DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4387                     grpSize = df.getGroupingSize();
4388                 }
4389             }
4390 
4391             // localize the digits inserting group separators as necessary
4392             for (int j = offset; j < len; j++) {
4393                 if (j == dot) {
4394                     sb.append(decSep);
4395                     // no more group separators after the decimal separator
4396                     grpSep = '\0';
4397                     continue;
4398                 }
4399 
4400                 char c = value.charAt(j);
4401                 sb.append((char) ((c - '0') + zero));
4402                 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4403                     sb.append(grpSep);
4404                 }
4405             }
4406 
4407             // apply zero padding
4408             if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4409                 sb.ensureCapacity(width);
4410                 for (int k = sb.length(); k < width; k++) {
4411                     sb.insert(begin, zero);
4412                 }
4413             }
4414 
4415             return sb;
4416         }
4417     }
4418 
4419     private static class Flags {
4420         private int flags;
4421 
4422         static final Flags NONE          = new Flags(0);      // ''
4423 
4424         // duplicate declarations from Formattable.java
4425         static final Flags LEFT_JUSTIFY  = new Flags(1<<0);   // '-'
4426         static final Flags UPPERCASE     = new Flags(1<<1);   // '^'
4427         static final Flags ALTERNATE     = new Flags(1<<2);   // '#'
4428 
4429         // numerics
4430         static final Flags PLUS          = new Flags(1<<3);   // '+'
4431         static final Flags LEADING_SPACE = new Flags(1<<4);   // ' '
4432         static final Flags ZERO_PAD      = new Flags(1<<5);   // '0'
4433         static final Flags GROUP         = new Flags(1<<6);   // ','


4445         }
4446 
4447         public boolean contains(Flags f) {
4448             return (flags & f.valueOf()) == f.valueOf();
4449         }
4450 
4451         public Flags dup() {
4452             return new Flags(flags);
4453         }
4454 
4455         private Flags add(Flags f) {
4456             flags |= f.valueOf();
4457             return this;
4458         }
4459 
4460         public Flags remove(Flags f) {
4461             flags &= ~f.valueOf();
4462             return this;
4463         }
4464 
4465         public static Flags parse(String s, int start, int end) {

4466             Flags f = new Flags(0);
4467             for (int i = start; i < end; i++) {
4468                 char c = s.charAt(i);
4469                 Flags v = parse(c);
4470                 if (f.contains(v))
4471                     throw new DuplicateFormatFlagsException(v.toString());
4472                 f.add(v);
4473             }
4474             return f;
4475         }
4476 
4477         // parse those flags which may be provided by users
4478         private static Flags parse(char c) {
4479             switch (c) {
4480             case '-': return LEFT_JUSTIFY;
4481             case '#': return ALTERNATE;
4482             case '+': return PLUS;
4483             case ' ': return LEADING_SPACE;
4484             case '0': return ZERO_PAD;
4485             case ',': return GROUP;
4486             case '(': return PARENTHESES;
4487             case '<': return PREVIOUS;
4488             default: