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

Print this page
rev 10707 : 8050142: Optimize java.util.Formatter
Reviewed-by: sherman, bchristi, lagergren


2481      *          specifier that is incompatible with the given arguments,
2482      *          insufficient arguments given the format string, or other
2483      *          illegal conditions.  For specification of all possible
2484      *          formatting errors, see the <a href="#detail">Details</a>
2485      *          section of the formatter class specification.
2486      *
2487      * @throws  FormatterClosedException
2488      *          If this formatter has been closed by invoking its {@link
2489      *          #close()} method
2490      *
2491      * @return  This formatter
2492      */
2493     public Formatter format(Locale l, String format, Object ... args) {
2494         ensureOpen();
2495 
2496         // index of last argument referenced
2497         int last = -1;
2498         // last ordinary index
2499         int lasto = -1;
2500 
2501         FormatString[] fsa = parse(format);
2502         for (FormatString fs : fsa) {
2503             int index = fs.index();
2504             try {
2505                 switch (index) {
2506                 case -2:  // fixed string, "%n", or "%%"
2507                     fs.print(null, l);
2508                     break;
2509                 case -1:  // relative index
2510                     if (last < 0 || (args != null && last > args.length - 1))
2511                         throw new MissingFormatArgumentException(fs.toString());
2512                     fs.print((args == null ? null : args[last]), l);
2513                     break;
2514                 case 0:  // ordinary index
2515                     lasto++;
2516                     last = lasto;
2517                     if (args != null && lasto > args.length - 1)
2518                         throw new MissingFormatArgumentException(fs.toString());
2519                     fs.print((args == null ? null : args[lasto]), l);
2520                     break;
2521                 default:  // explicit index


2524                         throw new MissingFormatArgumentException(fs.toString());
2525                     fs.print((args == null ? null : args[last]), l);
2526                     break;
2527                 }
2528             } catch (IOException x) {
2529                 lastException = x;
2530             }
2531         }
2532         return this;
2533     }
2534 
2535     // %[argument_index$][flags][width][.precision][t]conversion
2536     private static final String formatSpecifier
2537         = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
2538 
2539     private static Pattern fsPattern = Pattern.compile(formatSpecifier);
2540 
2541     /**
2542      * Finds format specifiers in the format string.
2543      */
2544     private FormatString[] parse(String s) {
2545         ArrayList<FormatString> al = new ArrayList<>();
2546         Matcher m = fsPattern.matcher(s);
2547         for (int i = 0, len = s.length(); i < len; ) {
2548             if (m.find(i)) {
2549                 // Anything between the start of the string and the beginning
2550                 // of the format specifier is either fixed text or contains
2551                 // an invalid format string.
2552                 if (m.start() != i) {
2553                     // Make sure we didn't miss any invalid format specifiers
2554                     checkText(s, i, m.start());
2555                     // Assume previous characters were fixed text
2556                     al.add(new FixedString(s.substring(i, m.start())));
2557                 }
2558 
2559                 al.add(new FormatSpecifier(m));
2560                 i = m.end();
2561             } else {
2562                 // No more valid format specifiers.  Check for possible invalid
2563                 // format specifiers.
2564                 checkText(s, i, len);
2565                 // The rest of the string is fixed text
2566                 al.add(new FixedString(s.substring(i)));
2567                 break;
2568             }
2569         }
2570         return al.toArray(new FormatString[al.size()]);
2571     }
2572 
2573     private static void checkText(String s, int start, int end) {
2574         for (int i = start; i < end; i++) {
2575             // Any '%' found in the region starts an invalid format specifier.
2576             if (s.charAt(i) == '%') {
2577                 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
2578                 throw new UnknownFormatConversionException(String.valueOf(c));
2579             }
2580         }
2581     }
2582 
2583     private interface FormatString {
2584         int index();
2585         void print(Object arg, Locale l) throws IOException;
2586         String toString();
2587     }
2588 
2589     private class FixedString implements FormatString {
2590         private String s;
2591         FixedString(String s) { this.s = s; }






2592         public int index() { return -2; }
2593         public void print(Object arg, Locale l)
2594             throws IOException { a.append(s); }
2595         public String toString() { return s; }
2596     }
2597 
2598     /**
2599      * Enum for {@code BigDecimal} formatting.
2600      */
2601     public enum BigDecimalLayoutForm {
2602         /**
2603          * Format the {@code BigDecimal} in computerized scientific notation.
2604          */
2605         SCIENTIFIC,
2606 
2607         /**
2608          * Format the {@code BigDecimal} as a decimal number.
2609          */
2610         DECIMAL_FLOAT
2611     };
2612 
2613     private class FormatSpecifier implements FormatString {
2614         private int index = -1;
2615         private Flags f = Flags.NONE;


2618         private boolean dt = false;
2619         private char c;
2620 
2621         private int index(String s) {
2622             if (s != null) {
2623                 try {
2624                     index = Integer.parseInt(s.substring(0, s.length() - 1));
2625                 } catch (NumberFormatException x) {
2626                     assert(false);
2627                 }
2628             } else {
2629                 index = 0;
2630             }
2631             return index;
2632         }
2633 
2634         public int index() {
2635             return index;
2636         }
2637 
2638         private Flags flags(String s) {
2639             f = Flags.parse(s);
2640             if (f.contains(Flags.PREVIOUS))
2641                 index = -1;
2642             return f;
2643         }
2644 
2645         Flags flags() {
2646             return f;
2647         }
2648 
2649         private int width(String s) {
2650             width = -1;
2651             if (s != null) {
2652                 try {
2653                     width  = Integer.parseInt(s);
2654                     if (width < 0)
2655                         throw new IllegalFormatWidthException(width);
2656                 } catch (NumberFormatException x) {
2657                     assert(false);
2658                 }
2659             }
2660             return width;
2661         }
2662 
2663         int width() {
2664             return width;
2665         }
2666 
2667         private int precision(String s) {
2668             precision = -1;
2669             if (s != null) {
2670                 try {
2671                     // remove the '.'
2672                     precision = Integer.parseInt(s.substring(1));
2673                     if (precision < 0)
2674                         throw new IllegalFormatPrecisionException(precision);
2675                 } catch (NumberFormatException x) {
2676                     assert(false);
2677                 }
2678             }
2679             return precision;
2680         }
2681 
2682         int precision() {
2683             return precision;
2684         }
2685 
2686         private char conversion(String s) {
2687             c = s.charAt(0);
2688             if (!dt) {
2689                 if (!Conversion.isValid(c))
2690                     throw new UnknownFormatConversionException(String.valueOf(c));
2691                 if (Character.isUpperCase(c))

2692                     f.add(Flags.UPPERCASE);
2693                 c = Character.toLowerCase(c);
2694                 if (Conversion.isText(c))

2695                     index = -2;
2696             }
2697             return c;
2698         }
2699 
2700         private char conversion() {
2701             return c;
2702         }
2703 
2704         FormatSpecifier(Matcher m) {
2705             int idx = 1;
2706 
2707             index(m.group(idx++));
2708             flags(m.group(idx++));
2709             width(m.group(idx++));
2710             precision(m.group(idx++));
2711 
2712             String tT = m.group(idx++);
2713             if (tT != null) {

2714                 dt = true;
2715                 if (tT.equals("T"))
2716                     f.add(Flags.UPPERCASE);
2717             }

2718 
2719             conversion(m.group(idx));
2720 
2721             if (dt)
2722                 checkDateTime();
2723             else if (Conversion.isGeneral(c))
2724                 checkGeneral();
2725             else if (Conversion.isCharacter(c))
2726                 checkCharacter();
2727             else if (Conversion.isInteger(c))
2728                 checkInteger();
2729             else if (Conversion.isFloat(c))
2730                 checkFloat();
2731             else if (Conversion.isText(c))
2732                 checkText();
2733             else
2734                 throw new UnknownFormatConversionException(String.valueOf(c));
2735         }
2736 
2737         public void print(Object arg, Locale l) throws IOException {
2738             if (dt) {
2739                 printDateTime(arg, l);


2892                 s = ((arg instanceof Boolean)
2893                      ? ((Boolean)arg).toString()
2894                      : Boolean.toString(true));
2895             else
2896                 s = Boolean.toString(false);
2897             print(s);
2898         }
2899 
2900         private void printHashCode(Object arg) throws IOException {
2901             String s = (arg == null
2902                         ? "null"
2903                         : Integer.toHexString(arg.hashCode()));
2904             print(s);
2905         }
2906 
2907         private void print(String s) throws IOException {
2908             if (precision != -1 && precision < s.length())
2909                 s = s.substring(0, precision);
2910             if (f.contains(Flags.UPPERCASE))
2911                 s = s.toUpperCase();
2912             a.append(justify(s));
2913         }
2914 
2915         private String justify(String s) {
2916             if (width == -1)
2917                 return s;
2918             StringBuilder sb = new StringBuilder();
2919             boolean pad = f.contains(Flags.LEFT_JUSTIFY);
2920             int sp = width - s.length();
2921             if (!pad)
2922                 for (int i = 0; i < sp; i++) sb.append(' ');
2923             sb.append(s);
2924             if (pad)
2925                 for (int i = 0; i < sp; i++) sb.append(' ');
2926             return sb.toString();




2927         }
2928 
2929         public String toString() {
2930             StringBuilder sb = new StringBuilder("%");
2931             // Flags.UPPERCASE is set internally for legal conversions.
2932             Flags dupf = f.dup().remove(Flags.UPPERCASE);
2933             sb.append(dupf.toString());
2934             if (index > 0)
2935                 sb.append(index).append('$');
2936             if (width != -1)
2937                 sb.append(width);
2938             if (precision != -1)
2939                 sb.append('.').append(precision);
2940             if (dt)
2941                 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2942             sb.append(f.contains(Flags.UPPERCASE)
2943                       ? Character.toUpperCase(c) : c);
2944             return sb.toString();
2945         }
2946 


3071             print(v, l);
3072         }
3073 
3074         private void print(int value, Locale l) throws IOException {
3075             long v = value;
3076             if (value < 0
3077                 && (c == Conversion.OCTAL_INTEGER
3078                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3079                 v += (1L << 32);
3080                 assert v >= 0 : v;
3081             }
3082             print(v, l);
3083         }
3084 
3085         private void print(long value, Locale l) throws IOException {
3086 
3087             StringBuilder sb = new StringBuilder();
3088 
3089             if (c == Conversion.DECIMAL_INTEGER) {
3090                 boolean neg = value < 0;
3091                 char[] va;
3092                 if (value < 0)
3093                     va = Long.toString(value, 10).substring(1).toCharArray();
3094                 else
3095                     va = Long.toString(value, 10).toCharArray();
3096 
3097                 // leading sign indicator
3098                 leadingSign(sb, neg);
3099 
3100                 // the value
3101                 localizedMagnitude(sb, va, f, adjustWidth(width, f, neg), l);
3102 
3103                 // trailing sign indicator
3104                 trailingSign(sb, neg);
3105             } else if (c == Conversion.OCTAL_INTEGER) {
3106                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3107                               Flags.PLUS);
3108                 String s = Long.toOctalString(value);
3109                 int len = (f.contains(Flags.ALTERNATE)
3110                            ? s.length() + 1
3111                            : s.length());
3112 
3113                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3114                 if (f.contains(Flags.ALTERNATE))
3115                     sb.append('0');
3116                 if (f.contains(Flags.ZERO_PAD))
3117                     for (int i = 0; i < width - len; i++) sb.append('0');

3118                 sb.append(s);
3119             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3120                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3121                               Flags.PLUS);
3122                 String s = Long.toHexString(value);
3123                 int len = (f.contains(Flags.ALTERNATE)
3124                            ? s.length() + 2
3125                            : s.length());
3126 
3127                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3128                 if (f.contains(Flags.ALTERNATE))
3129                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3130                 if (f.contains(Flags.ZERO_PAD))
3131                     for (int i = 0; i < width - len; i++) sb.append('0');

3132                 if (f.contains(Flags.UPPERCASE))
3133                     s = s.toUpperCase();
3134                 sb.append(s);
3135             }
3136 
3137             // justify based on width
3138             a.append(justify(sb.toString()));
3139         }
3140 
3141         // neg := val < 0
3142         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3143             if (!neg) {
3144                 if (f.contains(Flags.PLUS)) {
3145                     sb.append('+');
3146                 } else if (f.contains(Flags.LEADING_SPACE)) {
3147                     sb.append(' ');
3148                 }
3149             } else {
3150                 if (f.contains(Flags.PARENTHESES))
3151                     sb.append('(');
3152                 else
3153                     sb.append('-');
3154             }
3155             return sb;
3156         }
3157 
3158         // neg := val < 0
3159         private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3160             if (neg && f.contains(Flags.PARENTHESES))
3161                 sb.append(')');
3162             return sb;
3163         }
3164 
3165         private void print(BigInteger value, Locale l) throws IOException {
3166             StringBuilder sb = new StringBuilder();
3167             boolean neg = value.signum() == -1;
3168             BigInteger v = value.abs();
3169 
3170             // leading sign indicator
3171             leadingSign(sb, neg);
3172 
3173             // the value
3174             if (c == Conversion.DECIMAL_INTEGER) {
3175                 char[] va = v.toString().toCharArray();
3176                 localizedMagnitude(sb, va, f, adjustWidth(width, f, neg), l);
3177             } else if (c == Conversion.OCTAL_INTEGER) {
3178                 String s = v.toString(8);
3179 
3180                 int len = s.length() + sb.length();
3181                 if (neg && f.contains(Flags.PARENTHESES))
3182                     len++;
3183 
3184                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3185                 if (f.contains(Flags.ALTERNATE)) {
3186                     len++;
3187                     sb.append('0');
3188                 }
3189                 if (f.contains(Flags.ZERO_PAD)) {
3190                     for (int i = 0; i < width - len; i++)
3191                         sb.append('0');
3192                 }
3193                 sb.append(s);
3194             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3195                 String s = v.toString(16);
3196 
3197                 int len = s.length() + sb.length();
3198                 if (neg && f.contains(Flags.PARENTHESES))
3199                     len++;
3200 
3201                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3202                 if (f.contains(Flags.ALTERNATE)) {
3203                     len += 2;
3204                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3205                 }
3206                 if (f.contains(Flags.ZERO_PAD))
3207                     for (int i = 0; i < width - len; i++)
3208                         sb.append('0');
3209                 if (f.contains(Flags.UPPERCASE))
3210                     s = s.toUpperCase();
3211                 sb.append(s);
3212             }
3213 
3214             // trailing sign indicator
3215             trailingSign(sb, (value.signum() == -1));
3216 
3217             // justify based on width
3218             a.append(justify(sb.toString()));
3219         }
3220 
3221         private void print(float value, Locale l) throws IOException {
3222             print((double) value, l);
3223         }
3224 
3225         private void print(double value, Locale l) throws IOException {
3226             StringBuilder sb = new StringBuilder();
3227             boolean neg = Double.compare(value, 0.0) == -1;
3228 
3229             if (!Double.isNaN(value)) {
3230                 double v = Math.abs(value);
3231 
3232                 // leading sign indicator
3233                 leadingSign(sb, neg);
3234 
3235                 // the value
3236                 if (!Double.isInfinite(v))
3237                     print(sb, v, l, f, c, precision, neg);
3238                 else
3239                     sb.append(f.contains(Flags.UPPERCASE)
3240                               ? "INFINITY" : "Infinity");
3241 
3242                 // trailing sign indicator
3243                 trailingSign(sb, neg);
3244             } else {
3245                 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3246             }
3247 
3248             // justify based on width
3249             a.append(justify(sb.toString()));
3250         }
3251 
3252         // !Double.isInfinite(value) && !Double.isNaN(value)
3253         private void print(StringBuilder sb, double value, Locale l,
3254                            Flags f, char c, int precision, boolean neg)
3255             throws IOException
3256         {
3257             if (c == Conversion.SCIENTIFIC) {
3258                 // Create a new FormattedFloatingDecimal with the desired
3259                 // precision.
3260                 int prec = (precision == -1 ? 6 : precision);
3261 
3262                 FormattedFloatingDecimal fd
3263                         = FormattedFloatingDecimal.valueOf(value, prec,
3264                           FormattedFloatingDecimal.Form.SCIENTIFIC);
3265 
3266                 char[] mant = addZeros(fd.getMantissa(), prec);

3267 
3268                 // If the precision is zero and the '#' flag is set, add the
3269                 // requested decimal point.
3270                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3271                     mant = addDot(mant);

3272 
3273                 char[] exp = (value == 0.0)
3274                     ? new char[] {'+','0','0'} : fd.getExponent();
3275 
3276                 int newW = width;
3277                 if (width != -1)
3278                     newW = adjustWidth(width - exp.length - 1, f, neg);
3279                 localizedMagnitude(sb, mant, f, newW, l);

3280 
3281                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3282 
3283                 Flags flags = f.dup().remove(Flags.GROUP);
3284                 char sign = exp[0];
3285                 assert(sign == '+' || sign == '-');
3286                 sb.append(sign);
3287 
3288                 char[] tmp = new char[exp.length - 1];
3289                 System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3290                 sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3291             } else if (c == Conversion.DECIMAL_FLOAT) {
3292                 // Create a new FormattedFloatingDecimal with the desired
3293                 // precision.
3294                 int prec = (precision == -1 ? 6 : precision);
3295 
3296                 FormattedFloatingDecimal fd
3297                         = FormattedFloatingDecimal.valueOf(value, prec,
3298                           FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3299 
3300                 char[] mant = addZeros(fd.getMantissa(), prec);

3301 
3302                 // If the precision is zero and the '#' flag is set, add the
3303                 // requested decimal point.
3304                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3305                     mant = addDot(mant);
3306 
3307                 int newW = width;
3308                 if (width != -1)
3309                     newW = adjustWidth(width, f, neg);
3310                 localizedMagnitude(sb, mant, f, newW, l);
3311             } else if (c == Conversion.GENERAL) {
3312                 int prec = precision;
3313                 if (precision == -1)
3314                     prec = 6;
3315                 else if (precision == 0)
3316                     prec = 1;
3317 
3318                 char[] exp;
3319                 char[] mant;
3320                 int expRounded;
3321                 if (value == 0.0) {
3322                     exp = null;
3323                     mant = new char[] {'0'};
3324                     expRounded = 0;
3325                 } else {
3326                     FormattedFloatingDecimal fd
3327                         = FormattedFloatingDecimal.valueOf(value, prec,
3328                           FormattedFloatingDecimal.Form.GENERAL);
3329                     exp = fd.getExponent();
3330                     mant = fd.getMantissa();
3331                     expRounded = fd.getExponentRounded();
3332                 }
3333 
3334                 if (exp != null) {
3335                     prec -= 1;
3336                 } else {
3337                     prec -= expRounded + 1;
3338                 }
3339 
3340                 mant = addZeros(mant, prec);
3341                 // If the precision is zero and the '#' flag is set, add the
3342                 // requested decimal point.
3343                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3344                     mant = addDot(mant);

3345 
3346                 int newW = width;
3347                 if (width != -1) {
3348                     if (exp != null)
3349                         newW = adjustWidth(width - exp.length - 1, f, neg);
3350                     else
3351                         newW = adjustWidth(width, f, neg);
3352                 }
3353                 localizedMagnitude(sb, mant, f, newW, l);
3354 
3355                 if (exp != null) {
3356                     sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3357 
3358                     Flags flags = f.dup().remove(Flags.GROUP);
3359                     char sign = exp[0];
3360                     assert(sign == '+' || sign == '-');
3361                     sb.append(sign);
3362 
3363                     char[] tmp = new char[exp.length - 1];
3364                     System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3365                     sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3366                 }
3367             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3368                 int prec = precision;
3369                 if (precision == -1)
3370                     // assume that we want all of the digits
3371                     prec = 0;
3372                 else if (precision == 0)
3373                     prec = 1;
3374 
3375                 String s = hexDouble(value, prec);
3376 
3377                 char[] va;
3378                 boolean upper = f.contains(Flags.UPPERCASE);
3379                 sb.append(upper ? "0X" : "0x");
3380 
3381                 if (f.contains(Flags.ZERO_PAD))
3382                     for (int i = 0; i < width - s.length() - 2; i++)
3383                         sb.append('0');
3384 
3385                 int idx = s.indexOf('p');
3386                 va = s.substring(0, idx).toCharArray();
3387                 if (upper) {
3388                     String tmp = new String(va);
3389                     // don't localize hex
3390                     tmp = tmp.toUpperCase(Locale.US);
3391                     va = tmp.toCharArray();





3392                 }
3393                 sb.append(prec != 0 ? addZeros(va, prec) : va);
3394                 sb.append(upper ? 'P' : 'p');
3395                 sb.append(s.substring(idx+1));
3396             }
3397         }
3398 
3399         // Add zeros to the requested precision.
3400         private char[] addZeros(char[] v, int prec) {
3401             // Look for the dot.  If we don't find one, the we'll need to add
3402             // it before we add the zeros.

3403             int i;
3404             for (i = 0; i < v.length; i++) {
3405                 if (v[i] == '.')
3406                     break;
3407             }

3408             boolean needDot = false;
3409             if (i == v.length) {
3410                 needDot = true;
3411             }
3412 
3413             // Determine existing precision.
3414             int outPrec = v.length - i - (needDot ? 0 : 1);
3415             assert (outPrec <= prec);
3416             if (outPrec == prec)
3417                 return v;
3418 
3419             // Create new array with existing contents.
3420             char[] tmp
3421                 = new char[v.length + prec - outPrec + (needDot ? 1 : 0)];
3422             System.arraycopy(v, 0, tmp, 0, v.length);
3423 
3424             // Add dot if previously determined to be necessary.
3425             int start = v.length;
3426             if (needDot) {
3427                 tmp[v.length] = '.';
3428                 start++;
3429             }
3430 
3431             // Add zeros.
3432             for (int j = start; j < tmp.length; j++)
3433                 tmp[j] = '0';
3434 
3435             return tmp;
3436         }
3437 
3438         // Method assumes that d > 0.
3439         private String hexDouble(double d, int prec) {
3440             // Let Double.toHexString handle simple cases
3441             if(!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13)
3442                 // remove "0x"
3443                 return Double.toHexString(d).substring(2);
3444             else {
3445                 assert(prec >= 1 && prec <= 12);
3446 
3447                 int exponent  = Math.getExponent(d);
3448                 boolean subnormal
3449                     = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3450 
3451                 // If this is subnormal input so normalize (could be faster to
3452                 // do as integer operation).
3453                 if (subnormal) {
3454                     scaleUp = Math.scalb(1.0, 54);
3455                     d *= scaleUp;
3456                     // Calculate the exponent.  This is not just exponent + 54
3457                     // since the former is not the normalized exponent.
3458                     exponent = Math.getExponent(d);
3459                     assert exponent >= DoubleConsts.MIN_EXPONENT &&
3460                         exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3461                 }
3462 
3463                 int precision = 1 + prec*4;
3464                 int shiftDistance


3517                 }
3518             }
3519         }
3520 
3521         private void print(BigDecimal value, Locale l) throws IOException {
3522             if (c == Conversion.HEXADECIMAL_FLOAT)
3523                 failConversion(c, value);
3524             StringBuilder sb = new StringBuilder();
3525             boolean neg = value.signum() == -1;
3526             BigDecimal v = value.abs();
3527             // leading sign indicator
3528             leadingSign(sb, neg);
3529 
3530             // the value
3531             print(sb, v, l, f, c, precision, neg);
3532 
3533             // trailing sign indicator
3534             trailingSign(sb, neg);
3535 
3536             // justify based on width
3537             a.append(justify(sb.toString()));
3538         }
3539 
3540         // value > 0
3541         private void print(StringBuilder sb, BigDecimal value, Locale l,
3542                            Flags f, char c, int precision, boolean neg)
3543             throws IOException
3544         {
3545             if (c == Conversion.SCIENTIFIC) {
3546                 // Create a new BigDecimal with the desired precision.
3547                 int prec = (precision == -1 ? 6 : precision);
3548                 int scale = value.scale();
3549                 int origPrec = value.precision();
3550                 int nzeros = 0;
3551                 int compPrec;
3552 
3553                 if (prec > origPrec - 1) {
3554                     compPrec = origPrec;
3555                     nzeros = prec - (origPrec - 1);
3556                 } else {
3557                     compPrec = prec + 1;
3558                 }
3559 
3560                 MathContext mc = new MathContext(compPrec);
3561                 BigDecimal v
3562                     = new BigDecimal(value.unscaledValue(), scale, mc);
3563 
3564                 BigDecimalLayout bdl
3565                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3566                                            BigDecimalLayoutForm.SCIENTIFIC);
3567 
3568                 char[] mant = bdl.mantissa();
3569 
3570                 // Add a decimal point if necessary.  The mantissa may not
3571                 // contain a decimal point if the scale is zero (the internal
3572                 // representation has no fractional part) or the original
3573                 // precision is one. Append a decimal point if '#' is set or if
3574                 // we require zero padding to get to the requested precision.
3575                 if ((origPrec == 1 || !bdl.hasDot())
3576                     && (nzeros > 0 || (f.contains(Flags.ALTERNATE))))
3577                     mant = addDot(mant);

3578 
3579                 // Add trailing zeros in the case precision is greater than
3580                 // the number of available digits after the decimal separator.
3581                 mant = trailingZeros(mant, nzeros);
3582 
3583                 char[] exp = bdl.exponent();
3584                 int newW = width;
3585                 if (width != -1)
3586                     newW = adjustWidth(width - exp.length - 1, f, neg);
3587                 localizedMagnitude(sb, mant, f, newW, l);

3588 
3589                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3590 
3591                 Flags flags = f.dup().remove(Flags.GROUP);
3592                 char sign = exp[0];
3593                 assert(sign == '+' || sign == '-');
3594                 sb.append(exp[0]);
3595 
3596                 char[] tmp = new char[exp.length - 1];
3597                 System.arraycopy(exp, 1, tmp, 0, exp.length - 1);
3598                 sb.append(localizedMagnitude(null, tmp, flags, -1, l));
3599             } else if (c == Conversion.DECIMAL_FLOAT) {
3600                 // Create a new BigDecimal with the desired precision.
3601                 int prec = (precision == -1 ? 6 : precision);
3602                 int scale = value.scale();
3603 
3604                 if (scale > prec) {
3605                     // more "scale" digits than the requested "precision"
3606                     int compPrec = value.precision();
3607                     if (compPrec <= scale) {
3608                         // case of 0.xxxxxx
3609                         value = value.setScale(prec, RoundingMode.HALF_UP);
3610                     } else {
3611                         compPrec -= (scale - prec);
3612                         value = new BigDecimal(value.unscaledValue(),
3613                                                scale,
3614                                                new MathContext(compPrec));
3615                     }
3616                 }
3617                 BigDecimalLayout bdl = new BigDecimalLayout(
3618                                            value.unscaledValue(),
3619                                            value.scale(),
3620                                            BigDecimalLayoutForm.DECIMAL_FLOAT);
3621 
3622                 char mant[] = bdl.mantissa();
3623                 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3624 
3625                 // Add a decimal point if necessary.  The mantissa may not
3626                 // contain a decimal point if the scale is zero (the internal
3627                 // representation has no fractional part).  Append a decimal
3628                 // point if '#' is set or we require zero padding to get to the
3629                 // requested precision.
3630                 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE) || nzeros > 0))
3631                     mant = addDot(bdl.mantissa());


