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

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


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


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






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


2618         private boolean dt = false;
2619         private char c;
2620 
2621         private int index(String s) {
2622             if (s != null) {
2623                 try {
2624                     index = Integer.parseInt(s.substring(0, s.length() - 1));
2625                 } catch (NumberFormatException x) {
2626                     assert(false);
2627                 }
2628             } else {
2629                 index = 0;
2630             }
2631             return index;
2632         }
2633 
2634         public int index() {
2635             return index;
2636         }
2637 
2638         private Flags flags(String s) {
2639             f = Flags.parse(s);
2640             if (f.contains(Flags.PREVIOUS))
2641                 index = -1;
2642             return f;
2643         }
2644 
2645         Flags flags() {
2646             return f;
2647         }
2648 
2649         private int width(String s) {
2650             width = -1;
2651             if (s != null) {
2652                 try {
2653                     width  = Integer.parseInt(s);
2654                     if (width < 0)
2655                         throw new IllegalFormatWidthException(width);
2656                 } catch (NumberFormatException x) {
2657                     assert(false);
2658                 }
2659             }


2666 
2667         private int precision(String s) {
2668             precision = -1;
2669             if (s != null) {
2670                 try {
2671                     // remove the '.'
2672                     precision = Integer.parseInt(s.substring(1));
2673                     if (precision < 0)
2674                         throw new IllegalFormatPrecisionException(precision);
2675                 } catch (NumberFormatException x) {
2676                     assert(false);
2677                 }
2678             }
2679             return precision;
2680         }
2681 
2682         int precision() {
2683             return precision;
2684         }
2685 
2686         private char conversion(String s) {
2687             c = s.charAt(0);
2688             if (!dt) {
2689                 if (!Conversion.isValid(c))
2690                     throw new UnknownFormatConversionException(String.valueOf(c));
2691                 if (Character.isUpperCase(c))

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

2695                     index = -2;
2696             }

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

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

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


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




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


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





3096 
3097                 // leading sign indicator
3098                 leadingSign(sb, neg);
3099 
3100                 // the value
3101                 localizedMagnitude(sb, va, f, adjustWidth(width, f, neg), l);
3102 
3103                 // trailing sign indicator
3104                 trailingSign(sb, neg);
3105             } else if (c == Conversion.OCTAL_INTEGER) {
3106                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3107                               Flags.PLUS);
3108                 String s = Long.toOctalString(value);
3109                 int len = (f.contains(Flags.ALTERNATE)
3110                            ? s.length() + 1
3111                            : s.length());
3112 
3113                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3114                 if (f.contains(Flags.ALTERNATE))
3115                     sb.append('0');
3116                 if (f.contains(Flags.ZERO_PAD))
3117                     for (int i = 0; i < width - len; i++) sb.append('0');

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

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

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

3272 
3273                 char[] exp = (value == 0.0)
3274                     ? new char[] {'+','0','0'} : fd.getExponent();




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

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





3331                     expRounded = fd.getExponentRounded();
3332                 }
3333 
3334                 if (exp != null) {
3335                     prec -= 1;
3336                 } else {
3337                     prec -= expRounded + 1;
3338                 }
3339 
3340                 mant = addZeros(mant, prec);
3341                 // If the precision is zero and the '#' flag is set, add the
3342                 // requested decimal point.
3343                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3344                     mant = addDot(mant);

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





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

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

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


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

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

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


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


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

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

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

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

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


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

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

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


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

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

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


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


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

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




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


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


2624         private boolean dt = false;
2625         private char c;
2626 
2627         private int index(String s) {
2628             if (s != null) {
2629                 try {
2630                     index = Integer.parseInt(s.substring(0, s.length() - 1));
2631                 } catch (NumberFormatException x) {
2632                     assert(false);
2633                 }
2634             } else {
2635                 index = 0;
2636             }
2637             return index;
2638         }
2639 
2640         public int index() {
2641             return index;
2642         }
2643 
2644         private Flags flags(String s, int start, int end) {
2645             f = Flags.parse(s, start, end);
2646             if (f.contains(Flags.PREVIOUS))
2647                 index = -1;
2648             return f;
2649         }
2650 
2651         Flags flags() {
2652             return f;
2653         }
2654 
2655         private int width(String s) {
2656             width = -1;
2657             if (s != null) {
2658                 try {
2659                     width  = Integer.parseInt(s);
2660                     if (width < 0)
2661                         throw new IllegalFormatWidthException(width);
2662                 } catch (NumberFormatException x) {
2663                     assert(false);
2664                 }
2665             }


2672 
2673         private int precision(String s) {
2674             precision = -1;
2675             if (s != null) {
2676                 try {
2677                     // remove the '.'
2678                     precision = Integer.parseInt(s.substring(1));
2679                     if (precision < 0)
2680                         throw new IllegalFormatPrecisionException(precision);
2681                 } catch (NumberFormatException x) {
2682                     assert(false);
2683                 }
2684             }
2685             return precision;
2686         }
2687 
2688         int precision() {
2689             return precision;
2690         }
2691 
2692         private char conversion(char conv) {
2693             c = conv;
2694             if (!dt) {
2695                 if (!Conversion.isValid(conv)) {
2696                     throw new UnknownFormatConversionException(String.valueOf(c));
2697                 }
2698                 if (Character.isUpperCase(conv)) {
2699                     f.add(Flags.UPPERCASE);
2700                     c = Character.toLowerCase(conv);
2701                 }
2702                 if (Conversion.isText(conv)) {
2703                     index = -2;
2704                 }
2705             }
2706             return c;
2707         }
2708 
2709         private char conversion() {
2710             return c;
2711         }
2712 
2713         FormatSpecifier(String s, Matcher m) {
2714             int idx = 1;
2715 
2716             index(m.group(idx++));
2717             flags(s, m.start(idx), m.end(idx++));
2718             width(m.group(idx++));
2719             precision(m.group(idx++));
2720 
2721             int tTStart = m.start(idx);
2722             int tTEnd = m.end(idx++);
2723             if (tTStart != -1 && tTEnd != -1) {
2724                 dt = true;
2725                 if (tTStart == tTEnd - 1 && s.charAt(tTStart) == 'T') {
2726                     f.add(Flags.UPPERCASE);
2727                 }
2728             }
2729 
2730             conversion(s.charAt(m.start(idx)));
2731 
2732             if (dt)
2733                 checkDateTime();
2734             else if (Conversion.isGeneral(c))
2735                 checkGeneral();
2736             else if (Conversion.isCharacter(c))
2737                 checkCharacter();
2738             else if (Conversion.isInteger(c))
2739                 checkInteger();
2740             else if (Conversion.isFloat(c))
2741                 checkFloat();
2742             else if (Conversion.isText(c))
2743                 checkText();
2744             else
2745                 throw new UnknownFormatConversionException(String.valueOf(c));
2746         }
2747 
2748         public void print(Object arg, Locale l) throws IOException {
2749             if (dt) {
2750                 printDateTime(arg, l);


2903                 s = ((arg instanceof Boolean)
2904                      ? ((Boolean)arg).toString()
2905                      : Boolean.toString(true));
2906             else
2907                 s = Boolean.toString(false);
2908             print(s);
2909         }
2910 
2911         private void printHashCode(Object arg) throws IOException {
2912             String s = (arg == null
2913                         ? "null"
2914                         : Integer.toHexString(arg.hashCode()));
2915             print(s);
2916         }
2917 
2918         private void print(String s) throws IOException {
2919             if (precision != -1 && precision < s.length())
2920                 s = s.substring(0, precision);
2921             if (f.contains(Flags.UPPERCASE))
2922                 s = s.toUpperCase();
2923             appendJustified(a, s);
2924         }
2925 
2926         private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
2927              if (width == -1) {
2928                  return a.append(cs);
2929              }
2930              boolean padLeft = f.contains(Flags.LEFT_JUSTIFY);
2931              int sp = width - cs.length();
2932              if (padLeft) {
2933                  a.append(cs);
2934              }
2935              for (int i = 0; i < sp; i++) {
2936                  a.append(' ');
2937              }
2938              if (!padLeft) {
2939                  a.append(cs);
2940              }
2941              return a;
2942         }
2943 
2944         public String toString() {
2945             StringBuilder sb = new StringBuilder("%");
2946             // Flags.UPPERCASE is set internally for legal conversions.
2947             Flags dupf = f.dup().remove(Flags.UPPERCASE);
2948             sb.append(dupf.toString());
2949             if (index > 0)
2950                 sb.append(index).append('$');
2951             if (width != -1)
2952                 sb.append(width);
2953             if (precision != -1)
2954                 sb.append('.').append(precision);
2955             if (dt)
2956                 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
2957             sb.append(f.contains(Flags.UPPERCASE)
2958                       ? Character.toUpperCase(c) : c);
2959             return sb.toString();
2960         }
2961 


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

3198             } else if (c == Conversion.OCTAL_INTEGER) {
3199                 String s = v.toString(8);
3200 
3201                 int len = s.length() + sb.length();
3202                 if (neg && f.contains(Flags.PARENTHESES))
3203                     len++;
3204 
3205                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3206                 if (f.contains(Flags.ALTERNATE)) {
3207                     len++;
3208                     sb.append('0');
3209                 }
3210                 if (f.contains(Flags.ZERO_PAD)) {
3211                     trailingZeros(sb, width - len);

3212                 }
3213                 sb.append(s);
3214             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3215                 String s = v.toString(16);
3216 
3217                 int len = s.length() + sb.length();
3218                 if (neg && f.contains(Flags.PARENTHESES))
3219                     len++;
3220 
3221                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3222                 if (f.contains(Flags.ALTERNATE)) {
3223                     len += 2;
3224                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3225                 }
3226                 if (f.contains(Flags.ZERO_PAD)) {
3227                     trailingZeros(sb, width - len);
3228                 }
3229                 if (f.contains(Flags.UPPERCASE))
3230                     s = s.toUpperCase();
3231                 sb.append(s);
3232             }
3233 
3234             // trailing sign indicator
3235             trailingSign(sb, (value.signum() == -1));
3236 
3237             // justify based on width
3238             appendJustified(a, sb);
3239         }
3240 
3241         private void print(float value, Locale l) throws IOException {
3242             print((double) value, l);
3243         }
3244 
3245         private void print(double value, Locale l) throws IOException {
3246             StringBuilder sb = new StringBuilder();
3247             boolean neg = Double.compare(value, 0.0) == -1;
3248 
3249             if (!Double.isNaN(value)) {
3250                 double v = Math.abs(value);
3251 
3252                 // leading sign indicator
3253                 leadingSign(sb, neg);
3254 
3255                 // the value
3256                 if (!Double.isInfinite(v))
3257                     print(sb, v, l, f, c, precision, neg);
3258                 else
3259                     sb.append(f.contains(Flags.UPPERCASE)
3260                               ? "INFINITY" : "Infinity");
3261 
3262                 // trailing sign indicator
3263                 trailingSign(sb, neg);
3264             } else {
3265                 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3266             }
3267 
3268             // justify based on width
3269             appendJustified(a, sb);
3270         }
3271 
3272         // !Double.isInfinite(value) && !Double.isNaN(value)
3273         private void print(StringBuilder sb, double value, Locale l,
3274                            Flags f, char c, int precision, boolean neg)
3275             throws IOException
3276         {
3277             if (c == Conversion.SCIENTIFIC) {
3278                 // Create a new FormattedFloatingDecimal with the desired
3279                 // precision.
3280                 int prec = (precision == -1 ? 6 : precision);
3281 
3282                 FormattedFloatingDecimal fd
3283                         = FormattedFloatingDecimal.valueOf(value, prec,
3284                           FormattedFloatingDecimal.Form.SCIENTIFIC);
3285 
3286                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3287                 addZeros(mant, prec);
3288 
3289                 // If the precision is zero and the '#' flag is set, add the
3290                 // requested decimal point.
3291                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3292                     mant.append('.');
3293                 }
3294 
3295                 StringBuilder exp = new StringBuilder();
3296                 if (value == 0.0) {
3297                     exp.append("+00");
3298                 } else {
3299                     exp.append(fd.getExponent());
3300                 }
3301 
3302                 int newW = width;
3303                 if (width != -1)
3304                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3305                 localizedMagnitude(sb, mant, 0, f, newW, l);
3306 
3307                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3308 
3309                 Flags flags = f.dup().remove(Flags.GROUP);
3310                 char sign = exp.charAt(0);
3311                 assert(sign == '+' || sign == '-');
3312                 sb.append(sign);
3313 
3314                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3315             } else if (c == Conversion.DECIMAL_FLOAT) {
3316                 // Create a new FormattedFloatingDecimal with the desired
3317                 // precision.
3318                 int prec = (precision == -1 ? 6 : precision);
3319 
3320                 FormattedFloatingDecimal fd
3321                         = FormattedFloatingDecimal.valueOf(value, prec,
3322                           FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3323 
3324                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3325                 addZeros(mant, prec);
3326 
3327                 // If the precision is zero and the '#' flag is set, add the
3328                 // requested decimal point.
3329                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3330                     mant.append('.');
3331 
3332                 int newW = width;
3333                 if (width != -1)
3334                     newW = adjustWidth(width, f, neg);
3335                 localizedMagnitude(sb, mant, 0, f, newW, l);
3336             } else if (c == Conversion.GENERAL) {
3337                 int prec = precision;
3338                 if (precision == -1)
3339                     prec = 6;
3340                 else if (precision == 0)
3341                     prec = 1;
3342 
3343                 StringBuilder exp;
3344                 StringBuilder mant = new StringBuilder();
3345                 int expRounded;
3346                 if (value == 0.0) {
3347                     exp = null;
3348                     mant.append('0');
3349                     expRounded = 0;
3350                 } else {
3351                     FormattedFloatingDecimal fd
3352                         = FormattedFloatingDecimal.valueOf(value, prec,
3353                           FormattedFloatingDecimal.Form.GENERAL);
3354                     char[] expArray = fd.getExponent();
3355                     if (expArray != null) {
3356                         exp = new StringBuilder().append(expArray);
3357                     } else {
3358                         exp = null;
3359                     }
3360                     mant.append(fd.getMantissa());
3361                     expRounded = fd.getExponentRounded();
3362                 }
3363 
3364                 if (exp != null) {
3365                     prec -= 1;
3366                 } else {
3367                     prec -= expRounded + 1;
3368                 }
3369 
3370                 addZeros(mant, prec);
3371                 // If the precision is zero and the '#' flag is set, add the
3372                 // requested decimal point.
3373                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3374                     mant.append('.');
3375                 }
3376 
3377                 int newW = width;
3378                 if (width != -1) {
3379                     if (exp != null)
3380                         newW = adjustWidth(width - exp.length() - 1, f, neg);
3381                     else
3382                         newW = adjustWidth(width, f, neg);
3383                 }
3384                 localizedMagnitude(sb, mant, 0, f, newW, l);
3385 
3386                 if (exp != null) {
3387                     sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3388 
3389                     Flags flags = f.dup().remove(Flags.GROUP);
3390                     char sign = exp.charAt(0);
3391                     assert(sign == '+' || sign == '-');
3392                     sb.append(sign);
3393 
3394                     sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3395                 }
3396             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3397                 int prec = precision;
3398                 if (precision == -1)
3399                     // assume that we want all of the digits
3400                     prec = 0;
3401                 else if (precision == 0)
3402                     prec = 1;
3403 
3404                 String s = hexDouble(value, prec);
3405 
3406                 StringBuilder va = new StringBuilder();
3407                 boolean upper = f.contains(Flags.UPPERCASE);
3408                 sb.append(upper ? "0X" : "0x");
3409 
3410                 if (f.contains(Flags.ZERO_PAD)) {
3411                     trailingZeros(sb, width - s.length() - 2);
3412                 }
3413 
3414                 int idx = s.indexOf('p');

3415                 if (upper) {
3416                     String tmp = s.substring(0, idx);
3417                     // don't localize hex
3418                     tmp = tmp.toUpperCase(Locale.US);
3419                     va.append(tmp);
3420                 } else {
3421                     va.append(s, 0, idx);
3422                 }
3423                 if (prec != 0) {
3424                     addZeros(va, prec);
3425                 }
3426                 sb.append(va);
3427                 sb.append(upper ? 'P' : 'p');
3428                 sb.append(s, idx+1, s.length());
3429             }
3430         }
3431 
3432         // Add zeros to the requested precision.
3433         private void addZeros(StringBuilder sb, int prec) {
3434             // Look for the dot.  If we don't find one, the we'll need to add
3435             // it before we add the zeros.
3436             int len = sb.length();
3437             int i;
3438             for (i = 0; i < len; i++) {
3439                 if (sb.charAt(i) == '.') {
3440                     break;
3441                 }
3442             }
3443             boolean needDot = false;
3444             if (i == len) {
3445                 needDot = true;
3446             }
3447 
3448             // Determine existing precision.
3449             int outPrec = len - i - (needDot ? 0 : 1);
3450             assert (outPrec <= prec);
3451             if (outPrec == prec) {
3452                 return;
3453             }




3454 
3455             // Add dot if previously determined to be necessary.

3456             if (needDot) {
3457                 sb.append('.');

3458             }
3459 
3460             // Add zeros.
3461             trailingZeros(sb, prec - outPrec);



3462         }
3463 
3464         // Method assumes that d > 0.
3465         private String hexDouble(double d, int prec) {
3466             // Let Double.toHexString handle simple cases
3467             if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3468                 // remove "0x"
3469                 return Double.toHexString(d).substring(2);
3470             } else {
3471                 assert(prec >= 1 && prec <= 12);
3472 
3473                 int exponent  = Math.getExponent(d);
3474                 boolean subnormal
3475                     = (exponent == DoubleConsts.MIN_EXPONENT - 1);
3476 
3477                 // If this is subnormal input so normalize (could be faster to
3478                 // do as integer operation).
3479                 if (subnormal) {
3480                     scaleUp = Math.scalb(1.0, 54);
3481                     d *= scaleUp;
3482                     // Calculate the exponent.  This is not just exponent + 54
3483                     // since the former is not the normalized exponent.
3484                     exponent = Math.getExponent(d);
3485                     assert exponent >= DoubleConsts.MIN_EXPONENT &&
3486                         exponent <= DoubleConsts.MAX_EXPONENT: exponent;
3487                 }
3488 
3489                 int precision = 1 + prec*4;
3490                 int shiftDistance


3543                 }
3544             }
3545         }
3546 
3547         private void print(BigDecimal value, Locale l) throws IOException {
3548             if (c == Conversion.HEXADECIMAL_FLOAT)
3549                 failConversion(c, value);
3550             StringBuilder sb = new StringBuilder();
3551             boolean neg = value.signum() == -1;
3552             BigDecimal v = value.abs();
3553             // leading sign indicator
3554             leadingSign(sb, neg);
3555 
3556             // the value
3557             print(sb, v, l, f, c, precision, neg);
3558 
3559             // trailing sign indicator
3560             trailingSign(sb, neg);
3561 
3562             // justify based on width
3563             appendJustified(a, sb);
3564         }
3565 
3566         // value > 0
3567         private void print(StringBuilder sb, BigDecimal value, Locale l,
3568                            Flags f, char c, int precision, boolean neg)
3569             throws IOException
3570         {
3571             if (c == Conversion.SCIENTIFIC) {
3572                 // Create a new BigDecimal with the desired precision.
3573                 int prec = (precision == -1 ? 6 : precision);
3574                 int scale = value.scale();
3575                 int origPrec = value.precision();
3576                 int nzeros = 0;
3577                 int compPrec;
3578 
3579                 if (prec > origPrec - 1) {
3580                     compPrec = origPrec;
3581                     nzeros = prec - (origPrec - 1);
3582                 } else {
3583                     compPrec = prec + 1;
3584                 }
3585 
3586                 MathContext mc = new MathContext(compPrec);
3587                 BigDecimal v
3588                     = new BigDecimal(value.unscaledValue(), scale, mc);
3589 
3590                 BigDecimalLayout bdl
3591                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3592                                            BigDecimalLayoutForm.SCIENTIFIC);
3593 
3594                 StringBuilder mant = bdl.mantissa();
3595 
3596                 // Add a decimal point if necessary.  The mantissa may not
3597                 // contain a decimal point if the scale is zero (the internal
3598                 // representation has no fractional part) or the original
3599                 // precision is one. Append a decimal point if '#' is set or if
3600                 // we require zero padding to get to the requested precision.
3601                 if ((origPrec == 1 || !bdl.hasDot())
3602                         && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3603                     mant.append('.');
3604                 }
3605 
3606                 // Add trailing zeros in the case precision is greater than
3607                 // the number of available digits after the decimal separator.
3608                 trailingZeros(mant, nzeros);
3609 
3610                 StringBuilder exp = bdl.exponent();
3611                 int newW = width;
3612                 if (width != -1) {
3613                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3614                 }
3615                 localizedMagnitude(sb, mant, 0, f, newW, l);
3616 
3617                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3618 
3619                 Flags flags = f.dup().remove(Flags.GROUP);
3620                 char sign = exp.charAt(0);
3621                 assert(sign == '+' || sign == '-');
3622                 sb.append(sign);
3623 
3624                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));


3625             } else if (c == Conversion.DECIMAL_FLOAT) {
3626                 // Create a new BigDecimal with the desired precision.
3627                 int prec = (precision == -1 ? 6 : precision);
3628                 int scale = value.scale();
3629 
3630                 if (scale > prec) {
3631                     // more "scale" digits than the requested "precision"
3632                     int compPrec = value.precision();
3633                     if (compPrec <= scale) {
3634                         // case of 0.xxxxxx
3635                         value = value.setScale(prec, RoundingMode.HALF_UP);
3636                     } else {
3637                         compPrec -= (scale - prec);
3638                         value = new BigDecimal(value.unscaledValue(),
3639                                                scale,
3640                                                new MathContext(compPrec));
3641                     }
3642                 }
3643                 BigDecimalLayout bdl = new BigDecimalLayout(
3644                                            value.unscaledValue(),
3645                                            value.scale(),
3646                                            BigDecimalLayoutForm.DECIMAL_FLOAT);
3647 
3648                 StringBuilder mant = bdl.mantissa();
3649                 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3650 
3651                 // Add a decimal point if necessary.  The mantissa may not
3652                 // contain a decimal point if the scale is zero (the internal
3653                 // representation has no fractional part).  Append a decimal
3654                 // point if '#' is set or we require zero padding to get to the
3655                 // requested precision.
3656                 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3657                         || nzeros > 0)) {
3658                     mant.append('.');
3659                 }
3660 
3661                 // Add trailing zeros if the precision is greater than the
3662                 // number of available digits after the decimal separator.
3663                 trailingZeros(mant, nzeros);
3664 
3665                 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3666             } else if (c == Conversion.GENERAL) {
3667                 int prec = precision;
3668                 if (precision == -1)
3669                     prec = 6;
3670                 else if (precision == 0)
3671                     prec = 1;
3672 
3673                 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3674                 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3675                 if ((value.equals(BigDecimal.ZERO))
3676                     || ((value.compareTo(tenToTheNegFour) != -1)
3677                         && (value.compareTo(tenToThePrec) == -1))) {
3678 
3679                     int e = - value.scale()
3680                         + (value.unscaledValue().toString().length() - 1);
3681 
3682                     // xxx.yyy
3683                     //   g precision (# sig digits) = #x + #y
3684                     //   f precision = #y
3685                     //   exponent = #x - 1


3704         }
3705 
3706         private class BigDecimalLayout {
3707             private StringBuilder mant;
3708             private StringBuilder exp;
3709             private boolean dot = false;
3710             private int scale;
3711 
3712             public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3713                 layout(intVal, scale, form);
3714             }
3715 
3716             public boolean hasDot() {
3717                 return dot;
3718             }
3719 
3720             public int scale() {
3721                 return scale;
3722             }
3723 
3724             public StringBuilder mantissa() {
3725                 return mant;










3726             }
3727 
3728             // The exponent will be formatted as a sign ('+' or '-') followed
3729             // by the exponent zero-padded to include at least two digits.
3730             public StringBuilder exponent() {
3731                 return exp;








3732             }
3733 
3734             private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3735                 String coeff = intVal.toString();
3736                 this.scale = scale;
3737 
3738                 // Construct a buffer, with sufficient capacity for all cases.
3739                 // If E-notation is needed, length will be: +1 if negative, +1
3740                 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3741                 // exponent.  Otherwise it could have +1 if negative, plus
3742                 // leading "0.00000"
3743                 int len = coeff.length();
3744                 mant = new StringBuilder(len + 14);
3745 
3746                 if (scale == 0) {

3747                     if (len > 1) {
3748                         mant.append(coeff.charAt(0));
3749                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3750                             mant.append('.');
3751                             dot = true;
3752                             mant.append(coeff, 1, len);
3753                             exp = new StringBuilder("+");
3754                             if (len < 10) {
3755                                 exp.append('0').append(len - 1);
3756                             } else {
3757                                 exp.append(len - 1);
3758                             }
3759                         } else {
3760                             mant.append(coeff, 1, len);
3761                         }
3762                     } else {
3763                         mant.append(coeff);
3764                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3765                             exp = new StringBuilder("+00");
3766                         }
3767                     }
3768                     return;
3769                 }
3770                 long adjusted = -(long) scale + (len - 1);
3771                 if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3772                     // count of padding zeros
3773                     int pad = scale - len;
3774                     if (pad >= 0) {
3775                         // 0.xxx form
3776                         mant.append("0.");
3777                         dot = true;
3778                         trailingZeros(mant, pad);
3779                         mant.append(coeff);
3780                     } else {
3781                         if (-pad < len) {
3782                             // xx.xx form
3783                             mant.append(coeff, 0, -pad);
3784                             mant.append('.');
3785                             dot = true;
3786                             mant.append(coeff, -pad, -pad + scale);
3787                         } else {
3788                             // xx form
3789                             mant.append(coeff, 0, len);
3790                             trailingZeros(mant, -scale);

3791                             this.scale = 0;
3792                         }
3793                     }
3794                 } else {
3795                     // x.xxx form
3796                     mant.append(coeff.charAt(0));
3797                     if (len > 1) {
3798                         mant.append('.');
3799                         dot = true;
3800                         mant.append(coeff, 1, len);
3801                     }
3802                     exp = new StringBuilder();
3803                     if (adjusted != 0) {
3804                         long abs = Math.abs(adjusted);
3805                         // require sign
3806                         exp.append(adjusted < 0 ? '-' : '+');
3807                         if (abs < 10) {
3808                             exp.append('0');
3809                         }
3810                         exp.append(abs);
3811                     } else {
3812                         exp.append("+00");
3813                     }
3814                 }
3815             }
3816         }
3817 
3818         private int adjustWidth(int width, Flags f, boolean neg) {
3819             int newW = width;
3820             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3821                 newW--;
3822             return newW;
3823         }
3824 
3825         // Add trailing zeros
3826         private void trailingZeros(StringBuilder sb, int nzeros) {
3827             for (int i = 0; i < nzeros; i++) {
3828                 sb.append('0');














3829             }

3830         }
3831 
3832         private void print(Calendar t, char c, Locale l)  throws IOException {

3833             StringBuilder sb = new StringBuilder();
3834             print(sb, t, c, l);
3835 
3836             // justify based on width
3837             if (f.contains(Flags.UPPERCASE)) {
3838                 appendJustified(a, sb.toString().toUpperCase());
3839             } else {
3840                 appendJustified(a, sb);
3841             }
3842         }
3843 
3844         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3845                 throws IOException {


3846             if (sb == null)
3847                 sb = new StringBuilder();
3848             switch (c) {
3849             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3850             case DateTime.HOUR_0:        // 'I' (01 - 12)
3851             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3852             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3853                 int i = t.get(Calendar.HOUR_OF_DAY);
3854                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3855                     i = (i == 0 || i == 12 ? 12 : i % 12);
3856                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3857                                || c == DateTime.HOUR_0
3858                                ? Flags.ZERO_PAD
3859                                : Flags.NONE);
3860                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3861                 break;
3862             }
3863             case DateTime.MINUTE:      { // 'M' (00 - 59)
3864                 int i = t.get(Calendar.MINUTE);
3865                 Flags flags = Flags.ZERO_PAD;


3998             // Composites
3999             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4000             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4001                 char sep = ':';
4002                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4003                 print(sb, t, DateTime.MINUTE, l);
4004                 if (c == DateTime.TIME) {
4005                     sb.append(sep);
4006                     print(sb, t, DateTime.SECOND, l);
4007                 }
4008                 break;
4009             }
4010             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4011                 char sep = ':';
4012                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4013                 print(sb, t, DateTime.MINUTE, l).append(sep);
4014                 print(sb, t, DateTime.SECOND, l).append(' ');
4015                 // this may be in wrong place for some locales
4016                 StringBuilder tsb = new StringBuilder();
4017                 print(tsb, t, DateTime.AM_PM, l);
4018 
4019                 sb.append(tsb.toString().toUpperCase(l != null ? l : Locale.US));
4020                 break;
4021             }
4022             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4023                 char sep = ' ';
4024                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4025                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4026                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4027                 print(sb, t, DateTime.TIME, l).append(sep);
4028                 print(sb, t, DateTime.ZONE, l).append(sep);
4029                 print(sb, t, DateTime.YEAR_4, l);
4030                 break;
4031             }
4032             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4033                 char sep = '/';
4034                 print(sb, t, DateTime.MONTH, l).append(sep);
4035                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4036                 print(sb, t, DateTime.YEAR_2, l);
4037                 break;
4038             }
4039             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4040                 char sep = '-';
4041                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4042                 print(sb, t, DateTime.MONTH, l).append(sep);
4043                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4044                 break;
4045             }
4046             default:
4047                 assert false;
4048             }
4049             return sb;
4050         }
4051 
4052         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4053             StringBuilder sb = new StringBuilder();
4054             print(sb, t, c, l);
4055             // justify based on width
4056             if (f.contains(Flags.UPPERCASE)) {
4057                 appendJustified(a, sb.toString().toUpperCase());
4058             } else {
4059                 appendJustified(a, sb);
4060             }
4061         }
4062 
4063         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4064                                  Locale l) throws IOException {
4065             if (sb == null)
4066                 sb = new StringBuilder();
4067             try {
4068                 switch (c) {
4069                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4070                     int i = t.get(ChronoField.HOUR_OF_DAY);
4071                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4072                     break;
4073                 }
4074                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4075                     int i = t.get(ChronoField.HOUR_OF_DAY);
4076                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4077                     break;
4078                 }
4079                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4080                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);


4288 
4289         // -- Methods to support throwing exceptions --
4290 
4291         private void failMismatch(Flags f, char c) {
4292             String fs = f.toString();
4293             throw new FormatFlagsConversionMismatchException(fs, c);
4294         }
4295 
4296         private void failConversion(char c, Object arg) {
4297             throw new IllegalFormatConversionException(c, arg.getClass());
4298         }
4299 
4300         private char getZero(Locale l) {
4301             if ((l != null) &&  !l.equals(locale())) {
4302                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4303                 return dfs.getZeroDigit();
4304             }
4305             return zero;
4306         }
4307 
4308         private StringBuilder localizedMagnitude(StringBuilder sb,
4309                 long value, Flags f, int width, Locale l) {
4310             return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);



4311         }
4312 
4313         private StringBuilder localizedMagnitude(StringBuilder sb,
4314                 CharSequence value, final int offset, Flags f, int width,
4315                 Locale l) {
4316             if (sb == null) {

4317                 sb = new StringBuilder();
4318             }
4319             int begin = sb.length();
4320 
4321             char zero = getZero(l);
4322 
4323             // determine localized grouping separator and size
4324             char grpSep = '\0';
4325             int  grpSize = -1;
4326             char decSep = '\0';
4327 
4328             int len = value.length();
4329             int dot = len;
4330             for (int j = offset; j < len; j++) {
4331                 if (value.charAt(j) == '.') {
4332                     dot = j;
4333                     break;
4334                 }
4335             }
4336 
4337             if (dot < len) {
4338                 if (l == null || l.equals(Locale.US)) {
4339                     decSep  = '.';
4340                 } else {
4341                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4342                     decSep  = dfs.getDecimalSeparator();
4343                 }
4344             }
4345 
4346             if (f.contains(Flags.GROUP)) {
4347                 if (l == null || l.equals(Locale.US)) {
4348                     grpSep = ',';
4349                     grpSize = 3;
4350                 } else {
4351                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4352                     grpSep = dfs.getGroupingSeparator();
4353                     DecimalFormat df = (DecimalFormat) NumberFormat.getIntegerInstance(l);
4354                     grpSize = df.getGroupingSize();
4355                 }
4356             }
4357 
4358             // localize the digits inserting group separators as necessary
4359             for (int j = offset; j < len; j++) {
4360                 if (j == dot) {
4361                     sb.append(decSep);
4362                     // no more group separators after the decimal separator
4363                     grpSep = '\0';
4364                     continue;
4365                 }
4366 
4367                 char c = value.charAt(j);
4368                 sb.append((char) ((c - '0') + zero));
4369                 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4370                     sb.append(grpSep);
4371                 }
4372             }
4373 
4374             // apply zero padding
4375             if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4376                 sb.ensureCapacity(width);
4377                 for (int k = sb.length(); k < width; k++) {
4378                     sb.insert(begin, zero);
4379                 }
4380             }
4381 
4382             return sb;
4383         }
4384     }
4385 
4386     private static class Flags {
4387         private int flags;
4388 
4389         static final Flags NONE          = new Flags(0);      // ''
4390 
4391         // duplicate declarations from Formattable.java
4392         static final Flags LEFT_JUSTIFY  = new Flags(1<<0);   // '-'
4393         static final Flags UPPERCASE     = new Flags(1<<1);   // '^'
4394         static final Flags ALTERNATE     = new Flags(1<<2);   // '#'
4395 
4396         // numerics
4397         static final Flags PLUS          = new Flags(1<<3);   // '+'
4398         static final Flags LEADING_SPACE = new Flags(1<<4);   // ' '
4399         static final Flags ZERO_PAD      = new Flags(1<<5);   // '0'
4400         static final Flags GROUP         = new Flags(1<<6);   // ','


4412         }
4413 
4414         public boolean contains(Flags f) {
4415             return (flags & f.valueOf()) == f.valueOf();
4416         }
4417 
4418         public Flags dup() {
4419             return new Flags(flags);
4420         }
4421 
4422         private Flags add(Flags f) {
4423             flags |= f.valueOf();
4424             return this;
4425         }
4426 
4427         public Flags remove(Flags f) {
4428             flags &= ~f.valueOf();
4429             return this;
4430         }
4431 
4432         public static Flags parse(String s, int start, int end) {

4433             Flags f = new Flags(0);
4434             for (int i = start; i < end; i++) {
4435                 char c = s.charAt(i);
4436                 Flags v = parse(c);
4437                 if (f.contains(v))
4438                     throw new DuplicateFormatFlagsException(v.toString());
4439                 f.add(v);
4440             }
4441             return f;
4442         }
4443 
4444         // parse those flags which may be provided by users
4445         private static Flags parse(char c) {
4446             switch (c) {
4447             case '-': return LEFT_JUSTIFY;
4448             case '#': return ALTERNATE;
4449             case '+': return PLUS;
4450             case ' ': return LEADING_SPACE;
4451             case '0': return ZERO_PAD;
4452             case ',': return GROUP;
4453             case '(': return PARENTHESES;
4454             case '<': return PREVIOUS;
4455             default: