< prev index next >

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

Print this page




  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util;
  27 
  28 import java.io.BufferedWriter;
  29 import java.io.Closeable;
  30 import java.io.IOException;
  31 import java.io.File;
  32 import java.io.FileOutputStream;
  33 import java.io.FileNotFoundException;
  34 import java.io.Flushable;
  35 import java.io.OutputStream;
  36 import java.io.OutputStreamWriter;
  37 import java.io.PrintStream;
  38 import java.io.UnsupportedEncodingException;

  39 import java.math.BigDecimal;
  40 import java.math.BigInteger;
  41 import java.math.MathContext;
  42 import java.math.RoundingMode;
  43 import java.nio.charset.Charset;
  44 import java.nio.charset.IllegalCharsetNameException;
  45 import java.nio.charset.UnsupportedCharsetException;
  46 import java.text.DateFormatSymbols;
  47 import java.text.DecimalFormat;
  48 import java.text.DecimalFormatSymbols;
  49 import java.text.NumberFormat;
  50 import java.text.spi.NumberFormatProvider;
  51 import java.util.regex.Matcher;
  52 import java.util.regex.Pattern;
  53 import java.util.Objects;
  54 
  55 import java.time.DateTimeException;
  56 import java.time.Instant;
  57 import java.time.ZoneId;
  58 import java.time.ZoneOffset;
  59 import java.time.temporal.ChronoField;
  60 import java.time.temporal.TemporalAccessor;
  61 import java.time.temporal.TemporalQueries;
  62 import java.time.temporal.UnsupportedTemporalTypeException;
  63 





  64 import jdk.internal.math.DoubleConsts;
  65 import jdk.internal.math.FormattedFloatingDecimal;
  66 import sun.util.locale.provider.LocaleProviderAdapter;
  67 import sun.util.locale.provider.ResourceBundleBasedAdapter;
  68 





  69 /**
  70  * An interpreter for printf-style format strings.  This class provides support
  71  * for layout justification and alignment, common formats for numeric, string,
  72  * and date/time data, and locale-specific output.  Common Java types such as
  73  * {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
  74  * are supported.  Limited formatting customization for arbitrary user types is
  75  * provided through the {@link Formattable} interface.
  76  *
  77  * <p> Formatters are not necessarily safe for multithreaded access.  Thread
  78  * safety is optional and is the responsibility of users of methods in this
  79  * class.
  80  *
  81  * <p> Formatted printing for the Java language is heavily inspired by C's
  82  * {@code printf}.  Although the format strings are similar to C, some
  83  * customizations have been made to accommodate the Java language and exploit
  84  * some of its features.  Also, Java formatting is more strict than C's; for
  85  * example, if a conversion is incompatible with a flag, an exception will be
  86  * thrown.  In C inapplicable flags are silently ignored.  The format strings
  87  * are thus intended to be recognizable to C programmers but not necessarily
  88  * completely compatible with those in C.


1896  *   // "c" and "d" are ignored because they are not referenced
1897  * </pre></blockquote>
1898  *
1899  * <p> The maximum number of arguments is limited by the maximum dimension of a
1900  * Java array as defined by
1901  * <cite>The Java&trade; Virtual Machine Specification</cite>.
1902  * If the argument index does not correspond to an
1903  * available argument, then a {@link MissingFormatArgumentException} is thrown.
1904  *
1905  * <p> If there are more arguments than format specifiers, the extra arguments
1906  * are ignored.
1907  *
1908  * <p> Unless otherwise specified, passing a {@code null} argument to any
1909  * method or constructor in this class will cause a {@link
1910  * NullPointerException} to be thrown.
1911  *
1912  * @author  Iris Clark
1913  * @since 1.5
1914  */
1915 public final class Formatter implements Closeable, Flushable {
1916     private Appendable a;
1917     private final Locale l;
1918 
1919     private IOException lastException;
1920 
1921     private final char zero;
1922     private static double scaleUp;
1923 
1924     // 1 (sign) + 19 (max # sig digits) + 1 ('.') + 1 ('e') + 1 (sign)
1925     // + 3 (max # exp digits) + 4 (error) = 30
1926     private static final int MAX_FD_CHARS = 30;




1927 
1928     /**
1929      * Returns a charset object for the given charset name.
1930      * @throws NullPointerException          is csn is null
1931      * @throws UnsupportedEncodingException  if the charset is not supported
1932      */
1933     private static Charset toCharset(String csn)
1934         throws UnsupportedEncodingException
1935     {
1936         Objects.requireNonNull(csn, "charsetName");
1937         try {
1938             return Charset.forName(csn);
1939         } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {
1940             // UnsupportedEncodingException should be thrown
1941             throw new UnsupportedEncodingException(csn);
1942         }
1943     }
1944 
1945     private static final Appendable nonNullAppendable(Appendable a) {
1946         if (a == null)


2588      *         Arguments referenced by the format specifiers in the format
2589      *         string.  If there are more arguments than format specifiers, the
2590      *         extra arguments are ignored.  The maximum number of arguments is
2591      *         limited by the maximum dimension of a Java array as defined by
2592      *         <cite>The Java&trade; Virtual Machine Specification</cite>.
2593      *
2594      * @throws  IllegalFormatException
2595      *          If a format string contains an illegal syntax, a format
2596      *          specifier that is incompatible with the given arguments,
2597      *          insufficient arguments given the format string, or other
2598      *          illegal conditions.  For specification of all possible
2599      *          formatting errors, see the <a href="#detail">Details</a>
2600      *          section of the formatter class specification.
2601      *
2602      * @throws  FormatterClosedException
2603      *          If this formatter has been closed by invoking its {@link
2604      *          #close()} method
2605      *
2606      * @return  This formatter
2607      */

2608     public Formatter format(String format, Object ... args) {
2609         return format(l, format, args);
2610     }
2611 
2612     /**
2613      * Writes a formatted string to this object's destination using the
2614      * specified locale, format string, and arguments.
2615      *
2616      * @param  l
2617      *         The {@linkplain java.util.Locale locale} to apply during
2618      *         formatting.  If {@code l} is {@code null} then no localization
2619      *         is applied.  This does not change this object's locale that was
2620      *         set during construction.
2621      *
2622      * @param  format
2623      *         A format string as described in <a href="#syntax">Format string
2624      *         syntax</a>
2625      *
2626      * @param  args
2627      *         Arguments referenced by the format specifiers in the format
2628      *         string.  If there are more arguments than format specifiers, the
2629      *         extra arguments are ignored.  The maximum number of arguments is
2630      *         limited by the maximum dimension of a Java array as defined by
2631      *         <cite>The Java&trade; Virtual Machine Specification</cite>.
2632      *
2633      * @throws  IllegalFormatException
2634      *          If a format string contains an illegal syntax, a format
2635      *          specifier that is incompatible with the given arguments,
2636      *          insufficient arguments given the format string, or other
2637      *          illegal conditions.  For specification of all possible
2638      *          formatting errors, see the <a href="#detail">Details</a>
2639      *          section of the formatter class specification.
2640      *
2641      * @throws  FormatterClosedException
2642      *          If this formatter has been closed by invoking its {@link
2643      *          #close()} method
2644      *
2645      * @return  This formatter
2646      */

2647     public Formatter format(Locale l, String format, Object ... args) {

2648         ensureOpen();
2649 
2650         // index of last argument referenced
2651         int last = -1;
2652         // last ordinary index
2653         int lasto = -1;
2654 
2655         List<FormatString> fsa = parse(format);
2656         for (FormatString fs : fsa) {
2657             int index = fs.index();
2658             try {

2659                 switch (index) {
2660                 case -2:  // fixed string, "%n", or "%%"
2661                     fs.print(null, l);
2662                     break;
2663                 case -1:  // relative index
2664                     if (last < 0 || (args != null && last > args.length - 1))
2665                         throw new MissingFormatArgumentException(fs.toString());
2666                     fs.print((args == null ? null : args[last]), l);
2667                     break;
2668                 case 0:  // ordinary index
2669                     lasto++;
2670                     last = lasto;
2671                     if (args != null && lasto > args.length - 1)
2672                         throw new MissingFormatArgumentException(fs.toString());
2673                     fs.print((args == null ? null : args[lasto]), l);
2674                     break;
2675                 default:  // explicit index
2676                     last = index - 1;
2677                     if (args != null && last > args.length - 1)
2678                         throw new MissingFormatArgumentException(fs.toString());
2679                     fs.print((args == null ? null : args[last]), l);
2680                     break;




2681                 }
2682             } catch (IOException x) {
2683                 lastException = x;
2684             }
2685         }
2686         return this;
2687     }
2688 
2689     // %[argument_index$][flags][width][.precision][t]conversion
2690     private static final String formatSpecifier
2691         = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
2692 
2693     private static Pattern fsPattern = Pattern.compile(formatSpecifier);
2694 
2695     /**
2696      * Finds format specifiers in the format string.
2697      */
2698     private List<FormatString> parse(String s) {
2699         ArrayList<FormatString> al = new ArrayList<>();
2700         Matcher m = fsPattern.matcher(s);
2701         for (int i = 0, len = s.length(); i < len; ) {
2702             if (m.find(i)) {
2703                 // Anything between the start of the string and the beginning
2704                 // of the format specifier is either fixed text or contains
2705                 // an invalid format string.
2706                 if (m.start() != i) {
2707                     // Make sure we didn't miss any invalid format specifiers
2708                     checkText(s, i, m.start());
2709                     // Assume previous characters were fixed text
2710                     al.add(new FixedString(s, i, m.start()));
2711                 }
2712 
2713                 al.add(new FormatSpecifier(s, m));
2714                 i = m.end();
2715             } else {
2716                 // No more valid format specifiers.  Check for possible invalid
2717                 // format specifiers.
2718                 checkText(s, i, len);
2719                 // The rest of the string is fixed text
2720                 al.add(new FixedString(s, i, s.length()));
2721                 break;
2722             }
2723         }
2724         return al;
2725     }
2726 
2727     private static void checkText(String s, int start, int end) {
2728         for (int i = start; i < end; i++) {
2729             // Any '%' found in the region starts an invalid format specifier.
2730             if (s.charAt(i) == '%') {
2731                 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
2732                 throw new UnknownFormatConversionException(String.valueOf(c));
2733             }
2734         }
2735     }
2736 
2737     private interface FormatString {
2738         int index();
2739         void print(Object arg, Locale l) throws IOException;
2740         String toString();
2741     }
2742 
2743     private class FixedString implements FormatString {
2744         private String s;
2745         private int start;
2746         private int end;
2747         FixedString(String s, int start, int end) {
2748             this.s = s;
2749             this.start = start;
2750             this.end = end;
2751         }
2752         public int index() { return -2; }
2753         public void print(Object arg, Locale l)
2754             throws IOException { a.append(s, start, end); }
2755         public String toString() { return s.substring(start, end); }
2756     }
2757 
2758     /**
2759      * Enum for {@code BigDecimal} formatting.
2760      */
2761     public enum BigDecimalLayoutForm {
2762         /**
2763          * Format the {@code BigDecimal} in computerized scientific notation.
2764          */
2765         SCIENTIFIC,
2766 
2767         /**
2768          * Format the {@code BigDecimal} as a decimal number.
2769          */
2770         DECIMAL_FLOAT
2771     };
2772 
2773     private class FormatSpecifier implements FormatString {
2774         private int index = -1;
2775         private Flags f = Flags.NONE;
2776         private int width;
2777         private int precision;
2778         private boolean dt = false;
2779         private char c;
2780 
2781         private int index(String s, int start, int end) {
2782             if (start >= 0) {
2783                 try {
2784                     // skip the trailing '$'
2785                     index = Integer.parseInt(s, start, end - 1, 10);
2786                 } catch (NumberFormatException x) {
2787                     assert(false);
2788                 }
2789             } else {
2790                 index = 0;
2791             }
2792             return index;
2793         }
2794 
2795         public int index() {
2796             return index;
2797         }
2798 
2799         private Flags flags(String s, int start, int end) {
2800             f = Flags.parse(s, start, end);
2801             if (f.contains(Flags.PREVIOUS))
2802                 index = -1;
2803             return f;
2804         }
2805 
2806         private int width(String s, int start, int end) {
2807             width = -1;
2808             if (start >= 0) {
2809                 try {
2810                     width = Integer.parseInt(s, start, end, 10);
2811                     if (width < 0)
2812                         throw new IllegalFormatWidthException(width);
2813                 } catch (NumberFormatException x) {
2814                     assert(false);
2815                 }
2816             }
2817             return width;
2818         }
2819 
2820         private int precision(String s, int start, int end) {
2821             precision = -1;
2822             if (start >= 0) {
2823                 try {
2824                     // skip the leading '.'
2825                     precision = Integer.parseInt(s, start + 1, end, 10);
2826                     if (precision < 0)
2827                         throw new IllegalFormatPrecisionException(precision);
2828                 } catch (NumberFormatException x) {
2829                     assert(false);
2830                 }
2831             }
2832             return precision;
2833         }
2834 
2835         private char conversion(char conv) {
2836             c = conv;
2837             if (!dt) {
2838                 if (!Conversion.isValid(c)) {
2839                     throw new UnknownFormatConversionException(String.valueOf(c));
2840                 }
2841                 if (Character.isUpperCase(c)) {
2842                     f.add(Flags.UPPERCASE);
2843                     c = Character.toLowerCase(c);
2844                 }
2845                 if (Conversion.isText(c)) {
2846                     index = -2;
2847                 }
2848             }
2849             return c;
2850         }
2851 
2852         FormatSpecifier(String s, Matcher m) {
2853             index(s, m.start(1), m.end(1));
2854             flags(s, m.start(2), m.end(2));
2855             width(s, m.start(3), m.end(3));
2856             precision(s, m.start(4), m.end(4));
2857 
2858             int tTStart = m.start(5);
2859             if (tTStart >= 0) {
2860                 dt = true;
2861                 if (s.charAt(tTStart) == 'T') {
2862                     f.add(Flags.UPPERCASE);
2863                 }
2864             }
2865             conversion(s.charAt(m.start(6)));
2866 
2867             if (dt)
2868                 checkDateTime();
2869             else if (Conversion.isGeneral(c))
2870                 checkGeneral();
2871             else if (Conversion.isCharacter(c))
2872                 checkCharacter();
2873             else if (Conversion.isInteger(c))
2874                 checkInteger();
2875             else if (Conversion.isFloat(c))
2876                 checkFloat();
2877             else if (Conversion.isText(c))
2878                 checkText();
2879             else
2880                 throw new UnknownFormatConversionException(String.valueOf(c));
2881         }
2882 
2883         public void print(Object arg, Locale l) throws IOException {
2884             if (dt) {
2885                 printDateTime(arg, l);
2886                 return;
2887             }
2888             switch(c) {
2889             case Conversion.DECIMAL_INTEGER:
2890             case Conversion.OCTAL_INTEGER:
2891             case Conversion.HEXADECIMAL_INTEGER:
2892                 printInteger(arg, l);
2893                 break;
2894             case Conversion.SCIENTIFIC:
2895             case Conversion.GENERAL:
2896             case Conversion.DECIMAL_FLOAT:
2897             case Conversion.HEXADECIMAL_FLOAT:
2898                 printFloat(arg, l);
2899                 break;
2900             case Conversion.CHARACTER:
2901             case Conversion.CHARACTER_UPPER:
2902                 printCharacter(arg, l);
2903                 break;
2904             case Conversion.BOOLEAN:
2905                 printBoolean(arg, l);
2906                 break;
2907             case Conversion.STRING:
2908                 printString(arg, l);
2909                 break;
2910             case Conversion.HASHCODE:
2911                 printHashCode(arg, l);
2912                 break;
2913             case Conversion.LINE_SEPARATOR:
2914                 a.append(System.lineSeparator());
2915                 break;
2916             case Conversion.PERCENT_SIGN:
2917                 print("%", l);
2918                 break;
2919             default:
2920                 assert false;
2921             }
2922         }


2923 
2924         private void printInteger(Object arg, Locale l) throws IOException {
2925             if (arg == null)
2926                 print("null", l);
2927             else if (arg instanceof Byte)
2928                 print(((Byte)arg).byteValue(), l);
2929             else if (arg instanceof Short)
2930                 print(((Short)arg).shortValue(), l);
2931             else if (arg instanceof Integer)
2932                 print(((Integer)arg).intValue(), l);
2933             else if (arg instanceof Long)
2934                 print(((Long)arg).longValue(), l);
2935             else if (arg instanceof BigInteger)
2936                 print(((BigInteger)arg), l);
2937             else
2938                 failConversion(c, arg);
2939         }
2940 
2941         private void printFloat(Object arg, Locale l) throws IOException {
2942             if (arg == null)
2943                 print("null", l);
2944             else if (arg instanceof Float)
2945                 print(((Float)arg).floatValue(), l);
2946             else if (arg instanceof Double)
2947                 print(((Double)arg).doubleValue(), l);
2948             else if (arg instanceof BigDecimal)
2949                 print(((BigDecimal)arg), l);
2950             else
2951                 failConversion(c, arg);
2952         }
2953 
2954         private void printDateTime(Object arg, Locale l) throws IOException {
2955             if (arg == null) {
2956                 print("null", l);
2957                 return;
2958             }
2959             Calendar cal = null;
2960 
2961             // Instead of Calendar.setLenient(true), perhaps we should
2962             // wrap the IllegalArgumentException that might be thrown?
2963             if (arg instanceof Long) {
2964                 // Note that the following method uses an instance of the
2965                 // default time zone (TimeZone.getDefaultRef().
2966                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2967                 cal.setTimeInMillis((Long)arg);
2968             } else if (arg instanceof Date) {
2969                 // Note that the following method uses an instance of the
2970                 // default time zone (TimeZone.getDefaultRef().
2971                 cal = Calendar.getInstance(l == null ? Locale.US : l);
2972                 cal.setTime((Date)arg);
2973             } else if (arg instanceof Calendar) {
2974                 cal = (Calendar) ((Calendar) arg).clone();
2975                 cal.setLenient(true);
2976             } else if (arg instanceof TemporalAccessor) {
2977                 print((TemporalAccessor) arg, c, l);
2978                 return;
2979             } else {
2980                 failConversion(c, arg);
2981             }
2982             // Use the provided locale so that invocations of
2983             // localizedMagnitude() use optimizations for null.
2984             print(cal, c, l);
2985         }
2986 
2987         private void printCharacter(Object arg, Locale l) throws IOException {
2988             if (arg == null) {
2989                 print("null", l);
2990                 return;
2991             }
2992             String s = null;
2993             if (arg instanceof Character) {
2994                 s = ((Character)arg).toString();
2995             } else if (arg instanceof Byte) {
2996                 byte i = ((Byte)arg).byteValue();
2997                 if (Character.isValidCodePoint(i))
2998                     s = new String(Character.toChars(i));
2999                 else
3000                     throw new IllegalFormatCodePointException(i);
3001             } else if (arg instanceof Short) {
3002                 short i = ((Short)arg).shortValue();
3003                 if (Character.isValidCodePoint(i))
3004                     s = new String(Character.toChars(i));
3005                 else
3006                     throw new IllegalFormatCodePointException(i);
3007             } else if (arg instanceof Integer) {
3008                 int i = ((Integer)arg).intValue();
3009                 if (Character.isValidCodePoint(i))
3010                     s = new String(Character.toChars(i));
3011                 else
3012                     throw new IllegalFormatCodePointException(i);
3013             } else {
3014                 failConversion(c, arg);
3015             }
3016             print(s, l);
3017         }

3018 
3019         private void printString(Object arg, Locale l) throws IOException {
3020             if (arg instanceof Formattable) {
3021                 Formatter fmt = Formatter.this;
3022                 if (fmt.locale() != l)
3023                     fmt = new Formatter(fmt.out(), l);
3024                 ((Formattable)arg).formatTo(fmt, f.valueOf(), width, precision);
3025             } else {
3026                 if (f.contains(Flags.ALTERNATE))
3027                     failMismatch(Flags.ALTERNATE, 's');
3028                 if (arg == null)
3029                     print("null", l);
3030                 else
3031                     print(arg.toString(), l);
3032             }






3033         }




3034 
3035         private void printBoolean(Object arg, Locale l) throws IOException {
3036             String s;
3037             if (arg != null)
3038                 s = ((arg instanceof Boolean)
3039                      ? ((Boolean)arg).toString()
3040                      : Boolean.toString(true));






3041             else
3042                 s = Boolean.toString(false);
3043             print(s, l);













3044         }


3045 
3046         private void printHashCode(Object arg, Locale l) throws IOException {
3047             String s = (arg == null
3048                         ? "null"
3049                         : Integer.toHexString(arg.hashCode()));
3050             print(s, l);








3051         }

3052 
3053         private void print(String s, Locale l) throws IOException {
3054             if (precision != -1 && precision < s.length())
3055                 s = s.substring(0, precision);
3056             if (f.contains(Flags.UPPERCASE))
3057                 s = toUpperCaseWithLocale(s, l);
3058             appendJustified(a, s);
3059         }



3060 
3061         private String toUpperCaseWithLocale(String s, Locale l) {
3062             return s.toUpperCase(Objects.requireNonNullElse(l,
3063                     Locale.getDefault(Locale.Category.FORMAT)));
3064         }
3065 
3066         private Appendable appendJustified(Appendable a, CharSequence cs) throws IOException {
3067              if (width == -1) {
3068                  return a.append(cs);
3069              }
3070              boolean padRight = f.contains(Flags.LEFT_JUSTIFY);
3071              int sp = width - cs.length();
3072              if (padRight) {
3073                  a.append(cs);
3074              }
3075              for (int i = 0; i < sp; i++) {
3076                  a.append(' ');
3077              }
3078              if (!padRight) {
3079                  a.append(cs);
3080              }
3081              return a;
3082         }
3083 
3084         public String toString() {
3085             StringBuilder sb = new StringBuilder("%");
3086             // Flags.UPPERCASE is set internally for legal conversions.
3087             Flags dupf = f.dup().remove(Flags.UPPERCASE);
3088             sb.append(dupf.toString());
3089             if (index > 0)
3090                 sb.append(index).append('$');
3091             if (width != -1)
3092                 sb.append(width);
3093             if (precision != -1)
3094                 sb.append('.').append(precision);
3095             if (dt)
3096                 sb.append(f.contains(Flags.UPPERCASE) ? 'T' : 't');
3097             sb.append(f.contains(Flags.UPPERCASE)
3098                       ? Character.toUpperCase(c) : c);
3099             return sb.toString();
3100         }
3101 
3102         private void checkGeneral() {
3103             if ((c == Conversion.BOOLEAN || c == Conversion.HASHCODE)
3104                 && f.contains(Flags.ALTERNATE))
3105                 failMismatch(Flags.ALTERNATE, c);
3106             // '-' requires a width
3107             if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))
3108                 throw new MissingFormatWidthException(toString());
3109             checkBadFlags(Flags.PLUS, Flags.LEADING_SPACE, Flags.ZERO_PAD,
3110                           Flags.GROUP, Flags.PARENTHESES);
3111         }
3112 
3113         private void checkDateTime() {
3114             if (precision != -1)
3115                 throw new IllegalFormatPrecisionException(precision);
3116             if (!DateTime.isValid(c))
3117                 throw new UnknownFormatConversionException("t" + c);
3118             checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,
3119                           Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);
3120             // '-' requires a width
3121             if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))
3122                 throw new MissingFormatWidthException(toString());
3123         }
3124 
3125         private void checkCharacter() {
3126             if (precision != -1)
3127                 throw new IllegalFormatPrecisionException(precision);
3128             checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,
3129                           Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);
3130             // '-' requires a width
3131             if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))
3132                 throw new MissingFormatWidthException(toString());
3133         }





3134 
3135         private void checkInteger() {
3136             checkNumeric();
3137             if (precision != -1)
3138                 throw new IllegalFormatPrecisionException(precision);
3139 
3140             if (c == Conversion.DECIMAL_INTEGER)
3141                 checkBadFlags(Flags.ALTERNATE);
3142             else if (c == Conversion.OCTAL_INTEGER)
3143                 checkBadFlags(Flags.GROUP);
3144             else
3145                 checkBadFlags(Flags.GROUP);
3146         }


3147 
3148         private void checkBadFlags(Flags ... badFlags) {
3149             for (Flags badFlag : badFlags)
3150                 if (f.contains(badFlag))
3151                     failMismatch(badFlag, c);



3152         }


3153 
3154         private void checkFloat() {
3155             checkNumeric();
3156             if (c == Conversion.DECIMAL_FLOAT) {
3157             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3158                 checkBadFlags(Flags.PARENTHESES, Flags.GROUP);
3159             } else if (c == Conversion.SCIENTIFIC) {
3160                 checkBadFlags(Flags.GROUP);
3161             } else if (c == Conversion.GENERAL) {
3162                 checkBadFlags(Flags.ALTERNATE);
3163             }
3164         }


3165 
3166         private void checkNumeric() {
3167             if (width != -1 && width < 0)
3168                 throw new IllegalFormatWidthException(width);
3169 
3170             if (precision != -1 && precision < 0)
3171                 throw new IllegalFormatPrecisionException(precision);

3172 
3173             // '-' and '0' require a width
3174             if (width == -1
3175                 && (f.contains(Flags.LEFT_JUSTIFY) || f.contains(Flags.ZERO_PAD)))
3176                 throw new MissingFormatWidthException(toString());
3177 
3178             // bad combination
3179             if ((f.contains(Flags.PLUS) && f.contains(Flags.LEADING_SPACE))
3180                 || (f.contains(Flags.LEFT_JUSTIFY) && f.contains(Flags.ZERO_PAD)))
3181                 throw new IllegalFormatFlagsException(f.toString());
3182         }
3183 
3184         private void checkText() {
3185             if (precision != -1)
3186                 throw new IllegalFormatPrecisionException(precision);
3187             switch (c) {
3188             case Conversion.PERCENT_SIGN:
3189                 if (f.valueOf() != Flags.LEFT_JUSTIFY.valueOf()
3190                     && f.valueOf() != Flags.NONE.valueOf())
3191                     throw new IllegalFormatFlagsException(f.toString());
3192                 // '-' requires a width
3193                 if (width == -1 && f.contains(Flags.LEFT_JUSTIFY))
3194                     throw new MissingFormatWidthException(toString());
3195                 break;
3196             case Conversion.LINE_SEPARATOR:
3197                 if (width != -1)
3198                     throw new IllegalFormatWidthException(width);
3199                 if (f.valueOf() != Flags.NONE.valueOf())
3200                     throw new IllegalFormatFlagsException(f.toString());
3201                 break;
3202             default:
3203                 assert false;
3204             }
3205         }
3206 
3207         private void print(byte value, Locale l) throws IOException {
3208             long v = value;
3209             if (value < 0
3210                 && (c == Conversion.OCTAL_INTEGER
3211                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3212                 v += (1L << 8);
3213                 assert v >= 0 : v;
3214             }
3215             print(v, l);

















3216         }
3217 
3218         private void print(short value, Locale l) throws IOException {
3219             long v = value;
3220             if (value < 0
3221                 && (c == Conversion.OCTAL_INTEGER
3222                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3223                 v += (1L << 16);
3224                 assert v >= 0 : v;





3225             }
3226             print(v, l);




3227         }







3228 
3229         private void print(int value, Locale l) throws IOException {
3230             long v = value;
3231             if (value < 0
3232                 && (c == Conversion.OCTAL_INTEGER
3233                     || c == Conversion.HEXADECIMAL_INTEGER)) {
3234                 v += (1L << 32);
3235                 assert v >= 0 : v;































3236             }
3237             print(v, l);





3238         }
3239 
3240         private void print(long value, Locale l) throws IOException {

3241 
3242             StringBuilder sb = new StringBuilder();


3243 
3244             if (c == Conversion.DECIMAL_INTEGER) {
3245                 boolean neg = value < 0;
3246                 String valueStr = Long.toString(value, 10);
3247 
3248                 // leading sign indicator
3249                 leadingSign(sb, neg);
3250 
3251                 // the value
3252                 localizedMagnitude(sb, valueStr, neg ? 1 : 0, f, adjustWidth(width, f, neg), l);
3253 
3254                 // trailing sign indicator
3255                 trailingSign(sb, neg);
3256             } else if (c == Conversion.OCTAL_INTEGER) {
3257                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3258                               Flags.PLUS);
3259                 String s = Long.toOctalString(value);
3260                 int len = (f.contains(Flags.ALTERNATE)
3261                            ? s.length() + 1
3262                            : s.length());
3263 
3264                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3265                 if (f.contains(Flags.ALTERNATE))
3266                     sb.append('0');
3267                 if (f.contains(Flags.ZERO_PAD)) {
3268                     trailingZeros(sb, width - len);
3269                 }
3270                 sb.append(s);
3271             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3272                 checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
3273                               Flags.PLUS);
3274                 String s = Long.toHexString(value);
3275                 int len = (f.contains(Flags.ALTERNATE)
3276                            ? s.length() + 2
3277                            : s.length());
3278 
3279                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3280                 if (f.contains(Flags.ALTERNATE))
3281                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3282                 if (f.contains(Flags.ZERO_PAD)) {
3283                     trailingZeros(sb, width - len);
3284                 }
3285                 if (f.contains(Flags.UPPERCASE))
3286                     s = toUpperCaseWithLocale(s, l);
3287                 sb.append(s);
3288             }
3289 
3290             // justify based on width
3291             appendJustified(a, sb);
3292         }
3293 
3294         // neg := val < 0
3295         private StringBuilder leadingSign(StringBuilder sb, boolean neg) {
3296             if (!neg) {
3297                 if (f.contains(Flags.PLUS)) {
3298                     sb.append('+');
3299                 } else if (f.contains(Flags.LEADING_SPACE)) {
3300                     sb.append(' ');
3301                 }
3302             } else {
3303                 if (f.contains(Flags.PARENTHESES))
3304                     sb.append('(');
3305                 else
3306                     sb.append('-');
3307             }
3308             return sb;
3309         }
3310 
3311         // neg := val < 0
3312         private StringBuilder trailingSign(StringBuilder sb, boolean neg) {
3313             if (neg && f.contains(Flags.PARENTHESES))
3314                 sb.append(')');
3315             return sb;
3316         }
3317 
3318         private void print(BigInteger value, Locale l) throws IOException {
3319             StringBuilder sb = new StringBuilder();
3320             boolean neg = value.signum() == -1;
3321             BigInteger v = value.abs();
3322 
3323             // leading sign indicator
3324             leadingSign(sb, neg);
3325 
3326             // the value
3327             if (c == Conversion.DECIMAL_INTEGER) {
3328                 localizedMagnitude(sb, v.toString(), 0, f, adjustWidth(width, f, neg), l);
3329             } else if (c == Conversion.OCTAL_INTEGER) {
3330                 String s = v.toString(8);
3331 
3332                 int len = s.length() + sb.length();
3333                 if (neg && f.contains(Flags.PARENTHESES))
3334                     len++;
3335 
3336                 // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3337                 if (f.contains(Flags.ALTERNATE)) {
3338                     len++;
3339                     sb.append('0');
3340                 }
3341                 if (f.contains(Flags.ZERO_PAD)) {
3342                     trailingZeros(sb, width - len);
3343                 }
3344                 sb.append(s);
3345             } else if (c == Conversion.HEXADECIMAL_INTEGER) {
3346                 String s = v.toString(16);
3347 
3348                 int len = s.length() + sb.length();
3349                 if (neg && f.contains(Flags.PARENTHESES))
3350                     len++;
3351 
3352                 // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3353                 if (f.contains(Flags.ALTERNATE)) {
3354                     len += 2;
3355                     sb.append(f.contains(Flags.UPPERCASE) ? "0X" : "0x");
3356                 }
3357                 if (f.contains(Flags.ZERO_PAD)) {
3358                     trailingZeros(sb, width - len);
3359                 }
3360                 if (f.contains(Flags.UPPERCASE))
3361                     s = toUpperCaseWithLocale(s, l);
3362                 sb.append(s);
3363             }
3364 
3365             // trailing sign indicator
3366             trailingSign(sb, (value.signum() == -1));
3367 
3368             // justify based on width
3369             appendJustified(a, sb);
3370         }
3371 
3372         private void print(float value, Locale l) throws IOException {
3373             print((double) value, l);
3374         }
3375 
3376         private void print(double value, Locale l) throws IOException {
3377             StringBuilder sb = new StringBuilder();
3378             boolean neg = Double.compare(value, 0.0) == -1;
3379 
3380             if (!Double.isNaN(value)) {
3381                 double v = Math.abs(value);
3382 
3383                 // leading sign indicator
3384                 leadingSign(sb, neg);
3385 
3386                 // the value
3387                 if (!Double.isInfinite(v))
3388                     print(sb, v, l, f, c, precision, neg);
3389                 else
3390                     sb.append(f.contains(Flags.UPPERCASE)
3391                               ? "INFINITY" : "Infinity");
3392 
3393                 // trailing sign indicator
3394                 trailingSign(sb, neg);
3395             } else {
3396                 sb.append(f.contains(Flags.UPPERCASE) ? "NAN" : "NaN");
3397             }
3398 
3399             // justify based on width
3400             appendJustified(a, sb);
3401         }
3402 
3403         // !Double.isInfinite(value) && !Double.isNaN(value)
3404         private void print(StringBuilder sb, double value, Locale l,
3405                            Flags f, char c, int precision, boolean neg)
3406             throws IOException
3407         {
3408             if (c == Conversion.SCIENTIFIC) {
3409                 // Create a new FormattedFloatingDecimal with the desired
3410                 // precision.
3411                 int prec = (precision == -1 ? 6 : precision);
3412 
3413                 FormattedFloatingDecimal fd
3414                         = FormattedFloatingDecimal.valueOf(value, prec,
3415                           FormattedFloatingDecimal.Form.SCIENTIFIC);
3416 
3417                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3418                 addZeros(mant, prec);
3419 
3420                 // If the precision is zero and the '#' flag is set, add the
3421                 // requested decimal point.
3422                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3423                     mant.append('.');
3424                 }
3425 
3426                 char[] exp = (value == 0.0)
3427                     ? new char[] {'+','0','0'} : fd.getExponent();
3428 
3429                 int newW = width;
3430                 if (width != -1) {
3431                     newW = adjustWidth(width - exp.length - 1, f, neg);
3432                 }
3433                 localizedMagnitude(sb, mant, 0, f, newW, l);
3434 
3435                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3436 
3437                 char sign = exp[0];
3438                 assert(sign == '+' || sign == '-');
3439                 sb.append(sign);
3440 
3441                 localizedMagnitudeExp(sb, exp, 1, l);
3442             } else if (c == Conversion.DECIMAL_FLOAT) {
3443                 // Create a new FormattedFloatingDecimal with the desired
3444                 // precision.
3445                 int prec = (precision == -1 ? 6 : precision);
3446 
3447                 FormattedFloatingDecimal fd
3448                         = FormattedFloatingDecimal.valueOf(value, prec,
3449                           FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3450 
3451                 StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3452                 addZeros(mant, prec);
3453 
3454                 // If the precision is zero and the '#' flag is set, add the
3455                 // requested decimal point.
3456                 if (f.contains(Flags.ALTERNATE) && (prec == 0))
3457                     mant.append('.');
3458 
3459                 int newW = width;
3460                 if (width != -1)
3461                     newW = adjustWidth(width, f, neg);
3462                 localizedMagnitude(sb, mant, 0, f, newW, l);
3463             } else if (c == Conversion.GENERAL) {
3464                 int prec = precision;
3465                 if (precision == -1)
3466                     prec = 6;
3467                 else if (precision == 0)
3468                     prec = 1;
3469 
3470                 char[] exp;
3471                 StringBuilder mant = new StringBuilder();
3472                 int expRounded;
3473                 if (value == 0.0) {
3474                     exp = null;
3475                     mant.append('0');
3476                     expRounded = 0;
3477                 } else {
3478                     FormattedFloatingDecimal fd
3479                         = FormattedFloatingDecimal.valueOf(value, prec,
3480                           FormattedFloatingDecimal.Form.GENERAL);
3481                     exp = fd.getExponent();
3482                     mant.append(fd.getMantissa());
3483                     expRounded = fd.getExponentRounded();
3484                 }
3485 
3486                 if (exp != null) {
3487                     prec -= 1;
3488                 } else {
3489                     prec -= expRounded + 1;
3490                 }
3491 
3492                 addZeros(mant, prec);
3493                 // If the precision is zero and the '#' flag is set, add the
3494                 // requested decimal point.
3495                 if (f.contains(Flags.ALTERNATE) && (prec == 0)) {
3496                     mant.append('.');
3497                 }
3498 
3499                 int newW = width;
3500                 if (width != -1) {
3501                     if (exp != null)
3502                         newW = adjustWidth(width - exp.length - 1, f, neg);
3503                     else
3504                         newW = adjustWidth(width, f, neg);
3505                 }
3506                 localizedMagnitude(sb, mant, 0, f, newW, l);
3507 
3508                 if (exp != null) {
3509                     sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');
3510 
3511                     char sign = exp[0];
3512                     assert(sign == '+' || sign == '-');
3513                     sb.append(sign);


3514 
3515                     localizedMagnitudeExp(sb, exp, 1, l);
3516                 }
3517             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3518                 int prec = precision;
3519                 if (precision == -1)
3520                     // assume that we want all of the digits
3521                     prec = 0;
3522                 else if (precision == 0)
3523                     prec = 1;
3524 
3525                 String s = hexDouble(value, prec);







3526 
3527                 StringBuilder va = new StringBuilder();
3528                 boolean upper = f.contains(Flags.UPPERCASE);
3529                 sb.append(upper ? "0X" : "0x");
3530 
3531                 if (f.contains(Flags.ZERO_PAD)) {
3532                     trailingZeros(sb, width - s.length() - 2);
3533                 }
3534 
3535                 int idx = s.indexOf('p');
3536                 if (upper) {
3537                     String tmp = s.substring(0, idx);
3538                     // don't localize hex
3539                     tmp = tmp.toUpperCase(Locale.ROOT);
3540                     va.append(tmp);
3541                 } else {
3542                     va.append(s, 0, idx);
3543                 }
3544                 if (prec != 0) {
3545                     addZeros(va, prec);
3546                 }
3547                 sb.append(va);
3548                 sb.append(upper ? 'P' : 'p');
3549                 sb.append(s, idx+1, s.length());
3550             }
3551         }
3552 
3553         // Add zeros to the requested precision.
3554         private void addZeros(StringBuilder sb, int prec) {
3555             // Look for the dot.  If we don't find one, the we'll need to add
3556             // it before we add the zeros.
3557             int len = sb.length();
3558             int i;
3559             for (i = 0; i < len; i++) {
3560                 if (sb.charAt(i) == '.') {
3561                     break;
3562                 }














3563             }
3564             boolean needDot = false;
3565             if (i == len) {
3566                 needDot = true;
3567             }





3568 
3569             // Determine existing precision.
3570             int outPrec = len - i - (needDot ? 0 : 1);
3571             assert (outPrec <= prec);
3572             if (outPrec == prec) {
3573                 return;




3574             }


3575 
3576             // Add dot if previously determined to be necessary.
3577             if (needDot) {
3578                 sb.append('.');
3579             }


3580 
3581             // Add zeros.
3582             trailingZeros(sb, prec - outPrec);

3583         }
3584 
3585         // Method assumes that d > 0.
3586         private String hexDouble(double d, int prec) {
3587             // Let Double.toHexString handle simple cases
3588             if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3589                 // remove "0x"
3590                 return Double.toHexString(d).substring(2);
3591             } else {
3592                 assert(prec >= 1 && prec <= 12);




3593 
3594                 int exponent  = Math.getExponent(d);
3595                 boolean subnormal
3596                     = (exponent == Double.MIN_EXPONENT - 1);
3597 
3598                 // If this is subnormal input so normalize (could be faster to
3599                 // do as integer operation).
3600                 if (subnormal) {
3601                     scaleUp = Math.scalb(1.0, 54);
3602                     d *= scaleUp;
3603                     // Calculate the exponent.  This is not just exponent + 54
3604                     // since the former is not the normalized exponent.
3605                     exponent = Math.getExponent(d);
3606                     assert exponent >= Double.MIN_EXPONENT &&
3607                         exponent <= Double.MAX_EXPONENT: exponent;
3608                 }
3609 
3610                 int precision = 1 + prec*4;
3611                 int shiftDistance
3612                     =  DoubleConsts.SIGNIFICAND_WIDTH - precision;
3613                 assert(shiftDistance >= 1 && shiftDistance < DoubleConsts.SIGNIFICAND_WIDTH);
3614 
3615                 long doppel = Double.doubleToLongBits(d);
3616                 // Deterime the number of bits to keep.
3617                 long newSignif
3618                     = (doppel & (DoubleConsts.EXP_BIT_MASK
3619                                  | DoubleConsts.SIGNIF_BIT_MASK))
3620                                      >> shiftDistance;
3621                 // Bits to round away.
3622                 long roundingBits = doppel & ~(~0L << shiftDistance);
3623 
3624                 // To decide how to round, look at the low-order bit of the
3625                 // working significand, the highest order discarded bit (the
3626                 // round bit) and whether any of the lower order discarded bits
3627                 // are nonzero (the sticky bit).
3628 
3629                 boolean leastZero = (newSignif & 0x1L) == 0L;
3630                 boolean round
3631                     = ((1L << (shiftDistance - 1) ) & roundingBits) != 0L;
3632                 boolean sticky  = shiftDistance > 1 &&
3633                     (~(1L<< (shiftDistance - 1)) & roundingBits) != 0;
3634                 if((leastZero && round && sticky) || (!leastZero && round)) {
3635                     newSignif++;
3636                 }
3637 
3638                 long signBit = doppel & DoubleConsts.SIGN_BIT_MASK;
3639                 newSignif = signBit | (newSignif << shiftDistance);
3640                 double result = Double.longBitsToDouble(newSignif);
3641 
3642                 if (Double.isInfinite(result) ) {
3643                     // Infinite result generated by rounding
3644                     return "1.0p1024";
3645                 } else {
3646                     String res = Double.toHexString(result).substring(2);
3647                     if (!subnormal)
3648                         return res;
3649                     else {
3650                         // Create a normalized subnormal string.
3651                         int idx = res.indexOf('p');
3652                         if (idx == -1) {
3653                             // No 'p' character in hex string.
3654                             assert false;
3655                             return null;
3656                         } else {
3657                             // Get exponent and append at the end.
3658                             String exp = res.substring(idx + 1);
3659                             int iexp = Integer.parseInt(exp) -54;
3660                             return res.substring(0, idx) + "p"
3661                                 + Integer.toString(iexp);
3662                         }
3663                     }
3664                 }
3665             }
3666         }

3667 
3668         private void print(BigDecimal value, Locale l) throws IOException {
3669             if (c == Conversion.HEXADECIMAL_FLOAT)
3670                 failConversion(c, value);
3671             StringBuilder sb = new StringBuilder();
3672             boolean neg = value.signum() == -1;
3673             BigDecimal v = value.abs();
3674             // leading sign indicator
3675             leadingSign(sb, neg);
3676 
3677             // the value
3678             print(sb, v, l, f, c, precision, neg);
3679 
3680             // trailing sign indicator
3681             trailingSign(sb, neg);
3682 
3683             // justify based on width
3684             appendJustified(a, sb);
3685         }
3686 
3687         // value > 0
3688         private void print(StringBuilder sb, BigDecimal value, Locale l,
3689                            Flags f, char c, int precision, boolean neg)
3690             throws IOException
3691         {
3692             if (c == Conversion.SCIENTIFIC) {
3693                 // Create a new BigDecimal with the desired precision.
3694                 int prec = (precision == -1 ? 6 : precision);
3695                 int scale = value.scale();
3696                 int origPrec = value.precision();
3697                 int nzeros = 0;
3698                 int compPrec;
3699 
3700                 if (prec > origPrec - 1) {
3701                     compPrec = origPrec;
3702                     nzeros = prec - (origPrec - 1);
3703                 } else {
3704                     compPrec = prec + 1;
3705                 }
3706 
3707                 MathContext mc = new MathContext(compPrec);
3708                 BigDecimal v
3709                     = new BigDecimal(value.unscaledValue(), scale, mc);
3710 
3711                 BigDecimalLayout bdl
3712                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3713                                            BigDecimalLayoutForm.SCIENTIFIC);
3714 
3715                 StringBuilder mant = bdl.mantissa();
3716 
3717                 // Add a decimal point if necessary.  The mantissa may not
3718                 // contain a decimal point if the scale is zero (the internal
3719                 // representation has no fractional part) or the original
3720                 // precision is one. Append a decimal point if '#' is set or if
3721                 // we require zero padding to get to the requested precision.
3722                 if ((origPrec == 1 || !bdl.hasDot())
3723                         && (nzeros > 0 || (f.contains(Flags.ALTERNATE)))) {
3724                     mant.append('.');
3725                 }
3726 
3727                 // Add trailing zeros in the case precision is greater than
3728                 // the number of available digits after the decimal separator.
3729                 trailingZeros(mant, nzeros);
3730 
3731                 StringBuilder exp = bdl.exponent();
3732                 int newW = width;
3733                 if (width != -1) {
3734                     newW = adjustWidth(width - exp.length() - 1, f, neg);
3735                 }
3736                 localizedMagnitude(sb, mant, 0, f, newW, l);
3737 
3738                 sb.append(f.contains(Flags.UPPERCASE) ? 'E' : 'e');


3739 
3740                 Flags flags = f.dup().remove(Flags.GROUP);
3741                 char sign = exp.charAt(0);
3742                 assert(sign == '+' || sign == '-');
3743                 sb.append(sign);


3744 
3745                 sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3746             } else if (c == Conversion.DECIMAL_FLOAT) {
3747                 // Create a new BigDecimal with the desired precision.
3748                 int prec = (precision == -1 ? 6 : precision);
3749                 int scale = value.scale();
3750 
3751                 if (scale > prec) {
3752                     // more "scale" digits than the requested "precision"
3753                     int compPrec = value.precision();
3754                     if (compPrec <= scale) {
3755                         // case of 0.xxxxxx
3756                         value = value.setScale(prec, RoundingMode.HALF_UP);
3757                     } else {
3758                         compPrec -= (scale - prec);
3759                         value = new BigDecimal(value.unscaledValue(),
3760                                                scale,
3761                                                new MathContext(compPrec));
3762                     }
3763                 }
3764                 BigDecimalLayout bdl = new BigDecimalLayout(
3765                                            value.unscaledValue(),
3766                                            value.scale(),
3767                                            BigDecimalLayoutForm.DECIMAL_FLOAT);
3768 
3769                 StringBuilder mant = bdl.mantissa();
3770                 int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3771 
3772                 // Add a decimal point if necessary.  The mantissa may not
3773                 // contain a decimal point if the scale is zero (the internal
3774                 // representation has no fractional part).  Append a decimal
3775                 // point if '#' is set or we require zero padding to get to the
3776                 // requested precision.
3777                 if (bdl.scale() == 0 && (f.contains(Flags.ALTERNATE)
3778                         || nzeros > 0)) {
3779                     mant.append('.');
3780                 }
3781 
3782                 // Add trailing zeros if the precision is greater than the
3783                 // number of available digits after the decimal separator.
3784                 trailingZeros(mant, nzeros);
3785 
3786                 localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3787             } else if (c == Conversion.GENERAL) {
3788                 int prec = precision;
3789                 if (precision == -1)
3790                     prec = 6;
3791                 else if (precision == 0)
3792                     prec = 1;
3793 
3794                 BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3795                 BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3796                 if ((value.equals(BigDecimal.ZERO))










































3797                     || ((value.compareTo(tenToTheNegFour) != -1)
3798                         && (value.compareTo(tenToThePrec) == -1))) {
3799 
3800                     int e = - value.scale()
3801                         + (value.unscaledValue().toString().length() - 1);
3802 
3803                     // xxx.yyy
3804                     //   g precision (# sig digits) = #x + #y
3805                     //   f precision = #y
3806                     //   exponent = #x - 1
3807                     // => f precision = g precision - exponent - 1
3808                     // 0.000zzz
3809                     //   g precision (# sig digits) = #z
3810                     //   f precision = #0 (after '.') + #z
3811                     //   exponent = - #0 (after '.') - 1
3812                     // => f precision = g precision - exponent - 1
3813                     prec = prec - e - 1;
3814 
3815                     print(sb, value, l, f, Conversion.DECIMAL_FLOAT, prec,
3816                           neg);
3817                 } else {
3818                     print(sb, value, l, f, Conversion.SCIENTIFIC, prec - 1, neg);
3819                 }
3820             } else if (c == Conversion.HEXADECIMAL_FLOAT) {
3821                 // This conversion isn't supported.  The error should be
3822                 // reported earlier.
3823                 assert false;
3824             }















3825         }
3826 
3827         private class BigDecimalLayout {
3828             private StringBuilder mant;
3829             private StringBuilder exp;
3830             private boolean dot = false;
3831             private int scale;
3832 
3833             public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3834                 layout(intVal, scale, form);
3835             }
3836 
3837             public boolean hasDot() {
3838                 return dot;
3839             }
3840 
3841             public int scale() {
3842                 return scale;
3843             }
3844 
3845             public StringBuilder mantissa() {
3846                 return mant;
3847             }
3848 
3849             // The exponent will be formatted as a sign ('+' or '-') followed
3850             // by the exponent zero-padded to include at least two digits.
3851             public StringBuilder exponent() {
3852                 return exp;
3853             }
3854 
3855             private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3856                 String coeff = intVal.toString();
3857                 this.scale = scale;
3858 
3859                 // Construct a buffer, with sufficient capacity for all cases.
3860                 // If E-notation is needed, length will be: +1 if negative, +1
3861                 // if '.' needed, +2 for "E+", + up to 10 for adjusted
3862                 // exponent.  Otherwise it could have +1 if negative, plus
3863                 // leading "0.00000"
3864                 int len = coeff.length();
3865                 mant = new StringBuilder(len + 14);
3866 
3867                 if (scale == 0) {
3868                     if (len > 1) {
3869                         mant.append(coeff.charAt(0));
3870                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3871                             mant.append('.');
3872                             dot = true;
3873                             mant.append(coeff, 1, len);
3874                             exp = new StringBuilder("+");
3875                             if (len < 10) {
3876                                 exp.append('0').append(len - 1);
3877                             } else {
3878                                 exp.append(len - 1);
3879                             }
3880                         } else {
3881                             mant.append(coeff, 1, len);
3882                         }
3883                     } else {
3884                         mant.append(coeff);
3885                         if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3886                             exp = new StringBuilder("+00");
3887                         }
3888                     }
3889                 } else if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3890                     // count of padding zeros
3891 
3892                     if (scale >= len) {
3893                         // 0.xxx form
3894                         mant.append("0.");
3895                         dot = true;
3896                         trailingZeros(mant, scale - len);
3897                         mant.append(coeff);
3898                     } else {
3899                         if (scale > 0) {
3900                             // xx.xx form
3901                             int pad = len - scale;
3902                             mant.append(coeff, 0, pad);
3903                             mant.append('.');
3904                             dot = true;
3905                             mant.append(coeff, pad, len);
3906                         } else { // scale < 0
3907                             // xx form
3908                             mant.append(coeff, 0, len);
3909                             if (intVal.signum() != 0) {
3910                                 trailingZeros(mant, -scale);
3911                             }
3912                             this.scale = 0;
3913                         }
3914                     }










3915                 } else {
3916                     // x.xxx form
3917                     mant.append(coeff.charAt(0));
3918                     if (len > 1) {

3919                         mant.append('.');
3920                         dot = true;
3921                         mant.append(coeff, 1, len);
3922                     }
3923                     exp = new StringBuilder();
3924                     long adjusted = -(long) scale + (len - 1);
3925                     if (adjusted != 0) {
3926                         long abs = Math.abs(adjusted);
3927                         // require sign
3928                         exp.append(adjusted < 0 ? '-' : '+');
3929                         if (abs < 10) {
3930                             exp.append('0');
3931                         }
3932                         exp.append(abs);
3933                     } else {
3934                         exp.append("+00");
3935                     }
3936                 }





















3937             }
3938         }

3939 
3940         private int adjustWidth(int width, Flags f, boolean neg) {
3941             int newW = width;
3942             if (newW != -1 && neg && f.contains(Flags.PARENTHESES))
3943                 newW--;
3944             return newW;
3945         }
3946 
3947         // Add trailing zeros
3948         private void trailingZeros(StringBuilder sb, int nzeros) {
3949             for (int i = 0; i < nzeros; i++) {
3950                 sb.append('0');
3951             }
3952         }

3953 
3954         private void print(Calendar t, char c, Locale l)  throws IOException {
3955             StringBuilder sb = new StringBuilder();
3956             print(sb, t, c, l);
3957 
3958             // justify based on width
3959             if (f.contains(Flags.UPPERCASE)) {
3960                 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
3961             } else {
3962                 appendJustified(a, sb);
3963             }
3964         }

3965 
3966         private Appendable print(StringBuilder sb, Calendar t, char c, Locale l)
3967                 throws IOException {
3968             if (sb == null)
3969                 sb = new StringBuilder();
3970             switch (c) {
3971             case DateTime.HOUR_OF_DAY_0: // 'H' (00 - 23)
3972             case DateTime.HOUR_0:        // 'I' (01 - 12)
3973             case DateTime.HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3974             case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
3975                 int i = t.get(Calendar.HOUR_OF_DAY);
3976                 if (c == DateTime.HOUR_0 || c == DateTime.HOUR)
3977                     i = (i == 0 || i == 12 ? 12 : i % 12);
3978                 Flags flags = (c == DateTime.HOUR_OF_DAY_0
3979                                || c == DateTime.HOUR_0
3980                                ? Flags.ZERO_PAD
3981                                : Flags.NONE);
3982                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3983                 break;
3984             }
3985             case DateTime.MINUTE:      { // 'M' (00 - 59)
3986                 int i = t.get(Calendar.MINUTE);
3987                 Flags flags = Flags.ZERO_PAD;
3988                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3989                 break;
3990             }
3991             case DateTime.NANOSECOND:  { // 'N' (000000000 - 999999999)
3992                 int i = t.get(Calendar.MILLISECOND) * 1000000;
3993                 Flags flags = Flags.ZERO_PAD;
3994                 sb.append(localizedMagnitude(null, i, flags, 9, l));
3995                 break;
3996             }
3997             case DateTime.MILLISECOND: { // 'L' (000 - 999)
3998                 int i = t.get(Calendar.MILLISECOND);
3999                 Flags flags = Flags.ZERO_PAD;
4000                 sb.append(localizedMagnitude(null, i, flags, 3, l));
4001                 break;
4002             }
4003             case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)
4004                 long i = t.getTimeInMillis();
4005                 Flags flags = Flags.NONE;
4006                 sb.append(localizedMagnitude(null, i, flags, width, l));
4007                 break;
4008             }
4009             case DateTime.AM_PM:       { // 'p' (am or pm)
4010                 // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper
4011                 String[] ampm = { "AM", "PM" };
4012                 if (l != null && l != Locale.US) {
4013                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);
4014                     ampm = dfs.getAmPmStrings();
4015                 }
4016                 String s = ampm[t.get(Calendar.AM_PM)];
4017                 sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
4018                             Locale.getDefault(Locale.Category.FORMAT))));
4019                 break;
4020             }
4021             case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
4022                 long i = t.getTimeInMillis() / 1000;
4023                 Flags flags = Flags.NONE;
4024                 sb.append(localizedMagnitude(null, i, flags, width, l));
4025                 break;
4026             }
4027             case DateTime.SECOND:      { // 'S' (00 - 60 - leap second)
4028                 int i = t.get(Calendar.SECOND);
4029                 Flags flags = Flags.ZERO_PAD;
4030                 sb.append(localizedMagnitude(null, i, flags, 2, l));
4031                 break;
4032             }
4033             case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?
4034                 int i = t.get(Calendar.ZONE_OFFSET) + t.get(Calendar.DST_OFFSET);
4035                 boolean neg = i < 0;
4036                 sb.append(neg ? '-' : '+');
4037                 if (neg)
4038                     i = -i;
4039                 int min = i / 60000;
4040                 // combine minute and hour into a single integer
4041                 int offset = (min / 60) * 100 + (min % 60);
4042                 Flags flags = Flags.ZERO_PAD;
4043 
4044                 sb.append(localizedMagnitude(null, offset, flags, 4, l));
4045                 break;
4046             }
4047             case DateTime.ZONE:        { // 'Z' (symbol)
4048                 TimeZone tz = t.getTimeZone();
4049                 sb.append(tz.getDisplayName((t.get(Calendar.DST_OFFSET) != 0),
4050                                            TimeZone.SHORT,
4051                                            Objects.requireNonNullElse(l, Locale.US)));
4052                 break;
4053             }
4054 
4055             // Date
4056             case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
4057             case DateTime.NAME_OF_DAY:          { // 'A'
4058                 int i = t.get(Calendar.DAY_OF_WEEK);
4059                 Locale lt = Objects.requireNonNullElse(l, Locale.US);
4060                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4061                 if (c == DateTime.NAME_OF_DAY)
4062                     sb.append(dfs.getWeekdays()[i]);
4063                 else
4064                     sb.append(dfs.getShortWeekdays()[i]);
4065                 break;
4066             }
4067             case DateTime.NAME_OF_MONTH_ABBREV:   // 'b'
4068             case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
4069             case DateTime.NAME_OF_MONTH:        { // 'B'
4070                 int i = t.get(Calendar.MONTH);
4071                 Locale lt = Objects.requireNonNullElse(l, Locale.US);
4072                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4073                 if (c == DateTime.NAME_OF_MONTH)
4074                     sb.append(dfs.getMonths()[i]);
4075                 else
4076                     sb.append(dfs.getShortMonths()[i]);
4077                 break;
4078             }
4079             case DateTime.CENTURY:                // 'C' (00 - 99)
4080             case DateTime.YEAR_2:                 // 'y' (00 - 99)
4081             case DateTime.YEAR_4:               { // 'Y' (0000 - 9999)
4082                 int i = t.get(Calendar.YEAR);
4083                 int size = 2;
4084                 switch (c) {
4085                 case DateTime.CENTURY:
4086                     i /= 100;
4087                     break;
4088                 case DateTime.YEAR_2:
4089                     i %= 100;
4090                     break;
4091                 case DateTime.YEAR_4:
4092                     size = 4;
4093                     break;
4094                 }
4095                 Flags flags = Flags.ZERO_PAD;
4096                 sb.append(localizedMagnitude(null, i, flags, size, l));
4097                 break;
4098             }
4099             case DateTime.DAY_OF_MONTH_0:         // 'd' (01 - 31)
4100             case DateTime.DAY_OF_MONTH:         { // 'e' (1 - 31) -- like d
4101                 int i = t.get(Calendar.DATE);
4102                 Flags flags = (c == DateTime.DAY_OF_MONTH_0
4103                                ? Flags.ZERO_PAD
4104                                : Flags.NONE);
4105                 sb.append(localizedMagnitude(null, i, flags, 2, l));
4106                 break;
4107             }
4108             case DateTime.DAY_OF_YEAR:          { // 'j' (001 - 366)
4109                 int i = t.get(Calendar.DAY_OF_YEAR);
4110                 Flags flags = Flags.ZERO_PAD;
4111                 sb.append(localizedMagnitude(null, i, flags, 3, l));
4112                 break;
4113             }
4114             case DateTime.MONTH:                { // 'm' (01 - 12)
4115                 int i = t.get(Calendar.MONTH) + 1;
4116                 Flags flags = Flags.ZERO_PAD;
4117                 sb.append(localizedMagnitude(null, i, flags, 2, l));
4118                 break;
4119             }
4120 
4121             // Composites
4122             case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4123             case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4124                 char sep = ':';
4125                 print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4126                 print(sb, t, DateTime.MINUTE, l);
4127                 if (c == DateTime.TIME) {
4128                     sb.append(sep);
4129                     print(sb, t, DateTime.SECOND, l);
4130                 }
4131                 break;
4132             }
4133             case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4134                 char sep = ':';
4135                 print(sb, t, DateTime.HOUR_0, l).append(sep);
4136                 print(sb, t, DateTime.MINUTE, l).append(sep);
4137                 print(sb, t, DateTime.SECOND, l).append(' ');
4138                 // this may be in wrong place for some locales
4139                 StringBuilder tsb = new StringBuilder();
4140                 print(tsb, t, DateTime.AM_PM, l);
4141 
4142                 sb.append(toUpperCaseWithLocale(tsb.toString(), l));
4143                 break;
4144             }
4145             case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4146                 char sep = ' ';
4147                 print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4148                 print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4149                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4150                 print(sb, t, DateTime.TIME, l).append(sep);
4151                 print(sb, t, DateTime.ZONE, l).append(sep);
4152                 print(sb, t, DateTime.YEAR_4, l);
4153                 break;
4154             }
4155             case DateTime.DATE:            { // 'D' (mm/dd/yy)
4156                 char sep = '/';
4157                 print(sb, t, DateTime.MONTH, l).append(sep);
4158                 print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4159                 print(sb, t, DateTime.YEAR_2, l);
4160                 break;
4161             }
4162             case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4163                 char sep = '-';
4164                 print(sb, t, DateTime.YEAR_4, l).append(sep);
4165                 print(sb, t, DateTime.MONTH, l).append(sep);
4166                 print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4167                 break;
4168             }
4169             default:
4170                 assert false;
4171             }
4172             return sb;
4173         }


4174 
4175         private void print(TemporalAccessor t, char c, Locale l)  throws IOException {
4176             StringBuilder sb = new StringBuilder();
4177             print(sb, t, c, l);
4178             // justify based on width
4179             if (f.contains(Flags.UPPERCASE)) {
4180                 appendJustified(a, toUpperCaseWithLocale(sb.toString(), l));
4181             } else {
4182                 appendJustified(a, sb);
4183             }
4184         }

4185 
4186         private Appendable print(StringBuilder sb, TemporalAccessor t, char c,
4187                                  Locale l) throws IOException {
4188             if (sb == null)
4189                 sb = new StringBuilder();
4190             try {
4191                 switch (c) {
4192                 case DateTime.HOUR_OF_DAY_0: {  // 'H' (00 - 23)
4193                     int i = t.get(ChronoField.HOUR_OF_DAY);
4194                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4195                     break;
4196                 }
4197                 case DateTime.HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
4198                     int i = t.get(ChronoField.HOUR_OF_DAY);
4199                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4200                     break;
4201                 }
4202                 case DateTime.HOUR_0:      {  // 'I' (01 - 12)
4203                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4204                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
4205                     break;
4206                 }
4207                 case DateTime.HOUR:        { // 'l' (1 - 12) -- like I
4208                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
4209                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
4210                     break;
4211                 }
4212                 case DateTime.MINUTE:      { // 'M' (00 - 59)
4213                     int i = t.get(ChronoField.MINUTE_OF_HOUR);
4214                     Flags flags = Flags.ZERO_PAD;
4215                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4216                     break;
4217                 }
4218                 case DateTime.NANOSECOND:  { // 'N' (000000000 - 999999999)
4219                     int i;
4220                     try {
4221                         i = t.get(ChronoField.NANO_OF_SECOND);
4222                     } catch (UnsupportedTemporalTypeException u) {
4223                         i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000;
4224                     }
4225                     Flags flags = Flags.ZERO_PAD;
4226                     sb.append(localizedMagnitude(null, i, flags, 9, l));
4227                     break;
4228                 }
4229                 case DateTime.MILLISECOND: { // 'L' (000 - 999)
4230                     int i = t.get(ChronoField.MILLI_OF_SECOND);
4231                     Flags flags = Flags.ZERO_PAD;
4232                     sb.append(localizedMagnitude(null, i, flags, 3, l));
4233                     break;
4234                 }
4235                 case DateTime.MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)
4236                     long i = t.getLong(ChronoField.INSTANT_SECONDS) * 1000L +
4237                              t.getLong(ChronoField.MILLI_OF_SECOND);
4238                     Flags flags = Flags.NONE;
4239                     sb.append(localizedMagnitude(null, i, flags, width, l));
4240                     break;
4241                 }
4242                 case DateTime.AM_PM:       { // 'p' (am or pm)
4243                     // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper
4244                     String[] ampm = { "AM", "PM" };
4245                     if (l != null && l != Locale.US) {
4246                         DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);
4247                         ampm = dfs.getAmPmStrings();
4248                     }
4249                     String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];
4250                     sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
4251                             Locale.getDefault(Locale.Category.FORMAT))));
4252                     break;
4253                 }
4254                 case DateTime.SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
4255                     long i = t.getLong(ChronoField.INSTANT_SECONDS);
4256                     Flags flags = Flags.NONE;
4257                     sb.append(localizedMagnitude(null, i, flags, width, l));
4258                     break;
4259                 }
4260                 case DateTime.SECOND:      { // 'S' (00 - 60 - leap second)
4261                     int i = t.get(ChronoField.SECOND_OF_MINUTE);
4262                     Flags flags = Flags.ZERO_PAD;
4263                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4264                     break;
4265                 }
4266                 case DateTime.ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?
4267                     int i = t.get(ChronoField.OFFSET_SECONDS);
4268                     boolean neg = i < 0;
4269                     sb.append(neg ? '-' : '+');
4270                     if (neg)
4271                         i = -i;
4272                     int min = i / 60;
4273                     // combine minute and hour into a single integer
4274                     int offset = (min / 60) * 100 + (min % 60);
4275                     Flags flags = Flags.ZERO_PAD;
4276                     sb.append(localizedMagnitude(null, offset, flags, 4, l));
4277                     break;
4278                 }
4279                 case DateTime.ZONE:        { // 'Z' (symbol)
4280                     ZoneId zid = t.query(TemporalQueries.zone());
4281                     if (zid == null) {
4282                         throw new IllegalFormatConversionException(c, t.getClass());
4283                     }
4284                     if (!(zid instanceof ZoneOffset) &&
4285                         t.isSupported(ChronoField.INSTANT_SECONDS)) {
4286                         Instant instant = Instant.from(t);
4287                         sb.append(TimeZone.getTimeZone(zid.getId())
4288                                           .getDisplayName(zid.getRules().isDaylightSavings(instant),
4289                                                           TimeZone.SHORT,
4290                                                           Objects.requireNonNullElse(l, Locale.US)));
4291                         break;
4292                     }
4293                     sb.append(zid.getId());
4294                     break;
4295                 }
4296                 // Date
4297                 case DateTime.NAME_OF_DAY_ABBREV:     // 'a'
4298                 case DateTime.NAME_OF_DAY:          { // 'A'
4299                     int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1;
4300                     Locale lt = Objects.requireNonNullElse(l, Locale.US);
4301                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4302                     if (c == DateTime.NAME_OF_DAY)
4303                         sb.append(dfs.getWeekdays()[i]);
4304                     else
4305                         sb.append(dfs.getShortWeekdays()[i]);
4306                     break;
4307                 }
4308                 case DateTime.NAME_OF_MONTH_ABBREV:   // 'b'
4309                 case DateTime.NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
4310                 case DateTime.NAME_OF_MONTH:        { // 'B'
4311                     int i = t.get(ChronoField.MONTH_OF_YEAR) - 1;
4312                     Locale lt = Objects.requireNonNullElse(l, Locale.US);
4313                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4314                     if (c == DateTime.NAME_OF_MONTH)
4315                         sb.append(dfs.getMonths()[i]);
4316                     else
4317                         sb.append(dfs.getShortMonths()[i]);
4318                     break;
4319                 }
4320                 case DateTime.CENTURY:                // 'C' (00 - 99)
4321                 case DateTime.YEAR_2:                 // 'y' (00 - 99)
4322                 case DateTime.YEAR_4:               { // 'Y' (0000 - 9999)
4323                     int i = t.get(ChronoField.YEAR_OF_ERA);
4324                     int size = 2;
4325                     switch (c) {
4326                     case DateTime.CENTURY:
4327                         i /= 100;
4328                         break;
4329                     case DateTime.YEAR_2:
4330                         i %= 100;
4331                         break;
4332                     case DateTime.YEAR_4:
4333                         size = 4;
4334                         break;
4335                     }
4336                     Flags flags = Flags.ZERO_PAD;
4337                     sb.append(localizedMagnitude(null, i, flags, size, l));
4338                     break;
4339                 }
4340                 case DateTime.DAY_OF_MONTH_0:         // 'd' (01 - 31)
4341                 case DateTime.DAY_OF_MONTH:         { // 'e' (1 - 31) -- like d
4342                     int i = t.get(ChronoField.DAY_OF_MONTH);
4343                     Flags flags = (c == DateTime.DAY_OF_MONTH_0
4344                                    ? Flags.ZERO_PAD
4345                                    : Flags.NONE);
4346                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4347                     break;
4348                 }
4349                 case DateTime.DAY_OF_YEAR:          { // 'j' (001 - 366)
4350                     int i = t.get(ChronoField.DAY_OF_YEAR);
4351                     Flags flags = Flags.ZERO_PAD;
4352                     sb.append(localizedMagnitude(null, i, flags, 3, l));
4353                     break;
4354                 }
4355                 case DateTime.MONTH:                { // 'm' (01 - 12)
4356                     int i = t.get(ChronoField.MONTH_OF_YEAR);
4357                     Flags flags = Flags.ZERO_PAD;
4358                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4359                     break;
4360                 }
4361 
4362                 // Composites
4363                 case DateTime.TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4364                 case DateTime.TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4365                     char sep = ':';
4366                     print(sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4367                     print(sb, t, DateTime.MINUTE, l);
4368                     if (c == DateTime.TIME) {
4369                         sb.append(sep);
4370                         print(sb, t, DateTime.SECOND, l);
4371                     }
4372                     break;
4373                 }
4374                 case DateTime.TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4375                     char sep = ':';
4376                     print(sb, t, DateTime.HOUR_0, l).append(sep);
4377                     print(sb, t, DateTime.MINUTE, l).append(sep);
4378                     print(sb, t, DateTime.SECOND, l).append(' ');
4379                     // this may be in wrong place for some locales
4380                     StringBuilder tsb = new StringBuilder();
4381                     print(tsb, t, DateTime.AM_PM, l);
4382                     sb.append(toUpperCaseWithLocale(tsb.toString(), l));
4383                     break;
4384                 }
4385                 case DateTime.DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4386                     char sep = ' ';
4387                     print(sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4388                     print(sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4389                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4390                     print(sb, t, DateTime.TIME, l).append(sep);
4391                     print(sb, t, DateTime.ZONE, l).append(sep);
4392                     print(sb, t, DateTime.YEAR_4, l);
4393                     break;
4394                 }
4395                 case DateTime.DATE:            { // 'D' (mm/dd/yy)
4396                     char sep = '/';
4397                     print(sb, t, DateTime.MONTH, l).append(sep);
4398                     print(sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4399                     print(sb, t, DateTime.YEAR_2, l);
4400                     break;
4401                 }
4402                 case DateTime.ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4403                     char sep = '-';
4404                     print(sb, t, DateTime.YEAR_4, l).append(sep);
4405                     print(sb, t, DateTime.MONTH, l).append(sep);
4406                     print(sb, t, DateTime.DAY_OF_MONTH_0, l);
4407                     break;
4408                 }
4409                 default:
4410                     assert false;
4411                 }
4412             } catch (DateTimeException x) {
4413                 throw new IllegalFormatConversionException(c, t.getClass());
4414             }
4415             return sb;

4416         }


4417 
4418         // -- Methods to support throwing exceptions --
4419 
4420         private void failMismatch(Flags f, char c) {
4421             String fs = f.toString();
4422             throw new FormatFlagsConversionMismatchException(fs, c);






4423         }


4424 
4425         private void failConversion(char c, Object arg) {
4426             throw new IllegalFormatConversionException(c, arg.getClass());
























4427         }
4428 
4429         private char getZero(Locale l) {
4430             if ((l != null) &&  !l.equals(locale())) {


4431                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4432                 return dfs.getZeroDigit();
4433             }
4434             return zero;
4435         }
4436 
4437         private StringBuilder localizedMagnitude(StringBuilder sb,
4438                 long value, Flags f, int width, Locale l) {
4439             return localizedMagnitude(sb, Long.toString(value, 10), 0, f, width, l);
































4440         }
4441 
4442         private StringBuilder localizedMagnitude(StringBuilder sb,
4443                 CharSequence value, final int offset, Flags f, int width,
4444                 Locale l) {
4445             if (sb == null) {
4446                 sb = new StringBuilder();


4447             }
4448             int begin = sb.length();
4449 
4450             char zero = getZero(l);





4451 
4452             // determine localized grouping separator and size
4453             char grpSep = '\0';
4454             int  grpSize = -1;
4455             char decSep = '\0';


4456 
4457             int len = value.length();
4458             int dot = len;
4459             for (int j = offset; j < len; j++) {
4460                 if (value.charAt(j) == '.') {
4461                     dot = j;
4462                     break;



















































4463                 }










4464             }



4465 
4466             if (dot < len) {
4467                 if (l == null || l.equals(Locale.US)) {
4468                     decSep  = '.';
4469                 } else {
4470                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4471                     decSep  = dfs.getDecimalSeparator();
4472                 }
4473             }


4474 
4475             if (f.contains(Flags.GROUP)) {
4476                 if (l == null || l.equals(Locale.US)) {
4477                     grpSep = ',';
4478                     grpSize = 3;
4479                 } else {
4480                     DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4481                     grpSep = dfs.getGroupingSeparator();
4482                     DecimalFormat df = null;
4483                     NumberFormat nf = NumberFormat.getNumberInstance(l);
4484                     if (nf instanceof DecimalFormat) {
4485                         df = (DecimalFormat) nf;
4486                     } else {
4487 
4488                         // Use DecimalFormat constructor to obtain the instance,
4489                         // in case NumberFormat.getNumberInstance(l)
4490                         // returns instance other than DecimalFormat
4491                         LocaleProviderAdapter adapter = LocaleProviderAdapter
4492                                 .getAdapter(NumberFormatProvider.class, l);
4493                         if (!(adapter instanceof ResourceBundleBasedAdapter)) {
4494                             adapter = LocaleProviderAdapter.getResourceBundleBased();
4495                         }
4496                         String[] all = adapter.getLocaleResources(l)
4497                                 .getNumberPatterns();
4498                         df = new DecimalFormat(all[0], dfs);
4499                     }
4500                     grpSize = df.getGroupingSize();
4501                     // Some locales do not use grouping (the number
4502                     // pattern for these locales does not contain group, e.g.
4503                     // ("#0.###")), but specify a grouping separator.
4504                     // To avoid unnecessary identification of the position of
4505                     // grouping separator, reset its value with null character
4506                     if (!df.isGroupingUsed() || grpSize == 0) {
4507                         grpSep = '\0';
4508                     }
4509                 }































4510             }


4511 
4512             // localize the digits inserting group separators as necessary
4513             for (int j = offset; j < len; j++) {
4514                 if (j == dot) {
4515                     sb.append(decSep);
4516                     // no more group separators after the decimal separator
4517                     grpSep = '\0';
4518                     continue;
4519                 }










4520 
4521                 char c = value.charAt(j);
4522                 sb.append((char) ((c - '0') + zero));
4523                 if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4524                     sb.append(grpSep);





4525                 }
4526             }

4527 
4528             // apply zero padding
4529             if (width != -1 && f.contains(Flags.ZERO_PAD)) {
4530                 for (int k = sb.length(); k < width; k++) {
4531                     sb.insert(begin, zero);






4532                 }
4533             }




































4534 
4535             return sb;

4536         }
4537 
4538         // Specialized localization of exponents, where the source value can only
4539         // contain characters '0' through '9', starting at index offset, and no
4540         // group separators is added for any locale.
4541         private void localizedMagnitudeExp(StringBuilder sb, char[] value,
4542                 final int offset, Locale l) {
4543             char zero = getZero(l);




























































4544 
4545             int len = value.length;
4546             for (int j = offset; j < len; j++) {
4547                 char c = value[j];
4548                 sb.append((char) ((c - '0') + zero));
4549             }
4550         }
4551     }
4552 
4553     private static class Flags {
4554         private int flags;








4555 
4556         static final Flags NONE          = new Flags(0);      // ''










4557 
4558         // duplicate declarations from Formattable.java
4559         static final Flags LEFT_JUSTIFY  = new Flags(1<<0);   // '-'
4560         static final Flags UPPERCASE     = new Flags(1<<1);   // '^'
4561         static final Flags ALTERNATE     = new Flags(1<<2);   // '#'





4562 
4563         // numerics
4564         static final Flags PLUS          = new Flags(1<<3);   // '+'
4565         static final Flags LEADING_SPACE = new Flags(1<<4);   // ' '
4566         static final Flags ZERO_PAD      = new Flags(1<<5);   // '0'
4567         static final Flags GROUP         = new Flags(1<<6);   // ','
4568         static final Flags PARENTHESES   = new Flags(1<<7);   // '('
4569 
4570         // indexing
4571         static final Flags PREVIOUS      = new Flags(1<<8);   // '<'





4572 
4573         private Flags(int f) {
4574             flags = f;


4575         }
4576 
4577         public int valueOf() {
4578             return flags;









4579         }
4580 
4581         public boolean contains(Flags f) {
4582             return (flags & f.valueOf()) == f.valueOf();














4583         }
4584 
4585         public Flags dup() {
4586             return new Flags(flags);



















4587         }
4588 
4589         private Flags add(Flags f) {
4590             flags |= f.valueOf();
4591             return this;


4592         }
4593 
4594         public Flags remove(Flags f) {
4595             flags &= ~f.valueOf();
4596             return this;

























4597         }
4598 
4599         public static Flags parse(String s, int start, int end) {
4600             Flags f = new Flags(0);




4601             for (int i = start; i < end; i++) {
4602                 char c = s.charAt(i);
4603                 Flags v = parse(c);
4604                 if (f.contains(v))
4605                     throw new DuplicateFormatFlagsException(v.toString());
4606                 f.add(v);
4607             }
4608             return f;
4609         }
4610 
4611         // parse those flags which may be provided by users
4612         private static Flags parse(char c) {
4613             switch (c) {
4614             case '-': return LEFT_JUSTIFY;
4615             case '#': return ALTERNATE;
4616             case '+': return PLUS;
4617             case ' ': return LEADING_SPACE;
4618             case '0': return ZERO_PAD;
4619             case ',': return GROUP;
4620             case '(': return PARENTHESES;
4621             case '<': return PREVIOUS;
4622             default:
4623                 throw new UnknownFormatFlagsException(String.valueOf(c));
4624             }
4625         }
4626 
4627         // Returns a string representation of the current {@code Flags}.
4628         public static String toString(Flags f) {
4629             return f.toString();
4630         }
4631 
4632         public String toString() {
4633             StringBuilder sb = new StringBuilder();
4634             if (contains(LEFT_JUSTIFY))  sb.append('-');
4635             if (contains(UPPERCASE))     sb.append('^');
4636             if (contains(ALTERNATE))     sb.append('#');
4637             if (contains(PLUS))          sb.append('+');
4638             if (contains(LEADING_SPACE)) sb.append(' ');
4639             if (contains(ZERO_PAD))      sb.append('0');
4640             if (contains(GROUP))         sb.append(',');
4641             if (contains(PARENTHESES))   sb.append('(');
4642             if (contains(PREVIOUS))      sb.append('<');
4643             return sb.toString();
4644         }
4645     }
4646 
4647     private static class Conversion {

4648         // Byte, Short, Integer, Long, BigInteger
4649         // (and associated primitives due to autoboxing)
4650         static final char DECIMAL_INTEGER     = 'd';
4651         static final char OCTAL_INTEGER       = 'o';
4652         static final char HEXADECIMAL_INTEGER = 'x';
4653         static final char HEXADECIMAL_INTEGER_UPPER = 'X';
4654 
4655         // Float, Double, BigDecimal
4656         // (and associated primitives due to autoboxing)
4657         static final char SCIENTIFIC          = 'e';
4658         static final char SCIENTIFIC_UPPER    = 'E';
4659         static final char GENERAL             = 'g';
4660         static final char GENERAL_UPPER       = 'G';
4661         static final char DECIMAL_FLOAT       = 'f';
4662         static final char HEXADECIMAL_FLOAT   = 'a';
4663         static final char HEXADECIMAL_FLOAT_UPPER = 'A';
4664 
4665         // Character, Byte, Short, Integer
4666         // (and associated primitives due to autoboxing)
4667         static final char CHARACTER           = 'c';
4668         static final char CHARACTER_UPPER     = 'C';
4669 
4670         // java.util.Date, java.util.Calendar, long
4671         static final char DATE_TIME           = 't';
4672         static final char DATE_TIME_UPPER     = 'T';
4673 
4674         // if (arg.TYPE != boolean) return boolean
4675         // if (arg != null) return true; else return false;
4676         static final char BOOLEAN             = 'b';
4677         static final char BOOLEAN_UPPER       = 'B';
4678         // if (arg instanceof Formattable) arg.formatTo()
4679         // else arg.toString();
4680         static final char STRING              = 's';
4681         static final char STRING_UPPER        = 'S';
4682         // arg.hashCode()
4683         static final char HASHCODE            = 'h';
4684         static final char HASHCODE_UPPER      = 'H';
4685 
4686         static final char LINE_SEPARATOR      = 'n';
4687         static final char PERCENT_SIGN        = '%';
4688 
4689         static boolean isValid(char c) {
4690             return (isGeneral(c) || isInteger(c) || isFloat(c) || isText(c)
4691                     || c == 't' || isCharacter(c));

4692         }
4693 
4694         // Returns true iff the Conversion is applicable to all objects.
4695         static boolean isGeneral(char c) {
4696             switch (c) {
4697             case BOOLEAN:
4698             case BOOLEAN_UPPER:
4699             case STRING:
4700             case STRING_UPPER:
4701             case HASHCODE:
4702             case HASHCODE_UPPER:
4703                 return true;
4704             default:
4705                 return false;






























4706             }
4707         }

4708 
4709         // Returns true iff the Conversion is applicable to character.
4710         static boolean isCharacter(char c) {













































4711             switch (c) {
4712             case CHARACTER:
4713             case CHARACTER_UPPER:
4714                 return true;
4715             default:




































































































































































































































4716                 return false;
4717             }
4718         }
4719 
4720         // Returns true iff the Conversion is an integer type.
4721         static boolean isInteger(char c) {
4722             switch (c) {



























































































































































































































































































































































































4723             case DECIMAL_INTEGER:
4724             case OCTAL_INTEGER:

4725             case HEXADECIMAL_INTEGER:
4726             case HEXADECIMAL_INTEGER_UPPER:
4727                 return true;






4728             default:
4729                 return false;
4730             }
4731         }
4732 
4733         // Returns true iff the Conversion is a floating-point type.
4734         static boolean isFloat(char c) {
4735             switch (c) {
4736             case SCIENTIFIC:
4737             case SCIENTIFIC_UPPER:
4738             case GENERAL:
4739             case GENERAL_UPPER:
4740             case DECIMAL_FLOAT:
4741             case HEXADECIMAL_FLOAT:
4742             case HEXADECIMAL_FLOAT_UPPER:
4743                 return true;
4744             default:
4745                 return false;























4746             }
4747         }


4748 
4749         // Returns true iff the Conversion does not require an argument
4750         static boolean isText(char c) {
4751             switch (c) {






















4752             case LINE_SEPARATOR:
4753             case PERCENT_SIGN:
4754                 return true;
4755             default:
4756                 return false;
4757             }

4758         }
4759     }
4760 
4761     private static class DateTime {
4762         static final char HOUR_OF_DAY_0 = 'H'; // (00 - 23)
4763         static final char HOUR_0        = 'I'; // (01 - 12)
4764         static final char HOUR_OF_DAY   = 'k'; // (0 - 23) -- like H
4765         static final char HOUR          = 'l'; // (1 - 12) -- like I
4766         static final char MINUTE        = 'M'; // (00 - 59)
4767         static final char NANOSECOND    = 'N'; // (000000000 - 999999999)
4768         static final char MILLISECOND   = 'L'; // jdk, not in gnu (000 - 999)
4769         static final char MILLISECOND_SINCE_EPOCH = 'Q'; // (0 - 99...?)
4770         static final char AM_PM         = 'p'; // (am or pm)
4771         static final char SECONDS_SINCE_EPOCH = 's'; // (0 - 99...?)
4772         static final char SECOND        = 'S'; // (00 - 60 - leap second)
4773         static final char TIME          = 'T'; // (24 hour hh:mm:ss)
4774         static final char ZONE_NUMERIC  = 'z'; // (-1200 - +1200) - ls minus?
4775         static final char ZONE          = 'Z'; // (symbol)




4776 
4777         // Date
4778         static final char NAME_OF_DAY_ABBREV    = 'a'; // 'a'
4779         static final char NAME_OF_DAY           = 'A'; // 'A'
4780         static final char NAME_OF_MONTH_ABBREV  = 'b'; // 'b'
4781         static final char NAME_OF_MONTH         = 'B'; // 'B'
4782         static final char CENTURY               = 'C'; // (00 - 99)
4783         static final char DAY_OF_MONTH_0        = 'd'; // (01 - 31)
4784         static final char DAY_OF_MONTH          = 'e'; // (1 - 31) -- like d
4785 // *    static final char ISO_WEEK_OF_YEAR_2    = 'g'; // cross %y %V
4786 // *    static final char ISO_WEEK_OF_YEAR_4    = 'G'; // cross %Y %V
4787         static final char NAME_OF_MONTH_ABBREV_X  = 'h'; // -- same b
4788         static final char DAY_OF_YEAR           = 'j'; // (001 - 366)
4789         static final char MONTH                 = 'm'; // (01 - 12)
4790 // *    static final char DAY_OF_WEEK_1         = 'u'; // (1 - 7) Monday
4791 // *    static final char WEEK_OF_YEAR_SUNDAY   = 'U'; // (0 - 53) Sunday+
4792 // *    static final char WEEK_OF_YEAR_MONDAY_01 = 'V'; // (01 - 53) Monday+
4793 // *    static final char DAY_OF_WEEK_0         = 'w'; // (0 - 6) Sunday
4794 // *    static final char WEEK_OF_YEAR_MONDAY   = 'W'; // (00 - 53) Monday
4795         static final char YEAR_2                = 'y'; // (00 - 99)
4796         static final char YEAR_4                = 'Y'; // (0000 - 9999)
4797 
4798         // Composites
4799         static final char TIME_12_HOUR  = 'r'; // (hh:mm:ss [AP]M)
4800         static final char TIME_24_HOUR  = 'R'; // (hh:mm same as %H:%M)
4801 // *    static final char LOCALE_TIME   = 'X'; // (%H:%M:%S) - parse format?
4802         static final char DATE_TIME             = 'c';
4803                                             // (Sat Nov 04 12:02:33 EST 1999)
4804         static final char DATE                  = 'D'; // (mm/dd/yy)
4805         static final char ISO_STANDARD_DATE     = 'F'; // (%Y-%m-%d)
4806 // *    static final char LOCALE_DATE           = 'x'; // (mm/dd/yy)
4807 
4808         static boolean isValid(char c) {
4809             switch (c) {
4810             case HOUR_OF_DAY_0:
4811             case HOUR_0:
4812             case HOUR_OF_DAY:
4813             case HOUR:
4814             case MINUTE:
4815             case NANOSECOND:
4816             case MILLISECOND:
4817             case MILLISECOND_SINCE_EPOCH:
4818             case AM_PM:
4819             case SECONDS_SINCE_EPOCH:
4820             case SECOND:
4821             case TIME:
4822             case ZONE_NUMERIC:
4823             case ZONE:
4824 
4825             // Date
4826             case NAME_OF_DAY_ABBREV:
4827             case NAME_OF_DAY:
4828             case NAME_OF_MONTH_ABBREV:
4829             case NAME_OF_MONTH:
4830             case CENTURY:
4831             case DAY_OF_MONTH_0:
4832             case DAY_OF_MONTH:
4833 // *        case ISO_WEEK_OF_YEAR_2:
4834 // *        case ISO_WEEK_OF_YEAR_4:
4835             case NAME_OF_MONTH_ABBREV_X:
4836             case DAY_OF_YEAR:
4837             case MONTH:
4838 // *        case DAY_OF_WEEK_1:
4839 // *        case WEEK_OF_YEAR_SUNDAY:
4840 // *        case WEEK_OF_YEAR_MONDAY_01:
4841 // *        case DAY_OF_WEEK_0:
4842 // *        case WEEK_OF_YEAR_MONDAY:
4843             case YEAR_2:
4844             case YEAR_4:
4845 
4846             // Composites
4847             case TIME_12_HOUR:
4848             case TIME_24_HOUR:
4849 // *        case LOCALE_TIME:
4850             case DATE_TIME:
4851             case DATE:
4852             case ISO_STANDARD_DATE:
4853 // *        case LOCALE_DATE:
4854                 return true;
4855             default:
4856                 return false;
4857             }
4858         }
4859     }
4860 }


  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.util;
  27 
  28 import java.io.BufferedWriter;
  29 import java.io.Closeable;
  30 import java.io.IOException;
  31 import java.io.File;
  32 import java.io.FileOutputStream;
  33 import java.io.FileNotFoundException;
  34 import java.io.Flushable;
  35 import java.io.OutputStream;
  36 import java.io.OutputStreamWriter;
  37 import java.io.PrintStream;
  38 import java.io.UnsupportedEncodingException;
  39 import java.lang.invoke.*;
  40 import java.math.BigDecimal;
  41 import java.math.BigInteger;
  42 import java.math.MathContext;
  43 import java.math.RoundingMode;
  44 import java.nio.charset.Charset;
  45 import java.nio.charset.IllegalCharsetNameException;
  46 import java.nio.charset.UnsupportedCharsetException;
  47 import java.text.DateFormatSymbols;
  48 import java.text.DecimalFormat;
  49 import java.text.DecimalFormatSymbols;
  50 import java.text.NumberFormat;
  51 import java.text.spi.NumberFormatProvider;



  52 
  53 import java.time.DateTimeException;
  54 import java.time.Instant;
  55 import java.time.ZoneId;
  56 import java.time.ZoneOffset;
  57 import java.time.temporal.ChronoField;
  58 import java.time.temporal.TemporalAccessor;
  59 import java.time.temporal.TemporalQueries;
  60 import java.time.temporal.UnsupportedTemporalTypeException;
  61 
  62 import java.lang.compiler.IntrinsicCandidate;
  63 import java.util.regex.Matcher;
  64 import java.util.regex.Pattern;
  65 import java.util.stream.IntStream;
  66 
  67 import jdk.internal.math.DoubleConsts;
  68 import jdk.internal.math.FormattedFloatingDecimal;
  69 import sun.util.locale.provider.LocaleProviderAdapter;
  70 import sun.util.locale.provider.ResourceBundleBasedAdapter;
  71 
  72 import static java.lang.invoke.MethodHandles.*;
  73 import static java.lang.invoke.MethodHandles.constant;
  74 import static java.lang.invoke.MethodHandles.filterArguments;
  75 import static java.lang.invoke.MethodType.methodType;
  76 
  77 /**
  78  * An interpreter for printf-style format strings.  This class provides support
  79  * for layout justification and alignment, common formats for numeric, string,
  80  * and date/time data, and locale-specific output.  Common Java types such as
  81  * {@code byte}, {@link java.math.BigDecimal BigDecimal}, and {@link Calendar}
  82  * are supported.  Limited formatting customization for arbitrary user types is
  83  * provided through the {@link Formattable} interface.
  84  *
  85  * <p> Formatters are not necessarily safe for multithreaded access.  Thread
  86  * safety is optional and is the responsibility of users of methods in this
  87  * class.
  88  *
  89  * <p> Formatted printing for the Java language is heavily inspired by C's
  90  * {@code printf}.  Although the format strings are similar to C, some
  91  * customizations have been made to accommodate the Java language and exploit
  92  * some of its features.  Also, Java formatting is more strict than C's; for
  93  * example, if a conversion is incompatible with a flag, an exception will be
  94  * thrown.  In C inapplicable flags are silently ignored.  The format strings
  95  * are thus intended to be recognizable to C programmers but not necessarily
  96  * completely compatible with those in C.


1904  *   // "c" and "d" are ignored because they are not referenced
1905  * </pre></blockquote>
1906  *
1907  * <p> The maximum number of arguments is limited by the maximum dimension of a
1908  * Java array as defined by
1909  * <cite>The Java&trade; Virtual Machine Specification</cite>.
1910  * If the argument index does not correspond to an
1911  * available argument, then a {@link MissingFormatArgumentException} is thrown.
1912  *
1913  * <p> If there are more arguments than format specifiers, the extra arguments
1914  * are ignored.
1915  *
1916  * <p> Unless otherwise specified, passing a {@code null} argument to any
1917  * method or constructor in this class will cause a {@link
1918  * NullPointerException} to be thrown.
1919  *
1920  * @author  Iris Clark
1921  * @since 1.5
1922  */
1923 public final class Formatter implements Closeable, Flushable {
1924     /** Receiving Appendable */
1925     Appendable a;
1926     /** Formatter locale */
1927     final Locale l;
1928     /** Last low level exception caught */
1929     IOException lastException;
1930     /** Zero for the locale */
1931     final char zero;
1932     /** Round up scaler */
1933     static double SCALEUP = Math.scalb(1.0, 54);
1934     /** Maximum floating decimal digits
1935      *    1 (sign) + 19 (max # sig digits) + 1 ('.') + 1 ('e') + 1 (sign)
1936      *    + 3 (max # exp digits) + 4 (error) = 30
1937      */
1938     static final int MAX_FD_CHARS = 30;
1939 
1940     /**
1941      * Returns a charset object for the given charset name.
1942      * @throws NullPointerException          is csn is null
1943      * @throws UnsupportedEncodingException  if the charset is not supported
1944      */
1945     private static Charset toCharset(String csn)
1946         throws UnsupportedEncodingException
1947     {
1948         Objects.requireNonNull(csn, "charsetName");
1949         try {
1950             return Charset.forName(csn);
1951         } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {
1952             // UnsupportedEncodingException should be thrown
1953             throw new UnsupportedEncodingException(csn);
1954         }
1955     }
1956 
1957     private static final Appendable nonNullAppendable(Appendable a) {
1958         if (a == null)


2600      *         Arguments referenced by the format specifiers in the format
2601      *         string.  If there are more arguments than format specifiers, the
2602      *         extra arguments are ignored.  The maximum number of arguments is
2603      *         limited by the maximum dimension of a Java array as defined by
2604      *         <cite>The Java&trade; Virtual Machine Specification</cite>.
2605      *
2606      * @throws  IllegalFormatException
2607      *          If a format string contains an illegal syntax, a format
2608      *          specifier that is incompatible with the given arguments,
2609      *          insufficient arguments given the format string, or other
2610      *          illegal conditions.  For specification of all possible
2611      *          formatting errors, see the <a href="#detail">Details</a>
2612      *          section of the formatter class specification.
2613      *
2614      * @throws  FormatterClosedException
2615      *          If this formatter has been closed by invoking its {@link
2616      *          #close()} method
2617      *
2618      * @return  This formatter
2619      */
2620     @IntrinsicCandidate
2621     public Formatter format(String format, Object ... args) {
2622         return format(l, format, args);
2623     }
2624 
2625     /**
2626      * Writes a formatted string to this object's destination using the
2627      * specified locale, format string, and arguments.
2628      *
2629      * @param  l
2630      *         The {@linkplain java.util.Locale locale} to apply during
2631      *         formatting.  If {@code l} is {@code null} then no localization
2632      *         is applied.  This does not change this object's locale that was
2633      *         set during construction.
2634      *
2635      * @param  format
2636      *         A format string as described in <a href="#syntax">Format string
2637      *         syntax</a>
2638      *
2639      * @param  args
2640      *         Arguments referenced by the format specifiers in the format
2641      *         string.  If there are more arguments than format specifiers, the
2642      *         extra arguments are ignored.  The maximum number of arguments is
2643      *         limited by the maximum dimension of a Java array as defined by
2644      *         <cite>The Java&trade; Virtual Machine Specification</cite>.
2645      *
2646      * @throws  IllegalFormatException
2647      *          If a format string contains an illegal syntax, a format
2648      *          specifier that is incompatible with the given arguments,
2649      *          insufficient arguments given the format string, or other
2650      *          illegal conditions.  For specification of all possible
2651      *          formatting errors, see the <a href="#detail">Details</a>
2652      *          section of the formatter class specification.
2653      *
2654      * @throws  FormatterClosedException
2655      *          If this formatter has been closed by invoking its {@link
2656      *          #close()} method
2657      *
2658      * @return  This formatter
2659      */
2660     @IntrinsicCandidate
2661     public Formatter format(Locale l, String format, Object ... args) {
2662         List<FormatToken> fsa = parse(format);
2663         ensureOpen();

2664         // index of last argument referenced
2665         int last = -1;
2666         // last ordinary index
2667         int lasto = -1;
2668 
2669         for (FormatToken ft : fsa) {


2670             try {
2671                 int index = ft.index();
2672                 switch (index) {
2673                     case -2:  // fixed string, "%n", or "%%"
2674                         if (ft instanceof FixedString) {
2675                             ((FixedString) ft).print(this);
2676                         } else {
2677                             print((FormatSpecifier) ft, (Object) null, l);
2678                         }
2679                         break;
2680                     case -1:  // relative index
2681                         if (last < 0 || (args != null && last > args.length - 1))
2682                             throw new MissingFormatArgumentException(ft.toString());
2683                         print((FormatSpecifier) ft, (args == null ? null : args[last]), l);
2684                         break;
2685                     case 0:  // ordinary index
2686                         lasto++;
2687                         last = lasto;
2688                         if (args != null && lasto > args.length - 1)
2689                             throw new MissingFormatArgumentException(ft.toString());
2690                         print((FormatSpecifier) ft, (args == null ? null : args[lasto]), l);
2691                         break;
2692                     default:  // explicit index
2693                         last = index - 1;
2694                         if (args != null && last > args.length - 1)
2695                             throw new MissingFormatArgumentException(ft.toString());
2696                         print((FormatSpecifier) ft, (args == null ? null : args[last]), l);
2697                         break;
2698                 }
2699             } catch (IOException x) {
2700                 lastException = x;
2701             }
2702         }
2703         return this;
2704     }
2705 
2706     private Formatter print(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2707         switch (spec.conversion()) {
2708             case DECIMAL_INTEGER:
2709             case OCTAL_INTEGER:
2710             case HEXADECIMAL_INTEGER:
2711                 printInteger(spec, arg, l);


























2712                 break;
2713             case DATE_TIME:
2714                 printDateTime(spec, arg, l);









































































































































































2715                 break;
2716             case SCIENTIFIC:
2717             case GENERAL:
2718             case DECIMAL_FLOAT:
2719             case HEXADECIMAL_FLOAT:
2720                 printFloat(spec, arg, l);
2721                 break;
2722             case CHARACTER:
2723             case CHARACTER_UPPER:
2724                 printCharacter(spec, arg, l);
2725                 break;
2726             case BOOLEAN:
2727                 printBoolean(spec, arg, l);
2728                 break;
2729             case STRING:
2730                 printString(spec, arg, l);
2731                 break;
2732             case HASHCODE:
2733                 printHashCode(spec, arg, l);
2734                 break;
2735             case LINE_SEPARATOR:
2736                 out().append(System.lineSeparator());
2737                 break;
2738             case PERCENT_SIGN:
2739                 print(spec, "%", l);
2740                 break;
2741             default:
2742                 assert false;

2743         }
2744         return this;
2745     }
2746 
2747     private void printInteger(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2748         if (arg == null)
2749             print(spec, "null", l);
2750         else if (arg instanceof Byte)
2751             print(spec, ((Byte) arg).byteValue(), l);
2752         else if (arg instanceof Short)
2753             print(spec, ((Short) arg).shortValue(), l);
2754         else if (arg instanceof Integer)
2755             print(spec, ((Integer) arg).intValue(), l);
2756         else if (arg instanceof Long)
2757             print(spec, ((Long) arg).longValue(), l);
2758         else if (arg instanceof BigInteger)
2759             print(spec, (BigInteger) arg, l);
2760         else
2761             spec.conversion().fail(arg);
2762     }
2763 
2764     private void printFloat(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2765         if (arg == null)
2766             print(spec, "null", l);
2767         else if (arg instanceof Float)
2768             print(spec, ((Float) arg).floatValue(), l);
2769         else if (arg instanceof Double)
2770             print(spec, ((Double) arg).doubleValue(), l);
2771         else if (arg instanceof BigDecimal)
2772             print(spec, (BigDecimal) arg, l);
2773         else
2774             spec.conversion().fail(arg);
2775     }
2776 
2777     private void printDateTime(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2778         if (arg == null) {
2779             print(spec, "null", l);
2780             return;



























































2781         }
2782         Calendar cal = null;
2783 
2784         // Instead of Calendar.setLenient(true), perhaps we should
2785         // wrap the IllegalArgumentException that might be thrown?
2786         if (arg instanceof Long) {
2787             // Note that the following method uses an instance of the
2788             // default time zone (TimeZone.getDefaultRef().
2789             cal = Calendar.getInstance(l == null ? Locale.US : l);
2790             cal.setTimeInMillis((Long)arg);
2791         } else if (arg instanceof Date) {
2792             // Note that the following method uses an instance of the
2793             // default time zone (TimeZone.getDefaultRef().
2794             cal = Calendar.getInstance(l == null ? Locale.US : l);
2795             cal.setTime((Date)arg);
2796         } else if (arg instanceof Calendar) {
2797             cal = (Calendar) ((Calendar) arg).clone();
2798             cal.setLenient(true);
2799         } else if (arg instanceof TemporalAccessor) {
2800             print(spec, (TemporalAccessor) arg, spec.dateTime(), l);
2801             return;
2802         } else {
2803             spec.conversion().fail(arg);
2804         }
2805         // Use the provided locale so that invocations of
2806         // localizedMagnitude() use optimizations for null.
2807         print(spec, cal, spec.dateTime(), l);
2808     }
2809 
2810     private void printCharacter(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2811         if (arg == null) {
2812             print(spec, "null", l);
2813             return;
2814         }
2815         String s = null;
2816         if (arg instanceof Character) {
2817             s = ((Character)arg).toString();
2818         } else if (arg instanceof Byte) {
2819             byte i = (Byte) arg;
2820             if (Character.isValidCodePoint(i))
2821                 s = new String(Character.toChars(i));
2822             else
2823                 throw new IllegalFormatCodePointException(i);
2824         } else if (arg instanceof Short) {
2825             short i = (Short) arg;
2826             if (Character.isValidCodePoint(i))
2827                 s = new String(Character.toChars(i));
2828             else
2829                 throw new IllegalFormatCodePointException(i);
2830         } else if (arg instanceof Integer) {
2831             int i = (Integer) arg;
2832             if (Character.isValidCodePoint(i))
2833                 s = new String(Character.toChars(i));
2834             else
2835                 throw new IllegalFormatCodePointException(i);
2836         } else {
2837             spec.conversion().fail(arg);
2838         }
2839         print(spec, s, l);
2840     }
2841 
2842     private void printString(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2843         if (arg instanceof Formattable) {
2844             Formatter fmt = this;
2845             if (fmt.locale() != l)
2846                 fmt = new Formatter(fmt.out(), l);
2847             ((Formattable)arg).formatTo(fmt, spec.flags(), spec.width(), spec.precision());
2848         } else {
2849             if (Flags.contains(spec.flags(), Flags.ALTERNATE))
2850                 failMismatch(Flags.ALTERNATE, 's');
2851             if (arg == null)
2852                 print(spec, "null", l);
2853             else
2854                 print(spec, arg.toString(), l);
2855         }
2856     }
2857 
2858     private void printBoolean(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2859         String s;
2860         if (arg != null)
2861             s = ((arg instanceof Boolean)
2862                     ? ((Boolean)arg).toString()
2863                     : Boolean.toString(true));
2864         else
2865             s = Boolean.toString(false);
2866         print(spec, s, l);
2867     }
2868 
2869     private Formatter printHashCode(FormatSpecifier spec, Object arg, Locale l) throws IOException {
2870         String s = (arg == null
2871                 ? "null"
2872                 : Integer.toHexString(arg.hashCode()));
2873         print(spec, s, l);
2874         return this;
2875     }















2876 
2877     private Formatter print(FormatSpecifier spec, String s, Locale l) throws IOException {
2878         if (spec.precision() != -1 && spec.precision() < s.length())
2879             s = s.substring(0, spec.precision());
2880         if (Flags.contains(spec.flags(), Flags.UPPERCASE))
2881             s = toUpperCaseWithLocale(s, l);
2882         appendJustified(spec, a, s);
2883         return this;
2884     }









2885 
2886     private String toUpperCaseWithLocale(String s, Locale l) {
2887         return s.toUpperCase(Objects.requireNonNullElse(l,
2888                 Locale.getDefault(Locale.Category.FORMAT)));
2889     }






2890 
2891     private Appendable appendJustified(FormatSpecifier spec, Appendable a, CharSequence cs) throws IOException {
2892         if (spec.width() == -1) {
2893             return a.append(cs);
2894         }
2895         boolean padRight = Flags.contains(spec.flags(), Flags.LEFT_JUSTIFY);
2896         int sp = spec.width() - cs.length();
2897         if (padRight) {
2898             a.append(cs);


2899         }
2900         for (int i = 0; i < sp; i++) {
2901             a.append(' ');







2902         }
2903         if (!padRight) {
2904             a.append(cs);
2905         }
2906         return a;
2907     }
2908 
2909     private Formatter print(FormatSpecifier spec, byte value, Locale l) throws IOException {
2910         long v = value;
2911         if (value < 0
2912                 && (spec.conversion() == Conversion.OCTAL_INTEGER
2913                 || spec.conversion() == Conversion.HEXADECIMAL_INTEGER)) {
2914             v += (1L << 8);
2915             assert v >= 0 : v;




2916         }
2917         return print(spec, v, l);
2918     }
2919 
2920     private Formatter print(FormatSpecifier spec, short value, Locale l) throws IOException {
2921         long v = value;
2922         if (value < 0
2923                 && (spec.conversion() == Conversion.OCTAL_INTEGER
2924                 || spec.conversion() == Conversion.HEXADECIMAL_INTEGER)) {
2925             v += (1L << 16);
2926             assert v >= 0 : v;
2927         }
2928         return print(spec, v, l);
2929     }
2930 
2931     private Formatter print(FormatSpecifier spec, int value, Locale l) throws IOException {
2932         long v = value;
2933         if (value < 0
2934                 && (spec.conversion() == Conversion.OCTAL_INTEGER
2935                 || spec.conversion() == Conversion.HEXADECIMAL_INTEGER)) {
2936             v += (1L << 32);
2937             assert v >= 0 : v;



2938         }
2939         return print(spec, v, l);
2940     }
2941 
2942     private Formatter print(FormatSpecifier spec, long value, Locale l) throws IOException {
2943         StringBuilder sb = new StringBuilder();

2944 
2945         if (spec.conversion() == Conversion.DECIMAL_INTEGER) {
2946             boolean neg = value < 0;
2947             String valueStr = Long.toString(value, 10);
2948 
2949             // leading sign indicator
2950             leadingSign(spec, sb, neg);


2951 
2952             // the value
2953             localizedMagnitude(sb, valueStr, neg ? 1 : 0, spec.flags(), adjustWidth(spec.width(), spec.flags(), neg), l);



2954 
2955             // trailing sign indicator
2956             trailingSign(spec, sb, neg);
2957         } else if (spec.conversion() == Conversion.OCTAL_INTEGER) {
2958             spec.checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE, Flags.PLUS);
2959             String s = Long.toOctalString(value);
2960             int len = (Flags.contains(spec.flags(), Flags.ALTERNATE)
2961                     ? s.length() + 1
2962                     : s.length());














2963 
2964             // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
2965             if (Flags.contains(spec.flags(), Flags.ALTERNATE))
2966                 sb.append('0');
2967             if (Flags.contains(spec.flags(), Flags.ZERO_PAD)) {
2968                 trailingZeros(sb, spec.width() - len);


2969             }
2970             sb.append(s);
2971         } else if (spec.conversion() == Conversion.HEXADECIMAL_INTEGER) {
2972             spec.checkBadFlags(Flags.PARENTHESES, Flags.LEADING_SPACE,
2973                     Flags.PLUS);
2974             String s = Long.toHexString(value);
2975             int len = (Flags.contains(spec.flags(), Flags.ALTERNATE)
2976                     ? s.length() + 2
2977                     : s.length());
2978 
2979             // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
2980             if (Flags.contains(spec.flags(), Flags.ALTERNATE))
2981                 sb.append(Flags.contains(spec.flags(), Flags.UPPERCASE) ? "0X" : "0x");
2982             if (Flags.contains(spec.flags(), Flags.ZERO_PAD)) {
2983                 trailingZeros(sb, spec.width() - len);
2984             }
2985             if (Flags.contains(spec.flags(), Flags.UPPERCASE))
2986                 s = toUpperCaseWithLocale(s, l);
2987             sb.append(s);
2988         }
2989 
2990         // justify based on width
2991         appendJustified(spec, a, sb);
2992         return this;
2993     }
2994 
2995     // neg := val < 0
2996     private void leadingSign(FormatSpecifier spec, StringBuilder sb, boolean neg) {
2997         if (!neg) {
2998             if (Flags.contains(spec.flags(), Flags.PLUS)) {
2999                 sb.append('+');
3000             } else if (Flags.contains(spec.flags(), Flags.LEADING_SPACE)) {
3001                 sb.append(' ');
3002             }
3003         } else {
3004             if (Flags.contains(spec.flags(), Flags.PARENTHESES))
3005                 sb.append('(');
3006             else
3007                 sb.append('-');
3008         }
3009     }
3010 
3011     // neg := val < 0
3012     private void trailingSign(FormatSpecifier spec, StringBuilder sb, boolean neg) {
3013         if (neg && Flags.contains(spec.flags(), Flags.PARENTHESES))
3014             sb.append(')');
3015     }
3016 
3017     private void print(FormatSpecifier spec, BigInteger value, Locale l) throws IOException {
3018         StringBuilder sb = new StringBuilder();
3019         boolean neg = value.signum() == -1;
3020         BigInteger v = value.abs();
3021 
3022         // leading sign indicator
3023         leadingSign(spec, sb, neg);
3024 
3025         // the value
3026         if (spec.conversion() == Conversion.DECIMAL_INTEGER) {
3027             localizedMagnitude(sb, v.toString(), 0, spec.flags(), adjustWidth(spec.width(), spec.flags(), neg), l);
3028         } else if (spec.conversion() == Conversion.OCTAL_INTEGER) {
3029             String s = v.toString(8);
3030 
3031             int len = s.length() + sb.length();
3032             if (neg && Flags.contains(spec.flags(), Flags.PARENTHESES))
3033                 len++;
3034 
3035             // apply ALTERNATE (radix indicator for octal) before ZERO_PAD
3036             if (Flags.contains(spec.flags(), Flags.ALTERNATE)) {
3037                 len++;
3038                 sb.append('0');
3039             }
3040             if (Flags.contains(spec.flags(), Flags.ZERO_PAD)) {
3041                 trailingZeros(sb, spec.width() - len);
3042             }
3043             sb.append(s);
3044         } else if (spec.conversion() == Conversion.HEXADECIMAL_INTEGER) {
3045             String s = v.toString(16);
3046 
3047             int len = s.length() + sb.length();
3048             if (neg && Flags.contains(spec.flags(), Flags.PARENTHESES))
3049                 len++;
3050 
3051             // apply ALTERNATE (radix indicator for hex) before ZERO_PAD
3052             if (Flags.contains(spec.flags(), Flags.ALTERNATE)) {
3053                 len += 2;
3054                 sb.append(Flags.contains(spec.flags(), Flags.UPPERCASE) ? "0X" : "0x");
3055             }
3056             if (Flags.contains(spec.flags(), Flags.ZERO_PAD)) {
3057                 trailingZeros(sb, spec.width() - len);
3058             }
3059             if (Flags.contains(spec.flags(), Flags.UPPERCASE))
3060                 s = toUpperCaseWithLocale(s, l);
3061             sb.append(s);
3062         }
3063 
3064         // trailing sign indicator
3065         trailingSign(spec, sb, (value.signum() == -1));
3066 
3067         // justify based on width
3068         appendJustified(spec, a, sb);
3069     }
3070 
3071     private Formatter print(FormatSpecifier spec, float value, Locale l) throws IOException {
3072         return print(spec, (double) value, l);
3073     }































































3074 
3075     private Formatter print(FormatSpecifier spec, double value, Locale l) throws IOException {
3076         StringBuilder sb = new StringBuilder();
3077         boolean neg = Double.compare(value, 0.0) == -1;



3078 
3079         if (!Double.isNaN(value)) {
3080             double v = Math.abs(value);


3081 
3082             // leading sign indicator
3083             leadingSign(spec, sb, neg);
3084 
3085             // the value
3086             if (!Double.isInfinite(v))
3087                 print(spec, sb, v, l, spec.flags(), spec.conversion(), spec.precision(), neg);
3088             else
3089                 sb.append(Flags.contains(spec.flags(), Flags.UPPERCASE)
3090                         ? "INFINITY" : "Infinity");
































3091 
3092             // trailing sign indicator
3093             trailingSign(spec, sb, neg);
3094         } else {
3095             sb.append(Flags.contains(spec.flags(), Flags.UPPERCASE) ? "NAN" : "NaN");





3096         }
3097 
3098         // justify based on width
3099         appendJustified(spec, a, sb);
3100         return this;
3101     }






















3102 
3103     // !Double.isInfinite(value) && !Double.isNaN(value)
3104     private void print(FormatSpecifier spec, StringBuilder sb, double value, Locale l,
3105                        int f, Conversion conversion, int precision, boolean neg)
3106             throws IOException
3107     {
3108         if (conversion == Conversion.SCIENTIFIC) {
3109             // Create a new FormattedFloatingDecimal with the desired
3110             // precision.
3111             int prec = (precision == -1 ? 6 : precision);
3112 
3113             FormattedFloatingDecimal fd
3114                     = FormattedFloatingDecimal.valueOf(value, prec,
3115                     FormattedFloatingDecimal.Form.SCIENTIFIC);
3116 
3117             StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3118             addZeros(mant, prec);
3119 
3120             // If the precision is zero and the '#' flag is set, add the
3121             // requested decimal point.
3122             if (Flags.contains(f, Flags.ALTERNATE) && (prec == 0)) {
3123                 mant.append('.');
3124             }
3125 
3126             char[] exp = (value == 0.0)
3127                     ? new char[] {'+','0','0'} : fd.getExponent();
3128 
3129             int width = spec.width();
3130             if (width != -1) {
3131                 width = adjustWidth(width - exp.length - 1, f, neg);
3132             }
3133             localizedMagnitude(sb, mant, 0, f, width, l);
3134 
3135             sb.append(Flags.contains(f, Flags.UPPERCASE) ? 'E' : 'e');
3136 
3137             char sign = exp[0];
3138             assert(sign == '+' || sign == '-');
3139             sb.append(sign);
3140 
3141             localizedMagnitudeExp(spec, sb, exp, 1, l);
3142         } else if (conversion == Conversion.DECIMAL_FLOAT) {
3143             // Create a new FormattedFloatingDecimal with the desired
3144             // precision.
3145             int prec = (precision == -1 ? 6 : precision);
3146 
3147             FormattedFloatingDecimal fd
3148                     = FormattedFloatingDecimal.valueOf(value, prec,
3149                     FormattedFloatingDecimal.Form.DECIMAL_FLOAT);
3150 
3151             StringBuilder mant = new StringBuilder().append(fd.getMantissa());
3152             addZeros(mant, prec);
3153 
3154             // If the precision is zero and the '#' flag is set, add the
3155             // requested decimal point.
3156             if (Flags.contains(f, Flags.ALTERNATE) && (prec == 0))
3157                 mant.append('.');
3158 
3159             int width = spec.width();
3160             if (width != -1)
3161                 width = adjustWidth(width, f, neg);
3162             localizedMagnitude(sb, mant, 0, f, width, l);
3163         } else if (conversion == Conversion.GENERAL) {
3164             int prec = precision;
3165             if (precision == -1)
3166                 prec = 6;
3167             else if (precision == 0)
3168                 prec = 1;
3169 
3170             char[] exp;
3171             StringBuilder mant = new StringBuilder();
3172             int expRounded;
3173             if (value == 0.0) {
3174                 exp = null;
3175                 mant.append('0');
3176                 expRounded = 0;
3177             } else {
3178                 FormattedFloatingDecimal fd
3179                         = FormattedFloatingDecimal.valueOf(value, prec,
3180                         FormattedFloatingDecimal.Form.GENERAL);
3181                 exp = fd.getExponent();
3182                 mant.append(fd.getMantissa());
3183                 expRounded = fd.getExponentRounded();
3184             }

























3185 
3186             if (exp != null) {
3187                 prec -= 1;
3188             } else {
3189                 prec -= expRounded + 1;
3190             }
3191 
3192             addZeros(mant, prec);
3193             // If the precision is zero and the '#' flag is set, add the
3194             // requested decimal point.
3195             if (Flags.contains(f, Flags.ALTERNATE) && (prec == 0)) {
3196                 mant.append('.');
3197             }



3198 
3199             int width = spec.width();
3200             if (width != -1) {
3201                 if (exp != null)
3202                     width = adjustWidth(width - exp.length - 1, f, neg);
3203                 else
3204                     width = adjustWidth(width, f, neg);
3205             }
3206             localizedMagnitude(sb, mant, 0, f, width, l);
3207 
3208             if (exp != null) {
3209                 sb.append(Flags.contains(f, Flags.UPPERCASE) ? 'E' : 'e');

3210 
3211                 char sign = exp[0];
3212                 assert(sign == '+' || sign == '-');
3213                 sb.append(sign);
3214 
3215                 localizedMagnitudeExp(spec, sb, exp, 1, l);














3216             }
3217         } else if (conversion == Conversion.HEXADECIMAL_FLOAT) {
3218             int prec = precision;
3219             if (precision == -1)
3220                 // assume that we want all of the digits
3221                 prec = 0;
3222             else if (precision == 0)
3223                 prec = 1;
3224 
3225             String s = hexDouble(value, prec);
3226 
3227             StringBuilder va = new StringBuilder();
3228             boolean upper = Flags.contains(f, Flags.UPPERCASE);
3229             sb.append(upper ? "0X" : "0x");
3230 
3231             if (Flags.contains(f, Flags.ZERO_PAD)) {
3232                 trailingZeros(sb, spec.width() - s.length() - 2);
3233             }
3234 
3235             int idx = s.indexOf('p');
3236             if (upper) {
3237                 String tmp = s.substring(0, idx);
3238                 // don't localize hex
3239                 tmp = tmp.toUpperCase(Locale.ROOT);
3240                 va.append(tmp);
3241             } else {
3242                 va.append(s, 0, idx);
3243             }
3244             if (prec != 0) {
3245                 addZeros(va, prec);

3246             }
3247             sb.append(va);
3248             sb.append(upper ? 'P' : 'p');
3249             sb.append(s, idx+1, s.length());
3250         }
3251     }
3252 
3253     // Add zeros to the requested precision.
3254     private void addZeros(StringBuilder sb, int prec) {
3255         // Look for the dot.  If we don't find one, the we'll need to add
3256         // it before we add the zeros.
3257         int len = sb.length();
3258         int i;
3259         for (i = 0; i < len; i++) {
3260             if (sb.charAt(i) == '.') {
3261                 break;
3262             }
3263         }
3264         boolean needDot = i == len;
3265 
3266         // Determine existing precision.
3267         int outPrec = len - i - (needDot ? 0 : 1);
3268         assert (outPrec <= prec);
3269         if (outPrec == prec) {
3270             return;
3271         }
3272 
3273         // Add dot if previously determined to be necessary.
3274         if (needDot) {
3275             sb.append('.');
3276         }
3277 
3278         // Add zeros.
3279         trailingZeros(sb, prec - outPrec);
3280     }
3281 
3282     // Method assumes that d > 0.
3283     private String hexDouble(double d, int prec) {
3284         // Let Double.toHexString handle simple cases
3285         if (!Double.isFinite(d) || d == 0.0 || prec == 0 || prec >= 13) {
3286             // remove "0x"
3287             return Double.toHexString(d).substring(2);
3288         } else {
3289             assert(prec >= 1 && prec <= 12);
3290 
3291             int exponent  = Math.getExponent(d);
3292             boolean subnormal
3293                     = (exponent == Double.MIN_EXPONENT - 1);
3294 
3295             // If this is subnormal input so normalize (could be faster to
3296             // do as integer operation).
3297             if (subnormal) {
3298                 d *= SCALEUP;
3299                 // Calculate the exponent.  This is not just exponent + 54
3300                 // since the former is not the normalized exponent.
3301                 exponent = Math.getExponent(d);
3302                 assert exponent >= Double.MIN_EXPONENT &&

3303                         exponent <= Double.MAX_EXPONENT: exponent;
3304             }
3305 
3306             int precision = 1 + prec*4;
3307             int shiftDistance
3308                     =  DoubleConsts.SIGNIFICAND_WIDTH - precision;
3309             assert(shiftDistance >= 1 && shiftDistance < DoubleConsts.SIGNIFICAND_WIDTH);
3310 
3311             long doppel = Double.doubleToLongBits(d);
3312             // Deterime the number of bits to keep.
3313             long newSignif
3314                     = (doppel & (DoubleConsts.EXP_BIT_MASK
3315                     | DoubleConsts.SIGNIF_BIT_MASK))
3316                     >> shiftDistance;
3317             // Bits to round away.
3318             long roundingBits = doppel & ~(~0L << shiftDistance);
3319 
3320             // To decide how to round, look at the low-order bit of the
3321             // working significand, the highest order discarded bit (the
3322             // round bit) and whether any of the lower order discarded bits
3323             // are nonzero (the sticky bit).
3324 
3325             boolean leastZero = (newSignif & 0x1L) == 0L;
3326             boolean round
3327                     = ((1L << (shiftDistance - 1) ) & roundingBits) != 0L;
3328             boolean sticky  = shiftDistance > 1 &&
3329                     (~(1L<< (shiftDistance - 1)) & roundingBits) != 0;
3330             if((leastZero && round && sticky) || (!leastZero && round)) {
3331                 newSignif++;
3332             }
3333 
3334             long signBit = doppel & DoubleConsts.SIGN_BIT_MASK;
3335             newSignif = signBit | (newSignif << shiftDistance);
3336             double result = Double.longBitsToDouble(newSignif);
3337 
3338             if (Double.isInfinite(result) ) {
3339                 // Infinite result generated by rounding
3340                 return "1.0p1024";
3341             } else {
3342                 String res = Double.toHexString(result).substring(2);
3343                 if (!subnormal)
3344                     return res;
3345                 else {
3346                     // Create a normalized subnormal string.
3347                     int idx = res.indexOf('p');
3348                     if (idx == -1) {
3349                         // No 'p' character in hex string.
3350                         assert false;
3351                         return null;
3352                     } else {
3353                         // Get exponent and append at the end.
3354                         String exp = res.substring(idx + 1);
3355                         int iexp = Integer.parseInt(exp) -54;
3356                         return res.substring(0, idx) + "p"
3357                                 + Integer.toString(iexp);

3358                     }
3359                 }
3360             }
3361         }
3362     }
3363 
3364     private void print(FormatSpecifier spec, BigDecimal value, Locale l) throws IOException {
3365         if (spec.conversion() == Conversion.HEXADECIMAL_FLOAT)
3366             spec.conversion().fail(value);
3367         StringBuilder sb = new StringBuilder();
3368         boolean neg = value.signum() == -1;
3369         BigDecimal v = value.abs();
3370         // leading sign indicator
3371         leadingSign(spec, sb, neg);
3372 
3373         // the value
3374         print(sb, v, l, spec.flags(), spec.conversion(), spec.width(), spec.precision(), neg);
3375 
3376         // trailing sign indicator
3377         trailingSign(spec, sb, neg);
3378 
3379         // justify based on width
3380         appendJustified(spec, a, sb);
3381     }
3382 
3383     // value > 0
3384     private void print(StringBuilder sb, BigDecimal value, Locale l,
3385                        int f, Conversion conversion, int width, int precision, boolean neg)
3386             throws IOException
3387     {
3388         if (conversion == Conversion.SCIENTIFIC) {
3389             // Create a new BigDecimal with the desired precision.
3390             int prec = (precision == -1 ? 6 : precision);
3391             int scale = value.scale();
3392             int origPrec = value.precision();
3393             int nzeros = 0;
3394             int compPrec;
3395 
3396             if (prec > origPrec - 1) {
3397                 compPrec = origPrec;
3398                 nzeros = prec - (origPrec - 1);
3399             } else {
3400                 compPrec = prec + 1;
3401             }
3402 
3403             MathContext mc = new MathContext(compPrec);
3404             BigDecimal v
3405                     = new BigDecimal(value.unscaledValue(), scale, mc);
3406 
3407             BigDecimalLayout bdl
3408                     = new BigDecimalLayout(v.unscaledValue(), v.scale(),
3409                     BigDecimalLayoutForm.SCIENTIFIC);


3410 
3411             StringBuilder mant = bdl.mantissa();








3412 
3413             // Add a decimal point if necessary.  The mantissa may not
3414             // contain a decimal point if the scale is zero (the internal
3415             // representation has no fractional part) or the original
3416             // precision is one. Append a decimal point if '#' is set or if
3417             // we require zero padding to get to the requested precision.
3418             if ((origPrec == 1 || !bdl.hasDot())
3419                     && (nzeros > 0 || (Flags.contains(f, Flags.ALTERNATE)))) {
3420                 mant.append('.');
3421             }

3422 
3423             // Add trailing zeros in the case precision is greater than
3424             // the number of available digits after the decimal separator.
3425             trailingZeros(mant, nzeros);
3426 
3427             StringBuilder exp = bdl.exponent();
3428             int newW = width;
3429             if (newW != -1) {
3430                 newW = adjustWidth(newW - exp.length() - 1, f, neg);
3431             }
3432             localizedMagnitude(sb, mant, 0, f, newW, l);
3433 
3434             sb.append(Flags.contains(f, Flags.UPPERCASE) ? 'E' : 'e');



































3435 
3436             int flags = Flags.remove(f, Flags.GROUP);
3437             char sign = exp.charAt(0);
3438             assert(sign == '+' || sign == '-');
3439             sb.append(sign);
3440 
3441             sb.append(localizedMagnitude(null, exp, 1, flags, -1, l));
3442         } else if (conversion == Conversion.DECIMAL_FLOAT) {
3443             // Create a new BigDecimal with the desired precision.
3444             int prec = (precision == -1 ? 6 : precision);
3445             int scale = value.scale();
3446 
3447             if (scale > prec) {
3448                 // more "scale" digits than the requested "precision"
3449                 int compPrec = value.precision();
3450                 if (compPrec <= scale) {
3451                     // case of 0.xxxxxx
3452                     value = value.setScale(prec, RoundingMode.HALF_UP);
3453                 } else {
3454                     compPrec -= (scale - prec);
3455                     value = new BigDecimal(value.unscaledValue(),
3456                             scale,
3457                             new MathContext(compPrec));
3458                 }
3459             }
3460             BigDecimalLayout bdl = new BigDecimalLayout(
3461                     value.unscaledValue(),
3462                     value.scale(),
3463                     BigDecimalLayoutForm.DECIMAL_FLOAT);
3464 
3465             StringBuilder mant = bdl.mantissa();
3466             int nzeros = (bdl.scale() < prec ? prec - bdl.scale() : 0);
3467 
3468             // Add a decimal point if necessary.  The mantissa may not
3469             // contain a decimal point if the scale is zero (the internal
3470             // representation has no fractional part).  Append a decimal
3471             // point if '#' is set or we require zero padding to get to the
3472             // requested precision.
3473             if (bdl.scale() == 0 && (Flags.contains(f, Flags.ALTERNATE)
3474                     || nzeros > 0)) {
3475                 mant.append('.');
3476             }
3477 
3478             // Add trailing zeros if the precision is greater than the
3479             // number of available digits after the decimal separator.
3480             trailingZeros(mant, nzeros);
3481 
3482             localizedMagnitude(sb, mant, 0, f, adjustWidth(width, f, neg), l);
3483         } else if (conversion == Conversion.GENERAL) {
3484             int prec = precision;
3485             if (precision == -1)
3486                 prec = 6;
3487             else if (precision == 0)
3488                 prec = 1;
3489 
3490             BigDecimal tenToTheNegFour = BigDecimal.valueOf(1, 4);
3491             BigDecimal tenToThePrec = BigDecimal.valueOf(1, -prec);
3492             if ((value.equals(BigDecimal.ZERO))
3493                     || ((value.compareTo(tenToTheNegFour) != -1)
3494                     && (value.compareTo(tenToThePrec) == -1))) {
3495 
3496                 int e = - value.scale()
3497                         + (value.unscaledValue().toString().length() - 1);
3498 
3499                 // xxx.yyy
3500                 //   g precision (# sig digits) = #x + #y
3501                 //   f precision = #y
3502                 //   exponent = #x - 1
3503                 // => f precision = g precision - exponent - 1
3504                 // 0.000zzz
3505                 //   g precision (# sig digits) = #z
3506                 //   f precision = #0 (after '.') + #z
3507                 //   exponent = - #0 (after '.') - 1
3508                 // => f precision = g precision - exponent - 1
3509                 prec = prec - e - 1;
3510 
3511                 print(sb, value, l, f, Conversion.DECIMAL_FLOAT, width, prec, neg);
3512             } else {
3513                 print(sb, value, l, f, Conversion.SCIENTIFIC, width, prec - 1, neg);






3514             }
3515         } else if (conversion == Conversion.HEXADECIMAL_FLOAT) {
3516             // This conversion isn't supported.  The error should be
3517             // reported earlier.
3518             assert false;
3519         }
3520     }
3521 
3522     private class BigDecimalLayout {
3523         private StringBuilder mant;
3524         private StringBuilder exp;
3525         private boolean dot = false;
3526         private int scale;
3527 
3528         public BigDecimalLayout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3529             layout(intVal, scale, form);
3530         }
3531 
3532         public boolean hasDot() {
3533             return dot;
3534         }
3535 
3536         public int scale() {
3537             return scale;
3538         }
3539 
3540         public StringBuilder mantissa() {
3541             return mant;
3542         }
3543 
3544         // The exponent will be formatted as a sign ('+' or '-') followed
3545         // by the exponent zero-padded to include at least two digits.
3546         public StringBuilder exponent() {
3547             return exp;
3548         }
3549 
3550         private void layout(BigInteger intVal, int scale, BigDecimalLayoutForm form) {
3551             String coeff = intVal.toString();
3552             this.scale = scale;
3553 
3554             // Construct a buffer, with sufficient capacity for all cases.
3555             // If E-notation is needed, length will be: +1 if negative, +1
3556             // if '.' needed, +2 for "E+", + up to 10 for adjusted
3557             // exponent.  Otherwise it could have +1 if negative, plus
3558             // leading "0.00000"
3559             int len = coeff.length();
3560             mant = new StringBuilder(len + 14);
3561 
3562             if (scale == 0) {
3563                 if (len > 1) {
3564                     mant.append(coeff.charAt(0));
3565                     if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3566                         mant.append('.');
3567                         dot = true;
3568                         mant.append(coeff, 1, len);
3569                         exp = new StringBuilder("+");
3570                         if (len < 10) {
3571                             exp.append('0').append(len - 1);













3572                         } else {
3573                             exp.append(len - 1);
3574                         }
3575                     } else {
3576                         mant.append(coeff, 1, len);



3577                     }
3578                 } else {
3579                     mant.append(coeff);
3580                     if (form == BigDecimalLayoutForm.SCIENTIFIC) {
3581                         exp = new StringBuilder("+00");





















3582                     }
3583                 }
3584             } else if (form == BigDecimalLayoutForm.DECIMAL_FLOAT) {
3585                 // count of padding zeros
3586 
3587                 if (scale >= len) {
3588                     // 0.xxx form
3589                     mant.append("0.");
3590                     dot = true;
3591                     trailingZeros(mant, scale - len);
3592                     mant.append(coeff);
3593                 } else {
3594                     if (scale > 0) {
3595                         // xx.xx form
3596                         int pad = len - scale;
3597                         mant.append(coeff, 0, pad);
3598                         mant.append('.');
3599                         dot = true;
3600                         mant.append(coeff, pad, len);
3601                     } else { // scale < 0
3602                         // xx form
3603                         mant.append(coeff, 0, len);
3604                         if (intVal.signum() != 0) {
3605                             trailingZeros(mant, -scale);




3606                         }
3607                         this.scale = 0;


3608                     }
3609                 }
3610             } else {
3611                 // x.xxx form
3612                 mant.append(coeff.charAt(0));
3613                 if (len > 1) {
3614                     mant.append('.');
3615                     dot = true;
3616                     mant.append(coeff, 1, len);
3617                 }
3618                 exp = new StringBuilder();
3619                 long adjusted = -(long) scale + (len - 1);
3620                 if (adjusted != 0) {
3621                     long abs = Math.abs(adjusted);
3622                     // require sign
3623                     exp.append(adjusted < 0 ? '-' : '+');
3624                     if (abs < 10) {
3625                         exp.append('0');
3626                     }
3627                     exp.append(abs);
3628                 } else {
3629                     exp.append("+00");
3630                 }
3631             }
3632         }
3633     }
3634 
3635     private int adjustWidth(int width, int f, boolean neg) {
3636         int newW = width;
3637         if (newW != -1 && neg && Flags.contains(f, Flags.PARENTHESES))
3638             newW--;
3639         return newW;
3640     }
3641 
3642     // Add trailing zeros
3643     private void trailingZeros(StringBuilder sb, int nzeros) {
3644         for (int i = 0; i < nzeros; i++) {
3645             sb.append('0');

3646         }
3647     }
3648 
3649     private void print(FormatSpecifier spec, Calendar t, DateTime dt, Locale l)  throws IOException {
3650         StringBuilder sb = new StringBuilder();
3651         print(spec, sb, t, dt, l);
3652 
3653         // justify based on width
3654         if (Flags.contains(spec.flags(), Flags.UPPERCASE)) {
3655             appendJustified(spec, a, toUpperCaseWithLocale(sb.toString(), l));
3656         } else {
3657             appendJustified(spec, a, sb);

3658         }
3659     }
3660 
3661     private Appendable print(FormatSpecifier spec, StringBuilder sb, Calendar t, DateTime dt, Locale l)
3662             throws IOException {
3663         if (sb == null)
3664             sb = new StringBuilder();
3665         switch (dt) {
3666             case HOUR_OF_DAY_0: // 'H' (00 - 23)
3667             case HOUR_0:        // 'I' (01 - 12)
3668             case HOUR_OF_DAY:   // 'k' (0 - 23) -- like H
3669             case HOUR:        { // 'l' (1 - 12) -- like I
3670                 int i = t.get(Calendar.HOUR_OF_DAY);
3671                 if (dt == DateTime.HOUR_0 || dt == DateTime.HOUR)
3672                     i = (i == 0 || i == 12 ? 12 : i % 12);
3673                 int flags = (dt == DateTime.HOUR_OF_DAY_0
3674                         || dt == DateTime.HOUR_0
3675                         ? Flags.ZERO_PAD
3676                         : Flags.NONE);
3677                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3678                 break;
3679             }
3680             case MINUTE:      { // 'M' (00 - 59)
3681                 int i = t.get(Calendar.MINUTE);
3682                 int flags = Flags.ZERO_PAD;
3683                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3684                 break;
3685             }
3686             case NANOSECOND:  { // 'N' (000000000 - 999999999)
3687                 int i = t.get(Calendar.MILLISECOND) * 1000000;
3688                 int flags = Flags.ZERO_PAD;
3689                 sb.append(localizedMagnitude(null, i, flags, 9, l));
3690                 break;
3691             }
3692             case MILLISECOND: { // 'L' (000 - 999)
3693                 int i = t.get(Calendar.MILLISECOND);
3694                 int flags = Flags.ZERO_PAD;
3695                 sb.append(localizedMagnitude(null, i, flags, 3, l));
3696                 break;
3697             }
3698             case MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)
3699                 long i = t.getTimeInMillis();
3700                 int flags = Flags.NONE;
3701                 sb.append(localizedMagnitude(null, i, flags, spec.width(), l));
3702                 break;
3703             }
3704             case AM_PM:       { // 'p' (am or pm)
3705                 // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper
3706                 String[] ampm = { "AM", "PM" };
3707                 if (l != null && l != Locale.US) {
3708                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);
3709                     ampm = dfs.getAmPmStrings();
3710                 }
3711                 String s = ampm[t.get(Calendar.AM_PM)];
3712                 sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
3713                         Locale.getDefault(Locale.Category.FORMAT))));
3714                 break;
3715             }
3716             case SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
3717                 long i = t.getTimeInMillis() / 1000;
3718                 int flags = Flags.NONE;
3719                 sb.append(localizedMagnitude(null, i, flags, spec.width(), l));
3720                 break;
3721             }
3722             case SECOND:      { // 'S' (00 - 60 - leap second)
3723                 int i = t.get(Calendar.SECOND);
3724                 int flags = Flags.ZERO_PAD;
3725                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3726                 break;
3727             }
3728             case ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?
3729                 int i = t.get(Calendar.ZONE_OFFSET) + t.get(Calendar.DST_OFFSET);
3730                 boolean neg = i < 0;
3731                 sb.append(neg ? '-' : '+');
3732                 if (neg)
3733                     i = -i;
3734                 int min = i / 60000;
3735                 // combine minute and hour into a single integer
3736                 int offset = (min / 60) * 100 + (min % 60);
3737                 int flags = Flags.ZERO_PAD;
3738 
3739                 sb.append(localizedMagnitude(null, offset, flags, 4, l));
3740                 break;
3741             }
3742             case ZONE:        { // 'Z' (symbol)
3743                 TimeZone tz = t.getTimeZone();
3744                 sb.append(tz.getDisplayName((t.get(Calendar.DST_OFFSET) != 0),
3745                         TimeZone.SHORT,
3746                         Objects.requireNonNullElse(l, Locale.US)));
3747                 break;
3748             }
3749 
3750             // Date
3751             case NAME_OF_DAY_ABBREV:     // 'a'
3752             case NAME_OF_DAY:          { // 'A'
3753                 int i = t.get(Calendar.DAY_OF_WEEK);
3754                 Locale lt = Objects.requireNonNullElse(l, Locale.US);
3755                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
3756                 if (dt == DateTime.NAME_OF_DAY)
3757                     sb.append(dfs.getWeekdays()[i]);
3758                 else
3759                     sb.append(dfs.getShortWeekdays()[i]);
3760                 break;
3761             }
3762             case NAME_OF_MONTH_ABBREV:   // 'b'
3763             case NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
3764             case NAME_OF_MONTH:        { // 'B'
3765                 int i = t.get(Calendar.MONTH);
3766                 Locale lt = Objects.requireNonNullElse(l, Locale.US);
3767                 DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
3768                 if (dt == DateTime.NAME_OF_MONTH)
3769                     sb.append(dfs.getMonths()[i]);
3770                 else
3771                     sb.append(dfs.getShortMonths()[i]);
3772                 break;
3773             }
3774             case CENTURY:                // 'C' (00 - 99)
3775             case YEAR_2:                 // 'y' (00 - 99)
3776             case YEAR_4:               { // 'Y' (0000 - 9999)
3777                 int i = t.get(Calendar.YEAR);
3778                 int size = 2;
3779                 switch (dt) {
3780                     case CENTURY:
3781                         i /= 100;
3782                         break;
3783                     case YEAR_2:
3784                         i %= 100;
3785                         break;
3786                     case YEAR_4:
3787                         size = 4;
3788                         break;
3789                 }
3790                 int flags = Flags.ZERO_PAD;
3791                 sb.append(localizedMagnitude(null, i, flags, size, l));
3792                 break;
3793             }
3794             case DAY_OF_MONTH_0:         // 'd' (01 - 31)
3795             case DAY_OF_MONTH:         { // 'e' (1 - 31) -- like d
3796                 int i = t.get(Calendar.DATE);
3797                 int flags = (dt == DateTime.DAY_OF_MONTH_0
3798                         ? Flags.ZERO_PAD
3799                         : Flags.NONE);
3800                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3801                 break;
3802             }
3803             case DAY_OF_YEAR:          { // 'j' (001 - 366)
3804                 int i = t.get(Calendar.DAY_OF_YEAR);
3805                 int flags = Flags.ZERO_PAD;
3806                 sb.append(localizedMagnitude(null, i, flags, 3, l));
3807                 break;
3808             }
3809             case MONTH:                { // 'm' (01 - 12)
3810                 int i = t.get(Calendar.MONTH) + 1;
3811                 int flags = Flags.ZERO_PAD;
3812                 sb.append(localizedMagnitude(null, i, flags, 2, l));
3813                 break;
3814             }
3815 
3816             // Composites
3817             case TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
3818             case TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
3819                 char sep = ':';
3820                 print(spec, sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
3821                 print(spec, sb, t, DateTime.MINUTE, l);
3822                 if (dt == DateTime.TIME) {
3823                     sb.append(sep);
3824                     print(spec, sb, t, DateTime.SECOND, l);
3825                 }
3826                 break;
3827             }
3828             case TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
3829                 char sep = ':';
3830                 print(spec, sb, t, DateTime.HOUR_0, l).append(sep);
3831                 print(spec, sb, t, DateTime.MINUTE, l).append(sep);
3832                 print(spec, sb, t, DateTime.SECOND, l).append(' ');
3833                 // this may be in wrong place for some locales
3834                 StringBuilder tsb = new StringBuilder();
3835                 print(spec, tsb, t, DateTime.AM_PM, l);
3836 
3837                 sb.append(toUpperCaseWithLocale(tsb.toString(), l));
3838                 break;
3839             }
3840             case DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
3841                 char sep = ' ';
3842                 print(spec, sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
3843                 print(spec, sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
3844                 print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
3845                 print(spec, sb, t, DateTime.TIME, l).append(sep);
3846                 print(spec, sb, t, DateTime.ZONE, l).append(sep);
3847                 print(spec, sb, t, DateTime.YEAR_4, l);
3848                 break;
3849             }
3850             case DATE:            { // 'D' (mm/dd/yy)
3851                 char sep = '/';
3852                 print(spec, sb, t, DateTime.MONTH, l).append(sep);
3853                 print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
3854                 print(spec, sb, t, DateTime.YEAR_2, l);
3855                 break;
3856             }
3857             case ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
3858                 char sep = '-';
3859                 print(spec, sb, t, DateTime.YEAR_4, l).append(sep);
3860                 print(spec, sb, t, DateTime.MONTH, l).append(sep);
3861                 print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l);
3862                 break;
3863             }
3864             default:
3865                 assert false;


3866         }
3867         return sb;
3868     }
3869 
3870     private void print(FormatSpecifier spec, TemporalAccessor t, DateTime dt, Locale l)
3871             throws IOException {
3872         StringBuilder sb = new StringBuilder();
3873         print(spec, sb, t, dt, l);
3874         // justify based on width
3875         if (Flags.contains(spec.flags(), Flags.UPPERCASE)) {
3876             appendJustified(spec, a, toUpperCaseWithLocale(sb.toString(), l));
3877         } else {
3878             appendJustified(spec, a, sb);
3879         }
3880     }
3881 
3882     private Appendable print(FormatSpecifier spec, StringBuilder sb, TemporalAccessor t,
3883                              DateTime dt, Locale l) throws IOException {
3884         if (sb == null)
3885             sb = new StringBuilder();
3886         try {
3887             switch (dt) {
3888                 case HOUR_OF_DAY_0: {  // 'H' (00 - 23)
3889                     int i = t.get(ChronoField.HOUR_OF_DAY);
3890                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
3891                     break;
3892                 }
3893                 case HOUR_OF_DAY: {   // 'k' (0 - 23) -- like H
3894                     int i = t.get(ChronoField.HOUR_OF_DAY);
3895                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
3896                     break;
3897                 }
3898                 case HOUR_0:      {  // 'I' (01 - 12)
3899                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
3900                     sb.append(localizedMagnitude(null, i, Flags.ZERO_PAD, 2, l));
3901                     break;
3902                 }
3903                 case HOUR:        { // 'l' (1 - 12) -- like I
3904                     int i = t.get(ChronoField.CLOCK_HOUR_OF_AMPM);
3905                     sb.append(localizedMagnitude(null, i, Flags.NONE, 2, l));
3906                     break;
3907                 }
3908                 case MINUTE:      { // 'M' (00 - 59)
3909                     int i = t.get(ChronoField.MINUTE_OF_HOUR);
3910                     int flags = Flags.ZERO_PAD;
3911                     sb.append(localizedMagnitude(null, i, flags, 2, l));
3912                     break;
3913                 }
3914                 case NANOSECOND:  { // 'N' (000000000 - 999999999)
3915                     int i;
3916                     try {
3917                         i = t.get(ChronoField.NANO_OF_SECOND);
3918                     } catch (UnsupportedTemporalTypeException u) {
3919                         i = t.get(ChronoField.MILLI_OF_SECOND) * 1000000;
3920                     }
3921                     int flags = Flags.ZERO_PAD;
3922                     sb.append(localizedMagnitude(null, i, flags, 9, l));
3923                     break;
3924                 }
3925                 case MILLISECOND: { // 'L' (000 - 999)
3926                     int i = t.get(ChronoField.MILLI_OF_SECOND);
3927                     int flags = Flags.ZERO_PAD;
3928                     sb.append(localizedMagnitude(null, i, flags, 3, l));
3929                     break;
3930                 }
3931                 case MILLISECOND_SINCE_EPOCH: { // 'Q' (0 - 99...?)
3932                     long i = t.getLong(ChronoField.INSTANT_SECONDS) * 1000L +
3933                             t.getLong(ChronoField.MILLI_OF_SECOND);
3934                     int flags = Flags.NONE;
3935                     sb.append(localizedMagnitude(null, i, flags, spec.width(), l));
3936                     break;
3937                 }
3938                 case AM_PM:       { // 'p' (am or pm)
3939                     // Calendar.AM = 0, Calendar.PM = 1, LocaleElements defines upper
3940                     String[] ampm = { "AM", "PM" };
3941                     if (l != null && l != Locale.US) {
3942                         DateFormatSymbols dfs = DateFormatSymbols.getInstance(l);
3943                         ampm = dfs.getAmPmStrings();
3944                     }
3945                     String s = ampm[t.get(ChronoField.AMPM_OF_DAY)];
3946                     sb.append(s.toLowerCase(Objects.requireNonNullElse(l,
3947                             Locale.getDefault(Locale.Category.FORMAT))));
3948                     break;
3949                 }
3950                 case SECONDS_SINCE_EPOCH: { // 's' (0 - 99...?)
3951                     long i = t.getLong(ChronoField.INSTANT_SECONDS);
3952                     int flags = Flags.NONE;
3953                     sb.append(localizedMagnitude(null, i, flags, spec.width(), l));
3954                     break;
3955                 }
3956                 case SECOND:      { // 'S' (00 - 60 - leap second)
3957                     int i = t.get(ChronoField.SECOND_OF_MINUTE);
3958                     int flags = Flags.ZERO_PAD;
3959                     sb.append(localizedMagnitude(null, i, flags, 2, l));
3960                     break;
3961                 }
3962                 case ZONE_NUMERIC: { // 'z' ({-|+}####) - ls minus?
3963                     int i = t.get(ChronoField.OFFSET_SECONDS);
3964                     boolean neg = i < 0;
3965                     sb.append(neg ? '-' : '+');
3966                     if (neg)
3967                         i = -i;
3968                     int min = i / 60;
3969                     // combine minute and hour into a single integer
3970                     int offset = (min / 60) * 100 + (min % 60);
3971                     int flags = Flags.ZERO_PAD;
3972                     sb.append(localizedMagnitude(null, offset, flags, 4, l));
3973                     break;
3974                 }
3975                 case ZONE:        { // 'Z' (symbol)
3976                     ZoneId zid = t.query(TemporalQueries.zone());
3977                     if (zid == null) {
3978                         dt.fail(t);
3979                     }
3980                     if (!(zid instanceof ZoneOffset) &&
3981                             t.isSupported(ChronoField.INSTANT_SECONDS)) {
3982                         Instant instant = Instant.from(t);
3983                         sb.append(TimeZone.getTimeZone(zid.getId())
3984                                 .getDisplayName(zid.getRules().isDaylightSavings(instant),
3985                                         TimeZone.SHORT,
3986                                         Objects.requireNonNullElse(l, Locale.US)));
3987                         break;
3988                     }
3989                     sb.append(zid.getId());
3990                     break;
3991                 }
3992                 // Date
3993                 case NAME_OF_DAY_ABBREV:     // 'a'
3994                 case NAME_OF_DAY:          { // 'A'
3995                     int i = t.get(ChronoField.DAY_OF_WEEK) % 7 + 1;
3996                     Locale lt = Objects.requireNonNullElse(l, Locale.US);
3997                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
3998                     if (dt == DateTime.NAME_OF_DAY)
3999                         sb.append(dfs.getWeekdays()[i]);
4000                     else
4001                         sb.append(dfs.getShortWeekdays()[i]);
4002                     break;
4003                 }
4004                 case NAME_OF_MONTH_ABBREV:   // 'b'
4005                 case NAME_OF_MONTH_ABBREV_X: // 'h' -- same b
4006                 case NAME_OF_MONTH:        { // 'B'
4007                     int i = t.get(ChronoField.MONTH_OF_YEAR) - 1;
4008                     Locale lt = Objects.requireNonNullElse(l, Locale.US);
4009                     DateFormatSymbols dfs = DateFormatSymbols.getInstance(lt);
4010                     if (dt == DateTime.NAME_OF_MONTH)
4011                         sb.append(dfs.getMonths()[i]);
4012                     else
4013                         sb.append(dfs.getShortMonths()[i]);
4014                     break;
4015                 }
4016                 case CENTURY:                // 'C' (00 - 99)
4017                 case YEAR_2:                 // 'y' (00 - 99)
4018                 case YEAR_4:               { // 'Y' (0000 - 9999)
4019                     int i = t.get(ChronoField.YEAR_OF_ERA);
4020                     int size = 2;
4021                     switch (dt) {
4022                         case CENTURY:
4023                             i /= 100;
4024                             break;
4025                         case YEAR_2:
4026                             i %= 100;
4027                             break;
4028                         case YEAR_4:
4029                             size = 4;
4030                             break;
4031                     }
4032                     int flags = Flags.ZERO_PAD;
4033                     sb.append(localizedMagnitude(null, i, flags, size, l));
4034                     break;
4035                 }
4036                 case DAY_OF_MONTH_0:         // 'd' (01 - 31)
4037                 case DAY_OF_MONTH:         { // 'e' (1 - 31) -- like d
4038                     int i = t.get(ChronoField.DAY_OF_MONTH);
4039                     int flags = (dt == DateTime.DAY_OF_MONTH_0
4040                             ? Flags.ZERO_PAD
4041                             : Flags.NONE);
4042                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4043                     break;
4044                 }
4045                 case DAY_OF_YEAR:          { // 'j' (001 - 366)
4046                     int i = t.get(ChronoField.DAY_OF_YEAR);
4047                     int flags = Flags.ZERO_PAD;
4048                     sb.append(localizedMagnitude(null, i, flags, 3, l));
4049                     break;
4050                 }
4051                 case MONTH:                { // 'm' (01 - 12)
4052                     int i = t.get(ChronoField.MONTH_OF_YEAR);
4053                     int flags = Flags.ZERO_PAD;
4054                     sb.append(localizedMagnitude(null, i, flags, 2, l));
4055                     break;
4056                 }
4057 
4058                 // Composites
4059                 case TIME:         // 'T' (24 hour hh:mm:ss - %tH:%tM:%tS)
4060                 case TIME_24_HOUR:    { // 'R' (hh:mm same as %H:%M)
4061                     char sep = ':';
4062                     print(spec, sb, t, DateTime.HOUR_OF_DAY_0, l).append(sep);
4063                     print(spec, sb, t, DateTime.MINUTE, l);
4064                     if (dt == DateTime.TIME) {
4065                         sb.append(sep);
4066                         print(spec, sb, t, DateTime.SECOND, l);
4067                     }
4068                     break;
4069                 }
4070                 case TIME_12_HOUR:    { // 'r' (hh:mm:ss [AP]M)
4071                     char sep = ':';
4072                     print(spec, sb, t, DateTime.HOUR_0, l).append(sep);
4073                     print(spec, sb, t, DateTime.MINUTE, l).append(sep);
4074                     print(spec, sb, t, DateTime.SECOND, l).append(' ');
4075                     // this may be in wrong place for some locales
4076                     StringBuilder tsb = new StringBuilder();
4077                     print(spec, tsb, t, DateTime.AM_PM, l);
4078                     sb.append(toUpperCaseWithLocale(tsb.toString(), l));
4079                     break;
4080                 }
4081                 case DATE_TIME:    { // 'c' (Sat Nov 04 12:02:33 EST 1999)
4082                     char sep = ' ';
4083                     print(spec, sb, t, DateTime.NAME_OF_DAY_ABBREV, l).append(sep);
4084                     print(spec, sb, t, DateTime.NAME_OF_MONTH_ABBREV, l).append(sep);
4085                     print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4086                     print(spec, sb, t, DateTime.TIME, l).append(sep);
4087                     print(spec, sb, t, DateTime.ZONE, l).append(sep);
4088                     print(spec, sb, t, DateTime.YEAR_4, l);
4089                     break;
4090                 }
4091                 case DATE:            { // 'D' (mm/dd/yy)
4092                     char sep = '/';
4093                     print(spec, sb, t, DateTime.MONTH, l).append(sep);
4094                     print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l).append(sep);
4095                     print(spec, sb, t, DateTime.YEAR_2, l);
4096                     break;
4097                 }
4098                 case ISO_STANDARD_DATE: { // 'F' (%Y-%m-%d)
4099                     char sep = '-';
4100                     print(spec, sb, t, DateTime.YEAR_4, l).append(sep);
4101                     print(spec, sb, t, DateTime.MONTH, l).append(sep);
4102                     print(spec, sb, t, DateTime.DAY_OF_MONTH_0, l);
4103                     break;
4104                 }
4105                 default:
4106                     assert false;



4107             }
4108         } catch (DateTimeException x) {
4109             spec.conversion().fail(t);
4110         }
4111         return sb;
4112     }
4113 
4114     // -- Methods to support throwing exceptions --
4115 
4116     private void failMismatch(int flags, char c) {
4117         String fs = Flags.toString(flags);
4118         throw new FormatFlagsConversionMismatchException(fs, c);
4119     }
4120 
4121     private static char getZero(Formatter fmt, Locale l) {
4122         if (l != null && !l.equals(fmt.locale())) {
4123             DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4124             return dfs.getZeroDigit();
4125         }
4126         return fmt.zero;
4127     }
4128 
4129     private StringBuilder localizedMagnitude(StringBuilder sb, long value, int flags, int width, Locale l) {
4130         return localizedMagnitude(sb, Long.toString(value, 10), 0, flags, width, l);
4131     }
4132 
4133     StringBuilder localizedMagnitude(StringBuilder sb,
4134                                      CharSequence value, final int offset, int flags,
4135                                      int width, Locale l) {
4136         if (sb == null) {
4137             sb = new StringBuilder();
4138         }
4139         int begin = sb.length();
4140 
4141         char zero = getZero(this, l);
4142 
4143         // determine localized grouping separator and size
4144         char grpSep = '\0';
4145         int  grpSize = -1;
4146         char decSep = '\0';
4147 
4148         int len = value.length();
4149         int dot = len;
4150         for (int j = offset; j < len; j++) {
4151             if (value.charAt(j) == '.') {
4152                 dot = j;
4153                 break;
4154             }
4155         }
4156 
4157         if (dot < len) {
4158             if (l == null || l.equals(Locale.US)) {
4159                 decSep  = '.';
4160             } else {
4161                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4162                 decSep  = dfs.getDecimalSeparator();
4163             }

4164         }
4165 
4166         if (Flags.contains(flags, Flags.GROUP)) {
4167             if (l == null || l.equals(Locale.US)) {
4168                 grpSep = ',';
4169                 grpSize = 3;
4170             } else {
4171                 DecimalFormatSymbols dfs = DecimalFormatSymbols.getInstance(l);
4172                 grpSep = dfs.getGroupingSeparator();
4173                 DecimalFormat df = null;
4174                 NumberFormat nf = NumberFormat.getNumberInstance(l);
4175                 if (nf instanceof DecimalFormat) {
4176                     df = (DecimalFormat) nf;
4177                 } else {
4178 
4179                     // Use DecimalFormat constructor to obtain the instance,
4180                     // in case NumberFormat.getNumberInstance(l)
4181                     // returns instance other than DecimalFormat
4182                     LocaleProviderAdapter adapter = LocaleProviderAdapter
4183                             .getAdapter(NumberFormatProvider.class, l);
4184                     if (!(adapter instanceof ResourceBundleBasedAdapter)) {
4185                         adapter = LocaleProviderAdapter.getResourceBundleBased();
4186                     }
4187                     String[] all = adapter.getLocaleResources(l)
4188                             .getNumberPatterns();
4189                     df = new DecimalFormat(all[0], dfs);
4190                 }
4191                 grpSize = df.getGroupingSize();
4192                 // Some locales do not use grouping (the number
4193                 // pattern for these locales does not contain group, e.g.
4194                 // ("#0.###")), but specify a grouping separator.
4195                 // To avoid unnecessary identification of the position of
4196                 // grouping separator, reset its value with null character
4197                 if (!df.isGroupingUsed() || grpSize == 0) {
4198                     grpSep = '\0';
4199                 }
4200             }
4201         }
4202 
4203         // localize the digits inserting group separators as necessary
4204         for (int j = offset; j < len; j++) {
4205             if (j == dot) {
4206                 sb.append(decSep);
4207                 // no more group separators after the decimal separator
4208                 grpSep = '\0';
4209                 continue;
4210             }

4211 
4212             char c = value.charAt(j);
4213             sb.append((char) ((c - '0') + zero));
4214             if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
4215                 sb.append(grpSep);
4216             }
4217         }
4218 
4219         // apply zero padding
4220         if (width != -1 && Flags.contains(flags, Flags.ZERO_PAD)) {
4221             for (int k = sb.length(); k < width; k++) {
4222                 sb.insert(begin, zero);
4223             }
4224         }
4225 
4226         return sb;
4227     }
4228 
4229     // Specialized localization of exponents, where the source value can only
4230     // contain characters '0' through '9', starting at index offset, and no
4231     // group separators is added for any locale.
4232     private void localizedMagnitudeExp(FormatSpecifier spec, StringBuilder sb, char[] value,
4233                                        final int offset, Locale l) {
4234         char zero = getZero(this, l);
4235 
4236         int len = value.length;
4237         for (int j = offset; j < len; j++) {
4238             char c = value[j];
4239             sb.append((char) ((c - '0') + zero));
4240         }
4241     }
4242 
4243 
4244     /**
4245      * Enum for {@code BigDecimal} formatting.
4246      */
4247     public enum BigDecimalLayoutForm {
4248         /**
4249          * Format the {@code BigDecimal} in computerized scientific notation.
4250          */
4251         SCIENTIFIC,
4252 
4253         /**
4254          * Format the {@code BigDecimal} as a decimal number.
4255          */
4256         DECIMAL_FLOAT
4257     };
4258 
4259     // refactoring to provide additional tools to the Formatter BSM
4260     // %[argument_index$][flags][width][.precision][t]conversion
4261     private static final String formatSpecifier
4262             = "%(\\d+\\$)?([-#+ 0,(\\<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])";
4263 
4264     private static Pattern fsPattern = Pattern.compile(formatSpecifier);
4265 
4266     /**
4267      * Creates a parsed format list.
4268      * @param format to parse
4269      */
4270     private static List<FormatToken> parse(String format) {
4271         ArrayList<FormatToken> al = new ArrayList<>();
4272         Matcher m = fsPattern.matcher(format);
4273         for (int i = 0, len = format.length(); i < len; ) {
4274             if (m.find(i)) {
4275                 // Anything between the start of the string and the beginning
4276                 // of the format specifier is either fixed text or contains
4277                 // an invalid format string.
4278                 if (m.start() != i) {
4279                     // Make sure we didn't miss any invalid format specifiers
4280                     checkText(format, i, m.start());
4281                     // Assume previous characters were fixed text
4282                     al.add(new FixedString(format, i, m.start()));
4283                 }
4284 
4285                 al.add(new FormatSpecifier(format, m));
4286                 i = m.end();
4287             } else {
4288                 // No more valid format specifiers.  Check for possible invalid
4289                 // format specifiers.
4290                 checkText(format, i, len);
4291                 // The rest of the string is fixed text
4292                 al.add(new FixedString(format, i, format.length()));
4293                 break;
4294             }
4295         }
4296         return al;
4297     }
4298 
4299     private static void checkText(String s, int start, int end) {
4300         for (int i = start; i < end; i++) {
4301             // Any '%' found in the region starts an invalid format specifier.
4302             if (s.charAt(i) == '%') {
4303                 char c = (i == end - 1) ? '%' : s.charAt(i + 1);
4304                 throw new UnknownFormatConversionException(String.valueOf(c));

4305             }
4306         }
4307     }
4308 
4309     /**
4310      * Interface for Formatter specifiers.
4311      */
4312     private interface FormatToken {








4313 
4314         /**
4315          * Return the specifier index.
4316          * @return the index
4317          */
4318         int index();
4319 
4320     }
4321 
4322     private static class FixedString implements FormatToken {
4323         private String s;
4324         private int start;
4325         private int end;
4326 
4327         FixedString(String s, int start, int end) {
4328             this.s = s;
4329             this.start = start;
4330             this.end = end;
4331         }
4332 
4333         public int index() {
4334             return -2;
4335         }
4336 
4337         public Formatter print(Formatter formatter) throws IOException {
4338             formatter.out().append(s, start, end);
4339             return formatter;
4340         }
4341 
4342         public String toString() {
4343             return s.substring(start, end);
4344         }
4345     }
4346 
4347     private static class FormatSpecifier implements FormatToken {
4348         private int index = -1;
4349         private int f = Flags.NONE;
4350         private int width;
4351         private int precision;
4352         private Conversion conversion;
4353         private DateTime dateTime;
4354 
4355         FormatSpecifier(String s, Matcher m) {
4356             index(s, m.start(1), m.end(1));
4357             flags(s, m.start(2), m.end(2));
4358             width(s, m.start(3), m.end(3));
4359             precision(s, m.start(4), m.end(4));
4360 
4361             int tTStart = m.start(5);
4362             if (tTStart >= 0) {
4363                 conversion(s.charAt(tTStart));
4364                 dateTime = DateTime.lookup(s.charAt(m.start(6)));
4365             } else {
4366                 conversion(s.charAt(m.start(6)));
4367             }
4368             checkConversion();
4369         }
4370 
4371         private void index(String s, int start, int end) {
4372             if (start >= 0) {
4373                 try {
4374                     // skip the trailing '$'
4375                     index = Integer.parseInt(s, start, end - 1, 10);
4376                 } catch (NumberFormatException x) {
4377                     assert (false);
4378                 }
4379             } else {
4380                 index = 0;
4381             }
4382         }
4383 
4384         private void flags(String s, int start, int end) {
4385             f = Flags.parse(s, start, end);
4386             if (Flags.contains(f, Flags.PREVIOUS))
4387                 index = -1;
4388         }
4389 
4390         private void width(String s, int start, int end) {
4391             width = -1;
4392             if (start >= 0) {
4393                 try {
4394                     width = Integer.parseInt(s, start, end, 10);
4395                     if (width < 0)
4396                         throw new IllegalFormatWidthException(width);
4397                 } catch (NumberFormatException x) {
4398                     assert (false);
4399                 }
4400             }
4401         }
4402 
4403         private void precision(String s, int start, int end) {
4404             precision = -1;
4405             if (start >= 0) {
4406                 try {
4407                     // skip the leading '.'
4408                     precision = Integer.parseInt(s, start + 1, end, 10);
4409                     if (precision < 0)
4410                         throw new IllegalFormatPrecisionException(precision);
4411                 } catch (NumberFormatException x) {
4412                     assert (false);
4413                 }
4414             }
4415         }
4416 
4417         private void conversion(char conv) {
4418             conversion = Conversion.lookup(conv);
4419             if (Character.isUpperCase(conv)) {
4420                 f = Flags.add(f, Flags.UPPERCASE);
4421                 conversion = Conversion.lookup(Character.toLowerCase(conv));
4422             }
4423             if (Conversion.isText(conversion)) {
4424                 index = -2;
4425             }
4426         }
4427 
4428         public int index() {
4429             return index;
4430         }
4431 
4432         public String value() {
4433             return toString();
4434         }
4435 
4436         public Conversion conversion() {
4437             return conversion;
4438         }
4439 
4440         public DateTime dateTime() {
4441             return dateTime;
4442         }
4443 
4444         public int flags() {
4445             return f;
4446         }
4447 
4448         public int width() {
4449             return width;
4450         }
4451 
4452         public int precision() {
4453             return precision;
4454         }
4455 
4456         public String toString() {
4457             StringBuilder sb = new StringBuilder("%");
4458             // Flags.UPPERCASE is set internally for legal conversions.
4459             sb.append(Flags.toString(Flags.remove(f, Flags.UPPERCASE)));
4460             if (index > 0)
4461                 sb.append(index).append('$');
4462             if (width != -1)
4463                 sb.append(width);
4464             if (precision != -1)
4465                 sb.append('.').append(precision);
4466             sb.append(Flags.contains(f, Flags.UPPERCASE)
4467                     ? Character.toUpperCase(conversion.c) : conversion.c);
4468             if (dateTime != null)
4469                 sb.append(dateTime.c);
4470             return sb.toString();
4471         }
4472 
4473         private void checkConversion() {
4474             switch (conversion) {
4475 
4476                 // Conversions applicable to all objects.
4477                 case BOOLEAN:
4478                 case BOOLEAN_UPPER:
4479                 case STRING:
4480                 case STRING_UPPER:
4481                 case HASHCODE:
4482                 case HASHCODE_UPPER:
4483                     checkGeneral();
4484                     break;
4485 
4486                 // Conversions applicable to date objects.
4487                 case DATE_TIME:
4488                 case DATE_TIME_UPPER:
4489                     checkDateTime();
4490                     break;
4491 
4492                 // Conversions applicable to character.
4493                 case CHARACTER:
4494                 case CHARACTER_UPPER:
4495                     checkCharacter();
4496                     break;
4497 
4498                 // Conversions applicable to integer types.
4499                 case DECIMAL_INTEGER:
4500                 case OCTAL_INTEGER:
4501                 case HEXADECIMAL_INTEGER:
4502                 case HEXADECIMAL_INTEGER_UPPER:
4503                     checkInteger();
4504                     break;
4505 
4506                 // Conversions applicable to floating-point types.
4507                 case SCIENTIFIC:
4508                 case SCIENTIFIC_UPPER:
4509                 case GENERAL:
4510                 case GENERAL_UPPER:
4511                 case DECIMAL_FLOAT:
4512                 case HEXADECIMAL_FLOAT:
4513                 case HEXADECIMAL_FLOAT_UPPER:
4514                     checkFloat();
4515                     break;
4516 
4517                 // Conversions that do not require an argument
4518                 case LINE_SEPARATOR:
4519                 case PERCENT_SIGN:
4520                     checkText();
4521                     break;
4522 




4523             }
4524         }

4525 
4526         private void checkGeneral() {
4527             if ((conversion == Conversion.BOOLEAN || conversion == Conversion.HASHCODE)
4528                     && Flags.contains(f, Flags.ALTERNATE))
4529                 failMismatch(Flags.ALTERNATE, conversion);
4530             // '-' requires a width
4531             if (width == -1 && Flags.contains(f, Flags.LEFT_JUSTIFY))
4532                 throw new MissingFormatWidthException(toString());
4533             checkBadFlags(Flags.PLUS, Flags.LEADING_SPACE, Flags.ZERO_PAD,
4534                     Flags.GROUP, Flags.PARENTHESES);
4535         }
4536 
4537         private void checkDateTime() {
4538             if (precision != -1)
4539                 throw new IllegalFormatPrecisionException(precision);
4540             if (dateTime == null)
4541                 throw new UnknownFormatConversionException(String.valueOf(conversion.c));
4542             checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,
4543                     Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);
4544             // '-' requires a width
4545             if (width == -1 && Flags.contains(f, Flags.LEFT_JUSTIFY))
4546                 throw new MissingFormatWidthException(toString());
4547         }
4548 
4549         private void checkCharacter() {
4550             if (precision != -1)
4551                 throw new IllegalFormatPrecisionException(precision);
4552             checkBadFlags(Flags.ALTERNATE, Flags.PLUS, Flags.LEADING_SPACE,
4553                     Flags.ZERO_PAD, Flags.GROUP, Flags.PARENTHESES);
4554             // '-' requires a width
4555             if (width == -1 && Flags.contains(f, Flags.LEFT_JUSTIFY))
4556                 throw new MissingFormatWidthException(toString());
4557         }
4558 
4559         private void checkInteger() {
4560             checkNumeric();
4561             if (precision != -1)
4562                 throw new IllegalFormatPrecisionException(precision);


4563 
4564             if (conversion == Conversion.DECIMAL_INTEGER)
4565                 checkBadFlags(Flags.ALTERNATE);
4566             else if (conversion == Conversion.OCTAL_INTEGER)
4567                 checkBadFlags(Flags.GROUP);
4568             else
4569                 checkBadFlags(Flags.GROUP);
4570         }
4571 
4572         public void checkBadFlags(int... badFlags) {
4573             for (int badFlag : badFlags)
4574                 if (Flags.contains(f, badFlag))
4575                     failMismatch(badFlag, conversion);
4576         }
4577 
4578         private void checkFloat() {
4579             checkNumeric();
4580             if (conversion == Conversion.DECIMAL_FLOAT) {
4581                 // no check
4582             } else if (conversion == Conversion.HEXADECIMAL_FLOAT) {
4583                 checkBadFlags(Flags.PARENTHESES, Flags.GROUP);
4584             } else if (conversion == Conversion.SCIENTIFIC) {
4585                 checkBadFlags(Flags.GROUP);
4586             } else if (conversion == Conversion.GENERAL) {
4587                 checkBadFlags(Flags.ALTERNATE);
4588             }
4589         }
4590 
4591         private void checkNumeric() {
4592             if (width != -1 && width < 0)
4593                 throw new IllegalFormatWidthException(width);
4594 
4595             if (precision != -1 && precision < 0)
4596                 throw new IllegalFormatPrecisionException(precision);
4597 
4598             // '-' and '0' require a width
4599             if (width == -1
4600                     && (Flags.contains(f, Flags.LEFT_JUSTIFY) || Flags.contains(f, Flags.ZERO_PAD)))
4601                 throw new MissingFormatWidthException(toString());
4602 
4603             // bad combination
4604             if ((Flags.contains(f, Flags.PLUS) && Flags.contains(f, Flags.LEADING_SPACE))
4605                     || (Flags.contains(f, Flags.LEFT_JUSTIFY) && Flags.contains(f, Flags.ZERO_PAD)))
4606                 throw new IllegalFormatFlagsException(Flags.toString(f));
4607         }
4608 
4609         private void checkText() {
4610             if (precision != -1)
4611                 throw new IllegalFormatPrecisionException(precision);
4612             switch (conversion) {
4613                 case PERCENT_SIGN:
4614                     if (f != Flags.LEFT_JUSTIFY
4615                             && f != Flags.NONE)
4616                         throw new IllegalFormatFlagsException(Flags.toString(f));
4617                     // '-' requires a width
4618                     if (width == -1 && Flags.contains(f, Flags.LEFT_JUSTIFY))
4619                         throw new MissingFormatWidthException(toString());
4620                     break;
4621                 case LINE_SEPARATOR:
4622                     if (width != -1)
4623                         throw new IllegalFormatWidthException(width);
4624                     if (f != Flags.NONE)
4625                         throw new IllegalFormatFlagsException(Flags.toString(f));
4626                     break;
4627                 default:
4628                     assert false;
4629             }
4630         }
4631 
4632         // -- Methods to support throwing exceptions --
4633 
4634         public static void failMismatch(int flags, Conversion conersion) {
4635             String fs = Flags.toString(flags);
4636             throw new FormatFlagsConversionMismatchException(fs, conersion.c);
4637         }
4638 
4639     }
4640 
4641     private static class Flags {
4642         public static final int NONE          = 0;         // ''
4643 
4644         // duplicate declarations from Formattable.java
4645         public static final int LEFT_JUSTIFY  = 1 << 0;   // '-'
4646         public static final int UPPERCASE     = 1 << 1;   // '^'
4647         public static final int ALTERNATE     = 1 << 2;   // '#'
4648 
4649         // numerics
4650         public static final int PLUS          = 1 << 3;   // '+'
4651         public static final int LEADING_SPACE = 1 << 4;   // ' '
4652         public static final int ZERO_PAD      = 1 << 5;   // '0'
4653         public static final int GROUP         = 1 << 6;   // ','
4654         public static final int PARENTHESES   = 1 << 7;   // '('
4655 
4656         // indexing
4657         public static final int PREVIOUS      = 1 << 8;   // '<'
4658 
4659         private Flags() {}
4660 
4661         public static boolean contains(int flags, int f) {
4662             return (flags & f) == f;
4663         }
4664 
4665         public static int remove(int flags, int f) {
4666             return flags & ~f;
4667         }
4668 
4669         private static int add(int flags, int f) {
4670             return flags | f;
4671         }
4672 
4673         private static int parse(String s, int start, int end) {
4674             int f = NONE;
4675             for (int i = start; i < end; i++) {
4676                 char c = s.charAt(i);
4677                 int v = parse(c);
4678                 if (contains(f, v))
4679                     throw new DuplicateFormatFlagsException(toString(v));
4680                 f = add(f, v);
4681             }
4682             return f;
4683         }
4684 
4685         // parse those flags which may be provided by users
4686         private static int parse(char c) {
4687             switch (c) {
4688                 case '-': return LEFT_JUSTIFY;
4689                 case '#': return ALTERNATE;
4690                 case '+': return PLUS;
4691                 case ' ': return LEADING_SPACE;
4692                 case '0': return ZERO_PAD;
4693                 case ',': return GROUP;
4694                 case '(': return PARENTHESES;
4695                 case '<': return PREVIOUS;
4696                 default:
4697                     throw new UnknownFormatFlagsException(String.valueOf(c));
4698             }
4699         }
4700 
4701         // Returns a string representation of the current {@code flags}.
4702         public static String toString(int f) {




4703             StringBuilder sb = new StringBuilder();
4704             if (contains(f, LEFT_JUSTIFY))  sb.append('-');
4705             if (contains(f, UPPERCASE))     sb.append('^');
4706             if (contains(f, ALTERNATE))     sb.append('#');
4707             if (contains(f, PLUS))          sb.append('+');
4708             if (contains(f, LEADING_SPACE)) sb.append(' ');
4709             if (contains(f, ZERO_PAD))      sb.append('0');
4710             if (contains(f, GROUP))         sb.append(',');
4711             if (contains(f, PARENTHESES))   sb.append('(');
4712             if (contains(f, PREVIOUS))      sb.append('<');
4713             return sb.toString();
4714         }
4715     }
4716 
4717     private enum Conversion {
4718         NONE('\0'),
4719         // Byte, Short, Integer, Long, BigInteger
4720         // (and associated primitives due to autoboxing)
4721         DECIMAL_INTEGER('d'),            // 'd'
4722         OCTAL_INTEGER('o'),              // 'o'
4723         HEXADECIMAL_INTEGER('x'),        // 'x'
4724         HEXADECIMAL_INTEGER_UPPER('X'),  // 'X'
4725 
4726         // Float, Double, BigDecimal
4727         // (and associated primitives due to autoboxing)
4728         SCIENTIFIC('e'),                 // 'e';
4729         SCIENTIFIC_UPPER('E'),           // 'E';
4730         GENERAL('g'),                    // 'g';
4731         GENERAL_UPPER('G'),              // 'G';
4732         DECIMAL_FLOAT('f'),              // 'f';
4733         HEXADECIMAL_FLOAT('a'),          // 'a';
4734         HEXADECIMAL_FLOAT_UPPER('A'),   // 'A';
4735 
4736         // Character, Byte, Short, Integer
4737         // (and associated primitives due to autoboxing)
4738         CHARACTER('c'),                 // 'c';
4739         CHARACTER_UPPER('C'),           // 'C';
4740 
4741         // java.util.Date, java.util.Calendar, long
4742         DATE_TIME('t'),                 // 't';
4743         DATE_TIME_UPPER('T'),           // 'T';
4744 
4745         // if (arg.TYPE != boolean) return boolean
4746         // if (arg != null) return true; else return false;
4747         BOOLEAN('b'),                   // 'b';
4748         BOOLEAN_UPPER('B'),             // 'B';
4749         // if (arg instanceof Formattable) arg.formatTo()
4750         // else arg.toString();
4751         STRING('s'),                    // 's';
4752         STRING_UPPER('S'),              // 'S';
4753         // arg.hashCode()
4754         HASHCODE('h'),                  // 'h';
4755         HASHCODE_UPPER('H'),            // 'H';
4756 
4757         LINE_SEPARATOR('n'),            // 'n';
4758         PERCENT_SIGN('%');              // '%';
4759 
4760         private final char c;
4761 
4762         Conversion (char c) {
4763             this.c = c;
4764         }
4765 
4766         static Conversion lookup(char c) {

4767             switch (c) {
4768                 case 'd': return DECIMAL_INTEGER;
4769                 case 'o': return OCTAL_INTEGER;
4770                 case 'x': return HEXADECIMAL_INTEGER;
4771                 case 'X': return HEXADECIMAL_INTEGER_UPPER;
4772                 case 'e': return SCIENTIFIC;
4773                 case 'E': return SCIENTIFIC_UPPER;
4774                 case 'g': return GENERAL;
4775                 case 'G': return GENERAL_UPPER;
4776                 case 'f': return DECIMAL_FLOAT;
4777                 case 'a': return HEXADECIMAL_FLOAT;
4778                 case 'A': return HEXADECIMAL_FLOAT_UPPER;
4779                 case 'c': return CHARACTER;
4780                 case 'C': return CHARACTER_UPPER;
4781                 case 't': return DATE_TIME;
4782                 case 'T': return DATE_TIME_UPPER;
4783                 case 'b': return BOOLEAN;
4784                 case 'B': return BOOLEAN_UPPER;
4785                 case 's': return STRING;
4786                 case 'S': return STRING_UPPER;
4787                 case 'h': return HASHCODE;
4788                 case 'H': return HASHCODE_UPPER;
4789                 case 'n': return LINE_SEPARATOR;
4790                 case '%': return PERCENT_SIGN;
4791                 default:
4792                     throw new UnknownFormatConversionException(String.valueOf(c));
4793             }
4794         }
4795 
4796         public void fail(Object arg) {
4797             throw new IllegalFormatConversionException(c, arg.getClass());
4798         }
4799 
4800         static boolean isText(Conversion conv) {
4801             switch (conv) {
4802                 case LINE_SEPARATOR:
4803                 case PERCENT_SIGN:
4804                     return true;
4805                 default:
4806                     return false;
4807             }
4808         }
4809     }
4810 
4811     private enum DateTime {
4812         HOUR_OF_DAY_0('H'),            // 'H' (00 - 23)
4813         HOUR_0('I'),                   // 'I' (01 - 12)
4814         HOUR_OF_DAY('k'),              // 'k'  (0 - 23) -- like H
4815         HOUR('l'),                     // 'l'  (1 - 12) -- like I
4816         MINUTE('M'),                   // 'M'  (00 - 59)
4817         NANOSECOND('N'),               // 'N'  (000000000 - 999999999)
4818         MILLISECOND('L'),              // 'L'  jdk, not in gnu (000 - 999)
4819         MILLISECOND_SINCE_EPOCH('Q'), // 'Q'  (0 - 99...?)
4820         AM_PM('p'),                    // 'p'  (am or pm)
4821         SECONDS_SINCE_EPOCH('s'),     // 's'  (0 - 99...?)
4822         SECOND('S'),                   // 'S'  (00 - 60 - leap second)
4823         TIME('T'),                     // 'T'  (24 hour hh:mm:ss)
4824         ZONE_NUMERIC('z'),             // 'z'  (-1200 - +1200) - ls minus?
4825         ZONE('Z'),                     // 'Z'  (symbol)
4826 
4827         // Date
4828         NAME_OF_DAY_ABBREV('a'),       // 'a' 'a'
4829         NAME_OF_DAY('A'),              // 'A' 'A'
4830         NAME_OF_MONTH_ABBREV('b'),    // 'b' 'b'
4831         NAME_OF_MONTH('B'),            // 'B'  'B'
4832         CENTURY('C'),                  // 'C' (00 - 99)
4833         DAY_OF_MONTH_0('d'),           // 'd' (01 - 31)
4834         DAY_OF_MONTH('e'),             // 'e' (1 - 31) -- like d
4835         // *    ISO_WEEK_OF_YEAR_2('g'),       // 'g'  cross %y %V
4836 // *    ISO_WEEK_OF_YEAR_4('G'),       // 'G'  cross %Y %V
4837         NAME_OF_MONTH_ABBREV_X('h'),  // 'h'  -- same b
4838         DAY_OF_YEAR('j'),              // 'j'  (001 - 366)
4839         MONTH('m'),                    // 'm'  (01 - 12)
4840         // *    DAY_OF_WEEK_1('u'),             // 'u'  (1 - 7) Monday
4841 // *    WEEK_OF_YEAR_SUNDAY('U'),       // 'U'  (0 - 53) Sunday+
4842 // *    WEEK_OF_YEAR_MONDAY_01('V'),    // 'V'  (01 - 53) Monday+
4843 // *    DAY_OF_WEEK_0('w'),             // 'w'  (0 - 6) Sunday
4844 // *    WEEK_OF_YEAR_MONDAY('W'),       // 'W'  (00 - 53) Monday
4845         YEAR_2('y'),                    // 'y'  (00 - 99)
4846         YEAR_4('Y'),                    // 'Y'  (0000 - 9999)
4847 
4848         // Composites
4849         TIME_12_HOUR('r'),             // 'r'  (hh:mm:ss [AP]M)
4850         TIME_24_HOUR('R'),             // 'R'  (hh:mm same as %H:%M)
4851         // *    LOCALE_TIME('X'),               // 'X'  (%H:%M:%S) - parse format?
4852         DATE_TIME('c'),                // 'c'  (Sat Nov 04 12:02:33 EST 1999)
4853         DATE('D'),                     // 'D'  (mm/dd/yy)
4854         ISO_STANDARD_DATE('F');       // 'F'  (%Y-%m-%d)
4855 // *    LOCALE_DATE('x')                // 'x'  (mm/dd/yy)
4856 
4857         static DateTime lookup(char c) {
4858             switch (c) {
4859                 case 'H': return HOUR_OF_DAY_0;
4860                 case 'I': return HOUR_0;
4861                 case 'k': return HOUR_OF_DAY;
4862                 case 'l': return HOUR;
4863                 case 'M': return MINUTE;
4864                 case 'N': return NANOSECOND;
4865                 case 'L': return MILLISECOND;
4866                 case 'Q': return MILLISECOND_SINCE_EPOCH;
4867                 case 'p': return AM_PM;
4868                 case 's': return SECONDS_SINCE_EPOCH;
4869                 case 'S': return SECOND;
4870                 case 'T': return TIME;
4871                 case 'z': return ZONE_NUMERIC;
4872                 case 'Z': return ZONE;
4873 
4874                 // Date
4875                 case 'a': return NAME_OF_DAY_ABBREV;
4876                 case 'A': return NAME_OF_DAY;
4877                 case 'b': return NAME_OF_MONTH_ABBREV;
4878                 case 'B': return NAME_OF_MONTH;
4879                 case 'C': return CENTURY;
4880                 case 'd': return DAY_OF_MONTH_0;
4881                 case 'e': return DAY_OF_MONTH;
4882 // *        case 'g': return ISO_WEEK_OF_YEAR_2;
4883 // *        case 'G': return ISO_WEEK_OF_YEAR_4;
4884                 case 'h': return NAME_OF_MONTH_ABBREV_X;
4885                 case 'j': return DAY_OF_YEAR;
4886                 case 'm': return MONTH;
4887 // *        case 'u': return DAY_OF_WEEK_1;
4888 // *        case 'U': return WEEK_OF_YEAR_SUNDAY;
4889 // *        case 'V': return WEEK_OF_YEAR_MONDAY_01;
4890 // *        case 'w': return DAY_OF_WEEK_0;
4891 // *        case 'W': return WEEK_OF_YEAR_MONDAY;
4892                 case 'y': return YEAR_2;
4893                 case 'Y': return YEAR_4;
4894 
4895                 // Composites
4896                 case 'r': return TIME_12_HOUR;
4897                 case 'R': return TIME_24_HOUR;
4898 // *        case 'X': return LOCALE_TIME;
4899                 case 'c': return DATE_TIME;
4900                 case 'D': return DATE;
4901                 case 'F': return ISO_STANDARD_DATE;
4902 // *        case 'x': return LOCALE_DATE;
4903                 default:
4904                     throw new UnknownFormatConversionException("t" + c);
4905             }
4906         }
4907 
4908         private final char c;
4909 
4910         DateTime(char c) {
4911             this.c = c;
4912         }
4913 
4914         public void fail(Object arg) {
4915             throw new IllegalFormatConversionException(c, arg.getClass());
4916         }
4917     }
4918 
4919     // Formatter BSM
4920 
4921     private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
4922 
4923     private static final MethodHandle SPECIFIER_PRINT =
4924             findVirtualMethodHandle(Formatter.class, "print",
4925                     methodType(Formatter.class, FormatSpecifier.class, Object.class, Locale.class));
4926     private static final MethodHandle SPECIFIER_PRINT_STRING =
4927             findVirtualMethodHandle(Formatter.class, "print",
4928                     methodType(Formatter.class, FormatSpecifier.class, String.class, Locale.class));
4929     private static final MethodHandle SPECIFIER_PRINT_INT =
4930             findVirtualMethodHandle(Formatter.class, "print",
4931                     methodType(Formatter.class, FormatSpecifier.class, int.class, Locale.class));
4932     private static final MethodHandle SPECIFIER_PRINT_LONG =
4933             findVirtualMethodHandle(Formatter.class, "print",
4934                     methodType(Formatter.class, FormatSpecifier.class, long.class, Locale.class));
4935     private static final MethodHandle SPECIFIER_PRINT_BYTE =
4936             findVirtualMethodHandle(Formatter.class, "print",
4937                     methodType(Formatter.class, FormatSpecifier.class, byte.class, Locale.class));
4938     private static final MethodHandle SPECIFIER_PRINT_SHORT =
4939             findVirtualMethodHandle(Formatter.class, "print",
4940                     methodType(Formatter.class, FormatSpecifier.class, short.class, Locale.class));
4941     private static final MethodHandle SPECIFIER_PRINT_FLOAT =
4942             findVirtualMethodHandle(Formatter.class, "print",
4943                     methodType(Formatter.class, FormatSpecifier.class, float.class, Locale.class));
4944     private static final MethodHandle SPECIFIER_PRINT_DOUBLE =
4945             findVirtualMethodHandle(Formatter.class, "print",
4946                     methodType(Formatter.class, FormatSpecifier.class, double.class, Locale.class));
4947     private static final MethodHandle SPECIFIER_PRINT_HASHCODE =
4948             findVirtualMethodHandle(Formatter.class, "printHashCode",
4949                     methodType(Formatter.class, FormatSpecifier.class, Object.class, Locale.class));
4950     private static final MethodHandle FIXED_STRING_PRINT =
4951             findVirtualMethodHandle(FixedString.class, "print",
4952                     methodType(Formatter.class, Formatter.class));
4953 
4954     private static final MethodHandle CONSTRUCT_FORMATTER =
4955             findConstructorMethodHandle(Formatter.class, methodType(void.class))
4956                     .asType(methodType(Formatter.class));
4957     private static final MethodHandle CONSTRUCT_FORMATTER_APPENDABLE =
4958             findConstructorMethodHandle(Formatter.class, methodType(void.class, Appendable.class))
4959                     .asType(methodType(Formatter.class, Appendable.class));
4960 
4961     private static final MethodHandle CONSTRUCT_MISSING_FORMAT_ARGUMENT_EXCEPTION =
4962             findConstructorMethodHandle(MissingFormatArgumentException.class, methodType(void.class, String.class));
4963     private static final MethodHandle APPENDABLE_TO_STRING =
4964             findVirtualMethodHandle(Appendable.class, "toString", methodType(String.class));
4965     private static final MethodHandle FORMATTER_OUT =
4966             findVirtualMethodHandle(Formatter.class, "out", methodType(Appendable.class));
4967     private static final MethodHandle LOCALE_GETDEFAULT =
4968             insertArguments(findStaticMethodHandle(Locale.class, "getDefault",
4969                     methodType(Locale.class, Locale.Category.class)),0, Locale.Category.FORMAT);
4970     private static final MethodHandle FORMATTER_LOCALE =
4971             findVirtualMethodHandle(Formatter.class, "locale", methodType(Locale.class));
4972     private static final MethodHandle ILLEGAL_FORMAT_EXCEPTION_CLONE =
4973             findVirtualMethodHandle(IllegalFormatException.class, "clone", methodType(IllegalFormatException.class));
4974 
4975     private static final MethodHandle INT_TO_STRING =
4976             findStaticMethodHandle(Integer.class, "toString", methodType(String.class, int.class));
4977     private static final MethodHandle BOOLEAN_TO_STRING =
4978             findStaticMethodHandle(Boolean.class, "toString", methodType(String.class, boolean.class));
4979     private static final MethodHandle OBJECT_HASHCODE =
4980             findVirtualMethodHandle(Object.class, "hashCode", methodType(int.class));
4981     private static final MethodHandle INTEGER_TO_HEX_STRING =
4982             findStaticMethodHandle(Integer.class, "toHexString", methodType(String.class, int.class));
4983     private static final MethodHandle INTEGER_TO_OCTAL_STRING =
4984             findStaticMethodHandle(Integer.class, "toOctalString", methodType(String.class, int.class));
4985     private static final MethodHandle LONG_TO_STRING =
4986             findStaticMethodHandle(Long.class, "toString", methodType(String.class, long.class));
4987     private static final MethodHandle LONG_TO_HEX_STRING =
4988             findStaticMethodHandle(Long.class, "toHexString", methodType(String.class, long.class));
4989     private static final MethodHandle LONG_TO_OCTAL_STRING =
4990             findStaticMethodHandle(Long.class, "toOctalString", methodType(String.class, long.class));
4991     private static final MethodHandle STRING_TO_UPPER_CASE =
4992             findVirtualMethodHandle(String.class, "toUpperCase", methodType(String.class));
4993 
4994     private static final MethodHandle LOCALE_GUARD = findStaticMethodHandle(Formatter.class, "localeGuard",
4995             methodType(boolean.class, Locale.class, Locale.class));
4996     private static final MethodHandle BOOLEAN_OBJECT_FILTER = findStaticMethodHandle(Formatter.class, "booleanObjectFilter",
4997             methodType(boolean.class, Object.class));
4998     private static final MethodHandle NOT_NULL_TEST = findStaticMethodHandle(Formatter.class, "notNullTest",
4999             methodType(boolean.class, Object.class));
5000 
5001     private static final int MISSING_ARGUMENT_INDEX = Integer.MIN_VALUE;
5002 
5003     /**
5004      * formatterFormatBootstrap bootstrap.
5005      * @param lookup               MethodHandles lookup
5006      * @param name                 Name of method
5007      * @param methodType           Method signature
5008      * @param format               Formatter format string
5009      * @param isStringMethod       Method's owner is String or not
5010      * @param hasLocale            has Locale
5011      * @throws NoSuchMethodException no such method
5012      * @throws IllegalAccessException illegal access
5013      * @throws StringConcatException string concat error
5014      * @return Callsite for intrinsic method
5015      */
5016     public static CallSite formatterBootstrap(MethodHandles.Lookup lookup,
5017                                               String name,
5018                                               MethodType methodType,
5019                                               String format,
5020                                               int isStringMethod,
5021                                               int hasLocale)
5022             throws NoSuchMethodException, IllegalAccessException, StringConcatException {
5023         boolean isString = isStringMethod == 1;
5024         boolean hasLocaleArg = hasLocale == 1;
5025         boolean isVarArgs = isVarArgsType(methodType, isString, hasLocaleArg);
5026         if (isVarArgs) {
5027             return new ConstantCallSite(fallbackMethodHandle(
5028                     lookup, name, methodType, format,
5029                     isString, hasLocaleArg, isVarArgs));
5030         }
5031 
5032         List<FormatToken> specs;
5033 
5034         try {
5035             specs = parse(format);
5036         } catch (IllegalFormatException illegalFormatException) {
5037             return new ConstantCallSite(illegalFormatThrower(illegalFormatException, methodType));
5038         }
5039 
5040         if (specs.isEmpty()) {
5041             return new ConstantCallSite(isString ?
5042                     constant(String.class, "").asType(methodType) :
5043                     identity(methodType.parameterType(0)).asType(methodType));
5044         }
5045         // Array of formatter args excluding target and locale
5046         Class<?>[] argTypes = methodType.dropParameterTypes(0, firstFormatterArg(isString, hasLocaleArg)).parameterArray();
5047         // index array is needed for argument analysis
5048         int[] argIndexes = calculateArgumentIndexes(specs, argTypes.length);
5049         return isString && mayNotNeedFormatter(specs, argTypes, argIndexes) ?
5050                 new ConstantCallSite(new StringConcatHandleBuilder(specs, argTypes, argIndexes, hasLocaleArg).getHandle(lookup, methodType)) :
5051                 new ConstantCallSite(fallbackMethodHandle(lookup, name, methodType, format, isString, hasLocaleArg, isVarArgs));
5052     }
5053 
5054     private static int[] calculateArgumentIndexes(List<FormatToken> specs, int argCount) {
5055         int[] argIndexes = new int[specs.size()];
5056         int last = -1;
5057         int lasto = -1;
5058 
5059         // Calculate indices and throw exceptions for missing arguments
5060         for (int i = 0; i < specs.size(); i++) {
5061             FormatToken ft = specs.get(i);
5062 
5063             int index = ft.index();
5064             switch (index) {
5065                 case -2:  // fixed string, "%n", or "%%"
5066                     argIndexes[i] = -1;
5067                     break;
5068                 case -1:  // relative index
5069                     argIndexes[i] = (last < 0 || last >= argCount) ? MISSING_ARGUMENT_INDEX : last;
5070                     break;
5071                 case 0:  // ordinary index
5072                     lasto++;
5073                     last = lasto;
5074                     argIndexes[i] = (last < 0 || last >= argCount) ? MISSING_ARGUMENT_INDEX : last;
5075                     break;
5076                 default:  // explicit index
5077                     last = index - 1;
5078                     argIndexes[i] = (last < 0 || last >= argCount) ? MISSING_ARGUMENT_INDEX : last;
5079                     break;
5080             }
5081         }
5082 
5083         return argIndexes;
5084     }
5085 
5086     private static boolean mayNotNeedFormatter(List<FormatToken> specs, Class<?>[] argTypes, int[] argIndexes) {
5087         for (int i = 0; i < specs.size(); i++) {
5088             if (argIndexes[i] >= 0
5089                     && !canUseDirectConcat((FormatSpecifier) specs.get(i), argTypes[argIndexes[i]])
5090                     && !canUseSpecialConverter((FormatSpecifier) specs.get(i), argTypes[argIndexes[i]])) {
5091                 return false;
5092             }
5093         }
5094 
5095         return true;
5096     }
5097 
5098     private static int firstFormatterArg(boolean isStringMethod, boolean hasLocaleArg) {
5099         int index = isStringMethod ? 0 : 1;
5100         return hasLocaleArg ? index + 1 : index;
5101     }
5102 
5103     private static MethodHandle findVirtualMethodHandle(Class<?> type, String name, MethodType methodType) {
5104         try {
5105             return LOOKUP.findVirtual(type, name, methodType);
5106         } catch (NoSuchMethodException | IllegalAccessException e) {
5107             throw new RuntimeException(e);
5108         }
5109     }
5110 
5111     private static MethodHandle findStaticMethodHandle(Class<?> type, String name, MethodType methodType) {
5112         try {
5113             return LOOKUP.findStatic(type, name, methodType);
5114         } catch (NoSuchMethodException | IllegalAccessException e) {
5115             throw new RuntimeException(e);
5116         }
5117     }
5118 
5119     private static MethodHandle findConstructorMethodHandle(Class<?> type, MethodType methodType) {
5120         try {
5121             return LOOKUP.findConstructor(type, methodType);
5122         } catch (NoSuchMethodException | IllegalAccessException e) {
5123             throw new RuntimeException(e);
5124         }
5125     }
5126 
5127 
5128     static abstract class FormatHandleBuilder {
5129         final List<FormatToken> specs;
5130         final Class<?>[] argTypes;
5131         final int[] argIndexes;
5132         final boolean hasLocaleArg;
5133 
5134         FormatHandleBuilder(List<FormatToken> specs, Class<?>[] argTypes, int[] argIndexes, boolean hasLocaleArg) {
5135             this.specs = specs;
5136             this.argTypes = argTypes;
5137             this.argIndexes = argIndexes;
5138             this.hasLocaleArg = hasLocaleArg;
5139         }
5140 
5141         void buildHandles() {
5142             for (int i = 0; i < specs.size(); i++) {
5143                 if (argIndexes[i] == -1) {
5144                     addConstantMethodHandle(argTypes, specs.get(i));
5145                 } else if (argIndexes[i] == MISSING_ARGUMENT_INDEX) {
5146                     addMissingArgumentMethodHandle(argTypes, (FormatSpecifier) specs.get(i));
5147                 } else {
5148                     addArgumentMethodHandle(argTypes, (FormatSpecifier) specs.get(i), argIndexes[i]);
5149                 }
5150             }
5151         }
5152 
5153         abstract void addArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec, int argIndex);
5154         abstract void addConstantMethodHandle(Class<?>[] argTypes, FormatToken spec);
5155         abstract void addMissingArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec);
5156         abstract MethodHandle getHandle(MethodHandles.Lookup lookup, MethodType methodType);
5157     }
5158 
5159 
5160     static class FormatterFormatHandleBuilder extends FormatHandleBuilder {
5161 
5162         private MethodHandle handle = null;
5163         final boolean isFormatterMethod;
5164         final boolean isStringMethod;
5165 
5166         FormatterFormatHandleBuilder(List<FormatToken> specs, Class<?>[] argTypes, int[] argIndexes,
5167                                      boolean hasLocaleArg, boolean isFormatterMethod, boolean isStringMethod) {
5168             super(specs, argTypes, argIndexes, hasLocaleArg);
5169             this.isFormatterMethod = isFormatterMethod;
5170             this.isStringMethod = isStringMethod;
5171         }
5172 
5173         @Override
5174         public void addArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec, int argIndex) {
5175             MethodHandle appender;
5176 
5177             if (canUseSpecialConverter(spec, argTypes[argIndex])) {
5178                 MethodHandle conversionFilter = getSpecializedConverter(spec, argTypes[argIndex]);
5179                 appender = filterArguments(SPECIFIER_PRINT_STRING, 2, conversionFilter);
5180                 appender = insertArguments(appender, 1, spec);
5181             } else {
5182                 appender = getPrintHandle(argTypes[argIndex], spec);
5183                 appender = insertArguments(appender, 1, spec);
5184             }
5185 
5186             appender = appender.asType(appender.type().changeParameterType(1, argTypes[argIndex]));
5187 
5188             if (argIndex > 0) {
5189                 appender = dropArguments(appender, 1, Arrays.copyOfRange(argTypes, 0, argIndex));
5190             }
5191             if (argIndex < argTypes.length - 1) {
5192                 appender = dropArguments(appender, argIndex + 2, Arrays.copyOfRange(argTypes, argIndex + 1, argTypes.length));
5193             }
5194 
5195             if (handle == null) {
5196                 handle = appender;
5197             } else {
5198                 handle = foldArguments(appender, handle.asType(handle.type().changeReturnType(void.class)));
5199             }
5200         }
5201 
5202         @Override
5203         public void addConstantMethodHandle(Class<?>[] argTypes, FormatToken spec) {
5204             MethodHandle appender;
5205             if (spec instanceof FixedString) {
5206                 appender = dropArguments(insertArguments(FIXED_STRING_PRINT, 0, spec), 1, Locale.class);
5207             } else {
5208                 appender = insertArguments(SPECIFIER_PRINT, 1, spec);
5209                 appender = insertArguments(appender, 1, (Object) null);
5210             }
5211             appender = dropArguments(appender, 1, Arrays.copyOfRange(argTypes, 0, argTypes.length));
5212 
5213             if (handle == null) {
5214                 handle = appender;
5215             } else {
5216                 handle = foldArguments(appender, handle.asType(handle.type().changeReturnType(void.class)));
5217             }
5218         }
5219 
5220         @Override
5221         public void addMissingArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec) {
5222             MethodHandle thrower = missingFormatArgumentThrower(spec.toString(), Formatter.class);
5223             thrower = dropArguments(thrower, 0, Formatter.class);
5224             thrower = dropArguments(thrower, 1, argTypes);
5225             thrower = dropArguments(thrower, thrower.type().parameterCount(), Locale.class);
5226 
5227             if (handle == null) {
5228                 handle = thrower;
5229             } else {
5230                 handle = foldArguments(thrower, handle.asType(handle.type().changeReturnType(void.class)));
5231             }
5232         }
5233 
5234         @Override
5235         public MethodHandle getHandle(MethodHandles.Lookup lookup, MethodType methodType) {
5236 
5237             buildHandles();
5238 
5239             MethodHandle wrapper;
5240 
5241             if (isFormatterMethod) {
5242                 wrapper = handle;
5243             } else {
5244                 if (isStringMethod) {
5245                     wrapper = foldArguments(handle, 0, CONSTRUCT_FORMATTER);
5246                 } else {
5247                     wrapper = filterArguments(handle, 0, CONSTRUCT_FORMATTER_APPENDABLE);
5248                 }
5249                 wrapper = filterReturnValue(wrapper, FORMATTER_OUT);
5250             }
5251 
5252             if (hasLocaleArg) {
5253                 int[] argmap = new int[methodType.parameterCount()];
5254                 if (!isStringMethod) {
5255                     argmap[0] = 0;
5256                     argmap[argmap.length - 1] = 1;
5257                     for (int i = 1; i < argmap.length - 1; i++) {
5258                         argmap[i] = i + 1;
5259                     }
5260                 } else {
5261                     argmap[argmap.length - 1] = 0;
5262                     for (int i = 0; i < argmap.length - 1; i++) {
5263                         argmap[i] = i + 1;
5264                     }
5265                 }
5266                 MethodType newType = methodType.changeReturnType(wrapper.type().returnType());
5267                 if (!isStringMethod) {
5268                     newType = newType.changeParameterType(0, wrapper.type().parameterType(0));
5269                 }
5270                 wrapper = MethodHandles.permuteArguments(wrapper, newType, argmap);
5271             } else {
5272                 if (isFormatterMethod) {
5273                     wrapper = foldLocaleFromFormatter(wrapper, methodType.parameterCount());
5274                 } else {
5275                     wrapper = foldArguments(wrapper, methodType.parameterCount(), LOCALE_GETDEFAULT);
5276                 }
5277             }
5278             if (isStringMethod) {
5279                 wrapper = filterReturnValue(wrapper, APPENDABLE_TO_STRING);
5280             }
5281             return wrapper.asType(methodType);
5282         }
5283     }
5284 
5285     static class StringConcatHandleBuilder extends FormatHandleBuilder {
5286 
5287         MethodType concatType = methodType(String.class);
5288         StringBuilder recipe = new StringBuilder();
5289 
5290         List<Object> constants = new ArrayList<>();
5291         List<Integer> reorder = new ArrayList<>();
5292         List<MethodHandle> argumentFormatters = new ArrayList<>();
5293 
5294         boolean needsLocaleGuard = false;
5295 
5296         StringConcatHandleBuilder(List<FormatToken> specs, Class<?>[] argTypes, int[] argIndexes, boolean hasLocaleArg) {
5297             super(specs, argTypes, argIndexes, hasLocaleArg);
5298         }
5299 
5300         @Override
5301         public void addArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec, int argIndex) {
5302 
5303             // Add argument token to recipe
5304             recipe.append('\1');
5305 
5306             Class<?> argType = argTypes[argIndex];
5307             boolean useDirectConcat = canUseDirectConcat(spec, argType);
5308             concatType = concatType.appendParameterTypes(useDirectConcat ? argType : String.class);
5309             reorder.add(argIndex);
5310 
5311             if (useDirectConcat) {
5312                 if (spec.conversion() == Conversion.DECIMAL_INTEGER) {
5313                     // Direct string concat, but we need to guard against locales requiring Unicode decimal symbols
5314                     needsLocaleGuard = true;
5315                 }
5316                 argumentFormatters.add(null);
5317             } else {
5318                 // Direct handle requiring no formatter or localization
5319                 assert canUseSpecialConverter(spec, argType);
5320                 MethodHandle conversionFilter = getSpecializedConverter(spec, argType);
5321                 argumentFormatters.add(conversionFilter);
5322 
5323             }
5324         }
5325 
5326         @Override
5327         public void addConstantMethodHandle(Class<?>[] argTypes, FormatToken spec) {
5328             String value = getConstantSpecValue(spec);
5329             // '\1' and '\2' denote argument or constant to StringConcatFactory
5330             if (value.indexOf('\1') == -1 && value.indexOf('\2') == -1) {
5331                 recipe.append(value);
5332             } else {
5333                 recipe.append('\2');
5334                 constants.add(value);
5335             }
5336         }
5337 
5338         @Override
5339         public void addMissingArgumentMethodHandle(Class<?>[] argTypes, FormatSpecifier spec) {
5340             MethodHandle thrower = throwException(void.class, MissingFormatArgumentException.class);
5341             thrower = foldArguments(thrower, insertArguments(CONSTRUCT_MISSING_FORMAT_ARGUMENT_EXCEPTION, 0, spec.toString()));
5342             argumentFormatters.add(thrower);
5343         }
5344 
5345         @Override
5346         public MethodHandle getHandle(MethodHandles.Lookup lookup, MethodType methodType) {
5347 
5348             buildHandles();
5349 
5350             CallSite cs;
5351 
5352             try {
5353                 cs = StringConcatFactory.makeConcatWithConstants(lookup, "formatterBootstrap", concatType, recipe.toString(), constants.toArray());
5354             } catch (StringConcatException sce) {
5355                 throw new RuntimeException(sce);
5356             }
5357 
5358             MethodHandle handle = dropArguments(cs.getTarget(), concatType.parameterCount(), Locale.class);
5359 
5360             int paramIndex = 0;
5361 
5362             for (MethodHandle formatter : argumentFormatters) {
5363 
5364                 if (formatter != null) {
5365                     int paramCount = formatter.type().parameterCount();
5366                     if (paramCount == 0) {
5367                         handle = foldArguments(handle, 0, formatter);
5368                     } else {
5369                         assert paramCount == 1;
5370                         handle = filterArguments(handle, paramIndex, formatter);
5371                     }
5372                 }
5373 
5374                 paramIndex++;
5375             }
5376 
5377             // move Locale argument from last to first position
5378             handle = moveArgToFront(handle, handle.type().parameterCount() - 1);
5379 
5380             if (needsLocaleGuard) {
5381                 // We have a decimal int without formatter - this doesn't work for
5382                 // locales using unicode decimal symbols, so add a guard and fallback handle for that case
5383                 Locale safeDefaultLocale = getSafeDefaultLocale();
5384                 MethodType mt = hasLocaleArg ? methodType : methodType.insertParameterTypes(0, Locale.class);
5385                 handle = MethodHandles.guardWithTest(
5386                         insertArguments(LOCALE_GUARD, 0, safeDefaultLocale),
5387                         handle,
5388                         new FormatterFormatHandleBuilder(specs, argTypes, argIndexes, true, false, true)
5389                                 .getHandle(lookup, mt));
5390             }
5391 
5392             if (!hasLocaleArg) {
5393                 handle = foldArguments(handle, 0, LOCALE_GETDEFAULT);
5394             }
5395 
5396             int[] reorderArray = hasLocaleArg ?
5397                     // Leading Locale arg - add initial element to keep it in place and increase other values by 1
5398                     IntStream.concat(IntStream.of(0), reorder.stream().mapToInt(i -> i + 1)).toArray() :
5399                     reorder.stream().mapToInt(i -> i).toArray();
5400 
5401             return MethodHandles.permuteArguments(handle, methodType, reorderArray);
5402         }
5403     }
5404 
5405     private static Locale getSafeDefaultLocale() {
5406         Locale defaultLocale = Locale.getDefault(Locale.Category.FORMAT);
5407         if (defaultLocale == null || DecimalFormatSymbols.getInstance(defaultLocale).getZeroDigit() != '0') {
5408             defaultLocale = Locale.US;
5409         }
5410         return defaultLocale;
5411     }
5412 
5413     private static MethodHandle moveArgToFront(MethodHandle handle, int argIndex) {
5414         Class<?>[] paramTypes = handle.type().parameterArray();
5415 
5416         MethodType methodType = methodType(handle.type().returnType(), paramTypes[argIndex]);
5417         int[] reorder = new int[paramTypes.length];
5418         reorder[argIndex] = 0;
5419 
5420         for (int i = 0, j = 1; i < paramTypes.length; i++) {
5421             if (i != argIndex) {
5422                 methodType = methodType.appendParameterTypes(paramTypes[i]);
5423                 reorder[i] = j++;
5424             }
5425         }
5426         return permuteArguments(handle, methodType, reorder);
5427     }
5428 
5429     private static MethodHandle foldLocaleFromFormatter(MethodHandle handle, int localeArgIndex) {
5430         return foldArguments(moveArgToFront(handle, localeArgIndex), FORMATTER_LOCALE);
5431     }
5432 
5433     private static String getConstantSpecValue(Object o) {
5434         if (o instanceof FormatSpecifier) {
5435             FormatSpecifier spec = (FormatSpecifier) o;
5436             assert spec.index() == -2;
5437             if (spec.conversion() == Conversion.LINE_SEPARATOR) {
5438                 return System.lineSeparator();
5439             } else if (spec.conversion() == Conversion.PERCENT_SIGN) {
5440                 return String.format(spec.toString());
5441             }
5442         }
5443         return o.toString();
5444     }
5445 
5446     private static MethodHandle missingFormatArgumentThrower(String message, Class<?> returnType) {
5447         MethodHandle thrower = throwException(returnType, MissingFormatArgumentException.class);
5448         return foldArguments(thrower, insertArguments(CONSTRUCT_MISSING_FORMAT_ARGUMENT_EXCEPTION, 0, message));
5449     }
5450 
5451     private static MethodHandle illegalFormatThrower(IllegalFormatException illegalFormat, MethodType methodType) {
5452         MethodHandle thrower = throwException(methodType.returnType(), IllegalFormatException.class);
5453         thrower = foldArguments(thrower, 0, insertArguments(ILLEGAL_FORMAT_EXCEPTION_CLONE, 0, illegalFormat));
5454         return dropArguments(thrower, 0, methodType.parameterArray());
5455     }
5456 
5457     private static boolean localeGuard(Locale locale1, Locale locale2) {
5458         return locale1 == locale2;
5459     }
5460 
5461     private static boolean booleanObjectFilter(Object arg) {
5462         return arg != null && (! (arg instanceof Boolean) || ((Boolean) arg));
5463     }
5464 
5465     private static boolean notNullTest(Object arg) {
5466         return arg != null;
5467     }
5468 
5469 
5470     private static MethodHandle getSpecializedConverter(FormatSpecifier spec, Class<?> argType) {
5471         MethodHandle conversionFilter;
5472 
5473         switch (spec.conversion()) {
5474             case HASHCODE:
5475                 conversionFilter = filterArguments(INTEGER_TO_HEX_STRING, 0, OBJECT_HASHCODE);
5476                 break;
5477             case DECIMAL_INTEGER:
5478                 conversionFilter = argType == long.class ? LONG_TO_STRING : INT_TO_STRING;
5479                 break;
5480             case HEXADECIMAL_INTEGER:
5481                 conversionFilter =  argType == long.class ? LONG_TO_HEX_STRING : INTEGER_TO_HEX_STRING;
5482                 break;
5483             case OCTAL_INTEGER:
5484                 conversionFilter =  argType == long.class ? LONG_TO_OCTAL_STRING : INTEGER_TO_OCTAL_STRING;
5485                 break;
5486             case BOOLEAN:
5487                 conversionFilter = BOOLEAN_TO_STRING;
5488                 break;
5489             default:
5490                 throw new IllegalStateException("Unexpected conversion: " + spec.conversion());

5491         }
5492 
5493         if (conversionFilter.type().parameterType(0) != argType) {
5494             if (spec.conversion() == Conversion.BOOLEAN)
5495                 conversionFilter = filterArguments(conversionFilter, 0, BOOLEAN_OBJECT_FILTER);
5496             else if (! argType.isPrimitive())
5497                 conversionFilter = guardWithTest(NOT_NULL_TEST,
5498                         conversionFilter.asType(methodType(String.class, Object.class)),
5499                         dropArguments(constant(String.class, "null"), 0, Object.class));
5500             conversionFilter = conversionFilter.asType(conversionFilter.type().changeParameterType(0, argType));
5501         }
5502 
5503         if (spec.flags() == Flags.UPPERCASE) {
5504             conversionFilter = filterArguments(STRING_TO_UPPER_CASE,0, conversionFilter);
5505         }
5506 
5507         return conversionFilter;
5508     }
5509 
5510     private static boolean canUseSpecialConverter(FormatSpecifier spec, Class<?> argType) {
5511         return (spec.flags() == Flags.NONE || spec.flags() == Flags.UPPERCASE)
5512                 && spec.width() == -1
5513                 && !requiresLocalization(spec)
5514                 && isSafeArgumentType(spec.conversion(), argType);
5515     }
5516 
5517     private static boolean canUseDirectConcat(FormatSpecifier spec, Class<?> argType) {
5518         if (spec.flags() == Flags.NONE
5519                 && spec.width() == -1
5520                 && isSafeArgumentType(spec.conversion(), argType)) {
5521             switch (spec.conversion()) {
5522                 case STRING:
5523                 case BOOLEAN:
5524                 case CHARACTER:
5525                 case DECIMAL_INTEGER:
5526                 case LINE_SEPARATOR:
5527                 case PERCENT_SIGN:
5528                     return true;
5529             }
5530         }
5531         return false;
5532     }
5533 
5534     private static boolean isSafeArgumentType(Conversion conversion, Class<?> type) {
5535         if (conversion == Conversion.BOOLEAN) {
5536             return type == boolean.class || type == Boolean.class;
5537         }
5538         if (conversion == Conversion.CHARACTER) {
5539             return type == char.class || type == Character.class;
5540         }
5541         if (conversion == Conversion.DECIMAL_INTEGER
5542                 || conversion == Conversion.HEXADECIMAL_INTEGER
5543                 || conversion == Conversion.OCTAL_INTEGER) {
5544             return type == int.class || type == long.class || type == Integer.class;
5545         }
5546         if (conversion == Conversion.HASHCODE) {
5547             return true;
5548         }
5549         // Limit to String to prevent us from doing toString() on a java.util.Formattable
5550         return conversion == Conversion.STRING && type == String.class;
5551     }
5552 
5553     private static boolean requiresLocalization(FormatSpecifier spec) {
5554         switch (spec.conversion()) {
5555             case BOOLEAN:
5556             case HEXADECIMAL_INTEGER:
5557             case OCTAL_INTEGER:
5558             case HASHCODE:
5559             case LINE_SEPARATOR:
5560             case PERCENT_SIGN:


5561                 return false;
5562             default:
5563                 return true;
5564         }
5565     }
5566 
5567     private static MethodHandle getPrintHandle(Class<?> argType, FormatSpecifier spec) {
5568         if (spec.conversion() == Conversion.HASHCODE) {
5569             return SPECIFIER_PRINT_HASHCODE;
5570         } else if (spec.conversion() == Conversion.DECIMAL_INTEGER && argType == int.class) {
5571             return SPECIFIER_PRINT_INT;
5572         } else if (spec.conversion() == Conversion.DECIMAL_INTEGER && argType == long.class) {
5573             return SPECIFIER_PRINT_LONG;
5574         } else if (spec.conversion() == Conversion.DECIMAL_INTEGER && argType == byte.class) {
5575             return SPECIFIER_PRINT_BYTE;
5576         } else if (spec.conversion() == Conversion.DECIMAL_INTEGER && argType == short.class) {
5577             return SPECIFIER_PRINT_SHORT;
5578         } else if (spec.conversion() == Conversion.DECIMAL_FLOAT && argType == float.class) {
5579             return SPECIFIER_PRINT_FLOAT;
5580         } else if (spec.conversion() == Conversion.DECIMAL_FLOAT && argType == double.class) {
5581             return SPECIFIER_PRINT_DOUBLE;
5582         } else {
5583             return SPECIFIER_PRINT;
5584         }
5585     }
5586 
5587     private static MethodHandle fallbackMethodHandle(MethodHandles.Lookup lookup, String name,
5588                                                      MethodType methodType, String format, boolean isStringMethod,
5589                                                      boolean hasLocaleArg, boolean isVarArgs) {
5590         if (isStringMethod) {
5591             MethodHandle handle = findStaticMethodHandle(lookup, String.class, name,
5592                     hasLocaleArg ? methodType(String.class, Locale.class, String.class, Object[].class)
5593                             : methodType(String.class, String.class, Object[].class));
5594             return wrapHandle(handle, hasLocaleArg ? 1 : 0, format, methodType, isVarArgs);
5595         }
5596         Class<?> type = methodType.parameterType(0);
5597         MethodHandle handle = findVirtualMethodHandle(lookup, type, name,
5598                 hasLocaleArg ? methodType(type, Locale.class, String.class, Object[].class)
5599                         : methodType(type, String.class, Object[].class));
5600         return wrapHandle(handle, hasLocaleArg ? 2 : 1, format, methodType, isVarArgs);
5601     }





5602 
5603     private static MethodHandle wrapHandle(MethodHandle handle, int formatArgIndex, String format, MethodType methodType, boolean isVarArg) {
5604         MethodHandle h = MethodHandles.insertArguments(handle, formatArgIndex, format);
5605         if (!isVarArg) {
5606             h = h.asCollector(Object[].class, methodType.parameterCount() - formatArgIndex);
5607         }
5608         return h.asType(methodType);
5609     }


5610 
5611     private static boolean isVarArgsType(MethodType methodType, boolean isStringMethod, boolean hasLocaleArg) {
5612         int expectedArrayArgument = (isStringMethod ? 0 : 1) + (hasLocaleArg ? 1 : 0);
5613         return methodType.parameterCount() == expectedArrayArgument + 1
5614                 && methodType.parameterType(expectedArrayArgument) == Object[].class;
5615     }











5616 
5617     private static MethodHandle findVirtualMethodHandle(MethodHandles.Lookup lookup, Class<?> type, String name, MethodType methodType) {
5618         try {
5619             return lookup.findVirtual(type, name, methodType);
5620         } catch (NoSuchMethodException | IllegalAccessException e) {
5621             throw new RuntimeException(e);
5622         }
5623     }













5624 
5625     private static MethodHandle findStaticMethodHandle(MethodHandles.Lookup lookup, Class<?> type, String name, MethodType methodType) {
5626         try {
5627             return lookup.findStatic(type, name, methodType);
5628         } catch (NoSuchMethodException | IllegalAccessException e) {
5629             throw new RuntimeException(e);







5630         }
5631     }
5632 }
< prev index next >