3632 
3633                 // Add trailing zeros if the precision is greater than the
3634                 // number of available digits after the decimal separator.
3635                 mant = trailingZeros(mant, nzeros);
3636 
3637                 localizedMagnitude(sb, mant, f, adjustWidth(width, f, neg), l);
3638             } else if (c == Conversion.GENERAL) {
3639                 int prec = precision;
3640                 if (precision == -1)
3641                     prec = 6;
3642                 else if (precision == 0)
3643                     prec = 1;
3644 
3645                 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3646                 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3647                 if ((value.equals(BigDecimal.ZERO))
3648                     || ((value.compareTo(tenToTheNegFour) != -1)
3649                         && (value.compareTo(tenToThePrec) == -1))) {
3650 
3651                     int e = - value.scale()
3652                         + (value.unscaledValue().toString().length() - 1);
3653 
3654                     // xxx.yyy
3655                     //   g precision (# sig digits) = #x + #y
3656                     //   f precision = #y
3657                     //   exponent = #x - 1


3676         }
3677 
3678         private class BigDecimalLayout {
3679             private StringBuilder mant;
3680             private StringBuilder exp;
3681             private boolean dot = false;
3682             private int scale;
3683 
3684             public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3685                 layout(intVal, scale, form);
3686             }
3687 
3688             public boolean hasDot() {
3689                 return dot;
3690             }
3691 
3692             public int scale() {
3693                 return scale;
3694             }
3695 
3696             // char[] with canonical string representation
3697             public char[] layoutChars() {
3698                 StringBuilder sb = new StringBuilder(mant);
3699                 if (exp != null) {
3700                     sb.append('E');
3701                     sb.append(exp);
3702                 }
3703                 return toCharArray(sb);
3704             }
3705 
3706             public char[] mantissa() {
3707                 return toCharArray(mant);
3708             }
3709 
3710             // The exponent will be formatted as a sign ('+' or '-') followed
3711             // by the exponent zero-padded to include at least two digits.
3712             public char[] exponent() {
3713                 return toCharArray(exp);
3714             }
3715 
3716             private char[] toCharArray(StringBuilder sb) {
3717                 if (sb == null)
3718                     return null;
3719                 char[] result = new char[sb.length()];
3720                 sb.getChars(0, result.length, result, 0);
3721                 return result;
3722             }
3723 
3724             private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3725                 char coeff[] = intVal.toString().toCharArray();
3726                 this.scale = scale;
3727 
3728                 // Construct a buffer, with sufficient capacity for all cases.
3729                 // If E-notation is needed, length will be: +1 if negative, +1
3730                 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3731                 // exponent.  Otherwise it could have +1 if negative, plus
3732                 // leading "0.00000"
3733                 mant = new StringBuilder(coeff.length + 14);

3734 
3735                 if (scale == 0) {
3736                     int len = coeff.length;
3737                     if (len > 1) {
3738                         mant.append(coeff[0]);
3739                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3740                             mant.append('.');
3741                             dot = true;
3742                             mant.append(coeff, 1, len - 1);
3743                             exp = new StringBuilder("+");
3744                             if (len < 10)
3745                                 exp.append("0").append(len - 1);
3746                             else
3747                                 exp.append(len - 1);

3748                         } else {
3749                             mant.append(coeff, 1, len - 1);
3750                         }
3751                     } else {
3752                         mant.append(coeff);
3753                         if (form == BigDecimalLayoutForm.SCIENTIFIC)
3754                             exp = new StringBuilder("+00");
3755                     }

3756                     return;
3757                 }
3758                 long adjusted = -(long) scale + (coeff.length - 1);
3759                 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3760                     // count of padding zeros
3761                     int pad = scale - coeff.length;
3762                     if (pad >= 0) {
3763                         // 0.xxx form
3764                         mant.append("0.");
3765                         dot = true;
3766                         for (; pad > 0 ; pad--) mant.append('0');
3767                         mant.append(coeff);
3768                     } else {
3769                         if (-pad < coeff.length) {
3770                             // xx.xx form
3771                             mant.append(coeff, 0, -pad);
3772                             mant.append('.');
3773                             dot = true;
3774                             mant.append(coeff, -pad, scale);
3775                         } else {
3776                             // xx form
3777                             mant.append(coeff, 0, coeff.length);
3778                             for (int i = 0; i < -scale; i++)
3779                                 mant.append('0');
3780                             this.scale = 0;
3781                         }
3782                     }
3783                 } else {
3784                     // x.xxx form
3785                     mant.append(coeff[0]);
3786                     if (coeff.length > 1) {
3787                         mant.append('.');
3788                         dot = true;
3789                         mant.append(coeff, 1, coeff.length-1);
3790                     }
3791                     exp = new StringBuilder();
3792                     if (adjusted != 0) {
3793                         long abs = Math.abs(adjusted);
3794                         // require sign
3795                         exp.append(adjusted < 0 ? '-' : '+');
3796                         if (abs < 10)
3797                             exp.append('0');

3798                         exp.append(abs);
3799                     } else {
3800                         exp.append("+00");
3801                     }
3802                 }
3803             }
3804         }
3805 
3806         private int adjustWidth(int width, Flags f, boolean neg) {
3807             int newW = width;
3808             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3809                 newW--;
3810             return newW;
3811         }
3812 
3813         // Add a '.' to th mantissa if required
3814         private char[] addDot(char[] mant) {
3815             char[] tmp = mant;
3816             tmp = new char[mant.length + 1];
3817             System.arraycopy(mant, 0, tmp, 0, mant.length);
3818             tmp[tmp.length - 1] = '.';
3819             return tmp;
3820         }
3821 
3822         // Add trailing zeros in the case precision is greater than the number
3823         // of available digits after the decimal separator.
3824         private char[] trailingZeros(char[] mant, int nzeros) {
3825             char[] tmp = mant;
3826             if (nzeros > 0) {
3827                 tmp = new char[mant.length + nzeros];
3828                 System.arraycopy(mant, 0, tmp, 0, mant.length);
3829                 for (int i = mant.length; i < tmp.length; i++)
3830                     tmp[i] = '0';
3831             }
3832             return tmp;
3833         }
3834 
3835         private void print(Calendar t, char c, Locale l)  throws IOException
3836         {
3837             StringBuilder sb = new StringBuilder();
3838             print(sb, t, c, l);
3839 
3840             // justify based on width
3841             String s = justify(sb.toString());
3842             if (f.contains(Flags.UPPERCASE))
3843                 s = s.toUpperCase();
3844 
3845             a.append(s);
3846         }
3847 
3848         private Appendable print(StringBuilder sb, Calendar t, char c,
3849                                  Locale l)
3850             throws IOException
3851         {
3852             if (sb == null)
3853                 sb = new StringBuilder();
3854             switch (c) {
3855             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3856             case DateTime.HOUR_0:        // 'I' (01 - 12)
3857             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3858             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3859                 int i = t.get(Calendar.HOUR_OF_DAY);
3860                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3861                     i = (i == 0 || i == 12 ? 12 : i % 12);
3862                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3863                                || c == DateTime.HOUR_0
3864                                ? Flags.ZERO_PAD
3865                                : Flags.NONE);
3866                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3867                 break;
3868             }
3869             case DateTime.MINUTE:      { // 'M' (00 - 59)
3870                 int i = t.get(Calendar.MINUTE);
3871                 Flags flags = Flags.ZERO_PAD;


4004             // Composites
4005             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4006             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4007                 char sep = ':';
4008                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4009                 print(sb, t, DateTime.MINUTE, l);
4010                 if (c == DateTime.TIME) {
4011                     sb.append(sep);
4012                     print(sb, t, DateTime.SECOND, l);
4013                 }
4014                 break;
4015             }
4016             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4017                 char sep = ':';
4018                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4019                 print(sb, t, DateTime.MINUTE, l).append(sep);
4020                 print(sb, t, DateTime.SECOND, l).append(' ');
4021                 // this may be in wrong place for some locales
4022                 StringBuilder tsb = new StringBuilder();
4023                 print(tsb, t, DateTime.AM_PM, l);

4024                 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4025                 break;
4026             }
4027             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4028                 char sep = ' ';
4029                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4030                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4031                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4032                 print(sb, t, DateTime.TIME, l).append(sep);
4033                 print(sb, t, DateTime.ZONE, l).append(sep);
4034                 print(sb, t, DateTime.YEAR_4, l);
4035                 break;
4036             }
4037             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4038                 char sep = '/';
4039                 print(sb, t, DateTime.MONTH, l).append(sep);
4040                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4041                 print(sb, t, DateTime.YEAR_2, l);
4042                 break;
4043             }
4044             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4045                 char sep = '-';
4046                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4047                 print(sb, t, DateTime.MONTH, l).append(sep);
4048                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4049                 break;
4050             }
4051             default:
4052                 assert false;
4053             }
4054             return sb;
4055         }
4056 
4057         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4058             StringBuilder sb = new StringBuilder();
4059             print(sb, t, c, l);
4060             // justify based on width
4061             String s = justify(sb.toString());
4062             if (f.contains(Flags.UPPERCASE))
4063                 s = s.toUpperCase();
4064             a.append(s);

4065         }
4066 
4067         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4068                                  Locale l) throws IOException {
4069             if (sb == null)
4070                 sb = new StringBuilder();
4071             try {
4072                 switch (c) {
4073                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4074                     int i = t.get(ChronoField.HOUR_OF_DAY);
4075                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4076                     break;
4077                 }
4078                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4079                     int i = t.get(ChronoField.HOUR_OF_DAY);
4080                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4081                     break;
4082                 }
4083                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4084                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);


4292 
4293         // -- Methods to support throwing exceptions --
4294 
4295         private void failMismatch(Flags f, char c) {
4296             String fs = f.toString();
4297             throw new FormatFlagsConversionMismatchException(fs, c);
4298         }
4299 
4300         private void failConversion(char c, Object arg) {
4301             throw new IllegalFormatConversionException(c, arg.getClass());
4302         }
4303 
4304         private char getZero(Locale l) {
4305             if ((l != null) &&  !l.equals(locale())) {
4306                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4307                 return dfs.getZeroDigit();
4308             }
4309             return zero;
4310         }
4311 
4312         private StringBuilder
4313             localizedMagnitude(StringBuilder sb, long value, Flags f,
4314                                int width, Locale l)
4315         {
4316             char[] va = Long.toString(value, 10).toCharArray();
4317             return localizedMagnitude(sb, va, f, width, l);
4318         }
4319 
4320         private StringBuilder
4321             localizedMagnitude(StringBuilder sb, char[] value, Flags f,
4322                                int width, Locale l)
4323         {
4324             if (sb == null)
4325                 sb = new StringBuilder();

4326             int begin = sb.length();
4327 
4328             char zero = getZero(l);
4329 
4330             // determine localized grouping separator and size
4331             char grpSep = '\0';
4332             int  grpSize = -1;
4333             char decSep = '\0';
4334 
4335             int len = value.length;
4336             int dot = len;
4337             for (int j = 0; j < len; j++) {
4338                 if (value[j] == '.') {
4339                     dot = j;
4340                     break;
4341                 }
4342             }
4343 
4344             if (dot < len) {
4345                 if (l == null || l.equals(Locale.US)) {
4346                     decSep  = '.';
4347                 } else {
4348                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4349                     decSep  = dfs.getDecimalSeparator();
4350                 }
4351             }
4352 
4353             if (f.contains(Flags.GROUP)) {
4354                 if (l == null || l.equals(Locale.US)) {
4355                     grpSep = ',';
4356                     grpSize = 3;
4357                 } else {
4358                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4359                     grpSep = dfs.getGroupingSeparator();
4360                     DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4361                     grpSize = df.getGroupingSize();
4362                 }
4363             }
4364 
4365             // localize the digits inserting group separators as necessary
4366             for (int j = 0; j < len; j++) {
4367                 if (j == dot) {
4368                     sb.append(decSep);
4369                     // no more group separators after the decimal separator
4370                     grpSep = '\0';
4371                     continue;
4372                 }
4373 
4374                 char c = value[j];
4375                 sb.append((char) ((c - '0') + zero));
4376                 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1))
4377                     sb.append(grpSep);
4378             }

4379 
4380             // apply zero padding
4381             len = sb.length();
4382             if (width != -1 && f.contains(Flags.ZERO_PAD))
4383                 for (int k = 0; k < width - len; k++)
4384                     sb.insert(begin, zero);


4385 
4386             return sb;
4387         }














4388     }
4389 
4390     private static class Flags {
4391         private int flags;
4392 
4393         static final Flags NONE          = new Flags(0);      // ''
4394 
4395         // duplicate declarations from Formattable.java
4396         static final Flags LEFT_JUSTIFY  = new Flags(1<<0);   // '-'
4397         static final Flags UPPERCASE     = new Flags(1<<1);   // '^'
4398         static final Flags ALTERNATE     = new Flags(1<<2);   // '#'
4399 
4400         // numerics
4401         static final Flags PLUS          = new Flags(1<<3);   // '+'
4402         static final Flags LEADING_SPACE = new Flags(1<<4);   // ' '
4403         static final Flags ZERO_PAD      = new Flags(1<<5);   // '0'
4404         static final Flags GROUP         = new Flags(1<<6);   // ','
4405         static final Flags PARENTHESES   = new Flags(1<<7);   // '('
4406 
4407         // indexing


4416         }
4417 
4418         public boolean contains(Flags f) {
4419             return (flags & f.valueOf()) == f.valueOf();
4420         }
4421 
4422         public Flags dup() {
4423             return new Flags(flags);
4424         }
4425 
4426         private Flags add(Flags f) {
4427             flags |= f.valueOf();
4428             return this;
4429         }
4430 
4431         public Flags remove(Flags f) {
4432             flags &= ~f.valueOf();
4433             return this;
4434         }
4435 
4436         public static Flags parse(String s) {
4437             char[] ca = s.toCharArray();
4438             Flags f = new Flags(0);
4439             for (char c : ca) {

4440                 Flags v = parse(c);
4441                 if (f.contains(v))
4442                     throw new DuplicateFormatFlagsException(v.toString());
4443                 f.add(v);
4444             }
4445             return f;
4446         }
4447 
4448         // parse those flags which may be provided by users
4449         private static Flags parse(char c) {
4450             switch (c) {
4451             case '-': return LEFT_JUSTIFY;
4452             case '#': return ALTERNATE;
4453             case '+': return PLUS;
4454             case ' ': return LEADING_SPACE;
4455             case '0': return ZERO_PAD;
4456             case ',': return GROUP;
4457             case '(': return PARENTHESES;
4458             case '<': return PREVIOUS;
4459             default:




2481      *          specifier that is incompatible with the given arguments,
2482      *          insufficient arguments given the format string, or other
2483      *          illegal conditions.  For specification of all possible
2484      *          formatting errors, see the <a href="#detail">Details</a>
2485      *          section of the formatter class specification.
2486      *
2487      * @throws  FormatterClosedException
2488      *          If this formatter has been closed by invoking its {@link
2489      *          #close()} method
2490      *
2491      * @return  This formatter
2492      */
2493     public Formatter format(Locale l, String format, Object ... args) {
2494         ensureOpen();
2495 
2496         // index of last argument referenced
2497         int last = -1;
2498         // last ordinary index
2499         int lasto = -1;
2500 
2501         List<FormatString> fsa = parse(format);
2502         for (FormatString fs : fsa) {
2503             int index = fs.index();
2504             try {
2505                 switch (index) {
2506                 case -2:  // fixed string, "%n", or "%%"
2507                     fs.print(null, l);
2508                     break;
2509                 case -1:  // relative index
2510                     if (last < 0 || (args != null && last > args.length - 1))
2511                         throw new MissingFormatArgumentException(fs.toString());
2512                     fs.print((args == null ? null : args[last]), l);
2513                     break;
2514                 case 0:  // ordinary index
2515                     lasto++;
2516                     last = lasto;
2517                     if (args != null && lasto > args.length - 1)
2518                         throw new MissingFormatArgumentException(fs.toString());
2519                     fs.print((args == null ? null : args[lasto]), l);
2520                     break;
2521                 default:  // explicit index


2524                         throw new MissingFormatArgumentException(fs.toString());
2525                     fs.print((args == null ? null : args[last]), l);
2526                     break;
2527                 }
2528             } catch (IOException x) {
2529                 lastException = x;
2530             }
2531         }
2532         return this;
2533     }
2534 
2535     // %[argument_index$][flags][width][.precision][t]conversion
2536     private static final String formatSpecifier
2537         = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
2538 
2539     private static Pattern fsPattern = Pattern.compile(formatSpecifier);
2540 
2541     /**
2542      * Finds format specifiers in the format string.
2543      */
2544     private List<FormatString> parse(String s) {
2545         ArrayList<FormatString> al = new ArrayList<>();
2546         Matcher m = fsPattern.matcher(s);
2547         for (int i = 0, len = s.length(); i < len; ) {
2548             if (m.find(i)) {
2549                 // Anything between the start of the string and the beginning
2550                 // of the format specifier is either fixed text or contains
2551                 // an invalid format string.
2552                 if (m.start() != i) {
2553                     // Make sure we didn't miss any invalid format specifiers
2554                     checkText(s, i, m.start());
2555                     // Assume previous characters were fixed text
2556                     al.add(new FixedString(s, i, m.start()));
2557                 }
2558 
2559                 al.add(new FormatSpecifier(s, m));
2560                 i = m.end();
2561             } else {
2562                 // No more valid format specifiers.  Check for possible invalid
2563                 // format specifiers.
2564                 checkText(s, i, len);
2565                 // The rest of the string is fixed text
2566                 al.add(new FixedString(s, i, s.length()));
2567                 break;
2568             }
2569         }
2570         return al;
2571     }
2572 
2573     private static void checkText(String s, int start, int end) {
2574         for (int i = start; i < end; i++) {
2575             // Any '%' found in the region starts an invalid format specifier.
2576             if (s.charAt(i) == '%') {
2577                 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
2578                 throw new UnknownFormatConversionException(String.valueOf(c));
2579             }
2580         }
2581     }
2582 
2583     private interface FormatString {
2584         int index();
2585         void print(Object arg, Locale l) throws IOException;
2586         String toString();
2587     }
2588 
2589     private class FixedString implements FormatString {
2590         private String s;
2591         private int start;
2592         private int end;
2593         FixedString(String s, int start, int end) {
2594             this.s = s;
2595             this.start = start;
2596             this.end = end;
2597         }
2598         public int index() { return -2; }
2599         public void print(Object arg, Locale l)
2600             throws IOException { a.append(s, start, end); }
2601         public String toString() { return s.substring(start, end); }
2602     }
2603 
2604     /**
2605      * Enum for {@code BigDecimal} formatting.
2606      */
2607     public enum BigDecimalLayoutForm {
2608         /**
2609          * Format the {@code BigDecimal} in computerized scientific notation.
2610          */
2611         SCIENTIFIC,
2612 
2613         /**
2614          * Format the {@code BigDecimal} as a decimal number.
2615          */
2616         DECIMAL_FLOAT
2617     };
2618 
2619     private class FormatSpecifier implements FormatString {
2620         private int index = -1;
2621         private Flags f = Flags.NONE;


2624         private boolean dt = false;
2625         private char c;
2626 
2627         private int index(String s) {
2628             if (s != null) {
2629                 try {
2630                     index = Integer.parseInt(s.substring(0, s.length() - 1));
2631                 } catch (NumberFormatException x) {
2632                     assert(false);
2633                 }
2634             } else {
2635                 index = 0;
2636             }
2637             return index;
2638         }
2639 
2640         public int index() {
2641             return index;
2642         }
2643 
2644         private Flags flags(String s, int start, int end) {
2645             f = Flags.parse(s, start, end);
2646             if (f.contains(Flags.PREVIOUS))
2647                 index = -1;
2648             return f;
2649         }
2650 




2651         private int width(String s) {
2652             width = -1;
2653             if (s != null) {
2654                 try {
2655                     width  = Integer.parseInt(s);
2656                     if (width < 0)
2657                         throw new IllegalFormatWidthException(width);
2658                 } catch (NumberFormatException x) {
2659                     assert(false);
2660                 }
2661             }
2662             return width;
2663         }
2664 




2665         private int precision(String s) {
2666             precision = -1;
2667             if (s != null) {
2668                 try {
2669                     // remove the '.'
2670                     precision = Integer.parseInt(s.substring(1));
2671                     if (precision < 0)
2672                         throw new IllegalFormatPrecisionException(precision);
2673                 } catch (NumberFormatException x) {
2674                     assert(false);
2675                 }
2676             }
2677             return precision;
2678         }
2679 
2680         private char conversion(char conv) {
2681             c = conv;




2682             if (!dt) {
2683                 if (!Conversion.isValid(c)) {
2684                     throw new UnknownFormatConversionException(String.valueOf(c));
2685                 }
2686                 if (Character.isUpperCase(c)) {
2687                     f.add(Flags.UPPERCASE);
2688                     c = Character.toLowerCase(c);
2689                 }
2690                 if (Conversion.isText(c)) {
2691                     index = -2;
2692                 }

2693             }


2694             return c;
2695         }
2696 
2697         FormatSpecifier(String s, Matcher m) {
2698             int idx = 1;
2699 
2700             index(m.group(idx++));
2701             flags(s, m.start(idx), m.end(idx++));
2702             width(m.group(idx++));
2703             precision(m.group(idx++));
2704 
2705             int tTStart = m.start(idx);
2706             int tTEnd = m.end(idx++);
2707             if (tTStart != -1 && tTEnd != -1) {
2708                 dt = true;
2709                 if (tTStart == tTEnd - 1 && s.charAt(tTStart) == 'T') {
2710                     f.add(Flags.UPPERCASE);
2711                 }
2712             }
2713 
2714             conversion(s.charAt(m.start(idx)));
2715 
2716             if (dt)
2717                 checkDateTime();
2718             else if (Conversion.isGeneral(c))
2719                 checkGeneral();
2720             else if (Conversion.isCharacter(c))
2721                 checkCharacter();
2722             else if (Conversion.isInteger(c))
2723                 checkInteger();
2724             else if (Conversion.isFloat(c))
2725                 checkFloat();
2726             else if (Conversion.isText(c))
2727                 checkText();
2728             else
2729                 throw new UnknownFormatConversionException(String.valueOf(c));
2730         }
2731 
2732         public void print(Object arg, Locale l) throws IOException {
2733             if (dt) {
2734                 printDateTime(arg, l);


2887                 s = ((arg instanceof Boolean)
2888                      ? ((Boolean)arg).toString()
2889                      : Boolean.toString(true));
2890             else
2891                 s = Boolean.toString(false);
2892             print(s);
2893         }
2894 
2895         private void printHashCode(Object arg) throws IOException {
2896             String s = (arg == null
2897                         ? "null"
2898                         : Integer.toHexString(arg.hashCode()));
2899             print(s);
2900         }
2901 
2902         private void print(String s) throws IOException {
2903             if (precision != -1 && precision < s.length())
2904                 s = s.substring(0, precision);
2905             if (f.contains(Flags.UPPERCASE))
2906                 s = s.toUpperCase();
2907             appendJustified(a, s);
2908         }
2909 
2910         private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
2911              if (width == -1) {
2912                  return a.append(cs);
2913              }
2914              boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
2915              int sp = width - cs.length();
2916              if (padRight) {
2917                  a.append(cs);
2918              }
2919              for (int i = 0; i < sp; i++) {
2920                  a.append(' ');
2921              }
2922              if (!padRight) {
2923                  a.append(cs);
2924              }
2925              return a;
2926         }
2927 
2928         public String toString() {
2929             StringBuilder sb = new StringBuilder("%");
2930             // Flags.UPPERCASE is set internally for legal conversions.
2931             Flags dupf = f.dup().remove(Flags.UPPERCASE);
2932             sb.append(dupf.toString());
2933             if (index > 0)
2934                 sb.append(index).append('$');
2935             if (width != -1)
2936                 sb.append(width);
2937             if (precision != -1)
2938                 sb.append('.').append(precision);
2939             if (dt)
2940                 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2941             sb.append(f.contains(Flags.UPPERCASE)
2942                       ? Character.toUpperCase(c) : c);
2943             return sb.toString();
2944         }
2945 


3070             print(v, l);
3071         }
3072 
3073         private void print(int value, Locale l) throws IOException {
3074             long v = value;
3075             if (value < 0
3076                 && (c == Conversion.OCTAL_INTEGER
3077                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3078                 v += (1L << 32);
3079                 assert v >= 0 : v;
3080             }
3081             print(v, l);
3082         }
3083 
3084         private void print(long value, Locale l) throws IOException {
3085 
3086             StringBuilder sb = new StringBuilder();
3087 
3088             if (c == Conversion.DECIMAL_INTEGER) {
3089                 boolean neg = value < 0;
3090                 String valueStr = Long.toString(value, 10);




3091 
3092                 // leading sign indicator
3093                 leadingSign(sb, neg);
3094 
3095                 // the value
3096                 localizedMagnitude(sb, valueStr, neg ? 1 : 0, f, adjustWidth(width, f, neg), l);
3097 
3098                 // trailing sign indicator
3099                 trailingSign(sb, neg);
3100             } else if (c == Conversion.OCTAL_INTEGER) {
3101                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3102                               Flags.PLUS);
3103                 String s = Long.toOctalString(value);
3104                 int len = (f.contains(Flags.ALTERNATE)
3105                            ? s.length() + 1
3106                            : s.length());
3107 
3108                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3109                 if (f.contains(Flags.ALTERNATE))
3110                     sb.append('0');
3111                 if (f.contains(Flags.ZERO_PAD)) {
3112                     trailingZeros(sb, width - len);
3113                 }
3114                 sb.append(s);
3115             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3116                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3117                               Flags.PLUS);
3118                 String s = Long.toHexString(value);
3119                 int len = (f.contains(Flags.ALTERNATE)
3120                            ? s.length() + 2
3121                            : s.length());
3122 
3123                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3124                 if (f.contains(Flags.ALTERNATE))
3125                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3126                 if (f.contains(Flags.ZERO_PAD)) {
3127                     trailingZeros(sb, width - len);
3128                 }
3129                 if (f.contains(Flags.UPPERCASE))
3130                     s = s.toUpperCase();
3131                 sb.append(s);
3132             }
3133 
3134             // justify based on width
3135             appendJustified(a, sb);
3136         }
3137 
3138         // neg := val < 0
3139         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3140             if (!neg) {
3141                 if (f.contains(Flags.PLUS)) {
3142                     sb.append('+');
3143                 } else if (f.contains(Flags.LEADING_SPACE)) {
3144                     sb.append(' ');
3145                 }
3146             } else {
3147                 if (f.contains(Flags.PARENTHESES))
3148                     sb.append('(');
3149                 else
3150                     sb.append('-');
3151             }
3152             return sb;
3153         }
3154 
3155         // neg := val < 0
3156         private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3157             if (neg && f.contains(Flags.PARENTHESES))
3158                 sb.append(')');
3159             return sb;
3160         }
3161 
3162         private void print(BigInteger value, Locale l) throws IOException {
3163             StringBuilder sb = new StringBuilder();
3164             boolean neg = value.signum() == -1;
3165             BigInteger v = value.abs();
3166 
3167             // leading sign indicator
3168             leadingSign(sb, neg);
3169 
3170             // the value
3171             if (c == Conversion.DECIMAL_INTEGER) {
3172                 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);

3173             } else if (c == Conversion.OCTAL_INTEGER) {
3174                 String s = v.toString(8);
3175 
3176                 int len = s.length() + sb.length();
3177                 if (neg && f.contains(Flags.PARENTHESES))
3178                     len++;
3179 
3180                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3181                 if (f.contains(Flags.ALTERNATE)) {
3182                     len++;
3183                     sb.append('0');
3184                 }
3185                 if (f.contains(Flags.ZERO_PAD)) {
3186                     trailingZeros(sb, width - len);

3187                 }
3188                 sb.append(s);
3189             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3190                 String s = v.toString(16);
3191 
3192                 int len = s.length() + sb.length();
3193                 if (neg && f.contains(Flags.PARENTHESES))
3194                     len++;
3195 
3196                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3197                 if (f.contains(Flags.ALTERNATE)) {
3198                     len += 2;
3199                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3200                 }
3201                 if (f.contains(Flags.ZERO_PAD)) {
3202                     trailingZeros(sb, width - len);
3203                 }
3204                 if (f.contains(Flags.UPPERCASE))
3205                     s = s.toUpperCase();
3206                 sb.append(s);
3207             }
3208 
3209             // trailing sign indicator
3210             trailingSign(sb, (value.signum() == -1));
3211 
3212             // justify based on width
3213             appendJustified(a, sb);
3214         }
3215 
3216         private void print(float value, Locale l) throws IOException {
3217             print((double) value, l);
3218         }
3219 
3220         private void print(double value, Locale l) throws IOException {
3221             StringBuilder sb = new StringBuilder();
3222             boolean neg = Double.compare(value, 0.0) == -1;
3223 
3224             if (!Double.isNaN(value)) {
3225                 double v = Math.abs(value);
3226 
3227                 // leading sign indicator
3228                 leadingSign(sb, neg);
3229 
3230                 // the value
3231                 if (!Double.isInfinite(v))
3232                     print(sb, v, l, f, c, precision, neg);
3233                 else
3234                     sb.append(f.contains(Flags.UPPERCASE)
3235                               ? "INFINITY" : "Infinity");
3236 
3237                 // trailing sign indicator
3238                 trailingSign(sb, neg);
3239             } else {
3240                 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3241             }
3242 
3243             // justify based on width
3244             appendJustified(a, sb);
3245         }
3246 
3247         // !Double.isInfinite(value) && !Double.isNaN(value)
3248         private void print(StringBuilder sb, double value, Locale l,
3249                            Flags f, char c, int precision, boolean neg)
3250             throws IOException
3251         {
3252             if (c == Conversion.SCIENTIFIC) {
3253                 // Create a new FormattedFloatingDecimal with the desired
3254                 // precision.
3255                 int prec = (precision == -1 ? 6 : precision);
3256 
3257                 FormattedFloatingDecimal fd
3258                         = FormattedFloatingDecimal.valueOf(value, prec,
3259                           FormattedFloatingDecimal.Form.SCIENTIFIC);
3260 
3261                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3262                 addZeros(mant, prec);
3263 
3264                 // If the precision is zero and the '#' flag is set, add the
3265                 // requested decimal point.
3266                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3267                     mant.append('.');
3268                 }
3269 
3270                 char[] exp = (value == 0.0)
3271                     ? new char[] {'+','0','0'} : fd.getExponent();
3272 
3273                 int newW = width;
3274                 if (width != -1) {
3275                     newW = adjustWidth(width - exp.length - 1, f, neg);
3276                 }
3277                 localizedMagnitude(sb, mant, 0, f, newW, l);
3278 
3279                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3280 

3281                 char sign = exp[0];
3282                 assert(sign == '+' || sign == '-');
3283                 sb.append(sign);
3284 
3285                 localizedMagnitudeExp(sb, exp, 1, l);


3286             } else if (c == Conversion.DECIMAL_FLOAT) {
3287                 // Create a new FormattedFloatingDecimal with the desired
3288                 // precision.
3289                 int prec = (precision == -1 ? 6 : precision);
3290 
3291                 FormattedFloatingDecimal fd
3292                         = FormattedFloatingDecimal.valueOf(value, prec,
3293                           FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3294 
3295                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3296                 addZeros(mant, prec);
3297 
3298                 // If the precision is zero and the '#' flag is set, add the
3299                 // requested decimal point.
3300                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3301                     mant.append('.');
3302 
3303                 int newW = width;
3304                 if (width != -1)
3305                     newW = adjustWidth(width, f, neg);
3306                 localizedMagnitude(sb, mant, 0, f, newW, l);
3307             } else if (c == Conversion.GENERAL) {
3308                 int prec = precision;
3309                 if (precision == -1)
3310                     prec = 6;
3311                 else if (precision == 0)
3312                     prec = 1;
3313 
3314                 char[] exp;
3315                 StringBuilder mant = new StringBuilder();
3316                 int expRounded;
3317                 if (value == 0.0) {
3318                     exp = null;
3319                     mant.append('0');
3320                     expRounded = 0;
3321                 } else {
3322                     FormattedFloatingDecimal fd
3323                         = FormattedFloatingDecimal.valueOf(value, prec,
3324                           FormattedFloatingDecimal.Form.GENERAL);
3325                     exp = fd.getExponent();
3326                     mant.append(fd.getMantissa());
3327                     expRounded = fd.getExponentRounded();
3328                 }
3329 
3330                 if (exp != null) {
3331                     prec -= 1;
3332                 } else {
3333                     prec -= expRounded + 1;
3334                 }
3335 
3336                 addZeros(mant, prec);
3337                 // If the precision is zero and the '#' flag is set, add the
3338                 // requested decimal point.
3339                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3340                     mant.append('.');
3341                 }
3342 
3343                 int newW = width;
3344                 if (width != -1) {
3345                     if (exp != null)
3346                         newW = adjustWidth(width - exp.length - 1, f, neg);
3347                     else
3348                         newW = adjustWidth(width, f, neg);
3349                 }
3350                 localizedMagnitude(sb, mant, 0, f, newW, l);
3351 
3352                 if (exp != null) {
3353                     sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3354 

3355                     char sign = exp[0];
3356                     assert(sign == '+' || sign == '-');
3357                     sb.append(sign);
3358 
3359                     localizedMagnitudeExp(sb, exp, 1, l);


3360                 }
3361             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3362                 int prec = precision;
3363                 if (precision == -1)
3364                     // assume that we want all of the digits
3365                     prec = 0;
3366                 else if (precision == 0)
3367                     prec = 1;
3368 
3369                 String s = hexDouble(value, prec);
3370 
3371                 StringBuilder va = new StringBuilder();
3372                 boolean upper = f.contains(Flags.UPPERCASE);
3373                 sb.append(upper ? "0X" : "0x");
3374 
3375                 if (f.contains(Flags.ZERO_PAD)) {
3376                     trailingZeros(sb, width - s.length() - 2);
3377                 }
3378 
3379                 int idx = s.indexOf('p');

3380                 if (upper) {
3381                     String tmp = s.substring(0, idx);
3382                     // don't localize hex
3383                     tmp = tmp.toUpperCase(Locale.US);
3384                     va.append(tmp);
3385                 } else {
3386                     va.append(s, 0, idx);
3387                 }
3388                 if (prec != 0) {
3389                     addZeros(va, prec);
3390                 }
3391                 sb.append(va);
3392                 sb.append(upper ? 'P' : 'p');
3393                 sb.append(s, idx+1, s.length());
3394             }
3395         }
3396 
3397         // Add zeros to the requested precision.
3398         private void addZeros(StringBuilder sb, int prec) {
3399             // Look for the dot.  If we don't find one, the we'll need to add
3400             // it before we add the zeros.
3401             int len = sb.length();
3402             int i;
3403             for (i = 0; i < len; i++) {
3404                 if (sb.charAt(i) == '.') {
3405                     break;
3406                 }
3407             }
3408             boolean needDot = false;
3409             if (i == len) {
3410                 needDot = true;
3411             }
3412 
3413             // Determine existing precision.
3414             int outPrec = len - i - (needDot ? 0 : 1);
3415             assert (outPrec <= prec);
3416             if (outPrec == prec) {
3417                 return;
3418             }




3419 
3420             // Add dot if previously determined to be necessary.

3421             if (needDot) {
3422                 sb.append('.');

3423             }
3424 
3425             // Add zeros.
3426             trailingZeros(sb, prec - outPrec);



3427         }
3428 
3429         // Method assumes that d > 0.
3430         private String hexDouble(double d, int prec) {
3431             // Let Double.toHexString handle simple cases
3432             if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3433                 // remove "0x"
3434                 return Double.toHexString(d).substring(2);
3435             } else {
3436                 assert(prec >= 1 && prec <= 12);
3437 
3438                 int exponent  = Math.getExponent(d);
3439                 boolean subnormal
3440                     = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3441 
3442                 // If this is subnormal input so normalize (could be faster to
3443                 // do as integer operation).
3444                 if (subnormal) {
3445                     scaleUp = Math.scalb(1.0, 54);
3446                     d *= scaleUp;
3447                     // Calculate the exponent.  This is not just exponent + 54
3448                     // since the former is not the normalized exponent.
3449                     exponent = Math.getExponent(d);
3450                     assert exponent >= DoubleConsts.MIN_EXPONENT &&
3451                         exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3452                 }
3453 
3454                 int precision = 1 + prec*4;
3455                 int shiftDistance


3508                 }
3509             }
3510         }
3511 
3512         private void print(BigDecimal value, Locale l) throws IOException {
3513             if (c == Conversion.HEXADECIMAL_FLOAT)
3514                 failConversion(c, value);
3515             StringBuilder sb = new StringBuilder();
3516             boolean neg = value.signum() == -1;
3517             BigDecimal v = value.abs();
3518             // leading sign indicator
3519             leadingSign(sb, neg);
3520 
3521             // the value
3522             print(sb, v, l, f, c, precision, neg);
3523 
3524             // trailing sign indicator
3525             trailingSign(sb, neg);
3526 
3527             // justify based on width
3528             appendJustified(a, sb);
3529         }
3530 
3531         // value > 0
3532         private void print(StringBuilder sb, BigDecimal value, Locale l,
3533                            Flags f, char c, int precision, boolean neg)
3534             throws IOException
3535         {
3536             if (c == Conversion.SCIENTIFIC) {
3537                 // Create a new BigDecimal with the desired precision.
3538                 int prec = (precision == -1 ? 6 : precision);
3539                 int scale = value.scale();
3540                 int origPrec = value.precision();
3541                 int nzeros = 0;
3542                 int compPrec;
3543 
3544                 if (prec > origPrec - 1) {
3545                     compPrec = origPrec;
3546                     nzeros = prec - (origPrec - 1);
3547                 } else {
3548                     compPrec = prec + 1;
3549                 }
3550 
3551                 MathContext mc = new MathContext(compPrec);
3552                 BigDecimal v
3553                     = new BigDecimal(value.unscaledValue(), scale, mc);
3554 
3555                 BigDecimalLayout bdl
3556                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3557                                            BigDecimalLayoutForm.SCIENTIFIC);
3558 
3559                 StringBuilder mant = bdl.mantissa();
3560 
3561                 // Add a decimal point if necessary.  The mantissa may not
3562                 // contain a decimal point if the scale is zero (the internal
3563                 // representation has no fractional part) or the original
3564                 // precision is one. Append a decimal point if '#' is set or if
3565                 // we require zero padding to get to the requested precision.
3566                 if ((origPrec == 1 || !bdl.hasDot())
3567                         && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3568                     mant.append('.');
3569                 }
3570 
3571                 // Add trailing zeros in the case precision is greater than
3572                 // the number of available digits after the decimal separator.
3573                 trailingZeros(mant, nzeros);
3574 
3575                 StringBuilder exp = bdl.exponent();
3576                 int newW = width;
3577                 if (width != -1) {
3578                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3579                 }
3580                 localizedMagnitude(sb, mant, 0, f, newW, l);
3581 
3582                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3583 
3584                 Flags flags = f.dup().remove(Flags.GROUP);
3585                 char sign = exp.charAt(0);
3586                 assert(sign == '+' || sign == '-');
3587                 sb.append(sign);
3588 
3589                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3590             } else if (c == Conversion.DECIMAL_FLOAT) {
3591                 // Create a new BigDecimal with the desired precision.
3592                 int prec = (precision == -1 ? 6 : precision);
3593                 int scale = value.scale();
3594 
3595                 if (scale > prec) {
3596                     // more "scale" digits than the requested "precision"
3597                     int compPrec = value.precision();
3598                     if (compPrec <= scale) {
3599                         // case of 0.xxxxxx
3600                         value = value.setScale(prec, RoundingMode.HALF_UP);
3601                     } else {
3602                         compPrec -= (scale - prec);
3603                         value = new BigDecimal(value.unscaledValue(),
3604                                                scale,
3605                                                new MathContext(compPrec));
3606                     }
3607                 }
3608                 BigDecimalLayout bdl = new BigDecimalLayout(
3609                                            value.unscaledValue(),
3610                                            value.scale(),
3611                                            BigDecimalLayoutForm.DECIMAL_FLOAT);
3612 
3613                 StringBuilder mant = bdl.mantissa();
3614                 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3615 
3616                 // Add a decimal point if necessary.  The mantissa may not
3617                 // contain a decimal point if the scale is zero (the internal
3618                 // representation has no fractional part).  Append a decimal
3619                 // point if '#' is set or we require zero padding to get to the
3620                 // requested precision.
3621                 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3622                         || nzeros > 0)) {
3623                     mant.append('.');
3624                 }
3625 
3626                 // Add trailing zeros if the precision is greater than the
3627                 // number of available digits after the decimal separator.
3628                 trailingZeros(mant, nzeros);
3629 
3630                 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3631             } else if (c == Conversion.GENERAL) {
3632                 int prec = precision;
3633                 if (precision == -1)
3634                     prec = 6;
3635                 else if (precision == 0)
3636                     prec = 1;
3637 
3638                 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3639                 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3640                 if ((value.equals(BigDecimal.ZERO))
3641                     || ((value.compareTo(tenToTheNegFour) != -1)
3642                         && (value.compareTo(tenToThePrec) == -1))) {
3643 
3644                     int e = - value.scale()
3645                         + (value.unscaledValue().toString().length() - 1);
3646 
3647                     // xxx.yyy
3648                     //   g precision (# sig digits) = #x + #y
3649                     //   f precision = #y
3650                     //   exponent = #x - 1


3669         }
3670 
3671         private class BigDecimalLayout {
3672             private StringBuilder mant;
3673             private StringBuilder exp;
3674             private boolean dot = false;
3675             private int scale;
3676 
3677             public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3678                 layout(intVal, scale, form);
3679             }
3680 
3681             public boolean hasDot() {
3682                 return dot;
3683             }
3684 
3685             public int scale() {
3686                 return scale;
3687             }
3688 
3689             public StringBuilder mantissa() {
3690                 return mant;










3691             }
3692 
3693             // The exponent will be formatted as a sign ('+' or '-') followed
3694             // by the exponent zero-padded to include at least two digits.
3695             public StringBuilder exponent() {
3696                 return exp;








3697             }
3698 
3699             private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3700                 String coeff = intVal.toString();
3701                 this.scale = scale;
3702 
3703                 // Construct a buffer, with sufficient capacity for all cases.
3704                 // If E-notation is needed, length will be: +1 if negative, +1
3705                 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3706                 // exponent.  Otherwise it could have +1 if negative, plus
3707                 // leading "0.00000"
3708                 int len = coeff.length();
3709                 mant = new StringBuilder(len + 14);
3710 
3711                 if (scale == 0) {

3712                     if (len > 1) {
3713                         mant.append(coeff.charAt(0));
3714                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3715                             mant.append('.');
3716                             dot = true;
3717                             mant.append(coeff, 1, len);
3718                             exp = new StringBuilder("+");
3719                             if (len < 10) {
3720                                 exp.append('0').append(len - 1);
3721                             } else {
3722                                 exp.append(len - 1);
3723                             }
3724                         } else {
3725                             mant.append(coeff, 1, len);
3726                         }
3727                     } else {
3728                         mant.append(coeff);
3729                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3730                             exp = new StringBuilder("+00");
3731                         }
3732                     }
3733                     return;
3734                 }
3735                 long adjusted = -(long) scale + (len - 1);
3736                 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3737                     // count of padding zeros
3738                     int pad = scale - len;
3739                     if (pad >= 0) {
3740                         // 0.xxx form
3741                         mant.append("0.");
3742                         dot = true;
3743                         trailingZeros(mant, pad);
3744                         mant.append(coeff);
3745                     } else {
3746                         if (-pad < len) {
3747                             // xx.xx form
3748                             mant.append(coeff, 0, -pad);
3749                             mant.append('.');
3750                             dot = true;
3751                             mant.append(coeff, -pad, -pad + scale);
3752                         } else {
3753                             // xx form
3754                             mant.append(coeff, 0, len);
3755                             trailingZeros(mant, -scale);

3756                             this.scale = 0;
3757                         }
3758                     }
3759                 } else {
3760                     // x.xxx form
3761                     mant.append(coeff.charAt(0));
3762                     if (len > 1) {
3763                         mant.append('.');
3764                         dot = true;
3765                         mant.append(coeff, 1, len);
3766                     }
3767                     exp = new StringBuilder();
3768                     if (adjusted != 0) {
3769                         long abs = Math.abs(adjusted);
3770                         // require sign
3771                         exp.append(adjusted < 0 ? '-' : '+');
3772                         if (abs < 10) {
3773                             exp.append('0');
3774                         }
3775                         exp.append(abs);
3776                     } else {
3777                         exp.append("+00");
3778                     }
3779                 }
3780             }
3781         }
3782 
3783         private int adjustWidth(int width, Flags f, boolean neg) {
3784             int newW = width;
3785             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3786                 newW--;
3787             return newW;
3788         }
3789 
3790         // Add trailing zeros
3791         private void trailingZeros(StringBuilder sb, int nzeros) {
3792             for (int i = 0; i < nzeros; i++) {
3793                 sb.append('0');














3794             }

3795         }
3796 
3797         private void print(Calendar t, char c, Locale l)  throws IOException {

3798             StringBuilder sb = new StringBuilder();
3799             print(sb, t, c, l);
3800 
3801             // justify based on width
3802             if (f.contains(Flags.UPPERCASE)) {
3803                 appendJustified(a, sb.toString().toUpperCase());
3804             } else {
3805                 appendJustified(a, sb);
3806             }
3807         }
3808 
3809         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3810                 throws IOException {


3811             if (sb == null)
3812                 sb = new StringBuilder();
3813             switch (c) {
3814             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3815             case DateTime.HOUR_0:        // 'I' (01 - 12)
3816             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3817             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3818                 int i = t.get(Calendar.HOUR_OF_DAY);
3819                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3820                     i = (i == 0 || i == 12 ? 12 : i % 12);
3821                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3822                                || c == DateTime.HOUR_0
3823                                ? Flags.ZERO_PAD
3824                                : Flags.NONE);
3825                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3826                 break;
3827             }
3828             case DateTime.MINUTE:      { // 'M' (00 - 59)
3829                 int i = t.get(Calendar.MINUTE);
3830                 Flags flags = Flags.ZERO_PAD;


3963             // Composites
3964             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
3965             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
3966                 char sep = ':';
3967                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
3968                 print(sb, t, DateTime.MINUTE, l);
3969                 if (c == DateTime.TIME) {
3970                     sb.append(sep);
3971                     print(sb, t, DateTime.SECOND, l);
3972                 }
3973                 break;
3974             }
3975             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
3976                 char sep = ':';
3977                 print(sb, t, DateTime.HOUR_0, l).append(sep);
3978                 print(sb, t, DateTime.MINUTE, l).append(sep);
3979                 print(sb, t, DateTime.SECOND, l).append(' ');
3980                 // this may be in wrong place for some locales
3981                 StringBuilder tsb = new StringBuilder();
3982                 print(tsb, t, DateTime.AM_PM, l);
3983 
3984                 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
3985                 break;
3986             }
3987             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
3988                 char sep = ' ';
3989                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
3990                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
3991                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
3992                 print(sb, t, DateTime.TIME, l).append(sep);
3993                 print(sb, t, DateTime.ZONE, l).append(sep);
3994                 print(sb, t, DateTime.YEAR_4, l);
3995                 break;
3996             }
3997             case DateTime.DATE:            { // 'D' (mm/dd/yy)
3998                 char sep = '/';
3999                 print(sb, t, DateTime.MONTH, l).append(sep);
4000                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4001                 print(sb, t, DateTime.YEAR_2, l);
4002                 break;
4003             }
4004             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4005                 char sep = '-';
4006                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4007                 print(sb, t, DateTime.MONTH, l).append(sep);
4008                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4009                 break;
4010             }
4011             default:
4012                 assert false;
4013             }
4014             return sb;
4015         }
4016 
4017         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4018             StringBuilder sb = new StringBuilder();
4019             print(sb, t, c, l);
4020             // justify based on width
4021             if (f.contains(Flags.UPPERCASE)) {
4022                 appendJustified(a, sb.toString().toUpperCase());
4023             } else {
4024                 appendJustified(a, sb);
4025             }
4026         }
4027 
4028         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4029                                  Locale l) throws IOException {
4030             if (sb == null)
4031                 sb = new StringBuilder();
4032             try {
4033                 switch (c) {
4034                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4035                     int i = t.get(ChronoField.HOUR_OF_DAY);
4036                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4037                     break;
4038                 }
4039                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4040                     int i = t.get(ChronoField.HOUR_OF_DAY);
4041                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4042                     break;
4043                 }
4044                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4045                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);


4253 
4254         // -- Methods to support throwing exceptions --
4255 
4256         private void failMismatch(Flags f, char c) {
4257             String fs = f.toString();
4258             throw new FormatFlagsConversionMismatchException(fs, c);
4259         }
4260 
4261         private void failConversion(char c, Object arg) {
4262             throw new IllegalFormatConversionException(c, arg.getClass());
4263         }
4264 
4265         private char getZero(Locale l) {
4266             if ((l != null) &&  !l.equals(locale())) {
4267                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4268                 return dfs.getZeroDigit();
4269             }
4270             return zero;
4271         }
4272 
4273         private StringBuilder localizedMagnitude(StringBuilder sb,
4274                 long value, Flags f, int width, Locale l) {
4275             return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);



4276         }
4277 
4278         private StringBuilder localizedMagnitude(StringBuilder sb,
4279                 CharSequence value, final int offset, Flags f, int width,
4280                 Locale l) {
4281             if (sb == null) {

4282                 sb = new StringBuilder();
4283             }
4284             int begin = sb.length();
4285 
4286             char zero = getZero(l);
4287 
4288             // determine localized grouping separator and size
4289             char grpSep = '\0';
4290             int  grpSize = -1;
4291             char decSep = '\0';
4292 
4293             int len = value.length();
4294             int dot = len;
4295             for (int j = offset; j < len; j++) {
4296                 if (value.charAt(j) == '.') {
4297                     dot = j;
4298                     break;
4299                 }
4300             }
4301 
4302             if (dot < len) {
4303                 if (l == null || l.equals(Locale.US)) {
4304                     decSep  = '.';
4305                 } else {
4306                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4307                     decSep  = dfs.getDecimalSeparator();
4308                 }
4309             }
4310 
4311             if (f.contains(Flags.GROUP)) {
4312                 if (l == null || l.equals(Locale.US)) {
4313                     grpSep = ',';
4314                     grpSize = 3;
4315                 } else {
4316                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4317                     grpSep = dfs.getGroupingSeparator();
4318                     DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4319                     grpSize = df.getGroupingSize();
4320                 }
4321             }
4322 
4323             // localize the digits inserting group separators as necessary
4324             for (int j = offset; j < len; j++) {
4325                 if (j == dot) {
4326                     sb.append(decSep);
4327                     // no more group separators after the decimal separator
4328                     grpSep = '\0';
4329                     continue;
4330                 }
4331 
4332                 char c = value.charAt(j);
4333                 sb.append((char) ((c - '0') + zero));
4334                 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4335                     sb.append(grpSep);
4336                 }
4337             }
4338 
4339             // apply zero padding
4340             if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4341                 for (int k = sb.length(); k < width; k++) {

4342                     sb.insert(begin, zero);
4343                 }
4344             }
4345 
4346             return sb;
4347         }
4348         
4349         // Specialized localization of exponents, where the source value can only
4350         // contain characters '0' through '9', starting at index offset, and no
4351         // group separators is added for any locale.
4352         private void localizedMagnitudeExp(StringBuilder sb, char[] value,
4353                 final int offset, Locale l) {
4354             char zero = getZero(l);
4355 
4356             int len = value.length;
4357             for (int j = offset; j < len; j++) {
4358                 char c = value[j];
4359                 sb.append((char) ((c - '0') + zero));
4360             }
4361         }
4362     }
4363 
4364     private static class Flags {
4365         private int flags;
4366 
4367         static final Flags NONE          = new Flags(0);      // ''
4368 
4369         // duplicate declarations from Formattable.java
4370         static final Flags LEFT_JUSTIFY  = new Flags(1<<0);   // '-'
4371         static final Flags UPPERCASE     = new Flags(1<<1);   // '^'
4372         static final Flags ALTERNATE     = new Flags(1<<2);   // '#'
4373 
4374         // numerics
4375         static final Flags PLUS          = new Flags(1<<3);   // '+'
4376         static final Flags LEADING_SPACE = new Flags(1<<4);   // ' '
4377         static final Flags ZERO_PAD      = new Flags(1<<5);   // '0'
4378         static final Flags GROUP         = new Flags(1<<6);   // ','
4379         static final Flags PARENTHESES   = new Flags(1<<7);   // '('
4380 
4381         // indexing


4390         }
4391 
4392         public boolean contains(Flags f) {
4393             return (flags & f.valueOf()) == f.valueOf();
4394         }
4395 
4396         public Flags dup() {
4397             return new Flags(flags);
4398         }
4399 
4400         private Flags add(Flags f) {
4401             flags |= f.valueOf();
4402             return this;
4403         }
4404 
4405         public Flags remove(Flags f) {
4406             flags &= ~f.valueOf();
4407             return this;
4408         }
4409 
4410         public static Flags parse(String s, int start, int end) {

4411             Flags f = new Flags(0);
4412             for (int i = start; i < end; i++) {
4413                 char c = s.charAt(i);
4414                 Flags v = parse(c);
4415                 if (f.contains(v))
4416                     throw new DuplicateFormatFlagsException(v.toString());
4417                 f.add(v);
4418             }
4419             return f;
4420         }
4421 
4422         // parse those flags which may be provided by users
4423         private static Flags parse(char c) {
4424             switch (c) {
4425             case '-': return LEFT_JUSTIFY;
4426             case '#': return ALTERNATE;
4427             case '+': return PLUS;
4428             case ' ': return LEADING_SPACE;
4429             case '0': return ZERO_PAD;
4430             case ',': return GROUP;
4431             case '(': return PARENTHESES;
4432             case '<': return PREVIOUS;
4433             default: